From fb1052824ce999e2cba72125c52916793f1e460a Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Sat, 7 May 2016 20:24:16 +0300 Subject: [PATCH 0001/1808] Added 06.05.16 updates * Added the field emoji to the Sticker object. Your bot can now know the emoji a sticker corresponds to. * Added the field forwarded_from_chat to the Message object for messages forwarded from channels. --- telebot/types.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 665326f21..2ef11b5a4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -170,6 +170,8 @@ def de_json(cls, json_string): opts = {} if 'forward_from' in obj: opts['forward_from'] = User.de_json(obj['forward_from']) + if 'forward_from_chat' in obj: + opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) if 'forward_date' in obj: opts['forward_date'] = obj['forward_date'] if 'reply_to_message' in obj: @@ -261,6 +263,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.from_user = from_user self.date = date self.chat = chat + self.forward_from_chat = None self.forward_from = None self.forward_date = None self.reply_to_message = None @@ -394,14 +397,16 @@ def de_json(cls, json_string): thumb = None if 'thumb' in obj: thumb = PhotoSize.de_json(obj['thumb']) + emoji = obj.get('emoji') file_size = obj.get('file_size') - return cls(file_id, width, height, thumb, file_size) + return cls(file_id, width, height, thumb, emoji, file_size) - def __init__(self, file_id, width, height, thumb, file_size=None): + def __init__(self, file_id, width, height, thumb, emoji=None, file_size=None): self.file_id = file_id self.width = width self.height = height self.thumb = thumb + self.emoji = emoji self.file_size = file_size From 16838c30b6185d5d81cd6efdeb76384173083b25 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 8 May 2016 18:45:21 +0800 Subject: [PATCH 0002/1808] Update version. Change log: - Added the field emoji to the Sticker. - Added the field forward_from_chat to the Message object for messages forwarded from channels. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c258e5b64..54bedcf23 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.0.5', + version='2.1.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 0c420ee5e481a813d23e0eb4f5f1eeb53afb4d16 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 12 May 2016 11:09:21 +0800 Subject: [PATCH 0003/1808] Add missing title to InlineQueryResultLocation to_json. Fix #160 --- telebot/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 2ef11b5a4..fc3760535 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1162,7 +1162,8 @@ def __init__(self, id, title, latitude, longitude, reply_markup=None, self.thumb_height = thumb_height def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'latitude': self.latitude, 'longitude': self.longitude} + json_dict = {'type': self.type, 'id': self.id, 'latitude': self.latitude, 'longitude': self.longitude, + 'title': self.title} if self.thumb_url: json_dict['thumb_url'] = self.thumb_url if self.thumb_width: From a824ff967baaf8c9b4b3ce64fc92b91012df1142 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 12 May 2016 11:39:50 +0800 Subject: [PATCH 0004/1808] Version update. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 54bedcf23..ffaf4c956 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.0', + version='2.1.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From bc73f345b2906b938824904158576dd6fcc392ef Mon Sep 17 00:00:00 2001 From: "Mr.Gigabyte" Date: Sat, 14 May 2016 22:59:21 +0530 Subject: [PATCH 0005/1808] Update README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 14f085a35..31c75621d 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,20 @@ def query_text(inline_query): print(e) ``` +###Working with entities: +This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. +Attributes: +* `type` +* `url` +* `offset` +* `length` + + +**Here's an Example:**`message.entities[num].`
+Here `num` is the entity number or order of entity in a reply, for if incase there are multiple entities in the reply/message.
+`message.entities` returns a list of entities object.
+`message.entities[0].type` would give the type of the first entity
+Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra details ## Advanced use of the API From 1da5d1e0e2a77445d5a7a449a9d7b8738b7488ca Mon Sep 17 00:00:00 2001 From: agentik-007 Date: Mon, 16 May 2016 14:31:36 +0300 Subject: [PATCH 0006/1808] Update webhook_flask_echo_bot.py --- examples/webhook_examples/webhook_flask_echo_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py index 418e3ab72..26b189e83 100644 --- a/examples/webhook_examples/webhook_flask_echo_bot.py +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -48,7 +48,7 @@ def index(): @app.route(WEBHOOK_URL_PATH, methods=['POST']) def webhook(): if flask.request.headers.get('content-type') == 'application/json': - json_string = flask.request.get_data().decode('utf-8') + json_string = flask.request.get_data().encode('utf-8') update = telebot.types.Update.de_json(json_string) bot.process_new_messages([update.message]) return '' From 2545724a6fb31006103b98e38d2970b5d36232ed Mon Sep 17 00:00:00 2001 From: Pieter van den Ham Date: Thu, 19 May 2016 10:02:59 +0200 Subject: [PATCH 0007/1808] Fix #164 --- telebot/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index fc3760535..159321635 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3,6 +3,8 @@ import json import six +import util + class JsonSerializable: """ @@ -553,7 +555,7 @@ def add(self, *args): i = 1 row = [] for button in args: - if isinstance(button, str): + if util.is_string(button): row.append({'text': button}) else: row.append(button.to_dic()) @@ -574,7 +576,7 @@ def row(self, *args): """ btn_array = [] for button in args: - if isinstance(button, str): + if util.is_string(button): btn_array.append({'text': button}) else: btn_array.append(button.to_dic()) From 8017c8d91912b92a0707cbfed81bbfb34e38e9b6 Mon Sep 17 00:00:00 2001 From: Pieter van den Ham Date: Thu, 19 May 2016 10:18:00 +0200 Subject: [PATCH 0008/1808] Fix failing build for python 3.3, 3.4 and pypy3 --- telebot/types.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 159321635..eb547f2a1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3,8 +3,7 @@ import json import six -import util - +from telebot import util class JsonSerializable: """ From 3ebc47de8b85e223f84225a887f73ef9ba4f444c Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 20 May 2016 14:39:34 +0800 Subject: [PATCH 0009/1808] Add missing title in InlineQueryResultVenue. #165 --- telebot/types.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index eb547f2a1..baa60ea22 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5,6 +5,7 @@ from telebot import util + class JsonSerializable: """ Subclasses of this class are guaranteed to be able to be converted to JSON format. @@ -1195,8 +1196,8 @@ def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, self.thumb_height = thumb_height def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'latitude': self.latitude, 'longitude': self.longitude, - 'address': self.address} + json_dict = {'type': self.type, 'title': self.title, 'id': self.id, 'latitude': self.latitude, + 'longitude': self.longitude, 'address': self.address} if self.foursquare_id: json_dict['foursquare_id'] = self.foursquare_id if self.thumb_url: From fbaf88c237e7322b4cdd6a21db91a137749f2e3b Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 20 May 2016 14:41:00 +0800 Subject: [PATCH 0010/1808] Update version. --- setup.py | 2 +- telebot/types.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index ffaf4c956..628ff10ba 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.1', + version='2.1.2', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', diff --git a/telebot/types.py b/telebot/types.py index baa60ea22..adf37e191 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1196,7 +1196,7 @@ def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, self.thumb_height = thumb_height def to_json(self): - json_dict = {'type': self.type, 'title': self.title, 'id': self.id, 'latitude': self.latitude, + json_dict = {'type': self.type, 'id': self.id, 'title': self.title, 'latitude': self.latitude, 'longitude': self.longitude, 'address': self.address} if self.foursquare_id: json_dict['foursquare_id'] = self.foursquare_id From aee925556882b5013e3542cebd8b6e3668c61913 Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Fri, 20 May 2016 17:40:22 +0300 Subject: [PATCH 0011/1808] Changed "Process_new_message" to "Process_new_update" This way all types of queries (Message, Inline query, Callback query) are supported. --- examples/webhook_examples/webhook_cherrypy_echo_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/webhook_examples/webhook_cherrypy_echo_bot.py b/examples/webhook_examples/webhook_cherrypy_echo_bot.py index 05b43a160..83c9a01f2 100644 --- a/examples/webhook_examples/webhook_cherrypy_echo_bot.py +++ b/examples/webhook_examples/webhook_cherrypy_echo_bot.py @@ -46,7 +46,7 @@ def index(self): length = int(cherrypy.request.headers['content-length']) json_string = cherrypy.request.body.read(length).decode("utf-8") update = telebot.types.Update.de_json(json_string) - bot.process_new_messages([update.message]) + bot.process_new_updates([update]) return '' else: raise cherrypy.HTTPError(403) From 04df139efb27bcb322c07bbe5503674f9d763afe Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Sun, 22 May 2016 13:32:53 +0300 Subject: [PATCH 0012/1808] Added edit_date to Message object As of Bot API update 2.1, `Message` object now has optional `edit_date` field. --- telebot/types.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index adf37e191..82680b5f9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -178,6 +178,8 @@ def de_json(cls, json_string): opts['forward_date'] = obj['forward_date'] if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) + if 'edit_date' in obj: + opts['edit_date'] = Message.de_json(obj['edit_date']) if 'text' in obj: opts['text'] = obj['text'] content_type = 'text' @@ -269,6 +271,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.forward_from = None self.forward_date = None self.reply_to_message = None + self.edit_date = None self.text = None self.entities = None self.audio = None From 6a98d27f1add4c411d51171d869864ccb9323d1e Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Sun, 22 May 2016 13:35:21 +0300 Subject: [PATCH 0013/1808] Added edited_message to Update object As of Bot API update 2.1, Update object now has optional edited_message field. --- telebot/types.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index adf37e191..ccf964a3d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -91,6 +91,8 @@ def de_json(cls, json_type): callback_query = None if 'message' in obj: message = Message.de_json(obj['message']) + if 'edited_message' in obj: + edited_message = Message.de_json(obj['edited_message']) if 'inline_query' in obj: inline_query = InlineQuery.de_json(obj['inline_query']) if 'chosen_inline_result' in obj: @@ -99,9 +101,10 @@ def de_json(cls, json_type): callback_query = CallbackQuery.de_json(obj['callback_query']) return cls(update_id, message, inline_query, chosen_inline_result, callback_query) - def __init__(self, update_id, message, inline_query, chosen_inline_result, callback_query): + def __init__(self, update_id, message, edited_message, inline_query, chosen_inline_result, callback_query): self.update_id = update_id self.message = message + self.edited_message = edited_message self.inline_query = inline_query self.chosen_inline_result = chosen_inline_result self.callback_query = callback_query From 234dd8cf9f6fd5ceacee8b8b6fbad2f3507abf27 Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Sun, 22 May 2016 13:55:54 +0300 Subject: [PATCH 0014/1808] Edited edit_date field It's integer, not `Message` --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 82680b5f9..db409b113 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -179,7 +179,7 @@ def de_json(cls, json_string): if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) if 'edit_date' in obj: - opts['edit_date'] = Message.de_json(obj['edit_date']) + opts['edit_date'] = obj.get('edit_date') if 'text' in obj: opts['text'] = obj['text'] content_type = 'text' From 8a9d89591bb3d84912da0fd06081b10740a9a7f4 Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Sun, 22 May 2016 15:44:04 +0300 Subject: [PATCH 0015/1808] Fixed Travis build errors --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index ccf964a3d..195cd9a44 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -99,7 +99,7 @@ def de_json(cls, json_type): chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result']) if 'callback_query' in obj: callback_query = CallbackQuery.de_json(obj['callback_query']) - return cls(update_id, message, inline_query, chosen_inline_result, callback_query) + return cls(update_id, message, edited_message, inline_query, chosen_inline_result, callback_query) def __init__(self, update_id, message, edited_message, inline_query, chosen_inline_result, callback_query): self.update_id = update_id From 1a45b4844add7ecf774ccff7bbee603b0ef75766 Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Sun, 22 May 2016 15:47:44 +0300 Subject: [PATCH 0016/1808] Fixed Travis build (again) Yeah, I'm really sorry for all this errors, maybe tired a little bit. --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index 195cd9a44..a8f2d443b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -86,6 +86,7 @@ def de_json(cls, json_type): obj = cls.check_json(json_type) update_id = obj['update_id'] message = None + edited_message = None inline_query = None chosen_inline_result = None callback_query = None From 3413669a2359bc6d137b34e894c3453b37516ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Mu=C3=B1oz=20Callejo?= Date: Sun, 22 May 2016 18:35:20 +0200 Subject: [PATCH 0017/1808] Added optional connection (aka sending) timeouts to methods that may upload big chunks of data: send_audio, send_voice, send_document, send_sticker and send_video. --- telebot/__init__.py | 20 ++++++++++---------- telebot/apihelper.py | 20 +++++++++++++++----- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ca86bb36c..4e9ac687c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -334,7 +334,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep disable_notification)) def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, disable_notification=None): + reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. :param chat_id:Unique identifier for the message recipient @@ -348,10 +348,10 @@ def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, """ return types.Message.de_json( apihelper.send_audio(self.token, chat_id, audio, duration, performer, title, reply_to_message_id, - reply_markup, disable_notification)) + reply_markup, disable_notification, timeout)) def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): + disable_notification=None, timeout=None): """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. :param chat_id:Unique identifier for the message recipient. @@ -363,9 +363,9 @@ def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, re """ return types.Message.de_json( apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup, - disable_notification)) + disable_notification, timeout)) - def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None): + def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send general files. :param chat_id: @@ -376,9 +376,9 @@ def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=No """ return types.Message.de_json( apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup, - disable_notification)) + disable_notification, timeout)) - def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None): + def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send .webp stickers. :param chat_id: @@ -389,10 +389,10 @@ def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=Non """ return types.Message.de_json( apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup, - disable_notification)) + disable_notification, timeout)) def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): + disable_notification=None, timeout=None): """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -405,7 +405,7 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag """ return types.Message.de_json( apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, - disable_notification)) + disable_notification, timeout)) def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None, disable_notification=None): diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6f2766ea1..87b58caca 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -27,9 +27,11 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas request_url = base_url.format(token, method_name) logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) read_timeout = READ_TIMEOUT + connect_timeout = CONNECT_TIMEOUT if params: if 'timeout' in params: read_timeout = params['timeout'] + 10 - result = requests.request(method, request_url, params=params, files=files, timeout=(CONNECT_TIMEOUT, read_timeout)) + if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 + result = requests.request(method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout)) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] @@ -225,7 +227,7 @@ def send_chat_action(token, chat_id, action): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): + disable_notification=None, timeout=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -243,11 +245,13 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['reply_markup'] = _convert_markup(reply_markup) if disable_notification: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): + disable_notification=None, timeout=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -263,11 +267,13 @@ def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, r payload['reply_markup'] = _convert_markup(reply_markup) if disable_notification: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, disable_notification=None): + reply_markup=None, disable_notification=None, timeout=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -287,10 +293,12 @@ def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, payload['reply_markup'] = _convert_markup(reply_markup) if disable_notification: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, files=files, method='post') -def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None): +def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -304,6 +312,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['reply_markup'] = _convert_markup(reply_markup) if disable_notification: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, files=files, method='post') From 8c20f6302294ccf0601e9f0f7ea1477fdd7f4003 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 23 May 2016 11:12:20 +0800 Subject: [PATCH 0018/1808] Add user to MessageEntity. --- telebot/types.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index d837cbdc6..d0b9b4eff 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -311,13 +311,17 @@ def de_json(cls, json_string): offset = obj['offset'] length = obj['length'] url = obj.get('url') - return cls(type, offset, length, url) + user = None + if 'user' in obj: + user = User.de_json(obj['user']) + return cls(type, offset, length, url, user) - def __init__(self, type, offset, length, url=None): + def __init__(self, type, offset, length, url=None, user=None): self.type = type self.offset = offset self.length = length self.url = url + self.user = user class PhotoSize(JsonDeserializable): From d84aa796c03c657e47d5fc2cbac3d9cf0f5846ae Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 25 May 2016 01:42:25 +0600 Subject: [PATCH 0019/1808] Add files via upload --- .../webhook_flask_heroku_echo.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 examples/webhook_examples/webhook_flask_heroku_echo.py diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py new file mode 100644 index 000000000..dcd18e8a2 --- /dev/null +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -0,0 +1,32 @@ +import telebot +import os +from flask import Flask, request + +bot = telebot.TeleBot('token') + +server = Flask(__name__) + +@bot.message_handler(commands=['start']) +def start(message): + bot.reply_to(message, 'Hello, ' + message.from_user.first_name) + +@bot.message_handler(func=lambda message: True, content_types=['text']) +def echo_message(message): + bot.reply_to(message, message.text) + +@server.route("/bot", methods=['POST']) +def getMessage(): + bot.process_new_messages( + [telebot.types.Update.de_json(request.stream.read().decode("utf-8")).message + ]) + return "!", 200 + +@server.route("/") +def webhook(): + bot.remove_webhook() + bot.set_webhook(url="https://herokuProject_url/bot") + return "!", 200 + +server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000)) + +server = Flask(__name__) \ No newline at end of file From e87907f0b85a8845673d2ec8a5587281c2e2e6f8 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 25 May 2016 01:44:09 +0600 Subject: [PATCH 0020/1808] Update webhook_flask_heroku_echo.py --- examples/webhook_examples/webhook_flask_heroku_echo.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index dcd18e8a2..e16fa1d0e 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -17,8 +17,7 @@ def echo_message(message): @server.route("/bot", methods=['POST']) def getMessage(): bot.process_new_messages( - [telebot.types.Update.de_json(request.stream.read().decode("utf-8")).message - ]) + [telebot.types.Update.de_json(request.stream.read().decode("utf-8")).message]) return "!", 200 @server.route("/") @@ -29,4 +28,4 @@ def webhook(): server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000)) -server = Flask(__name__) \ No newline at end of file +server = Flask(__name__) From 59b19fbbc1959e01fe398dbf11181d00962ddf3f Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 25 May 2016 19:15:13 +0600 Subject: [PATCH 0021/1808] Update webhook_flask_heroku_echo.py --- examples/webhook_examples/webhook_flask_heroku_echo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index e16fa1d0e..4db065e35 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -2,7 +2,7 @@ import os from flask import Flask, request -bot = telebot.TeleBot('token') +bot = telebot.TeleBot('') server = Flask(__name__) From 05f1c87c7d90f9d024898ac39f72c98680e6b70d Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 25 May 2016 19:25:46 +0600 Subject: [PATCH 0022/1808] Update webhook_flask_heroku_echo.py --- examples/webhook_examples/webhook_flask_heroku_echo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index 4db065e35..4d8d58476 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -27,5 +27,4 @@ def webhook(): return "!", 200 server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000)) - server = Flask(__name__) From 851d76888bd93893dbfafbc5fa11ca144170c2d6 Mon Sep 17 00:00:00 2001 From: Kondra007 Date: Thu, 26 May 2016 00:44:39 +0300 Subject: [PATCH 0023/1808] Update Readme.md Added `user` to attributes list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 31c75621d..8af51a665 100644 --- a/README.md +++ b/README.md @@ -393,6 +393,7 @@ Attributes: * `url` * `offset` * `length` +* `user` **Here's an Example:**`message.entities[num].`
From e956800e343e4f71bb06f116b3e63f57289594b3 Mon Sep 17 00:00:00 2001 From: Mohammed Irfan Date: Sun, 29 May 2016 19:25:36 +0530 Subject: [PATCH 0024/1808] Fix inline demo code in readme.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8af51a665..245e412ea 100644 --- a/README.md +++ b/README.md @@ -379,8 +379,8 @@ def test_chosen(chosen_inline_result): @bot.inline_handler(lambda query: query.query == 'text') def query_text(inline_query): try: - r = types.InlineQueryResultArticle('1', 'Result', 'Result message.') - r2 = types.InlineQueryResultArticle('2', 'Result2', 'Result message2.') + r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.')) + r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.')) bot.answer_inline_query(inline_query.id, [r, r2]) except Exception as e: print(e) From ce24aa25f2bd559523f6b3621e2cbde3498615f8 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 2 Jun 2016 13:15:22 +0800 Subject: [PATCH 0025/1808] Fix #179. --- telebot/__init__.py | 4 ++-- telebot/apihelper.py | 5 ++++- tests/test_telebot.py | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4e9ac687c..8014a0746 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -365,7 +365,7 @@ def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, re apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup, disable_notification, timeout)) - def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): + def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send general files. :param chat_id: @@ -376,7 +376,7 @@ def send_document(self, chat_id, data, reply_to_message_id=None, reply_markup=No """ return types.Message.de_json( apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup, - disable_notification, timeout)) + disable_notification, timeout, caption=caption)) def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 87b58caca..6a68f33f2 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -298,7 +298,8 @@ def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, return _make_request(token, method_url, params=payload, files=files, method='post') -def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): +def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None, + timeout=None, caption=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -314,6 +315,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if caption: + payload['caption'] = caption return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 5aeaa3b1f..b0119892b 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -129,6 +129,15 @@ def test_send_file_dis_noti(self): ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id) assert ret_msg.message_id + def test_send_file_caption(self): + file_data = open('../examples/detailed_example/kitten.jpg', 'rb') + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_document(CHAT_ID, file_data, caption="Test") + assert ret_msg.message_id + + ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id) + assert ret_msg.message_id + def test_send_video(self): file_data = open('./test_data/test_video.mp4', 'rb') tb = telebot.TeleBot(TOKEN) From 6b255deed73916f96eefebab339f7bf2b5446e56 Mon Sep 17 00:00:00 2001 From: Gabriel R F Date: Thu, 2 Jun 2016 12:04:04 -0300 Subject: [PATCH 0026/1808] Added Send2KindleBot Also added description of urlprobot and lmgtfy_bot --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 245e412ea..878bb1930 100644 --- a/README.md +++ b/README.md @@ -518,8 +518,9 @@ Get help. Discuss. Chat. ## Bots using this API * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* -* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* -* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) by *GabrielRF* +* [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files. +* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you. +* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander. * [Telegram Proxy Bot](https://bitbucket.org/master_groosha/telegram-proxy-bot) by *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as some kind of "proxy". * [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`. * [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot. From 2cf8ffcbc0053bc865c0a84115448c1b71d39bda Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 3 Jun 2016 14:45:25 +0800 Subject: [PATCH 0027/1808] Update group link. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 878bb1930..43ba18f45 100644 --- a/README.md +++ b/README.md @@ -505,8 +505,7 @@ if message.chat.type == “channel”: Get help. Discuss. Chat. -* Join the pyTelegramBotAPI Telegram Chat Group - * Messge to @eternnoir by telegram for Invitation. +* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/pbtai) * We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi). ## More examples From ac740e4755e00904a708ff8530271ddb13202cad Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 3 Jun 2016 17:21:33 +0800 Subject: [PATCH 0028/1808] Change public link to invite link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43ba18f45..f94ab6427 100644 --- a/README.md +++ b/README.md @@ -505,7 +505,7 @@ if message.chat.type == “channel”: Get help. Discuss. Chat. -* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/pbtai) +* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A) * We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi). ## More examples From 2f20d70e891286a28868278c61bc2c0cc33e3c86 Mon Sep 17 00:00:00 2001 From: Pavel K Date: Sat, 4 Jun 2016 21:18:09 +0500 Subject: [PATCH 0029/1808] unicode strings for check_json --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index adf37e191..3dba678b7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -62,9 +62,14 @@ def check_json(json_type): :param json_type: :return: """ + try: + str_types = (str, unicode) + except NameError: + str_types = (str,) + if type(json_type) == dict: return json_type - elif type(json_type) == str: + elif type(json_type) in str_types: return json.loads(json_type) else: raise ValueError("json_type should be a json dict or string.") From 9e397c41cf8c62825442bb68d691b8340799d364 Mon Sep 17 00:00:00 2001 From: Alejandro Castilla Date: Sun, 5 Jun 2016 19:20:17 +0200 Subject: [PATCH 0030/1808] ComedoresUGRbot added to bots list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f94ab6427..6d1364e23 100644 --- a/README.md +++ b/README.md @@ -526,5 +526,6 @@ Get help. Discuss. Chat. * [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* * [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger* * [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi* +* [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From d2e7f4d8f2372810546d28cebf4e943763621ecb Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:00:44 +0800 Subject: [PATCH 0031/1808] Add 2.1 new method. --- telebot/__init__.py | 20 ++++++++++++++++++++ telebot/apihelper.py | 30 ++++++++++++++++++++++++++++++ telebot/types.py | 13 +++++++++++++ 3 files changed, 63 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8014a0746..aa837f9af 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -285,6 +285,26 @@ def get_user_profile_photos(self, user_id, offset=None, limit=None): result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit) return types.UserProfilePhotos.de_json(result) + def get_chat(self, chat_id): + result = apihelper.get_chat(self.token, chat_id) + return types.Chat.de_json(result) + + def leave_chat(self, chat_id): + result = apihelper.leave_chat(self.token, chat_id) + return result + + def get_chat_administrators(self, chat_id): + result = apihelper.get_chat_administrators(self.token, chat_id) + return [[types.ChatMember.de_json(y) for y in x] for x in result] + + def get_chat_members_count(self, chat_id): + result = apihelper.get_chat_members_count(self.token, chat_id) + return result + + def get_chat_member(self, chat_id, user_id): + result = apihelper.get_chat_member(self.token, chat_id,user_id) + return types.ChatMember.de_json(result) + def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6a68f33f2..693387d00 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -149,6 +149,36 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None): return _make_request(token, method_url, params=payload) +def get_chat(token, chat_id): + method_url = r'getChat' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + + +def leave_chat(token, chat_id): + method_url = r'leaveChat' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + + +def get_chat_administrators(token, chat_id): + method_url = r'getChatAdministrators' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + + +def get_chat_members_count(token, chat_id): + method_url = r'getChatMembersCount' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + + +def get_chat_member(token, chat_id, user_id): + method_url = r'getChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id} + return _make_request(token, method_url, params=payload) + + def forward_message(token, chat_id, from_chat_id, message_id, disable_notification=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} diff --git a/telebot/types.py b/telebot/types.py index 3dba678b7..c6f9b6ea4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -721,6 +721,19 @@ def __init__(self, id, from_user, data, message=None, inline_message_id=None): self.inline_message_id = inline_message_id +class ChatMember(JsonDeserializable): + @classmethod + def de_json(cls, json_type): + obj = cls.check_json(json_type) + user = User.de_json(obj['user']) + status = obj['status'] + return cls(id, user, status) + + def __init__(self, user, status): + self.user = user + self.status = status + + # InlineQuery class InlineQuery(JsonDeserializable): From 468a535257efdf32c69bd7bea4136ac94d501fbc Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:08:52 +0800 Subject: [PATCH 0032/1808] Fix de_json. --- telebot/__init__.py | 5 ++++- telebot/types.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index aa837f9af..05ae34edf 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -295,7 +295,10 @@ def leave_chat(self, chat_id): def get_chat_administrators(self, chat_id): result = apihelper.get_chat_administrators(self.token, chat_id) - return [[types.ChatMember.de_json(y) for y in x] for x in result] + ret = [] + for r in result: + ret.append(types.ChatMember.de_json(r)) + return ret def get_chat_members_count(self, chat_id): result = apihelper.get_chat_members_count(self.token, chat_id) diff --git a/telebot/types.py b/telebot/types.py index c6f9b6ea4..790646744 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -727,7 +727,7 @@ def de_json(cls, json_type): obj = cls.check_json(json_type) user = User.de_json(obj['user']) status = obj['status'] - return cls(id, user, status) + return cls(user, status) def __init__(self, user, status): self.user = user From 0b9f91c6fb53fe0e14cf5f6533b2ab723ec4b9f2 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:16:17 +0800 Subject: [PATCH 0033/1808] Add new method test case. --- tests/test_telebot.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index b0119892b..e294950b5 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -16,6 +16,7 @@ if not should_skip: TOKEN = os.environ['TOKEN'] CHAT_ID = os.environ['CHAT_ID'] + GROUP_ID = os.environ['GROUP_ID'] @pytest.mark.skipif(should_skip, reason="No environment variables configured") @@ -351,6 +352,21 @@ def test_edit_message_text(self): new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.text == 'Edit test' + def test_get_chat(self): + tb = telebot.TeleBot(TOKEN) + ch = tb.get_chat(GROUP_ID) + assert ch.id == GROUP_ID + + def test_get_chat_administrators(self): + tb = telebot.TeleBot(TOKEN) + cas = tb.get_chat_administrators(GROUP_ID) + assert len(cas) > 1 + + def test_get_chat_members_count(self): + tb = telebot.TeleBot(TOKEN) + cn = tb.get_chat_members_count(GROUP_ID) + assert cn > 1 + def test_edit_markup(self): text = 'CI Test Message' tb = telebot.TeleBot(TOKEN) From 8e3c9d8d24dac8d0c8032d489419c455037ced2b Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:29:12 +0800 Subject: [PATCH 0034/1808] Add edited message handler. --- telebot/__init__.py | 47 ++++++++++++++++++++++++++++++++++++++++----- telebot/types.py | 8 ++++++-- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 05ae34edf..72a2579ba 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -66,6 +66,7 @@ def __init__(self, token, threaded=True, skip_pending=False): self.pre_message_subscribers_next_step = {} self.message_handlers = [] + self.edited_message_handlers = [] self.inline_handlers = [] self.chosen_inline_handlers = [] self.callback_query_handlers = [] @@ -123,6 +124,7 @@ def __retrieve_updates(self, timeout=20): def process_new_updates(self, updates): new_messages = [] + edited_new_messages = [] new_inline_querys = [] new_chosen_inline_results = [] new_callback_querys = [] @@ -131,6 +133,8 @@ def process_new_updates(self, updates): self.last_update_id = update.update_id if update.message: new_messages.append(update.message) + if update.edited_message: + edited_new_messages.append(update.edited_message) if update.inline_query: new_inline_querys.append(update.inline_query) if update.chosen_inline_result: @@ -140,6 +144,8 @@ def process_new_updates(self, updates): logger.debug('Received {0} new updates'.format(len(updates))) if len(new_messages) > 0: self.process_new_messages(new_messages) + if len(edited_new_messages) > 0: + self.process_new_edited_messages(edited_new_messages) if len(new_inline_querys) > 0: self.process_new_inline_query(new_inline_querys) if len(new_chosen_inline_results) > 0: @@ -154,6 +160,9 @@ def process_new_messages(self, new_messages): self._notify_message_subscribers(new_messages) self._notify_message_next_handler(new_messages) + def process_new_edited_messages(self, edited_message): + self._notify_command_handlers(self.edited_message_handlers, edited_message) + def process_new_inline_query(self, new_inline_querys): self._notify_command_handlers(self.inline_handlers, new_inline_querys) @@ -305,7 +314,7 @@ def get_chat_members_count(self, chat_id): return result def get_chat_member(self, chat_id, user_id): - result = apihelper.get_chat_member(self.token, chat_id,user_id) + result = apihelper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, @@ -388,7 +397,8 @@ def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, re apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup, disable_notification, timeout)) - def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None, disable_notification=None, timeout=None): + def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None, + disable_notification=None, timeout=None): """ Use this method to send general files. :param chat_id: @@ -401,7 +411,8 @@ def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, r apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup, disable_notification, timeout, caption=caption)) - def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): + def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, + timeout=None): """ Use this method to send .webp stickers. :param chat_id: @@ -502,8 +513,8 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None): def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, disable_web_page_preview=None, reply_markup=None): result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - disable_web_page_preview, reply_markup) - if type(result) == bool: # if edit inline message return is bool not Message. + 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) @@ -670,6 +681,32 @@ def add_message_handler(self, handler, commands=None, regexp=None, func=None, co self.message_handlers.append(handler_dict) + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=['text']): + def decorator(handler): + self.add_edited_message_handler(handler, commands, regexp, func, content_types) + return handler + + return decorator + + def add_edited_message_handler(self, handler, commands=None, regexp=None, func=None, content_types=None): + if content_types is None: + content_types = ['text'] + + filters = {'content_types': content_types} + if regexp: + filters['regexp'] = regexp + if func: + filters['lambda'] = func + if commands: + filters['commands'] = commands + + handler_dict = { + 'function': handler, + 'filters': filters + } + + self.edited_message_handlers.append(handler_dict) + def inline_handler(self, func): def decorator(handler): self.add_inline_handler(handler, func) diff --git a/telebot/types.py b/telebot/types.py index 790646744..76bec18a5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -91,21 +91,25 @@ def de_json(cls, json_type): obj = cls.check_json(json_type) update_id = obj['update_id'] message = None + edited_message = None inline_query = None chosen_inline_result = None callback_query = None if 'message' in obj: message = Message.de_json(obj['message']) + if 'edited_message' in obj: + edited_message = Message.de_json(obj['edited_message']) if 'inline_query' in obj: inline_query = InlineQuery.de_json(obj['inline_query']) if 'chosen_inline_result' in obj: chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result']) if 'callback_query' in obj: callback_query = CallbackQuery.de_json(obj['callback_query']) - return cls(update_id, message, inline_query, chosen_inline_result, callback_query) + return cls(update_id, message, edited_message, inline_query, chosen_inline_result, callback_query) - def __init__(self, update_id, message, inline_query, chosen_inline_result, callback_query): + def __init__(self, update_id, message, edited_message, inline_query, chosen_inline_result, callback_query): self.update_id = update_id + self.edited_message = edited_message self.message = message self.inline_query = inline_query self.chosen_inline_result = chosen_inline_result From 9bf4be2caf0b744ea903461c6ae9ef2598804013 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:32:12 +0800 Subject: [PATCH 0035/1808] fix test case. --- tests/test_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index e294950b5..4ca3c4613 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -360,7 +360,7 @@ def test_get_chat(self): def test_get_chat_administrators(self): tb = telebot.TeleBot(TOKEN) cas = tb.get_chat_administrators(GROUP_ID) - assert len(cas) > 1 + assert len(cas) > 0 def test_get_chat_members_count(self): tb = telebot.TeleBot(TOKEN) From fab2f324d055e4501e6ae662be51c8447b3bb087 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:34:46 +0800 Subject: [PATCH 0036/1808] Fix test case. --- tests/test_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 4ca3c4613..6480df099 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -355,7 +355,7 @@ def test_edit_message_text(self): def test_get_chat(self): tb = telebot.TeleBot(TOKEN) ch = tb.get_chat(GROUP_ID) - assert ch.id == GROUP_ID + assert str(ch.id) == GROUP_ID def test_get_chat_administrators(self): tb = telebot.TeleBot(TOKEN) From 5bd8e9d3f5665851a0d1f4bb13dc1b116f79e550 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:40:35 +0800 Subject: [PATCH 0037/1808] Update readme. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 6d1364e23..88be0dc26 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,10 @@ def send_something(message): ``` **Important: all handlers are tested in the order in which they were declared** +#### Edited Message handlers + +Same as Message handlers + #### Callback Query Handler In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback_querys. From cdb6d6760d5bdeec792a9a5af85581ac418eeb2e Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:44:30 +0800 Subject: [PATCH 0038/1808] Add some comment. --- telebot/__init__.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 72a2579ba..226589767 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -295,14 +295,31 @@ def get_user_profile_photos(self, user_id, offset=None, limit=None): return types.UserProfilePhotos.de_json(result) def get_chat(self, chat_id): + """ + 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. + :param chat_id: + :return: + """ result = apihelper.get_chat(self.token, chat_id) return types.Chat.de_json(result) def leave_chat(self, chat_id): + """ + Use this method for your bot to leave a group, supergroup or channel. Returns True on success. + :param chat_id: + :return: + """ result = apihelper.leave_chat(self.token, chat_id) return result def get_chat_administrators(self, chat_id): + """ + Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects + that contains information about all chat administrators except other bots. + :param chat_id: + :return: + """ result = apihelper.get_chat_administrators(self.token, chat_id) ret = [] for r in result: @@ -310,10 +327,21 @@ def get_chat_administrators(self, chat_id): return ret def get_chat_members_count(self, chat_id): + """ + Use this method to get the number of members in a chat. Returns Int on success. + :param chat_id: + :return: + """ result = apihelper.get_chat_members_count(self.token, chat_id) return result def get_chat_member(self, chat_id, user_id): + """ + Use this method to get information about a member of a chat. Returns a ChatMember object on success. + :param chat_id: + :param user_id: + :return: + """ result = apihelper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) From 25ad3f055f35918ad1ee39637839d02708aa5791 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 19:57:40 +0800 Subject: [PATCH 0039/1808] Update version. Change log: - Added new methods: getChat, leaveChat, getChatAdministrators, getChatMember, getChatMembersCount. - New fields: edited_message in Update, edit_date in Message, user in MessageEntity. New value text_mention for the type field in MessageEntity. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 628ff10ba..2d6dde330 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.2', + version='2.1.3', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 57486b18cd79e2151f1bd9391934343e02366a72 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 7 Jun 2016 20:04:19 +0800 Subject: [PATCH 0040/1808] Remove download count from readme. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 88be0dc26..1d2324431 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Download Month](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) -[![Download Month](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) * [Getting started.](#getting-started) * [Writing your first bot](#writing-your-first-bot) From eb4d58bec179a455916eb212be3076e577e3defe Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 13 Jun 2016 14:15:15 +0300 Subject: [PATCH 0041/1808] little optimization on handler dictionary building: code duplication lessened --- telebot/__init__.py | 58 +++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 226589767..4e3102f7e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -654,6 +654,12 @@ def _append_pre_next_step_handler(self): self.message_subscribers_next_step[k] = self.pre_message_subscribers_next_step[k] self.pre_message_subscribers_next_step = {} + def _build_handler_dict(self, handler, **filters): + return { + 'function': handler, + 'filters': filters + } + def message_handler(self, commands=None, regexp=None, func=None, content_types=['text']): """ Message handler decorator. @@ -685,54 +691,34 @@ def default_command(message): """ def decorator(handler): - self.add_message_handler(handler, commands, regexp, func, content_types) - return handler + handler_dict = self._build_handler_dict(handler, + commands=commands, + regexp=regexp, + func=func, + content_types=content_types) - return decorator - - def add_message_handler(self, handler, commands=None, regexp=None, func=None, content_types=None): - if content_types is None: - content_types = ['text'] + self.add_message_handler(handler_dict) - filters = {'content_types': content_types} - if regexp: - filters['regexp'] = regexp - if func: - filters['lambda'] = func - if commands: - filters['commands'] = commands + return handler - handler_dict = { - 'function': handler, - 'filters': filters - } + return decorator + def add_message_handler(self, handler_dict): self.message_handlers.append(handler_dict) def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=['text']): def decorator(handler): - self.add_edited_message_handler(handler, commands, regexp, func, content_types) + handler_dict = self._build_handler_dict(handler, + commands=commands, + regexp=regexp, + func=func, + content_types=content_types) + self.add_edited_message_handler(handler_dict) return handler return decorator - def add_edited_message_handler(self, handler, commands=None, regexp=None, func=None, content_types=None): - if content_types is None: - content_types = ['text'] - - filters = {'content_types': content_types} - if regexp: - filters['regexp'] = regexp - if func: - filters['lambda'] = func - if commands: - filters['commands'] = commands - - handler_dict = { - 'function': handler, - 'filters': filters - } - + def add_edited_message_handler(self, handler_dict): self.edited_message_handlers.append(handler_dict) def inline_handler(self, func): From 1b47e5cc629ebfa50646a01178833aa073428887 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 13 Jun 2016 14:24:27 +0300 Subject: [PATCH 0042/1808] code duplication lessened --- telebot/__init__.py | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4e3102f7e..18e6265f1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -723,52 +723,34 @@ def add_edited_message_handler(self, handler_dict): def inline_handler(self, func): def decorator(handler): - self.add_inline_handler(handler, func) + handler_dict = self._build_handler_dict(handler, func=func) + self.add_inline_handler(handler_dict) return handler return decorator - def add_inline_handler(self, handler, func): - filters = {'lambda': func} - - handler_dict = { - 'function': handler, - 'filters': filters - } - + def add_inline_handler(self, handler_dict): self.inline_handlers.append(handler_dict) def chosen_inline_handler(self, func): def decorator(handler): - self.add_chosen_inline_handler(handler, func) + handler_dict = self._build_handler_dict(handler, func=func) + self.add_chosen_inline_handler(handler_dict) return handler return decorator - def add_chosen_inline_handler(self, handler, func): - filters = {'lambda': func} - - handler_dict = { - 'function': handler, - 'filters': filters - } - + def add_chosen_inline_handler(self, handler_dict): self.chosen_inline_handlers.append(handler_dict) def callback_query_handler(self, func): def decorator(handler): - self.add_callback_query_handler(handler, func) + handler_dict = self._build_handler_dict(handler, func=func) + self.add_callback_query_handler(handler_dict) return decorator - def add_callback_query_handler(self, handler, func): - filters = {'lambda': func} - - handler_dict = { - 'function': handler, - 'filters': filters - } - + def add_callback_query_handler(self, handler_dict): self.callback_query_handlers.append(handler_dict) @staticmethod @@ -786,7 +768,7 @@ def _test_filter(filter, filter_value, message): return message.content_type == 'text' and re.search(filter_value, message.text) if filter == 'commands': return message.content_type == 'text' and util.extract_command(message.text) in filter_value - if filter == 'lambda': + if filter == 'func': return filter_value(message) return False From 527351385be7dfd0c5d471d83381308299e1beee Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 13 Jun 2016 16:47:15 +0300 Subject: [PATCH 0043/1808] + Don't check filters against NoneType values; + More flexibility for subclassing: - __exec_task is protected now(was private) - _test_message_handler and _test_filter are class members now(used to be static methods) + More flexibility on extention of message_handler(**kwargs for additional parameters) --- telebot/__init__.py | 47 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 18e6265f1..4b86c064c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -174,7 +174,7 @@ def process_new_callback_query(self, new_callback_querys): def __notify_update(self, new_messages): for listener in self.update_listener: - self.__exec_task(listener, new_messages) + self._exec_task(listener, new_messages) def polling(self, none_stop=False, interval=0, timeout=20): """ @@ -260,7 +260,7 @@ def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3): logger.info('Stopped polling.') - def __exec_task(self, task, *args, **kwargs): + def _exec_task(self, task, *args, **kwargs): if self.threaded: self.worker_pool.put(task, *args, **kwargs) else: @@ -643,7 +643,7 @@ def _notify_message_next_handler(self, new_messages): if chat_id in self.message_subscribers_next_step: handlers = self.message_subscribers_next_step[chat_id] for handler in handlers: - self.__exec_task(handler, message) + self._exec_task(handler, message) self.message_subscribers_next_step.pop(chat_id, None) def _append_pre_next_step_handler(self): @@ -660,7 +660,7 @@ def _build_handler_dict(self, handler, **filters): 'filters': filters } - def message_handler(self, commands=None, regexp=None, func=None, content_types=['text']): + def message_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -695,7 +695,8 @@ def decorator(handler): commands=commands, regexp=regexp, func=func, - content_types=content_types) + content_types=content_types, + **kwargs) self.add_message_handler(handler_dict) @@ -706,13 +707,14 @@ def decorator(handler): def add_message_handler(self, handler_dict): self.message_handlers.append(handler_dict) - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=['text']): + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): def decorator(handler): handler_dict = self._build_handler_dict(handler, commands=commands, regexp=regexp, func=func, - content_types=content_types) + content_types=content_types, + **kwargs) self.add_edited_message_handler(handler_dict) return handler @@ -753,30 +755,31 @@ def decorator(handler): def add_callback_query_handler(self, handler_dict): self.callback_query_handlers.append(handler_dict) - @staticmethod - def _test_message_handler(message_handler, message): + def _test_message_handler(self, message_handler, message): for filter, filter_value in six.iteritems(message_handler['filters']): - if not TeleBot._test_filter(filter, filter_value, message): + if filter_value is None: + continue + + if not self._test_filter(filter, filter_value, message): return False + return True - @staticmethod - def _test_filter(filter, filter_value, message): - if filter == 'content_types': - return message.content_type in filter_value - if filter == 'regexp': - return message.content_type == 'text' and re.search(filter_value, message.text) - if filter == 'commands': - return message.content_type == 'text' and util.extract_command(message.text) in filter_value - if filter == 'func': - return filter_value(message) - return False + def _test_filter(self, filter, filter_value, message): + test_cases = { + 'content_types': lambda msg: msg.content_type in filter_value, + 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text), + 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, + 'func': lambda msg: filter_value(msg) + } + + return test_cases.get(filter, lambda msg: False)(message) def _notify_command_handlers(self, handlers, new_messages): for message in new_messages: for message_handler in handlers: if self._test_message_handler(message_handler, message): - self.__exec_task(message_handler['function'], message) + self._exec_task(message_handler['function'], message) break From 70fa5b405a6b0050b71d53d4c779c61d7e268739 Mon Sep 17 00:00:00 2001 From: Nacho Soler Date: Wed, 15 Jun 2016 13:30:57 +0200 Subject: [PATCH 0044/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d2324431..339a94666 100644 --- a/README.md +++ b/README.md @@ -451,7 +451,7 @@ The TeleBot constructor takes the following optional arguments: As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example: ```python def handle_messages(messages): - for message in messsages: + for message in messages: # Do something with the message bot.reply_to(message, 'Hi') From b9a0c3e5114b986d21647310dbf5eb69894d7fe9 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 16 Jun 2016 11:49:51 +0300 Subject: [PATCH 0045/1808] + More flexibility for different handlers(callback_query_handler and etc.): now they are not limited in input arguments so can be easily extended by subclassing arguments --- telebot/__init__.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4b86c064c..25e2e20e9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -535,9 +535,6 @@ def kick_chat_member(self, chat_id, user_id): def unban_chat_member(self, chat_id, user_id): return apihelper.unban_chat_member(self.token, chat_id, user_id) - def answer_callback_query(self, callback_query_id, text=None, show_alert=None): - return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert) - def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, disable_web_page_preview=None, reply_markup=None): result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, @@ -723,9 +720,9 @@ def decorator(handler): def add_edited_message_handler(self, handler_dict): self.edited_message_handlers.append(handler_dict) - def inline_handler(self, func): + def inline_handler(self, func, **kwargs): def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func) + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_inline_handler(handler_dict) return handler @@ -734,9 +731,9 @@ def decorator(handler): def add_inline_handler(self, handler_dict): self.inline_handlers.append(handler_dict) - def chosen_inline_handler(self, func): + def chosen_inline_handler(self, func, **kwargs): def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func) + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_chosen_inline_handler(handler_dict) return handler @@ -745,9 +742,9 @@ def decorator(handler): def add_chosen_inline_handler(self, handler_dict): self.chosen_inline_handlers.append(handler_dict) - def callback_query_handler(self, func): + def callback_query_handler(self, func, **kwargs): def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func) + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_callback_query_handler(handler_dict) return decorator From 29ef0e74aff981629f27a336bb294773588ea6a3 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 17 Jun 2016 17:50:06 +0800 Subject: [PATCH 0046/1808] Fix typo. --- telebot/types.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 222e2c05e..abc5cfa1d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1132,7 +1132,7 @@ def __init__(self, id, voice_url, title, performer=None, voice_duration=None, re 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.titlee} + json_dict = {'type': self.type, 'id': self.id, 'voice_url': self.voice_url, 'title': self.title} if self.performer: json_dict['performer'] = self.performer if self.voice_duration: @@ -1161,7 +1161,8 @@ def __init__(self, id, title, document_url, mime_type, caption=None, description self.thumb_height = thumb_height def to_json(self): - json_dict = {'type': self.type, 'id': self.id, 'document_url': self.document_url, 'mime_type': self.titlee} + 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.description: From 2044fba29a1968f48d0f22c4911b62c50e8349c6 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 17 Jun 2016 17:55:53 +0800 Subject: [PATCH 0047/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2d6dde330..aaaa91829 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.3', + version='2.1.4', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 4205e46608d6144a56b4d215df182eb2e59e77b1 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 6 Jul 2016 10:13:42 +0800 Subject: [PATCH 0048/1808] Fix missing location object in InlineQuery. --- telebot/types.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index abc5cfa1d..f8f4434cd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -754,23 +754,28 @@ def de_json(cls, json_type): obj = cls.check_json(json_type) id = obj['id'] from_user = User.de_json(obj['from']) + location = None + if 'location' in obj: + location = Location.de_json(obj['location']) query = obj['query'] offset = obj['offset'] - return cls(id, from_user, query, offset) + return cls(id, from_user, location, query, offset) - def __init__(self, id, from_user, query, offset): + def __init__(self, id, from_user, location, query, offset): """ This object represents an incoming inline query. When the user sends an empty query, your bot could return some default or trending results. :param id: string Unique identifier for this query :param from_user: User Sender + :param location: Sender location, only for bots that request user location :param query: String Text of the query :param offset: String Offset of the results to be returned, can be controlled by the bot :return: InlineQuery Object """ self.id = id self.from_user = from_user + self.location = location self.query = query self.offset = offset From 3dcd59c22ef88c3757cf36b1b17e24e27ff7ec89 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Mon, 11 Jul 2016 21:29:25 +0800 Subject: [PATCH 0049/1808] Fix example --- examples/webhook_examples/webhook_flask_heroku_echo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index 4d8d58476..27cf95515 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -16,8 +16,7 @@ def echo_message(message): @server.route("/bot", methods=['POST']) def getMessage(): - bot.process_new_messages( - [telebot.types.Update.de_json(request.stream.read().decode("utf-8")).message]) + bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))]) return "!", 200 @server.route("/") From 303406020b2e2f8cb2fd93484b9b1858085937da Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 6 Jul 2016 10:27:37 +0800 Subject: [PATCH 0050/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aaaa91829..322898475 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.4', + version='2.1.5', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From b79c3165a1952c5e0fc76d574cd0c63dde40737c Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 11 Jul 2016 21:45:20 +0800 Subject: [PATCH 0051/1808] Add issue templaate. --- .github/ISSUE_TEMPLATE | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 000000000..ea8bee5e4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,7 @@ +Please answer these questions before submitting your issue. Thanks! + +1. What version of pyTelegramBotAPI are you using? + +2. What OS are you using? + +3. What version of python are you using? From 653c892b33cdb3ed85d6fdcaa08c7047d651d03c Mon Sep 17 00:00:00 2001 From: Roman Shchekin Date: Sun, 17 Jul 2016 15:54:26 +0300 Subject: [PATCH 0052/1808] Example of inline bot fixed --- examples/inline_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/inline_example.py b/examples/inline_example.py index c4192e1cc..c97dea2e2 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -53,7 +53,7 @@ def query_video(inline_query): @bot.inline_handler(lambda query: len(query.query) is 0) def default_query(inline_query): try: - r = types.InlineQueryResultArticle('1', 'default', 'default') + r = types.InlineQueryResultArticle('1', 'default', types.InputTextMessageContent('default')) bot.answer_inline_query(inline_query.id, [r]) except Exception as e: print(e) From 8a4d2000d2575871973c5219642723af37e8f1db Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sun, 17 Jul 2016 23:36:43 +0800 Subject: [PATCH 0053/1808] Update message's content_type doc. #199 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 339a94666..f12e36791 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)). -The Message object also has a `content_types`attribute, which defines the type of the Message. `content_types` can be one of the following strings: +The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings: 'text', 'audio', 'document', 'photo', 'sticker', 'video', 'voice', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'. ### Methods From c61b82ace6c9415e2da95f61e590ab0941c33604 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sun, 17 Jul 2016 23:38:37 +0800 Subject: [PATCH 0054/1808] Update create_threads to threads. #198 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f12e36791..f03768c55 100644 --- a/README.md +++ b/README.md @@ -443,9 +443,9 @@ for text in splitted_text: ### Controlling the amount of Threads used by TeleBot The TeleBot constructor takes the following optional arguments: - - create_threads: True/False (default True). A flag to indicate whether + - threaded: True/False (default True). A flag to indicate whether TeleBot should execute message handlers on it's polling Thread. - - num_threads: integer (default 4). Controls the amount of WorkerThreads created for the internal thread pool that TeleBot uses to execute message handlers. Is not used when create_threads is False. + - num_threads: integer (default 4). Controls the amount of WorkerThreads created for the internal thread pool that TeleBot uses to execute message handlers. Is not used when threaded is False. ### The listener mechanism As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example: From ca338015658f5a4280556bcc0c2f2ecf1c875144 Mon Sep 17 00:00:00 2001 From: Dmitriy Olshevskiy Date: Tue, 9 Aug 2016 16:12:27 +0300 Subject: [PATCH 0055/1808] fix relative link for "callback query handlers" in readme --- README.md | 2 +- README.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f03768c55..5ed512aa3 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ * [Methods](#methods) * [General use of the API](#general-use-of-the-api) * [Message handlers](#message-handlers) - * [Callback Query handlers](#message-handlers) + * [Callback Query handlers](#callback-query-handler) * [TeleBot](#telebot) * [Reply markup](#reply-markup) * [Inline Mode](#inline-mode) diff --git a/README.rst b/README.rst index fad0cabc3..5064ec0f1 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ API `__. - `Methods <#methods>`__ - `General use of the API <#general-use-of-the-api>`__ - `Message handlers <#message-handlers>`__ - - `Callback Query handlers <#message-handlers>`__ + - `Callback Query handlers <#callback-query-handler>`__ - `TeleBot <#telebot>`__ - `Reply markup <#reply-markup>`__ - `Inline Mode <#inline-mode>`__ From ff5f6f727a141fb90a2af513730a89a2251a8c9b Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 11 Aug 2016 22:46:40 +0800 Subject: [PATCH 0056/1808] Fix document #220 --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5ed512aa3..6a65168dd 100644 --- a/README.md +++ b/README.md @@ -445,7 +445,6 @@ The TeleBot constructor takes the following optional arguments: - threaded: True/False (default True). A flag to indicate whether TeleBot should execute message handlers on it's polling Thread. - - num_threads: integer (default 4). Controls the amount of WorkerThreads created for the internal thread pool that TeleBot uses to execute message handlers. Is not used when threaded is False. ### The listener mechanism As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example: From 6da88c97511ce894e02cf2310f57b6c1eb1f8380 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 29 Aug 2016 20:21:56 +0800 Subject: [PATCH 0057/1808] FIx #225 --- telebot/__init__.py | 8 +++++--- tests/test_telebot.py | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 25e2e20e9..dbd0df543 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -549,9 +549,11 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_messag ) def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): - return types.Message.de_json( - apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup) - ) + result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup) + if isinstance(result, bool): + return result + else: + return types.Message.de_json(result) def reply_to(self, message, text, **kwargs): """ diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 6480df099..75409c314 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -352,6 +352,12 @@ def test_edit_message_text(self): new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.text == 'Edit test' + def test_edit_message_text(self): + tb = telebot.TeleBot(TOKEN) + msg = tb.send_message(CHAT_ID, 'Test') + new_msg = tb.edit_message_text(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id) + assert new_msg.text == 'Test' + def test_get_chat(self): tb = telebot.TeleBot(TOKEN) ch = tb.get_chat(GROUP_ID) From ea92e8696e9ea8016780e14343a1814ddc231015 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 29 Aug 2016 20:48:09 +0800 Subject: [PATCH 0058/1808] Fix test case. --- tests/test_telebot.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 75409c314..0923a187f 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -352,11 +352,12 @@ def test_edit_message_text(self): new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.text == 'Edit test' - def test_edit_message_text(self): + def test_edit_message_caption(self): + file_data = open('../examples/detailed_example/kitten.jpg', 'rb') tb = telebot.TeleBot(TOKEN) - msg = tb.send_message(CHAT_ID, 'Test') - new_msg = tb.edit_message_text(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id) - assert new_msg.text == 'Test' + msg = tb.send_document(CHAT_ID, file_data, caption="Test") + new_msg = tb.edit_message_caption(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id) + assert new_msg.caption == 'Edit test' def test_get_chat(self): tb = telebot.TeleBot(TOKEN) From 89cf2658aeba256d26f7409c371975001a6e07d7 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 29 Aug 2016 20:50:27 +0800 Subject: [PATCH 0059/1808] Fix edit reply markup return bool. --- telebot/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index dbd0df543..1f4e8b131 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -544,16 +544,16 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ return types.Message.de_json(result) def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): - return types.Message.de_json( - apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) - ) + result = apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) + if type(result) == bool: + return result + return types.Message.de_json(result) def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup) - if isinstance(result, bool): + if type(result) == bool: return result - else: - return types.Message.de_json(result) + return types.Message.de_json(result) def reply_to(self, message, text, **kwargs): """ From 404f81fd43f58f444cc41b03f391a1bda29aa62b Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 29 Aug 2016 21:50:26 +0800 Subject: [PATCH 0060/1808] Version update. Change log: - bugs fix. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 322898475..7215aba57 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.5', + version='2.1.6', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 590b27ca8a082721edf7051eece0ccf44f9b33d4 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 12 Sep 2016 16:38:54 +0800 Subject: [PATCH 0061/1808] Add ujson support. --- setup.py | 3 +++ telebot/types.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7215aba57..893f8de81 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,9 @@ def readme(): license='GPL2', keywords='telegram bot api tools', install_requires=['requests', 'six'], + extras_require={ + 'json': 'ujson', + }, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Programming Language :: Python :: 2', diff --git a/telebot/types.py b/telebot/types.py index f8f4434cd..bf724bf4e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- -import json +try: + import ujson as json +except ImportError: + import json + import six from telebot import util From e69790a8fc3f61bbcbc6cdb6abf8d001897f5d64 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 12 Sep 2016 16:53:48 +0800 Subject: [PATCH 0062/1808] CI support py3.5. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ab066dc49..491d4b917 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "2.7" - "3.3" - "3.4" + - "3.5" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From bac269d48af8f39fb887746b9ab79170c02f94c1 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 12 Sep 2016 17:13:48 +0800 Subject: [PATCH 0063/1808] Update pytest. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 169f93288..1f9cd026a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ py==1.4.29 -pytest==2.7.2 +pytest==3.0.2 requests==2.7.0 six==1.9.0 wheel==0.24.0 From a5ed76018da85f72b505c309a1eb1313c4e0997f Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 17 Sep 2016 07:38:18 +0800 Subject: [PATCH 0064/1808] Fix venue Loacation dejson. --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index bf724bf4e..97a08c351 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -492,7 +492,7 @@ class Venue(JsonDeserializable): @classmethod def de_json(cls, json_type): obj = cls.check_json(json_type) - location = obj['location'] + location = Location.de_json(obj['location']) title = obj['title'] address = obj['address'] foursquare_id = obj.get('foursquare_id') From acdc2058c595b1a3a28d829d3e8ac61315ca8273 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 17 Sep 2016 07:49:05 +0800 Subject: [PATCH 0065/1808] Version change. Change log: - Fix venue's location not dejson to Location object. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 893f8de81..4f79f2530 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.6', + version='2.1.7', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 88e49bdaef7c429d8bcfdcb16fadee67843d8b6f Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 17 Sep 2016 07:55:25 +0800 Subject: [PATCH 0066/1808] Update test case. --- tests/test_telebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 0923a187f..b49e7e178 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -331,6 +331,7 @@ def test_send_venue(self): lon = -161.2901042 ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address") assert ret_msg.venue.title == "Test Venue" + assert int(lat) == int(ret_msg.venue.location.latitude) def test_send_venue_dis_noti(self): tb = telebot.TeleBot(TOKEN) From 057d130baa4d2e3d87c6009bc88bf3f63241482f Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sat, 17 Sep 2016 20:32:54 +0800 Subject: [PATCH 0067/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6a65168dd..05cce5ad7 100644 --- a/README.md +++ b/README.md @@ -529,5 +529,6 @@ Get help. Discuss. Chat. * [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger* * [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi* * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. +* [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 48002f280b73a7416a9bb514a32ca69035131f75 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Sun, 18 Sep 2016 20:36:40 +0300 Subject: [PATCH 0068/1808] Update Readme.md Added new content_types names in documentation. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05cce5ad7..963216ce2 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)). The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings: -'text', 'audio', 'document', 'photo', 'sticker', 'video', 'voice', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'. +`text`, `audio`, `document`, `photo`, `sticker`, `video`, `voice`, `location`, `contact`, `new_chat_member`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`. ### Methods From 1b0a87261989c55acd6c9a6e863457a31c861459 Mon Sep 17 00:00:00 2001 From: Ihor Polyakov Date: Wed, 28 Sep 2016 18:41:36 +0700 Subject: [PATCH 0069/1808] return statement added to callback_query_handler in decorator --- telebot/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1f4e8b131..f234c6a64 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -748,6 +748,7 @@ def callback_query_handler(self, func, **kwargs): def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_callback_query_handler(handler_dict) + return handler return decorator From 4dc2d5f1dbfd123d88443e52405e2d0401ec28d1 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 29 Sep 2016 13:25:01 +0800 Subject: [PATCH 0070/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 963216ce2..ce1b7a756 100644 --- a/README.md +++ b/README.md @@ -530,5 +530,6 @@ Get help. Discuss. Chat. * [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi* * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. * [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin. +* [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From cd89de5a9a5d48a6ef68b6643cf96f8562bd63d4 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 29 Sep 2016 13:35:42 +0800 Subject: [PATCH 0071/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ce1b7a756..c701130b9 100644 --- a/README.md +++ b/README.md @@ -531,5 +531,6 @@ Get help. Discuss. Chat. * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. * [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin. * [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 +* [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From de740be5063c7f2fc5c441bb95af7ed72baa152e Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 8 Oct 2016 19:50:29 +0800 Subject: [PATCH 0072/1808] Add class Game,Animation,GameHighScore. For Game feature. --- telebot/types.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 97a08c351..abca65baa 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1419,3 +1419,80 @@ def __init__(self, id, audio_file_id, reply_markup=None, input_message_content=N self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['audio_file_id'] = audio_file_id + + +# Games + +class Game(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + title = obj['title'] + description = obj['description'] + photo = Game.parse_photo(obj['photo']) + text = obj.get('text') + text_entities = None + if 'text_entities' in obj: + text_entities = Game.parse_entities(obj['text_entities']) + animation = None + if 'animation' in obj: + animation = Animation.de_json(obj['animation']) + return cls(title, description, photo, text, text_entities, animation) + + @classmethod + def parse_photo(cls, photo_size_array): + ret = [] + for ps in photo_size_array: + ret.append(PhotoSize.de_json(ps)) + return ret + + @classmethod + def parse_entities(cls, message_entity_array): + ret = [] + for me in message_entity_array: + ret.append(MessageEntity.de_json(me)) + return ret + + def __init__(self, title, description, photo, text=None, text_entities=None, animation=None): + self.title = title + self.description = description + self.photo = photo + self.text = text + self.text_entities = text_entities + self.animation = animation + + +class Animation(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + file_id = obj['file_id'] + thumb = None + if 'thumb' in obj: + thumb = PhotoSize.de_json(obj['thumb']) + file_name = obj.get('file_name') + mime_type = obj.get('mime_type') + file_size = obj.get('file_size') + return cls(file_id, thumb, file_name, mime_type, file_size) + + def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_size=None): + self.file_id = file_id + self.thumb = thumb + self.file_name = file_name + self.mime_type = mime_type + self.file_size = file_size + + +class GameHighScore(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + position = obj['position'] + user = User.de_json(obj['user']) + score = obj['score'] + return cls(position, user, score) + + def __init__(self, position, user, score): + self.position = position + self.user = user + self.score = score From 795a00f92c7f78cddb9a9eb93d583c7811ce05ec Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 8 Oct 2016 20:06:08 +0800 Subject: [PATCH 0073/1808] Add game in Message. InlineQueryResultGame --- telebot/types.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index abca65baa..ec8376292 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -205,6 +205,9 @@ def de_json(cls, json_string): if 'document' in obj: opts['document'] = Document.de_json(obj['document']) content_type = 'document' + if 'game' in obj: + opts['game'] = Game.de_json(obj['photo']) + content_type = 'game' if 'photo' in obj: opts['photo'] = Message.parse_photo(obj['photo']) content_type = 'photo' @@ -1423,6 +1426,20 @@ def __init__(self, id, audio_file_id, reply_markup=None, input_message_content=N # Games +class InlineQueryResultGame(JsonSerializable): + def __init__(self, type, id, game_short_name, reply_markup=None): + self.type = type + 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 + return json.dumps(json_dic) + + class Game(JsonDeserializable): @classmethod def de_json(cls, json_string): From b8e5c4359812fa20e449cfc5aa1c26177e84c0b9 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 8 Oct 2016 20:36:48 +0800 Subject: [PATCH 0074/1808] Add send game method. --- telebot/__init__.py | 9 ++++++++- telebot/apihelper.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f234c6a64..3c90cf195 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -549,8 +549,15 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_messag return result return types.Message.de_json(result) + def send_game(self, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, + reply_markup=None): + result = apihelper.send_game(self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, + reply_markup) + return types.Message.de_json(result) + def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): - result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup) + result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, + reply_markup) if type(result) == bool: return result return types.Message.de_json(result) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 693387d00..076cb3aeb 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -418,6 +418,20 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa return _make_request(token, method_url, params=payload) +# Game + +def send_game(token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None): + method_url = r'sendGame' + payload = {'chat_id': chat_id, 'game_short_name': game_short_name} + if disable_notification: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + + # InlineQuery def answer_callback_query(token, callback_query_id, text=None, show_alert=None): From 740d7f44cfea1c8e83b6831a6bb918728bb9648c Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 8 Oct 2016 21:55:28 +0800 Subject: [PATCH 0075/1808] Add url param in answer inline query. --- telebot/__init__.py | 6 +++--- telebot/apihelper.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3c90cf195..d48d97ee0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -552,7 +552,7 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_messag def send_game(self, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None): result = apihelper.send_game(self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, - reply_markup) + reply_markup) return types.Message.de_json(result) def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): @@ -586,7 +586,7 @@ def answer_inline_query(self, inline_query_id, results, cache_time=None, is_pers return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, switch_pm_text, switch_pm_parameter) - def answer_callback_query(self, callback_query_id, text=None, show_alert=None): + def answer_callback_query(self, callback_query_id, text=None, show_alert=None, url=None): """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. @@ -595,7 +595,7 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None): :param show_alert: :return: """ - return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert) + return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url) def register_for_reply(self, message, callback): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 076cb3aeb..85f856b46 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -434,13 +434,15 @@ def send_game(token, chat_id, game_short_name, disable_notification=None, reply_ # InlineQuery -def answer_callback_query(token, callback_query_id, text=None, show_alert=None): +def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None): method_url = 'answerCallbackQuery' payload = {'callback_query_id': callback_query_id} if text: payload['text'] = text if show_alert: payload['show_alert'] = show_alert + if url: + payload['url'] = url return _make_request(token, method_url, params=payload, method='post') From 8d65856dec9e3d838d7664bc97d997755f925c0b Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 8 Oct 2016 22:04:44 +0800 Subject: [PATCH 0076/1808] New field callback_game in InlineKeyboardButton, new fields game_short_name and chat_instance in CallbackQuery. --- telebot/types.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ec8376292..8c26bcb08 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -699,11 +699,14 @@ def to_dic(self): class InlineKeyboardButton(JsonSerializable): - def __init__(self, text, url=None, callback_data=None, switch_inline_query=None): + def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, + switch_inline_query_current_chat=None, callback_game=None): self.text = text self.url = url self.callback_data = callback_data self.switch_inline_query = switch_inline_query + self.switch_inline_query_current_chat = switch_inline_query_current_chat + self.callback_game = callback_game def to_json(self): return json.dumps(self.to_dic()) @@ -716,6 +719,10 @@ def to_dic(self): json_dic['callback_data'] = self.callback_data if self.switch_inline_query is not None: json_dic['switch_inline_query'] = self.switch_inline_query + if self.switch_inline_query_current_chat is not None: + json_dic['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat + if self.callback_game is not None: + json_dic['callback_game'] = self.callback_game return json_dic @@ -729,10 +736,14 @@ def de_json(cls, json_type): if 'message' in obj: message = Message.de_json(obj['message']) inline_message_id = obj.get('inline_message_id') + chat_instance = obj['chat_instance'] data = obj['data'] - return cls(id, from_user, data, message, inline_message_id) + game_short_name = obj.get('game_short_name') + return cls(id, from_user, data, chat_instance, message, inline_message_id, game_short_name) - def __init__(self, id, from_user, data, message=None, inline_message_id=None): + def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None): + self.game_short_name = game_short_name + self.chat_instance = chat_instance self.id = id self.from_user = from_user self.message = message From 11aa5fcb852fb70a79a35648e4286683432ff53c Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Tue, 11 Oct 2016 22:43:44 +0300 Subject: [PATCH 0077/1808] Added "all_members_are_administrators" field to Chat object --- telebot/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 8c26bcb08..e069e7cb3 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -161,15 +161,17 @@ def de_json(cls, json_string): username = obj.get('username') first_name = obj.get('first_name') last_name = obj.get('last_name') - return cls(id, type, title, username, first_name, last_name) + all_members_are_administrators = obj.get('all_members_are_administrators') + return cls(id, type, title, username, first_name, last_name, all_members_are_administrators) - def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None): + def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, all_members_are_administrators=None): self.type = type self.last_name = last_name self.first_name = first_name self.username = username self.id = id self.title = title + self.all_members_are_administrators = all_members_are_administrators class Message(JsonDeserializable): From 7e94810ece35279f7bf11951c662a02e6bfb06fc Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Tue, 11 Oct 2016 22:51:20 +0300 Subject: [PATCH 0078/1808] Added Caption field to sendAudio & sendVoice --- telebot/apihelper.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 85f856b46..1622b4b99 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -280,7 +280,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa return _make_request(token, method_url, params=payload, files=files, method='post') -def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None, +def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} @@ -289,6 +289,8 @@ def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, r files = {'voice': voice} else: payload['voice'] = voice + if caption: + payload['caption'] = caption if duration: payload['duration'] = duration if reply_to_message_id: @@ -302,7 +304,7 @@ def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, r return _make_request(token, method_url, params=payload, files=files, method='post') -def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None, +def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} @@ -311,6 +313,8 @@ def send_audio(token, chat_id, audio, duration=None, performer=None, title=None, files = {'audio': audio} else: payload['audio'] = audio + if caption: + payload['caption'] = caption if duration: payload['duration'] = duration if performer: From 08dd7d15934a3dc1f90b820897eb17a3e64142ba Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Tue, 11 Oct 2016 22:57:16 +0300 Subject: [PATCH 0079/1808] Added caption field to several objects InlineQueryResultAudio, InlineQueryResultVoice, InlineQueryResultCachedAudio, InlineQueryResultCachedVoice --- telebot/types.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 8c26bcb08..248f728df 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1118,12 +1118,13 @@ def to_json(self): class InlineQueryResultAudio(JsonSerializable): - def __init__(self, id, audio_url, title, performer=None, audio_duration=None, reply_markup=None, + def __init__(self, id, audio_url, title, caption=None, performer=None, audio_duration=None, reply_markup=None, input_message_content=None): self.type = 'audio' self.id = id self.audio_url = audio_url self.title = title + self.caption = caption self.performer = performer self.audio_duration = audio_duration self.reply_markup = reply_markup @@ -1131,6 +1132,8 @@ def __init__(self, id, audio_url, title, performer=None, audio_duration=None, re 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.performer: json_dict['performer'] = self.performer if self.audio_duration: @@ -1143,12 +1146,13 @@ def to_json(self): class InlineQueryResultVoice(JsonSerializable): - def __init__(self, id, voice_url, title, performer=None, voice_duration=None, reply_markup=None, + def __init__(self, id, voice_url, title, caption=None, performer=None, voice_duration=None, reply_markup=None, input_message_content=None): self.type = 'voice' self.id = id self.voice_url = voice_url self.title = title + self.caption = caption self.performer = performer self.voice_duration = voice_duration self.reply_markup = reply_markup @@ -1156,6 +1160,8 @@ def __init__(self, id, voice_url, title, performer=None, voice_duration=None, re 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.performer: json_dict['performer'] = self.performer if self.voice_duration: @@ -1413,23 +1419,25 @@ def __init__(self, id, video_file_id, title, description=None, caption=None, rep class InlineQueryResultCachedVoice(BaseInlineQueryResultCached): - def __init__(self, id, voice_file_id, title, reply_markup=None, input_message_content=None): + def __init__(self, id, voice_file_id, title, caption=None, reply_markup=None, input_message_content=None): BaseInlineQueryResultCached.__init__(self) self.type = 'voice' self.id = id self.voice_file_id = voice_file_id self.title = title + self.caption = caption self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['voice_file_id'] = voice_file_id class InlineQueryResultCachedAudio(BaseInlineQueryResultCached): - def __init__(self, id, audio_file_id, reply_markup=None, input_message_content=None): + def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_message_content=None): BaseInlineQueryResultCached.__init__(self) self.type = 'audio' self.id = id self.audio_file_id = audio_file_id + self.caption = caption self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['audio_file_id'] = audio_file_id From ffa0ea449b41481d5e1610c824ba0eaaf8b30421 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 12 Oct 2016 15:52:34 +0800 Subject: [PATCH 0080/1808] Fix test case. --- telebot/__init__.py | 8 ++++---- tests/test_telebot.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d48d97ee0..26237a2ad 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -393,7 +393,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, disable_notification)) - def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, reply_to_message_id=None, + def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -407,10 +407,10 @@ def send_audio(self, chat_id, audio, duration=None, performer=None, title=None, :return: Message """ return types.Message.de_json( - apihelper.send_audio(self.token, chat_id, audio, duration, performer, title, reply_to_message_id, + apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, reply_markup, disable_notification, timeout)) - def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, reply_markup=None, + def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -422,7 +422,7 @@ def send_voice(self, chat_id, voice, duration=None, reply_to_message_id=None, re :return: Message """ return types.Message.de_json( - apihelper.send_voice(self.token, chat_id, voice, duration, reply_to_message_id, reply_markup, + apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, disable_notification, timeout)) def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None, diff --git a/tests/test_telebot.py b/tests/test_telebot.py index b49e7e178..b9b460c82 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -193,7 +193,7 @@ def test_send_photo_dis_noti(self): def test_send_audio(self): file_data = open('./test_data/record.mp3', 'rb') tb = telebot.TeleBot(TOKEN) - ret_msg = tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram') + ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram') assert ret_msg.content_type == 'audio' assert ret_msg.audio.performer == 'eternnoir' assert ret_msg.audio.title == 'pyTelegram' @@ -201,7 +201,7 @@ def test_send_audio(self): def test_send_audio_dis_noti(self): file_data = open('./test_data/record.mp3', 'rb') tb = telebot.TeleBot(TOKEN) - ret_msg = tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram', disable_notification=True) + ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram', disable_notification=True) assert ret_msg.content_type == 'audio' assert ret_msg.audio.performer == 'eternnoir' assert ret_msg.audio.title == 'pyTelegram' From 702763edd6ab5f09701d3b4e282d1c8c4ae86caa Mon Sep 17 00:00:00 2001 From: Alex Root Junior Date: Thu, 20 Oct 2016 10:52:38 +0300 Subject: [PATCH 0081/1808] Get webhook info https://core.telegram.org/bots/api#getwebhookinfo --- telebot/__init__.py | 4 ++++ telebot/apihelper.py | 6 ++++++ telebot/types.py | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index f234c6a64..220750e13 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -78,6 +78,10 @@ def __init__(self, token, threaded=True, skip_pending=False): def set_webhook(self, url=None, certificate=None): return apihelper.set_webhook(self.token, url, certificate) + def get_webhook_info(self): + result = apihelper.get_webhook_info(self.token) + return types.WebhookInfo.de_json(result) + def remove_webhook(self): return self.set_webhook() # No params resets webhook diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 693387d00..0110b3352 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -127,6 +127,12 @@ def set_webhook(token, url=None, certificate=None): return _make_request(token, method_url, params=payload, files=files) +def get_webhook_info(token): + method_url = r'getWebhookInfo' + payload = {} + return _make_request(token, method_url, params=payload) + + def get_updates(token, offset=None, limit=None, timeout=None): method_url = r'getUpdates' payload = {} diff --git a/telebot/types.py b/telebot/types.py index 97a08c351..792cdcd6f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -121,6 +121,29 @@ def __init__(self, update_id, message, edited_message, inline_query, chosen_inli self.callback_query = callback_query +class WebhookInfo(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + url = obj['url'] + has_custom_certificate = obj['has_custom_certificate'] + pending_update_count = obj['pending_update_count'] + last_error_date = None + last_error_message = None + if 'last_error_message' in obj: + last_error_date = obj['last_error_date'] + if 'last_error_message' in obj: + last_error_message = obj['last_error_message'] + return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message) + + def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message): + self.url = url + self.has_custom_certificate = has_custom_certificate + self.pending_update_count = pending_update_count + self.last_error_date = last_error_date + self.last_error_message = last_error_message + + class User(JsonDeserializable): @classmethod def de_json(cls, json_string): From 6f8ebbae890b23040490757d53a1ae9294cca8a9 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Wed, 26 Oct 2016 16:19:04 +0300 Subject: [PATCH 0082/1808] Added setGameScore and getGameHighScores 1. https://core.telegram.org/bots/api#setgamescore 2. https://core.telegram.org/bots/api#getgamehighscores --- telebot/apihelper.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b40ea36ca..489b02b55 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -442,6 +442,55 @@ def send_game(token, chat_id, game_short_name, disable_notification=None, reply_ return _make_request(token, method_url, params=payload) +# https://core.telegram.org/bots/api#setgamescore +def set_game_score(token, user_id, score, chat_id=None, message_id=None, inline_message_id=None, edit_message=None): + """ + Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if the new score is not greater than the user's current score in the chat. + :param token: Bot's token (you don't need to fill this) + :param user_id: User identifier + :param score: New score, must be positive + :param chat_id: (Optional, required if inline_message_id is not specified) Unique identifier for the target chat (or username of the target channel in the format @channelusername) + :param message_id: (Optional, required if inline_message_id is not specified) Unique identifier of the sent message + :param inline_message_id: (Optional, required if chat_id and message_id are not specified) Identifier of the inline message + :param edit_message: (Optional) Pass True, if the game message should be automatically edited to include the current scoreboard + :return: + """ + method_url = r'setGameScore' + payload = {'user_id': user_id, 'score': score} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if edit_message: + payload['edit_message'] = edit_message + return _make_request(token, method_url, params=payload) + + +# https://core.telegram.org/bots/api#getgamehighscores +def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_message_id=None): + """ + Use this method to get data for high score tables. Will return the score of the specified user and several of his neighbors in a game. On success, returns an Array of GameHighScore objects. + This method will currently return scores for the target user, plus two of his closest neighbors on each side. Will also return the top three users if the user and his neighbors are not among them. Please note that this behavior is subject to change. + :param token: Bot's token (you don't need to fill this) + :param user_id: Target user id + :param chat_id: (Optional, required if inline_message_id is not specified) Unique identifier for the target chat (or username of the target channel in the format @channelusername) + :param message_id: (Optional, required if inline_message_id is not specified) Unique identifier of the sent message + :param inline_message_id: (Optional, required if chat_id and message_id are not specified) Identifier of the inline message + :return: + """ + method_url = r'getGameHighScores' + payload = {'user_id': user_id} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + return _make_request(token, method_url, params=payload) + + # InlineQuery def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None): From 54ed2038aa3c6eb670254cbf43abb3d0764362ae Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 29 Oct 2016 21:22:46 +0800 Subject: [PATCH 0083/1808] New methods setGameScore and getGameHighScores. --- telebot/__init__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index baf5838f9..afbb89bd1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -397,7 +397,8 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, disable_notification)) - def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, + def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, + reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -559,6 +560,20 @@ def send_game(self, chat_id, game_short_name, disable_notification=None, reply_t reply_markup) return types.Message.de_json(result) + def set_game_score(self, user_id, score, chat_id=None, message_id=None, inline_message_id=None, edit_message=None): + result = apihelper.set_game_score(self.token, user_id, score, chat_id, message_id, inline_message_id, + edit_message) + if type(result) == bool: + return result + return types.Message.de_json(result) + + def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None): + result = apihelper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) + ret = [] + for r in result: + ret.append(types.GameHighScore.de_json(r)) + return ret + def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup) From aad9251d48ad944675ea4cc6489a4148eff9b946 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 29 Oct 2016 21:44:02 +0800 Subject: [PATCH 0084/1808] Version update. Change log: * Gaming platform new methods. * Other change in October 3, 2016 update. (https://core.telegram.org/bots/api/#recent-changes) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4f79f2530..a4483e2a8 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.1.7', + version='2.2.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From d2e1acde6a3663cee0d1d414458f8e3aa8132837 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Sat, 29 Oct 2016 23:23:39 +0300 Subject: [PATCH 0085/1808] Fixed API object type mismatch --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 093d7a0a4..953f14ee9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -231,7 +231,7 @@ def de_json(cls, json_string): opts['document'] = Document.de_json(obj['document']) content_type = 'document' if 'game' in obj: - opts['game'] = Game.de_json(obj['photo']) + opts['game'] = Game.de_json(obj['game']) content_type = 'game' if 'photo' in obj: opts['photo'] = Message.parse_photo(obj['photo']) From 34b0a2404e30cc10b5d75f263ad7f6f3ccec7e3f Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 30 Oct 2016 07:05:15 +0800 Subject: [PATCH 0086/1808] Version change. Change log: - bug fix. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a4483e2a8..ac0a70401 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.2.0', + version='2.2.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 2af9209005a18bf09b0150044416542d38a1654d Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Sun, 30 Oct 2016 13:18:17 +0300 Subject: [PATCH 0087/1808] Fixed KeyError when data field is None in CallbackQuery obj['data'] raises KeyError when `data` is None, while obj.get('data') returns None --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 953f14ee9..0faa38c00 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -762,7 +762,7 @@ def de_json(cls, json_type): message = Message.de_json(obj['message']) inline_message_id = obj.get('inline_message_id') chat_instance = obj['chat_instance'] - data = obj['data'] + data = obj.get('data') game_short_name = obj.get('game_short_name') return cls(id, from_user, data, chat_instance, message, inline_message_id, game_short_name) From 2e8151cb7d3cd726504be47f033e52f1d8858e72 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 31 Oct 2016 00:35:07 +0800 Subject: [PATCH 0088/1808] Version update. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ac0a70401..b1d16f374 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.2.1', + version='2.2.2', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From e342b9fa6bfab617ced4a69a49a80df9535c0254 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 1 Nov 2016 01:10:06 +0800 Subject: [PATCH 0089/1808] Fix InlineQueryResultGame replymarkup do not to_dic. --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 0faa38c00..ea4b474ac 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1480,7 +1480,7 @@ def __init__(self, type, id, game_short_name, reply_markup=None): 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 + json_dic['reply_markup'] = self.reply_markup.to_dic() return json.dumps(json_dic) From 7a6bb4dcc8c88298c8b2378d90893f9e999cecb9 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 1 Nov 2016 01:14:28 +0800 Subject: [PATCH 0090/1808] Remove type in InlineQueryResultGame. --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ea4b474ac..f0c5953d5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1471,8 +1471,8 @@ def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_mes # Games class InlineQueryResultGame(JsonSerializable): - def __init__(self, type, id, game_short_name, reply_markup=None): - self.type = type + 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 From bf6634bc36c44b67b7392ee7485b173f6016f3a2 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 1 Nov 2016 01:23:51 +0800 Subject: [PATCH 0091/1808] Version update. Chnage log: - Fix InlineQueryResultGame's reply_makrup bug. - Remove type param from InlineQueryResultGame contractor. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b1d16f374..4b72352ef 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.2.2', + version='2.2.3', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 2c497edca670dac2ff3f18e1f0eac8f5d4d5683c Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 3 Nov 2016 16:43:46 +0800 Subject: [PATCH 0092/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c701130b9..1bca5413a 100644 --- a/README.md +++ b/README.md @@ -532,5 +532,6 @@ Get help. Discuss. Chat. * [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin. * [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash +* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From d9ca776e2ca78a290487d98633b83dd308bc5c0f Mon Sep 17 00:00:00 2001 From: Dmytryi Striletskyi Date: Wed, 9 Nov 2016 20:03:28 +0200 Subject: [PATCH 0093/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1bca5413a..7cf984f93 100644 --- a/README.md +++ b/README.md @@ -533,5 +533,6 @@ Get help. Discuss. Chat. * [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch +* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetables for one university in Kiev. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 9ddc529b28f5ef809c1abaf515d64390f37576b9 Mon Sep 17 00:00:00 2001 From: Dmytryi Strilteskyi Date: Wed, 9 Nov 2016 20:24:27 +0200 Subject: [PATCH 0094/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cf984f93..658cdb3d2 100644 --- a/README.md +++ b/README.md @@ -533,6 +533,6 @@ Get help. Discuss. Chat. * [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch -* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetables for one university in Kiev. +* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 1691e84d0167eae901c7a9f954be48b0a77c39f0 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Wed, 16 Nov 2016 14:18:39 +0300 Subject: [PATCH 0095/1808] ReplyKeyboardHide -> ReplyKeyboardRemove Since Telegram changed object name in API docs: https://core.telegram.org/bots/api#replykeyboardremove --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index f0c5953d5..9447064f7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -572,7 +572,7 @@ def to_json(self): return json.dumps(json_dict) -class ReplyKeyboardHide(JsonSerializable): +class ReplyKeyboardRemove(JsonSerializable): def __init__(self, selective=None): self.selective = selective From 91076d0b5936e760c546370c4b5bdfdf3e7ff697 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 18 Nov 2016 19:37:00 +0800 Subject: [PATCH 0096/1808] Add dailypepebot to list. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 658cdb3d2..c183ab000 100644 --- a/README.md +++ b/README.md @@ -534,5 +534,8 @@ Get help. Discuss. Chat. * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch * [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. +* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. +* [dailypepebot](https://telegram.me/dailypepebot) by [*jaime*](https://github.com/jiwidi) - Get's you random pepe images and gives you their id, then you can call this image with the number. + Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 11c2505d5081ef621c8cce42314d71c38fb0a2da Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 18 Nov 2016 19:37:29 +0800 Subject: [PATCH 0097/1808] Update readme. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c183ab000..82a0b78e2 100644 --- a/README.md +++ b/README.md @@ -534,7 +534,6 @@ Get help. Discuss. Chat. * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch * [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. -* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. * [dailypepebot](https://telegram.me/dailypepebot) by [*jaime*](https://github.com/jiwidi) - Get's you random pepe images and gives you their id, then you can call this image with the number. From 9aaa00c8fd4598d60ebaefccc094a810cace4da6 Mon Sep 17 00:00:00 2001 From: Rafael Medina Date: Sun, 20 Nov 2016 19:04:21 +0000 Subject: [PATCH 0098/1808] Added wat-bridge to the list of bots --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 82a0b78e2..41acc8b02 100644 --- a/README.md +++ b/README.md @@ -535,6 +535,7 @@ Get help. Discuss. Chat. * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch * [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. * [dailypepebot](https://telegram.me/dailypepebot) by [*jaime*](https://github.com/jiwidi) - Get's you random pepe images and gives you their id, then you can call this image with the number. +* [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 1c9a9b9622b164caeffe75e92cecffa01d4be106 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 21 Nov 2016 08:57:38 +0300 Subject: [PATCH 0099/1808] hide_keyboard -> remove_keyboard --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 9447064f7..0434a332c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -577,7 +577,7 @@ def __init__(self, selective=None): self.selective = selective def to_json(self): - json_dict = {'hide_keyboard': True} + json_dict = {'remove_keyboard': True} if self.selective: json_dict['selective'] = True return json.dumps(json_dict) From b2cd3c9716a5395a0d753727465e65acf100c6b2 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 21 Nov 2016 09:06:36 +0300 Subject: [PATCH 0100/1808] Added channel_post and edited_channel_post to Update object --- telebot/types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 0434a332c..0ac978e08 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -103,6 +103,10 @@ def de_json(cls, json_type): message = Message.de_json(obj['message']) if 'edited_message' in obj: edited_message = Message.de_json(obj['edited_message']) + if 'channel_post' in obj: + channel_post = Message.de_json(obj['channel_post']) + if 'edited_channel_post' in obj: + edited_channel_post = Message.de_json(obj['edited_channel_post']) if 'inline_query' in obj: inline_query = InlineQuery.de_json(obj['inline_query']) if 'chosen_inline_result' in obj: From 8c8be81bb936ebbda4439cfb2b11593448468902 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 21 Nov 2016 09:10:51 +0300 Subject: [PATCH 0101/1808] Added optional forward_from_message_id And changed `forward_date` to optional (as it should be) --- telebot/types.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 0ac978e08..7449ad2ef 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -217,8 +217,10 @@ def de_json(cls, json_string): opts['forward_from'] = User.de_json(obj['forward_from']) if 'forward_from_chat' in obj: opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) + if 'forward_from_message_id' in obj: + opts['forward_from_message_id'] = obj.get('forward_from_message_id') if 'forward_date' in obj: - opts['forward_date'] = obj['forward_date'] + opts['forward_date'] = obj.get('forward_date') if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) if 'edit_date' in obj: From 856af7259933f37a2bb703810a562d701325f6ff Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 21 Nov 2016 09:19:59 +0300 Subject: [PATCH 0102/1808] Added cache_time to answer_callback_query --- telebot/apihelper.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 489b02b55..330666839 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -493,7 +493,20 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m # InlineQuery -def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None): +def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None, cache_time=None): + """ + Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned. + Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via BotFather and accept the terms. Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter. + + :param token: Bot's token (you don't need to fill this) + :param callback_query_id: Unique identifier for the query to be answered + :param text: (Optional) Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters + :param show_alert: (Optional) If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false. + :param url: (Optional) URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your game – note that this will only work if the query comes from a callback_game button. + Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter. + :param cache_time: (Optional) The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0. + :return: + """ method_url = 'answerCallbackQuery' payload = {'callback_query_id': callback_query_id} if text: @@ -502,6 +515,8 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None, payload['show_alert'] = show_alert if url: payload['url'] = url + if cache_time: + payload['cache_time'] = cache_time return _make_request(token, method_url, params=payload, method='post') From c99bb166196d845ff1d67b1af0114eca4b236213 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 21 Nov 2016 09:28:32 +0300 Subject: [PATCH 0103/1808] Updated set_game_score MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • New field `force` • Changed `edit_message` to `disable_edit_message` --- telebot/apihelper.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 330666839..c56b08179 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -443,28 +443,31 @@ def send_game(token, chat_id, game_short_name, disable_notification=None, reply_ # https://core.telegram.org/bots/api#setgamescore -def set_game_score(token, user_id, score, chat_id=None, message_id=None, inline_message_id=None, edit_message=None): +def set_game_score(token, user_id, score, force=None, disable_edit_message=None, chat_id=None, message_id=None, inline_message_id=None): """ Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if the new score is not greater than the user's current score in the chat. :param token: Bot's token (you don't need to fill this) :param user_id: User identifier - :param score: New score, must be positive + :param score: New score, must be non-negative + :param force: (Optional) Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters + :param disable_edit_message: (Optional) Pass True, if the game message should not be automatically edited to include the current scoreboard :param chat_id: (Optional, required if inline_message_id is not specified) Unique identifier for the target chat (or username of the target channel in the format @channelusername) :param message_id: (Optional, required if inline_message_id is not specified) Unique identifier of the sent message :param inline_message_id: (Optional, required if chat_id and message_id are not specified) Identifier of the inline message - :param edit_message: (Optional) Pass True, if the game message should be automatically edited to include the current scoreboard :return: """ method_url = r'setGameScore' payload = {'user_id': user_id, 'score': score} + if force: + payload['force'] = force if chat_id: payload['chat_id'] = chat_id if message_id: payload['message_id'] = message_id if inline_message_id: payload['inline_message_id'] = inline_message_id - if edit_message: - payload['edit_message'] = edit_message + if disable_edit_message: + payload['disable_edit_message'] = disable_edit_message return _make_request(token, method_url, params=payload) From 30ed6e37d3cd66e38d326f3e89eece14455f3379 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 3 Dec 2016 13:28:22 +0800 Subject: [PATCH 0104/1808] Add channel_post, edited_channel_post support. --- telebot/__init__.py | 50 +++++++++++++++++++++++++++++++++++++++++++++ telebot/types.py | 11 +++++++--- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index afbb89bd1..e219b7712 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -67,6 +67,8 @@ def __init__(self, token, threaded=True, skip_pending=False): self.message_handlers = [] self.edited_message_handlers = [] + self.channel_post_handlers = [] + self.edited_channel_post_handlers = [] self.inline_handlers = [] self.chosen_inline_handlers = [] self.callback_query_handlers = [] @@ -129,6 +131,8 @@ def __retrieve_updates(self, timeout=20): def process_new_updates(self, updates): new_messages = [] edited_new_messages = [] + new_channel_posts = [] + new_edited_channel_posts = [] new_inline_querys = [] new_chosen_inline_results = [] new_callback_querys = [] @@ -139,6 +143,10 @@ def process_new_updates(self, updates): new_messages.append(update.message) if update.edited_message: edited_new_messages.append(update.edited_message) + if update.channel_post: + new_channel_posts.append(update.channel_post) + if update.edited_channel_post: + new_edited_channel_posts.append(update.edited_channel_post) if update.inline_query: new_inline_querys.append(update.inline_query) if update.chosen_inline_result: @@ -150,6 +158,10 @@ def process_new_updates(self, updates): self.process_new_messages(new_messages) if len(edited_new_messages) > 0: self.process_new_edited_messages(edited_new_messages) + if len(new_channel_posts) > 0: + self.process_new_channel_posts(new_channel_posts) + if len(new_edited_channel_posts) > 0: + self.process_new_edited_channel_posts(new_edited_channel_posts) if len(new_inline_querys) > 0: self.process_new_inline_query(new_inline_querys) if len(new_chosen_inline_results) > 0: @@ -167,6 +179,12 @@ def process_new_messages(self, new_messages): def process_new_edited_messages(self, edited_message): self._notify_command_handlers(self.edited_message_handlers, edited_message) + def process_new_channel_posts(self, channel_post): + self._notify_command_handlers(self.channel_post_handlers, channel_post) + + def process_new_edited_channel_posts(self, edited_channel_post): + self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post) + def process_new_inline_query(self, new_inline_querys): self._notify_command_handlers(self.inline_handlers, new_inline_querys) @@ -748,6 +766,38 @@ def decorator(handler): def add_edited_message_handler(self, handler_dict): self.edited_message_handlers.append(handler_dict) + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + commands=commands, + regexp=regexp, + func=func, + content_types=content_types, + **kwargs) + self.add_channel_post_handler(handler_dict) + return handler + + return decorator + + def add_channel_post_handler(self, handler_dict): + self.channel_post_handlers.append(handler_dict) + + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + commands=commands, + regexp=regexp, + func=func, + content_types=content_types, + **kwargs) + self.add_edited_message_handler(handler_dict) + return handler + + return decorator + + def add_edited_channel_post_handler(self, handler_dict): + self.edited_channel_post_handlers.append(handler_dict) + def inline_handler(self, func, **kwargs): def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) diff --git a/telebot/types.py b/telebot/types.py index 7449ad2ef..42d37ecc0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -113,13 +113,17 @@ def de_json(cls, json_type): chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result']) if 'callback_query' in obj: callback_query = CallbackQuery.de_json(obj['callback_query']) - return cls(update_id, message, edited_message, inline_query, chosen_inline_result, callback_query) + return cls(update_id, message, edited_message, channel_post, edited_channel_post, inline_query, + chosen_inline_result, callback_query) - def __init__(self, update_id, message, edited_message, inline_query, chosen_inline_result, callback_query): + def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query, + chosen_inline_result, callback_query): self.update_id = update_id self.edited_message = edited_message self.message = message self.edited_message = edited_message + self.channel_post = channel_post + self.edited_channel_post = edited_channel_post self.inline_query = inline_query self.chosen_inline_result = chosen_inline_result self.callback_query = callback_query @@ -191,7 +195,8 @@ def de_json(cls, json_string): all_members_are_administrators = obj.get('all_members_are_administrators') return cls(id, type, title, username, first_name, last_name, all_members_are_administrators) - def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, all_members_are_administrators=None): + def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, + all_members_are_administrators=None): self.type = type self.last_name = last_name self.first_name = first_name From 9a5e8302be3253c9b29340cf5b2bfa40a3bdadc8 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 3 Dec 2016 13:31:55 +0800 Subject: [PATCH 0105/1808] Add force params in setGameScore method. --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e219b7712..05fc12ee7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -578,8 +578,8 @@ def send_game(self, chat_id, game_short_name, disable_notification=None, reply_t reply_markup) return types.Message.de_json(result) - def set_game_score(self, user_id, score, chat_id=None, message_id=None, inline_message_id=None, edit_message=None): - result = apihelper.set_game_score(self.token, user_id, score, chat_id, message_id, inline_message_id, + def set_game_score(self, user_id, score, force=None,chat_id=None, message_id=None, inline_message_id=None, edit_message=None): + result = apihelper.set_game_score(self.token, user_id, score, force, chat_id, message_id, inline_message_id, edit_message) if type(result) == bool: return result From e555da86ddad6da752f2ec15aa43dd3ac8203a97 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 3 Dec 2016 13:38:30 +0800 Subject: [PATCH 0106/1808] Add cache_time to answerCallbackQuery --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 05fc12ee7..44e77c332 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -623,7 +623,7 @@ def answer_inline_query(self, inline_query_id, results, cache_time=None, is_pers return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, switch_pm_text, switch_pm_parameter) - def answer_callback_query(self, callback_query_id, text=None, show_alert=None, url=None): + def answer_callback_query(self, callback_query_id, text=None, show_alert=None, url=None, cache_time=None): """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. @@ -632,7 +632,7 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None, u :param show_alert: :return: """ - return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url) + return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) def register_for_reply(self, message, callback): """ From b0bc49c80398070bc6e4bf91bb564f1af22beea4 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 3 Dec 2016 13:56:22 +0800 Subject: [PATCH 0107/1808] Update readme. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 41acc8b02..0caba2dc1 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,14 @@ def send_something(message): Same as Message handlers +#### channel_post_handler + +Same as Message handlers + +#### edited_channel_post_handler + +Same as Message handlers + #### Callback Query Handler In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback_querys. From 509fae67924a1ec8aca4c72b9a7eb804b1baec5f Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 3 Dec 2016 15:17:06 +0800 Subject: [PATCH 0108/1808] Bug fix. --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 42d37ecc0..7ee76a7a6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -96,6 +96,8 @@ def de_json(cls, json_type): update_id = obj['update_id'] message = None edited_message = None + channel_post = None + edited_channel_post = None inline_query = None chosen_inline_result = None callback_query = None From b5e27d0fea9c006f186af01b76985eef60b02fc6 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 6 Dec 2016 11:42:15 +0800 Subject: [PATCH 0109/1808] Add max_connections and allowed_updates to set_webhook. --- telebot/__init__.py | 4 ++-- telebot/apihelper.py | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 44e77c332..f4232456e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -77,8 +77,8 @@ def __init__(self, token, threaded=True, skip_pending=False): if self.threaded: self.worker_pool = util.ThreadPool() - def set_webhook(self, url=None, certificate=None): - return apihelper.set_webhook(self.token, url, certificate) + def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): + return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) def get_webhook_info(self): result = apihelper.get_webhook_info(self.token) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c56b08179..499cadc5e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -115,7 +115,7 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m return _make_request(token, method_url, params=payload, method='post') -def set_webhook(token, url=None, certificate=None): +def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None): method_url = r'setWebhook' payload = { 'url': url if url else "", @@ -123,7 +123,10 @@ def set_webhook(token, url=None, certificate=None): files = None if certificate: files = {'certificate': certificate} - + if max_connections: + payload['max_connections'] = max_connections + if allowed_updates: + payload['allowed_updates'] = allowed_updates return _make_request(token, method_url, params=payload, files=files) @@ -443,7 +446,8 @@ def send_game(token, chat_id, game_short_name, disable_notification=None, reply_ # https://core.telegram.org/bots/api#setgamescore -def set_game_score(token, user_id, score, force=None, disable_edit_message=None, chat_id=None, message_id=None, inline_message_id=None): +def set_game_score(token, user_id, score, force=None, disable_edit_message=None, chat_id=None, message_id=None, + inline_message_id=None): """ Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if the new score is not greater than the user's current score in the chat. :param token: Bot's token (you don't need to fill this) From eadff07f79baff9f9e8b83d4f00d85d869f8be79 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 6 Dec 2016 11:44:30 +0800 Subject: [PATCH 0110/1808] Add allowed_updates to get_updates. --- telebot/__init__.py | 5 +++-- telebot/apihelper.py | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f4232456e..dfc71024b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -87,15 +87,16 @@ def get_webhook_info(self): def remove_webhook(self): return self.set_webhook() # No params resets webhook - def get_updates(self, offset=None, limit=None, timeout=20): + def get_updates(self, offset=None, limit=None, timeout=20, allowed_updates=None): """ Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. + :param allowed_updates: Array of string. List the types of updates you want your bot to receive. :param offset: Integer. Identifier of the first update to be returned. :param limit: Integer. Limits the number of updates to be retrieved. :param timeout: Integer. Timeout in seconds for long polling. :return: array of Updates """ - json_updates = apihelper.get_updates(self.token, offset, limit, timeout) + json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates) ret = [] for ju in json_updates: ret.append(types.Update.de_json(ju)) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 499cadc5e..da88b4702 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -136,7 +136,7 @@ def get_webhook_info(token): return _make_request(token, method_url, params=payload) -def get_updates(token, offset=None, limit=None, timeout=None): +def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=None): method_url = r'getUpdates' payload = {} if offset: @@ -145,6 +145,8 @@ def get_updates(token, offset=None, limit=None, timeout=None): payload['limit'] = limit if timeout: payload['timeout'] = timeout + if allowed_updates: + payload['allowed_updates'] = allowed_updates return _make_request(token, method_url, params=payload) From a06551daafe05aab8f2a315f1e58e95ba37b491d Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 6 Dec 2016 11:52:16 +0800 Subject: [PATCH 0111/1808] Add delete webhook. --- telebot/__init__.py | 7 +++++++ telebot/apihelper.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index dfc71024b..ad0dfa590 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -80,6 +80,13 @@ def __init__(self, token, threaded=True, skip_pending=False): def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) + def delete_webhook(self): + """ + Use this method to remove webhook integration if you decide to switch back to getUpdates. + :return: bool + """ + return apihelper.delete_webhook(self.token) + def get_webhook_info(self): result = apihelper.get_webhook_info(self.token) return types.WebhookInfo.de_json(result) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index da88b4702..d32c1bf5f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -130,6 +130,11 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed return _make_request(token, method_url, params=payload, files=files) +def delete_webhook(token): + method_url = r'deleteWebhook' + return _make_request(token, method_url) + + def get_webhook_info(token): method_url = r'getWebhookInfo' payload = {} From c168feea32a56ffbd7be2b949388bb5de36cf126 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 6 Dec 2016 12:09:28 +0800 Subject: [PATCH 0112/1808] Version update: Change log: - Telegram bot api 20161121 new feature. - Telegram bot api 20161204 new feature. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4b72352ef..67e77d69b 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.2.3', + version='2.3.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From d14bd9a36ba6ae34db25e684698b5c8f221a4a21 Mon Sep 17 00:00:00 2001 From: Yolley Date: Tue, 6 Dec 2016 17:12:28 +0300 Subject: [PATCH 0113/1808] Add isinstance for bytes to function 'add' in ReplyKeyboardMarkup All explanation is here https://github.com/eternnoir/pyTelegramBotAPI/issues/265 --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 7ee76a7a6..2ae5d2a9c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -619,6 +619,8 @@ def add(self, *args): for button in args: if util.is_string(button): row.append({'text': button}) + elif isinstance(button, bytes): + row.append({'text': button.decode('utf-8')}) else: row.append(button.to_dic()) if i % self.row_width == 0: From 12e7879325418c3438925622723227c5e38071b6 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 12 Dec 2016 19:29:57 +0300 Subject: [PATCH 0114/1808] Added max_connections and allowed updates to WebhookInfo --- telebot/types.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 7ee76a7a6..274785100 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -144,14 +144,20 @@ def de_json(cls, json_string): last_error_date = obj['last_error_date'] if 'last_error_message' in obj: last_error_message = obj['last_error_message'] - return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message) + if 'max_connections' in obj: + max_connections = obj['max_connections'] + if 'allowed_updates' in obj: + allowed_updates = obj['allowed_updates'] + return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, max_connections, allowed_updates) - def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message): + def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, max_connections, allowed_updates): self.url = url self.has_custom_certificate = has_custom_certificate self.pending_update_count = pending_update_count self.last_error_date = last_error_date self.last_error_message = last_error_message + self.max_connections = max_connections + self.allowed_updates = allowed_updates class User(JsonDeserializable): From 9fe8565d5368bda123cddb94ac544f472bad6dd0 Mon Sep 17 00:00:00 2001 From: Nicholas Guriev Date: Sat, 17 Dec 2016 09:03:00 +0300 Subject: [PATCH 0115/1808] Use session to ensure persistent connection to api.telegram.org --- telebot/apihelper.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d32c1bf5f..7f0490132 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -6,6 +6,7 @@ from telebot import util logger = telebot.logger +req_session = requests.session() API_URL = "https://api.telegram.org/bot{0}/{1}" FILE_URL = "https://api.telegram.org/file/bot{0}/{1}" @@ -31,7 +32,7 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas if params: if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 - result = requests.request(method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout)) + result = req_session.request(method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout)) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] @@ -80,7 +81,7 @@ def get_file(token, file_id): def download_file(token, file_path): url = FILE_URL.format(token, file_path) - result = requests.get(url) + result = req_session.get(url) if result.status_code != 200: msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \ .format(result.status_code, result.reason, result.text) From be87e4b2b9826fce56661b6f9ff545b55bde3c95 Mon Sep 17 00:00:00 2001 From: Andrey Mishakin Date: Wed, 4 Jan 2017 21:50:09 +0300 Subject: [PATCH 0116/1808] Fix error in webhook flask example --- examples/webhook_examples/webhook_flask_echo_bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py index 26b189e83..92ffa219b 100644 --- a/examples/webhook_examples/webhook_flask_echo_bot.py +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -48,9 +48,9 @@ def index(): @app.route(WEBHOOK_URL_PATH, methods=['POST']) def webhook(): if flask.request.headers.get('content-type') == 'application/json': - json_string = flask.request.get_data().encode('utf-8') + json_string = flask.request.get_data().decode('utf-8') update = telebot.types.Update.de_json(json_string) - bot.process_new_messages([update.message]) + bot.process_new_updates([update]) return '' else: flask.abort(403) From a5d6b541a581c9de64304c638bdfa661082da37e Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sun, 15 Jan 2017 00:57:05 +0800 Subject: [PATCH 0117/1808] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0caba2dc1..75b5c8061 100644 --- a/README.md +++ b/README.md @@ -544,6 +544,8 @@ Get help. Discuss. Chat. * [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. * [dailypepebot](https://telegram.me/dailypepebot) by [*jaime*](https://github.com/jiwidi) - Get's you random pepe images and gives you their id, then you can call this image with the number. * [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram - +* [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz) +* [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful. +* [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From b6fee0708907f62bee058acbdc827332515c337f Mon Sep 17 00:00:00 2001 From: i32ropie Date: Fri, 20 Jan 2017 10:55:34 +0100 Subject: [PATCH 0118/1808] Updated from ReplyKeyboardHide() methode to ReplyKeyboardRemove() --- README.md | 20 +++++++++---------- README.rst | 6 +++--- examples/detailed_example/detailed_example.py | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 75b5c8061..b788d4f8a 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ In bot2.0 update. You can get `callback_query` in update object. In telebot use @bot.callback_query_handler(func=lambda call: True) def test_callback(call): logger.info(call) -``` +``` #### TeleBot ```python @@ -303,7 +303,7 @@ file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, ``` #### Reply markup -All `send_xyz` functions of TeleBot take an optional `reply_markup` argument. This argument must be an instance of `ReplyKeyboardMarkup`, `ReplyKeyboardHide` or `ForceReply`, which are defined in types.py. +All `send_xyz` functions of TeleBot take an optional `reply_markup` argument. This argument must be an instance of `ReplyKeyboardMarkup`, `ReplyKeyboardRemove` or `ForceReply`, which are defined in types.py. ```python from telebot import types @@ -339,9 +339,9 @@ The last example yields this result: ![ReplyKeyboardMarkup](https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg "ReplyKeyboardMarkup") ```python -# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup +# ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup # Takes an optional selective argument (True/False, default False) -markup = types.ReplyKeyboardHide(selective=False) +markup = types.ReplyKeyboardRemove(selective=False) tb.send_message(chat_id, message, reply_markup=markup) ``` @@ -398,7 +398,7 @@ def query_text(inline_query): ``` ###Working with entities: -This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. +This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. Attributes: * `type` * `url` @@ -495,14 +495,14 @@ April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision espe Telegram Bot API support new type Chat for message.chat. - Check the ```type``` attribute in ```Chat``` object: -- +- ```python if message.chat.type == “private”: # private chat message if message.chat.type == “group”: # group chat message - + if message.chat.type == “supergroup”: # supergroup chat message @@ -533,7 +533,7 @@ Get help. Discuss. Chat. * [Telegram Proxy Bot](https://bitbucket.org/master_groosha/telegram-proxy-bot) by *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as some kind of "proxy". * [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`. * [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot. -* [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* +* [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* * [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger* * [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi* * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. @@ -541,8 +541,8 @@ Get help. Discuss. Chat. * [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch -* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. -* [dailypepebot](https://telegram.me/dailypepebot) by [*jaime*](https://github.com/jiwidi) - Get's you random pepe images and gives you their id, then you can call this image with the number. +* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. +* [dailypepebot](https://telegram.me/dailypepebot) by [*jaime*](https://github.com/jiwidi) - Get's you random pepe images and gives you their id, then you can call this image with the number. * [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram * [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz) * [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful. diff --git a/README.rst b/README.rst index 5064ec0f1..3cce442e4 100644 --- a/README.rst +++ b/README.rst @@ -497,7 +497,7 @@ Reply markup All ``send_xyz`` functions of TeleBot take an optional ``reply_markup`` argument. This argument must be an instance of ``ReplyKeyboardMarkup``, -``ReplyKeyboardHide`` or ``ForceReply``, which are defined in types.py. +``ReplyKeyboardRemove`` or ``ForceReply``, which are defined in types.py. .. code:: python @@ -538,9 +538,9 @@ The last example yields this result: .. code:: python - # ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup + # ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup # Takes an optional selective argument (True/False, default False) - markup = types.ReplyKeyboardHide(selective=False) + markup = types.ReplyKeyboardRemove(selective=False) tb.send_message(chat_id, message, reply_markup=markup) .. code:: python diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index bf5f632c6..8fad9af31 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -21,7 +21,7 @@ imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard imageSelect.add('cock', 'pussy') -hideBoard = types.ReplyKeyboardHide() # if sent as reply_markup, will hide the keyboard +hideBoard = types.ReplyKeyboardRemove() # if sent as reply_markup, will hide the keyboard # error handling if user isn't known yet From 6c770d81f96d75cd81c83d6a6edbd6d050b8020a Mon Sep 17 00:00:00 2001 From: robox Date: Mon, 30 Jan 2017 01:54:49 +0600 Subject: [PATCH 0119/1808] Extend examples with aiohttp --- examples/webhook_examples/README.md | 13 ++- .../webhook_aiohttp_echo_bot.py | 88 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 examples/webhook_examples/webhook_aiohttp_echo_bot.py diff --git a/examples/webhook_examples/README.md b/examples/webhook_examples/README.md index 2d47346fc..b1e435149 100644 --- a/examples/webhook_examples/README.md +++ b/examples/webhook_examples/README.md @@ -1,6 +1,6 @@ # Webhook examples using pyTelegramBotAPI -There are 3 examples in this directory using different libraries: +There are 4 examples in this directory using different libraries: * **Python (CPython):** *webhook_cpython_echo_bot.py* * **Pros:** @@ -32,5 +32,14 @@ There are 3 examples in this directory using different libraries: * The project seems not to be very active, latest version dates 2013. * They don't recommend to use it with Python 3, but may work. * May be a oversized for just handling webhook petitions. + +* **aiohttp (1.2.0):** *webhook_aiohttp_echo_bot.py* + * **Pros:** + * It's a web application framework + * Python 3 compatible + * Asynchronous, excellent perfomance + * Utilizes new async/await syntax + * **Cons:** + * Requires Python 3.4.2+, don't work with Python 2 -*Latest update of this document: 2015-10-06* +*Latest update of this document: 2017-01-30* diff --git a/examples/webhook_examples/webhook_aiohttp_echo_bot.py b/examples/webhook_examples/webhook_aiohttp_echo_bot.py new file mode 100644 index 000000000..d92cff936 --- /dev/null +++ b/examples/webhook_examples/webhook_aiohttp_echo_bot.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This is a simple echo bot using decorators and webhook with aiohttp +# It echoes any incoming text messages and does not use the polling method. + +import logging +import ssl + +from aiohttp import web + +import telebot + + +API_TOKEN = '' + +WEBHOOK_HOST = '' +WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open') +WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr + +WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate +WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key + +# Quick'n'dirty SSL certificate generation: +# +# openssl genrsa -out webhook_pkey.pem 2048 +# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem +# +# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply +# with the same value in you put in WEBHOOK_HOST + +WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT) +WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN) + + +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) + +bot = telebot.TeleBot(API_TOKEN) + +app = web.Application() + + +# Process webhook calls +async def handle(request): + if request.match_info.get('token') == bot.token: + request_body_dict = await request.json() + update = telebot.types.Update.de_json(request_body_dict) + bot.process_new_updates([update]) + return web.Response() + else: + return web.Response(status=403) + +app.router.add_post('/{token}/', handle) + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +def send_welcome(message): + bot.reply_to(message, + ("Hi there, I am EchoBot.\n" + "I am here to echo your kind words back to you.")) + + +# Handle all other messages +@bot.message_handler(func=lambda message: True, content_types=['text']) +def echo_message(message): + bot.reply_to(message, message.text) + + +# Remove webhook, it fails sometimes the set if there is a previous webhook +bot.remove_webhook() + +# Set webhook +bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH, + certificate=open(WEBHOOK_SSL_CERT, 'r')) + +# Build ssl context +context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) +context.load_cert_chain(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV) + +# Start aiohttp server +web.run_app( + app, + host=WEBHOOK_LISTEN, + port=WEBHOOK_PORT, + ssl_context=context, +) From f7fc538bd852824ed1ae52044480901ed3751572 Mon Sep 17 00:00:00 2001 From: Alex Kuznitsin Date: Mon, 30 Jan 2017 17:40:18 +0300 Subject: [PATCH 0120/1808] Non-ASCII chars for filename. Telegram doesn't accept rfc2231 styled filename. Using utf-8 directly. --- telebot/apihelper.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 7f0490132..f5fe0eea7 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- import requests +try: + from requests.packages.urllib3 import fields +except ImportError: + fields = None import telebot from telebot import types from telebot import util @@ -29,6 +33,8 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT + if files and fields: + fields.format_header_param = _no_encode(fields.format_header_param) if params: if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 @@ -568,6 +574,15 @@ def _convert_markup(markup): return markup +def _no_encode(func): + def wrapper(key, val): + if key == 'filename': + return '{0}={1}'.format(key, val) + else: + return func(key, val) + return wrapper + + class ApiException(Exception): """ This class represents an Exception thrown when a call to the Telegram API fails. From a84c0b984b73a7d79c150fe839c25e93ec4cec55 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 9 Feb 2017 16:49:15 +0300 Subject: [PATCH 0121/1808] Fixed typo: from plan to plain Line 184: from `return message.document.mime_type == 'text/plan'` to `return message.document.mime_type == 'text/plain'` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b788d4f8a..0ad5ae0bb 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,7 @@ def handle_text_doc(message): #Which could also be defined as: def test_message(message): - return message.document.mime_type == 'text/plan' + return message.document.mime_type == 'text/plain' @bot.message_handler(func=test_message, content_types=['document']) def handle_text_doc(message) From 8129b9511805ac02f76c30d33b120797c3ed5404 Mon Sep 17 00:00:00 2001 From: Cykrt Date: Wed, 15 Feb 2017 21:31:52 +0800 Subject: [PATCH 0122/1808] Fixed README.MD Fixed a problem that could not jump to "Using web hooks" correctly --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ad5ae0bb..23824577c 100644 --- a/README.md +++ b/README.md @@ -466,7 +466,7 @@ bot.set_update_listener(handle_messages) bot.polling() ``` -### Using webhooks +### Using web hooks When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages([update.message]) when you recieve it. There are some examples using webhooks in the *examples/webhook_examples* directory. From f8f0e0c343daba72100c9768f5775c0a36dfdb56 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 3 Mar 2017 11:00:03 +0800 Subject: [PATCH 0123/1808] Update Bots using this API list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0ad5ae0bb..9edf202da 100644 --- a/README.md +++ b/README.md @@ -547,5 +547,6 @@ Get help. Discuss. Chat. * [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz) * [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful. * [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic +* [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 76a48ffe82a51dda068ea3d39ef86b7d0206b3b2 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 4 Mar 2017 21:30:07 +0800 Subject: [PATCH 0124/1808] Bug fix for edited_channel_post_handler. --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ad0dfa590..248e9ef5b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -798,7 +798,7 @@ def decorator(handler): func=func, content_types=content_types, **kwargs) - self.add_edited_message_handler(handler_dict) + self.add_edited_channel_post_handler(handler_dict) return handler return decorator From 6515c7c4946227e798d7644aa2b1517a8322b402 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 4 Mar 2017 21:30:35 +0800 Subject: [PATCH 0125/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 67e77d69b..534fcf7e5 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.3.0', + version='2.3.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 19b8b4d2bf0c34fa5baedf9c77c668d620c204fd Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sun, 5 Mar 2017 20:17:46 +0800 Subject: [PATCH 0126/1808] Fix readme image url link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9edf202da..8c55827f7 100644 --- a/README.md +++ b/README.md @@ -336,7 +336,7 @@ tb.send_message(chat_id, "Choose one letter:", reply_markup=markup) ``` The last example yields this result: -![ReplyKeyboardMarkup](https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg "ReplyKeyboardMarkup") +![ReplyKeyboardMarkup](https://farm3.staticflickr.com/2933/32418726704_9ef76093cf_o_d.jpg "ReplyKeyboardMarkup") ```python # ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup @@ -353,7 +353,7 @@ tb.send_message(chat_id, "Send me another word:", reply_markup=markup) ``` ForceReply: -![ForceReply](https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg "ForceReply") +![ForceReply](https://farm4.staticflickr.com/3809/32418726814_d1baec0fc2_o_d.jpg "ForceReply") ### Inline Mode From 401a848927ea69a59fe549c1efd4094a6085a831 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 7 Mar 2017 21:43:14 +0300 Subject: [PATCH 0127/1808] Added option to disable CherryPy logging --- examples/webhook_examples/webhook_cherrypy_echo_bot.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/webhook_examples/webhook_cherrypy_echo_bot.py b/examples/webhook_examples/webhook_cherrypy_echo_bot.py index 83c9a01f2..d0f3da05f 100644 --- a/examples/webhook_examples/webhook_cherrypy_echo_bot.py +++ b/examples/webhook_examples/webhook_cherrypy_echo_bot.py @@ -73,6 +73,11 @@ def echo_message(message): bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH, certificate=open(WEBHOOK_SSL_CERT, 'r')) +# Disable CherryPy requests log +access_log = cherrypy.log.access_log +for handler in tuple(access_log.handlers): + access_log.removeHandler(handler) + # Start cherrypy server cherrypy.config.update({ 'server.socket_host': WEBHOOK_LISTEN, From 2c631b297396207ebc54c406a13a88af417f51a8 Mon Sep 17 00:00:00 2001 From: Andrew Developer Date: Thu, 23 Mar 2017 15:34:33 +0300 Subject: [PATCH 0128/1808] Added webhook example with Tornado --- .../webhook_tornado_echo_bot.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 examples/webhook_examples/webhook_tornado_echo_bot.py diff --git a/examples/webhook_examples/webhook_tornado_echo_bot.py b/examples/webhook_examples/webhook_tornado_echo_bot.py new file mode 100644 index 000000000..538b7b9f2 --- /dev/null +++ b/examples/webhook_examples/webhook_tornado_echo_bot.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This example shows webhook echo bot with Tornado web framework +# Documenation to Tornado: http://tornadoweb.org + +import telebot +import tornado.web +import tornado.ioloop +import tornado.httpserver +import tornado.options +import signal + +API_TOKEN = '' +WEBHOOK_CERT = "./cert.pem" +WEBHOOK_PKEY = "./pkey.pem" +WEBHOOK_HOST = "" +WEBHOOK_SECRET = " Date: Tue, 28 Mar 2017 16:24:28 +0300 Subject: [PATCH 0129/1808] Added IGNORECASE flag to message_handler Added re.IGNORECASE flag to message_handler, so it matches without chars case. --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 248e9ef5b..3b70be93e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -852,7 +852,7 @@ def _test_message_handler(self, message_handler, message): def _test_filter(self, filter, filter_value, message): test_cases = { 'content_types': lambda msg: msg.content_type in filter_value, - 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text), + 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, 'func': lambda msg: filter_value(msg) } From 662c69e09c56eba131931460f42608546956c3b9 Mon Sep 17 00:00:00 2001 From: Ihor Polyakov Date: Sun, 2 Apr 2017 14:56:53 +0700 Subject: [PATCH 0130/1808] RecursionError fix during sending files --- telebot/apihelper.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f5fe0eea7..90a101a87 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -3,8 +3,9 @@ import requests try: from requests.packages.urllib3 import fields + format_header_param = fields.format_header_param except ImportError: - fields = None + format_header_param = None import telebot from telebot import types from telebot import util @@ -33,8 +34,8 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT - if files and fields: - fields.format_header_param = _no_encode(fields.format_header_param) + if files and format_header_param: + fields.format_header_param = _no_encode(format_header_param) if params: if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 From e138d2e1ef7a6f1d74ae48e2ff088f7d878eff65 Mon Sep 17 00:00:00 2001 From: Artom-Kozincev Date: Thu, 6 Apr 2017 22:12:17 +0300 Subject: [PATCH 0131/1808] Add more accurate control over threads count --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3b70be93e..8d861d462 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -44,7 +44,7 @@ class TeleBot: getUpdates """ - def __init__(self, token, threaded=True, skip_pending=False): + def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): """ :param token: bot API token :return: Telebot object. @@ -75,7 +75,7 @@ def __init__(self, token, threaded=True, skip_pending=False): self.threaded = threaded if self.threaded: - self.worker_pool = util.ThreadPool() + self.worker_pool = util.ThreadPool(num_threads=num_threads) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) From fc65b30e3a14b00a081b8e3f5463529198e37b41 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sat, 15 Apr 2017 23:27:07 +0800 Subject: [PATCH 0132/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 66c1b472e..e9098265a 100644 --- a/README.md +++ b/README.md @@ -548,5 +548,6 @@ Get help. Discuss. Chat. * [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful. * [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic * [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video. +* [areajugonesbot](http://t.me/areajugonesbot)([link](http://t.me/areajugonesbot)) - The areajugonesbot sends news published on the videogames blog Areajugones to Telegram. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 8dca85b1f26102513d98d3526ee9006452c224d6 Mon Sep 17 00:00:00 2001 From: Santiago Castro Date: Tue, 18 Apr 2017 01:38:53 -0300 Subject: [PATCH 0133/1808] Fix broken Markdown headings --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9098265a..ab000acdd 100644 --- a/README.md +++ b/README.md @@ -397,7 +397,7 @@ def query_text(inline_query): print(e) ``` -###Working with entities: +### Working with entities: This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. Attributes: * `type` From 450ef42a836555987d8bd2a454af3526a213fad6 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 30 Apr 2017 19:40:27 +0800 Subject: [PATCH 0134/1808] Fix typo. --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 8cd9063fd..a8d2ef5dd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -865,7 +865,7 @@ def __init__(self, latitude, longitude): self.longitude = longitude def to_dic(self): - json_dic = {'latitude': self.latitudet, 'longitude': self.longitude} + json_dic = {'latitude': self.latitude, 'longitude': self.longitude} return json_dic From 8bf226e6bf267d724b1cd39d652d9eb7590e0958 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 30 Apr 2017 20:37:55 +0800 Subject: [PATCH 0135/1808] Bump version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 534fcf7e5..b8bf41a68 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.3.1', + version='2.3.2', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From f7cfb98b6089669647bd7fb2f24184942e73a64b Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Sun, 7 May 2017 17:37:03 +0300 Subject: [PATCH 0136/1808] Added option to delete messages. Added option to delete messages. Some bots do not support this method now, waiting for an official api release. --- telebot/__init__.py | 10 ++++++++++ telebot/apihelper.py | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3b70be93e..9d201b8ec 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -33,6 +33,7 @@ class TeleBot: getMe sendMessage forwardMessage + deleteMessage sendPhoto sendAudio sendDocument @@ -408,6 +409,15 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio return types.Message.de_json( apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification)) + def delete_message(self, chat_id, message_id): + """ + Use this method to delete message. Returns True on success. + :param chat_id: in which chat to delete + :param message_id: which message to delete + :return: API reply. + """ + return types.Message.de_json(apihelper.delete_message(self.token, chat_id, message_id)) + def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, disable_notification=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 90a101a87..c134cbc9e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -446,6 +446,12 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa return _make_request(token, method_url, params=payload) +def delete_message(token, chat_id=None, message_id=None): + method_url = r'deleteMessage' + payload = {'chat_id': chat_id, 'message_id': message_id} + return _make_request(token, method_url, params=payload) + + # Game def send_game(token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None): From b91eeb27526fcc48d44be1fbaca21ae15a87663e Mon Sep 17 00:00:00 2001 From: jiwidi Date: Sun, 7 May 2017 18:27:04 +0200 Subject: [PATCH 0137/1808] Update readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ab000acdd..9f446fd20 100644 --- a/README.md +++ b/README.md @@ -542,7 +542,8 @@ Get help. Discuss. Chat. * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch * [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. -* [dailypepebot](https://telegram.me/dailypepebot) by [*jaime*](https://github.com/jiwidi) - Get's you random pepe images and gives you their id, then you can call this image with the number. +* [dailypepebot](https://telegram.me/dailypepebot) by [*Jaime*](https://github.com/jiwidi/Dailypepe) - Get's you random pepe images and gives you their id, then you can call this image with the number. +* [DailyQwertee](https://t.me/DailyQwertee) by [*Jaime*](https://github.com/jiwidi/DailyQwertee) - Bot that manages a channel that sends qwertee daily tshirts every day at 00:00 * [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram * [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz) * [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful. From 89f515b12003efab471c9b08e66f037a9cebb997 Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Fri, 12 May 2017 01:13:40 +0300 Subject: [PATCH 0138/1808] deleteMessage returns Ok on success, not Message type --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9d201b8ec..38dfe3c7f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -416,7 +416,7 @@ def delete_message(self, chat_id, message_id): :param message_id: which message to delete :return: API reply. """ - return types.Message.de_json(apihelper.delete_message(self.token, chat_id, message_id)) + return apihelper.delete_message(self.token, chat_id, message_id) def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, disable_notification=None): From 34047c0121e3530499ccad70346eb022c8462e65 Mon Sep 17 00:00:00 2001 From: Kurbanov Bulat Date: Thu, 18 May 2017 13:55:55 +0400 Subject: [PATCH 0139/1808] Add methods to AsyncTeleBot --- telebot/__init__.py | 84 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index ec731de04..da2d0fe0b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -885,10 +885,38 @@ def __init__(self, *args, **kwargs): def get_me(self): return TeleBot.get_me(self) + @util.async() + def get_file(self, *args): + return TeleBot.get_file(self, *args) + + @util.async() + def download_file(self, *args): + return TeleBot.download_file(self, *args) + @util.async() def get_user_profile_photos(self, *args, **kwargs): return TeleBot.get_user_profile_photos(self, *args, **kwargs) + @util.async() + def get_chat(self, *args): + return TeleBot.get_chat(self, *args) + + @util.async() + def leave_chat(self, *args): + return TeleBot.leave_chat(self, *args) + + @util.async() + def get_chat_administrators(self, *args): + return TeleBot.get_chat_administrators(self, *args) + + @util.async() + def get_chat_members_count(self, *args): + return TeleBot.get_chat_members_count(self, *args) + + @util.async() + def get_chat_member(self, *args): + return TeleBot.get_chat_member(self, *args) + @util.async() def send_message(self, *args, **kwargs): return TeleBot.send_message(self, *args, **kwargs) @@ -897,6 +925,10 @@ def send_message(self, *args, **kwargs): def forward_message(self, *args, **kwargs): return TeleBot.forward_message(self, *args, **kwargs) + @util.async() + def delete_message(self, *args): + return TeleBot.delete_message(self, *args) + @util.async() def send_photo(self, *args, **kwargs): return TeleBot.send_photo(self, *args, **kwargs) @@ -905,6 +937,10 @@ def send_photo(self, *args, **kwargs): def send_audio(self, *args, **kwargs): return TeleBot.send_audio(self, *args, **kwargs) + @util.async() + def send_voice(self, *args, **kwargs): + return TeleBot.send_voice(self, *args, **kwargs) + @util.async() def send_document(self, *args, **kwargs): return TeleBot.send_document(self, *args, **kwargs) @@ -921,6 +957,54 @@ def send_video(self, *args, **kwargs): def send_location(self, *args, **kwargs): return TeleBot.send_location(self, *args, **kwargs) + @util.async() + def send_venue(self, *args, **kwargs): + return TeleBot.send_venue(self, *args, **kwargs) + + @util.async() + def send_contact(self, *args, **kwargs): + return TeleBot.send_contact(self, *args, **kwargs) + @util.async() def send_chat_action(self, *args, **kwargs): return TeleBot.send_chat_action(self, *args, **kwargs) + + @util.async() + def kick_chat_member(self, *args): + return TeleBot.kick_chat_member(self, *args) + + @util.async() + def unban_chat_member(self, *args): + return TeleBot.unban_chat_member(self, *args) + + @util.async() + def edit_message_text(self, *args, **kwargs): + return TeleBot.edit_message_text(self, *args, **kwargs) + + @util.async() + def edit_message_reply_markup(self, *args, **kwargs): + return TeleBot.edit_message_reply_markup(self, *args, **kwargs) + + @util.async() + def send_game(self, *args, **kwargs): + return TeleBot.send_game(self, *args, **kwargs) + + @util.async() + def set_game_score(self, *args, **kwargs): + return TeleBot.set_game_score(self, *args, **kwargs) + + @util.async() + def get_game_high_scores(self, *args, **kwargs): + return TeleBot.get_game_high_scores(self, *args, **kwargs) + + @util.async() + def edit_message_caption(self, *args, **kwargs): + return TeleBot.edit_message_caption(self, *args, **kwargs) + + @util.async() + def answer_inline_query(self, *args, **kwargs): + return TeleBot.answer_inline_query(self, *args, **kwargs) + + @util.async() + def answer_callback_query(self, *args, **kwargs): + return TeleBot.answer_callback_query(self, *args, **kwargs) From 35214b12707576e1adc11a712522dceca2129505 Mon Sep 17 00:00:00 2001 From: i32ropie Date: Thu, 18 May 2017 23:40:10 +0200 Subject: [PATCH 0140/1808] Added language code for users --- telebot/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index a8d2ef5dd..497996f02 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -168,13 +168,15 @@ def de_json(cls, json_string): first_name = obj['first_name'] last_name = obj.get('last_name') username = obj.get('username') - return cls(id, first_name, last_name, username) + language_code = obj.get('language_code') + return cls(id, first_name, last_name, username, language_code) - def __init__(self, id, first_name, last_name=None, username=None): + def __init__(self, id, first_name, last_name=None, username=None, language_code=None): self.id = id self.first_name = first_name self.username = username self.last_name = last_name + self.language_code = language_code class GroupChat(JsonDeserializable): From 6cda8d052c8639160ae481c0c8159e0e943a31d8 Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Fri, 19 May 2017 17:19:15 +0300 Subject: [PATCH 0141/1808] VideoNote support Send and recieve round video messages. Support for send_video_note metod and video_note content type. --- README.md | 7 ++++++- telebot/__init__.py | 21 +++++++++++++++++++++ telebot/apihelper.py | 26 +++++++++++++++++++++++++- telebot/types.py | 25 +++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9f446fd20..e4f532204 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)). The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings: -`text`, `audio`, `document`, `photo`, `sticker`, `video`, `voice`, `location`, `contact`, `new_chat_member`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`. +`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_member`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`. ### Methods @@ -284,6 +284,11 @@ video = open('/tmp/video.mp4', 'rb') tb.send_video(chat_id, video) tb.send_video(chat_id, "FILEID") +# sendVideoNote +videonote = open('/tmp/videonote.mp4', 'rb') +tb.send_video(chat_id, videonote) +tb.send_video(chat_id, "FILEID") + # sendLocation tb.send_location(chat_id, lat, lon) diff --git a/telebot/__init__.py b/telebot/__init__.py index da2d0fe0b..bac8206b2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -39,6 +39,7 @@ class TeleBot: sendDocument sendSticker sendVideo + sendVideoNote sendLocation sendChatAction getUserProfilePhotos @@ -510,6 +511,22 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, disable_notification, timeout)) + def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, + disable_notification=None, timeout=None): + """ + Use this method to send video files, Telegram clients support mp4 videos. + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) + :param reply_to_message_id: + :param reply_markup: + :return: + """ + return types.Message.de_json( + apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, + disable_notification, timeout)) + def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None, disable_notification=None): """ @@ -953,6 +970,10 @@ def send_sticker(self, *args, **kwargs): def send_video(self, *args, **kwargs): return TeleBot.send_video(self, *args, **kwargs) + @util.async() + def send_video_note(self, *args, **kwargs): + return TeleBot.send_video_note(self, *args, **kwargs) + @util.async() def send_location(self, *args, **kwargs): return TeleBot.send_location(self, *args, **kwargs) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c134cbc9e..72be649dd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -328,6 +328,31 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess return _make_request(token, method_url, params=payload, files=files, method='post') +def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, + disable_notification=None, timeout=None): + method_url = r'sendVideoNote' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(data): + files = {'video_note': data} + files['video_note']['length'] = 639 # seems like it is MAX length size + else: + payload['video_note'] = data + if duration: + payload['duration'] = duration + if length: + payload['length'] = length + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + if disable_notification: + payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout + return _make_request(token, method_url, params=payload, files=files, method='post') + + def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): method_url = r'sendAudio' @@ -525,7 +550,6 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None, """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned. Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via BotFather and accept the terms. Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter. - :param token: Bot's token (you don't need to fill this) :param callback_query_id: Unique identifier for the query to be answered :param text: (Optional) Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters diff --git a/telebot/types.py b/telebot/types.py index 497996f02..2c5fdb9fc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -263,6 +263,9 @@ def de_json(cls, json_string): if 'video' in obj: opts['video'] = Video.de_json(obj['video']) content_type = 'video' + if 'video_note' in obj: + opts['video_note'] = VideoNote.de_json(obj['video_note']) + content_type = 'video_note' if 'voice' in obj: opts['voice'] = Audio.de_json(obj['voice']) content_type = 'voice' @@ -342,6 +345,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.photo = None self.sticker = None self.video = None + self.video_note = None self.voice = None self.caption = None self.contact = None @@ -507,6 +511,27 @@ def __init__(self, file_id, width, height, duration, thumb=None, mime_type=None, self.file_size = file_size +class VideoNote(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + file_id = obj['file_id'] + length = obj['length'] + duration = obj['duration'] + thumb = None + if 'thumb' in obj: + thumb = PhotoSize.de_json(obj['thumb']) + file_size = obj.get('file_size') + return cls(file_id, length, duration, thumb, file_size) + + def __init__(self, file_id, length, duration, thumb=None, file_size=None): + self.file_id = file_id + self.length = length + self.duration = duration + self.thumb = thumb + self.file_size = file_size + + class Contact(JsonDeserializable): @classmethod def de_json(cls, json_string): From 443d81d4db5e72f41d470c6183db342aaae0aba5 Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Fri, 19 May 2017 18:08:07 +0300 Subject: [PATCH 0142/1808] FIX: Can't edit file bytes --- telebot/apihelper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 72be649dd..f72ce79dd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -335,13 +335,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m files = None if not util.is_string(data): files = {'video_note': data} - files['video_note']['length'] = 639 # seems like it is MAX length size else: payload['video_note'] = data if duration: payload['duration'] = duration if length: payload['length'] = length + else: + payload['length'] = 639 # seems like it is MAX length size if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: From 3f5596ddce5a94aaf2cfda11139aab9931c74f01 Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Sun, 21 May 2017 14:27:31 +0300 Subject: [PATCH 0143/1808] new_chat_members content type and new send_action actions --- telebot/__init__.py | 2 +- telebot/types.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index bac8206b2..dad016427 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -576,7 +576,7 @@ def send_chat_action(self, chat_id, action): its typing status). :param chat_id: :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', - 'record_audio', 'upload_audio', 'upload_document', 'find_location'. + 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', 'upload_video_note'. :return: API reply. :type: boolean """ return apihelper.send_chat_action(self.token, chat_id, action) diff --git a/telebot/types.py b/telebot/types.py index 2c5fdb9fc..475824a77 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -283,6 +283,9 @@ def de_json(cls, json_string): if 'new_chat_member' in obj: opts['new_chat_member'] = User.de_json(obj['new_chat_member']) content_type = 'new_chat_member' + if 'new_chat_members' in obj: + opts['new_chat_members'] = obj['new_chat_members'] + content_type = 'new_chat_members' if 'left_chat_member' in obj: opts['left_chat_member'] = User.de_json(obj['left_chat_member']) content_type = 'left_chat_member' From 9134e8dd1a6a1cff30fc68a666659f5069707715 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 21 May 2017 19:58:00 +0800 Subject: [PATCH 0144/1808] Add send video note test case. --- tests/test_telebot.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index b9b460c82..8c0cf760d 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -403,3 +403,9 @@ def test_is_string_string(self): def test_not_string(self): i1 = 10 assert not util.is_string(i1) + + def test_send_video_note(self): + file_data = open('./test_data/test_video.mp4', 'rb') + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_video_note(CHAT_ID, file_data) + assert ret_msg.message_id From 5ed333492b2ac59c04beb3c9dd1633d881c969a0 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 21 May 2017 21:45:12 +0800 Subject: [PATCH 0145/1808] All payment type done. --- telebot/apihelper.py | 13 ++++ telebot/types.py | 170 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f72ce79dd..b77515eab 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -544,6 +544,19 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m payload['inline_message_id'] = inline_message_id return _make_request(token, method_url, params=payload) +# Payments (https://core.telegram.org/bots/api#payments) + +def send_invoice(): + # TODO + pass + +def answer_shippingQuery(): + # TODO + pass + +def answer_pre_checkout_query(): + # TODO + pass # InlineQuery diff --git a/telebot/types.py b/telebot/types.py index 475824a77..1fe66de0d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -148,9 +148,11 @@ def de_json(cls, json_string): max_connections = obj['max_connections'] if 'allowed_updates' in obj: allowed_updates = obj['allowed_updates'] - return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, max_connections, allowed_updates) + return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, + max_connections, allowed_updates) - def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, max_connections, allowed_updates): + def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, + max_connections, allowed_updates): self.url = url self.has_custom_certificate = has_custom_certificate self.pending_update_count = pending_update_count @@ -1608,3 +1610,167 @@ def __init__(self, position, user, score): self.position = position self.user = user self.score = score + + +# Payments + +class LabeledPrice(JsonSerializable): + def __init__(self, label, amount): + self.label = label + self.amount = amount + + def to_json(self): + return json.dumps(self.to_dic()) + + def to_dic(self): + return {'label': self.label, 'amount': self.amount} + + +class Invoice(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + title = obj['title'] + description = obj['description'] + start_parameter = obj['start_parameter'] + currency = obj['currency'] + total_amount = obj['total_amount'] + return cls(title, description, start_parameter, currency, total_amount) + + def __init__(self, title, description, start_parameter, currency, total_amount): + self.title = title + self.description = description + self.start_parameter = start_parameter + self.currency = currency + self.total_amount = total_amount + + +class ShippingAddress(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + country_code = obj['country_code'] + state = obj['state'] + city = obj['city'] + street_line1 = obj['street_line1'] + street_line2 = obj['street_line2'] + post_code = obj['post_code'] + return cls(country_code, state, city, street_line1, street_line2, post_code) + + def __init__(self, country_code, state, city, street_line1, street_line2, post_code): + self.country_code = country_code + self.state = state + self.city = city + self.street_line1 = street_line1 + self.street_line2 = street_line2 + self.post_code = post_code + + +class OrderInfo(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + name = obj.get('name') + phone_number = obj.get('phone_number') + email = obj.get('email') + shipping_address = None + if 'shipping_address' in obj: + shipping_address = ShippingAddress.de_json(obj['shipping_address']) + return cls(name, phone_number, email, shipping_address) + + def __init__(self, name, phone_number, email, shipping_address): + self.name = name + self.phone_number = phone_number + self.email = email + self.shipping_address = shipping_address + + +class ShippingOption(JsonSerializable): + def __init__(self, id, title): + self.id = id + self.title = title + self.prices = [] + + def add_price(self, *args): + """ + Add LabeledPrice to ShippingOption + :param args: LabeledPrices + """ + for price in args: + self.prices.append(price) + + def to_json(self): + price_list = [] + for p in self.prices: + price_list.append(p.to_dic()) + json_dict = {'id': self.id, 'title': self.title, 'prices': price_list} + return json_dict + + +class SuccessfulPayment(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + currency = obj['currency'] + total_amount = obj['total_amount'] + invoice_payload = obj['invoice_payload'] + shipping_option_id = obj.get('shipping_option_id') + order_info = None + if 'order_info' in obj: + order_info = OrderInfo.de_json(obj['order_info']) + telegram_payment_charge_id = obj['telegram_payment_charge_id'] + provider_payment_charge_id = obj['provider_payment_charge_id'] + return cls(currency, total_amount, invoice_payload, shipping_option_id, order_info, + telegram_payment_charge_id, provider_payment_charge_id) + + def __init__(self, currency, total_amount, invoice_payload, shipping_option_id, order_info, + telegram_payment_charge_id, provider_payment_charge_id): + self.currency = currency + self.total_amount = total_amount + self.invoice_payload = invoice_payload + self.shipping_option_id = shipping_option_id + self.order_info = order_info + self.telegram_payment_charge_id = telegram_payment_charge_id + self.provider_payment_charge_id = provider_payment_charge_id + + +class ShippingQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + id = obj['id'] + from_user = User.de_json(obj['from']) + invoice_payload = obj['invoice_payload'] + shipping_address = ShippingAddress.de_json(obj['shipping_address']) + return cls(id, from_user, invoice_payload, shipping_address) + + def __init__(self, id, from_user, invoice_payload, shipping_address): + self.id = id + self.from_user = from_user + self.invoice_payload = invoice_payload + self.shipping_address = shipping_address + + +class PreCheckoutQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + id = obj['id'] + from_user = User.de_json(obj['from']) + currency = obj['currency'] + total_amount = obj['total_amount'] + invoice_payload = obj['invoice_payload'] + shipping_option_id = obj.get('shipping_option_id') + order_info = None + if 'order_info' in obj: + order_info = OrderInfo.de_json(obj['order_info']) + return cls(id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info) + + def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info): + self.id = id + self.from_user = from_user + self.currency = currency + self.total_amount = total_amount + self.invoice_payload = invoice_payload + self.shipping_option_id = shipping_option_id + self.order_info = order_info From 12791e1366a25380e4028db5d3dca3581593cd96 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 21 May 2017 21:52:56 +0800 Subject: [PATCH 0146/1808] Add payments type to update and message. --- telebot/types.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 1fe66de0d..68c1aa475 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -101,6 +101,8 @@ def de_json(cls, json_type): inline_query = None chosen_inline_result = None callback_query = None + shipping_query = None + pre_checkout_query = None if 'message' in obj: message = Message.de_json(obj['message']) if 'edited_message' in obj: @@ -115,11 +117,15 @@ def de_json(cls, json_type): chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result']) if 'callback_query' in obj: callback_query = CallbackQuery.de_json(obj['callback_query']) + if 'shipping_query' in obj: + shipping_query = ShippingQuery.de_json(obj['shipping_query']) + if 'pre_checkout_query' in obj: + pre_checkout_query = PreCheckoutQuery.de_json(obj['pre_checkout_query']) return cls(update_id, message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query) def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query): + chosen_inline_result, callback_query, shipping_query, pre_checkout_query): self.update_id = update_id self.edited_message = edited_message self.message = message @@ -129,6 +135,8 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.inline_query = inline_query self.chosen_inline_result = chosen_inline_result self.callback_query = callback_query + self.shipping_query = shipping_query + self.pre_checkout_query = pre_checkout_query class WebhookInfo(JsonDeserializable): @@ -309,6 +317,12 @@ def de_json(cls, json_string): opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] if 'pinned_message' in obj: opts['pinned_message'] = Message.de_json(obj['pinned_message']) + if 'invoice' in obj: + opts['invoice'] = Invoice.de_json(obj['invoice']) + content_type = 'invoice' + if 'successful_payment' in obj: + opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment']) + content_type = 'successful_payment' return cls(message_id, from_user, date, chat, content_type, opts) @classmethod @@ -367,6 +381,8 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.migrate_to_chat_id = None self.migrate_from_chat_id = None self.pinned_message = None + self.invoice = None + self.successful_payment = None for key in options: setattr(self, key, options[key]) From 3a10c907998b523630f6046341560f5dd2c2b4a3 Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Wed, 24 May 2017 01:23:52 +0300 Subject: [PATCH 0147/1808] Payments methods --- telebot/apihelper.py | 103 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 10 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b77515eab..afc2724c2 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -472,7 +472,7 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa return _make_request(token, method_url, params=payload) -def delete_message(token, chat_id=None, message_id=None): +def delete_message(token, chat_id, message_id): method_url = r'deleteMessage' payload = {'chat_id': chat_id, 'message_id': message_id} return _make_request(token, method_url, params=payload) @@ -546,17 +546,100 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m # Payments (https://core.telegram.org/bots/api#payments) -def send_invoice(): - # TODO - pass +def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter=None, + photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, + need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None): + """ + Use this method to send invoices. On success, the sent Message is returned. + :param token: Bot's token (you don't need to fill this) + :param chat_id: Unique identifier for the target private chat + :param title: Product name + :param description: Product description + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :param provider_token: Payments provider token, obtained via @Botfather + :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for. + :param photo_size: Photo size + :param photo_width: Photo width + :param photo_height: Photo height + :param need_name: Pass True, if you require the user's full name to complete the order + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :param need_email: Pass True, if you require the user's email to complete the order + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :param is_flexible: Pass True, if the final price depends on the shipping method + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_markup: 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' + payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, 'provider_token': provider_token, 'currency': currency, 'prices': prices} + if start_parameter: + payload['start_parameter'] = start_parameter + if photo_url: + payload['photo_url'] = photo_url + if photo_size: + payload['photo_size'] = photo_size + if photo_width: + payload['photo_width'] = photo_width + if photo_height: + payload['photo_height'] = photo_height + if need_name: + payload['need_name'] = need_name + if need_phone_number: + payload['need_phone_number'] = need_phone_number + if need_email: + payload['need_email'] = need_email + if need_shipping_address: + payload['need_shipping_address'] = need_shipping_address + if is_flexible: + payload['is_flexible'] = is_flexible + if disable_notification: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = reply_markup + return _make_request(token, method_url, params=payload) + -def answer_shippingQuery(): - # TODO - pass +def answer_shippingQuery(token, shipping_query_id, ok, shipping_options=None, error_message=None): + """ + If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned. + :param token: Bot's token (you don't need to fill this) + :param shipping_query_id: Unique identifier for the query to be answered + :param ok: Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible) + :param shipping_options: Required if ok is True. A JSON-serialized array of available shipping options. + :param error_message: Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. + :return: + """ + method_url = 'answerShippingQuery' + payload = {'shipping_query_id': shipping_query_id, 'ok': ok} + if shipping_options: + payload['reply_markup'] = shipping_options + if error_message: + payload['reply_markup'] = error_message + return _make_request(token, method_url, params=payload) + + +def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=None): + """ + Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the field pre_checkout_query. Use this method to respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. + :param token: Bot's token (you don't need to fill this) + :param pre_checkout_query_id: Unique identifier for the query to be answered + :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. + :param error_message: Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user. + :return: + """ + method_url = 'answerPreCheckoutQuery' + payload = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok} + if error_message: + payload['error_message'] = error_message + return _make_request(token, method_url, params=payload) -def answer_pre_checkout_query(): - # TODO - pass # InlineQuery From cf287af54990dac1f0c34bda9e3c717455456a19 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 10:56:58 +0800 Subject: [PATCH 0148/1808] Add payment method. PEP8. Refactor. --- telebot/__init__.py | 37 ++++++++++++++++++++++++++++++++++--- telebot/apihelper.py | 33 +++++++++++++++++++-------------- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index dad016427..4359a9cbb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -512,7 +512,7 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag disable_notification, timeout)) def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + disable_notification=None, timeout=None): """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -525,7 +525,7 @@ def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_me """ return types.Message.de_json( apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout)) + disable_notification, timeout)) def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None, disable_notification=None): @@ -613,7 +613,8 @@ def send_game(self, chat_id, game_short_name, disable_notification=None, reply_t reply_markup) return types.Message.de_json(result) - def set_game_score(self, user_id, score, force=None,chat_id=None, message_id=None, inline_message_id=None, edit_message=None): + def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None, + edit_message=None): result = apihelper.set_game_score(self.token, user_id, score, force, chat_id, message_id, inline_message_id, edit_message) if type(result) == bool: @@ -627,6 +628,24 @@ def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_me ret.append(types.GameHighScore.de_json(r)) return ret + def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices, + start_parameter=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, + is_flexible=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None): + result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token, + currency, prices, start_parameter, photo_url, photo_size, photo_width, + photo_height, + need_name, need_phone_number, need_email, need_shipping_address, is_flexible, + disable_notification, reply_to_message_id, reply_markup) + return types.Message.de_json(result) + + def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): + return apihelper.answer_shippingQuery(self.token, shipping_query_id, ok, shipping_options, error_message) + + def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None): + return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) + def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, reply_markup) @@ -1018,6 +1037,18 @@ def set_game_score(self, *args, **kwargs): def get_game_high_scores(self, *args, **kwargs): return TeleBot.get_game_high_scores(self, *args, **kwargs) + @util.async() + def send_invoice(self, *args, **kwargs): + return TeleBot.send_invoice(self, *args, **kwargs) + + @util.async() + def answer_shipping_query(self, *args, **kwargs): + return TeleBot.answer_shipping_query(self, *args, **kwargs) + + @util.async() + def answer_pre_checkout_query(self, *args, **kwargs): + return TeleBot.answer_pre_checkout_query(self, *args, **kwargs) + @util.async() def edit_message_caption(self, *args, **kwargs): return TeleBot.edit_message_caption(self, *args, **kwargs) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index afc2724c2..b9877d9d4 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- import requests + try: from requests.packages.urllib3 import fields + format_header_param = fields.format_header_param except ImportError: format_header_param = None @@ -39,7 +41,8 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas if params: if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 - result = req_session.request(method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout)) + result = req_session.request(method, request_url, params=params, files=files, + timeout=(connect_timeout, read_timeout)) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] @@ -544,11 +547,12 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m payload['inline_message_id'] = inline_message_id return _make_request(token, method_url, params=payload) + # Payments (https://core.telegram.org/bots/api#payments) -def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter=None, - photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, - need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, +def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices, + start_parameter=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): """ Use this method to send invoices. On success, the sent Message is returned. @@ -556,7 +560,7 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t :param chat_id: Unique identifier for the target private chat :param title: Product name :param description: Product description - :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. :param provider_token: Payments provider token, obtained via @Botfather :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) @@ -576,9 +580,9 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t :return: """ method_url = r'sendInvoice' - payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, 'provider_token': provider_token, 'currency': currency, 'prices': prices} - if start_parameter: - payload['start_parameter'] = start_parameter + payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, + 'provider_token': provider_token, 'start_parameter': start_parameter, 'currency': currency, + 'prices': _convert_list_json_serializable(prices)} if photo_url: payload['photo_url'] = photo_url if photo_size: @@ -602,11 +606,11 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: - payload['reply_markup'] = reply_markup + payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) -def answer_shippingQuery(token, shipping_query_id, ok, shipping_options=None, error_message=None): +def answer_shipping_query(token, shipping_query_id, ok, shipping_options=None, error_message=None): """ If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned. :param token: Bot's token (you don't need to fill this) @@ -619,9 +623,9 @@ def answer_shippingQuery(token, shipping_query_id, ok, shipping_options=None, er method_url = 'answerShippingQuery' payload = {'shipping_query_id': shipping_query_id, 'ok': ok} if shipping_options: - payload['reply_markup'] = shipping_options + payload['reply_markup'] = _convert_list_json_serializable(shipping_options) if error_message: - payload['reply_markup'] = error_message + payload['error_message'] = error_message return _make_request(token, method_url, params=payload) @@ -672,7 +676,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None, def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, switch_pm_text=None, switch_pm_parameter=None): method_url = 'answerInlineQuery' - payload = {'inline_query_id': inline_query_id, 'results': _convert_inline_results(results)} + payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)} if cache_time: payload['cache_time'] = cache_time if is_personal: @@ -686,7 +690,7 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per return _make_request(token, method_url, params=payload, method='post') -def _convert_inline_results(results): +def _convert_list_json_serializable(results): ret = '' for r in results: if isinstance(r, types.JsonSerializable): @@ -708,6 +712,7 @@ def wrapper(key, val): return '{0}={1}'.format(key, val) else: return func(key, val) + return wrapper From a8e60b28e00eb7a32e6ea0737bd656ad74e07d43 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 11:14:08 +0800 Subject: [PATCH 0149/1808] Fix requirement params. --- telebot/__init__.py | 2 +- telebot/apihelper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4359a9cbb..8f6e05968 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -629,7 +629,7 @@ def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_me return ret def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices, - start_parameter=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, + start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b9877d9d4..b5372cdf6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -551,7 +551,7 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m # Payments (https://core.telegram.org/bots/api#payments) def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices, - start_parameter=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, + start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): """ From 9025be0ef2a4e856a9aa75155843334ff1d6496f Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 11:22:40 +0800 Subject: [PATCH 0150/1808] Add handlers. --- telebot/__init__.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8f6e05968..ba12944b6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -74,6 +74,8 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): self.inline_handlers = [] self.chosen_inline_handlers = [] self.callback_query_handlers = [] + self.shipping_query_handlers = [] + self.pre_checkout_query_handlers = [] self.threaded = threaded if self.threaded: @@ -146,6 +148,9 @@ def process_new_updates(self, updates): new_inline_querys = [] new_chosen_inline_results = [] new_callback_querys = [] + new_shipping_query = [] + new_pre_checkout_query= [] + for update in updates: if update.update_id > self.last_update_id: self.last_update_id = update.update_id @@ -163,6 +168,11 @@ def process_new_updates(self, updates): new_chosen_inline_results.append(update.chosen_inline_result) if update.callback_query: new_callback_querys.append(update.callback_query) + if update.shipping_query: + new_shipping_query.append(update.shipping_query) + if update.pre_checkout_query: + new_pre_checkout_query.append(update.pre_checkout_query) + logger.debug('Received {0} new updates'.format(len(updates))) if len(new_messages) > 0: self.process_new_messages(new_messages) @@ -885,6 +895,28 @@ def decorator(handler): def add_callback_query_handler(self, handler_dict): self.callback_query_handlers.append(handler_dict) + def shipping_query_handler(self, func, **kwargs): + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_shipping_query_handler(handler_dict) + return handler + + return decorator + + def add_shipping_query_handler(self, handler_dict): + self.shipping_query_handlers.append(handler_dict) + + def pre_checkout_query_handler(self, func, **kwargs): + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_pre_checkout_queryhandler(handler_dict) + return handler + + return decorator + + def add_pre_checkout_queryhandler(self, handler_dict): + self.pre_checkout_query_handlers.append(handler_dict) + def _test_message_handler(self, message_handler, message): for filter, filter_value in six.iteritems(message_handler['filters']): if filter_value is None: From 84b1aca939f783fe90422d9deefb0c9192d8c75d Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 11:23:37 +0800 Subject: [PATCH 0151/1808] Fix method name. --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ba12944b6..8b6953d29 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -914,7 +914,7 @@ def decorator(handler): return decorator - def add_pre_checkout_queryhandler(self, handler_dict): + def add_pre_checkout_query_handler(self, handler_dict): self.pre_checkout_query_handlers.append(handler_dict) def _test_message_handler(self, message_handler, message): From 708635e42054aa35fbec4da7c8ab69bcf975a2df Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 11:45:44 +0800 Subject: [PATCH 0152/1808] Fix handler. --- telebot/__init__.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8b6953d29..381162d3f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -148,8 +148,8 @@ def process_new_updates(self, updates): new_inline_querys = [] new_chosen_inline_results = [] new_callback_querys = [] - new_shipping_query = [] - new_pre_checkout_query= [] + new_shipping_querys = [] + new_pre_checkout_querys = [] for update in updates: if update.update_id > self.last_update_id: @@ -169,9 +169,9 @@ def process_new_updates(self, updates): if update.callback_query: new_callback_querys.append(update.callback_query) if update.shipping_query: - new_shipping_query.append(update.shipping_query) + new_shipping_querys.append(update.shipping_query) if update.pre_checkout_query: - new_pre_checkout_query.append(update.pre_checkout_query) + new_pre_checkout_querys.append(update.pre_checkout_query) logger.debug('Received {0} new updates'.format(len(updates))) if len(new_messages) > 0: @@ -188,6 +188,10 @@ def process_new_updates(self, updates): self.process_new_chosen_inline_query(new_chosen_inline_results) if len(new_callback_querys) > 0: self.process_new_callback_query(new_callback_querys) + if len(new_pre_checkout_querys) > 0: + self.process_new_pre_checkout_query(new_pre_checkout_querys) + if len(new_shipping_querys) > 0: + self.process_new_shipping_query(new_shipping_querys) def process_new_messages(self, new_messages): self._append_pre_next_step_handler() @@ -214,6 +218,12 @@ def process_new_chosen_inline_query(self, new_chosen_inline_querys): def process_new_callback_query(self, new_callback_querys): self._notify_command_handlers(self.callback_query_handlers, new_callback_querys) + def process_new_shipping_query(self, new_shipping_querys): + self._notify_command_handlers(self.shipping_query_handlers, new_shipping_querys) + + def process_new_pre_checkout_query(self, pre_checkout_querys): + self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_querys) + def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) @@ -909,7 +919,7 @@ def add_shipping_query_handler(self, handler_dict): def pre_checkout_query_handler(self, func, **kwargs): def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_pre_checkout_queryhandler(handler_dict) + self.add_pre_checkout_query_handler(handler_dict) return handler return decorator From b2449e64c2fd61d12d59136bc9d18a3022fa44c1 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 11:48:16 +0800 Subject: [PATCH 0153/1808] Add pay in inline keyboard btn, --- telebot/types.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 68c1aa475..8eef60883 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -794,13 +794,14 @@ def to_dic(self): class InlineKeyboardButton(JsonSerializable): def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, - switch_inline_query_current_chat=None, callback_game=None): + switch_inline_query_current_chat=None, callback_game=None, pay = None): self.text = text self.url = url self.callback_data = callback_data self.switch_inline_query = switch_inline_query self.switch_inline_query_current_chat = switch_inline_query_current_chat self.callback_game = callback_game + self.pay = pay def to_json(self): return json.dumps(self.to_dic()) @@ -817,6 +818,8 @@ def to_dic(self): json_dic['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat if self.callback_game is not None: json_dic['callback_game'] = self.callback_game + if self.pay is not None: + json_dic['pay'] = self.pay return json_dic From 639218b3bf825e1b871615a96c1fb9c86fcdd040 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 13:27:13 +0800 Subject: [PATCH 0154/1808] New fields gif_duration in InlineQueryResultGif and mpeg4_duration in InlineQueryResultMpeg4Gif. --- telebot/types.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 8eef60883..fd1cb6816 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -794,7 +794,7 @@ def to_dic(self): class InlineKeyboardButton(JsonSerializable): def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, - switch_inline_query_current_chat=None, callback_game=None, pay = None): + switch_inline_query_current_chat=None, callback_game=None, pay=None): self.text = text self.url = url self.callback_data = callback_data @@ -1077,7 +1077,7 @@ def to_json(self): 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): + reply_markup=None, input_message_content=None, gif_duration=None): """ Represents a link to an animated GIF file. :param id: Unique identifier for this result, 1-64 bytes. @@ -1101,6 +1101,7 @@ def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, titl 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} @@ -1116,12 +1117,14 @@ def to_json(self): json_dict['reply_markup'] = self.reply_markup.to_dic() if self.input_message_content: json_dict['input_message_content'] = self.input_message_content.to_dic() + if self.gif_duration: + json_dict['gif_duration'] = self.gif_duration return json.dumps(json_dict) class InlineQueryResultMpeg4Gif(JsonSerializable): def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, title=None, caption=None, - reply_markup=None, input_message_content=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). :param id: Unique identifier for this result, 1-64 bytes @@ -1145,6 +1148,7 @@ def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None self.caption = caption 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} @@ -1160,6 +1164,8 @@ def to_json(self): json_dict['reply_markup'] = self.reply_markup.to_dic() if self.input_message_content: json_dict['input_message_content'] = self.input_message_content.to_dic() + if self.mpeg4_duration: + json_dict['mpeg4_duration '] = self.mpeg4_duration return json.dumps(json_dict) From 754ca7739465c92ffcab057b8e137be5a0797170 Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Thu, 25 May 2017 21:00:48 +0300 Subject: [PATCH 0155/1808] Add payments bot example --- examples/payments_example.py | 83 ++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 examples/payments_example.py diff --git a/examples/payments_example.py b/examples/payments_example.py new file mode 100644 index 000000000..3e829553b --- /dev/null +++ b/examples/payments_example.py @@ -0,0 +1,83 @@ +import telebot +from telebot.types import LabeledPrice +from telebot.types import ShippingOption + +token = '1234567890:AAAABBBBCCCCDDDDeeeeFFFFgggGHHHH' +provider_token = '1234567890:TEST:AAAABBBBCCCCDDDD' # @BotFather -> Bot Settings -> Payments +bot = telebot.TeleBot(token) + +# More about Payments: https://core.telegram.org/bots/payments + +prices = [LabeledPrice(label='Working Time Machine', amount=5750), LabeledPrice('Gift wrapping', 500)] + +shipping_options = [ + ShippingOption(id='instant', title='WorldWide Teleporter').add_price(LabeledPrice('Teleporter', 1000)), + ShippingOption(id='pickup', title='Local pickup').add_price(LabeledPrice('Pickup', 300))] + + +@bot.message_handler(commands=['start']) +def command_start(message): + bot.send_message(message.chat.id, + "Hello, I'm the demo merchant bot." + " I can sell you a Time Machine." + " Use /buy to order one, /terms for Terms and Conditions") + + +@bot.message_handler(commands=['terms']) +def command_terms(message): + bot.send_message(message.chat.id, + 'Thank you for shopping with our demo bot. We hope you like your new time machine!\n' + '1. If your time machine was not delivered on time, please rethink your concept of time and try again.\n' + '2. If you find that your time machine is not working, kindly contact our future service workshops on Trappist-1e.' + ' They will be accessible anywhere between May 2075 and November 4000 C.E.\n' + '3. If you would like a refund, kindly apply for one yesterday and we will have sent it to you immediately.') + + +@bot.message_handler(commands=['buy']) +def command_pay(message): + bot.send_message(message.chat.id, + "Real cards won't work with me, no money will be debited from your account." + " Use this test card number to pay for your Time Machine: `4242 4242 4242 4242`" + "\n\nThis is your demo invoice:", parse_mode='Markdown') + bot.send_invoice(message.chat.id, title='Working Time Machine', + description='Want to visit your great-great-great-grandparents?' + ' Make a fortune at the races?' + ' Shake hands with Hammurabi and take a stroll in the Hanging Gardens?' + ' Order our Working Time Machine today!', + provider_token=provider_token, + currency='usd', + photo_url='http://erkelzaar.tsudao.com/models/perrotta/TIME_MACHINE.jpg', + photo_height=512, # !=0/None or picture won't be shown + photo_width=512, + photo_size=512, + is_flexible=False, # True If you need to set up Shipping Fee + prices=prices, + start_parameter='time-machine-example', + invoice_payload='HAPPY FRIDAYS COUPON') + + +@bot.shipping_query_handler(func=lambda query: True) +def shipping(shipping_query): + print(shipping_query) + bot.answer_shipping_query(shipping_query.id, ok=True, shipping_options=shipping_options, + error_message='Oh, seems like our Dog couriers are having a lunch right now. Try again later!') + + +@bot.pre_checkout_query_handler(func=lambda query: True) +def checkout(pre_checkout_query): + bot.answer_pre_checkout_query(pre_checkout_query.id, ok=True, + error_message="Aliens tried to steal your card's CVV, but we successfully protected your credentials," + " try to pay again in a few minutes, we need a small rest.") + + +@bot.message_handler(content_types=['successful_payment']) +def got_payment(message): + bot.send_message(message.chat.id, + 'Hoooooray! Thanks for payment! We will proceed your order for `{} {}` as fast as possible! ' + 'Stay in touch.\n\nUse /buy again to get a Time Machine for your friend!'.format( + message.successful_payment.total_amount / 100, message.successful_payment.currency), + parse_mode='Markdown') + + +bot.skip_pending = True +bot.polling(none_stop=True, interval=0) From 0b1ae6ad8bc9584cd774f522daac6ad4e14686ae Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 25 May 2017 13:30:00 +0800 Subject: [PATCH 0156/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b8bf41a68..4a69fb878 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='2.3.2', + version='3.0.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 777a3afaaa5f30f5bd5db95761416206b962574c Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 30 May 2017 17:17:29 +0800 Subject: [PATCH 0157/1808] Fix #314 --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index fd1cb6816..5d84b20b1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -148,6 +148,8 @@ def de_json(cls, json_string): pending_update_count = obj['pending_update_count'] last_error_date = None last_error_message = None + max_connections = None + allowed_updates = None if 'last_error_message' in obj: last_error_date = obj['last_error_date'] if 'last_error_message' in obj: From d6aaf0716a575cab222496b36b2bf7295fc4c43e Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 30 May 2017 17:19:03 +0800 Subject: [PATCH 0158/1808] Update version. Change log: - Fix #314 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4a69fb878..cad62e041 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.0.0', + version='3.0.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 3f626d37ba7116c62c432fa887f4400b9b1ba7f7 Mon Sep 17 00:00:00 2001 From: Denis M Korzhenkov Date: Thu, 1 Jun 2017 10:44:46 +0300 Subject: [PATCH 0159/1808] Update README.md Fixed hyperlink formatting in the subtitle --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e4f532204..b3492b30b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ #

pyTelegramBotAPI -

A simple, but extensible Python implementation for the [Telegram Bot API](https://core.telegram.org/bots/api). +

A simple, but extensible Python implementation for the Telegram Bot API. [![Download Month](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) From cb4521f4973a30d3bd551dcd20aaedb06a19d074 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Fri, 2 Jun 2017 11:07:35 +0300 Subject: [PATCH 0160/1808] Fixed wrong method call Should be called `apihelper.answer_shipping_query` instead of `apihelper.answer_shippingQuery` --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 381162d3f..8059e6382 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -661,7 +661,7 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to return types.Message.de_json(result) def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): - return apihelper.answer_shippingQuery(self.token, shipping_query_id, ok, shipping_options, error_message) + return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None): return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) From 556a04ca8b4c0bdea423a569797453603493e5cc Mon Sep 17 00:00:00 2001 From: George Date: Fri, 16 Jun 2017 02:47:47 +0300 Subject: [PATCH 0161/1808] Added file opening mode --- examples/download_file_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/download_file_example.py b/examples/download_file_example.py index d9003d134..157cdc7a1 100644 --- a/examples/download_file_example.py +++ b/examples/download_file_example.py @@ -5,7 +5,7 @@ bot = telebot.TeleBot(TOKEN) -ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg')) +ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg', 'rb')) file_info = bot.get_file(ret_msg.voice.file_id) From 328cabead6a9283ad82f7933ba22eb91957f6db9 Mon Sep 17 00:00:00 2001 From: Yolley Date: Tue, 20 Jun 2017 15:45:01 +0300 Subject: [PATCH 0162/1808] Update util.py --- telebot/util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 8e0426713..42ffd32da 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -4,6 +4,7 @@ import re import sys import six +import traceback from six import string_types # Python3 queue support. @@ -56,8 +57,8 @@ def run(self): self.done_event.set() except Queue.Empty: pass - except: - logger.debug("Exception occurred") + except Exception as e: + logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc()) self.exc_info = sys.exc_info() self.exception_event.set() From 242456d92bf5b06fad561e5a54f038d6dca6a32e Mon Sep 17 00:00:00 2001 From: Yolley Date: Tue, 20 Jun 2017 15:45:18 +0300 Subject: [PATCH 0163/1808] Update util.py --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 42ffd32da..f5245776d 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- import threading +import traceback import re import sys import six -import traceback from six import string_types # Python3 queue support. From 3713b093b685b36e64831c2033b374de8d5b3a80 Mon Sep 17 00:00:00 2001 From: Ihor Polyakov Date: Thu, 22 Jun 2017 10:35:13 +0700 Subject: [PATCH 0164/1808] json.dumps(allowed_updates) before sending request --- telebot/apihelper.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b5372cdf6..9276a924a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- +try: + import ujson as json +except ImportError: + import json + import requests try: @@ -137,7 +142,7 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed if max_connections: payload['max_connections'] = max_connections if allowed_updates: - payload['allowed_updates'] = allowed_updates + payload['allowed_updates'] = json.dumps(allowed_updates) return _make_request(token, method_url, params=payload, files=files) @@ -162,7 +167,7 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No if timeout: payload['timeout'] = timeout if allowed_updates: - payload['allowed_updates'] = allowed_updates + payload['allowed_updates'] = json.dumps(allowed_updates) return _make_request(token, method_url, params=payload) From 6786f87d66fddd617b99f718b2eb9e44d52d3696 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 23 Jun 2017 17:01:05 +0300 Subject: [PATCH 0165/1808] Using some content types in one function --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b3492b30b..b056d0399 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,10 @@ All types are defined in types.py. They are all completely in line with the [Tel The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings: `text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_member`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`. +You can use some types in one function. Example: + +```content_types=["text", "sticker", "pinned_message", "photo", "audio"]``` + ### Methods All [API methods](https://core.telegram.org/bots/api#available-methods) are located in the TeleBot class. They are renamed to follow common Python naming conventions. E.g. `getMe` is renamed to `get_me` and `sendMessage` to `send_message`. From 6af3067a1293cca4c5c98f08bc1c956eeb8051bf Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 28 Jun 2017 17:44:07 +0800 Subject: [PATCH 0166/1808] Add proxy to readme. --- README.md | 11 +++++++++++ telebot/apihelper.py | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b056d0399..369e98f98 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ * [The listener mechanism](#the-listener-mechanism) * [Using web hooks](#using-web-hooks) * [Logging](#logging) + * [Proxy](#proxy) * [F.A.Q.](#faq) * [Bot 2.0](#bot-20) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) @@ -492,6 +493,16 @@ logger = telebot.logger telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. ``` +### Proxy + +You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument. + +```python +from telebot import apihelper + +apihelper.proxy = {'http', 'http://10.10.1.10:3128'} +``` + ## F.A.Q. ### Bot 2.0 diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9276a924a..c2f1141d5 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -19,6 +19,7 @@ logger = telebot.logger req_session = requests.session() +proxy = None API_URL = "https://api.telegram.org/bot{0}/{1}" FILE_URL = "https://api.telegram.org/file/bot{0}/{1}" @@ -47,7 +48,7 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 result = req_session.request(method, request_url, params=params, files=files, - timeout=(connect_timeout, read_timeout)) + timeout=(connect_timeout, read_timeout), proxies=proxy) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] From 3e04df7080899c889bf05b411135e8c57ea40a43 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Wed, 28 Jun 2017 18:01:07 +0800 Subject: [PATCH 0167/1808] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 369e98f98..48b6e1c94 100644 --- a/README.md +++ b/README.md @@ -503,6 +503,16 @@ from telebot import apihelper apihelper.proxy = {'http', 'http://10.10.1.10:3128'} ``` +If you want to use socket5 proxy you need install dependency `pip install requests[socks]`. + +```python +proxies = { + 'http': 'socks5://user:pass@host:port', + 'https': 'socks5://user:pass@host:port' +} +``` + + ## F.A.Q. ### Bot 2.0 From 25a37db2bb621dd8de682426639a5726c4110a23 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Fri, 30 Jun 2017 19:47:09 +0300 Subject: [PATCH 0168/1808] Bot API v3.1 --- telebot/__init__.py | 149 ++++++++++++++++++++++++++++++++++++++++++- telebot/apihelper.py | 65 +++++++++++++++++++ telebot/types.py | 64 +++++++++++++++++-- 3 files changed, 271 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8059e6382..caa8f894e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -44,7 +44,26 @@ class TeleBot: sendChatAction getUserProfilePhotos getUpdates - """ + getFile + kickChatMember + unbanChatMember + restrictChatMember + promoteChatMember + exportChatInviteLink + setChatPhoto + deleteChatPhoto + setChatTitle + setChatDescription + pinChatMessage + unpinChatMessage + leaveChat + getChat + getChatAdministrators + getChatMembersCount + getChatMember + answerCallbackQuery + answerInlineQuery + """ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): """ @@ -613,6 +632,134 @@ def kick_chat_member(self, chat_id, user_id): def unban_chat_member(self, chat_id, user_id): return apihelper.unban_chat_member(self.token, chat_id, user_id) + def restrict_chat_member(self, chat_id, user_id): + """ + Use this method to restrict a user in a supergroup. + The bot must be an administrator in the supergroup for this to work and must have + the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. + Returns True on success. + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup + or channel (in the format @channelusername) + :param user_id: Int : Unique identifier of the target user + :return: types.Message + """ + return apihelper.restrict_chat_member(self.token, chat_id, user_id) + + def promote_chat_member(self, chat_id, user_id, can_change_info=False, can_post_messages=False, + can_edit_messages=False, can_delete_messages=False, can_invite_users=False, + can_restrict_members=False, can_pin_messages=False, can_promote_members=False): + """ + Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + Pass False for all boolean parameters to demote a user. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target channel ( + in the format @channelusername) + :param user_id: Int : Unique identifier of the target user + :param can_change_info: Bool: Pass True, if the administrator can change chat title, photo and other settings + :param can_post_messages: Bool : Pass True, if the administrator can create channel posts, channels only + :param can_edit_messages: Bool : Pass True, if the administrator can edit messages of other users, channels only + :param can_delete_messages: Bool : Pass True, if the administrator can delete messages of other users + :param can_invite_users: Bool : Pass True, if the administrator can invite new users to the chat + :param can_restrict_members: Bool: Pass True, if the administrator can restrict, ban or unban chat members + :param can_pin_messages: Bool: Pass True, if the administrator can pin messages, supergroups only + :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset + of his own privileges or demote administrators that he has promoted, directly or indirectly + (promoted by administrators that were appointed by him) + :return: + """ + return apihelper.promote_chat_member(self.token, chat_id, user_id, can_change_info, can_post_messages, + can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members) + + def export_chat_invite_link(self, chat_id): + """ + Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + Returns exported invite link as String on success. + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: + """ + return apihelper.export_chat_invite_link(self.token, chat_id) + + def set_chat_photo(self, chat_id, photo): + """ + Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param photo: InputFile: New chat photo, uploaded using multipart/form-data + :return: + """ + return apihelper.set_chat_photo(self.token, chat_id, photo) + + def delete_chat_photo(self, chat_id): + """ + Use this method to delete a chat photo. Photos can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: + """ + return apihelper.delete_chat_photo(self.token, chat_id) + + def set_chat_title(self, chat_id, title): + """ + Use this method to change the title of a chat. Titles can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param title: New chat title, 1-255 characters + :return: + """ + return apihelper.set_chat_title(self.token, chat_id, title) + + def set_chat_description(self, chat_id, description): + """ + Use this method to change the description of a supergroup or a channel. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param description: Str: New chat description, 0-255 characters + :return: + """ + return apihelper.set_chat_description(self.token, chat_id, description) + + def pin_chat_message(self, chat_id, message_id, disable_notification=False): + """ + Use this method to pin a message in a supergroup. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param message_id: Int: Identifier of a message to pin + :param disable_notification: Bool: Pass True, if it is not necessary to send a notification + to all group members about the new pinned message + :return: + """ + return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) + + def unpin_chat_message(self, chat_id): + """ + Use this method to unpin a message in a supergroup chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: + """ + return apihelper.unpin_chat_message(self.token, chat_id) + def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, disable_web_page_preview=None, reply_markup=None): result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9276a924a..818527c3a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -431,6 +431,71 @@ def unban_chat_member(token, chat_id, user_id): return _make_request(token, method_url, params=payload, method='post') +def restrict_chat_member(token, chat_id, user_id): + method_url = 'restrictChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id} + return _make_request(token, method_url, params=payload, method='post') + + +def promote_chat_member(token, chat_id, user_id, can_change_info=False, can_post_messages=False, + can_edit_messages=False, can_delete_messages=False, can_invite_users=False, + can_restrict_members=False, can_pin_messages=False, can_promote_members=False): + method_url = 'promoteChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id, 'can_change_info': can_change_info, + 'can_post_messages': can_post_messages, 'can_edit_messages': can_edit_messages, + 'can_delete_messages': can_delete_messages, 'can_invite_users': can_invite_users, + 'can_restrict_members': can_restrict_members, 'can_pin_messages': can_pin_messages, + 'can_promote_members': can_promote_members } + return _make_request(token, method_url, params=payload, method='post') + + +def export_chat_invite_link(token, chat_id): + method_url = 'exportChatInviteLink' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload, method='post') + + +def set_chat_photo(token, chat_id, photo): + method_url = 'setChatPhoto' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(photo): + files = {'photo': photo} + else: + payload['photo'] = photo + return _make_request(token, method_url, params=payload, files=files, method='post') + + +def delete_chat_photo(token, chat_id): + method_url = 'deleteChatPhoto' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload, method='post') + + +def set_chat_title(token, chat_id, title): + method_url = 'setChatTitle' + payload = {'chat_id': chat_id, 'title': title} + return _make_request(token, method_url, params=payload, method='post') + + +def set_chat_description(token, chat_id, description): + method_url = 'setChatDescription' + payload = {'chat_id': chat_id, 'description': description} + return _make_request(token, method_url, params=payload, method='post') + + +def pin_chat_message(token, chat_id, message_id, disable_notification=False): + method_url = 'pinChatMessage' + payload = {'chat_id': chat_id, 'message_id': message_id, 'disable_notification': disable_notification} + return _make_request(token, method_url, params=payload, method='post') + + +def unpin_chat_message(token, chat_id): + method_url = 'unpinChatMessage' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload, method='post') + + # Updating messages def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, diff --git a/telebot/types.py b/telebot/types.py index 5d84b20b1..6f16c3843 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -215,10 +215,15 @@ def de_json(cls, json_string): first_name = obj.get('first_name') last_name = obj.get('last_name') all_members_are_administrators = obj.get('all_members_are_administrators') - return cls(id, type, title, username, first_name, last_name, all_members_are_administrators) + if 'photo' in obj: + photo = ChatPhoto.de_json(obj['photo']) + description = obj.get('description') + invite_link = obj.get('invite_link') + return cls(id, type, title, username, first_name, last_name, all_members_are_administrators, + photo, description, invite_link) def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, - all_members_are_administrators=None): + all_members_are_administrators=None, photo=None, description=None, invite_link=None): self.type = type self.last_name = last_name self.first_name = first_name @@ -226,6 +231,9 @@ def __init__(self, id, type, title=None, username=None, first_name=None, last_na self.id = id self.title = title self.all_members_are_administrators = all_members_are_administrators + self.photo = photo + self.description = description + self.invite_link = invite_link class Message(JsonDeserializable): @@ -850,18 +858,62 @@ def __init__(self, id, from_user, data, chat_instance, message=None, inline_mess self.inline_message_id = inline_message_id +class ChatPhoto(JsonDeserializable): + @classmethod + def de_json(cls, json_type): + obj = cls.check_json(json_type) + small_file_id = obj['small_file_id'] + big_file_id = obj['big_file_id'] + return cls(small_file_id, big_file_id) + + def __init__(self, small_file_id, big_file_id): + self.small_file_id = small_file_id + self.big_file_id = big_file_id + + class ChatMember(JsonDeserializable): @classmethod def de_json(cls, json_type): obj = cls.check_json(json_type) user = User.de_json(obj['user']) status = obj['status'] - return cls(user, status) - - def __init__(self, user, status): + until_date = obj.get('until_date') + can_be_edited = obj.get('can_be_edited') + can_change_info = obj.get('can_change_info') + can_post_messages = obj.get('can_post_messages') + can_edit_messages = obj.get('can_edit_messages') + can_delete_messages = obj.get('can_delete_messages') + can_invite_users = obj.get('can_invite_users') + can_restrict_members = obj.get('can_restrict_members') + can_pin_messages = obj.get('can_pin_messages') + can_promote_members = obj.get('can_promote_members') + can_send_messages = obj.get('can_send_messages') + can_send_media_messages = obj.get('can_send_media_messages') + can_send_other_messages = obj.get('can_send_other_messages') + can_add_web_page_previews = obj.get('can_add_web_page_previews') + return cls(user, status, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages, + can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, + can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews) + + def __init__(self, user, status, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages, + can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, + can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews): self.user = user self.status = status - + self.until_date = until_date + self.can_be_edited = can_be_edited + self.can_change_info = can_change_info + self.can_post_messages = can_post_messages + self.can_edit_messages = can_edit_messages + self.can_delete_messages = can_delete_messages + self.can_invite_users = can_invite_users + self.can_restrict_members = can_restrict_members + self.can_pin_messages = can_pin_messages + self.can_promote_members = can_promote_members + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_previews = can_add_web_page_previews # InlineQuery From 662a8341383a648a29fc01485130642bb11a35dc Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Fri, 30 Jun 2017 20:16:51 +0300 Subject: [PATCH 0169/1808] Added missing arguments to restrict_chat_member method --- telebot/__init__.py | 18 ++++++++++++++++-- telebot/apihelper.py | 8 ++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index caa8f894e..7486b51aa 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -632,7 +632,9 @@ def kick_chat_member(self, chat_id, user_id): def unban_chat_member(self, chat_id, user_id): return apihelper.unban_chat_member(self.token, chat_id, user_id) - def restrict_chat_member(self, chat_id, user_id): + def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=True, + can_send_media_messages=True, can_send_other_messages=True, + can_add_web_page_previews=True): """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -641,9 +643,21 @@ def restrict_chat_member(self, chat_id, user_id): :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :param user_id: Int : Unique identifier of the target user + :param until_date: Date when restrictions will be lifted for the user, unix time. + If user is restricted for more than 366 days or less than 30 seconds from the current time, + they are considered to be restricted forever + :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues + :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes + and voice notes, implies can_send_messages + :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and + use inline bots, implies can_send_media_messages + :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, + implies can_send_media_messages :return: types.Message """ - return apihelper.restrict_chat_member(self.token, chat_id, user_id) + return apihelper.restrict_chat_member(self.token, chat_id, user_id, until_date, can_send_messages, + can_send_media_messages, can_send_other_messages, + can_add_web_page_previews) def promote_chat_member(self, chat_id, user_id, can_change_info=False, can_post_messages=False, can_edit_messages=False, can_delete_messages=False, can_invite_users=False, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 818527c3a..5ccf71c57 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -431,9 +431,13 @@ def unban_chat_member(token, chat_id, user_id): return _make_request(token, method_url, params=payload, method='post') -def restrict_chat_member(token, chat_id, user_id): +def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_messages=True, + can_send_media_messages=True, can_send_other_messages=True, + can_add_web_page_previews=True): method_url = 'restrictChatMember' - payload = {'chat_id': chat_id, 'user_id': user_id} + payload = {'chat_id': chat_id, 'user_id': user_id, 'until_date': until_date, 'can_send_messages': can_send_messages, + 'can_send_media_messages': can_send_media_messages, 'can_send_other_messages': can_send_other_messages, + 'can_add_web_page_previews': can_add_web_page_previews} return _make_request(token, method_url, params=payload, method='post') From f97bb2f6153ec6b53c5de840073c52c2c3fbb88b Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 1 Jul 2017 11:05:14 +0800 Subject: [PATCH 0170/1808] FIx missing declare --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index 6f16c3843..dc1c1c4c1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -215,6 +215,7 @@ def de_json(cls, json_string): first_name = obj.get('first_name') last_name = obj.get('last_name') all_members_are_administrators = obj.get('all_members_are_administrators') + photo = None if 'photo' in obj: photo = ChatPhoto.de_json(obj['photo']) description = obj.get('description') From 5f8ed347a1a284e0d063787864d1265b986fde9e Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 1 Jul 2017 11:11:25 +0800 Subject: [PATCH 0171/1808] Add missing arg until_date for kickChatMember. --- telebot/__init__.py | 6 ++++-- telebot/apihelper.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7486b51aa..0b41938a4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -620,14 +620,16 @@ def send_chat_action(self, chat_id, action): """ return apihelper.send_chat_action(self.token, chat_id, action) - def kick_chat_member(self, chat_id, user_id): + def kick_chat_member(self, chat_id, user_id, until_date=None): """ Use this method to kick a user from a group or a supergroup. :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup :param user_id: Int : Unique identifier of the target user + :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or + less than 30 seconds from the current time they are considered to be banned forever :return: types.Message """ - return apihelper.kick_chat_member(self.token, chat_id, user_id) + return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date) def unban_chat_member(self, chat_id, user_id): return apihelper.unban_chat_member(self.token, chat_id, user_id) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3268930ab..9c1b7859f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -420,9 +420,11 @@ def get_method_by_type(data_type): return r'sendSticker' -def kick_chat_member(token, chat_id, user_id): +def kick_chat_member(token, chat_id, user_id, until_date=None): method_url = 'kickChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} + if until_date: + payload['until_date'] = until_date return _make_request(token, method_url, params=payload, method='post') From d2f694516a6d04e2fb16125c4aa7c971d5878746 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 1 Jul 2017 11:24:29 +0800 Subject: [PATCH 0172/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cad62e041..49dfa13df 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.0.1', + version='3.1.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 4979589faf87f17e00faba8e6c3b3f21262dd15d Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 2 Jul 2017 21:08:36 +0800 Subject: [PATCH 0173/1808] Fix not require args. #369 --- telebot/__init__.py | 12 +++++------ telebot/apihelper.py | 49 +++++++++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0b41938a4..6356b80f8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -634,9 +634,9 @@ def kick_chat_member(self, chat_id, user_id, until_date=None): def unban_chat_member(self, chat_id, user_id): return apihelper.unban_chat_member(self.token, chat_id, user_id) - def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=True, - can_send_media_messages=True, can_send_other_messages=True, - can_add_web_page_previews=True): + def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=None, + can_send_media_messages=None, can_send_other_messages=None, + can_add_web_page_previews=None): """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -661,9 +661,9 @@ def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messa can_send_media_messages, can_send_other_messages, can_add_web_page_previews) - def promote_chat_member(self, chat_id, user_id, can_change_info=False, can_post_messages=False, - can_edit_messages=False, can_delete_messages=False, can_invite_users=False, - can_restrict_members=False, can_pin_messages=False, can_promote_members=False): + def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_messages=None, + can_edit_messages=None, can_delete_messages=None, can_invite_users=None, + can_restrict_members=None, can_pin_messages=None, can_promote_members=None): """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9c1b7859f..e554730b7 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -434,25 +434,46 @@ def unban_chat_member(token, chat_id, user_id): return _make_request(token, method_url, params=payload, method='post') -def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_messages=True, - can_send_media_messages=True, can_send_other_messages=True, - can_add_web_page_previews=True): +def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_messages=None, + can_send_media_messages=None, can_send_other_messages=None, + can_add_web_page_previews=None): method_url = 'restrictChatMember' - payload = {'chat_id': chat_id, 'user_id': user_id, 'until_date': until_date, 'can_send_messages': can_send_messages, - 'can_send_media_messages': can_send_media_messages, 'can_send_other_messages': can_send_other_messages, - 'can_add_web_page_previews': can_add_web_page_previews} + payload = {'chat_id': chat_id, 'user_id': user_id} + if until_date: + payload['until_date'] = until_date + if can_send_messages: + payload['can_send_messages'] = can_send_messages + if can_send_media_messages: + payload['can_send_media_messages'] = can_send_media_messages + if can_send_other_messages: + payload['can_send_other_messages'] = can_send_other_messages + if can_add_web_page_previews: + payload['can_add_web_page_previews'] = can_add_web_page_previews + return _make_request(token, method_url, params=payload, method='post') -def promote_chat_member(token, chat_id, user_id, can_change_info=False, can_post_messages=False, - can_edit_messages=False, can_delete_messages=False, can_invite_users=False, - can_restrict_members=False, can_pin_messages=False, can_promote_members=False): +def promote_chat_member(token, chat_id, user_id, can_change_info=None, can_post_messages=None, + can_edit_messages=None, can_delete_messages=None, can_invite_users=None, + can_restrict_members=None, can_pin_messages=None, can_promote_members=None): method_url = 'promoteChatMember' - payload = {'chat_id': chat_id, 'user_id': user_id, 'can_change_info': can_change_info, - 'can_post_messages': can_post_messages, 'can_edit_messages': can_edit_messages, - 'can_delete_messages': can_delete_messages, 'can_invite_users': can_invite_users, - 'can_restrict_members': can_restrict_members, 'can_pin_messages': can_pin_messages, - 'can_promote_members': can_promote_members } + payload = {'chat_id': chat_id, 'user_id': user_id} + if can_change_info: + payload['can_change_info'] = can_change_info + if can_post_messages: + payload['can_post_messages'] = can_post_messages + if can_edit_messages: + payload['can_edit_messages'] = can_edit_messages + if can_delete_messages: + payload['can_delete_messages'] = can_delete_messages + if can_invite_users: + payload['can_invite_users'] = can_invite_users + if can_restrict_members: + payload['can_restrict_members'] = can_restrict_members + if can_pin_messages: + payload['can_pin_messages'] = can_pin_messages + if can_promote_members: + payload['can_promote_members'] = can_promote_members return _make_request(token, method_url, params=payload, method='post') From 0632cfb9b0ec8371e36339010c1194b006cc1b28 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 2 Jul 2017 21:24:19 +0800 Subject: [PATCH 0174/1808] Fix new chat members. --- setup.py | 2 +- telebot/types.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 49dfa13df..4d1dc2a49 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.1.0', + version='3.1.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', diff --git a/telebot/types.py b/telebot/types.py index dc1c1c4c1..6912db391 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -305,7 +305,11 @@ def de_json(cls, json_string): opts['new_chat_member'] = User.de_json(obj['new_chat_member']) content_type = 'new_chat_member' if 'new_chat_members' in obj: - opts['new_chat_members'] = obj['new_chat_members'] + chat_members = obj['new_chat_members'] + nms = [] + for m in chat_members: + nms.append(User.de_json(m)) + opts['new_chat_members'] = nms content_type = 'new_chat_members' if 'left_chat_member' in obj: opts['left_chat_member'] = User.de_json(obj['left_chat_member']) @@ -382,6 +386,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.location = None self.venue = None self.new_chat_member = None + self.new_chat_members = None self.left_chat_member = None self.new_chat_title = None self.new_chat_photo = None From 08d6ab549df8fdf5bda49b175bec69229de01452 Mon Sep 17 00:00:00 2001 From: Kylmakalle Date: Fri, 7 Jul 2017 00:54:18 +0300 Subject: [PATCH 0175/1808] Aync Methods for v3.1 --- telebot/__init__.py | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6356b80f8..e3948ff9c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1215,13 +1215,49 @@ def send_chat_action(self, *args, **kwargs): return TeleBot.send_chat_action(self, *args, **kwargs) @util.async() - def kick_chat_member(self, *args): - return TeleBot.kick_chat_member(self, *args) + def kick_chat_member(self, *args, **kwargs): + return TeleBot.kick_chat_member(self, *args, **kwargs) @util.async() def unban_chat_member(self, *args): return TeleBot.unban_chat_member(self, *args) + @util.async() + def restrict_chat_member(self, *args, **kwargs): + return TeleBot.restrict_chat_member(self, *args, **kwargs) + + @util.async() + def promote_chat_member(self, *args, **kwargs): + return TeleBot.promote_chat_member(self, *args, **kwargs) + + @util.async() + def export_chat_invite_link(self, *args): + return TeleBot.export_chat_invite_link(self, *args) + + @util.async() + def set_chat_photo(self, *args): + return TeleBot.set_chat_photo(self, *args) + + @util.async() + def delete_chat_photo(self, *args): + return TeleBot.delete_chat_photo(self, *args) + + @util.async() + def set_chat_title(self, *args): + return TeleBot.set_chat_title(self, *args) + + @util.async() + def set_chat_description(self, *args): + return TeleBot.set_chat_description(self, *args) + + @util.async() + def pin_chat_message(self, *args, **kwargs): + return TeleBot.pin_chat_message(self, *args, **kwargs) + + @util.async() + def unpin_chat_message(self, *args): + return TeleBot.unpin_chat_message(self, *args) + @util.async() def edit_message_text(self, *args, **kwargs): return TeleBot.edit_message_text(self, *args, **kwargs) From 1a80fc5a0e093c5072263b3c3b991538619510cd Mon Sep 17 00:00:00 2001 From: the31k Date: Wed, 19 Jul 2017 01:35:19 +0300 Subject: [PATCH 0176/1808] Per-thread singletons --- telebot/util.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index f5245776d..ab7650d7e 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -17,6 +17,9 @@ from telebot import logger +thread_local = threading.local() + + class WorkerThread(threading.Thread): count = 0 @@ -242,3 +245,12 @@ def extract_arguments(text): regexp = re.compile("\/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) result = regexp.match(text) return result.group(2) if is_command(text) else None + + +def per_thread(key, construct_value): + try: + return getattr(thread_local, key) + except AttributeError: + value = construct_value() + setattr(thread_local, key, value) + return value From feec19b7f414f0edf3e889ff07e8a44c8a02d0b4 Mon Sep 17 00:00:00 2001 From: the31k Date: Wed, 19 Jul 2017 01:35:44 +0300 Subject: [PATCH 0177/1808] Use per-thread requests sessions Reason is requests.Session is not thread-safe See: https://github.com/requests/requests/issues/2766 --- telebot/apihelper.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e554730b7..d1b83a098 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -18,7 +18,6 @@ from telebot import util logger = telebot.logger -req_session = requests.session() proxy = None API_URL = "https://api.telegram.org/bot{0}/{1}" @@ -28,6 +27,10 @@ READ_TIMEOUT = 9999 +def _get_req_session(): + return util.per_thread('req_session', lambda: requests.session()) + + def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL): """ Makes a request to the Telegram API. @@ -47,8 +50,8 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas if params: if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 - result = req_session.request(method, request_url, params=params, files=files, - timeout=(connect_timeout, read_timeout), proxies=proxy) + result = _get_req_session().req_session.request(method, request_url, params=params, files=files, + timeout=(connect_timeout, read_timeout), proxies=proxy) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] @@ -97,7 +100,7 @@ def get_file(token, file_id): def download_file(token, file_path): url = FILE_URL.format(token, file_path) - result = req_session.get(url) + result = _get_req_session().get(url) if result.status_code != 200: msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \ .format(result.status_code, result.reason, result.text) From 96569cbdac27a9ae4ad3405e2f1a04e5bac82829 Mon Sep 17 00:00:00 2001 From: the31k Date: Wed, 19 Jul 2017 15:44:10 +0300 Subject: [PATCH 0178/1808] Fix typo --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d1b83a098..961b3f55f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -50,7 +50,7 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas if params: if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 - result = _get_req_session().req_session.request(method, request_url, params=params, files=files, + result = _get_req_session().request(method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout), proxies=proxy) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] From e92dc3717e24c4861a94f9123c3c56632a905ffc Mon Sep 17 00:00:00 2001 From: Gabriel R F Date: Mon, 24 Jul 2017 14:28:27 -0300 Subject: [PATCH 0179/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 48b6e1c94..15f98eda5 100644 --- a/README.md +++ b/README.md @@ -580,5 +580,7 @@ Get help. Discuss. Chat. * [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic * [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video. * [areajugonesbot](http://t.me/areajugonesbot)([link](http://t.me/areajugonesbot)) - The areajugonesbot sends news published on the videogames blog Areajugones to Telegram. +* [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service. +* [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From ed29f9316fb3f05960dbcd5846f69917c112b99c Mon Sep 17 00:00:00 2001 From: noirgif Date: Sun, 30 Jul 2017 23:24:30 -0700 Subject: [PATCH 0180/1808] change the documentation of TeleBot.polling in readme --- README.md | 4 ++-- README.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 48b6e1c94..2271ca4cd 100644 --- a/README.md +++ b/README.md @@ -234,8 +234,8 @@ tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object # - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers # - interval: True/False (default False) - The interval between polling requests # Note: Editing this parameter harms the bot's response time -# - block: True/False (default True) - Blocks upon calling this function -tb.polling(none_stop=False, interval=0, block=True) +# - timeout: integer (default 20) - Timeout in seconds for long polling. +tb.polling(none_stop=False, interval=0, timeout=20) # getMe user = tb.get_me() diff --git a/README.rst b/README.rst index 3cce442e4..d7765715c 100644 --- a/README.rst +++ b/README.rst @@ -421,8 +421,8 @@ TeleBot # - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers # - interval: True/False (default False) - The interval between polling requests # Note: Editing this parameter harms the bot's response time - # - block: True/False (default True) - Blocks upon calling this function - tb.polling(none_stop=False, interval=0, block=True) + # - timeout: integer (default 20) - Timeout in seconds for long polling. + tb.polling(none_stop=False, interval=0, timeout=20) # getMe user = tb.get_me() From aefd66606238f94b01e576b25977da55af0d93a6 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 6 Aug 2017 12:00:26 +0800 Subject: [PATCH 0181/1808] Update sticker set. --- telebot/types.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 6912db391..6c327bb52 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -902,8 +902,8 @@ def de_json(cls, json_type): can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews) def __init__(self, user, status, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages, - can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews): + can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, + can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews): self.user = user self.status = status self.until_date = until_date @@ -1859,3 +1859,69 @@ def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipp self.invoice_payload = invoice_payload self.shipping_option_id = shipping_option_id self.order_info = order_info + +# Stickers + +class StickerSet(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + name = obj['name'] + title = obj['title'] + contains_masks = obj['contains_masks'] + stickers=[] + for s in obj['stickers'] + stickers.append(Sticker.de_json(s)) + return cls(name, title, contains_masks, stickers) + + def __init__(self, name, title, contains_masks,stickers): + self.stickers = stickers + self.contains_masks = contains_masks + self.title = title + self.name = name + + + +class Sticker(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + file_id = obj['file_id'] + width = obj['width'] + height = obj['height'] + thumb = None + if 'thumb' in obj: + thumb = PhotoSize.de_json(obj['thumb']) + emoji = obj.get('emoji') + set_name = obj.get('set_name') + mask_position = None + if 'mask_position' in obj: + mask_position = MaskPosition.de_json(obj['mask_position']) + file_size = obj.get('file_size') + return cls(file_id, width, height, thumb,emoji, set_name, mask_position, file_size) + + def __init__(self,file_id,width,height,thumb,emoji,set_name,mask_position,file_size): + self.file_id = file_id + self.width = width + self.height = height + self.thumb=thumb + self.emoji = emoji + self.set_name = set_name + self.mask_position = mask_position + self.file_size=file_size + +class MaskPosition(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + obj = cls.check_json(json_string) + point = obj['point'] + x_shift = obj['x_shift'] + y_shift = obj['y_shift'] + scale = obj['scale'] + return cls(point, x_shift, y_shift, scale) + + def __init__(self, point, x_shift, y_shift, scale): + self.point = point + self.x_shift = x_shift + self.y_shift = y_shift + self.scale = scale From af70313721fca2ff3aa24519b28eac282443f7eb Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 6 Aug 2017 14:25:25 +0800 Subject: [PATCH 0182/1808] New method for v3.2 --- telebot/__init__.py | 110 +++++++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 68 +++++++++++++++++++++++++- telebot/types.py | 27 +++++++---- 3 files changed, 195 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e3948ff9c..9a05d67d8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -255,6 +255,7 @@ def polling(self, none_stop=False, interval=0, timeout=20): Warning: Do not call this function more than once! Always get updates. + :param interval: :param none_stop: Do not stop polling when an ApiException occurs. :param timeout: Timeout in seconds for long polling. :return: @@ -462,6 +463,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep disable_notification=None): """ Use this method to send photos. + :param disable_notification: :param chat_id: :param photo: :param caption: @@ -871,6 +873,86 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None, u """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) + def send_sticker(self, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): + """ + Use this method to send .webp stickers. On success, the sent Message is returned. + :param chat_id: + :param sticker: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :return: + """ + result = apihelper.send_sticker(self.token, chat_id, sticker, disable_notification, reply_markup, reply_markup) + return types.Message.de_json(result) + + def get_sticker_set(self, name): + """ + Use this method to get a sticker set. On success, a StickerSet object is returned. + :param token: + :param name: + :return: + """ + result = apihelper.get_sticker_set(self.token, name) + return types.StickerSet.de_json(result) + + def upload_sticker_file(self, user_id, png_sticker): + """ + Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet + methods (can be used multiple times). Returns the uploaded File on success. + :param user_id: + :param png_sticker: + :return: + """ + result = apihelper.upload_sticker_file(self.token, user_id, png_sticker) + return types.File.de_json(result) + + def create_new_sticker_set(self, user_id, name, title, png_sticker, emojis, contains_masks=None, + mask_position=None): + """ + Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. + Returns True on success. + :param user_id: + :param name: + :param title: + :param png_sticker: + :param emojis: + :param contains_masks: + :param mask_position: + :return: + """ + return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, contains_masks, + mask_position) + + def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position): + """ + Use this method to add a new sticker to a set created by the bot. Returns True on success. + :param user_id: + :param name: + :param png_sticker: + :param emojis: + :param mask_position: + :return: + """ + return apihelper.add_sticker_to_set(self.token, user_id, name, png_sticker, emojis, mask_position) + + def set_sticker_position_in_set(self, sticker, position): + """ + Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. + :param sticker: + :param position: + :return: + """ + return apihelper.set_sticker_position_in_set(self.token, sticker, position) + + def delete_sticker_from_set(self, sticker): + """ + Use this method to delete a sticker from a set created by the bot. Returns True on success. + :param sticker: + :return: + """ + return apihelper.delete_sticker_from_set(self.token, sticker) + def register_for_reply(self, message, callback): """ Registers a callback function to be notified when a reply to `message` arrives. @@ -1301,3 +1383,31 @@ def answer_inline_query(self, *args, **kwargs): @util.async() def answer_callback_query(self, *args, **kwargs): return TeleBot.answer_callback_query(self, *args, **kwargs) + + @util.async() + def send_sticker(self, *args, **kwargs): + return TeleBot.send_sticker(self, *args, **kwargs) + + @util.async() + def get_sticker_set(self, *args, **kwargs): + return TeleBot.get_sticker_set(self, *args, **kwargs) + + @util.async() + def upload_sticker_file(self, *args, **kwargs): + return TeleBot.upload_sticker_file(self, *args, **kwargs) + + @util.async() + def create_new_sticker_set(self, *args, **kwargs): + return TeleBot.create_new_sticker_set(self, *args, **kwargs) + + @util.async() + def add_sticker_to_set(self, *args, **kwargs): + return TeleBot.add_sticker_to_set(self, *args, **kwargs) + + @util.async() + def set_sticker_position_in_set(self, *args, **kwargs): + return TeleBot.set_sticker_position_in_set(self, *args, **kwargs) + + @util.async() + def delete_sticker_from_set(self, *args, **kwargs): + return TeleBot.delete_sticker_from_set(self, *args, **kwargs) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 961b3f55f..af0506d94 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -51,7 +51,7 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas if 'timeout' in params: read_timeout = params['timeout'] + 10 if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 result = _get_req_session().request(method, request_url, params=params, files=files, - timeout=(connect_timeout, read_timeout), proxies=proxy) + timeout=(connect_timeout, read_timeout), proxies=proxy) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] @@ -791,6 +791,72 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per return _make_request(token, method_url, params=payload, method='post') +def send_sticker(token, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): + method_url = 'sendSticker' + payload = {'chat_id': chat_id} + if not util.is_string(sticker): + files = {'sticker': sticker} + else: + payload['sticker'] = sticker + if disable_notification: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload, files=files, method='post') + + +def get_sticker_set(token, name): + method_url = 'getStickerSet' + return _make_request(token, method_url, params={'name': name}) + + +def upload_sticker_file(token, user_id, png_sticker): + method_url = 'uploadStickerFile' + payload = {'user_id': user_id} + files = {'png_sticker', png_sticker} + return _make_request(token, method_url, params=payload, files=files, method='post') + + +def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, contains_masks=None, mask_position=None): + method_url = 'createNewStickerSet' + payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} + if not util.is_string(png_sticker): + files = {'png_sticker': png_sticker} + else: + payload['png_sticker'] = png_sticker + if contains_masks: + payload['contains_masks'] = contains_masks + if mask_position: + payload['mask_position'] = mask_position.to_json() + return _make_request(token, method_url, params=payload, files=files, method='post') + + +def add_sticker_to_set(token, user_id, name, png_sticker, emojis, mask_position): + method_url = 'addStickerToSet' + payload = {'user_id': user_id, 'name': name, 'emojis': emojis} + if not util.is_string(png_sticker): + files = {'png_sticker': png_sticker} + else: + payload['png_sticker'] = png_sticker + if mask_position: + payload['mask_position'] = mask_position.to_json() + return _make_request(token, method_url, params=payload, files=files, method='post') + + +def set_sticker_position_in_set(token, sticker, position): + method_url = 'setStickerPositionInSet' + payload = {'sticker': sticker, 'position': position} + return _make_request(token, method_url, params=payload, method='post') + + +def delete_sticker_from_set(token, sticker): + method_url = 'deleteStickerFromSet' + payload = {'sticker': sticker} + return _make_request(token, method_url, params=payload, method='post') + + def _convert_list_json_serializable(results): ret = '' for r in results: diff --git a/telebot/types.py b/telebot/types.py index 6c327bb52..5698c0bb3 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -921,6 +921,7 @@ def __init__(self, user, status, until_date, can_be_edited, can_change_info, can self.can_send_other_messages = can_send_other_messages self.can_add_web_page_previews = can_add_web_page_previews + # InlineQuery class InlineQuery(JsonDeserializable): @@ -1860,6 +1861,7 @@ def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipp self.shipping_option_id = shipping_option_id self.order_info = order_info + # Stickers class StickerSet(JsonDeserializable): @@ -1869,19 +1871,18 @@ def de_json(cls, json_string): name = obj['name'] title = obj['title'] contains_masks = obj['contains_masks'] - stickers=[] - for s in obj['stickers'] + stickers = [] + for s in obj['stickers']: stickers.append(Sticker.de_json(s)) return cls(name, title, contains_masks, stickers) - def __init__(self, name, title, contains_masks,stickers): + def __init__(self, name, title, contains_masks, stickers): self.stickers = stickers self.contains_masks = contains_masks self.title = title self.name = name - class Sticker(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -1898,19 +1899,20 @@ def de_json(cls, json_string): if 'mask_position' in obj: mask_position = MaskPosition.de_json(obj['mask_position']) file_size = obj.get('file_size') - return cls(file_id, width, height, thumb,emoji, set_name, mask_position, file_size) + return cls(file_id, width, height, thumb, emoji, set_name, mask_position, file_size) - def __init__(self,file_id,width,height,thumb,emoji,set_name,mask_position,file_size): + def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position, file_size): self.file_id = file_id self.width = width self.height = height - self.thumb=thumb + self.thumb = thumb self.emoji = emoji self.set_name = set_name self.mask_position = mask_position - self.file_size=file_size + self.file_size = file_size + -class MaskPosition(JsonDeserializable): +class MaskPosition(JsonDeserializable, JsonSerializable): @classmethod def de_json(cls, json_string): obj = cls.check_json(json_string) @@ -1925,3 +1927,10 @@ def __init__(self, point, x_shift, y_shift, scale): self.x_shift = x_shift self.y_shift = y_shift self.scale = scale + + def to_json(self): + return json.dumps(self.to_dic()) + + def to_dic(self): + return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale} + From 2e743b4b86462afb4b545f434590eefcec912421 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 6 Aug 2017 15:22:23 +0800 Subject: [PATCH 0183/1808] Add v3.2 method. --- telebot/__init__.py | 24 ++++++++++++------------ telebot/apihelper.py | 30 ++++++++++++++++-------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9a05d67d8..671f103fa 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -873,18 +873,18 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None, u """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - def send_sticker(self, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): - """ - Use this method to send .webp stickers. On success, the sent Message is returned. - :param chat_id: - :param sticker: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :return: - """ - result = apihelper.send_sticker(self.token, chat_id, sticker, disable_notification, reply_markup, reply_markup) - return types.Message.de_json(result) + # def send_sticker(self, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): + # """ + # Use this method to send .webp stickers. On success, the sent Message is returned. + # :param chat_id: + # :param sticker: + # :param disable_notification: + # :param reply_to_message_id: + # :param reply_markup: + # :return: + # """ + # result = apihelper.send_sticker(self.token, chat_id, sticker, disable_notification, reply_markup, reply_markup) + # return types.Message.de_json(result) def get_sticker_set(self, name): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index af0506d94..ea7bedcba 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -791,20 +791,20 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per return _make_request(token, method_url, params=payload, method='post') -def send_sticker(token, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): - method_url = 'sendSticker' - payload = {'chat_id': chat_id} - if not util.is_string(sticker): - files = {'sticker': sticker} - else: - payload['sticker'] = sticker - if disable_notification: - payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id - if reply_markup: - payload['reply_markup'] = _convert_markup(reply_markup) - return _make_request(token, method_url, params=payload, files=files, method='post') +# def send_sticker(token, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): +# method_url = 'sendSticker' +# payload = {'chat_id': chat_id} +# if not util.is_string(sticker): +# files = {'sticker': sticker} +# else: +# payload['sticker'] = sticker +# if disable_notification: +# payload['disable_notification'] = disable_notification +# if reply_to_message_id: +# payload['reply_to_message_id'] = reply_to_message_id +# if reply_markup: +# payload['reply_markup'] = _convert_markup(reply_markup) +# return _make_request(token, method_url, params=payload, files=files, method='post') def get_sticker_set(token, name): @@ -822,6 +822,7 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, contains_masks=None, mask_position=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} + files = None if not util.is_string(png_sticker): files = {'png_sticker': png_sticker} else: @@ -836,6 +837,7 @@ def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, con def add_sticker_to_set(token, user_id, name, png_sticker, emojis, mask_position): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} + files = None if not util.is_string(png_sticker): files = {'png_sticker': png_sticker} else: From dcddedcd24c83d7bc994624155eec075fbd47b67 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 6 Aug 2017 15:35:43 +0800 Subject: [PATCH 0184/1808] Fix file dic. --- telebot/apihelper.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ea7bedcba..a808eade4 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -791,22 +791,6 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per return _make_request(token, method_url, params=payload, method='post') -# def send_sticker(token, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): -# method_url = 'sendSticker' -# payload = {'chat_id': chat_id} -# if not util.is_string(sticker): -# files = {'sticker': sticker} -# else: -# payload['sticker'] = sticker -# if disable_notification: -# payload['disable_notification'] = disable_notification -# if reply_to_message_id: -# payload['reply_to_message_id'] = reply_to_message_id -# if reply_markup: -# payload['reply_markup'] = _convert_markup(reply_markup) -# return _make_request(token, method_url, params=payload, files=files, method='post') - - def get_sticker_set(token, name): method_url = 'getStickerSet' return _make_request(token, method_url, params={'name': name}) @@ -815,7 +799,7 @@ def get_sticker_set(token, name): def upload_sticker_file(token, user_id, png_sticker): method_url = 'uploadStickerFile' payload = {'user_id': user_id} - files = {'png_sticker', png_sticker} + files = {'png_sticker': png_sticker} return _make_request(token, method_url, params=payload, files=files, method='post') From 48bfb7b84f5a7df8d7ecf2ac7fb91979ca7036df Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sun, 6 Aug 2017 15:47:59 +0800 Subject: [PATCH 0185/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4d1dc2a49..369035328 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.1.1', + version='3.2.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 246e7e31d7a8be38af8322a603f6e4a2ff743c54 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Tue, 8 Aug 2017 21:50:54 +0800 Subject: [PATCH 0186/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2271ca4cd..fb1ab2b2e 100644 --- a/README.md +++ b/README.md @@ -580,5 +580,6 @@ Get help. Discuss. Chat. * [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic * [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video. * [areajugonesbot](http://t.me/areajugonesbot)([link](http://t.me/areajugonesbot)) - The areajugonesbot sends news published on the videogames blog Areajugones to Telegram. +* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram)) Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From af991ea76e526dc8e1848f35e26a96838c1e719e Mon Sep 17 00:00:00 2001 From: Fumycat Date: Sun, 20 Aug 2017 01:36:08 +0700 Subject: [PATCH 0187/1808] Fix optional parameter --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 671f103fa..40f410dc9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -924,7 +924,7 @@ def create_new_sticker_set(self, user_id, name, title, png_sticker, emojis, cont return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, contains_masks, mask_position) - def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position): + def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=None): """ Use this method to add a new sticker to a set created by the bot. Returns True on success. :param user_id: From 15d287919d62b25bbe41ca00ef8ac20075636006 Mon Sep 17 00:00:00 2001 From: mostafaqanbaryan <30743448+mostafaqanbaryan@users.noreply.github.com> Date: Mon, 21 Aug 2017 14:40:47 +0430 Subject: [PATCH 0188/1808] Update __init__.py Add clear_step_handler() for resetting bot --- telebot/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 671f103fa..c08df6821 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -998,6 +998,15 @@ def register_next_step_handler(self, message, callback): self.pre_message_subscribers_next_step[chat_id].append(callback) else: self.pre_message_subscribers_next_step[chat_id] = [callback] + + def clear_step_handler(self, message): + """ + Clears all callback functions registered by register_next_step_handler(). + + :param message: The message for which we want to handle new message after that in same chat. + """ + chat_id = message.chat.id + self.pre_message_subscribers_next_step[chat_id] = [] def _notify_message_next_handler(self, new_messages): for message in new_messages: From 211f1c607d9f827db20cdb57b1ac22af07c3a16e Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Wed, 23 Aug 2017 10:30:32 +0300 Subject: [PATCH 0189/1808] Bot API 3.3: - Added the new field pinned_message to the Chat object. - Added the new fields author_signature and forward_signature to the Message object. - Added the new field is_bot to the User object. --- telebot/types.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 5698c0bb3..7be236e32 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -177,14 +177,16 @@ class User(JsonDeserializable): def de_json(cls, json_string): obj = cls.check_json(json_string) id = obj['id'] + is_bot = obj['is_bot'] first_name = obj['first_name'] last_name = obj.get('last_name') username = obj.get('username') language_code = obj.get('language_code') - return cls(id, first_name, last_name, username, language_code) + return cls(id, is_bot, first_name, last_name, username, language_code) - def __init__(self, id, first_name, last_name=None, username=None, language_code=None): + def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None): self.id = id + self.is_bot = is_bot self.first_name = first_name self.username = username self.last_name = last_name @@ -220,11 +222,15 @@ def de_json(cls, json_string): photo = ChatPhoto.de_json(obj['photo']) description = obj.get('description') invite_link = obj.get('invite_link') + pinned_message = None + if 'pinned_message' in obj: + pinned_message = Message.de_json(obj['pinned_message']) return cls(id, type, title, username, first_name, last_name, all_members_are_administrators, - photo, description, invite_link) + photo, description, invite_link, pinned_message) def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, - all_members_are_administrators=None, photo=None, description=None, invite_link=None): + all_members_are_administrators=None, photo=None, description=None, invite_link=None, + pinned_message=None): self.type = type self.last_name = last_name self.first_name = first_name @@ -235,6 +241,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, last_na self.photo = photo self.description = description self.invite_link = invite_link + self.pinned_message = pinned_message class Message(JsonDeserializable): @@ -255,12 +262,16 @@ def de_json(cls, json_string): opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) if 'forward_from_message_id' in obj: opts['forward_from_message_id'] = obj.get('forward_from_message_id') + if 'forward_signature' in obj: + opts['forward_signature'] = obj.get('forward_signature') if 'forward_date' in obj: opts['forward_date'] = obj.get('forward_date') if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) if 'edit_date' in obj: opts['edit_date'] = obj.get('edit_date') + if 'author_signature' in obj: + opts['author_signature'] = obj.get('author_signature') if 'text' in obj: opts['text'] = obj['text'] content_type = 'text' From 7d3737466715b62f2f180011ff7dc3d9bbfaae4d Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 28 Aug 2017 12:13:26 +0800 Subject: [PATCH 0190/1808] Fix test case. --- tests/test_telebot.py | 2 +- tests/test_types.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 8c0cf760d..4d1b51219 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -389,7 +389,7 @@ def test_edit_markup(self): def create_text_message(self, text): params = {'text': text} - chat = types.User(11, 'test') + chat = types.User(11, False, 'test') return types.Message(1, None, None, chat, 'text', params) def test_is_string_unicode(self): diff --git a/tests/test_types.py b/tests/test_types.py index a91f7683d..c174d420b 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -6,19 +6,19 @@ def test_json_user(): - jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot"}' + jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot","is_bot":true}' u = types.User.de_json(jsonstring) assert u.id == 101176298 def test_json_message(): - jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}' + jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir","is_bot":true},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}' msg = types.Message.de_json(jsonstring) assert msg.text == 'HIHI' def test_json_message_group(): - json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}' + json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG","is_bot":true},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}' msg = types.Message.de_json(json_string) assert msg.text == 'HIHI' assert len(msg.chat.title) != 0 @@ -39,7 +39,7 @@ def test_json_Document(): def test_json_Message_Audio(): - json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}' + json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd","is_bot":true },"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}' msg = types.Message.de_json(json_string) assert msg.audio.duration == 1 assert msg.content_type == 'audio' @@ -48,7 +48,7 @@ def test_json_Message_Audio(): def test_json_Message_Sticker(): - json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' + json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb.height == 60 @@ -56,7 +56,7 @@ def test_json_Message_Sticker(): def test_json_Message_Sticker_without_thumb(): - json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' + json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb == None @@ -64,21 +64,21 @@ def test_json_Message_Sticker_without_thumb(): def test_json_Message_Document(): - json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}' + json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}' msg = types.Message.de_json(json_string) assert msg.document.file_name == 'Text File' assert msg.content_type == 'document' def test_json_Message_Photo(): - json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}' + json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}' msg = types.Message.de_json(json_string) assert len(msg.photo) == 3 assert msg.content_type == 'photo' def test_json_Message_Video(): - json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}' + json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}' msg = types.Message.de_json(json_string) assert msg.video assert msg.video.duration == 3 @@ -87,7 +87,7 @@ def test_json_Message_Video(): def test_json_Message_Location(): - json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}' + json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}' msg = types.Message.de_json(json_string) assert msg.location.latitude == 26.090577 assert msg.content_type == 'location' @@ -114,7 +114,7 @@ def test_json_voice(): assert voice.file_size == 10481 def test_json_update(): - json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}' + json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"is_bot":true,"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}' update = types.Update.de_json(json_string) assert update.update_id == 938203 assert update.message.message_id == 241 From 4a11bb60b457c391658e913922b5615169770604 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 8 Sep 2017 15:47:16 +0800 Subject: [PATCH 0191/1808] Update new_chat_member to new_chat_members --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ef89e051..bc748e619 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)). The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings: -`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_member`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`. +`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`. You can use some types in one function. Example: From 0a80fafd76c089cfae61e60772abb6a1443ee436 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sat, 9 Sep 2017 21:32:13 +0800 Subject: [PATCH 0192/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bc748e619..9aaf7e502 100644 --- a/README.md +++ b/README.md @@ -583,5 +583,6 @@ Get help. Discuss. Chat. * [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service. * [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service. * [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram)) +* [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 600c014515b5f73a668b5502f0595849b55544b9 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Fri, 22 Sep 2017 01:08:54 +0300 Subject: [PATCH 0193/1808] Added missing author_signature field to Message object `author_signature` field was checked, but never added to `Message` object. --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index 7be236e32..eacc2b4c0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -383,6 +383,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.forward_date = None self.reply_to_message = None self.edit_date = None + self.author_signature = None self.text = None self.entities = None self.audio = None From 5f8d99664ea91962e0d955bb2a5abcedb100a449 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Sun, 22 Oct 2017 19:50:51 +0300 Subject: [PATCH 0194/1808] Bot API 3.4: new methods for live locations, new objects. --- telebot/__init__.py | 39 +++++++++++++++++++++++++++++++++++-- telebot/apihelper.py | 46 +++++++++++++++++++++++++++++++++++++++++++- telebot/types.py | 20 +++++++++++++++---- 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c08df6821..d1099ba67 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -407,6 +407,32 @@ def get_chat_members_count(self, chat_id): result = apihelper.get_chat_members_count(self.token, chat_id) return result + def set_chat_sticker_set(self, chat_id, sticker_set_name): + """ + Use this method to set a new group sticker set for a supergroup. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + Use the field can_set_sticker_set optionally returned in getChat requests to check + if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param sticker_set_name: Name of the sticker set to be set as the group sticker set + :return: + """ + result = apihelper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) + return result + + def delete_chat_sticker_set(self, chat_id): + """ + Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat + for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set + optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :return: + """ + result = apihelper.delete_chat_sticker_set(self.token, chat_id) + return result + def get_chat_member(self, chat_id, user_id): """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. @@ -568,19 +594,20 @@ def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_me apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, disable_notification, timeout)) - def send_location(self, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None, + def send_location(self, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None): """ Use this method to send point on the map. :param chat_id: :param latitude: :param longitude: + :param live_period :param reply_to_message_id: :param reply_markup: :return: API reply. """ return types.Message.de_json( - apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup, + apihelper.send_location(self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, reply_markup, disable_notification)) def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, @@ -1245,6 +1272,14 @@ def get_chat_administrators(self, *args): def get_chat_members_count(self, *args): return TeleBot.get_chat_members_count(self, *args) + @util.async() + def set_chat_sticker_set(self, *args): + return TeleBot.set_chat_sticker_set(self, *args) + + @util.async() + def delete_chat_sticker_set(self, *args): + return TeleBot.delete_chat_sticker_set(self, *args) + @util.async() def get_chat_member(self, *args): return TeleBot.get_chat_member(self, *args) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a808eade4..3965c2a99 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -209,6 +209,18 @@ def get_chat_members_count(token, chat_id): return _make_request(token, method_url, params=payload) +def set_chat_sticker_set(token, chat_id, sticker_set_name): + method_url = r'setChatStickerSet' + payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name} + return _make_request(token, method_url, params=payload) + + +def delete_chat_sticker_set(token, chat_id): + method_url = r'deleteChatStickerSet' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + + def get_chat_member(token, chat_id, user_id): method_url = r'getChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} @@ -243,10 +255,12 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re return _make_request(token, method_url, params=payload, files=files, method='post') -def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, reply_markup=None, +def send_location(token, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} + if live_period: + payload['live_perion'] = live_period if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: @@ -256,6 +270,36 @@ def send_location(token, chat_id, latitude, longitude, reply_to_message_id=None, return _make_request(token, method_url, params=payload) +def edit_message_live_location(token, latitude, longitude, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None): + method_url = r'editMessageLiveLocation' + payload = {'latitude': latitude, 'longitude': longitude} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + + +def stop_message_live_location(token, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None): + method_url = r'stopMessageLiveLocation' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + + def send_venue(token, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): method_url = r'sendVenue' diff --git a/telebot/types.py b/telebot/types.py index eacc2b4c0..37d23d09b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -225,12 +225,14 @@ def de_json(cls, json_string): pinned_message = None if 'pinned_message' in obj: pinned_message = Message.de_json(obj['pinned_message']) + sticker_set_name = obj.get('sticker_set_name') + can_set_sticker_set = obj.get('can_set_sticker_set') return cls(id, type, title, username, first_name, last_name, all_members_are_administrators, - photo, description, invite_link, pinned_message) + photo, description, invite_link, pinned_message, sticker_set_name, can_set_sticker_set) def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, all_members_are_administrators=None, photo=None, description=None, invite_link=None, - pinned_message=None): + pinned_message=None, sticker_set_name=None, can_set_sticker_set=None): self.type = type self.last_name = last_name self.first_name = first_name @@ -242,6 +244,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, last_na self.description = description self.invite_link = invite_link self.pinned_message = pinned_message + self.sticker_set_name = sticker_set_name + self.can_set_sticker_set = can_set_sticker_set class Message(JsonDeserializable): @@ -277,6 +281,8 @@ def de_json(cls, json_string): content_type = 'text' if 'entities' in obj: opts['entities'] = Message.parse_entities(obj['entities']) + if 'caption_entities' in obj: + opts['caption_entities'] = Message.parse_entities(obj['caption_entities']) if 'audio' in obj: opts['audio'] = Audio.de_json(obj['audio']) content_type = 'audio' @@ -984,12 +990,15 @@ def to_dic(self): class InputLocationMessageContent(Dictionaryable): - def __init__(self, latitude, longitude): + def __init__(self, latitude, longitude, live_period=None): self.latitude = latitude self.longitude = longitude + self.live_period = live_period def to_dic(self): json_dic = {'latitude': self.latitude, 'longitude': self.longitude} + if self.live_period: + json_dic['live_period'] = self.live_period return json_dic @@ -1386,13 +1395,14 @@ def to_json(self): class InlineQueryResultLocation(JsonSerializable): - def __init__(self, id, title, latitude, longitude, reply_markup=None, + def __init__(self, id, title, latitude, longitude, 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 self.latitude = latitude self.longitude = longitude + self.live_period = live_period self.reply_markup = reply_markup self.input_message_content = input_message_content self.thumb_url = thumb_url @@ -1402,6 +1412,8 @@ def __init__(self, id, title, latitude, longitude, reply_markup=None, def to_json(self): json_dict = {'type': self.type, 'id': self.id, 'latitude': self.latitude, 'longitude': self.longitude, 'title': self.title} + if self.live_period: + json_dict['live_period'] = self.live_period if self.thumb_url: json_dict['thumb_url'] = self.thumb_url if self.thumb_width: From 8e71a612a666d53ac9880544e2c97e134795c1e8 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Sun, 22 Oct 2017 20:07:51 +0300 Subject: [PATCH 0195/1808] Added missing methods definitions to __init__.py and Async Telebot --- telebot/__init__.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index d1099ba67..c2ce45cb6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -610,6 +610,35 @@ def send_location(self, chat_id, latitude, longitude, live_period=None, reply_to apihelper.send_location(self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, reply_markup, disable_notification)) + def edit_message_live_location(self, latitude, longitude, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None): + """ + Use this method to edit live location + :param latitude: + :param longitude: + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ + return types.Message.de_json( + apihelper.edit_message_live_location(self, latitude, longitude, chat_id, message_id, + inline_message_id, reply_markup)) + + def stop_message_live_location(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + """ + Use this method to stop updating a live location message sent by the bot + or via the bot (for inline bots) before live_period expires + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ + return types.Message.de_json( + apihelper.stop_message_live_location(self, chat_id, message_id, inline_message_id, reply_markup)) + def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): """ @@ -1328,6 +1357,14 @@ def send_video_note(self, *args, **kwargs): def send_location(self, *args, **kwargs): return TeleBot.send_location(self, *args, **kwargs) + @util.async() + def edit_message_live_location(self, *args, **kwargs): + return TeleBot.edit_message_live_location(self, *args, **kwargs) + + @util.async() + def stop_message_live_location(self, *args, **kwargs): + return TeleBot.stop_message_live_location(self, *args, **kwargs) + @util.async() def send_venue(self, *args, **kwargs): return TeleBot.send_venue(self, *args, **kwargs) From d8587419e126d438517f49561070ac857584f2f6 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sat, 4 Nov 2017 15:09:29 +0200 Subject: [PATCH 0196/1808] Fixed bug when message has next step handler and exec command handlers. --- telebot/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c2ce45cb6..44dd27ba8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1259,10 +1259,13 @@ def _test_filter(self, filter, filter_value, message): def _notify_command_handlers(self, handlers, new_messages): for message in new_messages: - for message_handler in handlers: - if self._test_message_handler(message_handler, message): - self._exec_task(message_handler['function'], message) - break + # if message has next step handler, dont exec command handlers + if (isinstance(message, types.CallbackQuery)) or \ + (isinstance(message, types.Message) and (message.chat.id not in self.message_subscribers_next_step)): + for message_handler in handlers: + if self._test_message_handler(message_handler, message): + self._exec_task(message_handler['function'], message) + break class AsyncTeleBot(TeleBot): From 3986f33d3a0fe46cf2aec2becf7047df8c3ef58d Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 6 Nov 2017 08:42:57 +0800 Subject: [PATCH 0197/1808] Fix caption_entities without default value. --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index 37d23d09b..a23e0540d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -392,6 +392,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.author_signature = None self.text = None self.entities = None + self.caption_entities = None self.audio = None self.document = None self.photo = None From 46c803bf55b56efa0cee5709d2166f164c0ff221 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 13 Nov 2017 10:14:10 +0800 Subject: [PATCH 0198/1808] Fix shipping_options bug. #414 Update version. --- setup.py | 2 +- telebot/apihelper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 369035328..cf6314675 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.2.0', + version='3.2.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3965c2a99..6d5a20451 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -768,7 +768,7 @@ def answer_shipping_query(token, shipping_query_id, ok, shipping_options=None, e method_url = 'answerShippingQuery' payload = {'shipping_query_id': shipping_query_id, 'ok': ok} if shipping_options: - payload['reply_markup'] = _convert_list_json_serializable(shipping_options) + payload['shipping_options'] = _convert_list_json_serializable(shipping_options) if error_message: payload['error_message'] = error_message return _make_request(token, method_url, params=payload) From e45ced958aee48a3f3d66810021698cd62dd8936 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 13 Nov 2017 10:19:13 +0800 Subject: [PATCH 0199/1808] Update version to support 3.4. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cf6314675..ef1589776 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.2.1', + version='3.4.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 8bdbc2401469a743f6f871c590d7a1b1a3f238e6 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 13 Nov 2017 10:25:39 +0800 Subject: [PATCH 0200/1808] Fix ShippingOption to_json. #414 --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index a23e0540d..f060842cb 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1814,7 +1814,7 @@ def to_json(self): price_list = [] for p in self.prices: price_list.append(p.to_dic()) - json_dict = {'id': self.id, 'title': self.title, 'prices': price_list} + json_dict = json.dumps({'id': self.id, 'title': self.title, 'prices': price_list}) return json_dict From 47624a556e3c19b15ced497720e4faf178b22498 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 13 Nov 2017 10:26:27 +0800 Subject: [PATCH 0201/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ef1589776..b88a57c2c 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.4.0', + version='3.4.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 7ac246b8016efd7e5b616754dc26ff81d9df321b Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 15 Nov 2017 00:42:27 +0800 Subject: [PATCH 0202/1808] Fix inline_query_handler not work. --- telebot/__init__.py | 12 ++++++------ tests/test_telebot.py | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 44dd27ba8..37244b76e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1260,12 +1260,12 @@ def _test_filter(self, filter, filter_value, message): def _notify_command_handlers(self, handlers, new_messages): for message in new_messages: # if message has next step handler, dont exec command handlers - if (isinstance(message, types.CallbackQuery)) or \ - (isinstance(message, types.Message) and (message.chat.id not in self.message_subscribers_next_step)): - for message_handler in handlers: - if self._test_message_handler(message_handler, message): - self._exec_task(message_handler['function'], message) - break + if hasattr(message, 'chat') and message.chat and (message.chat.id in self.message_subscribers_next_step): + continue + for message_handler in handlers: + if self._test_message_handler(message_handler, message): + self._exec_task(message_handler['function'], message) + break class AsyncTeleBot(TeleBot): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 4d1b51219..3773ae4cd 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -201,7 +201,8 @@ def test_send_audio(self): def test_send_audio_dis_noti(self): file_data = open('./test_data/record.mp3', 'rb') tb = telebot.TeleBot(TOKEN) - ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram', disable_notification=True) + ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram', + disable_notification=True) assert ret_msg.content_type == 'audio' assert ret_msg.audio.performer == 'eternnoir' assert ret_msg.audio.title == 'pyTelegram' From a43f037bc9ee79bee30d91a5438f9195908531f5 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 15 Nov 2017 00:42:50 +0800 Subject: [PATCH 0203/1808] Version update. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b88a57c2c..3dce0d805 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.4.1', + version='3.4.2', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From e1a3ccadb7aa81f560760dfa9368528efc7a79bb Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 29 Nov 2017 13:45:25 +0800 Subject: [PATCH 0204/1808] Add sendMediaGroup method. --- telebot/__init__.py | 15 +++++++++++++++ telebot/apihelper.py | 12 ++++++++++++ telebot/types.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 37244b76e..81912c7eb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -594,6 +594,21 @@ def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_me apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, disable_notification, timeout)) + def send_media_group(self, chat_id, media, disable_notification=None, reply_to_message_id=None): + """ + send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + :param chat_id: + :param media: + :param disable_notification: + :param reply_to_message_id: + :return: + """ + result = apihelper.send_media_group(self.token, chat_id, media, disable_notification, reply_to_message_id) + ret = [] + for msg in result: + ret.append(types.Message.de_json(msg)) + return ret + def send_location(self, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6d5a20451..be19e7c2f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -255,6 +255,18 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re return _make_request(token, method_url, params=payload, files=files, method='post') +def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None): + method_url = r'sendMediaGroup' + media_json = _convert_list_json_serializable(media) + print(media_json) + payload = {'chat_id': chat_id, 'media': media_json} + if disable_notification: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + return _make_request(token, method_url, params=payload) + + def send_location(token, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None): method_url = r'sendLocation' diff --git a/telebot/types.py b/telebot/types.py index f060842cb..f14c9a134 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1959,3 +1959,45 @@ def to_json(self): def to_dic(self): return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale} + +# InputMedia + +class InputMediaPhoto(JsonSerializable): + def __init__(self, media, caption=None): + self.type = "photo" + self.media = media + self.caption = caption + + def to_json(self): + return json.dumps(self.to_dic()) + + def to_dic(self): + ret = {'type': self.type, 'media': self.media} + if self.caption: + ret['caption'] = self.caption + return ret + + +class InputMediaVideo(JsonSerializable): + def __init__(self, media, caption=None, width=None, height=None, duration=None): + self.type = "video" + self.media = media + self.caption = caption + self.width = width + self.height = height + self.duration = duration + + def to_json(self): + return json.dumps(self.to_dic()) + + def to_dic(self): + ret = {'type': self.type, 'media': self.media} + if self.caption: + ret['caption'] = self.caption + if self.width: + ret['width'] = self.width + if self.height: + ret['height'] = self.height + if self.duration: + ret['duration'] = self.duration + return ret From 8528ca9e4e60f82f9f5ba1f970b65eb5acb75a0e Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 29 Nov 2017 13:48:34 +0800 Subject: [PATCH 0205/1808] Add some message content type. Fix #426 --- telebot/types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index f14c9a134..32d987408 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -339,16 +339,20 @@ def de_json(cls, json_string): opts['delete_chat_photo'] = obj['delete_chat_photo'] if 'group_chat_created' in obj: opts['group_chat_created'] = obj['group_chat_created'] + content_type = 'group_chat_created' if 'supergroup_chat_created' in obj: opts['supergroup_chat_created'] = obj['supergroup_chat_created'] + content_type = 'supergroup_chat_created' if 'channel_chat_created' in obj: opts['channel_chat_created'] = obj['channel_chat_created'] + content_type = 'channel_chat_created' if 'migrate_to_chat_id' in obj: opts['migrate_to_chat_id'] = obj['migrate_to_chat_id'] if 'migrate_from_chat_id' in obj: opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] if 'pinned_message' in obj: opts['pinned_message'] = Message.de_json(obj['pinned_message']) + content_type = 'pinned_message' if 'invoice' in obj: opts['invoice'] = Invoice.de_json(obj['invoice']) content_type = 'invoice' From 2493b200a4e0409052be480a9c494cfc54b2e953 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 29 Nov 2017 13:53:39 +0800 Subject: [PATCH 0206/1808] Add provider_data to sendInvoice. --- telebot/__init__.py | 13 +++++++++---- telebot/apihelper.py | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 81912c7eb..ec48226d1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -622,7 +622,8 @@ def send_location(self, chat_id, latitude, longitude, live_period=None, reply_to :return: API reply. """ return types.Message.de_json( - apihelper.send_location(self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, reply_markup, + apihelper.send_location(self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, + reply_markup, disable_notification)) def edit_message_live_location(self, latitude, longitude, chat_id=None, message_id=None, @@ -888,12 +889,12 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None): + disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=Noen): result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, is_flexible, - disable_notification, reply_to_message_id, reply_markup) + disable_notification, reply_to_message_id, reply_markup, provider_data) return types.Message.de_json(result) def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): @@ -1069,7 +1070,7 @@ def register_next_step_handler(self, message, callback): self.pre_message_subscribers_next_step[chat_id].append(callback) else: self.pre_message_subscribers_next_step[chat_id] = [callback] - + def clear_step_handler(self, message): """ Clears all callback functions registered by register_next_step_handler(). @@ -1371,6 +1372,10 @@ def send_video(self, *args, **kwargs): def send_video_note(self, *args, **kwargs): return TeleBot.send_video_note(self, *args, **kwargs) + @util.async() + def send_media_group(self, *args, **kwargs): + return TeleBot.send_media_group(self, *args, **kwargs) + @util.async() def send_location(self, *args, **kwargs): return TeleBot.send_location(self, *args, **kwargs) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index be19e7c2f..6b52f7201 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -710,7 +710,7 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None): + disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -764,6 +764,8 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if provider_data: + payload['provider_data'] = provider_data return _make_request(token, method_url, params=payload) From c300195b4996f5d1fbf89bd11cb496b8208d8a17 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 29 Nov 2017 13:59:47 +0800 Subject: [PATCH 0207/1808] Add provider_data . --- telebot/__init__.py | 2 +- tests/test_telebot.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ec48226d1..97dfa4b44 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -889,7 +889,7 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=Noen): + disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None): result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 3773ae4cd..b696bf4e2 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -410,3 +410,11 @@ def test_send_video_note(self): tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_video_note(CHAT_ID, file_data) assert ret_msg.message_id + + def test_send_media_group(self): + tb = telebot.TeleBot(TOKEN) + img1 = 'https://i.imgur.com/CjXjcnU.png' + img2 = 'https://i.imgur.com/CjXjcnU.png' + medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")] + result = tb.send_media_group(CHAT_ID, medias) + assert len(result) == 2 From d0b4bb7c691c3f78704b1dbf2f556567ff7cf234 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 29 Nov 2017 14:09:57 +0800 Subject: [PATCH 0208/1808] Bump version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3dce0d805..a5d06f7c4 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.4.2', + version='3.5.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From b2f376a9067348be6791fd6ee8080adb0ff76836 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 30 Nov 2017 23:34:07 +0800 Subject: [PATCH 0209/1808] Remove debug message. Add content_type --- setup.py | 2 +- telebot/apihelper.py | 1 - telebot/types.py | 5 +++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index a5d06f7c4..6c78ce9a8 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.5.0', + version='3.5.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6b52f7201..8dd78c315 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -258,7 +258,6 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None): method_url = r'sendMediaGroup' media_json = _convert_list_json_serializable(media) - print(media_json) payload = {'chat_id': chat_id, 'media': media_json} if disable_notification: payload['disable_notification'] = disable_notification diff --git a/telebot/types.py b/telebot/types.py index 32d987408..1621b1ed2 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -333,10 +333,13 @@ def de_json(cls, json_string): content_type = 'left_chat_member' if 'new_chat_title' in obj: opts['new_chat_title'] = obj['new_chat_title'] + content_type = 'new_chat_title' if 'new_chat_photo' in obj: opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo']) + content_type = 'new_chat_photo' if 'delete_chat_photo' in obj: opts['delete_chat_photo'] = obj['delete_chat_photo'] + content_type = 'delete_chat_photo' if 'group_chat_created' in obj: opts['group_chat_created'] = obj['group_chat_created'] content_type = 'group_chat_created' @@ -348,8 +351,10 @@ def de_json(cls, json_string): content_type = 'channel_chat_created' if 'migrate_to_chat_id' in obj: opts['migrate_to_chat_id'] = obj['migrate_to_chat_id'] + content_type = 'migrate_to_chat_id' if 'migrate_from_chat_id' in obj: opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] + content_type = 'migrate_from_chat_id' if 'pinned_message' in obj: opts['pinned_message'] = Message.de_json(obj['pinned_message']) content_type = 'pinned_message' From 8444ea588a72160b90510f5ae3ab5536471c132a Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sun, 3 Dec 2017 20:34:37 +0200 Subject: [PATCH 0210/1808] Added SmartySBot to README Added SmartySBot to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9aaf7e502..a78d9bbd5 100644 --- a/README.md +++ b/README.md @@ -584,5 +584,5 @@ Get help. Discuss. Chat. * [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service. * [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram)) * [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students. - +* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 8aa8fa59864a788b4563aed459ff4df650230014 Mon Sep 17 00:00:00 2001 From: JekaFST Date: Tue, 5 Dec 2017 00:21:05 +0300 Subject: [PATCH 0211/1808] Fix for SendLocation with live period Fix for payload['live_perion'] typo -> payload['live_period'] --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8dd78c315..a12d38d51 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -271,7 +271,7 @@ def send_location(token, chat_id, latitude, longitude, live_period=None, reply_t method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: - payload['live_perion'] = live_period + payload['live_period'] = live_period if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: From 60596a95b828667a16f6919f65d0d96de71a5c39 Mon Sep 17 00:00:00 2001 From: JekaFST Date: Tue, 5 Dec 2017 01:31:47 +0300 Subject: [PATCH 0212/1808] Edit and stop live location fixes .token was missed in apihelper's methods calls --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 97dfa4b44..190e55602 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -639,7 +639,7 @@ def edit_message_live_location(self, latitude, longitude, chat_id=None, message_ :return: """ return types.Message.de_json( - apihelper.edit_message_live_location(self, latitude, longitude, chat_id, message_id, + apihelper.edit_message_live_location(self.token, latitude, longitude, chat_id, message_id, inline_message_id, reply_markup)) def stop_message_live_location(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): @@ -653,7 +653,7 @@ def stop_message_live_location(self, chat_id=None, message_id=None, inline_messa :return: """ return types.Message.de_json( - apihelper.stop_message_live_location(self, chat_id, message_id, inline_message_id, reply_markup)) + apihelper.stop_message_live_location(self.token, chat_id, message_id, inline_message_id, reply_markup)) def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): From 8b2dea1d5661fd64ae6a0c33849b2d3111e2617f Mon Sep 17 00:00:00 2001 From: reacheight Date: Wed, 10 Jan 2018 00:26:44 +0300 Subject: [PATCH 0213/1808] fixed example of usage sendVideoNote method --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a78d9bbd5..733d31209 100644 --- a/README.md +++ b/README.md @@ -291,8 +291,8 @@ tb.send_video(chat_id, "FILEID") # sendVideoNote videonote = open('/tmp/videonote.mp4', 'rb') -tb.send_video(chat_id, videonote) -tb.send_video(chat_id, "FILEID") +tb.send_video_note(chat_id, videonote) +tb.send_video_note(chat_id, "FILEID") # sendLocation tb.send_location(chat_id, lat, lon) From 2637e29dbe8aca5b391eb0ce40fe6a6130fd09fe Mon Sep 17 00:00:00 2001 From: heyyyoyy Date: Mon, 15 Jan 2018 16:08:50 +0300 Subject: [PATCH 0214/1808] Updated sendMediaGroup method --- telebot/apihelper.py | 18 ++++++++++++++++-- telebot/types.py | 6 ++++-- telebot/util.py | 7 ++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a12d38d51..24c6c83fe 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -257,13 +257,14 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None): method_url = r'sendMediaGroup' - media_json = _convert_list_json_serializable(media) + media_json, files = _convert_input_media(media) payload = {'chat_id': chat_id, 'media': media_json} if disable_notification: payload['disable_notification'] = disable_notification if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id - return _make_request(token, method_url, params=payload) + return _make_request(token, method_url, params=payload, method='post' if files else 'get', + files=files if files else None) def send_location(token, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None, @@ -916,6 +917,19 @@ def _convert_markup(markup): return markup +def _convert_input_media(array): + media = [] + files = {} + for input_media in array: + if isinstance(input_media, types.JsonSerializable): + media_dict = input_media.to_dic() + if media_dict['media'].startswith('attach://'): + key = media_dict['media'].replace('attach://', '') + files[key] = input_media.media + media.append(media_dict) + return json.dumps(media), files + + def _no_encode(func): def wrapper(key, val): if key == 'filename': diff --git a/telebot/types.py b/telebot/types.py index 1621b1ed2..5ea9b8926 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1981,7 +1981,8 @@ def to_json(self): return json.dumps(self.to_dic()) def to_dic(self): - ret = {'type': self.type, 'media': self.media} + ret = {'type': self.type, 'media': 'attach://' + util.generate_random_token() + if not util.is_string(self.media) else self.media} if self.caption: ret['caption'] = self.caption return ret @@ -2000,7 +2001,8 @@ def to_json(self): return json.dumps(self.to_dic()) def to_dic(self): - ret = {'type': self.type, 'media': self.media} + ret = {'type': self.type, 'media': 'attach://' + util.generate_random_token() + if not util.is_string(self.media) else self.media} if self.caption: ret['caption'] = self.caption if self.width: diff --git a/telebot/util.py b/telebot/util.py index ab7650d7e..ca080cc4d 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- - +import random +import string import threading import traceback import re @@ -254,3 +255,7 @@ def per_thread(key, construct_value): value = construct_value() setattr(thread_local, key, value) return value + + +def generate_random_token(): + return ''.join(random.sample(string.ascii_letters, 16)) \ No newline at end of file From afac177d7db70af537a42764f3f4b434963f624b Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 24 Jan 2018 19:05:38 +0800 Subject: [PATCH 0215/1808] Fix missing media_group_id in message. --- telebot/types.py | 3 +++ tests/test_telebot.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 1621b1ed2..481e09ffd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -274,6 +274,8 @@ def de_json(cls, json_string): opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) if 'edit_date' in obj: opts['edit_date'] = obj.get('edit_date') + if 'media_group_id' in obj: + opts['media_group_id'] = obj.get('media_group_id') if 'author_signature' in obj: opts['author_signature'] = obj.get('author_signature') if 'text' in obj: @@ -398,6 +400,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.forward_date = None self.reply_to_message = None self.edit_date = None + self.media_group_id = None self.author_signature = None self.text = None self.entities = None diff --git a/tests/test_telebot.py b/tests/test_telebot.py index b696bf4e2..c6792130a 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -418,3 +418,5 @@ def test_send_media_group(self): medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")] result = tb.send_media_group(CHAT_ID, medias) assert len(result) == 2 + assert result[0].media_group_id is not None + assert result[0].media_group_id == result[1].media_group_id From 94b4a2598064d87bb2e7f879417186b530e1bab5 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Wed, 24 Jan 2018 19:11:03 +0800 Subject: [PATCH 0216/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6c78ce9a8..0f723b4e0 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.5.1', + version='3.5.2', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From f47653d2e4c38265258960174f7374f573d0a437 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Wed, 24 Jan 2018 19:13:29 +0800 Subject: [PATCH 0217/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 733d31209..3ab083dca 100644 --- a/README.md +++ b/README.md @@ -585,4 +585,6 @@ Get help. Discuss. Chat. * [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram)) * [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students. * [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. +* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. + Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 2e199a56842f9b53e004b4890b7b50ff6537a7a2 Mon Sep 17 00:00:00 2001 From: heyyyoyy Date: Wed, 14 Feb 2018 20:27:55 +0000 Subject: [PATCH 0218/1808] Bot Api 3.6 --- telebot/__init__.py | 26 ++++++++++++++--------- telebot/apihelper.py | 22 +++++++++++++++----- telebot/types.py | 18 ++++++++++++++-- tests/test_telebot.py | 48 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 17 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7347c3002..1d25e4c27 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -486,24 +486,25 @@ def delete_message(self, chat_id, message_id): return apihelper.delete_message(self.token, chat_id, message_id) def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): + parse_mode=None, disable_notification=None): """ Use this method to send photos. :param disable_notification: :param chat_id: :param photo: :param caption: + :param parse_mode :param reply_to_message_id: :param reply_markup: :return: API reply. """ return types.Message.de_json( apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, - disable_notification)) + parse_mode, disable_notification)) def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, disable_notification=None, timeout=None): + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. :param chat_id:Unique identifier for the message recipient @@ -511,16 +512,17 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None :param duration:Duration of the audio in seconds :param performer:Performer :param title:Track name + :param parse_mode :param reply_to_message_id:If the message is a reply, ID of the original message :param reply_markup: :return: Message """ return types.Message.de_json( apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, - reply_markup, disable_notification, timeout)) + reply_markup, parse_mode, disable_notification, timeout)) def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. :param chat_id:Unique identifier for the message recipient. @@ -528,25 +530,27 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa :param duration:Duration of sent audio in seconds :param reply_to_message_id: :param reply_markup: + :param parse_mode :return: Message """ return types.Message.de_json( apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, - disable_notification, timeout)) + parse_mode, disable_notification, timeout)) def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None, - disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send general files. :param chat_id: :param data: :param reply_to_message_id: :param reply_markup: + :param parse_mode :return: API reply. """ return types.Message.de_json( apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup, - disable_notification, timeout, caption=caption)) + parse_mode, disable_notification, timeout, caption=caption)) def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): @@ -563,20 +567,22 @@ def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=Non disable_notification, timeout)) def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None): """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds :param caption: String : Video caption (may also be used when resending videos by file_id). + :param parse_mode: + :param supports_streaming: :param reply_to_message_id: :param reply_markup: :return: """ return types.Message.de_json( apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, - disable_notification, timeout)) + parse_mode, supports_streaming, disable_notification, timeout)) def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 24c6c83fe..971b2bf7b 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -236,7 +236,7 @@ def forward_message(token, chat_id, from_chat_id, message_id, disable_notificati def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): + parse_mode=None, disable_notification=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -250,6 +250,8 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode if disable_notification: payload['disable_notification'] = disable_notification return _make_request(token, method_url, params=payload, files=files, method='post') @@ -349,7 +351,7 @@ def send_chat_action(token, chat_id, action): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -365,6 +367,10 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode + if supports_streaming: + payload['supports_streaming'] = supports_streaming if disable_notification: payload['disable_notification'] = disable_notification if timeout: @@ -373,7 +379,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -389,6 +395,8 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode if disable_notification: payload['disable_notification'] = disable_notification if timeout: @@ -423,7 +431,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, disable_notification=None, timeout=None): + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -443,6 +451,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode if disable_notification: payload['disable_notification'] = disable_notification if timeout: @@ -451,7 +461,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None, - timeout=None, caption=None): + timeout=None, parse_mode=None, caption=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -463,6 +473,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if parse_mode and data_type == 'document': + payload['parse_mode'] = parse_mode if disable_notification: payload['disable_notification'] = disable_notification if timeout: diff --git a/telebot/types.py b/telebot/types.py index 3ad8fcddb..f497a7c8b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -366,6 +366,9 @@ def de_json(cls, json_string): if 'successful_payment' in obj: opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment']) content_type = 'successful_payment' + if 'connected_website' in obj: + opts['connected_website'] = obj['connected_website'] + content_type = 'connected_website' return cls(message_id, from_user, date, chat, content_type, opts) @classmethod @@ -430,6 +433,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.pinned_message = None self.invoice = None self.successful_payment = None + self.connected_website = None for key in options: setattr(self, key, options[key]) @@ -1975,10 +1979,11 @@ def to_dic(self): # InputMedia class InputMediaPhoto(JsonSerializable): - def __init__(self, media, caption=None): + def __init__(self, media, caption=None, parse_mode=None): self.type = "photo" self.media = media self.caption = caption + self.parse_mode = parse_mode def to_json(self): return json.dumps(self.to_dic()) @@ -1988,17 +1993,22 @@ def to_dic(self): if not util.is_string(self.media) else self.media} if self.caption: ret['caption'] = self.caption + if self.parse_mode: + ret['parse_mode'] = self.parse_mode return ret class InputMediaVideo(JsonSerializable): - def __init__(self, media, caption=None, width=None, height=None, duration=None): + def __init__(self, media, caption=None, parse_mode=None, width=None, height=None, duration=None, + supports_streaming=None): self.type = "video" self.media = media self.caption = caption + self.parse_mode = parse_mode self.width = width self.height = height self.duration = duration + self.supports_streaming = supports_streaming def to_json(self): return json.dumps(self.to_dic()) @@ -2008,10 +2018,14 @@ def to_dic(self): if not util.is_string(self.media) else self.media} if self.caption: ret['caption'] = self.caption + if self.parse_mode: + ret['parse_mode'] = self.parse_mode if self.width: ret['width'] = self.width if self.height: ret['height'] = self.height if self.duration: ret['duration'] = self.duration + if self.supports_streaming: + ret['supports_streaming'] = self.supports_streaming return ret diff --git a/tests/test_telebot.py b/tests/test_telebot.py index c6792130a..5a5599d40 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -420,3 +420,51 @@ def test_send_media_group(self): assert len(result) == 2 assert result[0].media_group_id is not None assert result[0].media_group_id == result[1].media_group_id + + def test_send_media_group_local_files(self): + photo = open('../examples/detailed_example/kitten.jpg', 'rb') + video = open('./test_data/test_video.mp4', 'rb') + tb = telebot.TeleBot(TOKEN) + medias = [types.InputMediaPhoto(photo, "View"), + types.InputMediaVideo(video)] + result = tb.send_media_group(CHAT_ID, medias) + assert len(result) == 2 + assert result[0].media_group_id is not None + assert result[1].media_group_id is not None + + def test_send_photo_formating_caption(self): + file_data = open('../examples/detailed_example/kitten.jpg', 'rb') + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') + assert ret_msg.caption_entities[0].type == 'italic' + + def test_send_video_formatting_caption(self): + file_data = open('./test_data/test_video.mp4', 'rb') + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') + assert ret_msg.caption_entities[0].type == 'italic' + + def test_send_audio_formatting_caption(self): + file_data = open('./test_data/record.mp3', 'rb') + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_audio(CHAT_ID, file_data, caption='bold', parse_mode='HTML') + assert ret_msg.caption_entities[0].type == 'bold' + + def test_send_voice_formatting_caprion(self): + file_data = open('./test_data/record.ogg', 'rb') + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_voice(CHAT_ID, file_data, caption='bold', parse_mode='HTML') + assert ret_msg.caption_entities[0].type == 'bold' + assert ret_msg.voice.mime_type == 'audio/ogg' + + def test_send_media_group_formatting_caption(self): + tb = telebot.TeleBot(TOKEN) + img1 = 'https://i.imgur.com/CjXjcnU.png' + img2 = 'https://i.imgur.com/CjXjcnU.png' + medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'), + types.InputMediaPhoto(img2, "_Dog_", parse_mode='Markdown')] + result = tb.send_media_group(CHAT_ID, medias) + assert len(result) == 2 + assert result[0].media_group_id is not None + assert result[0].caption_entities[0].type == 'bold' + assert result[1].caption_entities[0].type == 'italic' \ No newline at end of file From 903b1dfd50f5b740d28a7bc8d933e037aa95776e Mon Sep 17 00:00:00 2001 From: heyyyoyy Date: Fri, 16 Feb 2018 14:19:35 +0000 Subject: [PATCH 0219/1808] added parse_mode in edit_message_caption --- telebot/__init__.py | 5 +++-- telebot/apihelper.py | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1d25e4c27..1d2b82646 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -909,9 +909,10 @@ def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, er def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None): return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) - def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, + parse_mode=None, reply_markup=None): result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - reply_markup) + parse_mode, reply_markup) if type(result) == bool: return result return types.Message.de_json(result) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 971b2bf7b..c0ec1b8e0 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -616,7 +616,8 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message return _make_request(token, method_url, params=payload) -def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): +def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, + parse_mode=None, reply_markup=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -625,6 +626,8 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m payload['message_id'] = message_id if inline_message_id: payload['inline_message_id'] = inline_message_id + if parse_mode: + payload['parse_mode'] = parse_mode if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) From 518c49f23a4532c3298765f70a0c8457015ed032 Mon Sep 17 00:00:00 2001 From: heyyyoyy Date: Fri, 16 Feb 2018 18:29:29 +0300 Subject: [PATCH 0220/1808] fixing formatting of caption in the method send_document --- telebot/__init__.py | 7 ++++--- telebot/apihelper.py | 4 ++-- tests/test_telebot.py | 8 +++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1d2b82646..d6c16e1ca 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -503,8 +503,8 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep parse_mode, disable_notification)) def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, - reply_to_message_id=None, - reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): + reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, + timeout=None): """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. :param chat_id:Unique identifier for the message recipient @@ -545,7 +545,8 @@ def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, r :param data: :param reply_to_message_id: :param reply_markup: - :param parse_mode + :param parse_mode: + :param disable_notification: :return: API reply. """ return types.Message.de_json( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c0ec1b8e0..a02c8e4cd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -460,8 +460,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non return _make_request(token, method_url, params=payload, files=files, method='post') -def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, disable_notification=None, - timeout=None, parse_mode=None, caption=None): +def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, + disable_notification=None, timeout=None, caption=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 5a5599d40..89b6f7ac2 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -467,4 +467,10 @@ def test_send_media_group_formatting_caption(self): assert len(result) == 2 assert result[0].media_group_id is not None assert result[0].caption_entities[0].type == 'bold' - assert result[1].caption_entities[0].type == 'italic' \ No newline at end of file + assert result[1].caption_entities[0].type == 'italic' + + def test_send_document_formating_caption(self): + file_data = open('../examples/detailed_example/kitten.jpg', 'rb') + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') + assert ret_msg.caption_entities[0].type == 'italic' From bc067662dc4cbbfbb4ad00787e7caa942e5a9f37 Mon Sep 17 00:00:00 2001 From: Dmytro Striletskyi Date: Fri, 16 Feb 2018 17:52:37 +0200 Subject: [PATCH 0221/1808] Make the Heroku example actual --- .../webhook_flask_heroku_echo.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index 27cf95515..62d0a90e7 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -1,29 +1,35 @@ -import telebot import os -from flask import Flask, request -bot = telebot.TeleBot('') +import telebot +from flask import Flask, request +TOKEN = '' +bot = telebot.TeleBot(TOKEN) server = Flask(__name__) + @bot.message_handler(commands=['start']) def start(message): bot.reply_to(message, 'Hello, ' + message.from_user.first_name) + @bot.message_handler(func=lambda message: True, content_types=['text']) def echo_message(message): bot.reply_to(message, message.text) -@server.route("/bot", methods=['POST']) + +@server.route('/' + TOKEN, methods=['POST']) def getMessage(): bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))]) return "!", 200 + @server.route("/") def webhook(): bot.remove_webhook() - bot.set_webhook(url="https://herokuProject_url/bot") + bot.set_webhook(url='https://your_heroku_project.com/' + TOKEN) return "!", 200 -server.run(host="0.0.0.0", port=os.environ.get('PORT', 5000)) -server = Flask(__name__) + +if __name__ == "__main__": + server.run(host="0.0.0.0", port=int(os.environ.get('PORT', 5000))) From 8495229ce1f999d969c93b3198f1a63300dbb347 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 2 Mar 2018 19:28:34 +0800 Subject: [PATCH 0222/1808] Update version 3.6.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0f723b4e0..3e3faa117 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.5.2', + version='3.6.0', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 48e6757686bfa0c476f21fd9a6ff661623ebe4b9 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 10 Mar 2018 14:41:34 +0800 Subject: [PATCH 0223/1808] Fix import logger problem. --- telebot/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index ca080cc4d..769f73ba8 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -14,9 +14,9 @@ import Queue except ImportError: import queue as Queue +import logging -from telebot import logger - +logger = logging.getLogger('TeleBot') thread_local = threading.local() From e01f17e3a0c1aa7c70acf00334537ff68ff38c84 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 10 Mar 2018 14:46:47 +0800 Subject: [PATCH 0224/1808] Bump version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3e3faa117..11c22130b 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.6.0', + version='3.6.1', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From afc9abc26921bc50b9d1a73e8a708748f515672a Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 10 Mar 2018 14:48:30 +0800 Subject: [PATCH 0225/1808] Add ci test for python 3.6 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 491d4b917..f5f202a4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "3.3" - "3.4" - "3.5" + - "3.6" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From dadcd5a577caffe7c3b495977f80fafb7d56fca1 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Mon, 19 Mar 2018 15:13:48 +0800 Subject: [PATCH 0226/1808] Update "Bots using this API" entry. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3ab083dca..5bda4edf1 100644 --- a/README.md +++ b/README.md @@ -586,5 +586,6 @@ Get help. Discuss. Chat. * [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students. * [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. * [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. +* [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From d17d28a14482e05ee9e47e731aaf49bb4ce96d37 Mon Sep 17 00:00:00 2001 From: German Date: Tue, 20 Mar 2018 23:36:29 +0300 Subject: [PATCH 0227/1808] create field forward_from_message_id in Message https://core.telegram.org/bots/api#message --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index f497a7c8b..70f78847f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -399,6 +399,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.date = date self.chat = chat self.forward_from_chat = None + self.forward_from_message_id = None self.forward_from = None self.forward_date = None self.reply_to_message = None From c11a9f810c7019965ff3ceaeabae83a1ab19838f Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Wed, 21 Mar 2018 07:35:42 +0300 Subject: [PATCH 0228/1808] Update types.py Added 'json' property to class 'Message', to quickly save a message to the database --- telebot/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index f497a7c8b..a8dbe6266 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -369,7 +369,7 @@ def de_json(cls, json_string): if 'connected_website' in obj: opts['connected_website'] = obj['connected_website'] content_type = 'connected_website' - return cls(message_id, from_user, date, chat, content_type, opts) + return cls(message_id, from_user, date, chat, content_type, opts, json) @classmethod def parse_chat(cls, chat): @@ -436,6 +436,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.connected_website = None for key in options: setattr(self, key, options[key]) + self.json = json class MessageEntity(JsonDeserializable): From 82e252ec46891b1603575146e6eee84cab6067fa Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Wed, 21 Mar 2018 10:44:37 +0300 Subject: [PATCH 0229/1808] Update types.py Fix --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index a8dbe6266..e53f9cdcb 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -392,7 +392,7 @@ def parse_entities(cls, message_entity_array): ret.append(MessageEntity.de_json(me)) return ret - def __init__(self, message_id, from_user, date, chat, content_type, options): + def __init__(self, message_id, from_user, date, chat, content_type, options, json_string): self.content_type = content_type self.message_id = message_id self.from_user = from_user @@ -436,7 +436,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options): self.connected_website = None for key in options: setattr(self, key, options[key]) - self.json = json + self.json = json_string class MessageEntity(JsonDeserializable): From 0422e62f6574f0b5a8b3ab3bc2f7277c2c3438c3 Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Wed, 21 Mar 2018 10:45:34 +0300 Subject: [PATCH 0230/1808] Update types.py Fix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e53f9cdcb..7af3ac4e9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -369,7 +369,7 @@ def de_json(cls, json_string): if 'connected_website' in obj: opts['connected_website'] = obj['connected_website'] content_type = 'connected_website' - return cls(message_id, from_user, date, chat, content_type, opts, json) + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod def parse_chat(cls, chat): From 7f47f11444bc94d94c2097281bb7ec2ccd0a0925 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 23 Mar 2018 19:58:43 +0800 Subject: [PATCH 0231/1808] Fix #481 --- telebot/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d6c16e1ca..992e5fb6c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -302,9 +302,9 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout=3): except KeyboardInterrupt: logger.info("KeyboardInterrupt received.") self.__stop_polling.set() - polling_thread.stop() break + polling_thread.stop() logger.info('Stopped polling.') def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3): @@ -341,6 +341,11 @@ def _exec_task(self, task, *args, **kwargs): def stop_polling(self): self.__stop_polling.set() + def stop_bot(self): + self.stop_polling() + if self.worker_pool: + self.worker_pool.close() + def set_update_listener(self, listener): self.update_listener.append(listener) From d231b1fbaaefbd2d690b93d4bf150f3c3f46f8e4 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 23 Mar 2018 19:59:42 +0800 Subject: [PATCH 0232/1808] Bump version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 11c22130b..1c5529ad7 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.6.1', + version='3.6.2', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From ff3cbaf45bb3a4a29ee7abd4a376d40007bde5f6 Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Sun, 25 Mar 2018 13:21:55 +0300 Subject: [PATCH 0233/1808] Update apihelper.py --- telebot/apihelper.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a02c8e4cd..043bd1608 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -98,6 +98,11 @@ def get_file(token, file_id): return _make_request(token, method_url, params={'file_id': file_id}) +def get_file_url(token, file_id): + method_url = r'getFile' + return FILE_URL.format(token, types.File.de_json(_make_request(token, method_url, params={'file_id': file_id})).file_path) + + def download_file(token, file_path): url = FILE_URL.format(token, file_path) result = _get_req_session().get(url) From cb0256b37d5a5e99e9a221a1f620fce49c49b573 Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Sun, 25 Mar 2018 13:22:35 +0300 Subject: [PATCH 0234/1808] Update __init__.py --- telebot/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index d6c16e1ca..27381a41e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -351,6 +351,9 @@ def get_me(self): def get_file(self, file_id): return types.File.de_json(apihelper.get_file(self.token, file_id)) + def get_file_url(self, file_id): + return apihelper.get_file_url(self.token, file_id) + def download_file(self, file_path): return apihelper.download_file(self.token, file_path) From e761e1e1d9c4c8d2d48c9c0ef31fc3e2de2079c9 Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Sun, 25 Mar 2018 14:54:28 +0300 Subject: [PATCH 0235/1808] Update apihelper.py --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 043bd1608..1173aa30d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -100,7 +100,7 @@ def get_file(token, file_id): def get_file_url(token, file_id): method_url = r'getFile' - return FILE_URL.format(token, types.File.de_json(_make_request(token, method_url, params={'file_id': file_id})).file_path) + return FILE_URL.format(token, get_file(token, file_id).file_path) def download_file(token, file_path): From 36d088dfbf412c5f2e96d7b19ebd4960e54c1da3 Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Wed, 4 Apr 2018 10:47:37 +0300 Subject: [PATCH 0236/1808] Bugfixes and minor improvements --- telebot/__init__.py | 11 ++++++++- telebot/types.py | 59 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index aa74d117e..96e7e3d9e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -247,6 +247,15 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) + def infinity_polling(self, *args, **kwargs): + while not self.__stop_polling.is_set(): + try: + self.polling(*args, **kwargs) + except Exception as e: + time.sleep(5) + pass + logger.info("Break infinity polling") + def polling(self, none_stop=False, interval=0, timeout=20): """ This function creates a new Thread that calls an internal __retrieve_updates function. @@ -486,7 +495,7 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio def delete_message(self, chat_id, message_id): """ - Use this method to delete message. Returns True on success. + Use this method to delete message. Returns True on success. :param chat_id: in which chat to delete :param message_id: which message to delete :return: API reply. diff --git a/telebot/types.py b/telebot/types.py index 7af3ac4e9..f99643600 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -438,6 +438,63 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso setattr(self, key, options[key]) self.json = json_string + @property + def html_text(self): + """ + Author: @sviat9440 + Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" + + Example: + message.html_text + >> "Test parse formatting, url, text_mention and mention @username" + + Cusom subs: + You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. + Example: + message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} + message.html_text + >> "Test parse formatting, url and text_mention and mention @username" + """ + + if not self.entities: + return self.text + _subs = { + "bold": "{text}", + "italic": "{text}", + "pre": "

{text}
", + "code": "{text}", + "url": "{text}" + } + if hasattr(self, "custom_subs"): + for type in self.custom_subs: + _subs[type] = self.custom_subs[type] + html_text = "" + def func(text, type=None, url=None, user=None): + if type == "text_mention": + type = "url" + url = "tg://user?id={0}".format(user.id) + elif type == "mention": + url = "https://t.me/{0}".format(text[1:]) + if not type or not _subs.get(type): + return text + subs = _subs.get(type) + text = text.replace("&", "&").replace("<", "<").replace(">", ">") + return subs.format(text=text, url=url) + + offset = 0 + for entity in self.entities: + if entity.type == "bot_command": + entity.offset -= 1 + entity.length += 1 + if entity.offset > offset: + html_text += func(self.text[offset:entity.offset]) + offset = entity.offset + html_text += func(self.text[offset:offset + entity.length], entity.type, entity.url, entity.user) + offset += entity.length + if offset < len(self.text): + html_text += func(self.text[offset:]) + return html_text + class MessageEntity(JsonDeserializable): @classmethod @@ -1822,7 +1879,7 @@ def __init__(self, id, title): def add_price(self, *args): """ Add LabeledPrice to ShippingOption - :param args: LabeledPrices + :param args: LabeledPrices """ for price in args: self.prices.append(price) From 373d4d37ff7d9abafb9bb8efba39286e8c657356 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 10 Apr 2018 14:48:39 +0800 Subject: [PATCH 0237/1808] Fix test case. --- tests/test_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 89b6f7ac2..6818b5bc1 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -391,7 +391,7 @@ def test_edit_markup(self): def create_text_message(self, text): params = {'text': text} chat = types.User(11, False, 'test') - return types.Message(1, None, None, chat, 'text', params) + return types.Message(1, None, None, chat, 'text', params, "") def test_is_string_unicode(self): s1 = u'string' From 7957bc45a89c62e2286d67c86a61c66b005abd6f Mon Sep 17 00:00:00 2001 From: Waffle Date: Thu, 12 Apr 2018 13:45:32 +0300 Subject: [PATCH 0238/1808] Fixing and upgrading next step and reply handlers. + minor fixes Rename telebot package to pytelegrambotapi becouse lib named telebot exists and it raising many errors Add methods: | register_for_reply_by_message_id, | register_next_step_handler_by_chat_id, | clear_reply_handlers, | clear_reply_handlers_by_message_id --- examples/deep_linking.py | 4 +- examples/detailed_example/detailed_example.py | 6 +- examples/echo_bot.py | 4 +- examples/step_example.py | 6 +- examples/telebot_bot/telebot_bot.py | 4 +- .../webhook_aiohttp_echo_bot.py | 10 +- .../webhook_cherrypy_echo_bot.py | 10 +- .../webhook_cpython_echo_bot.py | 10 +- .../webhook_flask_echo_bot.py | 10 +- .../webhook_flask_heroku_echo.py | 6 +- .../webhook_tornado_echo_bot.py | 6 +- {telebot => pytelegrambotapi}/__init__.py | 147 +++++++++++------- {telebot => pytelegrambotapi}/apihelper.py | 8 +- {telebot => pytelegrambotapi}/types.py | 2 +- {telebot => pytelegrambotapi}/util.py | 0 tests/test_telebot.py | 112 ++++++------- tests/test_types.py | 2 +- 17 files changed, 192 insertions(+), 155 deletions(-) rename {telebot => pytelegrambotapi}/__init__.py (93%) rename {telebot => pytelegrambotapi}/apihelper.py (99%) rename {telebot => pytelegrambotapi}/types.py (99%) rename {telebot => pytelegrambotapi}/util.py (100%) diff --git a/examples/deep_linking.py b/examples/deep_linking.py index f92680f21..1d6060ea6 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -30,10 +30,10 @@ # Steps 1 to 4 will have to be implemented in a web server, using a language such as PHP, Python, C# or Java. These # steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example. -import telebot +import pytelegrambotapi import time -bot = telebot.TeleBot('TOKEN') +bot = pytelegrambotapi.TeleBot('TOKEN') def extract_unique_code(text): # Extracts the unique_code from the sent /start command. diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index 8fad9af31..2bafe0307 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -2,8 +2,8 @@ This is a detailed example using almost every command of the API """ -import telebot -from telebot import types +import pytelegrambotapi +from pytelegrambotapi import types import time TOKEN = '' @@ -48,7 +48,7 @@ def listener(messages): print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text -bot = telebot.TeleBot(TOKEN) +bot = pytelegrambotapi.TeleBot(TOKEN) bot.set_update_listener(listener) # register listener diff --git a/examples/echo_bot.py b/examples/echo_bot.py index f88d3bf9f..0cf4cb2e6 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -1,11 +1,11 @@ # This is a simple echo bot using the decorator mechanism. # It echoes any incoming text messages. -import telebot +import pytelegrambotapi API_TOKEN = '' -bot = telebot.TeleBot(API_TOKEN) +bot = pytelegrambotapi.TeleBot(API_TOKEN) # Handle '/start' and '/help' @bot.message_handler(commands=['help', 'start']) diff --git a/examples/step_example.py b/examples/step_example.py index fd8d07d57..bf7c0a981 100644 --- a/examples/step_example.py +++ b/examples/step_example.py @@ -4,12 +4,12 @@ """ import time -import telebot -from telebot import types +import pytelegrambotapi +from pytelegrambotapi import types API_TOKEN = '' -bot = telebot.TeleBot(API_TOKEN) +bot = pytelegrambotapi.TeleBot(API_TOKEN) user_dict = {} diff --git a/examples/telebot_bot/telebot_bot.py b/examples/telebot_bot/telebot_bot.py index cd29276fb..d2fbed0a0 100644 --- a/examples/telebot_bot/telebot_bot.py +++ b/examples/telebot_bot/telebot_bot.py @@ -3,7 +3,7 @@ # and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him! # WARNING: Tested with Python 2.7 -import telebot +import pytelegrambotapi import os text_messages = { @@ -30,7 +30,7 @@ if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ: raise AssertionError("Please configure TELEBOT_BOT_TOKEN and GROUP_CHAT_ID as environment variables") -bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"]) +bot = pytelegrambotapi.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"]) GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"]) def is_api_group(chat_id): diff --git a/examples/webhook_examples/webhook_aiohttp_echo_bot.py b/examples/webhook_examples/webhook_aiohttp_echo_bot.py index d92cff936..089366b38 100644 --- a/examples/webhook_examples/webhook_aiohttp_echo_bot.py +++ b/examples/webhook_examples/webhook_aiohttp_echo_bot.py @@ -9,7 +9,7 @@ from aiohttp import web -import telebot +import pytelegrambotapi API_TOKEN = '' @@ -33,10 +33,10 @@ WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN) -logger = telebot.logger -telebot.logger.setLevel(logging.INFO) +logger = pytelegrambotapi.logger +pytelegrambotapi.logger.setLevel(logging.INFO) -bot = telebot.TeleBot(API_TOKEN) +bot = pytelegrambotapi.TeleBot(API_TOKEN) app = web.Application() @@ -45,7 +45,7 @@ async def handle(request): if request.match_info.get('token') == bot.token: request_body_dict = await request.json() - update = telebot.types.Update.de_json(request_body_dict) + update = pytelegrambotapi.types.Update.de_json(request_body_dict) bot.process_new_updates([update]) return web.Response() else: diff --git a/examples/webhook_examples/webhook_cherrypy_echo_bot.py b/examples/webhook_examples/webhook_cherrypy_echo_bot.py index d0f3da05f..8d0814294 100644 --- a/examples/webhook_examples/webhook_cherrypy_echo_bot.py +++ b/examples/webhook_examples/webhook_cherrypy_echo_bot.py @@ -5,7 +5,7 @@ # It echoes any incoming text messages and does not use the polling method. import cherrypy -import telebot +import pytelegrambotapi import logging @@ -30,10 +30,10 @@ WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) -logger = telebot.logger -telebot.logger.setLevel(logging.INFO) +logger = pytelegrambotapi.logger +pytelegrambotapi.logger.setLevel(logging.INFO) -bot = telebot.TeleBot(API_TOKEN) +bot = pytelegrambotapi.TeleBot(API_TOKEN) # WebhookServer, process webhook calls @@ -45,7 +45,7 @@ def index(self): cherrypy.request.headers['content-type'] == 'application/json': length = int(cherrypy.request.headers['content-length']) json_string = cherrypy.request.body.read(length).decode("utf-8") - update = telebot.types.Update.de_json(json_string) + update = pytelegrambotapi.types.Update.de_json(json_string) bot.process_new_updates([update]) return '' else: diff --git a/examples/webhook_examples/webhook_cpython_echo_bot.py b/examples/webhook_examples/webhook_cpython_echo_bot.py index 5c1dfc94c..3308fe4cd 100644 --- a/examples/webhook_examples/webhook_cpython_echo_bot.py +++ b/examples/webhook_examples/webhook_cpython_echo_bot.py @@ -6,7 +6,7 @@ import BaseHTTPServer import ssl -import telebot +import pytelegrambotapi import logging @@ -31,10 +31,10 @@ WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) -logger = telebot.logger -telebot.logger.setLevel(logging.INFO) +logger = pytelegrambotapi.logger +pytelegrambotapi.logger.setLevel(logging.INFO) -bot = telebot.TeleBot(API_TOKEN) +bot = pytelegrambotapi.TeleBot(API_TOKEN) # WebhookHandler, process webhook calls @@ -59,7 +59,7 @@ def do_POST(self): self.send_response(200) self.end_headers() - update = telebot.types.Update.de_json(json_string) + update = pytelegrambotapi.types.Update.de_json(json_string) bot.process_new_messages([update.message]) else: self.send_error(403) diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py index 92ffa219b..e9422a281 100644 --- a/examples/webhook_examples/webhook_flask_echo_bot.py +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -5,7 +5,7 @@ # It echoes any incoming text messages and does not use the polling method. import flask -import telebot +import pytelegrambotapi import logging @@ -30,10 +30,10 @@ WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) -logger = telebot.logger -telebot.logger.setLevel(logging.INFO) +logger = pytelegrambotapi.logger +pytelegrambotapi.logger.setLevel(logging.INFO) -bot = telebot.TeleBot(API_TOKEN) +bot = pytelegrambotapi.TeleBot(API_TOKEN) app = flask.Flask(__name__) @@ -49,7 +49,7 @@ def index(): def webhook(): if flask.request.headers.get('content-type') == 'application/json': json_string = flask.request.get_data().decode('utf-8') - update = telebot.types.Update.de_json(json_string) + update = pytelegrambotapi.types.Update.de_json(json_string) bot.process_new_updates([update]) return '' else: diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index 62d0a90e7..392d9e9ec 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -1,10 +1,10 @@ import os -import telebot +import pytelegrambotapi from flask import Flask, request TOKEN = '' -bot = telebot.TeleBot(TOKEN) +bot = pytelegrambotapi.TeleBot(TOKEN) server = Flask(__name__) @@ -20,7 +20,7 @@ def echo_message(message): @server.route('/' + TOKEN, methods=['POST']) def getMessage(): - bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))]) + bot.process_new_updates([pytelegrambotapi.types.Update.de_json(request.stream.read().decode("utf-8"))]) return "!", 200 diff --git a/examples/webhook_examples/webhook_tornado_echo_bot.py b/examples/webhook_examples/webhook_tornado_echo_bot.py index 538b7b9f2..b6e3a2d0d 100644 --- a/examples/webhook_examples/webhook_tornado_echo_bot.py +++ b/examples/webhook_examples/webhook_tornado_echo_bot.py @@ -4,7 +4,7 @@ # This example shows webhook echo bot with Tornado web framework # Documenation to Tornado: http://tornadoweb.org -import telebot +import pytelegrambotapi import tornado.web import tornado.ioloop import tornado.httpserver @@ -27,7 +27,7 @@ # When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply # with the same value in you put in WEBHOOK_HOST -bot = telebot.TeleBot(API_TOKEN) +bot = pytelegrambotapi.TeleBot(API_TOKEN) class Root(tornado.web.RequestHandler): def get(self): @@ -45,7 +45,7 @@ def post(self): # length = int(self.request.headers['Content-Length']) json_data = self.request.body.decode("utf-8") - update = telebot.types.Update.de_json(json_data) + update = pytelegrambotapi.types.Update.de_json(json_data) bot.process_new_updates([update]) self.write("") self.finish() diff --git a/telebot/__init__.py b/pytelegrambotapi/__init__.py similarity index 93% rename from telebot/__init__.py rename to pytelegrambotapi/__init__.py index 96e7e3d9e..3132e2dc6 100644 --- a/telebot/__init__.py +++ b/pytelegrambotapi/__init__.py @@ -20,7 +20,7 @@ logger.setLevel(logging.ERROR) -from telebot import apihelper, types, util +from pytelegrambotapi import apihelper, types, util """ Module : telebot @@ -70,6 +70,7 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): :param token: bot API token :return: Telebot object. """ + self.token = token self.update_listener = [] self.skip_pending = skip_pending @@ -78,13 +79,11 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): self.last_update_id = 0 self.exc_info = None - self.message_subscribers_messages = [] - self.message_subscribers_callbacks = [] - self.message_subscribers_lock = threading.Lock() + # key: message_id, value: handler list + self.reply_handlers = {} # key: chat_id, value: handler list - self.message_subscribers_next_step = {} - self.pre_message_subscribers_next_step = {} + self.next_step_handlers = {} self.message_handlers = [] self.edited_message_handlers = [] @@ -213,11 +212,10 @@ def process_new_updates(self, updates): self.process_new_shipping_query(new_shipping_querys) def process_new_messages(self, new_messages): - self._append_pre_next_step_handler() + self._notify_next_handlers(new_messages) + self._notify_reply_handlers(new_messages) self.__notify_update(new_messages) self._notify_command_handlers(self.message_handlers, new_messages) - self._notify_message_subscribers(new_messages) - self._notify_message_next_handler(new_messages) def process_new_edited_messages(self, edited_message): self._notify_command_handlers(self.edited_message_handlers, edited_message) @@ -912,8 +910,8 @@ def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_me def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, - is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None): + is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, + provider_data=None): result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, @@ -1050,7 +1048,7 @@ def delete_sticker_from_set(self, sticker): """ return apihelper.delete_sticker_from_set(self.token, sticker) - def register_for_reply(self, message, callback): + def register_for_reply(self, message, callback, *args, **kwargs): """ Registers a callback function to be notified when a reply to `message` arrives. @@ -1061,40 +1059,60 @@ def register_for_reply(self, message, callback): :param callback: The callback function to be called when a reply arrives. Must accept one `message` parameter, which will contain the replied message. """ - with self.message_subscribers_lock: - self.message_subscribers_messages.insert(0, message.message_id) - self.message_subscribers_callbacks.insert(0, callback) - if len(self.message_subscribers_messages) > 10000: - self.message_subscribers_messages.pop() - self.message_subscribers_callbacks.pop() + message_id = message.message_id + self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs) - def _notify_message_subscribers(self, new_messages): - for message in new_messages: - if not message.reply_to_message: - continue + def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs): + """ + Registers a callback function to be notified when a reply to `message` arrives. - reply_msg_id = message.reply_to_message.message_id - if reply_msg_id in self.message_subscribers_messages: - index = self.message_subscribers_messages.index(reply_msg_id) - self.message_subscribers_callbacks[index](message) + Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see + the difference between a reply to `message` and an ordinary message. - with self.message_subscribers_lock: - index = self.message_subscribers_messages.index(reply_msg_id) - del self.message_subscribers_messages[index] - del self.message_subscribers_callbacks[index] + :param message: The message for which we are awaiting a reply. + :param callback: The callback function to be called when a reply arrives. Must accept one `message` + parameter, which will contain the replied message. + """ + if message_id in self.reply_handlers.keys(): + self.reply_handlers[message_id].append({"callback": callback, "args": args, "kwargs": kwargs}) + else: + self.reply_handlers[message_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] - def register_next_step_handler(self, message, callback): + def _notify_reply_handlers(self, new_messages): + for message in new_messages: + if hasattr(message, "reply_to_message") and message.reply_to_message is not None: + reply_msg_id = message.reply_to_message.message_id + if reply_msg_id in self.reply_handlers.keys(): + handlers = self.reply_handlers[reply_msg_id] + for handler in handlers: + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + self.reply_handlers.pop(reply_msg_id) + + def register_next_step_handler(self, message, callback, *args, **kwargs): """ Registers a callback function to be notified when new message arrives after `message`. - :param message: The message for which we want to handle new message after that in same chat. + :param message: The message for which we want to handle new message in the same chat. :param callback: The callback function which next new message arrives. + :param args: Args to pass in callback func + :param kwargs: Args to pass in callback func """ chat_id = message.chat.id - if chat_id in self.pre_message_subscribers_next_step: - self.pre_message_subscribers_next_step[chat_id].append(callback) + self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) + + def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwargs): + """ + Registers a callback function to be notified when new message arrives after `message`. + + :param chat_id: The chat for which we want to handle new message. + :param callback: The callback function which next new message arrives. + :param args: Args to pass in callback func + :param kwargs: Args to pass in callback func + """ + if chat_id in self.next_step_handlers.keys(): + self.next_step_handlers[chat_id].append({"callback": callback, "args": args, "kwargs": kwargs}) else: - self.pre_message_subscribers_next_step[chat_id] = [callback] + self.next_step_handlers[chat_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] def clear_step_handler(self, message): """ @@ -1103,26 +1121,48 @@ def clear_step_handler(self, message): :param message: The message for which we want to handle new message after that in same chat. """ chat_id = message.chat.id - self.pre_message_subscribers_next_step[chat_id] = [] + self.clear_step_handler_by_chat_id(chat_id) - def _notify_message_next_handler(self, new_messages): - for message in new_messages: + def clear_step_handler_by_chat_id(self, chat_id): + """ + Clears all callback functions registered by register_next_step_handler(). + + :param chat_id: The chat for which we want to clear next step handlers + """ + self.next_step_handlers[chat_id] = [] + + def clear_reply_handlers(self, message): + """ + Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). + + :param message_id: The message for which we want to clear reply handlers + """ + message_id = message.message_id + self.clear_reply_handlers_by_message_id(message_id) + + def clear_reply_handlers_by_message_id(self, message_id): + """ + Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). + + :param message_id: The message id for which we want to clear reply handlers + """ + self.reply_handlers[message_id] = [] + + def _notify_next_handlers(self, new_messages): + i = 0 + while i < len(new_messages): + message = new_messages[i] chat_id = message.chat.id - if chat_id in self.message_subscribers_next_step: - handlers = self.message_subscribers_next_step[chat_id] + if chat_id in self.next_step_handlers.keys(): + handlers = self.next_step_handlers[chat_id] for handler in handlers: - self._exec_task(handler, message) - self.message_subscribers_next_step.pop(chat_id, None) - - def _append_pre_next_step_handler(self): - for k in self.pre_message_subscribers_next_step.keys(): - if k in self.message_subscribers_next_step: - self.message_subscribers_next_step[k].extend(self.pre_message_subscribers_next_step[k]) - else: - self.message_subscribers_next_step[k] = self.pre_message_subscribers_next_step[k] - self.pre_message_subscribers_next_step = {} - - def _build_handler_dict(self, handler, **filters): + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + self.next_step_handlers.pop(chat_id, None) + new_messages.pop(i) # removing message that detects with next_step_handler + i += 1 + + @staticmethod + def _build_handler_dict(handler, **filters): return { 'function': handler, 'filters': filters @@ -1300,9 +1340,6 @@ def _test_filter(self, filter, filter_value, message): def _notify_command_handlers(self, handlers, new_messages): for message in new_messages: - # if message has next step handler, dont exec command handlers - if hasattr(message, 'chat') and message.chat and (message.chat.id in self.message_subscribers_next_step): - continue for message_handler in handlers: if self._test_message_handler(message_handler, message): self._exec_task(message_handler['function'], message) diff --git a/telebot/apihelper.py b/pytelegrambotapi/apihelper.py similarity index 99% rename from telebot/apihelper.py rename to pytelegrambotapi/apihelper.py index 1173aa30d..53c529ea1 100644 --- a/telebot/apihelper.py +++ b/pytelegrambotapi/apihelper.py @@ -13,11 +13,11 @@ format_header_param = fields.format_header_param except ImportError: format_header_param = None -import telebot -from telebot import types -from telebot import util +import pytelegrambotapi +from pytelegrambotapi import types +from pytelegrambotapi import util -logger = telebot.logger +logger = pytelegrambotapi.logger proxy = None API_URL = "https://api.telegram.org/bot{0}/{1}" diff --git a/telebot/types.py b/pytelegrambotapi/types.py similarity index 99% rename from telebot/types.py rename to pytelegrambotapi/types.py index f99643600..84340d424 100644 --- a/telebot/types.py +++ b/pytelegrambotapi/types.py @@ -7,7 +7,7 @@ import six -from telebot import util +from pytelegrambotapi import util class JsonSerializable: diff --git a/telebot/util.py b/pytelegrambotapi/util.py similarity index 100% rename from telebot/util.py rename to pytelegrambotapi/util.py diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 6818b5bc1..3a16ac257 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -7,9 +7,9 @@ import pytest import os -import telebot -from telebot import types -from telebot import util +import pytelegrambotapi +from pytelegrambotapi import types +from pytelegrambotapi import util should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ @@ -29,11 +29,11 @@ def test_message_listener(self): def listener(messages): assert len(messages) == 100 - tb = telebot.TeleBot('') + tb = pytelegrambotapi.TeleBot('') tb.set_update_listener(listener) def test_message_handler(self): - tb = telebot.TeleBot('') + tb = pytelegrambotapi.TeleBot('') msg = self.create_text_message('/help') @tb.message_handler(commands=['help', 'start']) @@ -45,7 +45,7 @@ def command_handler(message): assert msg.text == 'got' def test_message_handler_reg(self): - bot = telebot.TeleBot('') + bot = pytelegrambotapi.TeleBot('') msg = self.create_text_message(r'https://web.telegram.org/') @bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') @@ -57,7 +57,7 @@ def command_url(message): assert msg.text == 'got' def test_message_handler_lambda(self): - bot = telebot.TeleBot('') + bot = pytelegrambotapi.TeleBot('') msg = self.create_text_message(r'lambda_text') @bot.message_handler(func=lambda message: r'lambda' in message.text) @@ -69,7 +69,7 @@ def command_url(message): assert msg.text == 'got' def test_message_handler_lambda_fail(self): - bot = telebot.TeleBot('') + bot = pytelegrambotapi.TeleBot('') msg = self.create_text_message(r'text') @bot.message_handler(func=lambda message: r'lambda' in message.text) @@ -81,7 +81,7 @@ def command_url(message): assert not msg.text == 'got' def test_message_handler_reg_fail(self): - bot = telebot.TeleBot('') + bot = pytelegrambotapi.TeleBot('') msg = self.create_text_message(r'web.telegram.org/') @bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') @@ -93,7 +93,7 @@ def command_url(message): assert not msg.text == 'got' def test_send_message_with_markdown(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) markdown = """ *bold text* _italic text_ @@ -103,7 +103,7 @@ def test_send_message_with_markdown(self): assert ret_msg.message_id def test_send_message_with_disable_notification(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) markdown = """ *bold text* _italic text_ @@ -114,7 +114,7 @@ def test_send_message_with_disable_notification(self): def test_send_file(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data) assert ret_msg.message_id @@ -123,7 +123,7 @@ def test_send_file(self): def test_send_file_dis_noti(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data, disable_notification=True) assert ret_msg.message_id @@ -132,7 +132,7 @@ def test_send_file_dis_noti(self): def test_send_file_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data, caption="Test") assert ret_msg.message_id @@ -141,30 +141,30 @@ def test_send_file_caption(self): def test_send_video(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data) assert ret_msg.message_id def test_send_video_dis_noti(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True) assert ret_msg.message_id def test_send_video_more_params(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, 1) assert ret_msg.message_id def test_send_video_more_params_dis_noti(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, 1, disable_notification=True) assert ret_msg.message_id def test_send_file_exception(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) try: tb.send_document(CHAT_ID, None) assert False @@ -174,7 +174,7 @@ def test_send_file_exception(self): def test_send_photo(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_photo(CHAT_ID, file_data) assert ret_msg.message_id @@ -183,7 +183,7 @@ def test_send_photo(self): def test_send_photo_dis_noti(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_photo(CHAT_ID, file_data) assert ret_msg.message_id @@ -192,7 +192,7 @@ def test_send_photo_dis_noti(self): def test_send_audio(self): file_data = open('./test_data/record.mp3', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram') assert ret_msg.content_type == 'audio' assert ret_msg.audio.performer == 'eternnoir' @@ -200,7 +200,7 @@ def test_send_audio(self): def test_send_audio_dis_noti(self): file_data = open('./test_data/record.mp3', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram', disable_notification=True) assert ret_msg.content_type == 'audio' @@ -209,19 +209,19 @@ def test_send_audio_dis_noti(self): def test_send_voice(self): file_data = open('./test_data/record.ogg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data) assert ret_msg.voice.mime_type == 'audio/ogg' def test_send_voice_dis_noti(self): file_data = open('./test_data/record.ogg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True) assert ret_msg.voice.mime_type == 'audio/ogg' def test_get_file(self): file_data = open('./test_data/record.ogg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data) file_id = ret_msg.voice.file_id file_info = tb.get_file(file_id) @@ -229,7 +229,7 @@ def test_get_file(self): def test_get_file_dis_noti(self): file_data = open('./test_data/record.ogg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True) file_id = ret_msg.voice.file_id file_info = tb.get_file(file_id) @@ -237,19 +237,19 @@ def test_get_file_dis_noti(self): def test_send_message(self): text = 'CI Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_message(CHAT_ID, text) assert ret_msg.message_id def test_send_message_dis_noti(self): text = 'CI Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True) assert ret_msg.message_id def test_send_message_with_markup(self): text = 'CI Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) markup = types.ReplyKeyboardMarkup() markup.add(types.KeyboardButton("1")) markup.add(types.KeyboardButton("2")) @@ -258,7 +258,7 @@ def test_send_message_with_markup(self): def test_send_message_with_markup_use_string(self): text = 'CI Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) markup = types.ReplyKeyboardMarkup() markup.add("1") markup.add("2") @@ -269,7 +269,7 @@ def test_send_message_with_markup_use_string(self): def test_send_message_with_inlinemarkup(self): text = 'CI Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) markup = types.InlineKeyboardMarkup() markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com")) markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com")) @@ -278,28 +278,28 @@ def test_send_message_with_inlinemarkup(self): def test_forward_message(self): text = 'CI forward_message Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text) ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id) assert ret_msg.forward_from def test_forward_message_dis_noti(self): text = 'CI forward_message Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text) ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id, disable_notification=True) assert ret_msg.forward_from def test_reply_to(self): text = 'CI reply_to Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text) ret_msg = tb.reply_to(msg, text + ' REPLY') assert ret_msg.reply_to_message.message_id == msg.message_id def test_register_for_reply(self): text = 'CI reply_to Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text, reply_markup=types.ForceReply()) reply_msg = tb.reply_to(msg, text + ' REPLY') @@ -311,7 +311,7 @@ def process_reply(message): tb.process_new_messages([reply_msg]) def test_send_location(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_location(CHAT_ID, lat, lon) @@ -319,7 +319,7 @@ def test_send_location(self): assert int(ret_msg.location.latitude) == int(lat) def test_send_location_dis_noti(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_location(CHAT_ID, lat, lon, disable_notification=True) @@ -327,7 +327,7 @@ def test_send_location_dis_noti(self): assert int(ret_msg.location.latitude) == int(lat) def test_send_venue(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address") @@ -335,50 +335,50 @@ def test_send_venue(self): assert int(lat) == int(ret_msg.venue.location.latitude) def test_send_venue_dis_noti(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address", disable_notification=True) assert ret_msg.venue.title == "Test Venue" def test_Chat(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) me = tb.get_me() msg = tb.send_message(CHAT_ID, 'Test') assert me.id == msg.from_user.id assert msg.chat.id == int(CHAT_ID) def test_edit_message_text(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, 'Test') new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.text == 'Edit test' def test_edit_message_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) msg = tb.send_document(CHAT_ID, file_data, caption="Test") new_msg = tb.edit_message_caption(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.caption == 'Edit test' def test_get_chat(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ch = tb.get_chat(GROUP_ID) assert str(ch.id) == GROUP_ID def test_get_chat_administrators(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) cas = tb.get_chat_administrators(GROUP_ID) assert len(cas) > 0 def test_get_chat_members_count(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) cn = tb.get_chat_members_count(GROUP_ID) assert cn > 1 def test_edit_markup(self): text = 'CI Test Message' - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) markup = types.InlineKeyboardMarkup() markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com")) markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com")) @@ -407,12 +407,12 @@ def test_not_string(self): def test_send_video_note(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_video_note(CHAT_ID, file_data) assert ret_msg.message_id def test_send_media_group(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) img1 = 'https://i.imgur.com/CjXjcnU.png' img2 = 'https://i.imgur.com/CjXjcnU.png' medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")] @@ -424,7 +424,7 @@ def test_send_media_group(self): def test_send_media_group_local_files(self): photo = open('../examples/detailed_example/kitten.jpg', 'rb') video = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) medias = [types.InputMediaPhoto(photo, "View"), types.InputMediaVideo(video)] result = tb.send_media_group(CHAT_ID, medias) @@ -434,31 +434,31 @@ def test_send_media_group_local_files(self): def test_send_photo_formating_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' def test_send_video_formatting_caption(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' def test_send_audio_formatting_caption(self): file_data = open('./test_data/record.mp3', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_audio(CHAT_ID, file_data, caption='bold', parse_mode='HTML') assert ret_msg.caption_entities[0].type == 'bold' def test_send_voice_formatting_caprion(self): file_data = open('./test_data/record.ogg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data, caption='bold', parse_mode='HTML') assert ret_msg.caption_entities[0].type == 'bold' assert ret_msg.voice.mime_type == 'audio/ogg' def test_send_media_group_formatting_caption(self): - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) img1 = 'https://i.imgur.com/CjXjcnU.png' img2 = 'https://i.imgur.com/CjXjcnU.png' medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'), @@ -471,6 +471,6 @@ def test_send_media_group_formatting_caption(self): def test_send_document_formating_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = telebot.TeleBot(TOKEN) + tb = pytelegrambotapi.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' diff --git a/tests/test_types.py b/tests/test_types.py index c174d420b..87449439b 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -2,7 +2,7 @@ import sys sys.path.append('../') -from telebot import types +from pytelegrambotapi import types def test_json_user(): From 183230e92711f4feb95d1d779fe0511ce0a1144c Mon Sep 17 00:00:00 2001 From: Waffle Date: Thu, 12 Apr 2018 17:24:04 +0300 Subject: [PATCH 0239/1808] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1c5529ad7..e95e6d9fa 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def readme(): author='eternnoir', author_email='eternnoir@gmail.com', url='https://github.com/eternnoir/pyTelegramBotAPI', - packages=['telebot'], + packages=['pytelegrambotapi'], license='GPL2', keywords='telegram bot api tools', install_requires=['requests', 'six'], From ffe3a0c3d7f178dc6f772d8543ba032de0a7abab Mon Sep 17 00:00:00 2001 From: sviat9440 Date: Sun, 15 Apr 2018 19:19:29 +0300 Subject: [PATCH 0240/1808] Update types.py -- Fix encoding bug (emoji shifted offset) --- telebot/types.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f99643600..da89bd452 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -468,8 +468,10 @@ def html_text(self): if hasattr(self, "custom_subs"): for type in self.custom_subs: _subs[type] = self.custom_subs[type] + utf16_text = self.text.encode("utf-16-le") html_text = "" def func(text, type=None, url=None, user=None): + text = text.decode("utf-16-le") if type == "text_mention": type = "url" url = "tg://user?id={0}".format(user.id) @@ -483,16 +485,13 @@ def func(text, type=None, url=None, user=None): offset = 0 for entity in self.entities: - if entity.type == "bot_command": - entity.offset -= 1 - entity.length += 1 if entity.offset > offset: - html_text += func(self.text[offset:entity.offset]) + html_text += func(utf16_text[offset * 2 : entity.offset * 2]) offset = entity.offset - html_text += func(self.text[offset:offset + entity.length], entity.type, entity.url, entity.user) + html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) offset += entity.length - if offset < len(self.text): - html_text += func(self.text[offset:]) + if offset * 2 < len(utf16_text): + html_text += func(utf16_text[offset * 2:]) return html_text From 10ec897fb541d1197c39ac6cc2fa7923041e2075 Mon Sep 17 00:00:00 2001 From: LeoNeeD <35986338+LeoNeeDpk1@users.noreply.github.com> Date: Wed, 18 Apr 2018 10:05:26 +1200 Subject: [PATCH 0241/1808] Update README.md Updated and 100% working info in SOCKS5 proxy block. --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5bda4edf1..408b6be06 100644 --- a/README.md +++ b/README.md @@ -503,13 +503,10 @@ from telebot import apihelper apihelper.proxy = {'http', 'http://10.10.1.10:3128'} ``` -If you want to use socket5 proxy you need install dependency `pip install requests[socks]`. +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`. ```python -proxies = { - 'http': 'socks5://user:pass@host:port', - 'https': 'socks5://user:pass@host:port' -} +apihelper.proxy = {'https', ' 'socks5://userproxy:password@proxy_address:port''} ``` From 5dd88f8223f33e0ddf83e4f264bb3c90ed2f26cb Mon Sep 17 00:00:00 2001 From: Benny Date: Wed, 18 Apr 2018 15:00:05 +0800 Subject: [PATCH 0242/1808] fix issue #403: UnicodeEncodeError when sending a non-ASCII file in Python 2.7 --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1173aa30d..0bb536c95 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -953,7 +953,7 @@ def _convert_input_media(array): def _no_encode(func): def wrapper(key, val): if key == 'filename': - return '{0}={1}'.format(key, val) + return u'{0}={1}'.format(key, val) else: return func(key, val) From 989cae597b58bd06643d6467fbebca4f6b5f2c05 Mon Sep 17 00:00:00 2001 From: Jay-T Date: Wed, 18 Apr 2018 13:10:22 +0500 Subject: [PATCH 0243/1808] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 408b6be06..2ea415f42 100644 --- a/README.md +++ b/README.md @@ -500,13 +500,13 @@ 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://10.10.1.10: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`. ```python -apihelper.proxy = {'https', ' 'socks5://userproxy:password@proxy_address:port''} +apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} ``` From 99ff104a3f47592b3d6edb74e214f2ba6ba21914 Mon Sep 17 00:00:00 2001 From: khabibr Date: Tue, 24 Apr 2018 16:48:39 +0600 Subject: [PATCH 0244/1808] Update apihelper.py Correct files downloading when proxy used. --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0bb536c95..3d9b53ab4 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -105,7 +105,7 @@ def get_file_url(token, file_id): def download_file(token, file_path): url = FILE_URL.format(token, file_path) - result = _get_req_session().get(url) + result = _get_req_session().get(url, proxies=proxy) if result.status_code != 200: msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \ .format(result.status_code, result.reason, result.text) From 8f5546092463b0ccdb5f6aff4c935657fc184610 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Thu, 26 Apr 2018 09:53:55 +0800 Subject: [PATCH 0245/1808] Fix cache time is zero. --- telebot/apihelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3d9b53ab4..ceda96cd0 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -847,7 +847,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None, payload['show_alert'] = show_alert if url: payload['url'] = url - if cache_time: + if cache_time is not None: payload['cache_time'] = cache_time return _make_request(token, method_url, params=payload, method='post') @@ -856,7 +856,7 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per switch_pm_text=None, switch_pm_parameter=None): method_url = 'answerInlineQuery' payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)} - if cache_time: + if cache_time is not None: payload['cache_time'] = cache_time if is_personal: payload['is_personal'] = is_personal From 91f213ff3498eb78efa29db03fe775382fb82113 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 27 Apr 2018 15:47:03 +0800 Subject: [PATCH 0246/1808] Fix #501 --- telebot/types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index f99643600..fb6789b0d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -127,7 +127,6 @@ def de_json(cls, json_type): 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): self.update_id = update_id - self.edited_message = edited_message self.message = message self.edited_message = edited_message self.channel_post = channel_post From 3ba9799b98d11a16c99e5f10e627b71ddebff65d Mon Sep 17 00:00:00 2001 From: Waffle Date: Sat, 28 Apr 2018 13:50:59 +0300 Subject: [PATCH 0247/1808] Renaming back pytelegrambotapi module to telebot --- examples/deep_linking.py | 4 +- examples/detailed_example/detailed_example.py | 6 +- examples/echo_bot.py | 4 +- examples/step_example.py | 6 +- examples/telebot_bot/telebot_bot.py | 4 +- .../webhook_aiohttp_echo_bot.py | 10 +- .../webhook_cherrypy_echo_bot.py | 10 +- .../webhook_cpython_echo_bot.py | 10 +- .../webhook_flask_echo_bot.py | 10 +- .../webhook_flask_heroku_echo.py | 6 +- .../webhook_tornado_echo_bot.py | 6 +- setup.py | 2 +- {pytelegrambotapi => telebot}/__init__.py | 2 +- {pytelegrambotapi => telebot}/apihelper.py | 8 +- {pytelegrambotapi => telebot}/types.py | 2 +- {pytelegrambotapi => telebot}/util.py | 0 tests/test_telebot.py | 112 +++++++++--------- tests/test_types.py | 2 +- 18 files changed, 102 insertions(+), 102 deletions(-) rename {pytelegrambotapi => telebot}/__init__.py (99%) rename {pytelegrambotapi => telebot}/apihelper.py (99%) rename {pytelegrambotapi => telebot}/types.py (99%) rename {pytelegrambotapi => telebot}/util.py (100%) diff --git a/examples/deep_linking.py b/examples/deep_linking.py index 1d6060ea6..f92680f21 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -30,10 +30,10 @@ # Steps 1 to 4 will have to be implemented in a web server, using a language such as PHP, Python, C# or Java. These # steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example. -import pytelegrambotapi +import telebot import time -bot = pytelegrambotapi.TeleBot('TOKEN') +bot = telebot.TeleBot('TOKEN') def extract_unique_code(text): # Extracts the unique_code from the sent /start command. diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index 2bafe0307..8fad9af31 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -2,8 +2,8 @@ This is a detailed example using almost every command of the API """ -import pytelegrambotapi -from pytelegrambotapi import types +import telebot +from telebot import types import time TOKEN = '' @@ -48,7 +48,7 @@ def listener(messages): print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text -bot = pytelegrambotapi.TeleBot(TOKEN) +bot = telebot.TeleBot(TOKEN) bot.set_update_listener(listener) # register listener diff --git a/examples/echo_bot.py b/examples/echo_bot.py index 0cf4cb2e6..f88d3bf9f 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -1,11 +1,11 @@ # This is a simple echo bot using the decorator mechanism. # It echoes any incoming text messages. -import pytelegrambotapi +import telebot API_TOKEN = '' -bot = pytelegrambotapi.TeleBot(API_TOKEN) +bot = telebot.TeleBot(API_TOKEN) # Handle '/start' and '/help' @bot.message_handler(commands=['help', 'start']) diff --git a/examples/step_example.py b/examples/step_example.py index bf7c0a981..fd8d07d57 100644 --- a/examples/step_example.py +++ b/examples/step_example.py @@ -4,12 +4,12 @@ """ import time -import pytelegrambotapi -from pytelegrambotapi import types +import telebot +from telebot import types API_TOKEN = '' -bot = pytelegrambotapi.TeleBot(API_TOKEN) +bot = telebot.TeleBot(API_TOKEN) user_dict = {} diff --git a/examples/telebot_bot/telebot_bot.py b/examples/telebot_bot/telebot_bot.py index d2fbed0a0..cd29276fb 100644 --- a/examples/telebot_bot/telebot_bot.py +++ b/examples/telebot_bot/telebot_bot.py @@ -3,7 +3,7 @@ # and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him! # WARNING: Tested with Python 2.7 -import pytelegrambotapi +import telebot import os text_messages = { @@ -30,7 +30,7 @@ if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ: raise AssertionError("Please configure TELEBOT_BOT_TOKEN and GROUP_CHAT_ID as environment variables") -bot = pytelegrambotapi.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"]) +bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"]) GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"]) def is_api_group(chat_id): diff --git a/examples/webhook_examples/webhook_aiohttp_echo_bot.py b/examples/webhook_examples/webhook_aiohttp_echo_bot.py index 089366b38..d92cff936 100644 --- a/examples/webhook_examples/webhook_aiohttp_echo_bot.py +++ b/examples/webhook_examples/webhook_aiohttp_echo_bot.py @@ -9,7 +9,7 @@ from aiohttp import web -import pytelegrambotapi +import telebot API_TOKEN = '' @@ -33,10 +33,10 @@ WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN) -logger = pytelegrambotapi.logger -pytelegrambotapi.logger.setLevel(logging.INFO) +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) -bot = pytelegrambotapi.TeleBot(API_TOKEN) +bot = telebot.TeleBot(API_TOKEN) app = web.Application() @@ -45,7 +45,7 @@ async def handle(request): if request.match_info.get('token') == bot.token: request_body_dict = await request.json() - update = pytelegrambotapi.types.Update.de_json(request_body_dict) + update = telebot.types.Update.de_json(request_body_dict) bot.process_new_updates([update]) return web.Response() else: diff --git a/examples/webhook_examples/webhook_cherrypy_echo_bot.py b/examples/webhook_examples/webhook_cherrypy_echo_bot.py index 8d0814294..d0f3da05f 100644 --- a/examples/webhook_examples/webhook_cherrypy_echo_bot.py +++ b/examples/webhook_examples/webhook_cherrypy_echo_bot.py @@ -5,7 +5,7 @@ # It echoes any incoming text messages and does not use the polling method. import cherrypy -import pytelegrambotapi +import telebot import logging @@ -30,10 +30,10 @@ WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) -logger = pytelegrambotapi.logger -pytelegrambotapi.logger.setLevel(logging.INFO) +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) -bot = pytelegrambotapi.TeleBot(API_TOKEN) +bot = telebot.TeleBot(API_TOKEN) # WebhookServer, process webhook calls @@ -45,7 +45,7 @@ def index(self): cherrypy.request.headers['content-type'] == 'application/json': length = int(cherrypy.request.headers['content-length']) json_string = cherrypy.request.body.read(length).decode("utf-8") - update = pytelegrambotapi.types.Update.de_json(json_string) + update = telebot.types.Update.de_json(json_string) bot.process_new_updates([update]) return '' else: diff --git a/examples/webhook_examples/webhook_cpython_echo_bot.py b/examples/webhook_examples/webhook_cpython_echo_bot.py index 3308fe4cd..5c1dfc94c 100644 --- a/examples/webhook_examples/webhook_cpython_echo_bot.py +++ b/examples/webhook_examples/webhook_cpython_echo_bot.py @@ -6,7 +6,7 @@ import BaseHTTPServer import ssl -import pytelegrambotapi +import telebot import logging @@ -31,10 +31,10 @@ WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) -logger = pytelegrambotapi.logger -pytelegrambotapi.logger.setLevel(logging.INFO) +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) -bot = pytelegrambotapi.TeleBot(API_TOKEN) +bot = telebot.TeleBot(API_TOKEN) # WebhookHandler, process webhook calls @@ -59,7 +59,7 @@ def do_POST(self): self.send_response(200) self.end_headers() - update = pytelegrambotapi.types.Update.de_json(json_string) + update = telebot.types.Update.de_json(json_string) bot.process_new_messages([update.message]) else: self.send_error(403) diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py index e9422a281..92ffa219b 100644 --- a/examples/webhook_examples/webhook_flask_echo_bot.py +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -5,7 +5,7 @@ # It echoes any incoming text messages and does not use the polling method. import flask -import pytelegrambotapi +import telebot import logging @@ -30,10 +30,10 @@ WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) -logger = pytelegrambotapi.logger -pytelegrambotapi.logger.setLevel(logging.INFO) +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) -bot = pytelegrambotapi.TeleBot(API_TOKEN) +bot = telebot.TeleBot(API_TOKEN) app = flask.Flask(__name__) @@ -49,7 +49,7 @@ def index(): def webhook(): if flask.request.headers.get('content-type') == 'application/json': json_string = flask.request.get_data().decode('utf-8') - update = pytelegrambotapi.types.Update.de_json(json_string) + update = telebot.types.Update.de_json(json_string) bot.process_new_updates([update]) return '' else: diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index 392d9e9ec..62d0a90e7 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -1,10 +1,10 @@ import os -import pytelegrambotapi +import telebot from flask import Flask, request TOKEN = '' -bot = pytelegrambotapi.TeleBot(TOKEN) +bot = telebot.TeleBot(TOKEN) server = Flask(__name__) @@ -20,7 +20,7 @@ def echo_message(message): @server.route('/' + TOKEN, methods=['POST']) def getMessage(): - bot.process_new_updates([pytelegrambotapi.types.Update.de_json(request.stream.read().decode("utf-8"))]) + bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))]) return "!", 200 diff --git a/examples/webhook_examples/webhook_tornado_echo_bot.py b/examples/webhook_examples/webhook_tornado_echo_bot.py index b6e3a2d0d..538b7b9f2 100644 --- a/examples/webhook_examples/webhook_tornado_echo_bot.py +++ b/examples/webhook_examples/webhook_tornado_echo_bot.py @@ -4,7 +4,7 @@ # This example shows webhook echo bot with Tornado web framework # Documenation to Tornado: http://tornadoweb.org -import pytelegrambotapi +import telebot import tornado.web import tornado.ioloop import tornado.httpserver @@ -27,7 +27,7 @@ # When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply # with the same value in you put in WEBHOOK_HOST -bot = pytelegrambotapi.TeleBot(API_TOKEN) +bot = telebot.TeleBot(API_TOKEN) class Root(tornado.web.RequestHandler): def get(self): @@ -45,7 +45,7 @@ def post(self): # length = int(self.request.headers['Content-Length']) json_data = self.request.body.decode("utf-8") - update = pytelegrambotapi.types.Update.de_json(json_data) + update = telebot.types.Update.de_json(json_data) bot.process_new_updates([update]) self.write("") self.finish() diff --git a/setup.py b/setup.py index e95e6d9fa..1c5529ad7 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ def readme(): author='eternnoir', author_email='eternnoir@gmail.com', url='https://github.com/eternnoir/pyTelegramBotAPI', - packages=['pytelegrambotapi'], + packages=['telebot'], license='GPL2', keywords='telegram bot api tools', install_requires=['requests', 'six'], diff --git a/pytelegrambotapi/__init__.py b/telebot/__init__.py similarity index 99% rename from pytelegrambotapi/__init__.py rename to telebot/__init__.py index 3132e2dc6..8f9b60b75 100644 --- a/pytelegrambotapi/__init__.py +++ b/telebot/__init__.py @@ -20,7 +20,7 @@ logger.setLevel(logging.ERROR) -from pytelegrambotapi import apihelper, types, util +from telebot import apihelper, types, util """ Module : telebot diff --git a/pytelegrambotapi/apihelper.py b/telebot/apihelper.py similarity index 99% rename from pytelegrambotapi/apihelper.py rename to telebot/apihelper.py index 53c529ea1..1173aa30d 100644 --- a/pytelegrambotapi/apihelper.py +++ b/telebot/apihelper.py @@ -13,11 +13,11 @@ format_header_param = fields.format_header_param except ImportError: format_header_param = None -import pytelegrambotapi -from pytelegrambotapi import types -from pytelegrambotapi import util +import telebot +from telebot import types +from telebot import util -logger = pytelegrambotapi.logger +logger = telebot.logger proxy = None API_URL = "https://api.telegram.org/bot{0}/{1}" diff --git a/pytelegrambotapi/types.py b/telebot/types.py similarity index 99% rename from pytelegrambotapi/types.py rename to telebot/types.py index 84340d424..f99643600 100644 --- a/pytelegrambotapi/types.py +++ b/telebot/types.py @@ -7,7 +7,7 @@ import six -from pytelegrambotapi import util +from telebot import util class JsonSerializable: diff --git a/pytelegrambotapi/util.py b/telebot/util.py similarity index 100% rename from pytelegrambotapi/util.py rename to telebot/util.py diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 3a16ac257..6818b5bc1 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -7,9 +7,9 @@ import pytest import os -import pytelegrambotapi -from pytelegrambotapi import types -from pytelegrambotapi import util +import telebot +from telebot import types +from telebot import util should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ @@ -29,11 +29,11 @@ def test_message_listener(self): def listener(messages): assert len(messages) == 100 - tb = pytelegrambotapi.TeleBot('') + tb = telebot.TeleBot('') tb.set_update_listener(listener) def test_message_handler(self): - tb = pytelegrambotapi.TeleBot('') + tb = telebot.TeleBot('') msg = self.create_text_message('/help') @tb.message_handler(commands=['help', 'start']) @@ -45,7 +45,7 @@ def command_handler(message): assert msg.text == 'got' def test_message_handler_reg(self): - bot = pytelegrambotapi.TeleBot('') + bot = telebot.TeleBot('') msg = self.create_text_message(r'https://web.telegram.org/') @bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') @@ -57,7 +57,7 @@ def command_url(message): assert msg.text == 'got' def test_message_handler_lambda(self): - bot = pytelegrambotapi.TeleBot('') + bot = telebot.TeleBot('') msg = self.create_text_message(r'lambda_text') @bot.message_handler(func=lambda message: r'lambda' in message.text) @@ -69,7 +69,7 @@ def command_url(message): assert msg.text == 'got' def test_message_handler_lambda_fail(self): - bot = pytelegrambotapi.TeleBot('') + bot = telebot.TeleBot('') msg = self.create_text_message(r'text') @bot.message_handler(func=lambda message: r'lambda' in message.text) @@ -81,7 +81,7 @@ def command_url(message): assert not msg.text == 'got' def test_message_handler_reg_fail(self): - bot = pytelegrambotapi.TeleBot('') + bot = telebot.TeleBot('') msg = self.create_text_message(r'web.telegram.org/') @bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') @@ -93,7 +93,7 @@ def command_url(message): assert not msg.text == 'got' def test_send_message_with_markdown(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) markdown = """ *bold text* _italic text_ @@ -103,7 +103,7 @@ def test_send_message_with_markdown(self): assert ret_msg.message_id def test_send_message_with_disable_notification(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) markdown = """ *bold text* _italic text_ @@ -114,7 +114,7 @@ def test_send_message_with_disable_notification(self): def test_send_file(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data) assert ret_msg.message_id @@ -123,7 +123,7 @@ def test_send_file(self): def test_send_file_dis_noti(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data, disable_notification=True) assert ret_msg.message_id @@ -132,7 +132,7 @@ def test_send_file_dis_noti(self): def test_send_file_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data, caption="Test") assert ret_msg.message_id @@ -141,30 +141,30 @@ def test_send_file_caption(self): def test_send_video(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data) assert ret_msg.message_id def test_send_video_dis_noti(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True) assert ret_msg.message_id def test_send_video_more_params(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, 1) assert ret_msg.message_id def test_send_video_more_params_dis_noti(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, 1, disable_notification=True) assert ret_msg.message_id def test_send_file_exception(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) try: tb.send_document(CHAT_ID, None) assert False @@ -174,7 +174,7 @@ def test_send_file_exception(self): def test_send_photo(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_photo(CHAT_ID, file_data) assert ret_msg.message_id @@ -183,7 +183,7 @@ def test_send_photo(self): def test_send_photo_dis_noti(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_photo(CHAT_ID, file_data) assert ret_msg.message_id @@ -192,7 +192,7 @@ def test_send_photo_dis_noti(self): def test_send_audio(self): file_data = open('./test_data/record.mp3', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram') assert ret_msg.content_type == 'audio' assert ret_msg.audio.performer == 'eternnoir' @@ -200,7 +200,7 @@ def test_send_audio(self): def test_send_audio_dis_noti(self): file_data = open('./test_data/record.mp3', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram', disable_notification=True) assert ret_msg.content_type == 'audio' @@ -209,19 +209,19 @@ def test_send_audio_dis_noti(self): def test_send_voice(self): file_data = open('./test_data/record.ogg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data) assert ret_msg.voice.mime_type == 'audio/ogg' def test_send_voice_dis_noti(self): file_data = open('./test_data/record.ogg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True) assert ret_msg.voice.mime_type == 'audio/ogg' def test_get_file(self): file_data = open('./test_data/record.ogg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data) file_id = ret_msg.voice.file_id file_info = tb.get_file(file_id) @@ -229,7 +229,7 @@ def test_get_file(self): def test_get_file_dis_noti(self): file_data = open('./test_data/record.ogg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True) file_id = ret_msg.voice.file_id file_info = tb.get_file(file_id) @@ -237,19 +237,19 @@ def test_get_file_dis_noti(self): def test_send_message(self): text = 'CI Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_message(CHAT_ID, text) assert ret_msg.message_id def test_send_message_dis_noti(self): text = 'CI Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True) assert ret_msg.message_id def test_send_message_with_markup(self): text = 'CI Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) markup = types.ReplyKeyboardMarkup() markup.add(types.KeyboardButton("1")) markup.add(types.KeyboardButton("2")) @@ -258,7 +258,7 @@ def test_send_message_with_markup(self): def test_send_message_with_markup_use_string(self): text = 'CI Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) markup = types.ReplyKeyboardMarkup() markup.add("1") markup.add("2") @@ -269,7 +269,7 @@ def test_send_message_with_markup_use_string(self): def test_send_message_with_inlinemarkup(self): text = 'CI Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) markup = types.InlineKeyboardMarkup() markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com")) markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com")) @@ -278,28 +278,28 @@ def test_send_message_with_inlinemarkup(self): def test_forward_message(self): text = 'CI forward_message Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text) ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id) assert ret_msg.forward_from def test_forward_message_dis_noti(self): text = 'CI forward_message Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text) ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id, disable_notification=True) assert ret_msg.forward_from def test_reply_to(self): text = 'CI reply_to Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text) ret_msg = tb.reply_to(msg, text + ' REPLY') assert ret_msg.reply_to_message.message_id == msg.message_id def test_register_for_reply(self): text = 'CI reply_to Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, text, reply_markup=types.ForceReply()) reply_msg = tb.reply_to(msg, text + ' REPLY') @@ -311,7 +311,7 @@ def process_reply(message): tb.process_new_messages([reply_msg]) def test_send_location(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_location(CHAT_ID, lat, lon) @@ -319,7 +319,7 @@ def test_send_location(self): assert int(ret_msg.location.latitude) == int(lat) def test_send_location_dis_noti(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_location(CHAT_ID, lat, lon, disable_notification=True) @@ -327,7 +327,7 @@ def test_send_location_dis_noti(self): assert int(ret_msg.location.latitude) == int(lat) def test_send_venue(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address") @@ -335,50 +335,50 @@ def test_send_venue(self): assert int(lat) == int(ret_msg.venue.location.latitude) def test_send_venue_dis_noti(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) lat = 26.3875591 lon = -161.2901042 ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address", disable_notification=True) assert ret_msg.venue.title == "Test Venue" def test_Chat(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) me = tb.get_me() msg = tb.send_message(CHAT_ID, 'Test') assert me.id == msg.from_user.id assert msg.chat.id == int(CHAT_ID) def test_edit_message_text(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) msg = tb.send_message(CHAT_ID, 'Test') new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.text == 'Edit test' def test_edit_message_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) msg = tb.send_document(CHAT_ID, file_data, caption="Test") new_msg = tb.edit_message_caption(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.caption == 'Edit test' def test_get_chat(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ch = tb.get_chat(GROUP_ID) assert str(ch.id) == GROUP_ID def test_get_chat_administrators(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) cas = tb.get_chat_administrators(GROUP_ID) assert len(cas) > 0 def test_get_chat_members_count(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) cn = tb.get_chat_members_count(GROUP_ID) assert cn > 1 def test_edit_markup(self): text = 'CI Test Message' - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) markup = types.InlineKeyboardMarkup() markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com")) markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com")) @@ -407,12 +407,12 @@ def test_not_string(self): def test_send_video_note(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_video_note(CHAT_ID, file_data) assert ret_msg.message_id def test_send_media_group(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) img1 = 'https://i.imgur.com/CjXjcnU.png' img2 = 'https://i.imgur.com/CjXjcnU.png' medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")] @@ -424,7 +424,7 @@ def test_send_media_group(self): def test_send_media_group_local_files(self): photo = open('../examples/detailed_example/kitten.jpg', 'rb') video = open('./test_data/test_video.mp4', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) medias = [types.InputMediaPhoto(photo, "View"), types.InputMediaVideo(video)] result = tb.send_media_group(CHAT_ID, medias) @@ -434,31 +434,31 @@ def test_send_media_group_local_files(self): def test_send_photo_formating_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' def test_send_video_formatting_caption(self): file_data = open('./test_data/test_video.mp4', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' def test_send_audio_formatting_caption(self): file_data = open('./test_data/record.mp3', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_audio(CHAT_ID, file_data, caption='bold', parse_mode='HTML') assert ret_msg.caption_entities[0].type == 'bold' def test_send_voice_formatting_caprion(self): file_data = open('./test_data/record.ogg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_voice(CHAT_ID, file_data, caption='bold', parse_mode='HTML') assert ret_msg.caption_entities[0].type == 'bold' assert ret_msg.voice.mime_type == 'audio/ogg' def test_send_media_group_formatting_caption(self): - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) img1 = 'https://i.imgur.com/CjXjcnU.png' img2 = 'https://i.imgur.com/CjXjcnU.png' medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'), @@ -471,6 +471,6 @@ def test_send_media_group_formatting_caption(self): def test_send_document_formating_caption(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') - tb = pytelegrambotapi.TeleBot(TOKEN) + tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' diff --git a/tests/test_types.py b/tests/test_types.py index 87449439b..c174d420b 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -2,7 +2,7 @@ import sys sys.path.append('../') -from pytelegrambotapi import types +from telebot import types def test_json_user(): From b5a217013a9387b87d3fac3c70254fb6a12e2238 Mon Sep 17 00:00:00 2001 From: fojetin Date: Mon, 30 Apr 2018 15:41:12 +0300 Subject: [PATCH 0248/1808] Fix #253 #231 #485 --- examples/webhook_examples/webhook_flask_echo_bot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py index 92ffa219b..d0327d7cf 100644 --- a/examples/webhook_examples/webhook_flask_echo_bot.py +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -7,6 +7,7 @@ import flask import telebot import logging +import time API_TOKEN = '' @@ -73,6 +74,8 @@ def echo_message(message): # Remove webhook, it fails sometimes the set if there is a previous webhook bot.remove_webhook() +time.sleep(0.1) + # Set webhook bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH, certificate=open(WEBHOOK_SSL_CERT, 'r')) From 3be21ae36163dd35d165ceae4f80063d21a78803 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 14 May 2018 10:15:05 +0800 Subject: [PATCH 0249/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1c5529ad7..1e76d0362 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.6.2', + version='3.6.3', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 9c79ba2f876d34941c10d94acd5dde5a42b26224 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 14 May 2018 13:29:34 +0300 Subject: [PATCH 0250/1808] html_text fix and html_caption html_text now works with text_link html_caption now works for caption/caption_entities --- telebot/types.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index d958908ab..5ca1b4ebe 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -437,8 +437,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso setattr(self, key, options[key]) self.json = json_string - @property - def html_text(self): + def __html_text(self, text, entities): """ Author: @sviat9440 Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" @@ -455,19 +454,20 @@ def html_text(self): >> "Test parse formatting, url and text_mention and mention @username" """ - if not self.entities: - return self.text + if not entities: + return text _subs = { "bold": "{text}", "italic": "{text}", "pre": "
{text}
", "code": "{text}", - "url": "{text}" + "url": "{text}", + "text_link": "{text}" } if hasattr(self, "custom_subs"): for type in self.custom_subs: _subs[type] = self.custom_subs[type] - utf16_text = self.text.encode("utf-16-le") + utf16_text = text.encode("utf-16-le") html_text = "" def func(text, type=None, url=None, user=None): text = text.decode("utf-16-le") @@ -483,7 +483,7 @@ def func(text, type=None, url=None, user=None): return subs.format(text=text, url=url) offset = 0 - for entity in self.entities: + for entity in entities: if entity.offset > offset: html_text += func(utf16_text[offset * 2 : entity.offset * 2]) offset = entity.offset @@ -493,6 +493,13 @@ def func(text, type=None, url=None, user=None): html_text += func(utf16_text[offset * 2:]) return html_text + @property + def html_text(self): + return self.__html_text(self.text, self.entities) + + @property + def html_caption(self): + return self.__html_text(self.caption, self.caption_entities) class MessageEntity(JsonDeserializable): @classmethod From 49aee14fcaeb2fa17f6e58c7ae9ee3d993bdb8c7 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sat, 19 May 2018 00:42:06 +0300 Subject: [PATCH 0251/1808] Make _test_filter method static and a bit clear doc strings --- telebot/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8f9b60b75..511cb7337 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -984,7 +984,6 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None, u def get_sticker_set(self, name): """ Use this method to get a sticker set. On success, a StickerSet object is returned. - :param token: :param name: :return: """ @@ -1135,7 +1134,7 @@ def clear_reply_handlers(self, message): """ Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). - :param message_id: The message for which we want to clear reply handlers + :param message: The message for which we want to clear reply handlers """ message_id = message.message_id self.clear_reply_handlers_by_message_id(message_id) @@ -1328,7 +1327,8 @@ def _test_message_handler(self, message_handler, message): return True - def _test_filter(self, filter, filter_value, message): + @staticmethod + def _test_filter(filter, filter_value, message): test_cases = { 'content_types': lambda msg: msg.content_type in filter_value, 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), From e99fb8f84ffc5e0af98819b89b517f7f55336e1d Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 20 May 2018 23:40:25 +0300 Subject: [PATCH 0252/1808] Add methods to save (reply|next step) handlers [WIP] --- telebot/__init__.py | 85 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8f9b60b75..9b33dbc21 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7,6 +7,9 @@ import sys import six +import os +import json + import logging logger = logging.getLogger('TeleBot') @@ -85,6 +88,16 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): # key: chat_id, value: handler list self.next_step_handlers = {} + self.save_reply_handlers = False + self.reply_handlers_save_file = None + self.reply_save_delay = None + self.save_reply_timer = None + + self.save_step_handlers = False + self.step_handlers_save_file = None + self.step_save_delay = None + self.save_step_timer = None + self.message_handlers = [] self.edited_message_handlers = [] self.channel_post_handlers = [] @@ -99,6 +112,67 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): if self.threaded: self.worker_pool = util.ThreadPool(num_threads=num_threads) + @staticmethod + def dump_handlers(handlers, filename="./handler-saves/step.save", file_mode="w"): + dirs = filename.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + with open(filename + ".tmp", file_mode) as file: + for mid, handler in handlers.items(): + name = handler['callback'].__name__ + module = handler['callback'].__module__ + + tmp = {str(mid): {"callback": {"module": module, "name": name}, "args": handler["args"], + "kwargs": handler["kwargs"]}} + + json.dump(tmp, file) + + if os.path.isfile(filename): + os.remove(filename) + + os.rename(filename + ".tmp", filename) + + def save_next_step_handlers(self): + self.dump_handlers(self.next_step_handlers, self.step_handlers_save_file) + + def save_reply_handlers_method(self): + self.dump_handlers(self.next_step_handlers, self.step_handlers_save_file) + + def start_save_next_step_timer(self): + if self.save_step_timer.is_alive(): return + + self.save_step_timer.start() + + def start_save_reply_timer(self): + if self.save_reply_timer.is_alive(): return + + self.save_reply_timer.start() + + def enable_save_next_step_handlers(self, delay=120, filename="./handler-saves/step.save"): + self.save_step_handlers = True + self.step_handlers_save_file = filename + self.step_save_delay = delay + self.save_step_timer = threading.Timer(self.step_save_delay, self.save_next_step_handlers) + + def enable_save_reply_handlers(self, delay=120, filename="./handler-saves/reply.save"): + self.save_reply_handlers = True + self.reply_handlers_save_file = filename + self.reply_save_delay = delay + self.save_reply_timer = threading.Timer(self.reply_save_delay, self.save_reply_handlers_method) + + @staticmethod + def load_handlers(filename): + if os.path.isfile(filename) and os.path.getsize(filename) > 0: + with open(filename, "r") as file: + handlers = json.load(file) + + return handlers + + def load_next_step_handlers(self, filename="./handler-saves/step.save"): + self.next_step_handlers.update(self.load_handlers(filename)) + + def load_reply_handlers(self, filename="./handler-saves/reply.save"): + self.reply_handlers.update(self.load_handlers(filename)) + def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) @@ -1078,6 +1152,8 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs else: self.reply_handlers[message_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] + self.start_save_reply_timer() + def _notify_reply_handlers(self, new_messages): for message in new_messages: if hasattr(message, "reply_to_message") and message.reply_to_message is not None: @@ -1087,6 +1163,7 @@ def _notify_reply_handlers(self, new_messages): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) self.reply_handlers.pop(reply_msg_id) + self.start_save_reply_timer() def register_next_step_handler(self, message, callback, *args, **kwargs): """ @@ -1114,6 +1191,8 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar else: self.next_step_handlers[chat_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] + self.start_save_next_step_timer() + def clear_step_handler(self, message): """ Clears all callback functions registered by register_next_step_handler(). @@ -1131,6 +1210,8 @@ def clear_step_handler_by_chat_id(self, chat_id): """ self.next_step_handlers[chat_id] = [] + self.start_save_next_step_timer() + def clear_reply_handlers(self, message): """ Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). @@ -1148,6 +1229,8 @@ def clear_reply_handlers_by_message_id(self, message_id): """ self.reply_handlers[message_id] = [] + self.start_save_next_step_timer() + def _notify_next_handlers(self, new_messages): i = 0 while i < len(new_messages): @@ -1161,6 +1244,8 @@ def _notify_next_handlers(self, new_messages): new_messages.pop(i) # removing message that detects with next_step_handler i += 1 + self.start_save_next_step_timer() + @staticmethod def _build_handler_dict(handler, **filters): return { From ed7e33b4c65a6a0d73edb56861cd7b742aae289d Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 02:48:46 +0300 Subject: [PATCH 0253/1808] Fix loadings funcs --- telebot/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9b33dbc21..a877b6909 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -113,7 +113,7 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): self.worker_pool = util.ThreadPool(num_threads=num_threads) @staticmethod - def dump_handlers(handlers, filename="./handler-saves/step.save", file_mode="w"): + def dump_handlers(handlers, filename, file_mode="w"): dirs = filename.rsplit('/', maxsplit=1)[0] os.makedirs(dirs, exist_ok=True) with open(filename + ".tmp", file_mode) as file: @@ -165,6 +165,12 @@ def load_handlers(filename): with open(filename, "r") as file: handlers = json.load(file) + for handler in handlers: + name = handler["callback"]["name"] + module = handler["callback"]["module"] + + handler = getattr(sys.modules[module], name) + return handlers def load_next_step_handlers(self, filename="./handler-saves/step.save"): From 00c8dcc19b966d842a4e6437e7460832820492ac Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 03:10:40 +0300 Subject: [PATCH 0254/1808] Add async methods --- telebot/__init__.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index a877b6909..541a84442 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1441,6 +1441,48 @@ class AsyncTeleBot(TeleBot): def __init__(self, *args, **kwargs): TeleBot.__init__(self, *args, **kwargs) + @staticmethod + @util.async() + def dump_handlers(handlers, filename, file_mode="w"): + return TeleBot.dump_handlers(handlers, filename, file_mode) + + @util.async() + def save_next_step_handlers(self): + return TeleBot.save_next_step_handlers(self) + + @util.async() + def save_reply_handlers_method(self): + return TeleBot.save_reply_handlers_method(self) + + @util.async() + def start_save_next_step_timer(self): + return TeleBot.start_save_next_step_timer(self) + + @util.async() + def start_save_reply_timer(self): + return TeleBot.start_save_reply_timer(self) + + @util.async() + def enable_save_next_step_handlers(self, delay=120, filename="./handler-saves/step.save"): + return TeleBot.enable_save_next_step_handlers(self, delay, filename) + + @util.async() + def enable_save_reply_handlers(self, delay=120, filename="./handler-saves/reply.save"): + return TeleBot.enable_save_reply_handlers(self, delay, filename) + + @staticmethod + @util.async() + def load_handlers(filename): + return TeleBot.load_handlers(filename) + + @util.async() + def load_next_step_handlers(self, filename="./handler-saves/step.save"): + return TeleBot.load_next_step_handlers(self, filename) + + @util.async() + def load_reply_handlers(self, filename="./handler-saves/reply.save"): + return TeleBot.load_reply_handlers(self, filename) + @util.async() def get_me(self): return TeleBot.get_me(self) From b1d5cb2129d8b44838ab8ce3984e6320b71157ad Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 08:56:31 +0300 Subject: [PATCH 0255/1808] Rewrite. Add class 'Saver' that provides methods for saving (next step|reply) handlers. Add methods enable_save_next_step_handlers, enable_save_reply_handlers, disable_save_next_step_handlers, disable_save_reply_handlers, load_next_step_handlers, load_reply_handlers to Telebot and AsyncTelebot. update telebot/__init__.py --- telebot/__init__.py | 196 ++++++++++++++++++++++---------------------- 1 file changed, 100 insertions(+), 96 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 541a84442..73ab32838 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -30,6 +30,84 @@ """ +class Saver: + def __init__(self, handlers, filename, delay): + self.handlers = handlers + self.filename = filename + self.delay = delay + self.timer = threading.Timer(delay, self.save_handlers) + + def start_save_timer(self): + if not self.timer.is_alive(): + self.timer = threading.Timer(self.delay, self.save_handlers) + self.timer.start() + + def save_handlers(self): + self.dump_handlers(self.handlers, self.filename) + + def load_handlers(self, filename): + tmp = self.return_load_handlers(filename) + if tmp is not None: + self.handlers.update(tmp) + + @staticmethod + def dump_handlers(handlers, filename, file_mode="w"): + dirs = filename.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + to_dump = {} + with open(filename + ".tmp", file_mode) as file: + for id_, handlers_ in handlers.items(): + for handler in handlers_: + name = handler['callback'].__name__ + module = handler['callback'].__module__ + + tmp = {"callback": {"module": module, "name": name}, "args": handler["args"], + "kwargs": handler["kwargs"]} + if id_ in to_dump.keys(): + to_dump[id_].append(tmp) + else: + to_dump[id_] = [tmp] + + json.dump(to_dump, file) + + if os.path.isfile(filename): + os.remove(filename) + + os.rename(filename + ".tmp", filename) + + @staticmethod + def return_load_handlers(filename): + if os.path.isfile(filename) and os.path.getsize(filename) > 0: + with open(filename, "r") as file: + handlers = json.load(file) + + result = {} + for id_, handlers_ in handlers.items(): + for handler in handlers_: + name = handler['callback']["name"] + module = handler['callback']["module"] + callback = getattr(sys.modules[module], name) + + tmp = {"callback": callback, "args": handler["args"], "kwargs": handler["kwargs"]} + + if int(id_) in result.keys(): + result[int(id_)].append(tmp) + else: + result[int(id_)] = [tmp] + + a = """ for key, handlers_ in handlers.items(): + for handler in handlers_: + name = handler["callback"]["name"] + module = handler["callback"]["module"] + + callback = getattr(sys.modules["__main__"], "next_") + handler["callback"] = callback""" + + return result + + + + class TeleBot: """ This is TeleBot Class Methods: @@ -88,15 +166,8 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): # key: chat_id, value: handler list self.next_step_handlers = {} - self.save_reply_handlers = False - self.reply_handlers_save_file = None - self.reply_save_delay = None - self.save_reply_timer = None - - self.save_step_handlers = False - self.step_handlers_save_file = None - self.step_save_delay = None - self.save_step_timer = None + self.next_step_saver = None + self.reply_saver = None self.message_handlers = [] self.edited_message_handlers = [] @@ -112,72 +183,23 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): if self.threaded: self.worker_pool = util.ThreadPool(num_threads=num_threads) - @staticmethod - def dump_handlers(handlers, filename, file_mode="w"): - dirs = filename.rsplit('/', maxsplit=1)[0] - os.makedirs(dirs, exist_ok=True) - with open(filename + ".tmp", file_mode) as file: - for mid, handler in handlers.items(): - name = handler['callback'].__name__ - module = handler['callback'].__module__ - - tmp = {str(mid): {"callback": {"module": module, "name": name}, "args": handler["args"], - "kwargs": handler["kwargs"]}} - - json.dump(tmp, file) - - if os.path.isfile(filename): - os.remove(filename) - - os.rename(filename + ".tmp", filename) - - def save_next_step_handlers(self): - self.dump_handlers(self.next_step_handlers, self.step_handlers_save_file) - - def save_reply_handlers_method(self): - self.dump_handlers(self.next_step_handlers, self.step_handlers_save_file) - - def start_save_next_step_timer(self): - if self.save_step_timer.is_alive(): return - - self.save_step_timer.start() - - def start_save_reply_timer(self): - if self.save_reply_timer.is_alive(): return - - self.save_reply_timer.start() - def enable_save_next_step_handlers(self, delay=120, filename="./handler-saves/step.save"): - self.save_step_handlers = True - self.step_handlers_save_file = filename - self.step_save_delay = delay - self.save_step_timer = threading.Timer(self.step_save_delay, self.save_next_step_handlers) + self.next_step_saver = Saver(self.next_step_handlers, filename, delay) def enable_save_reply_handlers(self, delay=120, filename="./handler-saves/reply.save"): - self.save_reply_handlers = True - self.reply_handlers_save_file = filename - self.reply_save_delay = delay - self.save_reply_timer = threading.Timer(self.reply_save_delay, self.save_reply_handlers_method) + self.reply_saver = Saver(self.reply_handlers, filename, delay) - @staticmethod - def load_handlers(filename): - if os.path.isfile(filename) and os.path.getsize(filename) > 0: - with open(filename, "r") as file: - handlers = json.load(file) + def disable_save_next_step_handlers(self): + self.next_step_saver = None - for handler in handlers: - name = handler["callback"]["name"] - module = handler["callback"]["module"] - - handler = getattr(sys.modules[module], name) - - return handlers + def disable_save_reply_handlers(self): + self.reply_saver = None def load_next_step_handlers(self, filename="./handler-saves/step.save"): - self.next_step_handlers.update(self.load_handlers(filename)) + self.next_step_saver.load_handlers(filename) def load_reply_handlers(self, filename="./handler-saves/reply.save"): - self.reply_handlers.update(self.load_handlers(filename)) + self.reply_saver.load_handlers(filename) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) @@ -1158,7 +1180,7 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs else: self.reply_handlers[message_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] - self.start_save_reply_timer() + self.reply_saver.start_save_timer() def _notify_reply_handlers(self, new_messages): for message in new_messages: @@ -1169,7 +1191,7 @@ def _notify_reply_handlers(self, new_messages): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) self.reply_handlers.pop(reply_msg_id) - self.start_save_reply_timer() + self.reply_saver.start_save_timer() def register_next_step_handler(self, message, callback, *args, **kwargs): """ @@ -1197,7 +1219,7 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar else: self.next_step_handlers[chat_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] - self.start_save_next_step_timer() + self.next_step_saver.start_save_timer() def clear_step_handler(self, message): """ @@ -1216,7 +1238,7 @@ def clear_step_handler_by_chat_id(self, chat_id): """ self.next_step_handlers[chat_id] = [] - self.start_save_next_step_timer() + self.next_step_saver.start_save_timer() def clear_reply_handlers(self, message): """ @@ -1235,7 +1257,7 @@ def clear_reply_handlers_by_message_id(self, message_id): """ self.reply_handlers[message_id] = [] - self.start_save_next_step_timer() + self.reply_saver.start_save_timer() def _notify_next_handlers(self, new_messages): i = 0 @@ -1250,7 +1272,7 @@ def _notify_next_handlers(self, new_messages): new_messages.pop(i) # removing message that detects with next_step_handler i += 1 - self.start_save_next_step_timer() + self.next_step_saver.start_save_timer() @staticmethod def _build_handler_dict(handler, **filters): @@ -1441,27 +1463,6 @@ class AsyncTeleBot(TeleBot): def __init__(self, *args, **kwargs): TeleBot.__init__(self, *args, **kwargs) - @staticmethod - @util.async() - def dump_handlers(handlers, filename, file_mode="w"): - return TeleBot.dump_handlers(handlers, filename, file_mode) - - @util.async() - def save_next_step_handlers(self): - return TeleBot.save_next_step_handlers(self) - - @util.async() - def save_reply_handlers_method(self): - return TeleBot.save_reply_handlers_method(self) - - @util.async() - def start_save_next_step_timer(self): - return TeleBot.start_save_next_step_timer(self) - - @util.async() - def start_save_reply_timer(self): - return TeleBot.start_save_reply_timer(self) - @util.async() def enable_save_next_step_handlers(self, delay=120, filename="./handler-saves/step.save"): return TeleBot.enable_save_next_step_handlers(self, delay, filename) @@ -1470,10 +1471,13 @@ def enable_save_next_step_handlers(self, delay=120, filename="./handler-saves/st def enable_save_reply_handlers(self, delay=120, filename="./handler-saves/reply.save"): return TeleBot.enable_save_reply_handlers(self, delay, filename) - @staticmethod @util.async() - def load_handlers(filename): - return TeleBot.load_handlers(filename) + def disable_save_next_step_handlers(self): + return TeleBot.disable_save_next_step_handlers(self) + + @util.async() + def disable_save_reply_handlers(self): + return TeleBot.enable_save_reply_handlers(self) @util.async() def load_next_step_handlers(self, filename="./handler-saves/step.save"): From 4bcfc34a50f2096d1729711e71c0eba2aabb7734 Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 09:07:59 +0300 Subject: [PATCH 0256/1808] Update _notify_next_handlers and _notify_reply_handlers methods: Now if there wasn't any handler updates, timer willn't start. --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 73ab32838..f08ebb465 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1191,7 +1191,7 @@ def _notify_reply_handlers(self, new_messages): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) self.reply_handlers.pop(reply_msg_id) - self.reply_saver.start_save_timer() + self.reply_saver.start_save_timer() def register_next_step_handler(self, message, callback, *args, **kwargs): """ @@ -1270,9 +1270,9 @@ def _notify_next_handlers(self, new_messages): self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) self.next_step_handlers.pop(chat_id, None) new_messages.pop(i) # removing message that detects with next_step_handler + self.next_step_saver.start_save_timer() i += 1 - self.next_step_saver.start_save_timer() @staticmethod def _build_handler_dict(handler, **filters): From 4facc5f7d736e42e52e5bb532e1cbad028205004 Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 09:30:10 +0300 Subject: [PATCH 0257/1808] fix unenabled saving handlers. Updated telebot/__init__.py --- telebot/__init__.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f08ebb465..ab87189f8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1179,8 +1179,8 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs self.reply_handlers[message_id].append({"callback": callback, "args": args, "kwargs": kwargs}) else: self.reply_handlers[message_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] - - self.reply_saver.start_save_timer() + if self.reply_saver is not None: + self.reply_saver.start_save_timer() def _notify_reply_handlers(self, new_messages): for message in new_messages: @@ -1191,7 +1191,8 @@ def _notify_reply_handlers(self, new_messages): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) self.reply_handlers.pop(reply_msg_id) - self.reply_saver.start_save_timer() + if self.reply_saver is not None: + self.reply_saver.start_save_timer() def register_next_step_handler(self, message, callback, *args, **kwargs): """ @@ -1219,7 +1220,8 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar else: self.next_step_handlers[chat_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] - self.next_step_saver.start_save_timer() + if self.next_step_saver is not None: + self.next_step_saver.start_save_timer() def clear_step_handler(self, message): """ @@ -1238,7 +1240,8 @@ def clear_step_handler_by_chat_id(self, chat_id): """ self.next_step_handlers[chat_id] = [] - self.next_step_saver.start_save_timer() + if self.next_step_saver is not None: + self.next_step_saver.start_save_timer() def clear_reply_handlers(self, message): """ @@ -1257,7 +1260,8 @@ def clear_reply_handlers_by_message_id(self, message_id): """ self.reply_handlers[message_id] = [] - self.reply_saver.start_save_timer() + if self.reply_saver is not None: + self.reply_saver.start_save_timer() def _notify_next_handlers(self, new_messages): i = 0 @@ -1270,7 +1274,8 @@ def _notify_next_handlers(self, new_messages): self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) self.next_step_handlers.pop(chat_id, None) new_messages.pop(i) # removing message that detects with next_step_handler - self.next_step_saver.start_save_timer() + if self.next_step_saver is not None: + self.next_step_saver.start_save_timer() i += 1 From 7df6b3d4c98167cbed95bf6cfe2cdff88481dd9f Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 09:35:39 +0300 Subject: [PATCH 0258/1808] Fix situation where delay <= 0. Update telebot/__init__.py --- telebot/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ab87189f8..59ae6ff48 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -39,8 +39,11 @@ def __init__(self, handlers, filename, delay): def start_save_timer(self): if not self.timer.is_alive(): - self.timer = threading.Timer(self.delay, self.save_handlers) - self.timer.start() + if self.delay <= 0: + self.save_handlers() + else: + self.timer = threading.Timer(self.delay, self.save_handlers) + self.timer.start() def save_handlers(self): self.dump_handlers(self.handlers, self.filename) From 018e4597a2d6b6e777c19d475b90186fa1bc07f2 Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 09:40:29 +0300 Subject: [PATCH 0259/1808] Add del_file_after_loading param to Saver.return_load_handlers and Saver.load_handlers methods. Update telebot/__init__.py --- telebot/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 59ae6ff48..f89c33938 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -48,8 +48,8 @@ def start_save_timer(self): def save_handlers(self): self.dump_handlers(self.handlers, self.filename) - def load_handlers(self, filename): - tmp = self.return_load_handlers(filename) + def load_handlers(self, filename, del_file_after_loading=True): + tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) if tmp is not None: self.handlers.update(tmp) @@ -79,7 +79,7 @@ def dump_handlers(handlers, filename, file_mode="w"): os.rename(filename + ".tmp", filename) @staticmethod - def return_load_handlers(filename): + def return_load_handlers(filename, del_file_after_loading=True): if os.path.isfile(filename) and os.path.getsize(filename) > 0: with open(filename, "r") as file: handlers = json.load(file) @@ -105,7 +105,8 @@ def return_load_handlers(filename): callback = getattr(sys.modules["__main__"], "next_") handler["callback"] = callback""" - + if del_file_after_loading: + os.remove(filename) return result From 7e5f51e4ab6436c8f989b04ad7d16d5f2e175c77 Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 09:44:43 +0300 Subject: [PATCH 0260/1808] Remove old thing. Update telebot/__init__.py --- telebot/__init__.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f89c33938..06bbe3097 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -98,13 +98,6 @@ def return_load_handlers(filename, del_file_after_loading=True): else: result[int(id_)] = [tmp] - a = """ for key, handlers_ in handlers.items(): - for handler in handlers_: - name = handler["callback"]["name"] - module = handler["callback"]["module"] - - callback = getattr(sys.modules["__main__"], "next_") - handler["callback"] = callback""" if del_file_after_loading: os.remove(filename) return result From b989b7601b9f53c3ad630072d971a92865cb3831 Mon Sep 17 00:00:00 2001 From: Waffle Date: Fri, 25 May 2018 20:57:22 +0300 Subject: [PATCH 0261/1808] Add new class: Handler Change type of (next step|reply) handlers from dict to Handler [WIP] update: telebot/__init__.py --- telebot/__init__.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ca830e568..f15d8d79f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -30,6 +30,25 @@ """ +class Handler: + def __init__(self, callback: {dict, function}, *args, **kwargs): + if type(callback) == dict: + self.callback = getattr(sys.modules[callback["module"]], callback["name"]) + else: + self.callback = callback + + self.args = args + self.kwargs = kwargs + + def __getitem__(self, item): + return getattr(self, item) + + def copy_to_dump(self): + module_ = self.callback.__module__ + name = self.callback.__name__ + return Handler({"module": module_, "name": name}, *self.args, **self.kwargs) + + class Saver: def __init__(self, handlers, filename, delay): self.handlers = handlers @@ -61,15 +80,10 @@ def dump_handlers(handlers, filename, file_mode="w"): with open(filename + ".tmp", file_mode) as file: for id_, handlers_ in handlers.items(): for handler in handlers_: - name = handler['callback'].__name__ - module = handler['callback'].__module__ - - tmp = {"callback": {"module": module, "name": name}, "args": handler["args"], - "kwargs": handler["kwargs"]} if id_ in to_dump.keys(): - to_dump[id_].append(tmp) + to_dump[id_].append(handler.copy_to_dump()) else: - to_dump[id_] = [tmp] + to_dump[id_] = [handler.copy_to_dump()] json.dump(to_dump, file) @@ -87,11 +101,8 @@ def return_load_handlers(filename, del_file_after_loading=True): result = {} for id_, handlers_ in handlers.items(): for handler in handlers_: - name = handler['callback']["name"] - module = handler['callback']["module"] - callback = getattr(sys.modules[module], name) - tmp = {"callback": callback, "args": handler["args"], "kwargs": handler["kwargs"]} + tmp = Handler(handler['callback'], handler["args"], handler["kwargs"]) if int(id_) in result.keys(): result[int(id_)].append(tmp) @@ -1172,9 +1183,9 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs parameter, which will contain the replied message. """ if message_id in self.reply_handlers.keys(): - self.reply_handlers[message_id].append({"callback": callback, "args": args, "kwargs": kwargs}) + self.reply_handlers[message_id].append(Handler(callback, *args, **kwargs)) else: - self.reply_handlers[message_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] + self.reply_handlers[message_id] = [Handler(callback, *args, **kwargs)] if self.reply_saver is not None: self.reply_saver.start_save_timer() @@ -1212,9 +1223,9 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar :param kwargs: Args to pass in callback func """ if chat_id in self.next_step_handlers.keys(): - self.next_step_handlers[chat_id].append({"callback": callback, "args": args, "kwargs": kwargs}) + self.next_step_handlers[chat_id].append(Handler(callback, *args, **kwargs)) else: - self.next_step_handlers[chat_id] = [{"callback": callback, "args": args, "kwargs": kwargs}] + self.next_step_handlers[chat_id] = [Handler(callback, *args, **kwargs)] if self.next_step_saver is not None: self.next_step_saver.start_save_timer() From 17971ff48b39f2a4695829bc9c776c0a711d57bc Mon Sep 17 00:00:00 2001 From: Waffle Date: Sat, 26 May 2018 12:19:01 +0300 Subject: [PATCH 0262/1808] Move from json to pickle. Update: relebot/__init__.py --- telebot/__init__.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f15d8d79f..838bbd1b0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -9,6 +9,7 @@ import os import json +import pickle import logging @@ -31,7 +32,7 @@ class Handler: - def __init__(self, callback: {dict, function}, *args, **kwargs): + def __init__(self, callback: {dict, 'function'}, *args, **kwargs): if type(callback) == dict: self.callback = getattr(sys.modules[callback["module"]], callback["name"]) else: @@ -73,7 +74,7 @@ def load_handlers(self, filename, del_file_after_loading=True): self.handlers.update(tmp) @staticmethod - def dump_handlers(handlers, filename, file_mode="w"): + def dump_handlers(handlers, filename, file_mode="wb"): dirs = filename.rsplit('/', maxsplit=1)[0] os.makedirs(dirs, exist_ok=True) to_dump = {} @@ -85,7 +86,8 @@ def dump_handlers(handlers, filename, file_mode="w"): else: to_dump[id_] = [handler.copy_to_dump()] - json.dump(to_dump, file) + # json.dump(to_dump, file) + pickle.dump(to_dump, file) if os.path.isfile(filename): os.remove(filename) @@ -95,14 +97,14 @@ def dump_handlers(handlers, filename, file_mode="w"): @staticmethod def return_load_handlers(filename, del_file_after_loading=True): if os.path.isfile(filename) and os.path.getsize(filename) > 0: - with open(filename, "r") as file: - handlers = json.load(file) - + with open(filename, "rb") as file: + # handlers = json.load(file) + handlers = pickle.load(file) result = {} for id_, handlers_ in handlers.items(): for handler in handlers_: - tmp = Handler(handler['callback'], handler["args"], handler["kwargs"]) + tmp = Handler(handler['callback'], *handler["args"], **handler["kwargs"]) if int(id_) in result.keys(): result[int(id_)].append(tmp) From 3c890a7846abbd7113f9eef92e8d9e6ecde12a73 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sat, 26 May 2018 16:37:25 +0300 Subject: [PATCH 0263/1808] Remove 2 spaces up to TeleBot class. Update: telebot/__init__.py --- telebot/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 838bbd1b0..14cfe3f97 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -116,8 +116,6 @@ def return_load_handlers(filename, del_file_after_loading=True): return result - - class TeleBot: """ This is TeleBot Class Methods: From 47e6dfd6bce65d5ee8dbbc6930107cfcc203ca99 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sat, 26 May 2018 16:52:30 +0300 Subject: [PATCH 0264/1808] Remove rudiment json things Update: telebot/__init__.py --- telebot/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 14cfe3f97..2f9d1c9b0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -8,7 +8,6 @@ import six import os -import json import pickle import logging @@ -86,7 +85,6 @@ def dump_handlers(handlers, filename, file_mode="wb"): else: to_dump[id_] = [handler.copy_to_dump()] - # json.dump(to_dump, file) pickle.dump(to_dump, file) if os.path.isfile(filename): @@ -98,7 +96,6 @@ def dump_handlers(handlers, filename, file_mode="wb"): def return_load_handlers(filename, del_file_after_loading=True): if os.path.isfile(filename) and os.path.getsize(filename) > 0: with open(filename, "rb") as file: - # handlers = json.load(file) handlers = pickle.load(file) result = {} for id_, handlers_ in handlers.items(): @@ -106,10 +103,10 @@ def return_load_handlers(filename, del_file_after_loading=True): tmp = Handler(handler['callback'], *handler["args"], **handler["kwargs"]) - if int(id_) in result.keys(): - result[int(id_)].append(tmp) + if id_ in result.keys(): + result[id_].append(tmp) else: - result[int(id_)] = [tmp] + result[id_] = [tmp] if del_file_after_loading: os.remove(filename) From 1de356dcc337751d0ae25a3dbf82aaec401d8a08 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sat, 26 May 2018 17:10:00 +0300 Subject: [PATCH 0265/1808] Change default save directory to "./.handler-saves/". Add del_file_after_loading param to load methods. Update: telebot/__init__.py --- telebot/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2f9d1c9b0..ce3963e6c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -188,10 +188,10 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): if self.threaded: self.worker_pool = util.ThreadPool(num_threads=num_threads) - def enable_save_next_step_handlers(self, delay=120, filename="./handler-saves/step.save"): + def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save", del_file_after_loading=True): self.next_step_saver = Saver(self.next_step_handlers, filename, delay) - def enable_save_reply_handlers(self, delay=120, filename="./handler-saves/reply.save"): + def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save", del_file_after_loading=True): self.reply_saver = Saver(self.reply_handlers, filename, delay) def disable_save_next_step_handlers(self): @@ -200,10 +200,10 @@ def disable_save_next_step_handlers(self): def disable_save_reply_handlers(self): self.reply_saver = None - def load_next_step_handlers(self, filename="./handler-saves/step.save"): + def load_next_step_handlers(self, filename="./.handler-saves/step.save"): self.next_step_saver.load_handlers(filename) - def load_reply_handlers(self, filename="./handler-saves/reply.save"): + def load_reply_handlers(self, filename="./.handler-saves/reply.save"): self.reply_saver.load_handlers(filename) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): @@ -1474,12 +1474,12 @@ def __init__(self, *args, **kwargs): TeleBot.__init__(self, *args, **kwargs) @util.async() - def enable_save_next_step_handlers(self, delay=120, filename="./handler-saves/step.save"): - return TeleBot.enable_save_next_step_handlers(self, delay, filename) + def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save", del_file_after_loading=True): + return TeleBot.enable_save_next_step_handlers(self, delay, filename, del_file_after_loading) @util.async() - def enable_save_reply_handlers(self, delay=120, filename="./handler-saves/reply.save"): - return TeleBot.enable_save_reply_handlers(self, delay, filename) + def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save", del_file_after_loading=True): + return TeleBot.enable_save_reply_handlers(self, delay, filename, del_file_after_loading) @util.async() def disable_save_next_step_handlers(self): @@ -1490,11 +1490,11 @@ def disable_save_reply_handlers(self): return TeleBot.enable_save_reply_handlers(self) @util.async() - def load_next_step_handlers(self, filename="./handler-saves/step.save"): + def load_next_step_handlers(self, filename="./.handler-saves/step.save"): return TeleBot.load_next_step_handlers(self, filename) @util.async() - def load_reply_handlers(self, filename="./handler-saves/reply.save"): + def load_reply_handlers(self, filename="./.handler-saves/reply.save"): return TeleBot.load_reply_handlers(self, filename) @util.async() From 13df7b59081411bd7f9e5ce05ee9357e35e4845c Mon Sep 17 00:00:00 2001 From: Waffle Date: Sat, 26 May 2018 17:21:39 +0300 Subject: [PATCH 0266/1808] Add enable_save_next_step_handlers and load_next_step_handlers methods to step_example/ Update: examples/step_example.py --- examples/step_example.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/step_example.py b/examples/step_example.py index fd8d07d57..0fc0ef158 100644 --- a/examples/step_example.py +++ b/examples/step_example.py @@ -75,4 +75,14 @@ def process_sex_step(message): bot.reply_to(message, 'oooops') +# Enable saving next step handlers to file "./.handlers-saves/step.save". +# Delay=2 means that after any change in next step handlers (e.g. calling register_next_step_handler()) +# saving will hapen after delay 2 seconds. +bot.enable_save_next_step_handlers(delay=2) + +# Load next_step_handlers from save file (default "./.handlers-saves/step.save") +# WARNING It will work only if enable_save_next_step_handlers was called! +bot.load_next_step_handlers() + + bot.polling() From d61de35a32272e31843d8bad3b3f37dba073907e Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 27 May 2018 01:23:20 +0300 Subject: [PATCH 0267/1808] Remove rudiment json things, again! Update: telebot/__init__.py --- telebot/__init__.py | 40 +++++++++------------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ce3963e6c..9669d872a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -31,23 +31,17 @@ class Handler: - def __init__(self, callback: {dict, 'function'}, *args, **kwargs): - if type(callback) == dict: - self.callback = getattr(sys.modules[callback["module"]], callback["name"]) - else: - self.callback = callback - + """ + Class for (next step|reply) handlers + """ + def __init__(self, callback: 'function', *args, **kwargs): + self.callback = callback self.args = args self.kwargs = kwargs def __getitem__(self, item): return getattr(self, item) - def copy_to_dump(self): - module_ = self.callback.__module__ - name = self.callback.__name__ - return Handler({"module": module_, "name": name}, *self.args, **self.kwargs) - class Saver: def __init__(self, handlers, filename, delay): @@ -76,16 +70,9 @@ def load_handlers(self, filename, del_file_after_loading=True): def dump_handlers(handlers, filename, file_mode="wb"): dirs = filename.rsplit('/', maxsplit=1)[0] os.makedirs(dirs, exist_ok=True) - to_dump = {} - with open(filename + ".tmp", file_mode) as file: - for id_, handlers_ in handlers.items(): - for handler in handlers_: - if id_ in to_dump.keys(): - to_dump[id_].append(handler.copy_to_dump()) - else: - to_dump[id_] = [handler.copy_to_dump()] - pickle.dump(to_dump, file) + with open(filename + ".tmp", file_mode) as file: + pickle.dump(handlers, file) if os.path.isfile(filename): os.remove(filename) @@ -97,20 +84,11 @@ def return_load_handlers(filename, del_file_after_loading=True): if os.path.isfile(filename) and os.path.getsize(filename) > 0: with open(filename, "rb") as file: handlers = pickle.load(file) - result = {} - for id_, handlers_ in handlers.items(): - for handler in handlers_: - - tmp = Handler(handler['callback'], *handler["args"], **handler["kwargs"]) - - if id_ in result.keys(): - result[id_].append(tmp) - else: - result[id_] = [tmp] if del_file_after_loading: os.remove(filename) - return result + + return handlers class TeleBot: From fa038c2e4286fdeefcc1678de8f26dab9ee32942 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 27 May 2018 01:30:14 +0300 Subject: [PATCH 0268/1808] Move del_file_after_loading param to right methods :face_palm: Update: telebot/__init__.py --- telebot/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9669d872a..c06b97778 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -166,10 +166,10 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): if self.threaded: self.worker_pool = util.ThreadPool(num_threads=num_threads) - def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save", del_file_after_loading=True): + def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): self.next_step_saver = Saver(self.next_step_handlers, filename, delay) - def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save", del_file_after_loading=True): + def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): self.reply_saver = Saver(self.reply_handlers, filename, delay) def disable_save_next_step_handlers(self): @@ -178,10 +178,10 @@ def disable_save_next_step_handlers(self): def disable_save_reply_handlers(self): self.reply_saver = None - def load_next_step_handlers(self, filename="./.handler-saves/step.save"): - self.next_step_saver.load_handlers(filename) + def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): + self.next_step_saver.load_handlers(filename, del_file_after_loading) - def load_reply_handlers(self, filename="./.handler-saves/reply.save"): + def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): self.reply_saver.load_handlers(filename) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): From 333949683f51018c21bc957a46d144d108cf888f Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 27 May 2018 01:37:06 +0300 Subject: [PATCH 0269/1808] Add doc strings to new TeleBot methods Update telebot/__init__.py --- telebot/__init__.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index c06b97778..422ac3675 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -44,6 +44,9 @@ def __getitem__(self, item): class Saver: + """ + Class for saving (next step|reply) handlers + """ def __init__(self, handlers, filename, delay): self.handlers = handlers self.filename = filename @@ -167,21 +170,51 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): self.worker_pool = util.ThreadPool(num_threads=num_threads) def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): + """ + Enable saving next step handlers (by default saving disable) + + :param delay: Delay between changes in handlers and saving + :param filename: Filename of save file + """ self.next_step_saver = Saver(self.next_step_handlers, filename, delay) def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): + """ + Enable saving reply handlers (by default saving disable) + + :param delay: Delay between changes in handlers and saving + :param filename: Filename of save file + """ self.reply_saver = Saver(self.reply_handlers, filename, delay) def disable_save_next_step_handlers(self): + """ + Disable saving next step handlers (by default saving disable) + """ self.next_step_saver = None def disable_save_reply_handlers(self): + """ + Disable saving next step handlers (by default saving disable) + """ self.reply_saver = None def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): + """ + Load next step handlers from save file + + :param filename: Filename of the file where handlers was saved + :param del_file_after_loading: Is passed True, after loading save file will be deleted + """ self.next_step_saver.load_handlers(filename, del_file_after_loading) def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): + """ + Load reply handlers from save file + + :param filename: Filename of the file where handlers was saved + :param del_file_after_loading: Is passed True, after loading save file will be deleted + """ self.reply_saver.load_handlers(filename) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): From 424c77fd2c1077c538c1d032b11fbd87b1c8332a Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 27 May 2018 16:54:56 +0300 Subject: [PATCH 0270/1808] Remove type hint for 2.x and PyPy python compatibility --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 422ac3675..ca7e92f23 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -34,7 +34,7 @@ class Handler: """ Class for (next step|reply) handlers """ - def __init__(self, callback: 'function', *args, **kwargs): + def __init__(self, callback, *args, **kwargs): self.callback = callback self.args = args self.kwargs = kwargs From 909d570dca83baf40e4734db218596ca02341744 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 27 May 2018 17:01:07 +0300 Subject: [PATCH 0271/1808] Add warning about lambda functions in callbacks --- telebot/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index ca7e92f23..e52711ac3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1172,6 +1172,8 @@ def register_for_reply(self, message, callback, *args, **kwargs): Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see the difference between a reply to `message` and an ordinary message. + Warning: In case `callback` as lambda function, saving reply handlers will not work. + :param message: The message for which we are awaiting a reply. :param callback: The callback function to be called when a reply arrives. Must accept one `message` parameter, which will contain the replied message. @@ -1186,6 +1188,8 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see the difference between a reply to `message` and an ordinary message. + Warning: In case `callback` as lambda function, saving reply handlers will not work. + :param message: The message for which we are awaiting a reply. :param callback: The callback function to be called when a reply arrives. Must accept one `message` parameter, which will contain the replied message. @@ -1213,6 +1217,8 @@ def register_next_step_handler(self, message, callback, *args, **kwargs): """ Registers a callback function to be notified when new message arrives after `message`. + Warning: In case `callback` as lambda function, saving next step handlers will not work. + :param message: The message for which we want to handle new message in the same chat. :param callback: The callback function which next new message arrives. :param args: Args to pass in callback func @@ -1225,6 +1231,8 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar """ Registers a callback function to be notified when new message arrives after `message`. + Warning: In case `callback` as lambda function, saving next step handlers will not work. + :param chat_id: The chat for which we want to handle new message. :param callback: The callback function which next new message arrives. :param args: Args to pass in callback func From 893d5386c5721e18b310e118ddee87fd81d4d19e Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 27 May 2018 17:02:04 +0300 Subject: [PATCH 0272/1808] Fix register_for_reply_by_message_id doc strings. --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e52711ac3..2545f5fcc 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1190,7 +1190,7 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs Warning: In case `callback` as lambda function, saving reply handlers will not work. - :param message: The message for which we are awaiting a reply. + :param message_id: The id of the message for which we are awaiting a reply. :param callback: The callback function to be called when a reply arrives. Must accept one `message` parameter, which will contain the replied message. """ From bc855f7610e777691d5e68bc38badc4324684843 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 27 May 2018 17:05:01 +0300 Subject: [PATCH 0273/1808] Fix register_for_reply_by_message_id and register_for_reply doc strings. --- telebot/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2545f5fcc..4c4996ac0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1169,9 +1169,6 @@ def register_for_reply(self, message, callback, *args, **kwargs): """ Registers a callback function to be notified when a reply to `message` arrives. - Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see - the difference between a reply to `message` and an ordinary message. - Warning: In case `callback` as lambda function, saving reply handlers will not work. :param message: The message for which we are awaiting a reply. @@ -1185,9 +1182,6 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs """ Registers a callback function to be notified when a reply to `message` arrives. - Warning: `message` must be sent with reply_markup=types.ForceReply(), otherwise TeleBot will not be able to see - the difference between a reply to `message` and an ordinary message. - Warning: In case `callback` as lambda function, saving reply handlers will not work. :param message_id: The id of the message for which we are awaiting a reply. From 78afd045d81b32bdc5b024e7eb6c8c2645650cd4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 27 May 2018 23:24:37 +0300 Subject: [PATCH 0274/1808] _notify_next_handlers drops messages if empty handler list After calling clear_step_handler(...) code: self.next_step_handlers[chat_id] = [] left the key in next_step_handlers. When a next message arrives, the old handler executes nothing (no handlers), but still remove message from message queue: new_messages.pop(i). Updated to pop message only when there are real handlers in the list. --- telebot/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8f9b60b75..2204bfa5a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1155,10 +1155,11 @@ def _notify_next_handlers(self, new_messages): chat_id = message.chat.id if chat_id in self.next_step_handlers.keys(): handlers = self.next_step_handlers[chat_id] - for handler in handlers: - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + if (handlers): + for handler in handlers: + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + new_messages.pop(i) # removing message that detects with next_step_handler self.next_step_handlers.pop(chat_id, None) - new_messages.pop(i) # removing message that detects with next_step_handler i += 1 @staticmethod From 776a699a8d78d621e8d2dcce9bac7e7041a36307 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 29 May 2018 18:55:41 +0300 Subject: [PATCH 0275/1808] _notify_next_handlers skips sequential messages Is there are several sequential messages and next_step_handlers are set, the _notify_next_handlers will process only every even message dew to execute both .pop(i) and i+=1 --- telebot/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2204bfa5a..32ed84df4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1153,14 +1153,17 @@ def _notify_next_handlers(self, new_messages): while i < len(new_messages): message = new_messages[i] chat_id = message.chat.id + was_poped = False if chat_id in self.next_step_handlers.keys(): handlers = self.next_step_handlers[chat_id] if (handlers): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) new_messages.pop(i) # removing message that detects with next_step_handler + was_poped = True self.next_step_handlers.pop(chat_id, None) - i += 1 + if (not was_poped): + i += 1 @staticmethod def _build_handler_dict(handler, **filters): From c8b2b1415745abc5278749dfc42e059bd65f332b Mon Sep 17 00:00:00 2001 From: users Date: Mon, 2 Jul 2018 18:13:11 +0300 Subject: [PATCH 0276/1808] rename async -> async_dec --- telebot/__init__.py | 114 ++++++++++++++++++++++---------------------- telebot/util.py | 2 +- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 32ed84df4..fb7770bde 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1354,230 +1354,230 @@ class AsyncTeleBot(TeleBot): def __init__(self, *args, **kwargs): TeleBot.__init__(self, *args, **kwargs) - @util.async() + @util.async_dec() def get_me(self): return TeleBot.get_me(self) - @util.async() + @util.async_dec() def get_file(self, *args): return TeleBot.get_file(self, *args) - @util.async() + @util.async_dec() def download_file(self, *args): return TeleBot.download_file(self, *args) - @util.async() + @util.async_dec() def get_user_profile_photos(self, *args, **kwargs): return TeleBot.get_user_profile_photos(self, *args, **kwargs) - @util.async() + @util.async_dec() def get_chat(self, *args): return TeleBot.get_chat(self, *args) - @util.async() + @util.async_dec() def leave_chat(self, *args): return TeleBot.leave_chat(self, *args) - @util.async() + @util.async_dec() def get_chat_administrators(self, *args): return TeleBot.get_chat_administrators(self, *args) - @util.async() + @util.async_dec() def get_chat_members_count(self, *args): return TeleBot.get_chat_members_count(self, *args) - @util.async() + @util.async_dec() def set_chat_sticker_set(self, *args): return TeleBot.set_chat_sticker_set(self, *args) - @util.async() + @util.async_dec() def delete_chat_sticker_set(self, *args): return TeleBot.delete_chat_sticker_set(self, *args) - @util.async() + @util.async_dec() def get_chat_member(self, *args): return TeleBot.get_chat_member(self, *args) - @util.async() + @util.async_dec() def send_message(self, *args, **kwargs): return TeleBot.send_message(self, *args, **kwargs) - @util.async() + @util.async_dec() def forward_message(self, *args, **kwargs): return TeleBot.forward_message(self, *args, **kwargs) - @util.async() + @util.async_dec() def delete_message(self, *args): return TeleBot.delete_message(self, *args) - @util.async() + @util.async_dec() def send_photo(self, *args, **kwargs): return TeleBot.send_photo(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_audio(self, *args, **kwargs): return TeleBot.send_audio(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_voice(self, *args, **kwargs): return TeleBot.send_voice(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_document(self, *args, **kwargs): return TeleBot.send_document(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_sticker(self, *args, **kwargs): return TeleBot.send_sticker(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_video(self, *args, **kwargs): return TeleBot.send_video(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_video_note(self, *args, **kwargs): return TeleBot.send_video_note(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_media_group(self, *args, **kwargs): return TeleBot.send_media_group(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_location(self, *args, **kwargs): return TeleBot.send_location(self, *args, **kwargs) - @util.async() + @util.async_dec() def edit_message_live_location(self, *args, **kwargs): return TeleBot.edit_message_live_location(self, *args, **kwargs) - @util.async() + @util.async_dec() def stop_message_live_location(self, *args, **kwargs): return TeleBot.stop_message_live_location(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_venue(self, *args, **kwargs): return TeleBot.send_venue(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_contact(self, *args, **kwargs): return TeleBot.send_contact(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_chat_action(self, *args, **kwargs): return TeleBot.send_chat_action(self, *args, **kwargs) - @util.async() + @util.async_dec() def kick_chat_member(self, *args, **kwargs): return TeleBot.kick_chat_member(self, *args, **kwargs) - @util.async() + @util.async_dec() def unban_chat_member(self, *args): return TeleBot.unban_chat_member(self, *args) - @util.async() + @util.async_dec() def restrict_chat_member(self, *args, **kwargs): return TeleBot.restrict_chat_member(self, *args, **kwargs) - @util.async() + @util.async_dec() def promote_chat_member(self, *args, **kwargs): return TeleBot.promote_chat_member(self, *args, **kwargs) - @util.async() + @util.async_dec() def export_chat_invite_link(self, *args): return TeleBot.export_chat_invite_link(self, *args) - @util.async() + @util.async_dec() def set_chat_photo(self, *args): return TeleBot.set_chat_photo(self, *args) - @util.async() + @util.async_dec() def delete_chat_photo(self, *args): return TeleBot.delete_chat_photo(self, *args) - @util.async() + @util.async_dec() def set_chat_title(self, *args): return TeleBot.set_chat_title(self, *args) - @util.async() + @util.async_dec() def set_chat_description(self, *args): return TeleBot.set_chat_description(self, *args) - @util.async() + @util.async_dec() def pin_chat_message(self, *args, **kwargs): return TeleBot.pin_chat_message(self, *args, **kwargs) - @util.async() + @util.async_dec() def unpin_chat_message(self, *args): return TeleBot.unpin_chat_message(self, *args) - @util.async() + @util.async_dec() def edit_message_text(self, *args, **kwargs): return TeleBot.edit_message_text(self, *args, **kwargs) - @util.async() + @util.async_dec() def edit_message_reply_markup(self, *args, **kwargs): return TeleBot.edit_message_reply_markup(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_game(self, *args, **kwargs): return TeleBot.send_game(self, *args, **kwargs) - @util.async() + @util.async_dec() def set_game_score(self, *args, **kwargs): return TeleBot.set_game_score(self, *args, **kwargs) - @util.async() + @util.async_dec() def get_game_high_scores(self, *args, **kwargs): return TeleBot.get_game_high_scores(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_invoice(self, *args, **kwargs): return TeleBot.send_invoice(self, *args, **kwargs) - @util.async() + @util.async_dec() def answer_shipping_query(self, *args, **kwargs): return TeleBot.answer_shipping_query(self, *args, **kwargs) - @util.async() + @util.async_dec() def answer_pre_checkout_query(self, *args, **kwargs): return TeleBot.answer_pre_checkout_query(self, *args, **kwargs) - @util.async() + @util.async_dec() def edit_message_caption(self, *args, **kwargs): return TeleBot.edit_message_caption(self, *args, **kwargs) - @util.async() + @util.async_dec() def answer_inline_query(self, *args, **kwargs): return TeleBot.answer_inline_query(self, *args, **kwargs) - @util.async() + @util.async_dec() def answer_callback_query(self, *args, **kwargs): return TeleBot.answer_callback_query(self, *args, **kwargs) - @util.async() + @util.async_dec() def send_sticker(self, *args, **kwargs): return TeleBot.send_sticker(self, *args, **kwargs) - @util.async() + @util.async_dec() def get_sticker_set(self, *args, **kwargs): return TeleBot.get_sticker_set(self, *args, **kwargs) - @util.async() + @util.async_dec() def upload_sticker_file(self, *args, **kwargs): return TeleBot.upload_sticker_file(self, *args, **kwargs) - @util.async() + @util.async_dec() def create_new_sticker_set(self, *args, **kwargs): return TeleBot.create_new_sticker_set(self, *args, **kwargs) - @util.async() + @util.async_dec() def add_sticker_to_set(self, *args, **kwargs): return TeleBot.add_sticker_to_set(self, *args, **kwargs) - @util.async() + @util.async_dec() def set_sticker_position_in_set(self, *args, **kwargs): return TeleBot.set_sticker_position_in_set(self, *args, **kwargs) - @util.async() + @util.async_dec() def delete_sticker_from_set(self, *args, **kwargs): return TeleBot.delete_sticker_from_set(self, *args, **kwargs) diff --git a/telebot/util.py b/telebot/util.py index 769f73ba8..f448d7803 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -143,7 +143,7 @@ def wait(self): return self.result -def async(): +def async_dec(): def decorator(fn): def wrapper(*args, **kwargs): return AsyncTask(fn, *args, **kwargs) From 9547a8d7b1dbf9934f44b765d9ec587c41c1d0cc Mon Sep 17 00:00:00 2001 From: user Date: Mon, 2 Jul 2018 23:23:48 +0300 Subject: [PATCH 0277/1808] test python 3.7 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f5f202a4f..312da5fef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From 4079772fd3e0186bfea516490c8bd92189d79837 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 2 Jul 2018 23:29:07 +0300 Subject: [PATCH 0278/1808] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 312da5fef..693456c20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ python: - "3.4" - "3.5" - "3.6" - - "3.7" + - "3.7-dev" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From c3b6ee9dc0757b51234df87e9372f93d5a7b8c75 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 2 Jul 2018 23:41:37 +0300 Subject: [PATCH 0279/1808] bug in travis-ci --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 693456c20..f5f202a4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ python: - "3.4" - "3.5" - "3.6" - - "3.7-dev" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From a3a2bd5793cc2411fa7c73785f169630dee742fb Mon Sep 17 00:00:00 2001 From: FrankWang Date: Tue, 3 Jul 2018 11:17:44 +0800 Subject: [PATCH 0280/1808] Add python 3.7 in travis ci. #527 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f5f202a4f..312da5fef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From 8bc5b744959b490f1df070032403742b08dd6537 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Tue, 3 Jul 2018 22:34:28 +0800 Subject: [PATCH 0281/1808] Remove 3.7 stable. Travis ci not support now. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 312da5fef..f5f202a4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ python: - "3.4" - "3.5" - "3.6" - - "3.7" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From 2b822f782d8916492fd347f7d0970f296a01b9f3 Mon Sep 17 00:00:00 2001 From: Andru1999 Date: Sun, 22 Jul 2018 00:31:02 +1000 Subject: [PATCH 0282/1808] Update __init__.py I find bug when I use your library without threading. If call bot.register_next_step_handler in function that register next_handler in next_step_handlers but in function _notify_next_handlers this delete and bot don`t have handler, but in threading mode function self.next_step_handlers.pop(chat_id, None) has time to eval self.next_step_handlers.pop(chat_id, None) and bug disappear. Sorry for my English --- telebot/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fb7770bde..551f09ffb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6,6 +6,7 @@ import re import sys import six +import copy import logging @@ -1155,13 +1156,13 @@ def _notify_next_handlers(self, new_messages): chat_id = message.chat.id was_poped = False if chat_id in self.next_step_handlers.keys(): - handlers = self.next_step_handlers[chat_id] + handlers = copy.deepcopy(self.next_step_handlers[chat_id]) + self.next_step_handlers.pop(chat_id, None) if (handlers): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) new_messages.pop(i) # removing message that detects with next_step_handler was_poped = True - self.next_step_handlers.pop(chat_id, None) if (not was_poped): i += 1 From 27d442fabfa8b1c94b59ea788797af1dbbd0d3e5 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 24 Jul 2018 00:33:13 +0300 Subject: [PATCH 0283/1808] timeout for send_message Add optional "timeout" parameter to send_message (the same as exists in all other send_*). Equal rights for all send functions! :) --- telebot/__init__.py | 4 ++-- telebot/apihelper.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fb7770bde..5106f87ab 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -459,7 +459,7 @@ def get_chat_member(self, chat_id, user_id): return types.ChatMember.de_json(result) def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None): + parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send text messages. @@ -477,7 +477,7 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me """ return types.Message.de_json( apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, - reply_markup, parse_mode, disable_notification)) + reply_markup, parse_mode, disable_notification, timeout)) def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ceda96cd0..e919b5a8c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -114,7 +114,7 @@ def download_file(token, file_path): def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None): + parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send text messages. On success, the sent Message is returned. :param token: @@ -137,6 +137,8 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m payload['parse_mode'] = parse_mode if disable_notification: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, method='post') From 8634e652497065178dd5f538327fddd04a53f963 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 25 Jul 2018 12:44:18 +0300 Subject: [PATCH 0284/1808] Fix kick_chat_member decription Fix kick_chat_member return value type description (should be boolean according to API and is boolean by fact). --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5106f87ab..061d4d335 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -723,7 +723,7 @@ def kick_chat_member(self, chat_id, user_id, until_date=None): :param user_id: Int : Unique identifier of the target user :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever - :return: types.Message + :return: boolean """ return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date) From 44dd89881d37b760942eb6af94aed0d6c5ac81ee Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 26 Jul 2018 21:41:08 +0800 Subject: [PATCH 0285/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2ea415f42..52c49d383 100644 --- a/README.md +++ b/README.md @@ -584,5 +584,6 @@ Get help. Discuss. Chat. * [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. * [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. * [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. +* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Music quiz game with big music database which was manually collected. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 5c199bd24679af85a2e222178fcb28ebbf8f9e5e Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 26 Jul 2018 22:17:55 +0800 Subject: [PATCH 0286/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52c49d383..e1ec2a87c 100644 --- a/README.md +++ b/README.md @@ -584,6 +584,6 @@ Get help. Discuss. Chat. * [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. * [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. * [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. -* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Music quiz game with big music database which was manually collected. +* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 7061091c1cbf5ed9221103a3e783094c13910ee8 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Tue, 31 Jul 2018 08:58:04 +0800 Subject: [PATCH 0287/1808] Update version. * Fix python 3.7 async --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1e76d0362..01e10a195 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.6.3', + version='3.6.4', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 5035e0ce80e86e24b489c99cd78a02fff280b717 Mon Sep 17 00:00:00 2001 From: heyyyoyy Date: Thu, 2 Aug 2018 21:15:33 +0300 Subject: [PATCH 0288/1808] Added parse mode for objects in Inline mode --- telebot/types.py | 70 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 5ca1b4ebe..27866d92f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1191,7 +1191,7 @@ def to_json(self): class InlineQueryResultPhoto(JsonSerializable): def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None, - description=None, caption=None, reply_markup=None, input_message_content=None): + description=None, caption=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 @@ -1202,6 +1202,8 @@ def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None :param title: Title for the result. :param description: Short description of the result. :param caption: Caption of the photo to be sent, 0-200 characters. + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or + inline URLs in the media caption. :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo :return: @@ -1215,6 +1217,7 @@ def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None 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 @@ -1230,6 +1233,8 @@ def to_json(self): 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_dic() if self.input_message_content: @@ -1286,7 +1291,7 @@ def to_json(self): class InlineQueryResultMpeg4Gif(JsonSerializable): def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, title=None, caption=None, - reply_markup=None, input_message_content=None, mpeg4_duration=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). :param id: Unique identifier for this result, 1-64 bytes @@ -1296,6 +1301,8 @@ def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None :param mpeg4_height: Video height :param title: Title for the result :param caption: Caption of the MPEG-4 file to be sent, 0-200 characters + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text + or inline URLs in the media caption. :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo :return: @@ -1308,6 +1315,7 @@ def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None 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 @@ -1322,6 +1330,8 @@ def to_json(self): 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_dic() if self.input_message_content: @@ -1333,8 +1343,8 @@ def to_json(self): class InlineQueryResultVideo(JsonSerializable): def __init__(self, id, video_url, mime_type, thumb_url, title, - caption=None, video_width=None, video_height=None, video_duration=None, description=None, - reply_markup=None, input_message_content=None): + caption=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. :param id: Unique identifier for this result, 1-64 bytes @@ -1342,6 +1352,8 @@ def __init__(self, id, video_url, mime_type, thumb_url, title, :param mime_type: Mime type of the content of video url, “text/html” or “video/mp4” :param thumb_url: URL of the thumbnail (jpeg only) for the video :param title: Title for the result + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or + inline URLs in the media caption. :param video_width: Video width :param video_height: Video height :param video_duration: Video duration in seconds @@ -1358,6 +1370,7 @@ def __init__(self, id, video_url, mime_type, thumb_url, title, 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 @@ -1375,6 +1388,8 @@ def to_json(self): 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_dic() if self.input_message_content: @@ -1383,13 +1398,14 @@ def to_json(self): class InlineQueryResultAudio(JsonSerializable): - def __init__(self, id, audio_url, title, caption=None, performer=None, audio_duration=None, reply_markup=None, - input_message_content=None): + 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 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 @@ -1399,6 +1415,8 @@ 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 if self.performer: json_dict['performer'] = self.performer if self.audio_duration: @@ -1411,13 +1429,14 @@ def to_json(self): class InlineQueryResultVoice(JsonSerializable): - def __init__(self, id, voice_url, title, caption=None, performer=None, voice_duration=None, reply_markup=None, - input_message_content=None): + 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 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 @@ -1427,6 +1446,8 @@ 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 if self.voice_duration: @@ -1439,14 +1460,15 @@ def to_json(self): class InlineQueryResultDocument(JsonSerializable): - def __init__(self, id, title, document_url, mime_type, caption=None, description=None, reply_markup=None, - input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None): + 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 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 @@ -1459,6 +1481,8 @@ def to_json(self): 'mime_type': self.mime_type} if self.caption: json_dict['caption'] = self.caption + if self.parse_mode: + json_dict['parse_mode'] = self.parse_mode if self.description: json_dict['description'] = self.description if self.thumb_url: @@ -1601,8 +1625,8 @@ def to_json(self): class InlineQueryResultCachedPhoto(BaseInlineQueryResultCached): - def __init__(self, id, photo_file_id, title=None, description=None, caption=None, reply_markup=None, - input_message_content=None): + def __init__(self, id, photo_file_id, title=None, description=None, caption=None, parse_mode=None, + reply_markup=None, input_message_content=None): BaseInlineQueryResultCached.__init__(self) self.type = 'photo' self.id = id @@ -1613,10 +1637,11 @@ def __init__(self, id, photo_file_id, title=None, description=None, caption=None self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['photo_file_id'] = photo_file_id + self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedGif(BaseInlineQueryResultCached): - def __init__(self, id, gif_file_id, title=None, description=None, caption=None, reply_markup=None, + 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) self.type = 'gif' @@ -1628,11 +1653,12 @@ def __init__(self, id, gif_file_id, title=None, description=None, caption=None, self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['gif_file_id'] = gif_file_id + self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached): - def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, reply_markup=None, - input_message_content=None): + def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None, parse_mode=None, + reply_markup=None, input_message_content=None): BaseInlineQueryResultCached.__init__(self) self.type = 'mpeg4_gif' self.id = id @@ -1643,6 +1669,7 @@ def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['mpeg4_file_id'] = mpeg4_file_id + self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedSticker(BaseInlineQueryResultCached): @@ -1657,7 +1684,7 @@ def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content class InlineQueryResultCachedDocument(BaseInlineQueryResultCached): - def __init__(self, id, document_file_id, title, description=None, caption=None, reply_markup=None, + 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) self.type = 'document' @@ -1669,10 +1696,11 @@ def __init__(self, id, document_file_id, title, description=None, caption=None, self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['document_file_id'] = document_file_id + self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedVideo(BaseInlineQueryResultCached): - def __init__(self, id, video_file_id, title, description=None, caption=None, reply_markup=None, + def __init__(self, id, video_file_id, title, description=None, caption=None, parse_mode=None, reply_markup=None, input_message_content=None): BaseInlineQueryResultCached.__init__(self) self.type = 'video' @@ -1684,10 +1712,12 @@ def __init__(self, id, video_file_id, title, description=None, caption=None, rep self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['video_file_id'] = video_file_id + self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedVoice(BaseInlineQueryResultCached): - def __init__(self, id, voice_file_id, title, caption=None, reply_markup=None, input_message_content=None): + def __init__(self, id, voice_file_id, title, caption=None, parse_mode=None, reply_markup=None, + input_message_content=None): BaseInlineQueryResultCached.__init__(self) self.type = 'voice' self.id = id @@ -1697,10 +1727,11 @@ def __init__(self, id, voice_file_id, title, caption=None, reply_markup=None, in self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['voice_file_id'] = voice_file_id + self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedAudio(BaseInlineQueryResultCached): - def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_message_content=None): + def __init__(self, id, audio_file_id, caption=None, parse_mode=None, reply_markup=None, input_message_content=None): BaseInlineQueryResultCached.__init__(self) self.type = 'audio' self.id = id @@ -1709,6 +1740,7 @@ def __init__(self, id, audio_file_id, caption=None, reply_markup=None, input_mes self.reply_markup = reply_markup self.input_message_content = input_message_content self.payload_dic['audio_file_id'] = audio_file_id + self.payload_dic['parse_mode'] = parse_mode # Games From 35ea2a2b7e768c13da6841490109e09dd56b5f65 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 3 Aug 2018 08:34:21 +0800 Subject: [PATCH 0289/1808] Fix #548 --- telebot/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fd8ed1630..f520eadae 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1295,7 +1295,6 @@ def _notify_next_handlers(self, new_messages): self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) new_messages.pop(i) # removing message that detects with next_step_handler was_poped = True - new_messages.pop(i) # removing message that detects with next_step_handler if self.next_step_saver is not None: self.next_step_saver.start_save_timer() if (not was_poped): From 41f7c079593bb7d4eedc6f44112a8966e8fbd11a Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 3 Aug 2018 08:35:04 +0800 Subject: [PATCH 0290/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 01e10a195..7b737e9df 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): return f.read() setup(name='pyTelegramBotAPI', - version='3.6.4', + version='3.6.5', description='Python Telegram bot api. ', long_description=readme(), author='eternnoir', From 4eae4695282d871bbdc8267b8765be06635370b7 Mon Sep 17 00:00:00 2001 From: Ramzan Bekbulatov Date: Mon, 6 Aug 2018 13:54:46 +0300 Subject: [PATCH 0291/1808] Use last version of README.md for PyPI docs with pretty formatting https://packaging.python.org/specifications/core-metadata/#description-content-type https://pypi.org/project/pyTelegramBotAPI/ --- setup.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 7b737e9df..9a401bcb8 100644 --- a/setup.py +++ b/setup.py @@ -2,14 +2,15 @@ from setuptools import setup from io import open -def readme(): - with open('README.rst', encoding='utf-8') as f: - return f.read() +def read(filename): + with open(filename, encoding='utf-8') as file: + return file.read() setup(name='pyTelegramBotAPI', version='3.6.5', description='Python Telegram bot api. ', - long_description=readme(), + long_description=read('README.md'), + long_description_content_type="text/markdown", author='eternnoir', author_email='eternnoir@gmail.com', url='https://github.com/eternnoir/pyTelegramBotAPI', From 74f75884f30854e507240bf8f413a1e48ef5e094 Mon Sep 17 00:00:00 2001 From: Ramzan Bekbulatov Date: Mon, 6 Aug 2018 13:56:38 +0300 Subject: [PATCH 0292/1808] Delete now unused deprecated README.rst --- README.rst | 807 ----------------------------------------------------- 1 file changed, 807 deletions(-) delete mode 100644 README.rst diff --git a/README.rst b/README.rst deleted file mode 100644 index d7765715c..000000000 --- a/README.rst +++ /dev/null @@ -1,807 +0,0 @@ -# - -.. raw:: html - -

- -pyTelegramBotAPI - -.. raw:: html - -

- -A simple, but extensible Python implementation for the `Telegram Bot -API `__. - -|Download Month| |Build Status| |Download Month| - -- `Getting started. <#getting-started>`__ -- `Writing your first bot <#writing-your-first-bot>`__ - - - `Prerequisites <#prerequisites>`__ - - `A simple echo bot <#a-simple-echo-bot>`__ - -- `General API Documentation <#general-api-documentation>`__ - - - `Types <#types>`__ - - `Methods <#methods>`__ - - `General use of the API <#general-use-of-the-api>`__ - - `Message handlers <#message-handlers>`__ - - `Callback Query handlers <#callback-query-handler>`__ - - `TeleBot <#telebot>`__ - - `Reply markup <#reply-markup>`__ - - `Inline Mode <#inline-mode>`__ - -- `Advanced use of the API <#advanced-use-of-the-api>`__ - - - `Asynchronous delivery of - messages <#asynchronous-delivery-of-messages>`__ - - `Sending large text messages <#sending-large-text-messages>`__ - - `Controlling the amount of Threads used by - TeleBot <#controlling-the-amount-of-threads-used-by-telebot>`__ - - `The listener mechanism <#the-listener-mechanism>`__ - - `Using web hooks <#using-web-hooks>`__ - - `Logging <#logging>`__ - -- `F.A.Q. <#faq>`__ - - - `Bot 2.0 <#bot-20>`__ - - `How can I distinguish a User and a GroupChat in - message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__ - -- `The Telegram Chat Group <#the-telegram-chat-group>`__ -- `More examples <#more-examples>`__ -- `Bots using this API <#bots-using-this-api>`__ - -Getting started. ----------------- - -This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and -Pypy 3. There are two ways to install the library: - -- Installation using pip (a Python package manager)\*: - -:: - - $ pip install pyTelegramBotAPI - -- Installation from source (requires git): - -:: - - $ git clone https://github.com/eternnoir/pyTelegramBotAPI.git - $ cd pyTelegramBotAPI - $ python setup.py install - -It is generally recommended to use the first option. - -\*\*While the API is production-ready, it is still under development and -it has regular updates, do not forget to update it regularly by calling -``pip install pytelegrambotapi --upgrade``\ \* - -Writing your first bot ----------------------- - -Prerequisites -~~~~~~~~~~~~~ - -It is presumed that you [have obtained an API token with -@BotFather](https://core.telegram.org/bots#botfather). We will call this -token ``TOKEN``. Furthermore, you have basic knowledge of the Python -programming language and more importantly `the Telegram Bot -API `__. - -A simple echo bot -~~~~~~~~~~~~~~~~~ - -The TeleBot class (defined in \_\_init\_\_.py) encapsulates all API -calls in a single class. It provides functions such as ``send_xyz`` -(``send_message``, ``send_document`` etc.) and several ways to listen -for incoming messages. - -Create a file called ``echo_bot.py``. Then, open the file and create an -instance of the TeleBot class. - -.. code:: python - - import telebot - - bot = telebot.TeleBot("TOKEN") - -*Note: Make sure to actually replace TOKEN with your own API token.* - -After that declaration, we need to register some so-called message -handlers. Message handlers define filters which a message must pass. If -a message passes the filter, the decorated function is called and the -incoming message is passed as an argument. - -Let's define a message handler which handles incoming ``/start`` and -``/help`` commands. - -.. code:: python - - @bot.message_handler(commands=['start', 'help']) - def send_welcome(message): - bot.reply_to(message, "Howdy, how are you doing?") - -A function which is decorated by a message handler **can have an -arbitrary name, however, it must have only one parameter (the -message)**. - -Let's add another handler: - -.. code:: python - - @bot.message_handler(func=lambda m: True) - def echo_all(message): - bot.reply_to(message, message.text) - -This one echoes all incoming text messages back to the sender. It uses a -lambda function to test a message. If the lambda returns True, the -message is handled by the decorated function. Since we want all messages -to be handled by this function, we simply always return True. - -*Note: all handlers are tested in the order in which they were declared* - -We now have a basic bot which replies a static message to "/start" and -"/help" commands and which echoes the rest of the sent messages. To -start the bot, add the following to our source file: - -.. code:: python - - bot.polling() - -Alright, that's it! Our source file now looks like this: - -.. code:: python - - import telebot - - bot = telebot.TeleBot("TOKEN") - - @bot.message_handler(commands=['start', 'help']) - def send_welcome(message): - bot.reply_to(message, "Howdy, how are you doing?") - - @bot.message_handler(func=lambda message: True) - def echo_all(message): - bot.reply_to(message, message.text) - - bot.polling() - -To start the bot, simply open up a terminal and enter -``python echo_bot.py`` to run the bot! Test it by sending commands -('/start' and '/help') and arbitrary text messages. - -General API Documentation -------------------------- - -Types -~~~~~ - -All types are defined in types.py. They are all completely in line with -the `Telegram API's definition of the -types `__, except -for the Message's ``from`` field, which is renamed to ``from_user`` -(because ``from`` is a Python reserved token). Thus, attributes such as -``message_id`` can be accessed directly with ``message.message_id``. -Note that ``message.chat`` can be either an instance of ``User`` or -``GroupChat`` (see `How can I distinguish a User and a GroupChat in -message.chat? <#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat>`__). - -The Message object also has a ``content_types``\ attribute, which -defines the type of the Message. ``content_types`` can be one of the -following strings: 'text', 'audio', 'document', 'photo', 'sticker', -'video', 'voice', 'location', 'contact', 'new\_chat\_participant', -'left\_chat\_participant', 'new\_chat\_title', 'new\_chat\_photo', -'delete\_chat\_photo', 'group\_chat\_created'. - -Methods -~~~~~~~ - -All `API -methods `__ are -located in the TeleBot class. They are renamed to follow common Python -naming conventions. E.g. ``getMe`` is renamed to ``get_me`` and -``sendMessage`` to ``send_message``. - -General use of the API -~~~~~~~~~~~~~~~~~~~~~~ - -Outlined below are some general use cases of the API. - -Message handlers -^^^^^^^^^^^^^^^^ - -A message handler is a function that is decorated with the -``message_handler`` decorator of a TeleBot instance. Message handlers -consist of one or multiple filters. Each filter much return True for a -certain message in order for a message handler to become eligible to -handle that message. A message handler is declared in the following way -(provided ``bot`` is an instance of TeleBot): - -.. code:: python - - @bot.message_handler(filters) - def function_name(message): - bot.reply_to(message, "This is a message handler") - -``function_name`` is not bound to any restrictions. Any function name is -permitted with message handlers. The function must accept at most one -argument, which will be the message that the function must handle. -``filters`` is a list of keyword arguments. A filter is declared in the -following manner: ``name=argument``. One handler may have multiple -filters. TeleBot supports the following filters: - -+--------+------+------+ -| name | argu | Cond | -| | ment | itio | -| | (s) | n | -+========+======+======+ -| conten | list | ``Tr | -| t\_typ | of | ue`` | -| es | stri | if | -| | ngs | mess | -| | (def | age. | -| | ault | cont | -| | ``[' | ent\ | -| | text | _typ | -| | ']`` | e | -| | ) | is | -| | | in | -| | | the | -| | | list | -| | | of | -| | | stri | -| | | ngs. | -+--------+------+------+ -| regexp | a | ``Tr | -| | regu | ue`` | -| | lar | if | -| | expr | ``re | -| | essi | .sea | -| | on | rch( | -| | as a | rege | -| | stri | xp_a | -| | ng | rg)` | -| | | ` | -| | | retu | -| | | rns | -| | | ``Tr | -| | | ue`` | -| | | and | -| | | ``me | -| | | ssag | -| | | e.co | -| | | nten | -| | | t_ty | -| | | pe = | -| | | = 't | -| | | ext' | -| | | `` | -| | | (See | -| | | `Pyt | -| | | hon | -| | | Regu | -| | | lar | -| | | Expr | -| | | essi | -| | | ons | -| | | ` | -| | | __ | -+--------+------+------+ -| comman | list | ``Tr | -| ds | of | ue`` | -| | stri | if | -| | ngs | ``me | -| | | ssag | -| | | e.co | -| | | nten | -| | | t_ty | -| | | pe = | -| | | = 't | -| | | ext' | -| | | `` | -| | | and | -| | | ``me | -| | | ssag | -| | | e.te | -| | | xt`` | -| | | star | -| | | ts | -| | | with | -| | | a | -| | | comm | -| | | and | -| | | that | -| | | is | -| | | in | -| | | the | -| | | list | -| | | of | -| | | stri | -| | | ngs. | -+--------+------+------+ -| func | a | ``Tr | -| | func | ue`` | -| | tion | if | -| | (lam | the | -| | bda | lamb | -| | or | da | -| | func | or | -| | tion | func | -| | refe | tion | -| | renc | refe | -| | e) | renc | -| | | e | -| | | retu | -| | | rns | -| | | ``Tr | -| | | ue`` | -+--------+------+------+ - -Here are some examples of using the filters and message handlers: - -.. code:: python - - import telebot - bot = telebot.TeleBot("TOKEN") - - # Handles all text messages that contains the commands '/start' or '/help'. - @bot.message_handler(commands=['start', 'help']) - def handle_start_help(message): - pass - - # Handles all sent documents and audio files - @bot.message_handler(content_types=['document', 'audio']) - def handle_docs_audio(message): - pass - - # Handles all text messages that match the regular expression - @bot.message_handler(regexp="SOME_REGEXP") - def handle_message(message): - pass - - #Handles all messages for which the lambda returns True - @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document']) - def handle_text_doc(message): - pass - - #Which could also be defined as: - def test_message(message): - return message.document.mime_type == 'text/plan' - - @bot.message_handler(func=test_message, content_types=['document']) - def handle_text_doc(message) - pass - - # Handlers can be stacked to create a function which will be called if either message_handler is eligible - # This handler will be called if the message starts with '/hello' OR is some emoji - @bot.message_handler(commands=['hello']) - @bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI) - def send_something(message): - pass - -**Important: all handlers are tested in the order in which they were -declared** - -Callback Query Handler -^^^^^^^^^^^^^^^^^^^^^^ - -In bot2.0 update. You can get ``callback_query`` in update object. In -telebot use ``callback_query_handler`` to process callback\_querys. - -.. code:: python - - @bot.callback_query_handler(func=lambda call: True) - def test_callback(call): - logger.info(call) - -TeleBot -^^^^^^^ - -.. code:: python - - import telebot - - TOKEN = '' - tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object - - # Upon calling this function, TeleBot starts polling the Telegram servers for new messages. - # - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers - # - interval: True/False (default False) - The interval between polling requests - # Note: Editing this parameter harms the bot's response time - # - timeout: integer (default 20) - Timeout in seconds for long polling. - tb.polling(none_stop=False, interval=0, timeout=20) - - # getMe - user = tb.get_me() - - # setWebhook - tb.set_webhook(url="http://example.com", certificate=open('mycert.pem')) - # unset webhook - tb.remove_webhook() - - # getUpdates - updates = tb.get_updates() - updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout): - - # sendMessage - tb.send_message(chatid, text) - - # forwardMessage - tb.forward_message(to_chat_id, from_chat_id, message_id) - - # All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file. - # sendPhoto - photo = open('/tmp/photo.png', 'rb') - tb.send_photo(chat_id, photo) - tb.send_photo(chat_id, "FILEID") - - # sendAudio - audio = open('/tmp/audio.mp3', 'rb') - tb.send_audio(chat_id, audio) - tb.send_audio(chat_id, "FILEID") - - ## sendAudio with duration, performer and title. - tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram') - - # sendVoice - voice = open('/tmp/voice.ogg', 'rb') - tb.send_voice(chat_id, voice) - tb.send_voice(chat_id, "FILEID") - - # sendDocument - doc = open('/tmp/file.txt', 'rb') - tb.send_document(chat_id, doc) - tb.send_document(chat_id, "FILEID") - - # sendSticker - sti = open('/tmp/sti.webp', 'rb') - tb.send_sticker(chat_id, sti) - tb.send_sticker(chat_id, "FILEID") - - # sendVideo - video = open('/tmp/video.mp4', 'rb') - tb.send_video(chat_id, video) - tb.send_video(chat_id, "FILEID") - - # sendLocation - tb.send_location(chat_id, lat, lon) - - # sendChatAction - # action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', - # 'record_audio', 'upload_audio', 'upload_document' or 'find_location'. - tb.send_chat_action(chat_id, action_string) - - # getFile - # Downloading a file is straightforward - # Returns a File object - import requests - file_info = tb.get_file(file_id) - - file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path)) - -Reply markup -^^^^^^^^^^^^ - -All ``send_xyz`` functions of TeleBot take an optional ``reply_markup`` -argument. This argument must be an instance of ``ReplyKeyboardMarkup``, -``ReplyKeyboardRemove`` or ``ForceReply``, which are defined in types.py. - -.. code:: python - - from telebot import types - - # Using the ReplyKeyboardMarkup class - # It's constructor can take the following optional arguments: - # - resize_keyboard: True/False (default False) - # - one_time_keyboard: True/False (default False) - # - selective: True/False (default False) - # - row_width: integer (default 3) - # row_width is used in combination with the add() function. - # It defines how many buttons are fit on each row before continuing on the next row. - markup = types.ReplyKeyboardMarkup(row_width=2) - itembtn1 = types.KeyboardButton('a') - itembtn2 = types.KeyboardButton('v') - itembtn3 = types.KeyboardButton('d') - markup.add(itembtn1, itembtn2, itembtn3) - tb.send_message(chat_id, "Choose one letter:", reply_markup=markup) - - # or add strings one row at a time: - markup = types.ReplyKeyboardMarkup() - itembtna = types.KeyboardButton('a') - itembtnv = types.KeyboardButton('v') - itembtnc = types.KeyboardButton('c') - itembtnd = types.KeyboardButton('d') - itembtne = types.KeyboardButton('e') - markup.row(itembtna, itembtnv) - markup.row(itembtnc, itembtnd, itembtne) - tb.send_message(chat_id, "Choose one letter:", reply_markup=markup) - -The last example yields this result: - -.. figure:: https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg - :alt: ReplyKeyboardMarkup - - ReplyKeyboardMarkup - -.. code:: python - - # ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup - # Takes an optional selective argument (True/False, default False) - markup = types.ReplyKeyboardRemove(selective=False) - tb.send_message(chat_id, message, reply_markup=markup) - -.. code:: python - - # ForceReply: forces a user to reply to a message - # Takes an optional selective argument (True/False, default False) - markup = types.ForceReply(selective=False) - tb.send_message(chat_id, "Send me another word:", reply_markup=markup) - -ForceReply: - -.. figure:: https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg - :alt: ForceReply - - ForceReply - -Inline Mode -~~~~~~~~~~~ - -More information about `Inline -mode `__. - -inline\_handler -^^^^^^^^^^^^^^^ - -Now, you can use inline\_handler to get inline\_query in telebot. - -.. code:: python - - - @bot.inline_handler(lambda query: query.query == 'text') - def query_text(inline_query): - # Query message is text - -chosen\_inline\_handler -^^^^^^^^^^^^^^^^^^^^^^^ - -Use chosen\_inline\_handler to get chosen\_inline\_result in telebot. -Don't forgot add the /setinlinefeedback command for @Botfather. - -More information : -`collecting-feedback `__ - -.. code:: python - - @bot.chosen_inline_handler(func=lambda chosen_inline_result: True) - def test_chosen(chosen_inline_result): - # Process all chosen_inline_result. - -answer\_inline\_query -^^^^^^^^^^^^^^^^^^^^^ - -.. code:: python - - @bot.inline_handler(lambda query: query.query == 'text') - def query_text(inline_query): - try: - r = types.InlineQueryResultArticle('1', 'Result', 'Result message.') - r2 = types.InlineQueryResultArticle('2', 'Result2', 'Result message2.') - bot.answer_inline_query(inline_query.id, [r, r2]) - except Exception as e: - print(e) - -Advanced use of the API ------------------------ - -Asynchronous delivery of messages -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There exists an implementation of TeleBot which executes all -``send_xyz`` and the ``get_me`` functions asynchronously. This can speed -up you bot **significantly**, but it has unwanted side effects if used -without caution. To enable this behaviour, create an instance of -AsyncTeleBot instead of TeleBot. - -.. code:: python - - tb = telebot.AsyncTeleBot("TOKEN") - -Now, every function that calls the Telegram API is executed in a -separate Thread. The functions are modified to return an AsyncTask -instance (defined in util.py). Using AsyncTeleBot allows you to do the -following: - -.. code:: python - - import telebot - - tb = telebot.AsyncTeleBot("TOKEN") - task = tb.get_me() # Execute an API call - # Do some other operations... - a = 0 - for a in range(100): - a += 10 - - result = task.wait() # Get the result of the execution - -*Note: if you execute send\_xyz functions after eachother without -calling wait(), the order in which messages are delivered might be -wrong.* - -Sending large text messages -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sometimes you must send messages that exceed 5000 characters. The -Telegram API can not handle that many characters in one request, so we -need to split the message in multiples. Here is how to do that using the -API: - -.. code:: python - - from telebot import util - large_text = open("large_text.txt", "rb").read() - - # Split the text each 3000 characters. - # split_string returns a list with the splitted text. - splitted_text = util.split_string(large_text, 3000) - for text in splitted_text: - tb.send_message(chat_id, text) - -Controlling the amount of Threads used by TeleBot -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The TeleBot constructor takes the following optional arguments: - -- create\_threads: True/False (default True). A flag to indicate - whether TeleBot should execute message handlers on it's polling - Thread. -- num\_threads: integer (default 4). Controls the amount of - WorkerThreads created for the internal thread pool that TeleBot uses - to execute message handlers. Is not used when create\_threads is - False. - -The listener mechanism -~~~~~~~~~~~~~~~~~~~~~~ - -As an alternative to the message handlers, one can also register a -function as a listener to TeleBot. Example: - -.. code:: python - - def handle_messages(messages): - for message in messsages: - # Do something with the message - bot.reply_to(message, 'Hi') - - bot.set_update_listener(handle_messages) - bot.polling() - -Using webhooks -~~~~~~~~~~~~~~ - -When using webhooks telegram sends one Update per call, for processing -it you should call process\_new\_messages([update.message]) when you -recieve it. - -There are some examples using webhooks in the -*examples/webhook\_examples* directory. - -Logging -~~~~~~~ - -You can use the Telebot module logger to log debug info about Telebot. -Use ``telebot.logger`` to get the logger of the TeleBot module. It is -possible to add custom logging Handlers to the logger. Refer to the -`Python logging module -page `__ for more info. - -.. code:: python - - import logging - - logger = telebot.logger - telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. - -F.A.Q. ------- - -Bot 2.0 -~~~~~~~ - -April 9,2016 Telegram release new bot 2.0 API, which has a drastic -revision especially for the change of method's interface.If you want to -update to the latest version, please make sure you've switched bot's -code to bot 2.0 method interface. - -`More information about pyTelegramBotAPI support -bot2.0 `__ - -How can I distinguish a User and a GroupChat in message.chat? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Telegram Bot API support new type Chat for message.chat. - -- Check the ``type`` attribute in ``Chat`` object: -- \`\`\`python if message.chat.type == “private”: # private chat - message - -if message.chat.type == “group”: # group chat message - -if message.chat.type == “supergroup”: # supergroup chat message - -if message.chat.type == “channel”: # channel message - -\`\`\` - -The Telegram Chat Group ------------------------ - -Get help. Discuss. Chat. - -- Join the pyTelegramBotAPI Telegram Chat Group -- Messge to @eternnoir by telegram for Invitation. -- We now have a Telegram Channel as well! Keep yourself up to date with - API changes, and `join it `__. - -More examples -------------- - -- `Echo - Bot `__ -- `Deep - Linking `__ -- `next\_step\_handler - Example `__ - -Bots using this API -------------------- - -- `SiteAlert bot `__ - (`source `__) by - *ilteoood* - Monitors websites and sends a notification on changes -- `TelegramLoggingBot `__ - by *aRandomStranger* -- `Telegram - LMGTFY\_bot `__ by - *GabrielRF* -- `Telegram - UrlProBot `__ by - *GabrielRF* -- `Telegram Proxy - Bot `__ by - *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as - some kind of "proxy". -- `Telegram Proxy Bot `__ by - *mrgigabyte* - - ``Credits for the original version of this bot goes to`` **Groosha** - ``, simply added certain features which I thought were needed``. -- `RadRetroRobot `__ by - *Tronikart* - Multifunctional Telegram Bot RadRetroRobot. -- `League of Legends bot `__ - (`source `__) by *i32ropie* -- `NeoBot `__ by *neoranger* -- `TagAlertBot `__ by *pitasi* - -Want to have your bot listed here? Send a Telegram message to @eternnoir -or @pevdh. - -.. |Download Month| image:: https://img.shields.io/pypi/v/pyTelegramBotAPI.svg - :target: https://pypi.python.org/pypi/pyTelegramBotAPI -.. |Build Status| image:: https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master - :target: https://travis-ci.org/eternnoir/pyTelegramBotAPI -.. |Download Month| image:: https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg - :target: https://pypi.python.org/pypi/pyTelegramBotAPI From 494b535a9127c769401791c8acb7a1d1443560a0 Mon Sep 17 00:00:00 2001 From: Andru1999 Date: Wed, 8 Aug 2018 10:46:23 +1000 Subject: [PATCH 0293/1808] Fix issue When you use threading mode --- telebot/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f520eadae..320601c39 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6,7 +6,6 @@ import re import sys import six -import copy import os import pickle @@ -1288,8 +1287,7 @@ def _notify_next_handlers(self, new_messages): chat_id = message.chat.id was_poped = False if chat_id in self.next_step_handlers.keys(): - handlers = copy.deepcopy(self.next_step_handlers[chat_id]) - self.next_step_handlers.pop(chat_id, None) + handlers = self.next_step_handlers.pop(chat_id, None) if (handlers): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) From 12dbcb56d3d0892fe6d53d3d9079572635386ed9 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 9 Aug 2018 08:45:07 +0800 Subject: [PATCH 0294/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e1ec2a87c..9222b7b89 100644 --- a/README.md +++ b/README.md @@ -585,5 +585,7 @@ Get help. Discuss. Chat. * [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. * [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. * [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. +* [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) + Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 8ac6e664c5893e55d6a46adee0b61dbb55c1ffc9 Mon Sep 17 00:00:00 2001 From: uburuntu Date: Thu, 9 Aug 2018 19:10:01 +0300 Subject: [PATCH 0295/1808] new: InputMediaAnimation, InputMediaAudio, InputMediaDocument, editMessageMedia Added support for editing the media content of messages: added the method editMessageMedia and new types InputMediaAnimation, InputMediaAudio, and InputMediaDocument. --- telebot/__init__.py | 10 +++++ telebot/apihelper.py | 28 ++++++++++-- telebot/types.py | 102 +++++++++++++++++++++++++++++++++--------- tests/test_telebot.py | 11 +++++ 4 files changed, 128 insertions(+), 23 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f520eadae..ec195679c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -999,6 +999,12 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ return result return types.Message.de_json(result) + def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + result = apihelper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) + if type(result) == bool: # if edit inline message return is bool not Message. + return result + return types.Message.de_json(result) + def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): result = apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) if type(result) == bool: @@ -1677,6 +1683,10 @@ def unpin_chat_message(self, *args): def edit_message_text(self, *args, **kwargs): return TeleBot.edit_message_text(self, *args, **kwargs) + @util.async_dec() + def edit_message_media(self, *args, **kwargs): + return TeleBot.edit_message_media(self, *args, **kwargs) + @util.async_dec() def edit_message_reply_markup(self, *args, **kwargs): return TeleBot.edit_message_reply_markup(self, *args, **kwargs) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ceda96cd0..f74f7c734 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from telebot.types import InputMedia try: import ujson as json @@ -264,7 +265,7 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None): method_url = r'sendMediaGroup' - media_json, files = _convert_input_media(media) + media_json, files = _convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} if disable_notification: payload['disable_notification'] = disable_notification @@ -638,6 +639,21 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m return _make_request(token, method_url, params=payload) +def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + method_url = r'editMessageMedia' + media_json, file = _convert_input_media(media) + payload = {'media': media_json} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload, files=file) + + def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): method_url = r'editMessageReplyMarkup' payload = {} @@ -937,11 +953,17 @@ def _convert_markup(markup): return markup -def _convert_input_media(array): +def _convert_input_media(media): + if isinstance(media, InputMedia): + return media._convert_input_media() + return None, None + + +def _convert_input_media_array(array): media = [] files = {} for input_media in array: - if isinstance(input_media, types.JsonSerializable): + if isinstance(input_media, InputMedia): media_dict = input_media.to_dic() if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') diff --git a/telebot/types.py b/telebot/types.py index 27866d92f..f3e030006 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2073,48 +2073,56 @@ def to_dic(self): # InputMedia -class InputMediaPhoto(JsonSerializable): - def __init__(self, media, caption=None, parse_mode=None): - self.type = "photo" +class InputMedia(JsonSerializable): + def __init__(self, type, media, caption=None, parse_mode=None): + self.type = type self.media = media self.caption = caption self.parse_mode = parse_mode + self._media_dic = 'attach://' + util.generate_random_token() if not util.is_string(self.media) else self.media + def to_json(self): return json.dumps(self.to_dic()) def to_dic(self): - ret = {'type': self.type, 'media': 'attach://' + util.generate_random_token() - if not util.is_string(self.media) else self.media} + ret = {'type': self.type, 'media': self._media_dic} if self.caption: ret['caption'] = self.caption if self.parse_mode: ret['parse_mode'] = self.parse_mode return ret + def _convert_input_media(self): + if util.is_string(self.media): + return self.to_json(), None + + return self.to_json(), {self._media_dic: self.media} + + +class InputMediaPhoto(InputMedia): + def __init__(self, media, caption=None, parse_mode=None): + super(InputMedia).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) + + def to_dic(self): + ret = super(InputMedia).to_dic() + return ret + -class InputMediaVideo(JsonSerializable): - def __init__(self, media, caption=None, parse_mode=None, width=None, height=None, duration=None, +class InputMediaVideo(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, supports_streaming=None): - self.type = "video" - self.media = media - self.caption = caption - self.parse_mode = parse_mode + super(InputMedia).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb self.width = width self.height = height self.duration = duration self.supports_streaming = supports_streaming - def to_json(self): - return json.dumps(self.to_dic()) - def to_dic(self): - ret = {'type': self.type, 'media': 'attach://' + util.generate_random_token() - if not util.is_string(self.media) else self.media} - if self.caption: - ret['caption'] = self.caption - if self.parse_mode: - ret['parse_mode'] = self.parse_mode + ret = super(InputMedia).to_dic() + if self.thumb: + ret['thumb'] = self.thumb if self.width: ret['width'] = self.width if self.height: @@ -2124,3 +2132,57 @@ def to_dic(self): if self.supports_streaming: ret['supports_streaming'] = self.supports_streaming return ret + + +class InputMediaAnimation(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): + super(InputMedia).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.width = width + self.height = height + self.duration = duration + + def to_dic(self): + ret = super(InputMedia).to_dic() + if self.thumb: + ret['thumb'] = self.thumb + if self.width: + ret['width'] = self.width + if self.height: + ret['height'] = self.height + if self.duration: + ret['duration'] = self.duration + return ret + + +class InputMediaAudio(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): + super(InputMedia).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.duration = duration + self.performer = performer + self.title = title + + def to_dic(self): + ret = super(InputMedia).to_dic() + if self.thumb: + ret['thumb'] = self.thumb + if self.duration: + ret['duration'] = self.duration + if self.performer: + ret['performer'] = self.performer + if self.title: + ret['title'] = self.title + return ret + + +class InputMediaDocument(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None): + super(InputMedia).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + + def to_dic(self): + ret = super(InputMedia).to_dic() + if self.thumb: + ret['thumb'] = self.thumb + return ret diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 6818b5bc1..3279b30cb 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -361,6 +361,17 @@ def test_edit_message_caption(self): new_msg = tb.edit_message_caption(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id) assert new_msg.caption == 'Edit test' + def test_edit_message_media(self): + file_data = open('../examples/detailed_example/kitten.jpg', 'rb') + file_data_2 = open('../examples/detailed_example/rooster.jpg', 'rb') + tb = telebot.TeleBot(TOKEN) + msg = tb.send_photo(CHAT_ID, file_data) + new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id, media=file_data_2) + assert type(new_msg) != bool + + new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id, media=msg.photo[0].file_id) + assert type(new_msg) != bool + def test_get_chat(self): tb = telebot.TeleBot(TOKEN) ch = tb.get_chat(GROUP_ID) From cf69a06ab858f75f8ba7fca6b1570ce512f0ba4a Mon Sep 17 00:00:00 2001 From: uburuntu Date: Fri, 10 Aug 2018 16:47:59 +0300 Subject: [PATCH 0296/1808] enh: make code better and enhance test case --- telebot/apihelper.py | 7 +++---- telebot/types.py | 29 +++++++++++++++++------------ tests/test_telebot.py | 7 +++++-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f74f7c734..ae72e372d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from telebot.types import InputMedia try: import ujson as json @@ -651,7 +650,7 @@ def edit_message_media(token, media, chat_id=None, message_id=None, inline_messa payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) - return _make_request(token, method_url, params=payload, files=file) + return _make_request(token, method_url, params=payload, files=file, method='post' if file else 'get') def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): @@ -954,7 +953,7 @@ def _convert_markup(markup): def _convert_input_media(media): - if isinstance(media, InputMedia): + if isinstance(media, types.InputMedia): return media._convert_input_media() return None, None @@ -963,7 +962,7 @@ def _convert_input_media_array(array): media = [] files = {} for input_media in array: - if isinstance(input_media, InputMedia): + if isinstance(input_media, types.InputMedia): media_dict = input_media.to_dic() if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') diff --git a/telebot/types.py b/telebot/types.py index f3e030006..2e48b5d0d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2080,7 +2080,12 @@ def __init__(self, type, media, caption=None, parse_mode=None): self.caption = caption self.parse_mode = parse_mode - self._media_dic = 'attach://' + util.generate_random_token() if not util.is_string(self.media) else self.media + if util.is_string(self.media): + self._media_name = '' + self._media_dic = self.media + else: + self._media_name = util.generate_random_token() + self._media_dic = 'attach://{}'.format(self._media_name) def to_json(self): return json.dumps(self.to_dic()) @@ -2097,22 +2102,22 @@ def _convert_input_media(self): if util.is_string(self.media): return self.to_json(), None - return self.to_json(), {self._media_dic: self.media} + return self.to_json(), {self._media_name: self.media} class InputMediaPhoto(InputMedia): def __init__(self, media, caption=None, parse_mode=None): - super(InputMedia).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) + super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) def to_dic(self): - ret = super(InputMedia).to_dic() + ret = super(InputMediaPhoto, self).to_dic() return ret class InputMediaVideo(InputMedia): def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, supports_streaming=None): - super(InputMedia).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) + super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb self.width = width self.height = height @@ -2120,7 +2125,7 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, self.supports_streaming = supports_streaming def to_dic(self): - ret = super(InputMedia).to_dic() + ret = super(InputMediaVideo, self).to_dic() if self.thumb: ret['thumb'] = self.thumb if self.width: @@ -2136,14 +2141,14 @@ def to_dic(self): class InputMediaAnimation(InputMedia): def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): - super(InputMedia).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) + super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb self.width = width self.height = height self.duration = duration def to_dic(self): - ret = super(InputMedia).to_dic() + ret = super(InputMediaAnimation, self).to_dic() if self.thumb: ret['thumb'] = self.thumb if self.width: @@ -2157,14 +2162,14 @@ def to_dic(self): class InputMediaAudio(InputMedia): def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): - super(InputMedia).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) + super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb self.duration = duration self.performer = performer self.title = title def to_dic(self): - ret = super(InputMedia).to_dic() + ret = super(InputMediaAudio, self).to_dic() if self.thumb: ret['thumb'] = self.thumb if self.duration: @@ -2178,11 +2183,11 @@ def to_dic(self): class InputMediaDocument(InputMedia): def __init__(self, media, thumb=None, caption=None, parse_mode=None): - super(InputMedia).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) + super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb def to_dic(self): - ret = super(InputMedia).to_dic() + ret = super(InputMediaDocument, self).to_dic() if self.thumb: ret['thumb'] = self.thumb return ret diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 3279b30cb..b81485e99 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -366,11 +366,14 @@ def test_edit_message_media(self): file_data_2 = open('../examples/detailed_example/rooster.jpg', 'rb') tb = telebot.TeleBot(TOKEN) msg = tb.send_photo(CHAT_ID, file_data) - new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id, media=file_data_2) + new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id, + media=types.InputMediaPhoto(file_data_2, caption='Test editMessageMedia 0')) assert type(new_msg) != bool - new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id, media=msg.photo[0].file_id) + new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id, + media=types.InputMediaPhoto(msg.photo[0].file_id, caption='Test editMessageMedia')) assert type(new_msg) != bool + assert new_msg.caption == 'Test editMessageMedia' def test_get_chat(self): tb = telebot.TeleBot(TOKEN) From 7dd53b1396b28b132eca5cbe9978a37af0a13b06 Mon Sep 17 00:00:00 2001 From: uburuntu Date: Tue, 14 Aug 2018 12:23:15 +0300 Subject: [PATCH 0297/1808] fix: support python2 super() --- telebot/types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 2e48b5d0d..c59a4d61e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10,7 +10,7 @@ from telebot import util -class JsonSerializable: +class JsonSerializable(object): """ Subclasses of this class are guaranteed to be able to be converted to JSON format. All subclasses of this class must override to_json. @@ -26,7 +26,7 @@ def to_json(self): raise NotImplementedError -class Dictionaryable: +class Dictionaryable(object): """ Subclasses of this class are guaranteed to be able to be converted to dictionary. All subclasses of this class must override to_dic. @@ -42,7 +42,7 @@ def to_dic(self): raise NotImplementedError -class JsonDeserializable: +class JsonDeserializable(object): """ Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string. All subclasses of this class must override de_json. From e419214b4998524ac1bd6db03713fb747d13560e Mon Sep 17 00:00:00 2001 From: uburuntu Date: Tue, 14 Aug 2018 17:29:35 +0300 Subject: [PATCH 0298/1808] fix: python2 positional argument specifiers --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index c59a4d61e..7340c105e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2085,7 +2085,7 @@ def __init__(self, type, media, caption=None, parse_mode=None): self._media_dic = self.media else: self._media_name = util.generate_random_token() - self._media_dic = 'attach://{}'.format(self._media_name) + self._media_dic = 'attach://{0}'.format(self._media_name) def to_json(self): return json.dumps(self.to_dic()) From 6a4c7e731b80e1187a4f9423ae6e7594e2e6be4f Mon Sep 17 00:00:00 2001 From: uburuntu Date: Fri, 17 Aug 2018 12:46:40 +0300 Subject: [PATCH 0299/1808] fix: delete doubled Sticker class (left a new one) --- telebot/types.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 7340c105e..884e3d4ae 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -598,29 +598,6 @@ def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_siz self.file_size = file_size -class Sticker(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - obj = cls.check_json(json_string) - file_id = obj['file_id'] - width = obj['width'] - height = obj['height'] - thumb = None - if 'thumb' in obj: - thumb = PhotoSize.de_json(obj['thumb']) - emoji = obj.get('emoji') - file_size = obj.get('file_size') - return cls(file_id, width, height, thumb, emoji, file_size) - - def __init__(self, file_id, width, height, thumb, emoji=None, file_size=None): - self.file_id = file_id - self.width = width - self.height = height - self.thumb = thumb - self.emoji = emoji - self.file_size = file_size - - class Video(JsonDeserializable): @classmethod def de_json(cls, json_string): From 65a272b901b848bf5791c09ccbf03657aad47d71 Mon Sep 17 00:00:00 2001 From: uburuntu Date: Fri, 17 Aug 2018 12:47:44 +0300 Subject: [PATCH 0300/1808] fix: python 2/3 compatibility in examples --- examples/detailed_example/detailed_example.py | 4 ++-- examples/inline_example.py | 2 +- examples/telebot_bot/telebot_bot.py | 2 +- .../webhook_examples/webhook_cpython_echo_bot.py | 12 +++++++++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index 8fad9af31..4bd428b75 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -33,7 +33,7 @@ def get_user_step(uid): else: knownUsers.append(uid) userStep[uid] = 0 - print "New user detected, who hasn't used \"/start\" yet" + print("New user detected, who hasn't used \"/start\" yet") return 0 @@ -45,7 +45,7 @@ def listener(messages): for m in messages: if m.content_type == 'text': # print the sent message to the console - print str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text + print(str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text) bot = telebot.TeleBot(TOKEN) diff --git a/examples/inline_example.py b/examples/inline_example.py index c97dea2e2..725708e83 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -69,5 +69,5 @@ def main_loop(): try: main_loop() except KeyboardInterrupt: - print >> sys.stderr, '\nExiting by user request.\n' + print('\nExiting by user request.\n') sys.exit(0) diff --git a/examples/telebot_bot/telebot_bot.py b/examples/telebot_bot/telebot_bot.py index cd29276fb..46319ce6f 100644 --- a/examples/telebot_bot/telebot_bot.py +++ b/examples/telebot_bot/telebot_bot.py @@ -70,7 +70,7 @@ def on_start(message): def listener(messages): for m in messages: - print str(m) + print(str(m)) bot.set_update_listener(listener) bot.polling() diff --git a/examples/webhook_examples/webhook_cpython_echo_bot.py b/examples/webhook_examples/webhook_cpython_echo_bot.py index 5c1dfc94c..807b5a7e9 100644 --- a/examples/webhook_examples/webhook_cpython_echo_bot.py +++ b/examples/webhook_examples/webhook_cpython_echo_bot.py @@ -4,7 +4,13 @@ # This is a simple echo bot using decorators and webhook with BaseHTTPServer # It echoes any incoming text messages and does not use the polling method. -import BaseHTTPServer +try: + # Python 2 + from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +except ImportError: + # Python 3 + from http.server import BaseHTTPRequestHandler, HTTPServer + import ssl import telebot import logging @@ -38,7 +44,7 @@ # WebhookHandler, process webhook calls -class WebhookHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class WebhookHandler(BaseHTTPRequestHandler): server_version = "WebhookHandler/1.0" def do_HEAD(self): @@ -88,7 +94,7 @@ def echo_message(message): certificate=open(WEBHOOK_SSL_CERT, 'r')) # Start server -httpd = BaseHTTPServer.HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT), +httpd = HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT), WebhookHandler) httpd.socket = ssl.wrap_socket(httpd.socket, From 54eba946be7426641cc5e58d888cb1626f1438e2 Mon Sep 17 00:00:00 2001 From: uburuntu Date: Fri, 17 Aug 2018 12:48:59 +0300 Subject: [PATCH 0301/1808] fix: wrong arguments usage (fix fa038c2) --- telebot/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 41b65a4b0..7865d94d4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1496,12 +1496,12 @@ def __init__(self, *args, **kwargs): TeleBot.__init__(self, *args, **kwargs) @util.async_dec() - def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save", del_file_after_loading=True): - return TeleBot.enable_save_next_step_handlers(self, delay, filename, del_file_after_loading) + def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): + return TeleBot.enable_save_next_step_handlers(self, delay, filename) @util.async_dec() - def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save", del_file_after_loading=True): - return TeleBot.enable_save_reply_handlers(self, delay, filename, del_file_after_loading) + def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): + return TeleBot.enable_save_reply_handlers(self, delay, filename) @util.async_dec() def disable_save_next_step_handlers(self): @@ -1512,12 +1512,12 @@ def disable_save_reply_handlers(self): return TeleBot.enable_save_reply_handlers(self) @util.async_dec() - def load_next_step_handlers(self, filename="./.handler-saves/step.save"): - return TeleBot.load_next_step_handlers(self, filename) + def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): + return TeleBot.load_next_step_handlers(self, filename, del_file_after_loading) @util.async_dec() - def load_reply_handlers(self, filename="./.handler-saves/reply.save"): - return TeleBot.load_reply_handlers(self, filename) + def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): + return TeleBot.load_reply_handlers(self, filename, del_file_after_loading) @util.async_dec() From feec1dde566ef0de96cf698b58c94f9899649fa5 Mon Sep 17 00:00:00 2001 From: uburuntu Date: Fri, 17 Aug 2018 12:49:37 +0300 Subject: [PATCH 0302/1808] fix: little style fixes --- examples/deep_linking.py | 1 - examples/step_example.py | 1 - telebot/__init__.py | 4 ++-- telebot/apihelper.py | 6 ++++-- telebot/util.py | 2 +- tests/test_telebot.py | 3 ++- tests/test_types.py | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/deep_linking.py b/examples/deep_linking.py index f92680f21..91fcb1516 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -31,7 +31,6 @@ # steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example. import telebot -import time bot = telebot.TeleBot('TOKEN') diff --git a/examples/step_example.py b/examples/step_example.py index 0fc0ef158..fea047e02 100644 --- a/examples/step_example.py +++ b/examples/step_example.py @@ -2,7 +2,6 @@ """ This Example will show you how to use register_next_step handler. """ -import time import telebot from telebot import types diff --git a/telebot/__init__.py b/telebot/__init__.py index 7865d94d4..5a4ec35ea 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1294,14 +1294,14 @@ def _notify_next_handlers(self, new_messages): was_poped = False if chat_id in self.next_step_handlers.keys(): handlers = self.next_step_handlers.pop(chat_id, None) - if (handlers): + if handlers: for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) new_messages.pop(i) # removing message that detects with next_step_handler was_poped = True if self.next_step_saver is not None: self.next_step_saver.start_save_timer() - if (not was_poped): + if not was_poped: i += 1 diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ae72e372d..e36bcd7df 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -99,7 +99,6 @@ def get_file(token, file_id): def get_file_url(token, file_id): - method_url = r'getFile' return FILE_URL.format(token, get_file(token, file_id).file_path) @@ -123,6 +122,8 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m :param disable_web_page_preview: :param reply_to_message_id: :param reply_markup: + :param parse_mode: + :param disable_notification: :return: """ method_url = r'sendMessage' @@ -769,7 +770,8 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: 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: + :param provider_data: + :return: """ method_url = r'sendInvoice' payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, diff --git a/telebot/util.py b/telebot/util.py index f448d7803..e7af2c783 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -243,7 +243,7 @@ def extract_arguments(text): :param text: String to extract the arguments from a command :return: the arguments if `text` is a command (according to is_command), else None. """ - regexp = re.compile("\/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) + regexp = re.compile("/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) result = regexp.match(text) return result.group(2) if is_command(text) else None diff --git a/tests/test_telebot.py b/tests/test_telebot.py index b81485e99..908b14121 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -402,7 +402,8 @@ def test_edit_markup(self): new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup) assert new_msg.message_id - def create_text_message(self, text): + @staticmethod + def create_text_message(text): params = {'text': text} chat = types.User(11, False, 'test') return types.Message(1, None, None, chat, 'text', params, "") diff --git a/tests/test_types.py b/tests/test_types.py index c174d420b..42e285bf4 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -59,7 +59,7 @@ def test_json_Message_Sticker_without_thumb(): json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 - assert msg.sticker.thumb == None + assert msg.sticker.thumb is None assert msg.content_type == 'sticker' From 99466017c5ff09aa72b295f338e11a3e118420f8 Mon Sep 17 00:00:00 2001 From: uburuntu Date: Fri, 17 Aug 2018 12:54:26 +0300 Subject: [PATCH 0303/1808] enh: optimize imports --- examples/detailed_example/detailed_example.py | 3 ++- examples/inline_example.py | 7 ++++--- examples/payments_example.py | 3 +-- examples/telebot_bot/telebot_bot.py | 3 ++- .../webhook_examples/webhook_aiohttp_echo_bot.py | 1 - .../webhook_examples/webhook_cherrypy_echo_bot.py | 5 +++-- .../webhook_examples/webhook_cpython_echo_bot.py | 4 ++-- examples/webhook_examples/webhook_flask_echo_bot.py | 5 +++-- .../webhook_examples/webhook_flask_heroku_echo.py | 3 ++- .../webhook_examples/webhook_tornado_echo_bot.py | 10 ++++++---- telebot/__init__.py | 13 ++++++------- telebot/util.py | 5 +++-- 12 files changed, 34 insertions(+), 28 deletions(-) diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index 4bd428b75..83fcb676d 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -2,9 +2,10 @@ This is a detailed example using almost every command of the API """ +import time + import telebot from telebot import types -import time TOKEN = '' diff --git a/examples/inline_example.py b/examples/inline_example.py index 725708e83..cc7ba77ee 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -1,8 +1,9 @@ # This example show how to write an inline mode telegramt bot use pyTelegramBotAPI. -import telebot -import time -import sys import logging +import sys +import time + +import telebot from telebot import types API_TOKEN = '' diff --git a/examples/payments_example.py b/examples/payments_example.py index 3e829553b..d0f52d4c2 100644 --- a/examples/payments_example.py +++ b/examples/payments_example.py @@ -1,6 +1,5 @@ import telebot -from telebot.types import LabeledPrice -from telebot.types import ShippingOption +from telebot.types import LabeledPrice, ShippingOption token = '1234567890:AAAABBBBCCCCDDDDeeeeFFFFgggGHHHH' provider_token = '1234567890:TEST:AAAABBBBCCCCDDDD' # @BotFather -> Bot Settings -> Payments diff --git a/examples/telebot_bot/telebot_bot.py b/examples/telebot_bot/telebot_bot.py index 46319ce6f..b599efe6e 100644 --- a/examples/telebot_bot/telebot_bot.py +++ b/examples/telebot_bot/telebot_bot.py @@ -3,9 +3,10 @@ # and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him! # WARNING: Tested with Python 2.7 -import telebot import os +import telebot + text_messages = { 'welcome': u'Please welcome {name}!\n\n' diff --git a/examples/webhook_examples/webhook_aiohttp_echo_bot.py b/examples/webhook_examples/webhook_aiohttp_echo_bot.py index d92cff936..c949b84e6 100644 --- a/examples/webhook_examples/webhook_aiohttp_echo_bot.py +++ b/examples/webhook_examples/webhook_aiohttp_echo_bot.py @@ -11,7 +11,6 @@ import telebot - API_TOKEN = '' WEBHOOK_HOST = '' diff --git a/examples/webhook_examples/webhook_cherrypy_echo_bot.py b/examples/webhook_examples/webhook_cherrypy_echo_bot.py index d0f3da05f..c679a01db 100644 --- a/examples/webhook_examples/webhook_cherrypy_echo_bot.py +++ b/examples/webhook_examples/webhook_cherrypy_echo_bot.py @@ -4,10 +4,11 @@ # This is a simple echo bot using decorators and webhook with CherryPy # It echoes any incoming text messages and does not use the polling method. -import cherrypy -import telebot import logging +import cherrypy + +import telebot API_TOKEN = '' diff --git a/examples/webhook_examples/webhook_cpython_echo_bot.py b/examples/webhook_examples/webhook_cpython_echo_bot.py index 807b5a7e9..639dcae0b 100644 --- a/examples/webhook_examples/webhook_cpython_echo_bot.py +++ b/examples/webhook_examples/webhook_cpython_echo_bot.py @@ -11,10 +11,10 @@ # Python 3 from http.server import BaseHTTPRequestHandler, HTTPServer -import ssl -import telebot import logging +import ssl +import telebot API_TOKEN = '' diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py index d0327d7cf..2567cabb6 100644 --- a/examples/webhook_examples/webhook_flask_echo_bot.py +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -4,11 +4,12 @@ # This is a simple echo bot using decorators and webhook with flask # It echoes any incoming text messages and does not use the polling method. -import flask -import telebot import logging import time +import flask + +import telebot API_TOKEN = '' diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index 62d0a90e7..7bbf2bf7c 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -1,8 +1,9 @@ import os -import telebot from flask import Flask, request +import telebot + TOKEN = '' bot = telebot.TeleBot(TOKEN) server = Flask(__name__) diff --git a/examples/webhook_examples/webhook_tornado_echo_bot.py b/examples/webhook_examples/webhook_tornado_echo_bot.py index 538b7b9f2..c4c64b03b 100644 --- a/examples/webhook_examples/webhook_tornado_echo_bot.py +++ b/examples/webhook_examples/webhook_tornado_echo_bot.py @@ -4,12 +4,14 @@ # This example shows webhook echo bot with Tornado web framework # Documenation to Tornado: http://tornadoweb.org -import telebot -import tornado.web -import tornado.ioloop +import signal + import tornado.httpserver +import tornado.ioloop import tornado.options -import signal +import tornado.web + +import telebot API_TOKEN = '' WEBHOOK_CERT = "./cert.pem" diff --git a/telebot/__init__.py b/telebot/__init__.py index 5a4ec35ea..95fa03ad0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1,16 +1,15 @@ # -*- coding: utf-8 -*- from __future__ import print_function -import threading -import time -import re -import sys -import six - +import logging import os import pickle +import re +import sys +import threading +import time -import logging +import six logger = logging.getLogger('TeleBot') formatter = logging.Formatter( diff --git a/telebot/util.py b/telebot/util.py index e7af2c783..034aa49a4 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- import random +import re import string +import sys import threading import traceback -import re -import sys + import six from six import string_types From 36621bb22a23d10cbe7d298a5bba65c415bebb4a Mon Sep 17 00:00:00 2001 From: uburuntu Date: Fri, 17 Aug 2018 13:01:03 +0300 Subject: [PATCH 0304/1808] fix: some intendation --- examples/deep_linking.py | 10 ++++++++-- examples/detailed_example/detailed_example.py | 9 +++++---- examples/echo_bot.py | 2 ++ examples/step_example.py | 1 - examples/telebot_bot/telebot_bot.py | 11 ++++++++--- .../webhook_aiohttp_echo_bot.py | 4 ++-- .../webhook_cherrypy_echo_bot.py | 9 ++++----- .../webhook_cpython_echo_bot.py | 5 ++--- .../webhook_flask_echo_bot.py | 3 +-- .../webhook_tornado_echo_bot.py | 19 ++++++++++++++----- telebot/__init__.py | 7 +++---- telebot/types.py | 14 ++++++++------ tests/test_types.py | 5 ++++- 13 files changed, 61 insertions(+), 38 deletions(-) diff --git a/examples/deep_linking.py b/examples/deep_linking.py index 91fcb1516..98df2049b 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -34,30 +34,35 @@ bot = telebot.TeleBot('TOKEN') + def extract_unique_code(text): # Extracts the unique_code from the sent /start command. return text.split()[1] if len(text.split()) > 1 else None + def in_storage(unique_code): # (pseudo-code) Should check if a unique code exists in storage return True + def get_username_from_storage(unique_code): # (pseudo-code) Does a query to the storage, retrieving the associated username # Should be replaced by a real database-lookup. return "ABC" if in_storage(unique_code) else None + def save_chat_id(chat_id, username): # (pseudo-code) Save the chat_id->username to storage # Should be replaced by a real database query. pass + @bot.message_handler(commands=['start']) def send_welcome(message): unique_code = extract_unique_code(message.text) - if unique_code: # if the '/start' command contains a unique_code + if unique_code: # if the '/start' command contains a unique_code username = get_username_from_storage(unique_code) - if username: # if the username exists in our database + if username: # if the username exists in our database save_chat_id(message.chat.id, username) reply = "Hello {0}, how are you?".format(username) else: @@ -66,4 +71,5 @@ def send_welcome(message): reply = "Please visit me via a provided URL from the website." bot.reply_to(message, reply) + bot.polling() diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index 83fcb676d..f481832a0 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -13,10 +13,10 @@ userStep = {} # so they won't reset every time the bot restarts commands = { # command description used in the "help" command - 'start': 'Get used to the bot', - 'help': 'Gives you information about the available commands', - 'sendLongText': 'A test using the \'send_chat_action\' command', - 'getImage': 'A test using multi-stage messages, custom keyboard, and media sending' + 'start' : 'Get used to the bot', + 'help' : 'Gives you information about the available commands', + 'sendLongText': 'A test using the \'send_chat_action\' command', + 'getImage' : 'A test using multi-stage messages, custom keyboard, and media sending' } imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard @@ -129,4 +129,5 @@ def command_default(m): # this is the standard reply to a normal message bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help") + bot.polling() diff --git a/examples/echo_bot.py b/examples/echo_bot.py index f88d3bf9f..b20f09d84 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -7,6 +7,7 @@ bot = telebot.TeleBot(API_TOKEN) + # Handle '/start' and '/help' @bot.message_handler(commands=['help', 'start']) def send_welcome(message): @@ -21,4 +22,5 @@ def send_welcome(message): def echo_message(message): bot.reply_to(message, message.text) + bot.polling() diff --git a/examples/step_example.py b/examples/step_example.py index fea047e02..0fc17e538 100644 --- a/examples/step_example.py +++ b/examples/step_example.py @@ -83,5 +83,4 @@ def process_sex_step(message): # WARNING It will work only if enable_save_next_step_handlers was called! bot.load_next_step_handlers() - bot.polling() diff --git a/examples/telebot_bot/telebot_bot.py b/examples/telebot_bot/telebot_bot.py index b599efe6e..ac6b63cbf 100644 --- a/examples/telebot_bot/telebot_bot.py +++ b/examples/telebot_bot/telebot_bot.py @@ -34,8 +34,10 @@ bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"]) GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"]) + def is_api_group(chat_id): - return chat_id== GROUP_CHAT_ID + return chat_id == GROUP_CHAT_ID + @bot.message_handler(func=lambda m: True, content_types=['new_chat_participant']) def on_user_joins(message): @@ -51,6 +53,7 @@ def on_user_joins(message): bot.reply_to(message, text_messages['welcome'].format(name=name)) + @bot.message_handler(commands=['info', 'help']) def on_info(message): if not is_api_group(message.chat.id): @@ -59,21 +62,23 @@ def on_info(message): bot.reply_to(message, text_messages['info']) + @bot.message_handler(commands=["ping"]) def on_ping(message): bot.reply_to(message, "Still alive and kicking!") + @bot.message_handler(commands=['start']) def on_start(message): if not is_api_group(message.chat.id): bot.reply_to(message, text_messages['wrong_chat']) return + def listener(messages): for m in messages: print(str(m)) + bot.set_update_listener(listener) bot.polling() - - diff --git a/examples/webhook_examples/webhook_aiohttp_echo_bot.py b/examples/webhook_examples/webhook_aiohttp_echo_bot.py index c949b84e6..bbb7c6e3a 100644 --- a/examples/webhook_examples/webhook_aiohttp_echo_bot.py +++ b/examples/webhook_examples/webhook_aiohttp_echo_bot.py @@ -31,7 +31,6 @@ WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT) WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN) - logger = telebot.logger telebot.logger.setLevel(logging.INFO) @@ -50,6 +49,7 @@ async def handle(request): else: return web.Response(status=403) + app.router.add_post('/{token}/', handle) @@ -71,7 +71,7 @@ def echo_message(message): bot.remove_webhook() # Set webhook -bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH, +bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, certificate=open(WEBHOOK_SSL_CERT, 'r')) # Build ssl context diff --git a/examples/webhook_examples/webhook_cherrypy_echo_bot.py b/examples/webhook_examples/webhook_cherrypy_echo_bot.py index c679a01db..7b46e78d3 100644 --- a/examples/webhook_examples/webhook_cherrypy_echo_bot.py +++ b/examples/webhook_examples/webhook_cherrypy_echo_bot.py @@ -30,7 +30,6 @@ WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT) WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) - logger = telebot.logger telebot.logger.setLevel(logging.INFO) @@ -71,7 +70,7 @@ def echo_message(message): bot.remove_webhook() # Set webhook -bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH, +bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, certificate=open(WEBHOOK_SSL_CERT, 'r')) # Disable CherryPy requests log @@ -81,9 +80,9 @@ def echo_message(message): # Start cherrypy server cherrypy.config.update({ - 'server.socket_host': WEBHOOK_LISTEN, - 'server.socket_port': WEBHOOK_PORT, - 'server.ssl_module': 'builtin', + 'server.socket_host' : WEBHOOK_LISTEN, + 'server.socket_port' : WEBHOOK_PORT, + 'server.ssl_module' : 'builtin', 'server.ssl_certificate': WEBHOOK_SSL_CERT, 'server.ssl_private_key': WEBHOOK_SSL_PRIV }) diff --git a/examples/webhook_examples/webhook_cpython_echo_bot.py b/examples/webhook_examples/webhook_cpython_echo_bot.py index 639dcae0b..029f36162 100644 --- a/examples/webhook_examples/webhook_cpython_echo_bot.py +++ b/examples/webhook_examples/webhook_cpython_echo_bot.py @@ -36,7 +36,6 @@ WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT) WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) - logger = telebot.logger telebot.logger.setLevel(logging.INFO) @@ -90,12 +89,12 @@ def echo_message(message): bot.remove_webhook() # Set webhook -bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH, +bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, certificate=open(WEBHOOK_SSL_CERT, 'r')) # Start server httpd = HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT), - WebhookHandler) + WebhookHandler) httpd.socket = ssl.wrap_socket(httpd.socket, certfile=WEBHOOK_SSL_CERT, diff --git a/examples/webhook_examples/webhook_flask_echo_bot.py b/examples/webhook_examples/webhook_flask_echo_bot.py index 2567cabb6..daa39951e 100644 --- a/examples/webhook_examples/webhook_flask_echo_bot.py +++ b/examples/webhook_examples/webhook_flask_echo_bot.py @@ -31,7 +31,6 @@ WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT) WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN) - logger = telebot.logger telebot.logger.setLevel(logging.INFO) @@ -78,7 +77,7 @@ def echo_message(message): time.sleep(0.1) # Set webhook -bot.set_webhook(url=WEBHOOK_URL_BASE+WEBHOOK_URL_PATH, +bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, certificate=open(WEBHOOK_SSL_CERT, 'r')) # Start flask server diff --git a/examples/webhook_examples/webhook_tornado_echo_bot.py b/examples/webhook_examples/webhook_tornado_echo_bot.py index c4c64b03b..171d2d74c 100644 --- a/examples/webhook_examples/webhook_tornado_echo_bot.py +++ b/examples/webhook_examples/webhook_tornado_echo_bot.py @@ -31,15 +31,18 @@ bot = telebot.TeleBot(API_TOKEN) + class Root(tornado.web.RequestHandler): def get(self): self.write("Hi! This is webhook example!") self.finish() + class webhook_serv(tornado.web.RequestHandler): def get(self): self.write("What are you doing here?") self.finish() + def post(self): if "Content-Length" in self.request.headers and \ "Content-Type" in self.request.headers and \ @@ -54,21 +57,26 @@ def post(self): else: self.write("What are you doing here?") self.finish() - + + tornado.options.define("port", default=WEBHOOK_PORT, help="run on the given port", type=int) is_closing = False + + def signal_handler(signum, frame): global is_closing print("Exiting...") is_closing = True + def try_exit(): global is_closing if is_closing: # clean up here tornado.ioloop.IOLoop.instance().stop() print("Exit success!") - + + # Handle '/start' and '/help' @bot.message_handler(commands=['help', 'start']) def send_welcome(message): @@ -76,6 +84,7 @@ def send_welcome(message): ("Hi there, I am EchoBot.\n" "I am here to echo your kind words back to you.")) + bot.remove_webhook() bot.set_webhook(url=WEBHOOK_URL_BASE, certificate=open(WEBHOOK_CERT, 'r')) @@ -88,9 +97,9 @@ def send_welcome(message): ]) http_server = tornado.httpserver.HTTPServer(application, ssl_options={ - "certfile": WEBHOOK_CERT, - "keyfile": WEBHOOK_PKEY, - }) + "certfile": WEBHOOK_CERT, + "keyfile" : WEBHOOK_PKEY, +}) http_server.listen(tornado.options.options.port) tornado.ioloop.PeriodicCallback(try_exit, 100).start() tornado.ioloop.IOLoop.instance().start() diff --git a/telebot/__init__.py b/telebot/__init__.py index 95fa03ad0..52a4e5abb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -33,6 +33,7 @@ class Handler: """ Class for (next step|reply) handlers """ + def __init__(self, callback, *args, **kwargs): self.callback = callback self.args = args @@ -46,6 +47,7 @@ class Saver: """ Class for saving (next step|reply) handlers """ + def __init__(self, handlers, filename, delay): self.handlers = handlers self.filename = filename @@ -1303,12 +1305,11 @@ def _notify_next_handlers(self, new_messages): if not was_poped: i += 1 - @staticmethod def _build_handler_dict(handler, **filters): return { 'function': handler, - 'filters': filters + 'filters' : filters } def message_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): @@ -1518,8 +1519,6 @@ def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_fil def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): return TeleBot.load_reply_handlers(self, filename, del_file_after_loading) - @util.async_dec() - @util.async_dec() def get_me(self): return TeleBot.get_me(self) diff --git a/telebot/types.py b/telebot/types.py index 884e3d4ae..06a1c4483 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -457,11 +457,11 @@ def __html_text(self, text, entities): if not entities: return text _subs = { - "bold": "{text}", - "italic": "{text}", - "pre": "

{text}
", - "code": "{text}", - "url": "{text}", + "bold" : "{text}", + "italic" : "{text}", + "pre" : "
{text}
", + "code" : "{text}", + "url" : "{text}", "text_link": "{text}" } if hasattr(self, "custom_subs"): @@ -469,6 +469,7 @@ def __html_text(self, text, entities): _subs[type] = self.custom_subs[type] utf16_text = text.encode("utf-16-le") html_text = "" + def func(text, type=None, url=None, user=None): text = text.decode("utf-16-le") if type == "text_mention": @@ -501,6 +502,7 @@ def html_text(self): def html_caption(self): return self.__html_text(self.caption, self.caption_entities) + class MessageEntity(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -1069,7 +1071,7 @@ def __init__(self, latitude, longitude, title, address, foursquare_id=None): def to_dic(self): json_dic = {'latitude': self.latitude, 'longitude': self.longitude, 'title': self.title, - 'address': self.address} + 'address' : self.address} if self.foursquare_id: json_dic['foursquare_id'] = self.foursquare_id return json_dic diff --git a/tests/test_types.py b/tests/test_types.py index 42e285bf4..c229bdf3a 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -113,6 +113,7 @@ def test_json_voice(): assert voice.duration == 0 assert voice.file_size == 10481 + def test_json_update(): json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"is_bot":true,"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}' update = types.Update.de_json(json_string) @@ -120,6 +121,7 @@ def test_json_update(): assert update.message.message_id == 241 assert update.message.from_user.id == 9734 + def test_json_chat(): json_string = r'{"id": -111111,"title": "Test Title","type": "group"}' chat = types.Chat.de_json(json_string) @@ -127,6 +129,7 @@ def test_json_chat(): assert chat.type == 'group' assert chat.title == 'Test Title' + def test_InlineQueryResultCachedPhoto(): iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid') json_str = iq.to_json() @@ -143,6 +146,7 @@ def test_InlineQueryResultCachedPhoto_with_title(): assert 'Title' in json_str assert 'caption' not in json_str + def test_InlineQueryResultCachedPhoto_with_markup(): markup = types.InlineKeyboardMarkup() markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com")) @@ -154,4 +158,3 @@ def test_InlineQueryResultCachedPhoto_with_markup(): assert 'Title' in json_str assert 'caption' not in json_str assert 'reply_markup' in json_str - From d9ace2adc8f1646dab5e97ccb29bfacea7ebccfa Mon Sep 17 00:00:00 2001 From: SetazeR Date: Wed, 5 Sep 2018 12:32:19 +0700 Subject: [PATCH 0305/1808] fix typo + add inline keyboard example --- examples/inline_example.py | 2 +- examples/inline_keyboard_example.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 examples/inline_keyboard_example.py diff --git a/examples/inline_example.py b/examples/inline_example.py index c97dea2e2..5164eecdc 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -1,4 +1,4 @@ -# This example show how to write an inline mode telegramt bot use pyTelegramBotAPI. +# This example show how to write an inline mode telegram bot use pyTelegramBotAPI. import telebot import time import sys diff --git a/examples/inline_keyboard_example.py b/examples/inline_keyboard_example.py new file mode 100644 index 000000000..0618eee2c --- /dev/null +++ b/examples/inline_keyboard_example.py @@ -0,0 +1,27 @@ +# This example show how to use inline keyboards and process button presses +import telebot +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton + +TELEGRAM_TOKEN = '' + +bot = telebot.TeleBot(TELEGRAM_TOKEN) + +def gen_markup(): + markup = InlineKeyboardMarkup() + markup.row_width = 2 + markup.add(InlineKeyboardButton("Yes", callback_data=f"cb_yes"), + InlineKeyboardButton("No", callback_data=f"cb_no")) + return markup + +@bot.callback_query_handler(func=lambda call: True) +def callback_query(call): + if call.data == "cb_yes": + bot.answer_callback_query(call.id, "Answer is Yes") + elif call.data == "cb_no": + bot.answer_callback_query(call.id, "Answer is No") + +@bot.message_handler(func=lambda message: True) +def message_handler(message): + bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup()) + +bot.polling(none_stop=True) From bab9f7bbb9664b99edfc36b1926e5403fbb1a6f9 Mon Sep 17 00:00:00 2001 From: uburuntu Date: Thu, 6 Sep 2018 12:42:44 +0300 Subject: [PATCH 0306/1808] enh: reset requests.Session feature Need for proxy changing and other reconnection stuff --- telebot/apihelper.py | 4 ++-- telebot/util.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ae72e372d..a5e1df3b7 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -27,8 +27,8 @@ READ_TIMEOUT = 9999 -def _get_req_session(): - return util.per_thread('req_session', lambda: requests.session()) +def _get_req_session(reset=False): + return util.per_thread('req_session', lambda: requests.session(), reset) def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL): diff --git a/telebot/util.py b/telebot/util.py index f448d7803..e16ebb5fa 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -248,13 +248,12 @@ def extract_arguments(text): return result.group(2) if is_command(text) else None -def per_thread(key, construct_value): - try: - return getattr(thread_local, key) - except AttributeError: +def per_thread(key, construct_value, reset=False): + if reset or not hasattr(thread_local, key): value = construct_value() setattr(thread_local, key, value) - return value + + return getattr(thread_local, key) def generate_random_token(): From 2c57c5c01c6f5700b9072bb5f7aa9a177fae871d Mon Sep 17 00:00:00 2001 From: eternnoir Date: Fri, 7 Sep 2018 16:02:03 +0800 Subject: [PATCH 0307/1808] Update version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9a401bcb8..a52df5372 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(filename): return file.read() setup(name='pyTelegramBotAPI', - version='3.6.5', + version='3.6.6', description='Python Telegram bot api. ', long_description=read('README.md'), long_description_content_type="text/markdown", From 891988be932e43eebf2e856de35cfbd083d71198 Mon Sep 17 00:00:00 2001 From: Rafael Medina Date: Sat, 15 Sep 2018 20:25:06 +0200 Subject: [PATCH 0308/1808] Added check for parse_mode in BaseInlineQueryResultCached. Should fix #571 --- telebot/types.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 7340c105e..a7a74ebfc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1605,6 +1605,7 @@ def __init__(self): self.caption = None self.reply_markup = None self.input_message_content = None + self.parse_mode = None self.payload_dic = {} def to_json(self): @@ -1621,6 +1622,8 @@ def to_json(self): json_dict['reply_markup'] = self.reply_markup.to_dic() if self.input_message_content: json_dict['input_message_content'] = self.input_message_content.to_dic() + if self.parse_mode: + json_dict['parse_mode'] = self.parse_mode return json.dumps(json_dict) @@ -1636,8 +1639,8 @@ def __init__(self, id, photo_file_id, title=None, description=None, caption=None self.caption = caption 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 - self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedGif(BaseInlineQueryResultCached): @@ -1652,8 +1655,8 @@ def __init__(self, id, gif_file_id, title=None, description=None, caption=None, self.caption = caption 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 - self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedMpeg4Gif(BaseInlineQueryResultCached): @@ -1668,8 +1671,8 @@ def __init__(self, id, mpeg4_file_id, title=None, description=None, caption=None self.caption = caption 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 - self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedSticker(BaseInlineQueryResultCached): @@ -1695,8 +1698,8 @@ def __init__(self, id, document_file_id, title, description=None, caption=None, self.caption = caption 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 - self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedVideo(BaseInlineQueryResultCached): @@ -1711,8 +1714,8 @@ def __init__(self, id, video_file_id, title, description=None, caption=None, par self.caption = caption 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 - self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedVoice(BaseInlineQueryResultCached): @@ -1726,8 +1729,8 @@ def __init__(self, id, voice_file_id, title, caption=None, parse_mode=None, repl self.caption = caption 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 - self.payload_dic['parse_mode'] = parse_mode class InlineQueryResultCachedAudio(BaseInlineQueryResultCached): @@ -1739,8 +1742,8 @@ def __init__(self, id, audio_file_id, caption=None, parse_mode=None, reply_marku self.caption = caption self.reply_markup = reply_markup self.input_message_content = input_message_content + self.parse_mode = parse_mode self.payload_dic['audio_file_id'] = audio_file_id - self.payload_dic['parse_mode'] = parse_mode # Games From b8f442d06bfadf35e3a74ba5b3247f9f58cd2fc3 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Tue, 9 Oct 2018 17:32:27 +0800 Subject: [PATCH 0309/1808] Update Bots using this API --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9222b7b89..fbe40e8f9 100644 --- a/README.md +++ b/README.md @@ -586,6 +586,7 @@ Get help. Discuss. Chat. * [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. * [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. * [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) +* [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 584955962e3e304a44a62d45e878e892569a8779 Mon Sep 17 00:00:00 2001 From: khode-mohsen <44228057+khode-mohsen@users.noreply.github.com> Date: Fri, 19 Oct 2018 03:38:03 +0330 Subject: [PATCH 0310/1808] Update echo_bot.py add '#!/usr/bin/env' for direct execute --- examples/echo_bot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/echo_bot.py b/examples/echo_bot.py index f88d3bf9f..a3e8cbb9d 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + # This is a simple echo bot using the decorator mechanism. # It echoes any incoming text messages. From 76fc8fbe5ee76e6b79c1ea72c41010826b8704c1 Mon Sep 17 00:00:00 2001 From: khode-mohsen <44228057+khode-mohsen@users.noreply.github.com> Date: Fri, 19 Oct 2018 03:47:19 +0330 Subject: [PATCH 0311/1808] Update deep_linking.py add '#!/usr/bin/python' for direct execute --- examples/deep_linking.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/deep_linking.py b/examples/deep_linking.py index f92680f21..3d7ec77c3 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + # This example shows how to implement deep linking (https://core.telegram.org/bots#deep-linking) # with the pyTelegramBotAPI. # Note: This is not a working, production-ready sample. From 4131b05733e1a58e9e9ff2da21e135b32e6220cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natanael=20Andr=C3=A9s=20Garrido?= Date: Mon, 5 Nov 2018 15:11:31 -0300 Subject: [PATCH 0312/1808] Update README.md Adding another bot that user pyTelegramBotAPI. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fbe40e8f9..ee9c163e1 100644 --- a/README.md +++ b/README.md @@ -561,7 +561,7 @@ Get help. Discuss. Chat. * [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`. * [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot. * [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* -* [NeoBot](https://github.com/neoranger/NeoBot) by *neoranger* +* [NeoBot](https://github.com/neoranger/NeoBot) by [@NeoRanger](https://github.com/neoranger) * [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi* * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. * [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin. @@ -587,6 +587,7 @@ Get help. Discuss. Chat. * [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. * [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) * [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). +* [ManjaroBot](https://t.me/ManjaroBot) by [@NeoRanger](https://github.com/neoranger) - Bot for Manjaro Linux Spanish group with a lot of info for Manjaro Newbies. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From ceea457cf1202b74aa4411ecdb320a7e74c105b0 Mon Sep 17 00:00:00 2001 From: KanerL <40599431+KanerL@users.noreply.github.com> Date: Mon, 12 Nov 2018 01:43:00 +0200 Subject: [PATCH 0313/1808] Update shipping option Setting list of ShippingOptions like in payments_example.py shipping_options = [ ShippingOption(id='instant', title='WorldWide Teleporter').add_price(LabeledPrice('Teleporter', 1000)), ShippingOption(id='pickup', title='Local pickup').add_price(LabeledPrice('Pickup', 300))] gives us [None,None],so It's better add_price to return self --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index 6165c9479..149765787 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1902,6 +1902,7 @@ def add_price(self, *args): """ for price in args: self.prices.append(price) + return self def to_json(self): price_list = [] From 18e37f3d20e0537aba14439a507af92ed3ee649a Mon Sep 17 00:00:00 2001 From: nailer Date: Sat, 17 Nov 2018 12:58:56 +0200 Subject: [PATCH 0314/1808] sleep time timeout time instead of 5 seconds always --- telebot/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 52a4e5abb..3a937509b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -364,12 +364,12 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, *args, **kwargs): + def infinity_polling(self, timeout: int = 20, *args, **kwargs): while not self.__stop_polling.is_set(): try: - self.polling(*args, **kwargs) + self.polling(timeout=timeout, *args, **kwargs) except Exception as e: - time.sleep(5) + time.sleep(timeout) pass logger.info("Break infinity polling") From b82ed70ec91baaadddf935f1baf07dc8ec345801 Mon Sep 17 00:00:00 2001 From: nailer Date: Sat, 17 Nov 2018 13:19:09 +0200 Subject: [PATCH 0315/1808] fix syntax errors --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3a937509b..2cfebbd16 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -364,7 +364,7 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout: int = 20, *args, **kwargs): + def infinity_polling(self, timeout=20, *args, **kwargs): while not self.__stop_polling.is_set(): try: self.polling(timeout=timeout, *args, **kwargs) From 8005ca2f6c43fc12d2a700b52eac2a6157589fe2 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sat, 29 Dec 2018 23:49:10 +0800 Subject: [PATCH 0316/1808] Update example for api server check webhook alive. --- examples/webhook_examples/webhook_cpython_echo_bot.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/webhook_examples/webhook_cpython_echo_bot.py b/examples/webhook_examples/webhook_cpython_echo_bot.py index 029f36162..528d63686 100644 --- a/examples/webhook_examples/webhook_cpython_echo_bot.py +++ b/examples/webhook_examples/webhook_cpython_echo_bot.py @@ -86,11 +86,13 @@ def echo_message(message): # Remove webhook, it fails sometimes the set if there is a previous webhook -bot.remove_webhook() +#bot.remove_webhook() # Set webhook -bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, - certificate=open(WEBHOOK_SSL_CERT, 'r')) +# Beacuse telegram bot api server will check webhook server is alive. +# Here we need set webhook after server started manually. +#bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, +# certificate=open(WEBHOOK_SSL_CERT, 'r')) # Start server httpd = HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT), From 79e6a3166da036435f31e197f8ea5a72edc838e6 Mon Sep 17 00:00:00 2001 From: Moon Princess Date: Sun, 20 Jan 2019 23:04:11 +0500 Subject: [PATCH 0317/1808] edit message_handler doc --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 52a4e5abb..8c5b9cda7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1333,7 +1333,7 @@ def command_handle_document(message): bot.send_message(message.chat.id, 'Document received, sir!') # Handle all other commands. - @bot.message_handler(func=lambda message: True, content_types=['audio', 'video', 'document', 'text', 'location', 'contact', 'sticker']) + @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', 'text', 'location', 'contact', 'sticker']) def default_command(message): bot.send_message(message.chat.id, "This is the default command handler.") From 99df992a66230f28707012d58cc7725e0dcaa346 Mon Sep 17 00:00:00 2001 From: Artem M Date: Fri, 15 Feb 2019 18:46:18 +0000 Subject: [PATCH 0318/1808] Added the method sendAnimation, which can be used instead of sendDocument to send animations, specifying their duration. --- telebot/__init__.py | 18 ++++++++++++++++++ telebot/apihelper.py | 26 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 52a4e5abb..cd7d71ed6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -107,6 +107,7 @@ class TeleBot: sendDocument sendSticker sendVideo + sendAnimation sendVideoNote sendLocation sendChatAction @@ -719,6 +720,23 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout)) + def send_animation(self, chat_id, animation, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None): + """ + Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param data: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param caption: String : Animation caption (may also be used when resending animation by file_id). + :param parse_mode: + :param reply_to_message_id: + :param reply_markup: + :return: + """ + return types.Message.de_json( + apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, + parse_mode, disable_notification, timeout)) + def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 839754f99..bf535c946 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -384,6 +384,32 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa return _make_request(token, method_url, params=payload, files=files, method='post') +def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None): + method_url = r'sendAnimation' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(data): + files = {'animation': data} + else: + payload['animation'] = data + if duration: + payload['duration'] = duration + if caption: + payload['caption'] = caption + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode + if disable_notification: + payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout + return _make_request(token, method_url, params=payload, files=files, method='post') + + def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): method_url = r'sendVoice' From 1a58731fb74890e3821fd27a86eeb5a693eae861 Mon Sep 17 00:00:00 2001 From: Victor Koropetskyi Date: Sat, 23 Feb 2019 16:15:20 +0200 Subject: [PATCH 0319/1808] Add 'method' parameter to methods that edit message --- telebot/apihelper.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 839754f99..749fc98c0 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -619,7 +619,7 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message payload['disable_web_page_preview'] = disable_web_page_preview if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) - return _make_request(token, method_url, params=payload) + return _make_request(token, method_url, params=payload, method='post') def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, @@ -636,7 +636,7 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m payload['parse_mode'] = parse_mode if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) - return _make_request(token, method_url, params=payload) + return _make_request(token, method_url, params=payload, method='post') def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): @@ -665,13 +665,13 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) - return _make_request(token, method_url, params=payload) + return _make_request(token, method_url, params=payload, method='post') def delete_message(token, chat_id, message_id): method_url = r'deleteMessage' payload = {'chat_id': chat_id, 'message_id': message_id} - return _make_request(token, method_url, params=payload) + return _make_request(token, method_url, params=payload, method='post') # Game @@ -814,7 +814,7 @@ def answer_shipping_query(token, shipping_query_id, ok, shipping_options=None, e :param ok: Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible) :param shipping_options: Required if ok is True. A JSON-serialized array of available shipping options. :param error_message: Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. - :return: + :return: """ method_url = 'answerShippingQuery' payload = {'shipping_query_id': shipping_query_id, 'ok': ok} @@ -832,7 +832,7 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No :param pre_checkout_query_id: Unique identifier for the query to be answered :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. :param error_message: Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user. - :return: + :return: """ method_url = 'answerPreCheckoutQuery' payload = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok} From c77307881d29f73048686eb4d30d5bf0931c50aa Mon Sep 17 00:00:00 2001 From: iv8 <44664770+iv8@users.noreply.github.com> Date: Mon, 4 Mar 2019 15:24:55 +0800 Subject: [PATCH 0320/1808] fix errors --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fbe40e8f9..c16cb36f6 100644 --- a/README.md +++ b/README.md @@ -524,16 +524,16 @@ Telegram Bot API support new type Chat for message.chat. - Check the ```type``` attribute in ```Chat``` object: - ```python -if message.chat.type == “private”: +if message.chat.type == "private": # private chat message -if message.chat.type == “group”: +if message.chat.type == "group": # group chat message -if message.chat.type == “supergroup”: +if message.chat.type == "supergroup": # supergroup chat message -if message.chat.type == “channel”: +if message.chat.type == "channel": # channel message ``` From 2285d0466e6dba3924a0bbfdb7a64d10ee221142 Mon Sep 17 00:00:00 2001 From: David Lorenzo <17401854+Pythoneiro@users.noreply.github.com> Date: Tue, 9 Apr 2019 11:32:15 +0200 Subject: [PATCH 0321/1808] update bot list Remove areajugonesbot; add vigobustelegrambot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c16cb36f6..0a8e71581 100644 --- a/README.md +++ b/README.md @@ -576,7 +576,6 @@ Get help. Discuss. Chat. * [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful. * [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic * [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video. -* [areajugonesbot](http://t.me/areajugonesbot)([link](http://t.me/areajugonesbot)) - The areajugonesbot sends news published on the videogames blog Areajugones to Telegram. * [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service. * [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service. * [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram)) @@ -587,6 +586,7 @@ Get help. Discuss. Chat. * [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. * [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) * [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). +* [VigoBusTelegramBot](https://t.me/vigobusbot) ([GitHub](https://github.com/Pythoneiro/VigoBus-TelegramBot)) - Bot that provides buses coming to a certain stop and their remaining time for the city of Vigo (Galicia - Spain) Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 7dc9abffc6477e8ea78157580c6c007fc63ba0b1 Mon Sep 17 00:00:00 2001 From: SetazeR Date: Tue, 16 Apr 2019 07:38:50 +0700 Subject: [PATCH 0322/1808] remove unnecessary f-strings --- examples/inline_keyboard_example.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/inline_keyboard_example.py b/examples/inline_keyboard_example.py index 0618eee2c..f2b3fcef7 100644 --- a/examples/inline_keyboard_example.py +++ b/examples/inline_keyboard_example.py @@ -9,8 +9,8 @@ def gen_markup(): markup = InlineKeyboardMarkup() markup.row_width = 2 - markup.add(InlineKeyboardButton("Yes", callback_data=f"cb_yes"), - InlineKeyboardButton("No", callback_data=f"cb_no")) + markup.add(InlineKeyboardButton("Yes", callback_data="cb_yes"), + InlineKeyboardButton("No", callback_data="cb_no")) return markup @bot.callback_query_handler(func=lambda call: True) From f51643836091284553b10b14cca96e9f69c46fdd Mon Sep 17 00:00:00 2001 From: Airat K Date: Sun, 21 Apr 2019 18:34:10 +0300 Subject: [PATCH 0323/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c16cb36f6..afb746579 100644 --- a/README.md +++ b/README.md @@ -587,6 +587,7 @@ Get help. Discuss. Chat. * [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. * [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) * [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). +* [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 2d0ebde481d7d531bb9dd2bc0c84fe63ca688292 Mon Sep 17 00:00:00 2001 From: Komron Date: Wed, 24 Apr 2019 11:29:52 +0200 Subject: [PATCH 0324/1808] added creationdatebot to bots api list --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index afb746579..62574f0bb 100644 --- a/README.md +++ b/README.md @@ -581,13 +581,14 @@ Get help. Discuss. Chat. * [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service. * [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram)) * [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students. -* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. +* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. * [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. * [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. * [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. * [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) * [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). * [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students. +* [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given ID’s Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From ceceeb7d8c4d55fed05eda7f0447d9dc33e12d02 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 24 May 2019 15:05:20 +0800 Subject: [PATCH 0325/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 62574f0bb..ef5a177fa 100644 --- a/README.md +++ b/README.md @@ -589,6 +589,8 @@ Get help. Discuss. Chat. * [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). * [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students. * [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given ID’s +* [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe. +* [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian). Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 55c7b6373c62154142176444bf3f73cf5c4e579a Mon Sep 17 00:00:00 2001 From: FacuM Date: Thu, 30 May 2019 00:35:23 -0300 Subject: [PATCH 0326/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef5a177fa..06d992c14 100644 --- a/README.md +++ b/README.md @@ -591,6 +591,6 @@ Get help. Discuss. Chat. * [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given ID’s * [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe. * [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian). - +* [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 9624b45314b46eff513a90240449a9fae50e153a Mon Sep 17 00:00:00 2001 From: Tiger 1 Date: Thu, 6 Jun 2019 21:47:08 +0300 Subject: [PATCH 0327/1808] add Poll, sendPoll, stopPoll --- telebot/__init__.py | 7 +++++++ telebot/apihelper.py | 20 ++++++++++++++++++ telebot/types.py | 48 ++++++++++++++++++++++++++++++++++++++++++++ tests/my_tests.py | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 tests/my_tests.py diff --git a/telebot/__init__.py b/telebot/__init__.py index 52a4e5abb..98076cd12 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -113,6 +113,7 @@ class TeleBot: getUserProfilePhotos getUpdates getFile + sendPoll kickChatMember unbanChatMember restrictChatMember @@ -1044,6 +1045,12 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to disable_notification, reply_to_message_id, reply_markup, provider_data) return types.Message.de_json(result) + def send_poll(self, chat_id, poll, disable_notifications=False, reply_to_message=None, reply_markup=None): + return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll.question, poll.options, disable_notifications, reply_to_message, reply_markup)) + + def stop_poll(self, chat_id, message_id): + return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id)) + def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 749fc98c0..094fbbf8e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -938,6 +938,26 @@ def delete_sticker_from_set(token, sticker): return _make_request(token, method_url, params=payload, method='post') +def send_poll(token, chat_id, question, options, disable_notifications=False, reply_to_message_id=None, reply_markup=None): + method_url = r'sendPoll' + payload = {'chat_id': str(chat_id), 'question': question, 'options': _convert_list_json_serializable(options)} + if disable_notifications: + payload['disable_notification'] = disable_notifications + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + + +def stop_poll(token, chat_id, message_id, reply_markup=None): + method_url = r'stopPoll' + payload = {'chat_id': str(chat_id), 'message_id': message_id} + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + + def _convert_list_json_serializable(results): ret = '' for r in results: diff --git a/telebot/types.py b/telebot/types.py index 6165c9479..0965ebd6c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -368,6 +368,9 @@ def de_json(cls, json_string): if 'connected_website' in obj: opts['connected_website'] = obj['connected_website'] content_type = 'connected_website' + if 'poll' in obj: + opts['poll'] = Poll.de_json(obj['poll']) + content_type = 'poll' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -2173,3 +2176,48 @@ def to_dic(self): if self.thumb: ret['thumb'] = self.thumb return ret + + +class PollOption(JsonSerializable, JsonDeserializable): + @classmethod + def de_json(cls, json_type): + obj = cls.check_json(json_type) + text = obj['text'] + voter_count = int(obj['voter_count']) + option = cls(text) + option.voter_count = voter_count + return option + + def __init__(self, text): + self.text = text + self.voter_count = 0 + + def to_json(self): + return json.dumps(self.text) + + +class Poll(JsonDeserializable): + @classmethod + def de_json(cls, json_type): + obj = cls.check_json(json_type) + poll_id = obj['id'] + question = obj['question'] + poll = cls(question) + options = [] + for opt in obj['options']: + options.append(PollOption.de_json(opt)) + poll.options = options + is_closed = obj['is_closed'] + poll.id = poll_id + poll.is_closed = is_closed + return poll + + def __init__(self, question): + self.options = [] + self.question = question + + def add(self, option): + if type(option) is PollOption: + self.options.append(option) + else: + self.options.append(PollOption(option)) diff --git a/tests/my_tests.py b/tests/my_tests.py new file mode 100644 index 000000000..05d40786d --- /dev/null +++ b/tests/my_tests.py @@ -0,0 +1,41 @@ +import os +from telebot import logger, logging, types, TeleBot +import telebot.apihelper as api + +try: + TOKEN = os.environ['TOKEN'] +except: + logger.error('Not variable \'TOKEN\' in environ') + exit(1) + +CHAT_ID = -1001405019571 +logger.setLevel(logging.DEBUG) + +bot = TeleBot(TOKEN) + + +@bot.message_handler(content_types=['poll']) +def po(m): + logger.debug('Give poll') + bot.send_message(m.chat.id, 'Я тоже так умею!') + m = bot.send_poll(m.chat.id, m.poll) + print(m.chat.id, m.message_id) + + +def test_send_poll(): + poll = types.Poll('Какой ты сегодня?') + poll.add('Добрый') + poll.add('Веселый') + poll.add('Грустный') + kb = types.InlineKeyboardMarkup() + kb.add(types.InlineKeyboardButton('1', url='t.me/dr_forse')) + result = bot.send_poll(CHAT_ID, poll, reply_to_message=60312, reply_markup=kb) + assert result['poll']['question'] == 'Какой ты сегодня?' + + +def test_stop_poll(): + res = bot.stop_poll(-1001405019571, 60370) + + +test_stop_poll() +bot.polling(none_stop=True, timeout=600) From b10e45f7140921805dc7e2bf0522ce8263634705 Mon Sep 17 00:00:00 2001 From: Tiger 1 Date: Thu, 6 Jun 2019 21:49:06 +0300 Subject: [PATCH 0328/1808] add Poll, sendPoll, stopPoll --- tests/my_tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/my_tests.py b/tests/my_tests.py index 05d40786d..80f4abd03 100644 --- a/tests/my_tests.py +++ b/tests/my_tests.py @@ -1,6 +1,5 @@ import os from telebot import logger, logging, types, TeleBot -import telebot.apihelper as api try: TOKEN = os.environ['TOKEN'] From a8cf9f4ae59ee02b6f6737b4b2d8aa82df56ab10 Mon Sep 17 00:00:00 2001 From: OslikAi <48922415+OslikAi@users.noreply.github.com> Date: Thu, 6 Jun 2019 21:54:06 +0300 Subject: [PATCH 0329/1808] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 06d992c14..5dcd01b58 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ * [Using web hooks](#using-web-hooks) * [Logging](#logging) * [Proxy](#proxy) + * [New in library](#new-in-library) * [F.A.Q.](#faq) * [Bot 2.0](#bot-20) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) @@ -510,6 +511,10 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} ``` +## New in library + +06.06.2019 - Добавленна поддержка опросов (Poll). Добавлены функции send_poll, stop_poll + ## F.A.Q. ### Bot 2.0 From 63df69aeb89a4aaa9ed25c42b47be6da73179c4f Mon Sep 17 00:00:00 2001 From: OslikAi <48922415+OslikAi@users.noreply.github.com> Date: Thu, 6 Jun 2019 22:23:11 +0300 Subject: [PATCH 0330/1808] Delete my_tests.py --- tests/my_tests.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 tests/my_tests.py diff --git a/tests/my_tests.py b/tests/my_tests.py deleted file mode 100644 index 80f4abd03..000000000 --- a/tests/my_tests.py +++ /dev/null @@ -1,40 +0,0 @@ -import os -from telebot import logger, logging, types, TeleBot - -try: - TOKEN = os.environ['TOKEN'] -except: - logger.error('Not variable \'TOKEN\' in environ') - exit(1) - -CHAT_ID = -1001405019571 -logger.setLevel(logging.DEBUG) - -bot = TeleBot(TOKEN) - - -@bot.message_handler(content_types=['poll']) -def po(m): - logger.debug('Give poll') - bot.send_message(m.chat.id, 'Я тоже так умею!') - m = bot.send_poll(m.chat.id, m.poll) - print(m.chat.id, m.message_id) - - -def test_send_poll(): - poll = types.Poll('Какой ты сегодня?') - poll.add('Добрый') - poll.add('Веселый') - poll.add('Грустный') - kb = types.InlineKeyboardMarkup() - kb.add(types.InlineKeyboardButton('1', url='t.me/dr_forse')) - result = bot.send_poll(CHAT_ID, poll, reply_to_message=60312, reply_markup=kb) - assert result['poll']['question'] == 'Какой ты сегодня?' - - -def test_stop_poll(): - res = bot.stop_poll(-1001405019571, 60370) - - -test_stop_poll() -bot.polling(none_stop=True, timeout=600) From 3c62e9d391e0b2947ba608526896ebc4b558c7d8 Mon Sep 17 00:00:00 2001 From: Vova Pytsyuk Date: Sat, 15 Jun 2019 22:59:41 +0300 Subject: [PATCH 0331/1808] Added LoginUrl to types --- telebot/types.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 6165c9479..bdc5bbc9c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -885,9 +885,30 @@ def to_dic(self): return json_dict +class LoginUrl(JsonSerializable): + def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None): + self.url = url + self.forward_text = forward_text + self.bot_username = bot_username + self.request_write_access = request_write_access + + def to_json(self): + return json.dumps(self.to_dic()) + + def to_dic(self): + json_dic = {'url': self.url} + if self.forward_text: + json_dic['forward_text'] = self.forward_text + if self.bot_username: + json_dic['bot_username'] = self.bot_username + if self.request_write_access: + json_dic['request_write_access'] = self.request_write_access + return json_dic + + class InlineKeyboardButton(JsonSerializable): def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, - switch_inline_query_current_chat=None, callback_game=None, pay=None): + switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None): self.text = text self.url = url self.callback_data = callback_data @@ -895,6 +916,7 @@ def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, self.switch_inline_query_current_chat = switch_inline_query_current_chat self.callback_game = callback_game self.pay = pay + self.login_url = login_url.to_dic() def to_json(self): return json.dumps(self.to_dic()) @@ -913,6 +935,8 @@ def to_dic(self): json_dic['callback_game'] = self.callback_game if self.pay is not None: json_dic['pay'] = self.pay + if self.login_url is not None: + json_dic['login_url'] = self.login_url return json_dic From 600002e1581a6fe457d96d2f6f0213c2b1f978e7 Mon Sep 17 00:00:00 2001 From: Vova Pytsyuk Date: Sat, 15 Jun 2019 23:09:59 +0300 Subject: [PATCH 0332/1808] Fixed bug with LoginUrl --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index bdc5bbc9c..4de616a6e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -916,7 +916,7 @@ def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, self.switch_inline_query_current_chat = switch_inline_query_current_chat self.callback_game = callback_game self.pay = pay - self.login_url = login_url.to_dic() + self.login_url = login_url def to_json(self): return json.dumps(self.to_dic()) @@ -936,7 +936,7 @@ def to_dic(self): if self.pay is not None: json_dic['pay'] = self.pay if self.login_url is not None: - json_dic['login_url'] = self.login_url + json_dic['login_url'] = self.login_url.to_dic() return json_dic From 4e57adbcb67eeced1b0a6a10f617d67f5785d2e2 Mon Sep 17 00:00:00 2001 From: desexcile <34170928+desexcile@users.noreply.github.com> Date: Wed, 26 Jun 2019 10:54:59 +0300 Subject: [PATCH 0333/1808] Update Readme added @asadov_bot to Bot list using this Api --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 06d992c14..0c5d40240 100644 --- a/README.md +++ b/README.md @@ -592,5 +592,6 @@ Get help. Discuss. Chat. * [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe. * [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian). * [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers. +* [AsadovBot](https://t.me/asadov_bot) ([source])(https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 3a1bdc289929e55d7322945cb21ceca30cc6a9a6 Mon Sep 17 00:00:00 2001 From: P0lunin Date: Thu, 27 Jun 2019 15:07:41 +0300 Subject: [PATCH 0334/1808] add Poll, sendPoll, stopPoll --- telebot/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 98076cd12..e8cc10b12 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1757,3 +1757,11 @@ def set_sticker_position_in_set(self, *args, **kwargs): @util.async_dec() def delete_sticker_from_set(self, *args, **kwargs): return TeleBot.delete_sticker_from_set(self, *args, **kwargs) + + @util.async_dec() + def send_poll(self, *args, **kwargs): + return TeleBot.send_poll(self, *args, **kwargs) + + @util.async_dec() + def stop_poll(self, *args, **kwargs): + return TeleBot.stop_poll(self, *args, **kwargs) From 87e811ce3ef03bb523879f1f09171d38bd49f450 Mon Sep 17 00:00:00 2001 From: desexcile <34170928+desexcile@users.noreply.github.com> Date: Sat, 29 Jun 2019 09:39:14 +0300 Subject: [PATCH 0335/1808] Update Readme Fixed source link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0790aa8f..874f01b66 100644 --- a/README.md +++ b/README.md @@ -597,6 +597,6 @@ Get help. Discuss. Chat. * [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe. * [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian). * [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers. -* [AsadovBot](https://t.me/asadov_bot) ([source])(https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov. +* [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From be3b6f88e8f729bf68a3577cbd937a138c835b0f Mon Sep 17 00:00:00 2001 From: Taha Date: Sun, 14 Jul 2019 18:53:59 +0430 Subject: [PATCH 0336/1808] Added Animation --- telebot/types.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 4d6a0414b..774b52644 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -296,6 +296,9 @@ def de_json(cls, json_string): if 'photo' in obj: opts['photo'] = Message.parse_photo(obj['photo']) content_type = 'photo' + if 'animation' in obj: + opts['animation'] = Animation.de_json(obj['animation']) + content_type = 'animation' if 'sticker' in obj: opts['sticker'] = Sticker.de_json(obj['sticker']) content_type = 'sticker' @@ -421,6 +424,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.contact = None self.location = None self.venue = None + self.animation = None self.new_chat_member = None self.new_chat_members = None self.left_chat_member = None @@ -2056,7 +2060,6 @@ def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position self.mask_position = mask_position self.file_size = file_size - class MaskPosition(JsonDeserializable, JsonSerializable): @classmethod def de_json(cls, json_string): From f0835a1a14f2d30395b2f1795301d715556c5698 Mon Sep 17 00:00:00 2001 From: cmd410 Date: Tue, 30 Jul 2019 12:46:39 +0300 Subject: [PATCH 0337/1808] Support for animated stickers --- telebot/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 4d6a0414b..825ab3f43 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2035,6 +2035,7 @@ def de_json(cls, json_string): file_id = obj['file_id'] width = obj['width'] height = obj['height'] + is_animated = obj['is_animated'] thumb = None if 'thumb' in obj: thumb = PhotoSize.de_json(obj['thumb']) @@ -2044,9 +2045,9 @@ def de_json(cls, json_string): if 'mask_position' in obj: mask_position = MaskPosition.de_json(obj['mask_position']) file_size = obj.get('file_size') - return cls(file_id, width, height, thumb, emoji, set_name, mask_position, file_size) + return cls(file_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated) - def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position, file_size): + def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated): self.file_id = file_id self.width = width self.height = height @@ -2055,6 +2056,7 @@ def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position self.set_name = set_name self.mask_position = mask_position self.file_size = file_size + self.is_animated = is_animated class MaskPosition(JsonDeserializable, JsonSerializable): From 6fb9e1838541c88400c02a1cda46571d3fe08018 Mon Sep 17 00:00:00 2001 From: Dmitry Sokolov Date: Mon, 12 Aug 2019 17:09:52 +0300 Subject: [PATCH 0338/1808] Update apihelper.py Hi, to indicate a third-party api-url (reverse proxy) added conditions. Perhaps not the most elegant solution, but this functionality is very lacking. apihelper.API_URL = "http://reverseproxy.example.com/bot{0}/{1}" --- telebot/apihelper.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 094fbbf8e..83479a901 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -20,8 +20,8 @@ logger = telebot.logger proxy = None -API_URL = "https://api.telegram.org/bot{0}/{1}" -FILE_URL = "https://api.telegram.org/file/bot{0}/{1}" +API_URL = None +FILE_URL = None CONNECT_TIMEOUT = 3.5 READ_TIMEOUT = 9999 @@ -41,6 +41,11 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas :param files: Optional files. :return: The result parsed to a JSON dictionary. """ + if base_url is None: + request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) + else: + request_url = base_url.format(token, method_name) + request_url = base_url.format(token, method_name) logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) read_timeout = READ_TIMEOUT @@ -99,8 +104,11 @@ def get_file(token, file_id): def get_file_url(token, file_id): - return FILE_URL.format(token, get_file(token, file_id).file_path) - + if FILE_URL is None: + return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id).file_path) + else: + return FILE_URL.format(token, get_file(token, file_id).file_path) + def download_file(token, file_path): url = FILE_URL.format(token, file_path) From 9e30cfbda67c29fc792de44bcd3adfbbfb51a245 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 12 Aug 2019 22:47:59 +0200 Subject: [PATCH 0339/1808] Use ==/!= to compare str, bytes, and int literals Identity is not the same thing as equality in Python. --- examples/inline_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/inline_example.py b/examples/inline_example.py index e36932dd6..21f05eb6f 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -51,7 +51,7 @@ def query_video(inline_query): print(e) -@bot.inline_handler(lambda query: len(query.query) is 0) +@bot.inline_handler(lambda query: len(query.query) == 0) def default_query(inline_query): try: r = types.InlineQueryResultArticle('1', 'default', types.InputTextMessageContent('default')) From f241ef1eacb9c328e7d02a8a943c29bc8076f5ec Mon Sep 17 00:00:00 2001 From: Vadim Ryazanov Date: Tue, 27 Aug 2019 11:55:14 +0300 Subject: [PATCH 0340/1808] passport_data content type added --- telebot/types.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 4d6a0414b..497519023 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -371,6 +371,9 @@ def de_json(cls, json_string): if 'poll' in obj: opts['poll'] = Poll.de_json(obj['poll']) content_type = 'poll' + if 'passport_data' in obj: + opts['passport_data'] = obj['passport_data'] + content_type = 'passport_data' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod From 8cb2da37750d3d1c73aebfc0c1de59eb4eeac215 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2019 11:55:18 +0000 Subject: [PATCH 0341/1808] Bump requests from 2.7.0 to 2.20.0 Bumps [requests](https://github.com/requests/requests) from 2.7.0 to 2.20.0. - [Release notes](https://github.com/requests/requests/releases) - [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md) - [Commits](https://github.com/requests/requests/compare/v2.7.0...v2.20.0) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1f9cd026a..6e4ca4073 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ py==1.4.29 pytest==3.0.2 -requests==2.7.0 +requests==2.20.0 six==1.9.0 wheel==0.24.0 From e644ed910a2847d9b1a7ce76935c5a78d06d9f6f Mon Sep 17 00:00:00 2001 From: Stepan Date: Sun, 13 Oct 2019 23:49:43 +0300 Subject: [PATCH 0342/1808] Using Azure Functions for serverless bots. Simple echo bot using Azure Functions as webhook. --- examples/serverless/azure_functions | 69 +++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/serverless/azure_functions diff --git a/examples/serverless/azure_functions b/examples/serverless/azure_functions new file mode 100644 index 000000000..df90e6c79 --- /dev/null +++ b/examples/serverless/azure_functions @@ -0,0 +1,69 @@ +# Using Azure Functions for serverless bots. +# (Thanks to twitter.com/masyan for the idea) + +# By default the Azure Functions url is https://.../api/HttpTrigger for HttpTrigger type. +# In this example we will use clear webhook url without /api/ -> https://.../HttpTrigger. +# Also we set "authLevel": "anonymous". + +# For HttpTrigger type set "route" and "authLevel" in functions.json +# { +# "bindings": [ +# ... +# "authLevel": "anonymous" +# "route": "HttpTrigger" +# ] +# } + +# To avoid using /api/ in url set "routePrefix":"" in host.json +# { +# ... +# "extensions": { +# "http": { +# "routePrefix": "" +# } +# } +# } + +import logging + +import azure.functions as func +import telebot +from telebot import apihelper, types + +logger = telebot.logger +telebot.logger.setLevel(logging.DEBUG) + +# Set bot token +TOKEN = '' + +# Uncomment this for using proxy for request +# PROXY = '' +# apihelper.proxy = {'https': PROXY} + +# Set WEBHOOK as your Azure Functions url (https://...azurewebsites.net/HttpTrigger) +WEBHOOK = '' + +bot = telebot.TeleBot(TOKEN) + +@bot.message_handler(commands=['start']) +def start(message): + bot.reply_to(message, 'Hello, ' + message.from_user.first_name) + +@bot.message_handler(func=lambda message: True, content_types=['text']) +def echo_message(message): + bot.reply_to(message, message.text) + +# To avoid "error 429 too many request" set webhook only once. Or use time.sleep(1). +def main(req: func.HttpRequest) -> func.HttpResponse: + bot.set_webhook(url=WEBHOOK) + request_body_dict = req.get_json() + update = telebot.types.Update.de_json(request_body_dict) + bot.process_new_messages([update.message]) + return func.HttpResponse(body='', status_code=200) + +# Sometimes "requests" version is important. +# azure-functions==1.0.4 +# PySocks==1.7.1 +# pyTelegramBotAPI==3.6.6 +# requests==2.10.0 + From 1a30a9a249d10ff0efdc42dcb82b2ca5506aece0 Mon Sep 17 00:00:00 2001 From: Innokentiy Min Date: Wed, 30 Oct 2019 14:02:00 +0300 Subject: [PATCH 0343/1808] Fixed #578 --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 094fbbf8e..d3d53a698 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -99,7 +99,7 @@ def get_file(token, file_id): def get_file_url(token, file_id): - return FILE_URL.format(token, get_file(token, file_id).file_path) + return FILE_URL.format(token, get_file(token, file_id)['file_path']) def download_file(token, file_path): From a749acde150419fe349a73c26bb62d49c859ffda Mon Sep 17 00:00:00 2001 From: Dmitry Sokolov Date: Tue, 5 Nov 2019 17:37:53 +0300 Subject: [PATCH 0344/1808] Update apihelper.py #2 Merge Fixes # 684 --- telebot/apihelper.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6e51bd973..142086d1a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -31,7 +31,7 @@ def _get_req_session(reset=False): return util.per_thread('req_session', lambda: requests.session(), reset) -def _make_request(token, method_name, method='get', params=None, files=None, base_url=API_URL): +def _make_request(token, method_name, method='get', params=None, files=None): """ Makes a request to the Telegram API. :param token: The bot's API token. (Created with @BotFather) @@ -41,12 +41,11 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas :param files: Optional files. :return: The result parsed to a JSON dictionary. """ - if base_url is None: + if API_URL is None: request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) else: - request_url = base_url.format(token, method_name) + request_url = API_URL.format(token, method_name) - request_url = base_url.format(token, method_name) logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT @@ -105,13 +104,17 @@ def get_file(token, file_id): def get_file_url(token, file_id): if FILE_URL is None: - return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id).file_path) + return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id)['file_path']) else: return FILE_URL.format(token, get_file(token, file_id)['file_path']) def download_file(token, file_path): - url = FILE_URL.format(token, file_path) + if FILE_URL is None: + url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path) + else: + url = FILE_URL.format(token, file_path) + result = _get_req_session().get(url, proxies=proxy) if result.status_code != 200: msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \ From 68db5997900cd5f40640308b8171b805165d5d95 Mon Sep 17 00:00:00 2001 From: Dzianis Talkachou Date: Sat, 23 Nov 2019 21:25:29 +0300 Subject: [PATCH 0345/1808] Delete duplicate string --- telebot/apihelper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6e51bd973..df8535d5e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -45,8 +45,7 @@ def _make_request(token, method_name, method='get', params=None, files=None, bas request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) else: request_url = base_url.format(token, method_name) - - request_url = base_url.format(token, method_name) + logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT From a71030dcdd791e2ee07aa3570db4c989a9defb97 Mon Sep 17 00:00:00 2001 From: Pablo Hinojosa Date: Sat, 30 Nov 2019 05:51:56 +0100 Subject: [PATCH 0346/1808] Change chatid to chat_id to be the same as other examples Change in README.md chatid to chat_id to be the same as other examples --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 874f01b66..1e608a03c 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ updates = tb.get_updates() updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout): # sendMessage -tb.send_message(chatid, text) +tb.send_message(chat_id, text) # forwardMessage tb.forward_message(to_chat_id, from_chat_id, message_id) From a413a51221d4d39c5eb0b0e9369551ed3b20e01e Mon Sep 17 00:00:00 2001 From: Lev Svalov <30590045+LeoSvalov@users.noreply.github.com> Date: Tue, 24 Dec 2019 16:25:58 +0500 Subject: [PATCH 0347/1808] Update README Adding another bot that user pyTelegramBotAPI. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 874f01b66..cbcdad0cb 100644 --- a/README.md +++ b/README.md @@ -598,5 +598,6 @@ Get help. Discuss. Chat. * [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian). * [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers. * [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov. +* [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From bf844ed2026ee9334d33e32331f120b79ed1e7e8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 1 Jan 2020 13:46:18 +0300 Subject: [PATCH 0348/1808] HTML symbols not replaced HTML symbols not replaced because return is before replace. --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 6165c9479..d8fe23671 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -477,10 +477,10 @@ def func(text, type=None, url=None, user=None): url = "tg://user?id={0}".format(user.id) elif type == "mention": url = "https://t.me/{0}".format(text[1:]) + text = text.replace("&", "&").replace("<", "<").replace(">", ">") if not type or not _subs.get(type): return text subs = _subs.get(type) - text = text.replace("&", "&").replace("<", "<").replace(">", ">") return subs.format(text=text, url=url) offset = 0 From 8c62b99057adf29c166b29e6988801bfb9a82da1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 3 Jan 2020 02:05:32 +0300 Subject: [PATCH 0349/1808] Update .travis.yml Travis: removed 2.6 and 3.3 (not supported). Added 3.7. Also added 3.8 (for experiment) --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f5f202a4f..d31672241 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,10 @@ language: python python: - - "2.6" - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" + - "3.7" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From 620ea5dee087fa7e16662da09500ed02fad9dc2b Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 3 Jan 2020 14:36:08 +0100 Subject: [PATCH 0350/1808] Travis CI: Drop EOL Py34 and add current Py38 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d31672241..b305175bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: python python: - "2.7" - - "3.4" - "3.5" - "3.6" - "3.7" + - "3.8" - "pypy" - "pypy3" install: "pip install -r requirements.txt" From 0603a0df4ca1261dbcac840fa823198f6bd76d10 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 3 Jan 2020 17:51:05 +0300 Subject: [PATCH 0351/1808] Update types.py Animation is moved before document to save backward compatibility. content_type = 'document' should override content_type = 'animation' to save previous behaviour. --- telebot/types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index aa54e0643..dac9d79c4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -287,6 +287,9 @@ def de_json(cls, json_string): if 'audio' in obj: opts['audio'] = Audio.de_json(obj['audio']) content_type = 'audio' + if 'animation' in obj: + opts['animation'] = Animation.de_json(obj['animation']) + content_type = 'animation' if 'document' in obj: opts['document'] = Document.de_json(obj['document']) content_type = 'document' @@ -296,9 +299,6 @@ def de_json(cls, json_string): if 'photo' in obj: opts['photo'] = Message.parse_photo(obj['photo']) content_type = 'photo' - if 'animation' in obj: - opts['animation'] = Animation.de_json(obj['animation']) - content_type = 'animation' if 'sticker' in obj: opts['sticker'] = Sticker.de_json(obj['sticker']) content_type = 'sticker' From b5ba2445d34089f81aa238254ff9f395a2692b2f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 3 Jan 2020 19:18:18 +0300 Subject: [PATCH 0352/1808] Update test_types.py Updated test vectors for stickers. --- tests/test_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index c229bdf3a..590b080c7 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -48,7 +48,7 @@ def test_json_Message_Audio(): def test_json_Message_Sticker(): - json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' + json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb.height == 60 @@ -56,7 +56,7 @@ def test_json_Message_Sticker(): def test_json_Message_Sticker_without_thumb(): - json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' + json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb is None From 5cd97ebc968f3b813c54305debfadb74bfe05394 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 3 Jan 2020 19:24:10 +0300 Subject: [PATCH 0353/1808] Update detailed_example.py Vulgarity replaced. --- examples/detailed_example/detailed_example.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index f481832a0..4c848e87c 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -20,7 +20,7 @@ } imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard -imageSelect.add('cock', 'pussy') +imageSelect.add('Mickey', 'Minnie') hideBoard = types.ReplyKeyboardRemove() # if sent as reply_markup, will hide the keyboard @@ -113,7 +113,7 @@ def msg_image_select(m): bot.send_photo(cid, open('kitten.jpg', 'rb'), reply_markup=hideBoard) userStep[cid] = 0 else: - bot.send_message(cid, "Don't type bullsh*t, if I give you a predefined keyboard!") + bot.send_message(cid, "Don't type crap, if I give you a predefined keyboard!") bot.send_message(cid, "Please try again") From 760ea5a2f0c8e40ff55f87fa06acff48b51b2014 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 8 Jan 2020 20:06:40 +0300 Subject: [PATCH 0354/1808] Design updates from #711 Significant design updated from https://github.com/eternnoir/pyTelegramBotAPI/pull/711 --- examples/detailed_example/detailed_example.py | 6 +- setup.py | 1 - telebot/__init__.py | 335 ++++++++++++++++-- 3 files changed, 312 insertions(+), 30 deletions(-) diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index 4c848e87c..8f2878ae3 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -105,15 +105,15 @@ def msg_image_select(m): # for some reason the 'upload_photo' status isn't quite working (doesn't show at all) bot.send_chat_action(cid, 'typing') - if text == "cock": # send the appropriate image based on the reply to the "/getImage" command + if text == 'Mickey': # send the appropriate image based on the reply to the "/getImage" command bot.send_photo(cid, open('rooster.jpg', 'rb'), reply_markup=hideBoard) # send file and hide keyboard, after image is sent userStep[cid] = 0 # reset the users step back to 0 - elif text == "pussy": + elif text == 'Minnie': bot.send_photo(cid, open('kitten.jpg', 'rb'), reply_markup=hideBoard) userStep[cid] = 0 else: - bot.send_message(cid, "Don't type crap, if I give you a predefined keyboard!") + bot.send_message(cid, "Please, use the predefined keyboard!") bot.send_message(cid, "Please try again") diff --git a/setup.py b/setup.py index a52df5372..7eee05e72 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,6 @@ def read(filename): classifiers=[ 'Development Status :: 5 - Production/Stable', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Environment :: Console', diff --git a/telebot/__init__.py b/telebot/__init__.py index 5dc42ebca..c7d85af02 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -396,7 +396,7 @@ def polling(self, none_stop=False, interval=0, timeout=20): def __threaded_polling(self, none_stop=False, interval=0, timeout=3): logger.info('Started polling.') self.__stop_polling.clear() - error_interval = .25 + error_interval = 0.25 polling_thread = util.WorkerThread(name="PollingThread") or_event = util.OrEvent( @@ -415,7 +415,7 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout=3): polling_thread.raise_exceptions() self.worker_pool.raise_exceptions() - error_interval = .25 + error_interval = 0.25 except apihelper.ApiException as e: logger.error(e) if not none_stop: @@ -438,12 +438,12 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout=3): def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3): logger.info('Started polling.') self.__stop_polling.clear() - error_interval = .25 + error_interval = 0.25 while not self.__stop_polling.wait(interval): try: self.__retrieve_updates(timeout) - error_interval = .25 + error_interval = 0.25 except apihelper.ApiException as e: logger.error(e) if not none_stop: @@ -645,12 +645,15 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. :param chat_id:Unique identifier for the message recipient :param audio:Audio file to send. + :param caption: :param duration:Duration of the audio in seconds :param performer:Performer :param title:Track name - :param parse_mode :param reply_to_message_id:If the message is a reply, ID of the original message :param reply_markup: + :param parse_mode + :param disable_notification: + :param timeout: :return: Message """ return types.Message.de_json( @@ -663,10 +666,13 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. :param chat_id:Unique identifier for the message recipient. :param voice: + :param caption: :param duration:Duration of sent audio in seconds :param reply_to_message_id: :param reply_markup: :param parse_mode + :param disable_notification: + :param timeout: :return: Message """ return types.Message.de_json( @@ -680,9 +686,11 @@ def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, r :param chat_id: :param data: :param reply_to_message_id: + :param caption: :param reply_markup: :param parse_mode: :param disable_notification: + :param timeout: :return: API reply. """ return types.Message.de_json( @@ -697,6 +705,8 @@ def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=Non :param data: :param reply_to_message_id: :param reply_markup: + :param disable_notification: to disable the notification + :param timeout: timeout :return: API reply. """ return types.Message.de_json( @@ -715,6 +725,8 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param supports_streaming: :param reply_to_message_id: :param reply_markup: + :param disable_notification: + :param timeout: :return: """ return types.Message.de_json( @@ -748,6 +760,8 @@ def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_me :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) :param reply_to_message_id: :param reply_markup: + :param disable_notification: + :param timeout: :return: """ return types.Message.de_json( @@ -779,6 +793,7 @@ def send_location(self, chat_id, latitude, longitude, live_period=None, reply_to :param live_period :param reply_to_message_id: :param reply_markup: + :param disable_notification: :return: API reply. """ return types.Message.de_json( @@ -866,6 +881,12 @@ def kick_chat_member(self, chat_id, user_id, until_date=None): return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date) def unban_chat_member(self, chat_id, user_id): + """ + Removes member from the ban + :param chat_id: + :param user_id: + :return: + """ return apihelper.unban_chat_member(self.token, chat_id, user_id) def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=None, @@ -1012,6 +1033,17 @@ def unpin_chat_message(self, chat_id): def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, disable_web_page_preview=None, reply_markup=None): + """ + Use this method to edit text and game messages. + :param text: + :param chat_id: + :param message_id: + :param inline_message_id: + :param parse_mode: + :param disable_web_page_preview: + :param reply_markup: + :return: + """ result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, disable_web_page_preview, reply_markup) if type(result) == bool: # if edit inline message return is bool not Message. @@ -1019,12 +1051,29 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ return types.Message.de_json(result) def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + """ + Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. + :param media: + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ result = apihelper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + """ + Use this method to edit only the reply markup of messages. + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ result = apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) if type(result) == bool: return result @@ -1032,12 +1081,32 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_messag def send_game(self, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None): + """ + Used to send the game + :param chat_id: + :param game_short_name: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :return: + """ result = apihelper.send_game(self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, reply_markup) return types.Message.de_json(result) def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None, edit_message=None): + """ + Sets the value of points in the game to a specific user + :param user_id: + :param score: + :param force: + :param chat_id: + :param message_id: + :param inline_message_id: + :param edit_message: + :return: + """ result = apihelper.set_game_score(self.token, user_id, score, force, chat_id, message_id, inline_message_id, edit_message) if type(result) == bool: @@ -1045,6 +1114,14 @@ def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=No return types.Message.de_json(result) def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None): + """ + Gets top points and game play + :param user_id: + :param chat_id: + :param message_id: + :param inline_message_id: + :return: + """ result = apihelper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) ret = [] for r in result: @@ -1056,6 +1133,31 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None): + """ + Sends invoice + :param chat_id: + :param title: + :param description: + :param invoice_payload: + :param provider_token: + :param currency: + :param prices: + :param start_parameter: + :param photo_url: + :param photo_size: + :param photo_width: + :param photo_height: + :param need_name: + :param need_phone_number: + :param need_email: + :param need_shipping_address: + :param is_flexible: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param provider_data: + :return: + """ result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, @@ -1064,19 +1166,59 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to return types.Message.de_json(result) def send_poll(self, chat_id, poll, disable_notifications=False, reply_to_message=None, reply_markup=None): + """ + Sends poll + :param chat_id: + :param poll: + :param disable_notifications: + :param reply_to_message: + :param reply_markup: + :return: + """ return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll.question, poll.options, disable_notifications, reply_to_message, reply_markup)) def stop_poll(self, chat_id, message_id): + """ + Stops poll + :param chat_id: + :param message_id: + :return: + """ return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id)) def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): + """ + Asks for an answer to a shipping question + :param shipping_query_id: + :param ok: + :param shipping_options: + :param error_message: + :return: + """ return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None): + """ + Response to a request for pre-inspection + :param pre_checkout_query_id: + :param ok: + :param error_message: + :return: + """ return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, reply_markup=None): + """ + Use this method to edit captions of messages + :param caption: + :param chat_id: + :param message_id: + :param inline_message_id: + :param parse_mode: + :param reply_markup: + :return: + """ result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, parse_mode, reply_markup) if type(result) == bool: @@ -1086,6 +1228,10 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me def reply_to(self, message, text, **kwargs): """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` + :param message: + :param text: + :param kwargs: + :return: """ return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) @@ -1114,23 +1260,12 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None, u :param callback_query_id: :param text: :param show_alert: + :param url: + :param cache_time: :return: """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - # def send_sticker(self, chat_id, sticker, disable_notification=None, reply_to_message_id=None, reply_markup=None): - # """ - # Use this method to send .webp stickers. On success, the sent Message is returned. - # :param chat_id: - # :param sticker: - # :param disable_notification: - # :param reply_to_message_id: - # :param reply_markup: - # :return: - # """ - # result = apihelper.send_sticker(self.token, chat_id, sticker, disable_notification, reply_markup, reply_markup) - # return types.Message.de_json(result) - def get_sticker_set(self, name): """ Use this method to get a sticker set. On success, a StickerSet object is returned. @@ -1228,6 +1363,11 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs self.reply_saver.start_save_timer() def _notify_reply_handlers(self, new_messages): + """ + Notify handlers of the answers + :param new_messages: + :return: + """ for message in new_messages: if hasattr(message, "reply_to_message") and message.reply_to_message is not None: reply_msg_id = message.reply_to_message.message_id @@ -1313,6 +1453,11 @@ def clear_reply_handlers_by_message_id(self, message_id): self.reply_saver.start_save_timer() def _notify_next_handlers(self, new_messages): + """ + Description: TBD + :param new_messages: + :return: + """ i = 0 while i < len(new_messages): message = new_messages[i] @@ -1332,12 +1477,18 @@ def _notify_next_handlers(self, new_messages): @staticmethod def _build_handler_dict(handler, **filters): + """ + Builds a dictionary for a handler + :param handler: + :param filters: + :return: + """ return { 'function': handler, 'filters' : filters } - def message_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -1367,6 +1518,9 @@ def default_command(message): :param content_types: This commands' supported content types. Must be a list. Defaults to ['text']. """ + if content_types is None: + content_types = ["text"] + def decorator(handler): handler_dict = self._build_handler_dict(handler, commands=commands, @@ -1382,9 +1536,27 @@ def decorator(handler): return decorator def add_message_handler(self, handler_dict): + """ + Adds a message handler + :param handler_dict: + :return: + """ self.message_handlers.append(handler_dict) - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Edit message handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param kwargs: + :return: + """ + + if content_types is None: + content_types = ["text"] + def decorator(handler): handler_dict = self._build_handler_dict(handler, commands=commands, @@ -1398,9 +1570,27 @@ def decorator(handler): return decorator def add_edited_message_handler(self, handler_dict): + """ + Adds the edit message handler + :param handler_dict: + :return: + """ self.edited_message_handlers.append(handler_dict) - def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Channel post handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param kwargs: + :return: + """ + + if content_types is None: + content_types = ["text"] + def decorator(handler): handler_dict = self._build_handler_dict(handler, commands=commands, @@ -1414,9 +1604,27 @@ def decorator(handler): return decorator def add_channel_post_handler(self, handler_dict): + """ + Adds channel post handler + :param handler_dict: + :return: + """ self.channel_post_handlers.append(handler_dict) - def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=['text'], **kwargs): + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Edit channel post handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param kwargs: + :return: + """ + + if content_types is None: + content_types = ["text"] + def decorator(handler): handler_dict = self._build_handler_dict(handler, commands=commands, @@ -1430,9 +1638,20 @@ def decorator(handler): return decorator def add_edited_channel_post_handler(self, handler_dict): + """ + Adds the edit channel post handler + :param handler_dict: + :return: + """ self.edited_channel_post_handlers.append(handler_dict) def inline_handler(self, func, **kwargs): + """ + Inline call handler decorator + :param func: + :param kwargs: + :return: + """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_inline_handler(handler_dict) @@ -1441,9 +1660,20 @@ def decorator(handler): return decorator def add_inline_handler(self, handler_dict): + """ + Adds inline call handler + :param handler_dict: + :return: + """ self.inline_handlers.append(handler_dict) def chosen_inline_handler(self, func, **kwargs): + """ + Description: TBD + :param func: + :param kwargs: + :return: + """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_chosen_inline_handler(handler_dict) @@ -1452,9 +1682,20 @@ def decorator(handler): return decorator def add_chosen_inline_handler(self, handler_dict): + """ + Description: TBD + :param handler_dict: + :return: + """ self.chosen_inline_handlers.append(handler_dict) def callback_query_handler(self, func, **kwargs): + """ + Callback request handler decorator + :param func: + :param kwargs: + :return: + """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_callback_query_handler(handler_dict) @@ -1463,9 +1704,20 @@ def decorator(handler): return decorator def add_callback_query_handler(self, handler_dict): + """ + Adds a callback request handler + :param handler_dict: + :return: + """ self.callback_query_handlers.append(handler_dict) def shipping_query_handler(self, func, **kwargs): + """ + Shipping request handler + :param func: + :param kwargs: + :return: + """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_shipping_query_handler(handler_dict) @@ -1474,9 +1726,20 @@ def decorator(handler): return decorator def add_shipping_query_handler(self, handler_dict): + """ + Adds a shipping request handler + :param handler_dict: + :return: + """ self.shipping_query_handlers.append(handler_dict) def pre_checkout_query_handler(self, func, **kwargs): + """ + Pre-checkout request handler + :param func: + :param kwargs: + :return: + """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_pre_checkout_query_handler(handler_dict) @@ -1485,9 +1748,20 @@ def decorator(handler): return decorator def add_pre_checkout_query_handler(self, handler_dict): + """ + Adds a pre-checkout request handler + :param handler_dict: + :return: + """ self.pre_checkout_query_handlers.append(handler_dict) def _test_message_handler(self, message_handler, message): + """ + Test message handler + :param message_handler: + :param message: + :return: + """ for filter, filter_value in six.iteritems(message_handler['filters']): if filter_value is None: continue @@ -1499,6 +1773,13 @@ def _test_message_handler(self, message_handler, message): @staticmethod def _test_filter(filter, filter_value, message): + """ + Test filters + :param filter: + :param filter_value: + :param message: + :return: + """ test_cases = { 'content_types': lambda msg: msg.content_type in filter_value, 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), @@ -1509,6 +1790,12 @@ def _test_filter(filter, filter_value, message): return test_cases.get(filter, lambda msg: False)(message) def _notify_command_handlers(self, handlers, new_messages): + """ + Notifies command handlers + :param handlers: + :param new_messages: + :return: + """ for message in new_messages: for message_handler in handlers: if self._test_message_handler(message_handler, message): @@ -1748,10 +2035,6 @@ def answer_inline_query(self, *args, **kwargs): def answer_callback_query(self, *args, **kwargs): return TeleBot.answer_callback_query(self, *args, **kwargs) - @util.async_dec() - def send_sticker(self, *args, **kwargs): - return TeleBot.send_sticker(self, *args, **kwargs) - @util.async_dec() def get_sticker_set(self, *args, **kwargs): return TeleBot.get_sticker_set(self, *args, **kwargs) From aa02ddb573b206831a8f543ffe0197f06e7ea7c4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 8 Jan 2020 20:17:25 +0300 Subject: [PATCH 0355/1808] TAB fix TAB fix --- telebot/__init__.py | 76 ++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c7d85af02..f2f759194 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1033,7 +1033,7 @@ def unpin_chat_message(self, chat_id): def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, disable_web_page_preview=None, reply_markup=None): - """ + """ Use this method to edit text and game messages. :param text: :param chat_id: @@ -1051,7 +1051,7 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ return types.Message.de_json(result) def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): - """ + """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. :param media: :param chat_id: @@ -1066,7 +1066,7 @@ def edit_message_media(self, media, chat_id=None, message_id=None, inline_messag return types.Message.de_json(result) def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): - """ + """ Use this method to edit only the reply markup of messages. :param chat_id: :param message_id: @@ -1081,7 +1081,7 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_messag def send_game(self, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None): - """ + """ Used to send the game :param chat_id: :param game_short_name: @@ -1096,7 +1096,7 @@ def send_game(self, chat_id, game_short_name, disable_notification=None, reply_t def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None, edit_message=None): - """ + """ Sets the value of points in the game to a specific user :param user_id: :param score: @@ -1114,7 +1114,7 @@ def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=No return types.Message.de_json(result) def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None): - """ + """ Gets top points and game play :param user_id: :param chat_id: @@ -1133,18 +1133,18 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None): - """ + """ Sends invoice - :param chat_id: - :param title: - :param description: - :param invoice_payload: - :param provider_token: - :param currency: - :param prices: - :param start_parameter: - :param photo_url: - :param photo_size: + :param chat_id: + :param title: + :param description: + :param invoice_payload: + :param provider_token: + :param currency: + :param prices: + :param start_parameter: + :param photo_url: + :param photo_size: :param photo_width: :param photo_height: :param need_name: @@ -1166,7 +1166,7 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to return types.Message.de_json(result) def send_poll(self, chat_id, poll, disable_notifications=False, reply_to_message=None, reply_markup=None): - """ + """ Sends poll :param chat_id: :param poll: @@ -1178,7 +1178,7 @@ def send_poll(self, chat_id, poll, disable_notifications=False, reply_to_message return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll.question, poll.options, disable_notifications, reply_to_message, reply_markup)) def stop_poll(self, chat_id, message_id): - """ + """ Stops poll :param chat_id: :param message_id: @@ -1187,7 +1187,7 @@ def stop_poll(self, chat_id, message_id): return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id)) def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): - """ + """ Asks for an answer to a shipping question :param shipping_query_id: :param ok: @@ -1198,18 +1198,18 @@ def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, er return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None): - """ + """ Response to a request for pre-inspection - :param pre_checkout_query_id: - :param ok: - :param error_message: - :return: - """ + :param pre_checkout_query_id: + :param ok: + :param error_message: + :return: + """ return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, reply_markup=None): - """ + """ Use this method to edit captions of messages :param caption: :param chat_id: @@ -1228,7 +1228,7 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me def reply_to(self, message, text, **kwargs): """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` - :param message: + :param message: :param text: :param kwargs: :return: @@ -1477,9 +1477,9 @@ def _notify_next_handlers(self, new_messages): @staticmethod def _build_handler_dict(handler, **filters): - """ - Builds a dictionary for a handler - :param handler: + """ + Builds a dictionary for a handler + :param handler: :param filters: :return: """ @@ -1536,7 +1536,7 @@ def decorator(handler): return decorator def add_message_handler(self, handler_dict): - """ + """ Adds a message handler :param handler_dict: :return: @@ -1756,7 +1756,7 @@ def add_pre_checkout_query_handler(self, handler_dict): self.pre_checkout_query_handlers.append(handler_dict) def _test_message_handler(self, message_handler, message): - """ + """ Test message handler :param message_handler: :param message: @@ -1773,11 +1773,11 @@ def _test_message_handler(self, message_handler, message): @staticmethod def _test_filter(filter, filter_value, message): - """ - Test filters - :param filter: - :param filter_value: - :param message: + """ + Test filters + :param filter: + :param filter_value: + :param message: :return: """ test_cases = { From 583021d11407c432a69c8de70fcc99f0cd04e43d Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Tue, 14 Jan 2020 16:33:05 +0800 Subject: [PATCH 0356/1808] Update version to 3.6.7, --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7eee05e72..44a5cde6a 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(filename): return file.read() setup(name='pyTelegramBotAPI', - version='3.6.6', + version='3.6.7', description='Python Telegram bot api. ', long_description=read('README.md'), long_description_content_type="text/markdown", From b323a868f08577c8e37a385ad351495b52cc03d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E5=8D=9A=E4=BB=81=28Buo-ren=20Lin=29?= Date: Fri, 17 Jan 2020 17:21:30 +0800 Subject: [PATCH 0357/1808] Fix typo in README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 林博仁(Buo-ren, Lin) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4e05343b..1e40cf6aa 100644 --- a/README.md +++ b/README.md @@ -588,7 +588,7 @@ Get help. Discuss. Chat. * [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. * [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. * [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. -* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audiosamles and try to name the performer of the song. +* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audio samples and try to name the performer of the song. * [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) * [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). * [ManjaroBot](https://t.me/ManjaroBot) by [@NeoRanger](https://github.com/neoranger) - Bot for Manjaro Linux Spanish group with a lot of info for Manjaro Newbies. From d68e89fc9a48bb07a5a504e426c7fb45ab4ae05c Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 28 Jan 2020 20:56:03 +0200 Subject: [PATCH 0358/1808] Added missed bracket --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e40cf6aa..74abb8734 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ TeleBot supports the following filters: |name|argument(s)|Condition| |:---:|---| ---| |content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.| -|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html)| +|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))| |commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.| |func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` From dfac26706e7fbca410011c02eadaed4c541f7a1d Mon Sep 17 00:00:00 2001 From: Quantum Date: Fri, 7 Feb 2020 12:53:31 +0300 Subject: [PATCH 0359/1808] Add is_anonymous flag to Poll type --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index b6a2cdf3a..a288e6ef1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2244,6 +2244,8 @@ def de_json(cls, json_type): is_closed = obj['is_closed'] poll.id = poll_id poll.is_closed = is_closed + is_anonymous = obj['is_anonymous'] + poll.is_anonymous = is_anonymous return poll def __init__(self, question): From c57cfa39497ff6dfafc4e50b0f003341fad32a44 Mon Sep 17 00:00:00 2001 From: faeelol Date: Sat, 8 Feb 2020 17:44:28 +0300 Subject: [PATCH 0360/1808] Add missed colon --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74abb8734..f2d96d536 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ def test_message(message): return message.document.mime_type == 'text/plain' @bot.message_handler(func=test_message, content_types=['document']) -def handle_text_doc(message) +def handle_text_doc(message): pass # Handlers can be stacked to create a function which will be called if either message_handler is eligible From 3ecb7cef3b3d171e1773a0997877246f1cbba58b Mon Sep 17 00:00:00 2001 From: HolidayMan <37782099+HolidayMan@users.noreply.github.com> Date: Thu, 27 Feb 2020 00:59:21 +0200 Subject: [PATCH 0361/1808] Some clarification of custom listeners working --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f2d96d536..ef23ba735 100644 --- a/README.md +++ b/README.md @@ -466,7 +466,10 @@ The TeleBot constructor takes the following optional arguments: TeleBot should execute message handlers on it's polling Thread. ### The listener mechanism -As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example: +As an alternative to the message handlers, one can also register a function as a listener to TeleBot. + +NOTICE: handlers won't disappear! Your message will be processed both by handlers and listeners. Also, it's impossible to predict which will work at first because of threading. If you use threaded=False, custom listeners will work earlier, after them handlers will be called. +Example: ```python def handle_messages(messages): for message in messages: From 7ca629dc10cb866ce4ae228cb8ab379e6c8cacbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E5=8D=9A=E4=BB=81=28Buo-ren=20Lin=29?= Date: Thu, 27 Feb 2020 16:41:13 +0800 Subject: [PATCH 0362/1808] Fix missing padding space in code sample comments in README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 林博仁(Buo-ren, Lin) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2d96d536..141ae3024 100644 --- a/README.md +++ b/README.md @@ -180,12 +180,12 @@ def handle_docs_audio(message): def handle_message(message): pass -#Handles all messages for which the lambda returns True +# Handles all messages for which the lambda returns True @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document']) def handle_text_doc(message): pass -#Which could also be defined as: +# Which could also be defined as: def test_message(message): return message.document.mime_type == 'text/plain' From a4e5a09ab22c7798a2f57193c729f70d359e4a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E5=8D=9A=E4=BB=81=28Buo-ren=20Lin=29?= Date: Thu, 27 Feb 2020 16:44:43 +0800 Subject: [PATCH 0363/1808] Use proper language for * query mentions in README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 林博仁(Buo-ren, Lin) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2d96d536..ba060ea47 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ Same as Message handlers #### Callback Query Handler -In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback_querys. +In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback queries. ```python @bot.callback_query_handler(func=lambda call: True) @@ -372,7 +372,7 @@ More information about [Inline mode](https://core.telegram.org/bots/inline). #### inline_handler -Now, you can use inline_handler to get inline_query in telebot. +Now, you can use inline_handler to get inline queries in telebot. ```python From 12547efa08ff2d81ed282da91d83cee1b7320abf Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 9 Mar 2020 17:25:37 +0700 Subject: [PATCH 0364/1808] Fix order for consistency in process_new_updates --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f2f759194..16bfc4a3b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -327,10 +327,10 @@ def process_new_updates(self, updates): self.process_new_chosen_inline_query(new_chosen_inline_results) if len(new_callback_querys) > 0: self.process_new_callback_query(new_callback_querys) - if len(new_pre_checkout_querys) > 0: - self.process_new_pre_checkout_query(new_pre_checkout_querys) if len(new_shipping_querys) > 0: self.process_new_shipping_query(new_shipping_querys) + if len(new_pre_checkout_querys) > 0: + self.process_new_pre_checkout_query(new_pre_checkout_querys) def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) From d2f9c51a5a9b97db56b49d4d3c6d09deb241baa6 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 9 Mar 2020 17:25:54 +0700 Subject: [PATCH 0365/1808] Handle Poll update --- telebot/__init__.py | 31 +++++++++++++++++++++++++++++++ telebot/types.py | 22 ++++++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 16bfc4a3b..b7b5d7921 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -167,6 +167,7 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): self.callback_query_handlers = [] self.shipping_query_handlers = [] self.pre_checkout_query_handlers = [] + self.poll_handlers = [] self.threaded = threaded if self.threaded: @@ -289,6 +290,7 @@ def process_new_updates(self, updates): new_callback_querys = [] new_shipping_querys = [] new_pre_checkout_querys = [] + new_polls = [] for update in updates: if update.update_id > self.last_update_id: @@ -311,6 +313,8 @@ def process_new_updates(self, updates): new_shipping_querys.append(update.shipping_query) if update.pre_checkout_query: new_pre_checkout_querys.append(update.pre_checkout_query) + if update.poll: + new_polls.append(update.poll) logger.debug('Received {0} new updates'.format(len(updates))) if len(new_messages) > 0: @@ -331,6 +335,8 @@ def process_new_updates(self, updates): self.process_new_shipping_query(new_shipping_querys) if len(new_pre_checkout_querys) > 0: self.process_new_pre_checkout_query(new_pre_checkout_querys) + if len(new_polls) > 0: + self.process_new_poll(new_polls) def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) @@ -362,6 +368,9 @@ def process_new_shipping_query(self, new_shipping_querys): def process_new_pre_checkout_query(self, pre_checkout_querys): self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_querys) + def process_new_poll(self, polls): + self._notify_command_handlers(self.poll_handlers, polls) + def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) @@ -1755,6 +1764,28 @@ def add_pre_checkout_query_handler(self, handler_dict): """ self.pre_checkout_query_handlers.append(handler_dict) + def poll_handler(self, func, **kwargs): + """ + Poll request handler + :param func: + :param kwargs: + :return: + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_poll_handler(handler_dict) + return handler + + return decorator + + def add_poll_handler(self, handler_dict): + """ + Adds a poll request handler + :param handler_dict: + :return: + """ + self.poll_handlers.append(handler_dict) + def _test_message_handler(self, message_handler, message): """ Test message handler diff --git a/telebot/types.py b/telebot/types.py index a288e6ef1..643c32cd6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -103,6 +103,7 @@ def de_json(cls, json_type): callback_query = None shipping_query = None pre_checkout_query = None + poll = None if 'message' in obj: message = Message.de_json(obj['message']) if 'edited_message' in obj: @@ -121,11 +122,13 @@ def de_json(cls, json_type): shipping_query = ShippingQuery.de_json(obj['shipping_query']) if 'pre_checkout_query' in obj: pre_checkout_query = PreCheckoutQuery.de_json(obj['pre_checkout_query']) + if 'poll' in obj: + poll = Poll.de_json(obj['poll']) return cls(update_id, message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query, shipping_query, pre_checkout_query) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) 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): + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -136,6 +139,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.callback_query = callback_query self.shipping_query = shipping_query self.pre_checkout_query = pre_checkout_query + self.poll = poll class WebhookInfo(JsonDeserializable): @@ -2240,12 +2244,22 @@ def de_json(cls, json_type): options = [] for opt in obj['options']: options.append(PollOption.de_json(opt)) - poll.options = options + total_voter_count = obj['total_voter_count'] is_closed = obj['is_closed'] + is_anonymous = obj['is_anonymous'] + poll_type = obj['type'] + allows_multiple_answers = obj['allows_multiple_answers'] + correct_option_id = None + if 'correct_option_id' in obj: + correct_option_id = obj['correct_option_id'] poll.id = poll_id + poll.options = options + poll.total_voter_count = total_voter_count poll.is_closed = is_closed - is_anonymous = obj['is_anonymous'] poll.is_anonymous = is_anonymous + poll.type = poll_type + poll.allows_multiple_answers = allows_multiple_answers + poll.correct_option_id = correct_option_id return poll def __init__(self, question): From ed7cf30034c1d9b0bc1709ee65c4b277ff8f380e Mon Sep 17 00:00:00 2001 From: Ilya-Revenko Date: Fri, 27 Mar 2020 16:38:22 +0200 Subject: [PATCH 0366/1808] Add new bot in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 31f1f298d..9d78f0d18 100644 --- a/README.md +++ b/README.md @@ -603,5 +603,6 @@ Get help. Discuss. Chat. * [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers. * [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov. * [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram. +* [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...) Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 1e242f22634f8504e456d161d083757a1d075ce8 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Wed, 8 Apr 2020 23:13:19 +0500 Subject: [PATCH 0367/1808] Add Middleware support --- telebot/__init__.py | 74 +++++++++++++++++++++++++++++++++++++++++++ tests/test_telebot.py | 49 ++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index b7b5d7921..a1434795e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -169,6 +169,21 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): self.pre_checkout_query_handlers = [] self.poll_handlers = [] + self.typed_middleware_handlers = { + 'message': [], + 'edited_message': [], + 'channel_post': [], + 'edited_channel_post': [], + 'inline_query': [], + 'chosen_inline_result': [], + 'callback_query': [], + 'shipping_query': [], + 'pre_checkout_query': [], + 'poll': [], + } + + self.default_middleware_handlers = [] + self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(num_threads=num_threads) @@ -293,6 +308,9 @@ def process_new_updates(self, updates): new_polls = [] for update in updates: + + self.process_middlewares(update) + if update.update_id > self.last_update_id: self.last_update_id = update.update_id if update.message: @@ -371,6 +389,16 @@ def process_new_pre_checkout_query(self, pre_checkout_querys): def process_new_poll(self, polls): self._notify_command_handlers(self.poll_handlers, polls) + def process_middlewares(self, update): + for update_type, middlewares in self.typed_middleware_handlers.items(): + if hasattr(update, update_type): + for typed_middleware_handler in middlewares: + typed_middleware_handler(self, getattr(update, update_type)) + + if len(self.default_middleware_handlers) > 0: + for default_middleware_handler in self.default_middleware_handlers: + default_middleware_handler(self, update) + def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) @@ -1497,6 +1525,52 @@ def _build_handler_dict(handler, **filters): 'filters' : filters } + def middleware_handler(self, update_types=None): + """ + Middleware handler decorator. + + This decorator can be used to decorate functions that must be handled as middlewares before entering any other + message handlers + But, be careful and check type of the update inside the handler if more than one update_type is given + + Example: + + bot = TeleBot('TOKEN') + + # Print post message text before entering to any post_channel handlers + @bot.middleware_handler(update_types=['channel_post', 'edited_channel_post']) + def print_channel_post_text(bot_instance, channel_post): + print(channel_post.text) + + # Print update id before entering to any handlers + @bot.middleware_handler() + def print_channel_post_text(bot_instance, update): + print(update.update_id) + + :param update_types: Optional list of update types that can be passed into the middleware handler. + + """ + + def decorator(handler): + self.add_middleware_handler(handler, update_types) + + return handler + + return decorator + + def add_middleware_handler(self, handler, update_types=None): + """ + Add middleware handler + :param handler: + :param update_types: + :return: + """ + if update_types: + for update_type in update_types: + self.typed_middleware_handlers[update_type].append(handler) + else: + self.default_middleware_handlers.append(handler) + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Message handler decorator. diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 908b14121..3157bdde6 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -408,6 +408,23 @@ def create_text_message(text): chat = types.User(11, False, 'test') return types.Message(1, None, None, chat, 'text', params, "") + @staticmethod + def create_message_update(text): + params = {'text': text} + chat = types.User(11, False, 'test') + message = types.Message(1, None, None, chat, 'text', params, "") + edited_message = None + channel_post = None + edited_channel_post = None + inline_query = None + chosen_inline_result = None + callback_query = None + shipping_query = None + pre_checkout_query = None + poll = None + return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query, + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + def test_is_string_unicode(self): s1 = u'string' assert util.is_string(s1) @@ -489,3 +506,35 @@ def test_send_document_formating_caption(self): tb = telebot.TeleBot(TOKEN) ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' + + def test_typed_middleware_handler(self): + tb = telebot.TeleBot('') + update = self.create_message_update('/help') + + @tb.middleware_handler(update_types=['message']) + def middleware(tb_instance, message): + message.text = 'got' + + @tb.message_handler(func=lambda m: m.text == 'got') + def command_handler(message): + message.text = message.text + message.text + + tb.process_new_updates([update]) + time.sleep(1) + assert update.message.text == 'got' * 2 + + def test_default_middleware_handler(self): + tb = telebot.TeleBot('') + update = self.create_message_update('/help') + + @tb.middleware_handler() + def middleware(tb_instance, update): + update.message.text = 'got' + + @tb.message_handler(func=lambda m: m.text == 'got') + def command_handler(message): + message.text = message.text + message.text + + tb.process_new_updates([update]) + time.sleep(1) + assert update.message.text == 'got' * 2 From 932ac9477b5d3b3f950f085a251daf6e1288cc6f Mon Sep 17 00:00:00 2001 From: bedilbek Date: Sat, 11 Apr 2020 13:02:50 +0500 Subject: [PATCH 0368/1808] Add ENABLE_MIDDLEWARE=False in apihelpers to keep backward compatibility --- telebot/__init__.py | 3 ++- telebot/apihelper.py | 2 ++ tests/test_telebot.py | 8 ++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a1434795e..de928dda3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -309,7 +309,8 @@ def process_new_updates(self, updates): for update in updates: - self.process_middlewares(update) + if apihelper.ENABLE_MIDDLEWARE: + self.process_middlewares(update) if update.update_id > self.last_update_id: self.last_update_id = update.update_id diff --git a/telebot/apihelper.py b/telebot/apihelper.py index cbcc08c36..4c4c68070 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -26,6 +26,8 @@ CONNECT_TIMEOUT = 3.5 READ_TIMEOUT = 9999 +ENABLE_MIDDLEWARE = False + def _get_req_session(reset=False): return util.per_thread('req_session', lambda: requests.session(), reset) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 3157bdde6..b3fbfcf3b 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -508,6 +508,10 @@ def test_send_document_formating_caption(self): assert ret_msg.caption_entities[0].type == 'italic' def test_typed_middleware_handler(self): + from telebot import apihelper + + apihelper.ENABLE_MIDDLEWARE = True + tb = telebot.TeleBot('') update = self.create_message_update('/help') @@ -524,6 +528,10 @@ def command_handler(message): assert update.message.text == 'got' * 2 def test_default_middleware_handler(self): + from telebot import apihelper + + apihelper.ENABLE_MIDDLEWARE = True + tb = telebot.TeleBot('') update = self.create_message_update('/help') From 56cbc2ff937cc15971feff3e06a7fe9d03544d2c Mon Sep 17 00:00:00 2001 From: bedilbek Date: Sat, 11 Apr 2020 13:03:52 +0500 Subject: [PATCH 0369/1808] Add examples to better understand middleware handler --- examples/middleware/i18n.py | 53 +++++++++++++++++++++++++++++ examples/middleware/session.py | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 examples/middleware/i18n.py create mode 100644 examples/middleware/session.py diff --git a/examples/middleware/i18n.py b/examples/middleware/i18n.py new file mode 100644 index 000000000..3cea87544 --- /dev/null +++ b/examples/middleware/i18n.py @@ -0,0 +1,53 @@ +#!/usr/bin/python + +# This example shows how to implement i18n (internationalization) l10n (localization) to create +# multi-language bots with middleware handler. +# +# Note: For the sake of simplicity of this example no extra library is used. However, it is recommended to use +# better i18n systems (gettext and etc) for handling multilingual translations. +# This is not a working, production-ready sample and it is highly recommended not to use it in production. +# +# In this example let's imagine we want to introduce localization or internationalization into our project and +# we need some global function to activate the language once and to use that language in all other message +# handler functions for not repeatedly activating it. +# The middleware (i18n and l10n) is explained: + +import telebot +from telebot import apihelper + +apihelper.ENABLE_MIDDLEWARE = True + +TRANSLATIONS = { + 'hello': { + 'en': 'hello', + 'ru': 'привет', + 'uz': 'salom' + } +} + +_lang = 'en' + + +def activate(lang): + global _lang + _lang = lang + + +def _(string): + return TRANSLATIONS[string][_lang] + + +bot = telebot.TeleBot('TOKEN') + + +@bot.middleware_handler(update_types=['message']) +def activate_language(bot_instance, message): + activate(message.from_user.language_code) + + +@bot.message_handler(commands=['start']) +def start(message): + bot.send_message(message.chat.id, _('hello')) + + +bot.polling() diff --git a/examples/middleware/session.py b/examples/middleware/session.py new file mode 100644 index 000000000..a1a30e5bc --- /dev/null +++ b/examples/middleware/session.py @@ -0,0 +1,61 @@ +#!/usr/bin/python + +# This example shows how to implement session creation and retrieval based on user id with middleware handler. +# +# Note: For the sake of simplicity of this example no extra library is used. However, it is recommended to use +# in-memory or on-disk storage implementations (redis, mysql, postgres and etc) for storing and retrieving structures. +# This is not a working, production-ready sample and it is highly recommended not to use it in production. +# +# In this example let's imagine we want to create a session for each user who communicates with the bot to store +# different kind of temporary data while session is active. As an example we want to track the state of the user +# with the help of this session. So, we need a way to store this session data somewhere globally to enable other +# message handler functions to be able to use it. +# The middleware session is explained: + +import telebot +from telebot import apihelper + +apihelper.ENABLE_MIDDLEWARE = True + +INFO_STATE = 'ON_INFO_MENU' +MAIN_STATE = 'ON_MAIN_MENU' + +SESSIONS = { + -10000: { + 'state': INFO_STATE + }, + -11111: { + 'state': MAIN_STATE + } +} + + +def get_or_create_session(user_id): + try: + return SESSIONS[user_id] + except KeyError: + SESSIONS[user_id] = {'state': MAIN_STATE} + return SESSIONS[user_id] + + +bot = telebot.TeleBot('TOKEN') + + +@bot.middleware_handler(update_types=['message']) +def set_session(bot_instance, message): + bot_instance.session = get_or_create_session(message.from_user.id) + + +@bot.message_handler(commands=['start']) +def start(message): + bot.session['state'] = MAIN_STATE + bot.send_message(message.chat.id, bot.session['state']) + + +@bot.message_handler(commands=['info']) +def start(message): + bot.session['state'] = INFO_STATE + bot.send_message(message.chat.id, bot.session['state']) + + +bot.polling() From ec86182f62bc6980a235922f192936cb39d4d025 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Apr 2020 13:42:34 +0300 Subject: [PATCH 0370/1808] Possibility to use alternative serializer With apihelper.CUSTOM_SERIALIZER you can replace pickle with other "dumper" like dill. --- telebot/__init__.py | 10 ++++++++-- telebot/apihelper.py | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index de928dda3..72f6c7169 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -76,7 +76,10 @@ def dump_handlers(handlers, filename, file_mode="wb"): os.makedirs(dirs, exist_ok=True) with open(filename + ".tmp", file_mode) as file: - pickle.dump(handlers, file) + if (apihelper.CUSTOM_SERIALIZER is None): + pickle.dump(handlers, file) + else: + apihelper.CUSTOM_SERIALIZER.dump(handlers, file) if os.path.isfile(filename): os.remove(filename) @@ -87,7 +90,10 @@ def dump_handlers(handlers, filename, file_mode="wb"): def return_load_handlers(filename, del_file_after_loading=True): if os.path.isfile(filename) and os.path.getsize(filename) > 0: with open(filename, "rb") as file: - handlers = pickle.load(file) + if (apihelper.CUSTOM_SERIALIZER is None): + handlers = pickle.load(file) + else: + handlers = apihelper.CUSTOM_SERIALIZER.load(file) if del_file_after_loading: os.remove(filename) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4c4c68070..9db62cc37 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -18,6 +18,7 @@ from telebot import util logger = telebot.logger + proxy = None API_URL = None @@ -26,6 +27,8 @@ CONNECT_TIMEOUT = 3.5 READ_TIMEOUT = 9999 +CUSTOM_SERIALIZER = None + ENABLE_MIDDLEWARE = False From 995814d846002d79d6b62beddb54a495c4d1d846 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Apr 2020 16:54:25 +0300 Subject: [PATCH 0371/1808] Refactoring and API conformance Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking. --- README.md | 24 +++++- telebot/types.py | 197 +++++++++++++++++++---------------------------- 2 files changed, 101 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 9d78f0d18..57b1ca7c8 100644 --- a/README.md +++ b/README.md @@ -514,9 +514,29 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} ``` -## New in library +## API conformance -06.06.2019 - Добавленна поддержка опросов (Poll). Добавлены функции send_poll, stop_poll +_Checking is in progress..._ + +✅ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) _- To be checked..._ + +* ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017) +* ✔ [Bot API 3.3](https://core.telegram.org/bots/api-changelog#august-23-2017) +* ✔ [Bot API 3.2](https://core.telegram.org/bots/api-changelog#july-21-2017) +* ✔ [Bot API 3.1](https://core.telegram.org/bots/api-changelog#june-30-2017) +* ✔ [Bot API 3.0](https://core.telegram.org/bots/api-changelog#may-18-2017) +* ✔ [Bot API 2.3.1](https://core.telegram.org/bots/api-changelog#december-4-2016) +* ✔ [Bot API 2.3](https://core.telegram.org/bots/api-changelog#november-21-2016) +* ✔ [Bot API 2.2](https://core.telegram.org/bots/api-changelog#october-3-2016) +* ✔ [Bot API 2.1](https://core.telegram.org/bots/api-changelog#may-22-2016) +* ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016) + + +## Change log + +11.09.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking. + +06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll ## F.A.Q. diff --git a/telebot/types.py b/telebot/types.py index 643c32cd6..60fc73ea8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -66,14 +66,9 @@ def check_json(json_type): :param json_type: :return: """ - try: - str_types = (str, unicode) - except NameError: - str_types = (str,) - - if type(json_type) == dict: + if isinstance(json_type, dict): return json_type - elif type(json_type) in str_types: + elif isinstance(json_type, str): return json.loads(json_type) else: raise ValueError("json_type should be a json dict or string.") @@ -94,36 +89,16 @@ class Update(JsonDeserializable): def de_json(cls, json_type): obj = cls.check_json(json_type) update_id = obj['update_id'] - message = None - edited_message = None - channel_post = None - edited_channel_post = None - inline_query = None - chosen_inline_result = None - callback_query = None - shipping_query = None - pre_checkout_query = None - poll = None - if 'message' in obj: - message = Message.de_json(obj['message']) - if 'edited_message' in obj: - edited_message = Message.de_json(obj['edited_message']) - if 'channel_post' in obj: - channel_post = Message.de_json(obj['channel_post']) - if 'edited_channel_post' in obj: - edited_channel_post = Message.de_json(obj['edited_channel_post']) - if 'inline_query' in obj: - inline_query = InlineQuery.de_json(obj['inline_query']) - if 'chosen_inline_result' in obj: - chosen_inline_result = ChosenInlineResult.de_json(obj['chosen_inline_result']) - if 'callback_query' in obj: - callback_query = CallbackQuery.de_json(obj['callback_query']) - if 'shipping_query' in obj: - shipping_query = ShippingQuery.de_json(obj['shipping_query']) - if 'pre_checkout_query' in obj: - pre_checkout_query = PreCheckoutQuery.de_json(obj['pre_checkout_query']) - if 'poll' in obj: - poll = Poll.de_json(obj['poll']) + message = Message.de_json(obj.get('message')) + edited_message = Message.de_json(obj.get('edited_message')) + channel_post = Message.de_json(obj.get('channel_post')) + edited_channel_post = Message.de_json(obj.get('edited_channel_post')) + inline_query = InlineQuery.de_json(obj.get('inline_query')) + chosen_inline_result = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) + callback_query = CallbackQuery.de_json(obj.get('callback_query')) + shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) + pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) + poll = Poll.de_json(obj['poll']) 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) @@ -145,22 +120,15 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan class WebhookInfo(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) url = obj['url'] has_custom_certificate = obj['has_custom_certificate'] pending_update_count = obj['pending_update_count'] - last_error_date = None - last_error_message = None - max_connections = None - allowed_updates = None - if 'last_error_message' in obj: - last_error_date = obj['last_error_date'] - if 'last_error_message' in obj: - last_error_message = obj['last_error_message'] - if 'max_connections' in obj: - max_connections = obj['max_connections'] - if 'allowed_updates' in obj: - allowed_updates = obj['allowed_updates'] + last_error_date = obj.get('last_error_date') + last_error_message = obj.get('last_error_message') + max_connections = obj.get('max_connections') + allowed_updates = obj.get('allowed_updates') return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, max_connections, allowed_updates) @@ -178,6 +146,7 @@ def __init__(self, url, has_custom_certificate, pending_update_count, last_error class User(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) id = obj['id'] is_bot = obj['is_bot'] @@ -199,6 +168,7 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua class GroupChat(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) id = obj['id'] title = obj['title'] @@ -212,6 +182,7 @@ def __init__(self, id, title): class Chat(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) id = obj['id'] type = obj['type'] @@ -220,14 +191,10 @@ def de_json(cls, json_string): first_name = obj.get('first_name') last_name = obj.get('last_name') all_members_are_administrators = obj.get('all_members_are_administrators') - photo = None - if 'photo' in obj: - photo = ChatPhoto.de_json(obj['photo']) + photo = ChatPhoto.de_json(obj.get('photo')) description = obj.get('description') invite_link = obj.get('invite_link') - pinned_message = None - if 'pinned_message' in obj: - pinned_message = Message.de_json(obj['pinned_message']) + pinned_message = Message.de_json(obj.get('pinned_message')) sticker_set_name = obj.get('sticker_set_name') can_set_sticker_set = obj.get('can_set_sticker_set') return cls(id, type, title, username, first_name, last_name, all_members_are_administrators, @@ -254,11 +221,10 @@ def __init__(self, id, type, title=None, username=None, first_name=None, last_na class Message(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) message_id = obj['message_id'] - from_user = None - if 'from' in obj: - from_user = User.de_json(obj['from']) + from_user = User.de_json(obj.get('from')) date = obj['date'] chat = Chat.de_json(obj['chat']) content_type = None @@ -326,15 +292,11 @@ def de_json(cls, json_string): if 'venue' in obj: opts['venue'] = Venue.de_json(obj['venue']) content_type = 'venue' - if 'new_chat_member' in obj: - opts['new_chat_member'] = User.de_json(obj['new_chat_member']) - content_type = 'new_chat_member' if 'new_chat_members' in obj: - chat_members = obj['new_chat_members'] - nms = [] - for m in chat_members: - nms.append(User.de_json(m)) - opts['new_chat_members'] = nms + new_chat_members = [] + for member in obj['new_chat_members']: + new_chat_members.append(User.de_json(member)) + opts['new_chat_members'] = new_chat_members content_type = 'new_chat_members' if 'left_chat_member' in obj: opts['left_chat_member'] = User.de_json(obj['left_chat_member']) @@ -410,9 +372,10 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.from_user = from_user self.date = date self.chat = chat + self.forward_from = None self.forward_from_chat = None self.forward_from_message_id = None - self.forward_from = None + self.forward_signature = None self.forward_date = None self.reply_to_message = None self.edit_date = None @@ -433,7 +396,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.location = None self.venue = None self.animation = None - self.new_chat_member = None + self.new_chat_member = None # Deprecated since Bot API 3.0. Not processed anymore self.new_chat_members = None self.left_chat_member = None self.new_chat_title = None @@ -455,6 +418,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso def __html_text(self, text, entities): """ Author: @sviat9440 + Updaters: @badiboy Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" Example: @@ -471,12 +435,13 @@ def __html_text(self, text, entities): if not entities: return text + _subs = { "bold" : "{text}", "italic" : "{text}", "pre" : "
{text}
", "code" : "{text}", - "url" : "{text}", + #"url" : "{text}", # @badiboy plain URLs have no text and do not need tags "text_link": "{text}" } if hasattr(self, "custom_subs"): @@ -503,8 +468,15 @@ def func(text, type=None, url=None, user=None): if entity.offset > offset: html_text += func(utf16_text[offset * 2 : entity.offset * 2]) offset = entity.offset - html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) - offset += entity.length + html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + offset += entity.length + elif entity.offset == offset: + html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + offset += entity.length + else: + # TODO: process nested entities from Bot API 4.5 + # Now ignoring them + pass if offset * 2 < len(utf16_text): html_text += func(utf16_text[offset * 2:]) return html_text @@ -521,14 +493,13 @@ def html_caption(self): class MessageEntity(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) type = obj['type'] offset = obj['offset'] length = obj['length'] url = obj.get('url') - user = None - if 'user' in obj: - user = User.de_json(obj['user']) + user = User.de_json(obj.get('user')) return cls(type, offset, length, url, user) def __init__(self, type, offset, length, url=None, user=None): @@ -542,6 +513,7 @@ def __init__(self, type, offset, length, url=None, user=None): class PhotoSize(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] width = obj['width'] @@ -559,6 +531,7 @@ def __init__(self, file_id, width, height, file_size=None): class Audio(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] duration = obj['duration'] @@ -580,6 +553,7 @@ def __init__(self, file_id, duration, performer=None, title=None, mime_type=None class Voice(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] duration = obj['duration'] @@ -597,6 +571,7 @@ def __init__(self, file_id, duration, mime_type=None, file_size=None): class Document(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] thumb = None @@ -618,14 +593,13 @@ def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_siz class Video(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] width = obj['width'] height = obj['height'] duration = obj['duration'] - thumb = None - if 'thumb' in obj: - thumb = PhotoSize.de_json(obj['thumb']) + thumb = PhotoSize.de_json(obj.get('thumb')) mime_type = obj.get('mime_type') file_size = obj.get('file_size') return cls(file_id, width, height, duration, thumb, mime_type, file_size) @@ -643,13 +617,12 @@ def __init__(self, file_id, width, height, duration, thumb=None, mime_type=None, class VideoNote(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] length = obj['length'] duration = obj['duration'] - thumb = None - if 'thumb' in obj: - thumb = PhotoSize.de_json(obj['thumb']) + thumb = PhotoSize.de_json(obj.get('thumb')) file_size = obj.get('file_size') return cls(file_id, length, duration, thumb, file_size) @@ -664,6 +637,7 @@ def __init__(self, file_id, length, duration, thumb=None, file_size=None): class Contact(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) phone_number = obj['phone_number'] first_name = obj['first_name'] @@ -681,6 +655,7 @@ def __init__(self, phone_number, first_name, last_name=None, user_id=None): class Location(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) longitude = obj['longitude'] latitude = obj['latitude'] @@ -711,6 +686,7 @@ def __init__(self, location, title, address, foursquare_id=None): class UserProfilePhotos(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) total_count = obj['total_count'] photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] @@ -764,7 +740,6 @@ def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, self.one_time_keyboard = one_time_keyboard self.selective = selective self.row_width = row_width - self.keyboard = [] def add(self, *args): @@ -818,13 +793,10 @@ def to_json(self): json_dict = {'keyboard': self.keyboard} if self.one_time_keyboard: json_dict['one_time_keyboard'] = True - if self.resize_keyboard: json_dict['resize_keyboard'] = True - if self.selective: json_dict['selective'] = True - return json.dumps(json_dict) @@ -849,7 +821,6 @@ def to_dic(self): class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): def __init__(self, row_width=3): self.row_width = row_width - self.keyboard = [] def add(self, *args): @@ -961,9 +932,7 @@ def de_json(cls, json_type): obj = cls.check_json(json_type) id = obj['id'] from_user = User.de_json(obj['from']) - message = None - if 'message' in obj: - message = Message.de_json(obj['message']) + message = Message.de_json(obj.get('message')) inline_message_id = obj.get('inline_message_id') chat_instance = obj['chat_instance'] data = obj.get('data') @@ -1046,9 +1015,7 @@ def de_json(cls, json_type): obj = cls.check_json(json_type) id = obj['id'] from_user = User.de_json(obj['from']) - location = None - if 'location' in obj: - location = Location.de_json(obj['location']) + location = Location.de_json(obj.get('location')) query = obj['query'] offset = obj['offset'] return cls(id, from_user, location, query, offset) @@ -1136,9 +1103,7 @@ def de_json(cls, json_type): result_id = obj['result_id'] from_user = User.de_json(obj['from']) query = obj['query'] - location = None - if 'location' in obj: - location = Location.de_json(obj['location']) + location = Location.de_json(obj.get('location')) inline_message_id = obj.get('inline_message_id') return cls(result_id, from_user, query, location, inline_message_id) @@ -1783,6 +1748,7 @@ def to_json(self): class Game(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) title = obj['title'] description = obj['description'] @@ -1791,9 +1757,7 @@ def de_json(cls, json_string): text_entities = None if 'text_entities' in obj: text_entities = Game.parse_entities(obj['text_entities']) - animation = None - if 'animation' in obj: - animation = Animation.de_json(obj['animation']) + animation = Animation.de_json(obj.get('animation')) return cls(title, description, photo, text, text_entities, animation) @classmethod @@ -1822,11 +1786,10 @@ def __init__(self, title, description, photo, text=None, text_entities=None, ani class Animation(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] - thumb = None - if 'thumb' in obj: - thumb = PhotoSize.de_json(obj['thumb']) + thumb = PhotoSize.de_json(obj.get('thumb')) file_name = obj.get('file_name') mime_type = obj.get('mime_type') file_size = obj.get('file_size') @@ -1843,6 +1806,7 @@ def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_siz class GameHighScore(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) position = obj['position'] user = User.de_json(obj['user']) @@ -1872,6 +1836,7 @@ def to_dic(self): class Invoice(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) title = obj['title'] description = obj['description'] @@ -1891,6 +1856,7 @@ def __init__(self, title, description, start_parameter, currency, total_amount): class ShippingAddress(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) country_code = obj['country_code'] state = obj['state'] @@ -1912,13 +1878,12 @@ def __init__(self, country_code, state, city, street_line1, street_line2, post_c class OrderInfo(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) name = obj.get('name') phone_number = obj.get('phone_number') email = obj.get('email') - shipping_address = None - if 'shipping_address' in obj: - shipping_address = ShippingAddress.de_json(obj['shipping_address']) + shipping_address = ShippingAddress.de_json(obj.get('shipping_address')) return cls(name, phone_number, email, shipping_address) def __init__(self, name, phone_number, email, shipping_address): @@ -1954,14 +1919,13 @@ def to_json(self): class SuccessfulPayment(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) currency = obj['currency'] total_amount = obj['total_amount'] invoice_payload = obj['invoice_payload'] shipping_option_id = obj.get('shipping_option_id') - order_info = None - if 'order_info' in obj: - order_info = OrderInfo.de_json(obj['order_info']) + order_info = OrderInfo.de_json(obj.get('order_info')) telegram_payment_charge_id = obj['telegram_payment_charge_id'] provider_payment_charge_id = obj['provider_payment_charge_id'] return cls(currency, total_amount, invoice_payload, shipping_option_id, order_info, @@ -1981,6 +1945,7 @@ def __init__(self, currency, total_amount, invoice_payload, shipping_option_id, class ShippingQuery(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) id = obj['id'] from_user = User.de_json(obj['from']) @@ -1998,6 +1963,7 @@ def __init__(self, id, from_user, invoice_payload, shipping_address): class PreCheckoutQuery(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) id = obj['id'] from_user = User.de_json(obj['from']) @@ -2005,9 +1971,7 @@ def de_json(cls, json_string): total_amount = obj['total_amount'] invoice_payload = obj['invoice_payload'] shipping_option_id = obj.get('shipping_option_id') - order_info = None - if 'order_info' in obj: - order_info = OrderInfo.de_json(obj['order_info']) + order_info = OrderInfo.de_json(obj.get('order_info')) return cls(id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info) def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info): @@ -2025,6 +1989,7 @@ def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipp class StickerSet(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) name = obj['name'] title = obj['title'] @@ -2044,19 +2009,16 @@ def __init__(self, name, title, contains_masks, stickers): class Sticker(JsonDeserializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] width = obj['width'] height = obj['height'] is_animated = obj['is_animated'] - thumb = None - if 'thumb' in obj: - thumb = PhotoSize.de_json(obj['thumb']) + thumb = PhotoSize.de_json(obj.get('thumb')) emoji = obj.get('emoji') set_name = obj.get('set_name') - mask_position = None - if 'mask_position' in obj: - mask_position = MaskPosition.de_json(obj['mask_position']) + mask_position = MaskPosition.de_json(obj.get('mask_position')) file_size = obj.get('file_size') return cls(file_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated) @@ -2074,6 +2036,7 @@ def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position class MaskPosition(JsonDeserializable, JsonSerializable): @classmethod def de_json(cls, json_string): + if (json_string is None): return None obj = cls.check_json(json_string) point = obj['point'] x_shift = obj['x_shift'] @@ -2249,9 +2212,7 @@ def de_json(cls, json_type): is_anonymous = obj['is_anonymous'] poll_type = obj['type'] allows_multiple_answers = obj['allows_multiple_answers'] - correct_option_id = None - if 'correct_option_id' in obj: - correct_option_id = obj['correct_option_id'] + correct_option_id = obj.get('correct_option_id') poll.id = poll_id poll.options = options poll.total_voter_count = total_voter_count From 247fe6e947d3f5a21804a1a25813d56dbb278446 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Apr 2020 17:06:14 +0300 Subject: [PATCH 0372/1808] Refactoring bugfix --- telebot/types.py | 52 +++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 60fc73ea8..8ac1b9a8a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -49,7 +49,7 @@ class JsonDeserializable(object): """ @classmethod - def de_json(cls, json_type): + def de_json(cls, json_string): """ Returns an instance of this class from the given json dict or string. @@ -86,8 +86,9 @@ def __str__(self): class Update(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) update_id = obj['update_id'] message = Message.de_json(obj.get('message')) edited_message = Message.de_json(obj.get('edited_message')) @@ -668,8 +669,9 @@ def __init__(self, longitude, latitude): class Venue(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) location = Location.de_json(obj['location']) title = obj['title'] address = obj['address'] @@ -699,8 +701,9 @@ def __init__(self, total_count, photos): class File(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) file_id = obj['file_id'] file_size = obj.get('file_size') file_path = obj.get('file_path') @@ -928,8 +931,9 @@ def to_dic(self): class CallbackQuery(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) id = obj['id'] from_user = User.de_json(obj['from']) message = Message.de_json(obj.get('message')) @@ -951,8 +955,9 @@ def __init__(self, id, from_user, data, chat_instance, message=None, inline_mess class ChatPhoto(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) small_file_id = obj['small_file_id'] big_file_id = obj['big_file_id'] return cls(small_file_id, big_file_id) @@ -964,8 +969,9 @@ def __init__(self, small_file_id, big_file_id): class ChatMember(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) user = User.de_json(obj['user']) status = obj['status'] until_date = obj.get('until_date') @@ -1011,8 +1017,9 @@ def __init__(self, user, status, until_date, can_be_edited, can_change_info, can class InlineQuery(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) id = obj['id'] from_user = User.de_json(obj['from']) location = Location.de_json(obj.get('location')) @@ -1098,8 +1105,9 @@ def to_dic(self): class ChosenInlineResult(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) result_id = obj['result_id'] from_user = User.de_json(obj['from']) query = obj['query'] @@ -2181,8 +2189,9 @@ def to_dic(self): class PollOption(JsonSerializable, JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) text = obj['text'] voter_count = int(obj['voter_count']) option = cls(text) @@ -2199,8 +2208,9 @@ def to_json(self): class Poll(JsonDeserializable): @classmethod - def de_json(cls, json_type): - obj = cls.check_json(json_type) + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) poll_id = obj['id'] question = obj['question'] poll = cls(question) From dab80d421b16101d48543e84cb589e043e992d1f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Apr 2020 17:38:47 +0300 Subject: [PATCH 0373/1808] Refactoring update 2 --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 8ac1b9a8a..ffa1232bf 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -99,7 +99,7 @@ def de_json(cls, json_string): callback_query = CallbackQuery.de_json(obj.get('callback_query')) shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) - poll = Poll.de_json(obj['poll']) + poll = Poll.de_json(obj.get('poll')) 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) From b912e4dbaf190362e8236c01f58d4ffeaf1be07a Mon Sep 17 00:00:00 2001 From: bedilbek Date: Sun, 12 Apr 2020 01:41:34 +0500 Subject: [PATCH 0374/1808] Update with middleware handler --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 9d78f0d18..22470a34b 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,24 @@ In bot2.0 update. You can get `callback_query` in update object. In telebot use def test_callback(call): logger.info(call) ``` +#### Middleware Handler + +A middleware handler is a function that allows you to modify requests or the bot context as they pass through the +Telegram to the bot. You can imagine middleware as a chain of logic connection handled before any other handlers are +executed. + +```python +@bot.middleware_handler(update_types=['message']) +def modify_message(bot_instance, message): + # modifying the message before it reaches any other handler + message.another_text = message.text + ':changed' + +@bot.message_handler(commands=['start']) +def start(message): + # the message is already modified when it reaches message handler + assert message.another_text == message.text + ':changed' +``` +There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory. #### TeleBot ```python From 68330c9a0708de1d07edf734db51a5f83e023ff1 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Sun, 12 Apr 2020 01:44:15 +0500 Subject: [PATCH 0375/1808] Update contents with middleware handler --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 22470a34b..7ecb129d7 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ * [General use of the API](#general-use-of-the-api) * [Message handlers](#message-handlers) * [Callback Query handlers](#callback-query-handler) + * [Middleware handlers](#middleware-handler) * [TeleBot](#telebot) * [Reply markup](#reply-markup) * [Inline Mode](#inline-mode) From 286188f380e98028d03a8fdb543296cca09be480 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Sun, 12 Apr 2020 22:41:32 +0500 Subject: [PATCH 0376/1808] Add Step/Reply Handler Backend Mechanism Implement Memory, File, Redis Backends --- requirements.txt | 1 + setup.py | 1 + telebot/__init__.py | 191 ++++++++++++++---------------------- telebot/handler_backends.py | 144 +++++++++++++++++++++++++++ 4 files changed, 217 insertions(+), 120 deletions(-) create mode 100644 telebot/handler_backends.py diff --git a/requirements.txt b/requirements.txt index 6e4ca4073..f58156ab2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ pytest==3.0.2 requests==2.20.0 six==1.9.0 wheel==0.24.0 +redis==3.4.1 diff --git a/setup.py b/setup.py index 44a5cde6a..7c7c856b9 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ def read(filename): install_requires=['requests', 'six'], extras_require={ 'json': 'ujson', + 'redis': 'redis>=3.4.1' }, classifiers=[ 'Development Status :: 5 - Production/Stable', diff --git a/telebot/__init__.py b/telebot/__init__.py index 72f6c7169..89b792a0a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2,8 +2,6 @@ from __future__ import print_function import logging -import os -import pickle import re import sys import threading @@ -23,6 +21,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, types, util +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend """ Module : telebot @@ -43,64 +42,6 @@ def __getitem__(self, item): return getattr(self, item) -class Saver: - """ - Class for saving (next step|reply) handlers - """ - - def __init__(self, handlers, filename, delay): - self.handlers = handlers - self.filename = filename - self.delay = delay - self.timer = threading.Timer(delay, self.save_handlers) - - def start_save_timer(self): - if not self.timer.is_alive(): - if self.delay <= 0: - self.save_handlers() - else: - self.timer = threading.Timer(self.delay, self.save_handlers) - self.timer.start() - - def save_handlers(self): - self.dump_handlers(self.handlers, self.filename) - - def load_handlers(self, filename, del_file_after_loading=True): - tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) - if tmp is not None: - self.handlers.update(tmp) - - @staticmethod - def dump_handlers(handlers, filename, file_mode="wb"): - dirs = filename.rsplit('/', maxsplit=1)[0] - os.makedirs(dirs, exist_ok=True) - - with open(filename + ".tmp", file_mode) as file: - if (apihelper.CUSTOM_SERIALIZER is None): - pickle.dump(handlers, file) - else: - apihelper.CUSTOM_SERIALIZER.dump(handlers, file) - - if os.path.isfile(filename): - os.remove(filename) - - os.rename(filename + ".tmp", filename) - - @staticmethod - def return_load_handlers(filename, del_file_after_loading=True): - if os.path.isfile(filename) and os.path.getsize(filename) > 0: - with open(filename, "rb") as file: - if (apihelper.CUSTOM_SERIALIZER is None): - handlers = pickle.load(file) - else: - handlers = apihelper.CUSTOM_SERIALIZER.load(file) - - if del_file_after_loading: - os.remove(filename) - - return handlers - - class TeleBot: """ This is TeleBot Class Methods: @@ -141,7 +82,10 @@ class TeleBot: answerInlineQuery """ - def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): + def __init__( + self, token, threaded=True, skip_pending=False, num_threads=2, + next_step_backend=None, reply_backend=None + ): """ :param token: bot API token :return: Telebot object. @@ -155,14 +99,13 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): self.last_update_id = 0 self.exc_info = None - # key: message_id, value: handler list - self.reply_handlers = {} + self.next_step_backend = next_step_backend + if not self.next_step_backend: + self.next_step_backend = MemoryHandlerBackend() - # key: chat_id, value: handler list - self.next_step_handlers = {} - - self.next_step_saver = None - self.reply_saver = None + self.reply_backend = reply_backend + if not self.reply_backend: + self.reply_backend = MemoryHandlerBackend() self.message_handlers = [] self.edited_message_handlers = [] @@ -196,51 +139,89 @@ def __init__(self, token, threaded=True, skip_pending=False, num_threads=2): def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): """ - Enable saving next step handlers (by default saving disable) + Enable saving next step handlers (by default saving disabled) + + This function explicitly assigns FileHandlerBackend (instead of Saver) just to keep backward + compatibility whose purpose was to enable file saving capability for handlers. And the same + implementation is now available with FileHandlerBackend + + Most probably this function should be deprecated in future major releases :param delay: Delay between changes in handlers and saving :param filename: Filename of save file """ - self.next_step_saver = Saver(self.next_step_handlers, filename, delay) + self.next_step_backend = FileHandlerBackend(self.next_step_backend.handlers, filename, delay) def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): """ Enable saving reply handlers (by default saving disable) + This function explicitly assigns FileHandlerBackend (instead of Saver) just to keep backward + compatibility whose purpose was to enable file saving capability for handlers. And the same + implementation is now available with FileHandlerBackend + + Most probably this function should be deprecated in future major releases + :param delay: Delay between changes in handlers and saving :param filename: Filename of save file """ - self.reply_saver = Saver(self.reply_handlers, filename, delay) + self.reply_backend = FileHandlerBackend(self.reply_backend.handlers, filename, delay) def disable_save_next_step_handlers(self): """ Disable saving next step handlers (by default saving disable) + + This function is left to keep backward compatibility whose purpose was to disable file saving capability + for handlers. For the same purpose, MemoryHandlerBackend is reassigned as a new next_step_backend backend + instead of FileHandlerBackend. + + Most probably this function should be deprecated in future major releases """ - self.next_step_saver = None + self.next_step_backend = MemoryHandlerBackend(self.next_step_backend.handlers) def disable_save_reply_handlers(self): """ Disable saving next step handlers (by default saving disable) + + This function is left to keep backward compatibility whose purpose was to disable file saving capability + for handlers. For the same purpose, MemoryHandlerBackend is reassigned as a new reply_backend backend + instead of FileHandlerBackend. + + Most probably this function should be deprecated in future major releases """ - self.reply_saver = None + self.reply_backend = MemoryHandlerBackend(self.reply_backend.handlers) def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): """ Load next step handlers from save file + This function is left to keep backward compatibility whose purpose was to load handlers from file with the + help of FileHandlerBackend and is only recommended to use if next_step_backend was assigned as + FileHandlerBackend before entering this function + + Most probably this function should be deprecated in future major releases + :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ - self.next_step_saver.load_handlers(filename, del_file_after_loading) + self.next_step_backend: FileHandlerBackend + self.next_step_backend.load_handlers(filename, del_file_after_loading) def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): """ Load reply handlers from save file + This function is left to keep backward compatibility whose purpose was to load handlers from file with the + help of FileHandlerBackend and is only recommended to use if reply_backend was assigned as + FileHandlerBackend before entering this function + + Most probably this function should be deprecated in future major releases + :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ - self.reply_saver.load_handlers(filename) + self.reply_backend: FileHandlerBackend + self.reply_backend.load_handlers(filename, del_file_after_loading) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) @@ -1399,12 +1380,7 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs :param callback: The callback function to be called when a reply arrives. Must accept one `message` parameter, which will contain the replied message. """ - if message_id in self.reply_handlers.keys(): - self.reply_handlers[message_id].append(Handler(callback, *args, **kwargs)) - else: - self.reply_handlers[message_id] = [Handler(callback, *args, **kwargs)] - if self.reply_saver is not None: - self.reply_saver.start_save_timer() + self.reply_backend.register_handler(message_id, Handler(callback, *args, **kwargs)) def _notify_reply_handlers(self, new_messages): """ @@ -1414,14 +1390,9 @@ def _notify_reply_handlers(self, new_messages): """ for message in new_messages: if hasattr(message, "reply_to_message") and message.reply_to_message is not None: - reply_msg_id = message.reply_to_message.message_id - if reply_msg_id in self.reply_handlers.keys(): - handlers = self.reply_handlers[reply_msg_id] - for handler in handlers: - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - self.reply_handlers.pop(reply_msg_id) - if self.reply_saver is not None: - self.reply_saver.start_save_timer() + handlers = self.reply_backend.get_handlers(message.reply_to_message.message_id) + for handler in handlers: + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) def register_next_step_handler(self, message, callback, *args, **kwargs): """ @@ -1448,13 +1419,7 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar :param args: Args to pass in callback func :param kwargs: Args to pass in callback func """ - if chat_id in self.next_step_handlers.keys(): - self.next_step_handlers[chat_id].append(Handler(callback, *args, **kwargs)) - else: - self.next_step_handlers[chat_id] = [Handler(callback, *args, **kwargs)] - - if self.next_step_saver is not None: - self.next_step_saver.start_save_timer() + self.next_step_backend.register_handler(chat_id, Handler(callback, *args, **kwargs)) def clear_step_handler(self, message): """ @@ -1471,10 +1436,7 @@ def clear_step_handler_by_chat_id(self, chat_id): :param chat_id: The chat for which we want to clear next step handlers """ - self.next_step_handlers[chat_id] = [] - - if self.next_step_saver is not None: - self.next_step_saver.start_save_timer() + self.next_step_backend.clear_handlers(chat_id) def clear_reply_handlers(self, message): """ @@ -1491,10 +1453,7 @@ def clear_reply_handlers_by_message_id(self, message_id): :param message_id: The message id for which we want to clear reply handlers """ - self.reply_handlers[message_id] = [] - - if self.reply_saver is not None: - self.reply_saver.start_save_timer() + self.reply_backend.clear_handlers(message_id) def _notify_next_handlers(self, new_messages): """ @@ -1502,22 +1461,14 @@ def _notify_next_handlers(self, new_messages): :param new_messages: :return: """ - i = 0 - while i < len(new_messages): - message = new_messages[i] - chat_id = message.chat.id - was_poped = False - if chat_id in self.next_step_handlers.keys(): - handlers = self.next_step_handlers.pop(chat_id, None) - if handlers: - for handler in handlers: - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - new_messages.pop(i) # removing message that detects with next_step_handler - was_poped = True - if self.next_step_saver is not None: - self.next_step_saver.start_save_timer() - if not was_poped: - i += 1 + for i, message in enumerate(new_messages): + need_pop = False + handlers = self.next_step_backend.get_handlers(message.chat.id) + for handler in handlers: + need_pop = True + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + if need_pop: + new_messages.pop(i) # removing message that detects with next_step_handler @staticmethod def _build_handler_dict(handler, **filters): diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py new file mode 100644 index 000000000..97dd44ff2 --- /dev/null +++ b/telebot/handler_backends.py @@ -0,0 +1,144 @@ +import os +import pickle +import threading + +from telebot import apihelper + + +class HandlerBackend: + """ + Class for saving (next step|reply) handlers + """ + handlers = {} + + def __init__(self, handlers=None): + if handlers: + self.handlers = handlers + + def register_handler(self, handler_group_id, handler): + raise NotImplementedError() + + def clear_handlers(self, handler_group_id): + raise NotImplementedError() + + def get_handlers(self, handler_group_id): + raise NotImplementedError() + + +class MemoryHandlerBackend(HandlerBackend): + def register_handler(self, handler_group_id, handler): + if handler_group_id in self.handlers: + self.handlers[handler_group_id].append(handler) + else: + self.handlers[handler_group_id] = [handler] + + def clear_handlers(self, handler_group_id): + self.handlers.pop(handler_group_id, []) + + def get_handlers(self, handler_group_id): + return self.handlers.pop(handler_group_id, []) + + +class FileHandlerBackend(HandlerBackend): + def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120): + super().__init__(handlers) + self.filename = filename + self.delay = delay + self.timer = threading.Timer(delay, self.save_handlers) + + def register_handler(self, handler_group_id, handler): + if handler_group_id in self.handlers: + self.handlers[handler_group_id].append(handler) + else: + self.handlers[handler_group_id] = [handler] + + self.start_save_timer() + + def clear_handlers(self, handler_group_id): + self.handlers.pop(handler_group_id, []) + + self.start_save_timer() + + def get_handlers(self, handler_group_id): + handlers = self.handlers.pop(handler_group_id, []) + + self.start_save_timer() + + return handlers + + def start_save_timer(self): + if not self.timer.is_alive(): + if self.delay <= 0: + self.save_handlers() + else: + self.timer = threading.Timer(self.delay, self.save_handlers) + self.timer.start() + + def save_handlers(self): + self.dump_handlers(self.handlers, self.filename) + + def load_handlers(self, filename, del_file_after_loading=True): + tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) + if tmp is not None: + self.handlers.update(tmp) + + @staticmethod + def dump_handlers(handlers, filename, file_mode="wb"): + dirs = filename.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + + with open(filename + ".tmp", file_mode) as file: + if (apihelper.CUSTOM_SERIALIZER is None): + pickle.dump(handlers, file) + else: + apihelper.CUSTOM_SERIALIZER.dump(handlers, file) + + if os.path.isfile(filename): + os.remove(filename) + + os.rename(filename + ".tmp", filename) + + @staticmethod + def return_load_handlers(filename, del_file_after_loading=True): + if os.path.isfile(filename) and os.path.getsize(filename) > 0: + with open(filename, "rb") as file: + if (apihelper.CUSTOM_SERIALIZER is None): + handlers = pickle.load(file) + else: + handlers = apihelper.CUSTOM_SERIALIZER.load(file) + + if del_file_after_loading: + os.remove(filename) + + return handlers + + +class RedisHandlerBackend(HandlerBackend): + def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot'): + super().__init__(handlers) + from redis import Redis + self.prefix = prefix + self.redis = Redis(host, port, db) + + def _key(self, handle_group_id): + return ':'.join((self.prefix, str(handle_group_id))) + + def register_handler(self, handler_group_id, handler): + handlers = [] + value = self.redis.get(self._key(handler_group_id)) + if value: + handlers = pickle.loads(value) + handlers.append(handler) + self.redis.set(self._key(handler_group_id), pickle.dumps(handlers)) + + def clear_handlers(self, handler_group_id): + self.redis.delete(self._key(handler_group_id)) + + def get_handlers(self, handler_group_id): + handlers = [] + value = self.redis.get(self._key(handler_group_id)) + if value: + handlers = pickle.loads(value) + self.clear_handlers(handler_group_id) + + return handlers From 003c5db37f0b5930f1fe8f042c0edd123ddbc2ff Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 01:45:19 +0500 Subject: [PATCH 0377/1808] Add filename checking --- telebot/handler_backends.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 97dd44ff2..cb6859418 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -77,7 +77,9 @@ def start_save_timer(self): def save_handlers(self): self.dump_handlers(self.handlers, self.filename) - def load_handlers(self, filename, del_file_after_loading=True): + def load_handlers(self, filename=None, del_file_after_loading=True): + if not filename: + filename = self.filename tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) if tmp is not None: self.handlers.update(tmp) From e7e7c5813318a2463c4aeb94a7699fe2fac7fb9a Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 01:45:52 +0500 Subject: [PATCH 0378/1808] Add Memory, File, Redis Backend tests --- tests/test_handler_backends.py | 261 +++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 tests/test_handler_backends.py diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py new file mode 100644 index 000000000..9f1811491 --- /dev/null +++ b/tests/test_handler_backends.py @@ -0,0 +1,261 @@ +import os +import time + +import pytest + +import telebot +from telebot import types, MemoryHandlerBackend, FileHandlerBackend +from telebot.handler_backends import RedisHandlerBackend + + +@pytest.fixture() +def telegram_bot(): + return telebot.TeleBot('', threaded=False) + + +@pytest.fixture +def private_chat(): + return types.Chat(id=11, type='private') + + +@pytest.fixture +def user(): + return types.User(id=10, is_bot=False, first_name='Some User') + + +@pytest.fixture() +def message(user, private_chat): + params = {'text': '/start'} + return types.Message( + message_id=1, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string="" + ) + + +@pytest.fixture() +def reply_to_message(user, private_chat, message): + params = {'text': '/start'} + reply_message = types.Message( + message_id=2, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string="" + ) + reply_message.reply_to_message = message + return reply_message + + +@pytest.fixture() +def update_type(message): + edited_message = None + channel_post = None + edited_channel_post = None + inline_query = None + chosen_inline_result = None + callback_query = None + shipping_query = None + pre_checkout_query = None + poll = None + return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query, + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + + +@pytest.fixture() +def reply_to_message_update_type(reply_to_message): + edited_message = None + channel_post = None + edited_channel_post = None + inline_query = None + chosen_inline_result = None + callback_query = None + shipping_query = None + pre_checkout_query = None + poll = None + return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post, + inline_query, + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + + +def next_handler(message): + message.text = 'entered next_handler' + + +def test_memory_handler_backend_default_backend(telegram_bot): + assert telegram_bot.reply_backend.__class__ == MemoryHandlerBackend + assert telegram_bot.next_step_backend.__class__ == MemoryHandlerBackend + + +def test_memory_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered next_handler' + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + +def test_memory_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.clear_step_handler_by_chat_id(private_chat.id) + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + +def test_memory_handler_backend_register_reply_handler(telegram_bot, private_chat, update_type, + reply_to_message_update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1 + + telegram_bot.process_new_updates([reply_to_message_update_type]) + assert reply_to_message_update_type.message.text == 'entered next_handler' + + assert private_chat.id not in telegram_bot.reply_backend.handlers + + +def test_memory_handler_backend_clear_reply_handler(telegram_bot, private_chat, update_type, + reply_to_message_update_type): + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1 + + telegram_bot.clear_reply_handlers_by_message_id(update_type.message.message_id) + + assert update_type.message.message_id not in telegram_bot.reply_backend.handlers + + telegram_bot.process_new_updates([reply_to_message_update_type]) + assert reply_to_message_update_type.message.text == 'entered start' + + +def test_file_handler_backend_register_next_step_handler(private_chat, update_type): + telegram_bot = telebot.TeleBot( + token='', + threaded=False, + next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=1) + ) + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + time.sleep(2) + + assert os.path.exists(telegram_bot.next_step_backend.filename) + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.next_step_backend.handlers = {} + + telegram_bot.next_step_backend.load_handlers() + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered next_handler' + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + time.sleep(2) + if os.path.exists(telegram_bot.next_step_backend.filename): + os.remove(telegram_bot.next_step_backend.filename) + + +def test_file_handler_backend_clear_next_step_handler(private_chat, update_type): + telegram_bot = telebot.TeleBot( + token='', + threaded=False, + next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=1) + ) + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 + + time.sleep(2) + + assert os.path.exists(telegram_bot.next_step_backend.filename) + + telegram_bot.clear_step_handler_by_chat_id(private_chat.id) + + time.sleep(2) + + telegram_bot.next_step_backend.load_handlers() + + assert private_chat.id not in telegram_bot.next_step_backend.handlers + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + time.sleep(2) + if os.path.exists(telegram_bot.next_step_backend.filename): + os.remove(telegram_bot.next_step_backend.filename) + + +def test_redis_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend1') + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered next_handler' + + +def test_redis_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend2') + + @telegram_bot.message_handler(commands=['start']) + def start(message): + message.text = 'entered start' + telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' + + telegram_bot.clear_step_handler_by_chat_id(private_chat.id) + + telegram_bot.process_new_updates([update_type]) + assert update_type.message.text == 'entered start' From 3aec66bc0dfbdd1aafe5616b0ba68f3a1492cd72 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 03:17:13 +0500 Subject: [PATCH 0379/1808] Remove class static variable --- telebot/handler_backends.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index cb6859418..cb6c688d3 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -9,11 +9,10 @@ class HandlerBackend: """ Class for saving (next step|reply) handlers """ - handlers = {} - def __init__(self, handlers=None): - if handlers: - self.handlers = handlers + if handlers is None: + handlers = {} + self.handlers = handlers def register_handler(self, handler_group_id, handler): raise NotImplementedError() From 0881e343819877ba79ad5dcf75452003cb6a9cda Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 13 Apr 2020 03:21:10 +0500 Subject: [PATCH 0380/1808] Refactor tests --- tests/test_handler_backends.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index 9f1811491..b16642e8c 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -1,3 +1,7 @@ +import sys + +sys.path.append('../') + import os import time @@ -155,12 +159,8 @@ def start(message): assert reply_to_message_update_type.message.text == 'entered start' -def test_file_handler_backend_register_next_step_handler(private_chat, update_type): - telegram_bot = telebot.TeleBot( - token='', - threaded=False, - next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=1) - ) +def test_file_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=0.1) @telegram_bot.message_handler(commands=['start']) def start(message): @@ -170,7 +170,7 @@ def start(message): telegram_bot.process_new_updates([update_type]) assert update_type.message.text == 'entered start' - time.sleep(2) + time.sleep(0.2) assert os.path.exists(telegram_bot.next_step_backend.filename) @@ -187,17 +187,13 @@ def start(message): assert private_chat.id not in telegram_bot.next_step_backend.handlers - time.sleep(2) + time.sleep(0.2) if os.path.exists(telegram_bot.next_step_backend.filename): os.remove(telegram_bot.next_step_backend.filename) -def test_file_handler_backend_clear_next_step_handler(private_chat, update_type): - telegram_bot = telebot.TeleBot( - token='', - threaded=False, - next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=1) - ) +def test_file_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): + telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=0.1) @telegram_bot.message_handler(commands=['start']) def start(message): @@ -209,13 +205,13 @@ def start(message): assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1 - time.sleep(2) + time.sleep(0.2) assert os.path.exists(telegram_bot.next_step_backend.filename) telegram_bot.clear_step_handler_by_chat_id(private_chat.id) - time.sleep(2) + time.sleep(0.2) telegram_bot.next_step_backend.load_handlers() @@ -224,7 +220,7 @@ def start(message): telegram_bot.process_new_updates([update_type]) assert update_type.message.text == 'entered start' - time.sleep(2) + time.sleep(0.2) if os.path.exists(telegram_bot.next_step_backend.filename): os.remove(telegram_bot.next_step_backend.filename) From 51b1fb7695c17fdd2113d8bef6c8c15eccc3c5dd Mon Sep 17 00:00:00 2001 From: dr_forse Date: Wed, 15 Apr 2020 06:10:05 +0100 Subject: [PATCH 0381/1808] added Dice and send_dice --- telebot/__init__.py | 18 ++++++++++++++++++ telebot/apihelper.py | 12 ++++++++++++ telebot/types.py | 22 ++++++++++++++++++++++ tests/test_telebot.py | 8 +++++++- tests/test_types.py | 8 ++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 72f6c7169..a38e02f4d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -117,6 +117,7 @@ class TeleBot: sendVideoNote sendLocation sendChatAction + sendDice getUserProfilePhotos getUpdates getFile @@ -665,6 +666,19 @@ def delete_message(self, chat_id, message_id): """ return apihelper.delete_message(self.token, chat_id, message_id) + def send_dice(self, chat_id, disable_notification=None, reply_to_message_id=None, reply_markup=None): + """ + Use this method to send dices. + :param chat_id: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :return: Message + """ + return types.Message.de_json( + apihelper.send_dice(self.token, chat_id, disable_notification, reply_to_message_id, reply_markup) + ) + def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None): """ @@ -1991,6 +2005,10 @@ def get_chat_member(self, *args): def send_message(self, *args, **kwargs): return TeleBot.send_message(self, *args, **kwargs) + @util.async_dec() + def send_dice(self, *args, **kwargs): + return TeleBot.send_dice(self, *args, **kwargs) + @util.async_dec() def forward_message(self, *args, **kwargs): return TeleBot.forward_message(self, *args, **kwargs) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9db62cc37..58a3e674e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -259,6 +259,18 @@ def forward_message(token, chat_id, from_chat_id, message_id, disable_notificati return _make_request(token, method_url, params=payload) +def send_dice(token, chat_id, disable_notification=None, reply_to_message_id=None, reply_markup=None): + method_url = r'sendDice' + payload = {'chat_id': chat_id} + if disable_notification: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + + def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None): method_url = r'sendPhoto' diff --git a/telebot/types.py b/telebot/types.py index ffa1232bf..89291111f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -293,6 +293,9 @@ def de_json(cls, json_string): if 'venue' in obj: opts['venue'] = Venue.de_json(obj['venue']) content_type = 'venue' + if 'dice' in obj: + opts['dice'] = Dice.de_json(obj['dice']) + content_type = 'dice' if 'new_chat_members' in obj: new_chat_members = [] for member in obj['new_chat_members']: @@ -397,6 +400,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.location = None self.venue = None self.animation = None + self.dice = None self.new_chat_member = None # Deprecated since Bot API 3.0. Not processed anymore self.new_chat_members = None self.left_chat_member = None @@ -511,6 +515,24 @@ def __init__(self, type, offset, length, url=None, user=None): self.user = user +class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + value = obj['value'] + return cls(value) + + def __init__(self, value): + self.value = value + + def to_json(self): + return json.dumps({'value': self.value}) + + def to_dic(self): + return {'value': self.value} + + class PhotoSize(JsonDeserializable): @classmethod def de_json(cls, json_string): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index b3fbfcf3b..7fe4c0fe1 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -11,7 +11,7 @@ from telebot import types from telebot import util -should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ +should_skip = False if not should_skip: TOKEN = os.environ['TOKEN'] @@ -241,6 +241,12 @@ def test_send_message(self): ret_msg = tb.send_message(CHAT_ID, text) assert ret_msg.message_id + def test_send_dice(self): + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_dice(CHAT_ID) + assert ret_msg.message_id + assert ret_msg.content_type == 'dice' + def test_send_message_dis_noti(self): text = 'CI Test Message' tb = telebot.TeleBot(TOKEN) diff --git a/tests/test_types.py b/tests/test_types.py index 590b080c7..d3bc80dec 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -17,6 +17,14 @@ def test_json_message(): assert msg.text == 'HIHI' +def test_json_message_with_dice(): + jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value":4}}' + msg = types.Message.de_json(jsonstring) + assert msg.content_type == 'dice' + assert isinstance(msg.dice, types.Dice) + assert msg.dice.value == 4 + + def test_json_message_group(): json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG","is_bot":true},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}' msg = types.Message.de_json(json_string) From 615402e4f811e87b9ef1ae616da48dffdca7974c Mon Sep 17 00:00:00 2001 From: dr_forse Date: Wed, 15 Apr 2020 06:16:07 +0100 Subject: [PATCH 0382/1808] return a line as it was --- tests/test_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 7fe4c0fe1..31a360f28 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -11,7 +11,7 @@ from telebot import types from telebot import util -should_skip = False +should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ if not should_skip: TOKEN = os.environ['TOKEN'] From aab560b4ee0b627ba19fb77a44f2666a5462c191 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 20 Apr 2020 11:30:03 +0500 Subject: [PATCH 0383/1808] Fix all the time invocations on typed_middleware handlers even if update did not have that update_type message --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index abcc273fe..d514ecea7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -380,7 +380,7 @@ def process_new_poll(self, polls): def process_middlewares(self, update): for update_type, middlewares in self.typed_middleware_handlers.items(): - if hasattr(update, update_type): + if getattr(update, update_type) is not None: for typed_middleware_handler in middlewares: typed_middleware_handler(self, getattr(update, update_type)) From da924dbaebffb48127e0a83547d9d41e12eb0df2 Mon Sep 17 00:00:00 2001 From: no_ideaw Date: Thu, 23 Apr 2020 23:59:04 +0430 Subject: [PATCH 0384/1808] Update __init__.py added can_invite_users parameter to restrict_chat_member function --- telebot/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d514ecea7..944d9ec84 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -930,7 +930,7 @@ def unban_chat_member(self, chat_id, user_id): def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=None, can_send_media_messages=None, can_send_other_messages=None, - can_add_web_page_previews=None): + can_add_web_page_previews=None, can_invite_users=None): """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -949,6 +949,8 @@ def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messa use inline bots, implies can_send_media_messages :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, implies can_send_media_messages + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, + implies can_invite_users :return: types.Message """ return apihelper.restrict_chat_member(self.token, chat_id, user_id, until_date, can_send_messages, From b1b2726ef661f6cb8a2bda7f0811aa0819e0f41a Mon Sep 17 00:00:00 2001 From: no_ideaw Date: Fri, 24 Apr 2020 00:21:05 +0430 Subject: [PATCH 0385/1808] Update apihelper.py added can_invite_users parameter to restrict_chat_member function --- telebot/apihelper.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 58a3e674e..8eb47447c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -569,7 +569,7 @@ def unban_chat_member(token, chat_id, user_id): def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_messages=None, can_send_media_messages=None, can_send_other_messages=None, - can_add_web_page_previews=None): + can_add_web_page_previews=None, can_invite_users=None): method_url = 'restrictChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if until_date: @@ -582,7 +582,9 @@ def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_mess payload['can_send_other_messages'] = can_send_other_messages if can_add_web_page_previews: payload['can_add_web_page_previews'] = can_add_web_page_previews - + if can_invite_users: + payload['can_invite_users'] = can_invite_users + return _make_request(token, method_url, params=payload, method='post') From 8c7c7b31b21348d3660a413f53c1f01d3013c41f Mon Sep 17 00:00:00 2001 From: no_ideaw Date: Fri, 24 Apr 2020 19:38:23 +0430 Subject: [PATCH 0386/1808] Update __init__.py added can_invite_users parameter to restrict_chat_member function --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d514ecea7..3d8e2179e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -930,7 +930,7 @@ def unban_chat_member(self, chat_id, user_id): def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=None, can_send_media_messages=None, can_send_other_messages=None, - can_add_web_page_previews=None): + can_add_web_page_previews=None, can_invite_users=None): """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -953,7 +953,7 @@ def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messa """ return apihelper.restrict_chat_member(self.token, chat_id, user_id, until_date, can_send_messages, can_send_media_messages, can_send_other_messages, - can_add_web_page_previews) + can_add_web_page_previews, can_invite_users) def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, From 24e984adf82fb6b804c520fa5b8d1db4aeab52f7 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 24 Apr 2020 18:19:30 +0300 Subject: [PATCH 0387/1808] Minor code refactoring (naming) --- telebot/__init__.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 72f6c7169..791a41cb8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -303,7 +303,7 @@ def __retrieve_updates(self, timeout=20): def process_new_updates(self, updates): new_messages = [] - edited_new_messages = [] + new_edited_messages = [] new_channel_posts = [] new_edited_channel_posts = [] new_inline_querys = [] @@ -314,7 +314,6 @@ def process_new_updates(self, updates): new_polls = [] for update in updates: - if apihelper.ENABLE_MIDDLEWARE: self.process_middlewares(update) @@ -323,7 +322,7 @@ def process_new_updates(self, updates): if update.message: new_messages.append(update.message) if update.edited_message: - edited_new_messages.append(update.edited_message) + new_edited_messages.append(update.edited_message) if update.channel_post: new_channel_posts.append(update.channel_post) if update.edited_channel_post: @@ -344,8 +343,8 @@ def process_new_updates(self, updates): logger.debug('Received {0} new updates'.format(len(updates))) if len(new_messages) > 0: self.process_new_messages(new_messages) - if len(edited_new_messages) > 0: - self.process_new_edited_messages(edited_new_messages) + if len(new_edited_messages) > 0: + self.process_new_edited_messages(new_edited_messages) if len(new_channel_posts) > 0: self.process_new_channel_posts(new_channel_posts) if len(new_edited_channel_posts) > 0: @@ -414,7 +413,7 @@ def infinity_polling(self, timeout=20, *args, **kwargs): while not self.__stop_polling.is_set(): try: self.polling(timeout=timeout, *args, **kwargs) - except Exception as e: + except Exception: time.sleep(timeout) pass logger.info("Break infinity polling") @@ -638,6 +637,7 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me :param reply_markup: :param parse_mode: :param disable_notification: Boolean, Optional. Sends the message silently. + :param timeout: :return: API reply. """ return types.Message.de_json( @@ -782,12 +782,14 @@ def send_animation(self, chat_id, animation, duration=None, caption=None, reply_ """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server + :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds :param caption: String : Animation caption (may also be used when resending animation by file_id). :param parse_mode: :param reply_to_message_id: :param reply_markup: + :param disable_notification: + :param timeout: :return: """ return types.Message.de_json( @@ -1598,11 +1600,12 @@ def command_help(message): def command_handle_document(message): bot.send_message(message.chat.id, 'Document received, sir!') - # Handle all other commands. + # Handle all other messages. @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', 'text', 'location', 'contact', 'sticker']) def default_command(message): bot.send_message(message.chat.id, "This is the default command handler.") + :param commands: Optional list of strings (commands to handle). :param regexp: Optional regular expression. :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param content_types: This commands' supported content types. Must be a list. Defaults to ['text']. @@ -1874,20 +1877,20 @@ def _test_message_handler(self, message_handler, message): :param message: :return: """ - for filter, filter_value in six.iteritems(message_handler['filters']): + for message_filter, filter_value in six.iteritems(message_handler['filters']): if filter_value is None: continue - if not self._test_filter(filter, filter_value, message): + if not self._test_filter(message_filter, filter_value, message): return False return True @staticmethod - def _test_filter(filter, filter_value, message): + def _test_filter(message_filter, filter_value, message): """ Test filters - :param filter: + :param message_filter: :param filter_value: :param message: :return: @@ -1899,7 +1902,7 @@ def _test_filter(filter, filter_value, message): 'func': lambda msg: filter_value(msg) } - return test_cases.get(filter, lambda msg: False)(message) + return test_cases.get(message_filter, lambda msg: False)(message) def _notify_command_handlers(self, handlers, new_messages): """ From 197dd2a582191ccf701fb4e3473e836e63b3125a Mon Sep 17 00:00:00 2001 From: Pavel Ermolaev Date: Fri, 24 Apr 2020 23:30:10 +0500 Subject: [PATCH 0388/1808] add requests session for use torpy --- telebot/apihelper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8eb47447c..1aaa536e7 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -20,6 +20,7 @@ logger = telebot.logger proxy = None +session = None API_URL = None FILE_URL = None @@ -33,7 +34,7 @@ def _get_req_session(reset=False): - return util.per_thread('req_session', lambda: requests.session(), reset) + return util.per_thread('req_session', lambda: session if session else requests.session(), reset) def _make_request(token, method_name, method='get', params=None, files=None): From bb4f6a71906382eb3f51f3c06e234835b687905d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Apr 2020 22:22:08 +0300 Subject: [PATCH 0389/1808] Polls are updated to the latest API state. Polls are updated to the latest API state. Minor code refactoring. --- telebot/__init__.py | 4 +- telebot/apihelper.py | 31 +++++++++++++-- telebot/types.py | 95 ++++++++++++++++++++++++++------------------ tests/test_types.py | 12 ++++++ 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ad3d00de0..d0dc4346f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1208,7 +1208,7 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to disable_notification, reply_to_message_id, reply_markup, provider_data) return types.Message.de_json(result) - def send_poll(self, chat_id, poll, disable_notifications=False, reply_to_message=None, reply_markup=None): + def send_poll(self, chat_id, poll, explanation_parse_mode=None, disable_notifications=False, reply_to_message=None, reply_markup=None): """ Sends poll :param chat_id: @@ -1218,7 +1218,7 @@ def send_poll(self, chat_id, poll, disable_notifications=False, reply_to_message :param reply_markup: :return: """ - return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll.question, poll.options, disable_notifications, reply_to_message, reply_markup)) + return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll, explanation_parse_mode, disable_notifications, reply_to_message, reply_markup)) def stop_poll(self, chat_id, message_id): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8eb47447c..c79bd7111 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -996,14 +996,37 @@ def delete_sticker_from_set(token, sticker): return _make_request(token, method_url, params=payload, method='post') -def send_poll(token, chat_id, question, options, disable_notifications=False, reply_to_message_id=None, reply_markup=None): +def send_poll(token, chat_id, poll, explanation_parse_mode=None, disable_notifications=False, reply_to_message_id=None, reply_markup=None): method_url = r'sendPoll' - payload = {'chat_id': str(chat_id), 'question': question, 'options': _convert_list_json_serializable(options)} + payload = { + 'chat_id': str(chat_id), + 'question': poll.question, + 'options': _convert_list_json_serializable(poll.options)} + + if poll.is_anonymous is not None: + payload['is_anonymous'] = poll.is_anonymous + if poll.type is not None: + payload['type'] = poll.type + if poll.allows_multiple_answers is not None: + payload['allows_multiple_answers'] = poll.allows_multiple_answers + if poll.correct_option_id is not None: + payload['correct_option_id'] = poll.correct_option_id + if poll.explanation is not None: + payload['explanation'] = poll.explanation + if explanation_parse_mode is not None: + payload['explanation_parse_mode'] = explanation_parse_mode + if poll.open_period is not None: + payload['open_period'] = poll.open_period + if poll.close_date is not None: + payload['close_date'] = poll.close_date + if poll.is_closed is not None: + payload['is_closed'] = poll.is_closed + if disable_notifications: payload['disable_notification'] = disable_notifications - if reply_to_message_id: + if reply_to_message_id is not None: payload['reply_to_message_id'] = reply_to_message_id - if reply_markup: + if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) diff --git a/telebot/types.py b/telebot/types.py index 89291111f..ee33ff274 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -34,10 +34,10 @@ class Dictionaryable(object): def to_dic(self): """ - Returns a JSON string representation of this class. + Returns a DICT with class field values This function must be overridden by subclasses. - :return: a JSON formatted string. + :return: a DICT """ raise NotImplementedError @@ -450,23 +450,23 @@ def __html_text(self, text, entities): "text_link": "{text}" } if hasattr(self, "custom_subs"): - for type in self.custom_subs: - _subs[type] = self.custom_subs[type] + for key, value in self.custom_subs.items(): + _subs[key] = value utf16_text = text.encode("utf-16-le") html_text = "" - def func(text, type=None, url=None, user=None): - text = text.decode("utf-16-le") - if type == "text_mention": - type = "url" + def func(upd_text, subst_type=None, url=None, user=None): + upd_text = upd_text.decode("utf-16-le") + if subst_type == "text_mention": + subst_type = "url" url = "tg://user?id={0}".format(user.id) - elif type == "mention": - url = "https://t.me/{0}".format(text[1:]) - text = text.replace("&", "&").replace("<", "<").replace(">", ">") - if not type or not _subs.get(type): - return text - subs = _subs.get(type) - return subs.format(text=text, url=url) + elif subst_type == "mention": + url = "https://t.me/{0}".format(upd_text[1:]) + upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") + if not subst_type or not _subs.get(subst_type): + return upd_text + subs = _subs.get(subst_type) + return subs.format(text=upd_text, url=url) offset = 0 for entity in entities: @@ -527,7 +527,7 @@ def __init__(self, value): self.value = value def to_json(self): - return json.dumps({'value': self.value}) + return json.dumps(self.to_dic()) def to_dic(self): return {'value': self.value} @@ -896,7 +896,7 @@ def to_dic(self): return json_dict -class LoginUrl(JsonSerializable): +class LoginUrl(Dictionaryable, JsonSerializable): def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None): self.url = url self.forward_text = forward_text @@ -917,7 +917,7 @@ def to_dic(self): return json_dic -class InlineKeyboardButton(JsonSerializable): +class InlineKeyboardButton(Dictionaryable, JsonSerializable): def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None): self.text = text @@ -2063,7 +2063,8 @@ def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position self.file_size = file_size self.is_animated = is_animated -class MaskPosition(JsonDeserializable, JsonSerializable): + +class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2089,7 +2090,7 @@ def to_dic(self): # InputMedia -class InputMedia(JsonSerializable): +class InputMedia(Dictionaryable, JsonSerializable): def __init__(self, type, media, caption=None, parse_mode=None): self.type = type self.media = media @@ -2216,15 +2217,14 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) text = obj['text'] voter_count = int(obj['voter_count']) - option = cls(text) - option.voter_count = voter_count - return option + return cls(text, voter_count) - def __init__(self, text): + def __init__(self, text, voter_count = 0): self.text = text - self.voter_count = 0 + self.voter_count = voter_count def to_json(self): + # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll return json.dumps(self.text) @@ -2235,7 +2235,6 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) poll_id = obj['id'] question = obj['question'] - poll = cls(question) options = [] for opt in obj['options']: options.append(PollOption.de_json(opt)) @@ -2245,19 +2244,39 @@ def de_json(cls, json_string): poll_type = obj['type'] allows_multiple_answers = obj['allows_multiple_answers'] correct_option_id = obj.get('correct_option_id') - poll.id = poll_id - poll.options = options - poll.total_voter_count = total_voter_count - poll.is_closed = is_closed - poll.is_anonymous = is_anonymous - poll.type = poll_type - poll.allows_multiple_answers = allows_multiple_answers - poll.correct_option_id = correct_option_id - return poll - - def __init__(self, question): - self.options = [] + explanation = obj.get('explanation') + if 'explanation_entities' in obj: + explanation_entities = Message.parse_entities(obj['explanation_entities']) + else: + explanation_entities = None + open_period = obj.get('open_period') + close_date = obj.get('close_date') + #poll = + return cls( + question, options, + poll_id, total_voter_count, is_closed, is_anonymous, poll_type, + allows_multiple_answers, correct_option_id, explanation, explanation_entities, + open_period, close_date) + + def __init__( + self, + question, options, + poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, + allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, + open_period=None, close_date=None): + self.id = poll_id self.question = question + self.options = options + self.total_voter_count = total_voter_count + self.is_closed = is_closed + self.is_anonymous = is_anonymous + self.type = poll_type + self.allows_multiple_answers = allows_multiple_answers + self.correct_option_id = correct_option_id + self.explanation = explanation + self.explanation_entities = explanation_entities if not(explanation_entities is None) else [] + self.open_period = open_period + self.close_date = close_date def add(self, option): if type(option) is PollOption: diff --git a/tests/test_types.py b/tests/test_types.py index d3bc80dec..e603960b2 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -166,3 +166,15 @@ def test_InlineQueryResultCachedPhoto_with_markup(): assert 'Title' in json_str assert 'caption' not in json_str assert 'reply_markup' in json_str + + +def test_json_poll_1(): + jsonstring = r'{"message_id": 395020,"from": {"id": 111,"is_bot": false,"first_name": "FN","last_name": "LN","username": "Badiboy","language_code": "ru"},"chat": {"id": 111,"first_name": "FN","last_name": "LN","username": "Badiboy","type": "private"},"date": 1587841239,"poll": {"id": "5272018969396510722","question": "Test poll 1","options": [{"text": "Answer 1","voter_count": 0},{"text": "Answer 2","voter_count": 0}],"total_voter_count": 0,"is_closed": false,"is_anonymous": true,"type": "regular","allows_multiple_answers": true}}' + msg = types.Message.de_json(jsonstring) + assert msg.poll is not None + assert isinstance(msg.poll, types.Poll) + assert msg.poll.id == '5272018969396510722' + assert msg.poll.question is not None + assert msg.poll.options is not None + assert len(msg.poll.options) == 2 + assert msg.poll.allows_multiple_answers == True From 99c63e9eba628f5e78c673c519b957f84949a974 Mon Sep 17 00:00:00 2001 From: dr_forse Date: Mon, 27 Apr 2020 06:30:05 +0100 Subject: [PATCH 0390/1808] add emoji field for dice --- telebot/__init__.py | 5 +++-- telebot/apihelper.py | 4 +++- telebot/types.py | 9 ++++++--- tests/test_telebot.py | 2 +- tests/test_types.py | 3 ++- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d0dc4346f..d9bee074f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -647,17 +647,18 @@ def delete_message(self, chat_id, message_id): """ return apihelper.delete_message(self.token, chat_id, message_id) - def send_dice(self, chat_id, disable_notification=None, reply_to_message_id=None, reply_markup=None): + def send_dice(self, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): """ Use this method to send dices. :param chat_id: + :param emoji: :param disable_notification: :param reply_to_message_id: :param reply_markup: :return: Message """ return types.Message.de_json( - apihelper.send_dice(self.token, chat_id, disable_notification, reply_to_message_id, reply_markup) + apihelper.send_dice(self.token, chat_id, emoji, disable_notification, reply_to_message_id, reply_markup) ) def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c79bd7111..8bda798e9 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -259,9 +259,11 @@ def forward_message(token, chat_id, from_chat_id, message_id, disable_notificati return _make_request(token, method_url, params=payload) -def send_dice(token, chat_id, disable_notification=None, reply_to_message_id=None, reply_markup=None): +def send_dice(token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): method_url = r'sendDice' payload = {'chat_id': chat_id} + if emoji: + payload['emoji'] = emoji if disable_notification: payload['disable_notification'] = disable_notification if reply_to_message_id: diff --git a/telebot/types.py b/telebot/types.py index ee33ff274..2e5a28f65 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -521,16 +521,19 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) value = obj['value'] - return cls(value) + emoji = obj['emoji'] + return cls(value, emoji) - def __init__(self, value): + def __init__(self, value, emoji): self.value = value + self.emoji = emoji def to_json(self): return json.dumps(self.to_dic()) def to_dic(self): - return {'value': self.value} + return {'value': self.value, + 'emoji': self.emoji} class PhotoSize(JsonDeserializable): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 31a360f28..101061f87 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -243,7 +243,7 @@ def test_send_message(self): def test_send_dice(self): tb = telebot.TeleBot(TOKEN) - ret_msg = tb.send_dice(CHAT_ID) + ret_msg = tb.send_dice(CHAT_ID, emoji='🎯') assert ret_msg.message_id assert ret_msg.content_type == 'dice' diff --git a/tests/test_types.py b/tests/test_types.py index e603960b2..dbdbecdf9 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -18,11 +18,12 @@ def test_json_message(): def test_json_message_with_dice(): - jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value":4}}' + jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value": 4, "emoji": "\ud83c\udfb2"}}' msg = types.Message.de_json(jsonstring) assert msg.content_type == 'dice' assert isinstance(msg.dice, types.Dice) assert msg.dice.value == 4 + assert msg.dice.emoji == '🎯' def test_json_message_group(): From be5d7bb73d937f2c6c2238a8ef3685dd536e3a69 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 27 Apr 2020 17:46:19 +0300 Subject: [PATCH 0391/1808] Disable REDIS tests to save Travis To enable REDIS set test_handler_backeds.REDIS_TEST = True before running tests. --- tests/test_handler_backends.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index b16642e8c..badc63466 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -2,6 +2,8 @@ sys.path.append('../') +REDIS_TESTS = False + import os import time @@ -9,7 +11,9 @@ import telebot from telebot import types, MemoryHandlerBackend, FileHandlerBackend -from telebot.handler_backends import RedisHandlerBackend + +if REDIS_TESTS: + from telebot.handler_backends import RedisHandlerBackend @pytest.fixture() @@ -226,6 +230,9 @@ def start(message): def test_redis_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): + if not(REDIS_TESTS): + return + telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend1') @telegram_bot.message_handler(commands=['start']) @@ -241,6 +248,9 @@ def start(message): def test_redis_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): + if not(REDIS_TESTS): + return + telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend2') @telegram_bot.message_handler(commands=['start']) From 9c87ed3679c986a40bb963f22f29f48e93dbf226 Mon Sep 17 00:00:00 2001 From: George Imedashvili Date: Mon, 27 Apr 2020 16:20:30 +0100 Subject: [PATCH 0392/1808] fix test --- tests/test_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_types.py b/tests/test_types.py index dbdbecdf9..8418a9fa1 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -18,7 +18,7 @@ def test_json_message(): def test_json_message_with_dice(): - jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value": 4, "emoji": "\ud83c\udfb2"}}' + jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value": 4, "emoji": "\ud83c\udfaf"}}' msg = types.Message.de_json(jsonstring) assert msg.content_type == 'dice' assert isinstance(msg.dice, types.Dice) From bcc3a1afb4697ced6c8b2e65027d6435301975c4 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Mon, 27 Apr 2020 23:43:39 +0500 Subject: [PATCH 0393/1808] Remove Type Hinting --- telebot/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d9bee074f..33d459d59 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -205,7 +205,6 @@ def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_fil :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ - self.next_step_backend: FileHandlerBackend self.next_step_backend.load_handlers(filename, del_file_after_loading) def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): @@ -221,7 +220,6 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ - self.reply_backend: FileHandlerBackend self.reply_backend.load_handlers(filename, del_file_after_loading) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): From f1a960c56b1ef998e7500bdf1a1da1b09ba0321e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 27 Apr 2020 21:56:37 +0300 Subject: [PATCH 0394/1808] Travis update: remove Python 2 --- .travis.yml | 2 -- README.md | 8 ++++++-- requirements.txt | 1 - setup.py | 2 -- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index b305175bc..2ce07115d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,9 @@ language: python python: - - "2.7" - "3.5" - "3.6" - "3.7" - "3.8" - - "pypy" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/README.md b/README.md index e7f8fb7ca..f8529b11b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ * [Using web hooks](#using-web-hooks) * [Logging](#logging) * [Proxy](#proxy) - * [New in library](#new-in-library) + * [API conformance](#api-conformance) + * [Change log](#сhange-log) * [F.A.Q.](#faq) * [Bot 2.0](#bot-20) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) @@ -553,7 +554,10 @@ _Checking is in progress..._ ## Change log -11.09.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking. +27.04.2020 - Poll and Dice are up to date. +Python2 conformance is not checked any more due to EOL. + +11.04.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking. 06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll diff --git a/requirements.txt b/requirements.txt index f58156ab2..6e4ca4073 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,3 @@ pytest==3.0.2 requests==2.20.0 six==1.9.0 wheel==0.24.0 -redis==3.4.1 diff --git a/setup.py b/setup.py index 7c7c856b9..3e6f6cee5 100644 --- a/setup.py +++ b/setup.py @@ -24,8 +24,6 @@ def read(filename): }, classifiers=[ 'Development Status :: 5 - Production/Stable', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Environment :: Console', 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', From 72d088940c4f7dfb46b6bdd8aa89eb43c494f8ee Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 27 Apr 2020 22:00:20 +0300 Subject: [PATCH 0395/1808] Readme content fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8529b11b..042e686ad 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ * [Logging](#logging) * [Proxy](#proxy) * [API conformance](#api-conformance) - * [Change log](#сhange-log) + * [Change log](#change-log) * [F.A.Q.](#faq) * [Bot 2.0](#bot-20) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) From 601b570b858d78331bcc5454cf860961fcfdcc57 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Tue, 28 Apr 2020 00:04:12 +0500 Subject: [PATCH 0396/1808] Fix python2.7 compatibility for class inheritance --- telebot/handler_backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index cb6c688d3..c53275241 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -40,7 +40,7 @@ def get_handlers(self, handler_group_id): class FileHandlerBackend(HandlerBackend): def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120): - super().__init__(handlers) + super(FileHandlerBackend, self).__init__(handlers) self.filename = filename self.delay = delay self.timer = threading.Timer(delay, self.save_handlers) @@ -116,7 +116,7 @@ def return_load_handlers(filename, del_file_after_loading=True): class RedisHandlerBackend(HandlerBackend): def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot'): - super().__init__(handlers) + super(RedisHandlerBackend, self).__init__(handlers) from redis import Redis self.prefix = prefix self.redis = Redis(host, port, db) From d7ebaa5bb30594bb7e0f655ac61bd1dad84f538e Mon Sep 17 00:00:00 2001 From: bedilbek Date: Tue, 28 Apr 2020 00:24:47 +0500 Subject: [PATCH 0397/1808] Fix importing dependencies --- tests/test_handler_backends.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index badc63466..6ed2c1869 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -10,7 +10,8 @@ import pytest import telebot -from telebot import types, MemoryHandlerBackend, FileHandlerBackend +from telebot import types +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend if REDIS_TESTS: from telebot.handler_backends import RedisHandlerBackend @@ -230,8 +231,8 @@ def start(message): def test_redis_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type): - if not(REDIS_TESTS): - return + if not REDIS_TESTS: + pytest.skip('please install redis and configure redis server, then enable REDIS_TESTS') telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend1') @@ -248,8 +249,8 @@ def start(message): def test_redis_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type): - if not(REDIS_TESTS): - return + if not REDIS_TESTS: + pytest.skip('please install redis and configure redis server, then enable REDIS_TESTS') telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend2') From 24deb8a51df999aeb6d2b3d6c05d1f3e231a6e66 Mon Sep 17 00:00:00 2001 From: bedilbek Date: Tue, 28 Apr 2020 00:34:52 +0500 Subject: [PATCH 0398/1808] Change class from new-style class to object class --- telebot/handler_backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index c53275241..e71fb2454 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -5,7 +5,7 @@ from telebot import apihelper -class HandlerBackend: +class HandlerBackend(object): """ Class for saving (next step|reply) handlers """ From 57fb8d2fad7b8993a7fc1d0d50e8a601d133c044 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Tue, 28 Apr 2020 19:18:44 +0800 Subject: [PATCH 0399/1808] Bump version. 3.7.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3e6f6cee5..71659896f 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(filename): return file.read() setup(name='pyTelegramBotAPI', - version='3.6.7', + version='3.7.0', description='Python Telegram bot api. ', long_description=read('README.md'), long_description_content_type="text/markdown", From ef81868ebc0f2f372b9d6936a1467133eff7b121 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 1 May 2020 11:25:51 +0300 Subject: [PATCH 0400/1808] Fix Deprecation warning due to invalid escape sequences --- telebot/util.py | 2 +- tests/test_telebot.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 21826d09a..31d2d3530 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -244,7 +244,7 @@ def extract_arguments(text): :param text: String to extract the arguments from a command :return: the arguments if `text` is a command (according to is_command), else None. """ - regexp = re.compile("/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) + regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) result = regexp.match(text) return result.group(2) if is_command(text) else None diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 101061f87..5d324dd1a 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -48,7 +48,7 @@ def test_message_handler_reg(self): bot = telebot.TeleBot('') msg = self.create_text_message(r'https://web.telegram.org/') - @bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') + @bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') def command_url(message): msg.text = 'got' @@ -84,7 +84,7 @@ def test_message_handler_reg_fail(self): bot = telebot.TeleBot('') msg = self.create_text_message(r'web.telegram.org/') - @bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') + @bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') def command_url(message): msg.text = 'got' From 6e1cf24946a8683cf3d91d6534c54d8e5aa022d6 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 2 May 2020 13:09:52 +0300 Subject: [PATCH 0401/1808] send_poll revised to standart signature --- README.md | 3 ++- telebot/__init__.py | 37 ++++++++++++++++++++++++++++++------ telebot/apihelper.py | 45 ++++++++++++++++++++++++-------------------- telebot/util.py | 20 +++++++++++++++++++- 4 files changed, 77 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 042e686ad..6fd8c5307 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@

A simple, but extensible Python implementation for the Telegram Bot API. -[![Download Month](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) +[![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) +[![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) * [Getting started.](#getting-started) diff --git a/telebot/__init__.py b/telebot/__init__.py index 33d459d59..c1d7511f1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1207,17 +1207,42 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to disable_notification, reply_to_message_id, reply_markup, provider_data) return types.Message.de_json(result) - def send_poll(self, chat_id, poll, explanation_parse_mode=None, disable_notifications=False, reply_to_message=None, reply_markup=None): - """ - Sends poll + def send_poll( + self, 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_notifications=False, reply_to_message_id=None, reply_markup=None): + """ + Send polls :param chat_id: - :param poll: + :param question: + :param options: + :param is_anonymous: + :param type: + :param allows_multiple_answers: + :param correct_option_id: + :param explanation: + :param explanation_parse_mode: + :param open_period: + :param close_date: + :param is_closed: :param disable_notifications: - :param reply_to_message: + :param reply_to_message_id: :param reply_markup: :return: """ - return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll, explanation_parse_mode, disable_notifications, reply_to_message, reply_markup)) + + if isinstance(question, types.Poll): + raise Exception("The send_poll signature was changed, please see send_poll function details.") + + return types.Message.de_json( + apihelper.send_poll( + self.token, chat_id, + question, options, + is_anonymous, type, allows_multiple_answers, correct_option_id, + explanation, explanation_parse_mode, open_period, close_date, is_closed, + disable_notifications, reply_to_message_id, reply_markup)) def stop_poll(self, chat_id, message_id): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8bda798e9..e59515a48 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -998,31 +998,36 @@ def delete_sticker_from_set(token, sticker): return _make_request(token, method_url, params=payload, method='post') -def send_poll(token, chat_id, poll, explanation_parse_mode=None, disable_notifications=False, reply_to_message_id=None, reply_markup=None): +def send_poll( + 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_notifications=False, reply_to_message_id=None, reply_markup=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), - 'question': poll.question, - 'options': _convert_list_json_serializable(poll.options)} - - if poll.is_anonymous is not None: - payload['is_anonymous'] = poll.is_anonymous - if poll.type is not None: - payload['type'] = poll.type - if poll.allows_multiple_answers is not None: - payload['allows_multiple_answers'] = poll.allows_multiple_answers - if poll.correct_option_id is not None: - payload['correct_option_id'] = poll.correct_option_id - if poll.explanation is not None: - payload['explanation'] = poll.explanation + 'question': question, + 'options': _convert_list_json_serializable(options)} + + if is_anonymous is not None: + payload['is_anonymous'] = is_anonymous + if type is not None: + 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: + payload['explanation'] = explanation if explanation_parse_mode is not None: payload['explanation_parse_mode'] = explanation_parse_mode - if poll.open_period is not None: - payload['open_period'] = poll.open_period - if poll.close_date is not None: - payload['close_date'] = poll.close_date - if poll.is_closed is not None: - payload['is_closed'] = poll.is_closed + if open_period is not None: + payload['open_period'] = open_period + if close_date is not None: + payload['close_date'] = close_date + if is_closed is not None: + payload['is_closed'] = is_closed if disable_notifications: payload['disable_notification'] = disable_notifications diff --git a/telebot/util.py b/telebot/util.py index 31d2d3530..9e97ad3ac 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -5,6 +5,8 @@ import sys import threading import traceback +import warnings +import functools import six from six import string_types @@ -258,4 +260,20 @@ def per_thread(key, construct_value, reset=False): def generate_random_token(): - return ''.join(random.sample(string.ascii_letters, 16)) \ No newline at end of file + return ''.join(random.sample(string.ascii_letters, 16)) + + +def deprecated(func): + """This is a decorator which can be used to mark functions + as deprecated. It will result in a warning being emitted + when the function is used.""" + # https://stackoverflow.com/a/30253848/441814 + @functools.wraps(func) + def new_func(*args, **kwargs): + warnings.simplefilter('always', DeprecationWarning) # turn off filter + warnings.warn("Call to deprecated function {}.".format(func.__name__), + category=DeprecationWarning, + stacklevel=2) + warnings.simplefilter('default', DeprecationWarning) # reset filter + return func(*args, **kwargs) + return new_func From e2d70da69408b1bade9e4621dbb6366582f4a3bd Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 2 May 2020 13:27:39 +0300 Subject: [PATCH 0402/1808] Fix poll options serialization --- telebot/__init__.py | 2 +- telebot/apihelper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c1d7511f1..e1d13f871 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1217,7 +1217,7 @@ def send_poll( Send polls :param chat_id: :param question: - :param options: + :param options: array of str with answers :param is_anonymous: :param type: :param allows_multiple_answers: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e59515a48..e171a1c76 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1008,7 +1008,7 @@ def send_poll( payload = { 'chat_id': str(chat_id), 'question': question, - 'options': _convert_list_json_serializable(options)} + 'options': json.dumps(options)} if is_anonymous is not None: payload['is_anonymous'] = is_anonymous From 6c455116052bc7666c380fc94ee433624ff5aaa9 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Sat, 2 May 2020 19:41:46 +0800 Subject: [PATCH 0403/1808] Update version 3.7.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 71659896f..05b07606f 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(filename): return file.read() setup(name='pyTelegramBotAPI', - version='3.7.0', + version='3.7.1', description='Python Telegram bot api. ', long_description=read('README.md'), long_description_content_type="text/markdown", From 5d388f7ec43487ced41b67d1d09691d708786a14 Mon Sep 17 00:00:00 2001 From: CSRedRat Date: Thu, 7 May 2020 19:56:17 +0500 Subject: [PATCH 0404/1808] Add handler decorator example --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6fd8c5307..f1bb9dcc3 100644 --- a/README.md +++ b/README.md @@ -207,15 +207,15 @@ def send_something(message): #### Edited Message handlers -Same as Message handlers +@bot.edited_message_handler(filters) #### channel_post_handler -Same as Message handlers +@bot.channel_post_handler(filters) #### edited_channel_post_handler -Same as Message handlers +@bot.edited_channel_post_handler(filters) #### Callback Query Handler From a56fb8cc54a1490258171e8922c1cee59bc7b336 Mon Sep 17 00:00:00 2001 From: Anthony Byuraev Date: Fri, 8 May 2020 21:06:39 +0300 Subject: [PATCH 0405/1808] UPG: Add BotCommand, setMyCommands --- .gitignore | 5 +- telebot/__init__.py | 10 ++++ telebot/apihelper.py | 6 +++ telebot/types.py | 109 +++++++++++++++++++++++++++---------------- 4 files changed, 90 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index e821e9eaf..dd11917ad 100644 --- a/.gitignore +++ b/.gitignore @@ -58,4 +58,7 @@ docs/_build/ # PyBuilder target/ -testMain.py \ No newline at end of file +testMain.py + +#VS Code +.vscode/ diff --git a/telebot/__init__.py b/telebot/__init__.py index e1d13f871..d67d40af1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -80,6 +80,7 @@ class TeleBot: getChatMembersCount getChatMember answerCallbackQuery + setMyCommands answerInlineQuery """ @@ -1022,6 +1023,15 @@ def delete_chat_photo(self, chat_id): """ return apihelper.delete_chat_photo(self.token, chat_id) + def set_my_commands(self, commands): + """ + Use this method to change the list of the bot's commands. + :param commands: Array of BotCommand. A JSON-serialized list of bot commands + to be set as the list of the bot's commands. At most 100 commands can be specified. + :return: + """ + return apihelper.set_my_commands(self.token, commands) + def set_chat_title(self, chat_id, title): """ Use this method to change the title of a chat. Titles can't be changed for private chats. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e171a1c76..958f83e20 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -643,6 +643,12 @@ def set_chat_title(token, chat_id, title): return _make_request(token, method_url, params=payload, method='post') +def set_my_commands(token, commands): + method_url = r'setMyCommands' + payload = {'commands': _convert_list_json_serializable(commands)} + return _make_request(token, method_url, params=payload, method='post') + + def set_chat_description(token, chat_id, description): method_url = 'setChatDescription' payload = {'chat_id': chat_id, 'description': description} diff --git a/telebot/types.py b/telebot/types.py index 2e5a28f65..cb3a191cf 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -523,14 +523,14 @@ def de_json(cls, json_string): value = obj['value'] emoji = obj['emoji'] return cls(value, emoji) - + def __init__(self, value, emoji): self.value = value self.emoji = emoji - + def to_json(self): return json.dumps(self.to_dic()) - + def to_dic(self): return {'value': self.value, 'emoji': self.emoji} @@ -619,7 +619,8 @@ def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_siz class Video(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) file_id = obj['file_id'] width = obj['width'] @@ -643,7 +644,8 @@ def __init__(self, file_id, width, height, duration, thumb=None, mime_type=None, class VideoNote(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) file_id = obj['file_id'] length = obj['length'] @@ -663,7 +665,8 @@ def __init__(self, file_id, length, duration, thumb=None, file_size=None): class Contact(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) phone_number = obj['phone_number'] first_name = obj['first_name'] @@ -681,7 +684,8 @@ def __init__(self, phone_number, first_name, last_name=None, user_id=None): class Location(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) longitude = obj['longitude'] latitude = obj['latitude'] @@ -695,7 +699,8 @@ def __init__(self, longitude, latitude): class Venue(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) location = Location.de_json(obj['location']) title = obj['title'] @@ -713,7 +718,8 @@ def __init__(self, location, title, address, foursquare_id=None): class UserProfilePhotos(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) total_count = obj['total_count'] photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] @@ -727,7 +733,8 @@ def __init__(self, total_count, photos): class File(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) file_id = obj['file_id'] file_size = obj.get('file_size') @@ -835,15 +842,15 @@ def __init__(self, text, request_contact=None, request_location=None): self.request_location = request_location def to_json(self): - return json.dumps(self.to_dic()) + return json.dumps(self.to_dict()) - def to_dic(self): - json_dic = {'text': self.text} + def to_dict(self): + json_dict = {'text': self.text} if self.request_contact: - json_dic['request_contact'] = self.request_contact + json_dict['request_contact'] = self.request_contact if self.request_location: - json_dic['request_location'] = self.request_location - return json_dic + json_dict['request_location'] = self.request_location + return json_dict class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): @@ -894,7 +901,7 @@ def to_json(self): json_dict = {'inline_keyboard': self.keyboard} return json.dumps(json_dict) - def to_dic(self): + def to_dict(self): json_dict = {'inline_keyboard': self.keyboard} return json_dict @@ -907,17 +914,17 @@ def __init__(self, url, forward_text=None, bot_username=None, request_write_acce self.request_write_access = request_write_access def to_json(self): - return json.dumps(self.to_dic()) + return json.dumps(self.to_dict()) - def to_dic(self): - json_dic = {'url': self.url} + def to_dict(self): + json_dict = {'url': self.url} if self.forward_text: - json_dic['forward_text'] = self.forward_text + json_dict['forward_text'] = self.forward_text if self.bot_username: - json_dic['bot_username'] = self.bot_username + json_dict['bot_username'] = self.bot_username if self.request_write_access: - json_dic['request_write_access'] = self.request_write_access - return json_dic + json_dict['request_write_access'] = self.request_write_access + return json_dict class InlineKeyboardButton(Dictionaryable, JsonSerializable): @@ -933,25 +940,25 @@ def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, self.login_url = login_url def to_json(self): - return json.dumps(self.to_dic()) + return json.dumps(self.to_dict()) - def to_dic(self): - json_dic = {'text': self.text} + def to_dict(self): + json_dict = {'text': self.text} if self.url: - json_dic['url'] = self.url + json_dict['url'] = self.url if self.callback_data: - json_dic['callback_data'] = self.callback_data + json_dict['callback_data'] = self.callback_data if self.switch_inline_query is not None: - json_dic['switch_inline_query'] = self.switch_inline_query + json_dict['switch_inline_query'] = self.switch_inline_query if self.switch_inline_query_current_chat is not None: - json_dic['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat + json_dict['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat if self.callback_game is not None: - json_dic['callback_game'] = self.callback_game + json_dict['callback_game'] = self.callback_game if self.pay is not None: - json_dic['pay'] = self.pay + json_dict['pay'] = self.pay if self.login_url is not None: - json_dic['login_url'] = self.login_url.to_dic() - return json_dic + json_dict['login_url'] = self.login_url.to_dict() + return json_dict class CallbackQuery(JsonDeserializable): @@ -981,7 +988,8 @@ def __init__(self, id, from_user, data, chat_instance, message=None, inline_mess class ChatPhoto(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) small_file_id = obj['small_file_id'] big_file_id = obj['big_file_id'] @@ -995,7 +1003,8 @@ def __init__(self, small_file_id, big_file_id): class ChatMember(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) user = User.de_json(obj['user']) status = obj['status'] @@ -1038,12 +1047,33 @@ def __init__(self, user, status, until_date, can_be_edited, can_change_info, can self.can_add_web_page_previews = can_add_web_page_previews +class BotCommand(JsonSerializable): + def __init__(self, command, description): + """ + This object represents a bot command. + :param command: Text of the command, 1-32 characters. + Can contain only lowercase English letters, digits and underscores. + :param description: Description of the command, 3-256 characters. + :return: + """ + self.command = command + self.description = description + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'command': self.command, 'description': self.description} + return json_dict + + # InlineQuery class InlineQuery(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) id = obj['id'] from_user = User.de_json(obj['from']) @@ -1131,7 +1161,8 @@ def to_dic(self): class ChosenInlineResult(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) result_id = obj['result_id'] from_user = User.de_json(obj['from']) From 2849e67029711828470620cf3553ed06a352c037 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 9 May 2020 00:51:18 +0300 Subject: [PATCH 0406/1808] Fix to_dic->to_dict refactoring --- telebot/apihelper.py | 2 +- telebot/types.py | 146 ++++++++++++++++++++++--------------------- 2 files changed, 76 insertions(+), 72 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 958f83e20..97a7a668e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1079,7 +1079,7 @@ def _convert_input_media_array(array): files = {} for input_media in array: if isinstance(input_media, types.InputMedia): - media_dict = input_media.to_dic() + media_dict = input_media.to_dict() if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media diff --git a/telebot/types.py b/telebot/types.py index cb3a191cf..f2b3dea48 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -29,10 +29,10 @@ def to_json(self): class Dictionaryable(object): """ Subclasses of this class are guaranteed to be able to be converted to dictionary. - All subclasses of this class must override to_dic. + All subclasses of this class must override to_dict. """ - def to_dic(self): + def to_dict(self): """ Returns a DICT with class field values @@ -529,9 +529,9 @@ def __init__(self, value, emoji): self.emoji = emoji def to_json(self): - return json.dumps(self.to_dic()) + return json.dumps(self.to_dict()) - def to_dic(self): + def to_dict(self): return {'value': self.value, 'emoji': self.emoji} @@ -794,7 +794,7 @@ def add(self, *args): elif isinstance(button, bytes): row.append({'text': button.decode('utf-8')}) else: - row.append(button.to_dic()) + row.append(button.to_dict()) if i % self.row_width == 0: self.keyboard.append(row) row = [] @@ -815,7 +815,7 @@ def row(self, *args): if util.is_string(button): btn_array.append({'text': button}) else: - btn_array.append(button.to_dic()) + btn_array.append(button.to_dict()) self.keyboard.append(btn_array) return self @@ -870,7 +870,7 @@ def add(self, *args): i = 1 row = [] for button in args: - row.append(button.to_dic()) + row.append(button.to_dict()) if i % self.row_width == 0: self.keyboard.append(row) row = [] @@ -888,7 +888,7 @@ def row(self, *args): """ btn_array = [] for button in args: - btn_array.append(button.to_dic()) + btn_array.append(button.to_dict()) self.keyboard.append(btn_array) return self @@ -1063,8 +1063,7 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - json_dict = {'command': self.command, 'description': self.description} - return json_dict + return {'command': self.command, 'description': self.description} # InlineQuery @@ -1107,7 +1106,7 @@ def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None) self.parse_mode = parse_mode self.disable_web_page_preview = disable_web_page_preview - def to_dic(self): + def to_dict(self): json_dic = {'message_text': self.message_text} if self.parse_mode: json_dic['parse_mode'] = self.parse_mode @@ -1122,7 +1121,7 @@ def __init__(self, latitude, longitude, live_period=None): self.longitude = longitude self.live_period = live_period - def to_dic(self): + def to_dict(self): json_dic = {'latitude': self.latitude, 'longitude': self.longitude} if self.live_period: json_dic['live_period'] = self.live_period @@ -1137,12 +1136,16 @@ def __init__(self, latitude, longitude, title, address, foursquare_id=None): self.address = address self.foursquare_id = foursquare_id - def to_dic(self): - json_dic = {'latitude': self.latitude, 'longitude': self.longitude, 'title': self.title, - 'address' : self.address} + def to_dict(self): + json_dict = { + 'latitude': self.latitude, + 'longitude': self.longitude, + 'title': self.title, + 'address' : self.address + } if self.foursquare_id: - json_dic['foursquare_id'] = self.foursquare_id - return json_dic + json_dict['foursquare_id'] = self.foursquare_id + return json_dict class InputContactMessageContent(Dictionaryable): @@ -1151,11 +1154,11 @@ def __init__(self, phone_number, first_name, last_name=None): self.first_name = first_name self.last_name = last_name - def to_dic(self): - json_dic = {'phone_numbe': self.phone_number, 'first_name': self.first_name} + def to_dict(self): + json_dict = {'phone_numbe': self.phone_number, 'first_name': self.first_name} if self.last_name: - json_dic['last_name'] = self.last_name - return json_dic + json_dict['last_name'] = self.last_name + return json_dict class ChosenInlineResult(JsonDeserializable): @@ -1217,10 +1220,13 @@ def __init__(self, id, title, input_message_content, reply_markup=None, url=None 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_dic()} + 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_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.url: json_dict['url'] = self.url if self.hide_url: @@ -1283,9 +1289,9 @@ def to_json(self): if self.parse_mode: json_dict['parse_mode'] = self.parse_mode if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1328,9 +1334,9 @@ def to_json(self): if self.caption: json_dict['caption'] = self.caption if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() if self.gif_duration: json_dict['gif_duration'] = self.gif_duration return json.dumps(json_dict) @@ -1380,9 +1386,9 @@ def to_json(self): if self.parse_mode: json_dict['parse_mode'] = self.parse_mode if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() if self.mpeg4_duration: json_dict['mpeg4_duration '] = self.mpeg4_duration return json.dumps(json_dict) @@ -1438,9 +1444,9 @@ def to_json(self): if self.parse_mode: json_dict['parse_mode'] = self.parse_mode if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1469,9 +1475,9 @@ def to_json(self): if self.audio_duration: json_dict['audio_duration'] = self.audio_duration if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1500,9 +1506,9 @@ def to_json(self): if self.voice_duration: json_dict['voice_duration'] = self.voice_duration if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1539,9 +1545,9 @@ def to_json(self): if self.thumb_height: json_dict['thumb_height'] = self.thumb_height if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1572,9 +1578,9 @@ def to_json(self): if self.thumb_height: json_dict['thumb_height'] = self.thumb_height if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1606,9 +1612,9 @@ def to_json(self): if self.thumb_height: json_dict['thumb_height'] = self.thumb_height if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1637,9 +1643,9 @@ def to_json(self): if self.thumb_height: json_dict['thumb_height'] = self.thumb_height if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() return json.dumps(json_dict) @@ -1666,9 +1672,9 @@ def to_json(self): if self.caption: json_dict['caption'] = self.caption if self.reply_markup: - json_dict['reply_markup'] = self.reply_markup.to_dic() + json_dict['reply_markup'] = self.reply_markup.to_dict() if self.input_message_content: - json_dict['input_message_content'] = self.input_message_content.to_dic() + json_dict['input_message_content'] = self.input_message_content.to_dict() if self.parse_mode: json_dict['parse_mode'] = self.parse_mode return json.dumps(json_dict) @@ -1805,7 +1811,7 @@ def __init__(self, id, game_short_name, reply_markup=None): 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_dic() + json_dic['reply_markup'] = self.reply_markup.to_dict() return json.dumps(json_dic) @@ -1891,10 +1897,9 @@ def __init__(self, label, amount): self.amount = amount def to_json(self): - return json.dumps(self.to_dic()) - - def to_dic(self): - return {'label': self.label, 'amount': self.amount} + return json.dumps({ + 'label': self.label, 'amount': self.amount + }) class Invoice(JsonDeserializable): @@ -1975,7 +1980,7 @@ def add_price(self, *args): def to_json(self): price_list = [] for p in self.prices: - price_list.append(p.to_dic()) + price_list.append(p.to_dict()) json_dict = json.dumps({'id': self.id, 'title': self.title, 'prices': price_list}) return json_dict @@ -2116,9 +2121,9 @@ def __init__(self, point, x_shift, y_shift, scale): self.scale = scale def to_json(self): - return json.dumps(self.to_dic()) + return json.dumps(self.to_dict()) - def to_dic(self): + def to_dict(self): return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale} @@ -2139,15 +2144,15 @@ def __init__(self, type, media, caption=None, parse_mode=None): self._media_dic = 'attach://{0}'.format(self._media_name) def to_json(self): - return json.dumps(self.to_dic()) + return json.dumps(self.to_dict()) - def to_dic(self): - ret = {'type': self.type, 'media': self._media_dic} + def to_dict(self): + json_dict = {'type': self.type, 'media': self._media_dic} if self.caption: - ret['caption'] = self.caption + json_dict['caption'] = self.caption if self.parse_mode: - ret['parse_mode'] = self.parse_mode - return ret + json_dict['parse_mode'] = self.parse_mode + return json_dict def _convert_input_media(self): if util.is_string(self.media): @@ -2160,9 +2165,8 @@ class InputMediaPhoto(InputMedia): def __init__(self, media, caption=None, parse_mode=None): super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) - def to_dic(self): - ret = super(InputMediaPhoto, self).to_dic() - return ret + def to_dict(self): + return super(InputMediaPhoto, self).to_dict() class InputMediaVideo(InputMedia): @@ -2175,8 +2179,8 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, self.duration = duration self.supports_streaming = supports_streaming - def to_dic(self): - ret = super(InputMediaVideo, self).to_dic() + def to_dict(self): + ret = super(InputMediaVideo, self).to_dict() if self.thumb: ret['thumb'] = self.thumb if self.width: @@ -2198,8 +2202,8 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, self.height = height self.duration = duration - def to_dic(self): - ret = super(InputMediaAnimation, self).to_dic() + def to_dict(self): + ret = super(InputMediaAnimation, self).to_dict() if self.thumb: ret['thumb'] = self.thumb if self.width: @@ -2219,8 +2223,8 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=No self.performer = performer self.title = title - def to_dic(self): - ret = super(InputMediaAudio, self).to_dic() + def to_dict(self): + ret = super(InputMediaAudio, self).to_dict() if self.thumb: ret['thumb'] = self.thumb if self.duration: @@ -2237,8 +2241,8 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None): super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb - def to_dic(self): - ret = super(InputMediaDocument, self).to_dic() + def to_dict(self): + ret = super(InputMediaDocument, self).to_dict() if self.thumb: ret['thumb'] = self.thumb return ret From 1824637617c22a977a4ca391e42f9913bca25871 Mon Sep 17 00:00:00 2001 From: Anthony Byuraev Date: Sat, 9 May 2020 20:06:33 +0300 Subject: [PATCH 0407/1808] UPG: Refactoring InlineKeyboardMarkup --- telebot/types.py | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f2b3dea48..bfe6c772f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -855,17 +855,27 @@ def to_dict(self): class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): def __init__(self, row_width=3): + """ + This object represents an inline keyboard that appears + right next to the message it belongs to. + + :return: + """ self.row_width = row_width self.keyboard = [] def add(self, *args): """ - This function adds strings to the keyboard, while not exceeding row_width. - E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} + This method adds buttons to the keyboard without exceeding row_width. + + E.g. InlineKeyboardMarkup#add("A", "B", "C") yields the json result: + {keyboard: [["A"], ["B"], ["C"]]} when row_width is set to 1. - When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} - See https://core.telegram.org/bots/api#replykeyboardmarkup - :param args: KeyboardButton to append to the keyboard + When row_width is set to 2, the result: + {keyboard: [["A", "B"], ["C"]]} + See https://core.telegram.org/bots/api#inlinekeyboardmarkup + + :param args: Array of InlineKeyboardButton to append to the keyboard """ i = 1 row = [] @@ -880,26 +890,28 @@ def add(self, *args): def row(self, *args): """ - Adds a list of KeyboardButton to the keyboard. This function does not consider row_width. - ReplyKeyboardMarkup#row("A")#row("B", "C")#to_json() outputs '{keyboard: [["A"], ["B", "C"]]}' + Adds a list of InlineKeyboardButton to the keyboard. + This metod does not consider row_width. + + InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: + '{keyboard: [["A"], ["B", "C"]]}' See https://core.telegram.org/bots/api#inlinekeyboardmarkup - :param args: strings + + :param args: Array of InlineKeyboardButton to append to the keyboard :return: self, to allow function chaining. """ - btn_array = [] - for button in args: - btn_array.append(button.to_dict()) - self.keyboard.append(btn_array) + button_array = [button.to_dict() for button in args] + self.keyboard.append(button_array) return self def to_json(self): """ - Converts this object to its json representation following the Telegram API guidelines described here: + Converts this object to its json representation + following the Telegram API guidelines described here: https://core.telegram.org/bots/api#inlinekeyboardmarkup :return: """ - json_dict = {'inline_keyboard': self.keyboard} - return json.dumps(json_dict) + return json.dumps(self.to_dict()) def to_dict(self): json_dict = {'inline_keyboard': self.keyboard} From 8be9bcc8ed1ae216756adcc655eac74389eb0693 Mon Sep 17 00:00:00 2001 From: Anthony Byuraev Date: Sat, 9 May 2020 20:28:29 +0300 Subject: [PATCH 0408/1808] UPG: Add custom_title, slow_mode_delay --- telebot/types.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index bfe6c772f..62d74c96d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -183,7 +183,8 @@ def __init__(self, id, title): class Chat(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if (json_string is None): + return None obj = cls.check_json(json_string) id = obj['id'] type = obj['type'] @@ -196,10 +197,11 @@ def de_json(cls, json_string): description = obj.get('description') invite_link = obj.get('invite_link') pinned_message = Message.de_json(obj.get('pinned_message')) + slow_mode_delay = obj.get('slow_mode_delay') sticker_set_name = obj.get('sticker_set_name') can_set_sticker_set = obj.get('can_set_sticker_set') return cls(id, type, title, username, first_name, last_name, all_members_are_administrators, - photo, description, invite_link, pinned_message, sticker_set_name, can_set_sticker_set) + photo, description, invite_link, pinned_message, slow_mode_delay, sticker_set_name, can_set_sticker_set) def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, all_members_are_administrators=None, photo=None, description=None, invite_link=None, @@ -1020,6 +1022,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) user = User.de_json(obj['user']) status = obj['status'] + custom_title = obj.get('custom_title') until_date = obj.get('until_date') can_be_edited = obj.get('can_be_edited') can_change_info = obj.get('can_change_info') @@ -1034,7 +1037,7 @@ def de_json(cls, json_string): can_send_media_messages = obj.get('can_send_media_messages') can_send_other_messages = obj.get('can_send_other_messages') can_add_web_page_previews = obj.get('can_add_web_page_previews') - return cls(user, status, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages, + return cls(user, status, custom_title, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews) From a80927baf9fa9ec60e7df07d84fdf060f915e11a Mon Sep 17 00:00:00 2001 From: Anthony Byuraev Date: Sat, 9 May 2020 23:23:08 +0300 Subject: [PATCH 0409/1808] UPG: add setChatAdministratorCustomTitle --- telebot/__init__.py | 14 ++++++++++++++ telebot/apihelper.py | 8 +++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d67d40af1..0c4cf8a29 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -985,6 +985,20 @@ def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_m can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members) + def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title): + """ + Use this method to set a custom title for an administrator + in a supergroup promoted by the bot. + Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :param custom_title: New custom title for the administrator; + 0-16 characters, emoji are not allowed + :return: + """ + return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) + def export_chat_invite_link(self, chat_id): """ Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 97a7a668e..fbf58c00e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -586,7 +586,6 @@ def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_mess payload['can_add_web_page_previews'] = can_add_web_page_previews if can_invite_users: payload['can_invite_users'] = can_invite_users - return _make_request(token, method_url, params=payload, method='post') @@ -614,6 +613,13 @@ def promote_chat_member(token, chat_id, user_id, can_change_info=None, can_post_ return _make_request(token, method_url, params=payload, method='post') +def set_chat_administrator_custom_title(token, chat_id, user_id, custom_title): + method_url = 'setChatAdministratorCustomTitle' + payload = { + 'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title} + return _make_request(token, method_url, params=payload, method='post') + + def export_chat_invite_link(token, chat_id): method_url = 'exportChatInviteLink' payload = {'chat_id': chat_id} From a60253bf608a26e6761e24febadbb97b1dcdc5b3 Mon Sep 17 00:00:00 2001 From: Anthony Byuraev Date: Mon, 11 May 2020 16:38:09 +0300 Subject: [PATCH 0410/1808] UPG: Add ChatPermissions, set_chat_permissions --- telebot/__init__.py | 21 ++++++++++++++--- telebot/apihelper.py | 8 +++++++ telebot/types.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0c4cf8a29..b51035ac4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -547,9 +547,11 @@ def leave_chat(self, chat_id): def get_chat_administrators(self, chat_id): """ - Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects - that contains information about all chat administrators except other bots. - :param chat_id: + Use this method to get a list of administrators in a chat. + On success, returns an Array of ChatMember objects that contains + information about all chat administrators except other bots. + :param chat_id: Unique identifier for the target chat or username + of the target supergroup or channel (in the format @channelusername) :return: """ result = apihelper.get_chat_administrators(self.token, chat_id) @@ -999,6 +1001,19 @@ def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title): """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) + + def set_chat_permissions(self, chat_id, permissions): + """ + Use this method to set default chat permissions for all members. + The bot must be an administrator in the group or a supergroup for this to work + and must have the can_restrict_members admin rights. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param permissions: New default chat permissions + :return: + """ + return apihelper.set_chat_permissions(self.token, chat_id, permissions) + def export_chat_invite_link(self, chat_id): """ Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator diff --git a/telebot/apihelper.py b/telebot/apihelper.py index fbf58c00e..abb6f3fa8 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -620,6 +620,14 @@ def set_chat_administrator_custom_title(token, chat_id, user_id, custom_title): return _make_request(token, method_url, params=payload, method='post') +def set_chat_permissions(token, chat_id, permissions): + method_url = 'setChatPermissions' + payload = { + 'chat_id': chat_id, + 'permissions': _convert_list_json_serializable(permissions)} + return _make_request(token, method_url, params=payload, method='post') + + def export_chat_invite_link(token, chat_id): method_url = 'exportChatInviteLink' payload = {'chat_id': chat_id} diff --git a/telebot/types.py b/telebot/types.py index 62d74c96d..fe62bc5d1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1062,6 +1062,62 @@ def __init__(self, user, status, until_date, can_be_edited, can_change_info, can self.can_add_web_page_previews = can_add_web_page_previews +class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): + def __init__(self, can_send_messages=None, can_send_media_messages=None, + can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None): + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_polls = can_send_polls + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_previews = can_add_web_page_previews + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_pin_messages = can_pin_messages + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return json_string + obj = cls.check_json(json_string) + can_send_messages = obj.get('can_send_messages') + can_send_media_messages = obj.get('can_send_media_messages') + can_send_polls = obj.get('can_send_polls') + can_send_other_messages = obj.get('can_send_other_messages') + can_add_web_page_previews = obj.get('can_add_web_page_previews') + can_change_info = obj.get('can_change_info') + can_invite_users = obj.get('can_invite_users') + can_pin_messages = obj.get('can_pin_messages') + return cls( + can_send_messages, can_send_media_messages, can_send_polls, + can_send_other_messages, can_add_web_page_previews, + can_change_info, can_invite_users, can_pin_messages) + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = dict() + if self.can_send_messages is not None: + json_dict['can_send_messages'] = self.can_send_messages + if self.can_send_media_messages is not None: + json_dict['can_send_media_messages'] = self.can_send_media_messages + if self.can_send_polls is not None: + json_dict['can_send_polls'] = self.can_send_polls + if self.can_send_other_messages is not None: + json_dict['can_send_other_messages'] = self.can_send_other_messages + if self.can_add_web_page_previews is not None: + json_dict['can_add_web_page_previews'] = self.can_add_web_page_previews + if self.can_change_info is not None: + json_dict['can_change_info'] = self.can_change_info + if self.can_invite_users is not None: + json_dict['can_invite_users'] = self.can_invite_users + if self.can_pin_messages is not None: + json_dict['can_pin_messages'] = self.can_pin_messages + return json_dict + + class BotCommand(JsonSerializable): def __init__(self, command, description): """ From ee00d0458d5c118e121d09ac2bfe81d4c88fcba1 Mon Sep 17 00:00:00 2001 From: Anthony Byuraev Date: Mon, 11 May 2020 22:26:03 +0300 Subject: [PATCH 0411/1808] Fix some bugs --- telebot/types.py | 69 ++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index fe62bc5d1..37ec7b832 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -183,7 +183,7 @@ def __init__(self, id, title): class Chat(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): + if json_string is None: return None obj = cls.check_json(json_string) id = obj['id'] @@ -197,26 +197,34 @@ def de_json(cls, json_string): description = obj.get('description') invite_link = obj.get('invite_link') pinned_message = Message.de_json(obj.get('pinned_message')) + permissions = ChatPermissions.de_json(obj.get('permissions')) slow_mode_delay = obj.get('slow_mode_delay') sticker_set_name = obj.get('sticker_set_name') can_set_sticker_set = obj.get('can_set_sticker_set') - return cls(id, type, title, username, first_name, last_name, all_members_are_administrators, - photo, description, invite_link, pinned_message, slow_mode_delay, sticker_set_name, can_set_sticker_set) - - def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, - all_members_are_administrators=None, photo=None, description=None, invite_link=None, - pinned_message=None, sticker_set_name=None, can_set_sticker_set=None): - self.type = type - self.last_name = last_name - self.first_name = first_name - self.username = username + return cls( + id, type, title, username, first_name, last_name, + all_members_are_administrators, photo, description, invite_link, + pinned_message, permissions, slow_mode_delay, sticker_set_name, + can_set_sticker_set) + + def __init__(self, id, type, title=None, username=None, first_name=None, + last_name=None, all_members_are_administrators=None, + photo=None, description=None, invite_link=None, + pinned_message=None, permissions=None, slow_mode_delay=None, + sticker_set_name=None, can_set_sticker_set=None): self.id = id + self.type = type self.title = title + self.username = username + self.first_name = first_name + self.last_name = last_name self.all_members_are_administrators = all_members_are_administrators self.photo = photo self.description = description self.invite_link = invite_link self.pinned_message = pinned_message + self.permissions = permissions + self.slow_mode_delay = slow_mode_delay self.sticker_set_name = sticker_set_name self.can_set_sticker_set = can_set_sticker_set @@ -1017,7 +1025,7 @@ def __init__(self, small_file_id, big_file_id): class ChatMember(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): + if json_string is None: return None obj = cls.check_json(json_string) user = User.de_json(obj['user']) @@ -1025,39 +1033,50 @@ def de_json(cls, json_string): custom_title = obj.get('custom_title') until_date = obj.get('until_date') can_be_edited = obj.get('can_be_edited') - can_change_info = obj.get('can_change_info') can_post_messages = obj.get('can_post_messages') can_edit_messages = obj.get('can_edit_messages') can_delete_messages = obj.get('can_delete_messages') - can_invite_users = obj.get('can_invite_users') can_restrict_members = obj.get('can_restrict_members') - can_pin_messages = obj.get('can_pin_messages') can_promote_members = obj.get('can_promote_members') + can_change_info = obj.get('can_change_info') + can_invite_users = obj.get('can_invite_users') + can_pin_messages = obj.get('can_pin_messages') + is_member = obj.get('is_member') can_send_messages = obj.get('can_send_messages') can_send_media_messages = obj.get('can_send_media_messages') + can_send_polls = obj.get('can_send_polls') can_send_other_messages = obj.get('can_send_other_messages') can_add_web_page_previews = obj.get('can_add_web_page_previews') - return cls(user, status, custom_title, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages, - can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews) - - def __init__(self, user, status, until_date, can_be_edited, can_change_info, can_post_messages, can_edit_messages, - can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - can_send_messages, can_send_media_messages, can_send_other_messages, can_add_web_page_previews): + return cls( + user, status, custom_title, until_date, can_be_edited, can_post_messages, + can_edit_messages, can_delete_messages, can_restrict_members, + can_promote_members, can_change_info, can_invite_users, can_pin_messages, + is_member, can_send_messages, can_send_media_messages, can_send_polls, + can_send_other_messages, can_add_web_page_previews) + + def __init__(self, user, status, custom_title=None, until_date=None, can_be_edited=None, + can_post_messages=None, can_edit_messages=None, can_delete_messages=None, + can_restrict_members=None, can_promote_members=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None, is_member=None, + can_send_messages=None, can_send_media_messages=None, can_send_polls=None, + can_send_other_messages=None, can_add_web_page_previews=None): self.user = user self.status = status + self.custom_title = custom_title self.until_date = until_date self.can_be_edited = can_be_edited - self.can_change_info = can_change_info self.can_post_messages = can_post_messages self.can_edit_messages = can_edit_messages self.can_delete_messages = can_delete_messages - self.can_invite_users = can_invite_users self.can_restrict_members = can_restrict_members - self.can_pin_messages = can_pin_messages self.can_promote_members = can_promote_members + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_pin_messages = can_pin_messages + self.is_member = is_member self.can_send_messages = can_send_messages self.can_send_media_messages = can_send_media_messages + self.can_send_polls = can_send_polls self.can_send_other_messages = can_send_other_messages self.can_add_web_page_previews = can_add_web_page_previews From 75a018e18b5aab22ddb8030ca97f13dd1d903033 Mon Sep 17 00:00:00 2001 From: dr_forse Date: Tue, 12 May 2020 01:09:34 +0100 Subject: [PATCH 0412/1808] add PollAnswer, poll_answer_handler; make User Serializable and Dictionaryble; some pep fixes --- telebot/__init__.py | 33 ++++++++++++++++++++++++++++++++- telebot/types.py | 43 ++++++++++++++++++++++++++++++++++++++++--- tests/test_types.py | 11 ++++++++++- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b51035ac4..a6a33b6a7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -119,6 +119,7 @@ def __init__( self.shipping_query_handlers = [] self.pre_checkout_query_handlers = [] self.poll_handlers = [] + self.poll_answer_handlers = [] self.typed_middleware_handlers = { 'message': [], @@ -293,6 +294,7 @@ def process_new_updates(self, updates): new_shipping_querys = [] new_pre_checkout_querys = [] new_polls = [] + new_poll_answers = [] for update in updates: if apihelper.ENABLE_MIDDLEWARE: @@ -320,6 +322,8 @@ def process_new_updates(self, updates): new_pre_checkout_querys.append(update.pre_checkout_query) if update.poll: new_polls.append(update.poll) + if update.poll_answer: + new_poll_answers.append(update.poll_answer) logger.debug('Received {0} new updates'.format(len(updates))) if len(new_messages) > 0: @@ -342,6 +346,8 @@ def process_new_updates(self, updates): self.process_new_pre_checkout_query(new_pre_checkout_querys) if len(new_polls) > 0: self.process_new_poll(new_polls) + if len(new_poll_answers) > 0: + self.process_new_poll_answer(new_poll_answers) def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) @@ -376,6 +382,9 @@ def process_new_pre_checkout_query(self, pre_checkout_querys): def process_new_poll(self, polls): self._notify_command_handlers(self.poll_handlers, polls) + def process_new_poll_answer(self, poll_answers): + self._notify_command_handlers(self.poll_answer_handlers, poll_answers) + def process_middlewares(self, update): for update_type, middlewares in self.typed_middleware_handlers.items(): if getattr(update, update_type) is not None: @@ -1561,7 +1570,7 @@ def _build_handler_dict(handler, **filters): """ return { 'function': handler, - 'filters' : filters + 'filters': filters } def middleware_handler(self, update_types=None): @@ -1900,6 +1909,28 @@ def add_poll_handler(self, handler_dict): """ self.poll_handlers.append(handler_dict) + def poll_answer_handler(self, func=None, **kwargs): + """ + Poll_answer request handler + :param func: + :param kwargs: + :return: + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_poll_answer_handler(handler_dict) + return handler + + return decorator + + def add_poll_answer_handler(self, handler_dict): + """ + Adds a poll_answer request handler + :param handler_dict: + :return: + """ + self.poll_answer_handlers.append(handler_dict) + def _test_message_handler(self, message_handler, message): """ Test message handler diff --git a/telebot/types.py b/telebot/types.py index 37ec7b832..defe601a1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -100,11 +100,12 @@ def de_json(cls, json_string): shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) poll = Poll.de_json(obj.get('poll')) + poll_answer = PollAnswer.de_json(obj.get('poll_answer')) 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) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer) 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): + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -116,6 +117,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.shipping_query = shipping_query self.pre_checkout_query = pre_checkout_query self.poll = poll + self.poll_answer = poll_answer class WebhookInfo(JsonDeserializable): @@ -144,7 +146,7 @@ def __init__(self, url, has_custom_certificate, pending_update_count, last_error self.allowed_updates = allowed_updates -class User(JsonDeserializable): +class User(JsonDeserializable, Dictionaryable, JsonSerializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -165,6 +167,17 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.last_name = last_name self.language_code = language_code + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'id': self.id, + 'is_bot': self.is_bot, + 'first_name': self.first_name, + 'last_name': self.last_name, + 'username': self.username, + 'language_code': self.language_code} + class GroupChat(JsonDeserializable): @classmethod @@ -2411,3 +2424,27 @@ def add(self, option): self.options.append(option) else: self.options.append(PollOption(option)) + + +class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + poll_id = obj['poll_id'] + user = User.de_json(obj['user']) + options_ids = obj['option_ids'] + return cls(poll_id, user, options_ids) + + def __init__(self, poll_id, user, options_ids): + self.poll_id = poll_id + self.user = user + self.options_ids = options_ids + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'poll_id': self.poll_id, + 'user': self.user.to_dict(), + 'options_ids': self.options_ids} diff --git a/tests/test_types.py b/tests/test_types.py index 8418a9fa1..18337edc7 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -178,4 +178,13 @@ def test_json_poll_1(): assert msg.poll.question is not None assert msg.poll.options is not None assert len(msg.poll.options) == 2 - assert msg.poll.allows_multiple_answers == True + assert msg.poll.allows_multiple_answers is True + + +def test_json_poll_answer(): + jsonstring = r'{"poll_id": "5895675970559410186", "user": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "option_ids": [1]}' + __import__('pprint').pprint(__import__('json').loads(jsonstring)) + poll_answer = types.PollAnswer.de_json(jsonstring) + assert poll_answer.poll_id == '5895675970559410186' + assert isinstance(poll_answer.user, types.User) + assert poll_answer.options_ids == [1] From d04e70843867c219ee6452fe661eaf04ce97dba9 Mon Sep 17 00:00:00 2001 From: dr_forse Date: Tue, 12 May 2020 18:09:04 +0100 Subject: [PATCH 0413/1808] fix test --- tests/test_handler_backends.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index 6ed2c1869..9605900da 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -61,8 +61,9 @@ def update_type(message): shipping_query = None pre_checkout_query = None poll = None + poll_answer = None return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer) @pytest.fixture() @@ -76,9 +77,10 @@ def reply_to_message_update_type(reply_to_message): shipping_query = None pre_checkout_query = None poll = None + poll_answer = None return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer) def next_handler(message): From 3de8140c0b7e81733929106ee29179e7dcf0c405 Mon Sep 17 00:00:00 2001 From: dr_forse Date: Tue, 12 May 2020 18:29:36 +0100 Subject: [PATCH 0414/1808] fix test 1 --- tests/test_telebot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 5d324dd1a..e9f3c5a29 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -428,8 +428,9 @@ def create_message_update(text): shipping_query = None pre_checkout_query = None poll = None + poll_answer = None return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer) def test_is_string_unicode(self): s1 = u'string' From 236842133293737d56e5c97d776258ac23a20f5b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 16 May 2020 17:34:56 +0300 Subject: [PATCH 0415/1808] Correct processing of bool parameters. Some timeouts added. --- telebot/__init__.py | 139 ++++++++++++++++---------- telebot/apihelper.py | 231 ++++++++++++++++++++++++++++--------------- 2 files changed, 235 insertions(+), 135 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a6a33b6a7..ffde7e3a5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -636,7 +636,7 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) - def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None): + def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None): """ Use this method to forward messages of any kind. :param disable_notification: @@ -646,7 +646,7 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio :return: API reply. """ return types.Message.de_json( - apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification)) + apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) def delete_message(self, chat_id, message_id): """ @@ -657,7 +657,10 @@ def delete_message(self, chat_id, message_id): """ return apihelper.delete_message(self.token, chat_id, message_id) - def send_dice(self, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): + def send_dice( + self, chat_id, + emoji=None, disable_notification=None, reply_to_message_id=None, + reply_markup=None, timeout=None): """ Use this method to send dices. :param chat_id: @@ -668,11 +671,13 @@ def send_dice(self, chat_id, emoji=None, disable_notification=None, reply_to_mes :return: Message """ return types.Message.de_json( - apihelper.send_dice(self.token, chat_id, emoji, disable_notification, reply_to_message_id, reply_markup) + apihelper.send_dice( + self.token, chat_id, emoji, disable_notification, reply_to_message_id, + reply_markup, timeout) ) def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None): + parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send photos. :param disable_notification: @@ -686,7 +691,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep """ return types.Message.de_json( apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, - parse_mode, disable_notification)) + parse_mode, disable_notification, timeout)) def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, @@ -747,8 +752,9 @@ def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, r apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption=caption)) - def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=None, disable_notification=None, - timeout=None): + def send_sticker( + self, chat_id, data, reply_to_message_id=None, reply_markup=None, + disable_notification=None, timeout=None): """ Use this method to send .webp stickers. :param chat_id: @@ -760,8 +766,9 @@ def send_sticker(self, chat_id, data, reply_to_message_id=None, reply_markup=Non :return: API reply. """ return types.Message.de_json( - apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup, - disable_notification, timeout)) + apihelper.send_data( + self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup, + disable_notification, timeout)) def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None): @@ -820,7 +827,9 @@ def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_me apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, disable_notification, timeout)) - def send_media_group(self, chat_id, media, disable_notification=None, reply_to_message_id=None): + def send_media_group( + self, chat_id, media, + disable_notification=None, reply_to_message_id=None, timeout=None): """ send a group of photos or videos as an album. On success, an array of the sent Messages is returned. :param chat_id: @@ -829,14 +838,16 @@ def send_media_group(self, chat_id, media, disable_notification=None, reply_to_m :param reply_to_message_id: :return: """ - result = apihelper.send_media_group(self.token, chat_id, media, disable_notification, reply_to_message_id) + result = apihelper.send_media_group( + self.token, chat_id, media, disable_notification, reply_to_message_id, timeout) ret = [] for msg in result: ret.append(types.Message.de_json(msg)) return ret - def send_location(self, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): + def send_location( + self, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, + reply_markup=None, disable_notification=None, timeout=None): """ Use this method to send point on the map. :param chat_id: @@ -849,12 +860,12 @@ def send_location(self, chat_id, latitude, longitude, live_period=None, reply_to :return: API reply. """ return types.Message.de_json( - apihelper.send_location(self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, - reply_markup, - disable_notification)) + apihelper.send_location( + self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, + reply_markup, disable_notification, timeout)) def edit_message_live_location(self, latitude, longitude, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None): + inline_message_id=None, reply_markup=None, timeout=None): """ Use this method to edit live location :param latitude: @@ -866,10 +877,13 @@ def edit_message_live_location(self, latitude, longitude, chat_id=None, message_ :return: """ return types.Message.de_json( - apihelper.edit_message_live_location(self.token, latitude, longitude, chat_id, message_id, - inline_message_id, reply_markup)) + apihelper.edit_message_live_location( + self.token, latitude, longitude, chat_id, message_id, + inline_message_id, reply_markup, timeout)) - def stop_message_live_location(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + def stop_message_live_location( + self, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None, timeout=None): """ Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires @@ -880,10 +894,12 @@ def stop_message_live_location(self, chat_id=None, message_id=None, inline_messa :return: """ return types.Message.de_json( - apihelper.stop_message_live_location(self.token, chat_id, message_id, inline_message_id, reply_markup)) + apihelper.stop_message_live_location( + self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) - def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None): + def send_venue( + self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, + reply_to_message_id=None, reply_markup=None, timeout=None): """ Use this method to send information about a venue. :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel @@ -898,18 +914,22 @@ def send_venue(self, chat_id, latitude, longitude, title, address, foursquare_id :return: """ return types.Message.de_json( - apihelper.send_venue(self.token, chat_id, latitude, longitude, title, address, foursquare_id, - disable_notification, reply_to_message_id, reply_markup) + apihelper.send_venue( + self.token, chat_id, latitude, longitude, title, address, foursquare_id, + disable_notification, reply_to_message_id, reply_markup, timeout) ) - def send_contact(self, chat_id, phone_number, first_name, last_name=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None): + def send_contact( + self, chat_id, phone_number, first_name, + last_name=None, disable_notification=None, + reply_to_message_id=None, reply_markup=None, timeout=None): return types.Message.de_json( - apihelper.send_contact(self.token, chat_id, phone_number, first_name, last_name, disable_notification, - reply_to_message_id, reply_markup) + apihelper.send_contact( + self.token, chat_id, phone_number, first_name, last_name, disable_notification, + reply_to_message_id, reply_markup, timeout) ) - def send_chat_action(self, chat_id, action): + def send_chat_action(self, chat_id, action, timeout=None): """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear @@ -919,7 +939,7 @@ def send_chat_action(self, chat_id, action): 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', 'upload_video_note'. :return: API reply. :type: boolean """ - return apihelper.send_chat_action(self.token, chat_id, action) + return apihelper.send_chat_action(self.token, chat_id, action, timeout) def kick_chat_member(self, chat_id, user_id, until_date=None): """ @@ -941,9 +961,12 @@ def unban_chat_member(self, chat_id, user_id): """ return apihelper.unban_chat_member(self.token, chat_id, user_id) - def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=None, - can_send_media_messages=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_invite_users=None): + def restrict_chat_member( + self, chat_id, user_id, until_date=None, + can_send_messages=None, can_send_media_messages=None, + can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None): """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -958,17 +981,23 @@ def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messa :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages + :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, implies can_send_media_messages - :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, - implies can_invite_users + :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, + implies can_invite_users + :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups :return: types.Message """ - return apihelper.restrict_chat_member(self.token, chat_id, user_id, until_date, can_send_messages, - can_send_media_messages, can_send_other_messages, - can_add_web_page_previews, can_invite_users) + return apihelper.restrict_chat_member( + self.token, chat_id, user_id, until_date, + can_send_messages, can_send_media_messages, + can_send_polls, can_send_other_messages, + can_add_web_page_previews, can_change_info, + can_invite_users, can_pin_messages) def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, @@ -1169,8 +1198,9 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_messag return result return types.Message.de_json(result) - def send_game(self, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, - reply_markup=None): + def send_game( + self, chat_id, game_short_name, disable_notification=None, + reply_to_message_id=None, reply_markup=None, timeout=None): """ Used to send the game :param chat_id: @@ -1180,8 +1210,9 @@ def send_game(self, chat_id, game_short_name, disable_notification=None, reply_t :param reply_markup: :return: """ - result = apihelper.send_game(self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, - reply_markup) + result = apihelper.send_game( + self.token, chat_id, game_short_name, disable_notification, + reply_to_message_id, reply_markup, timeout) return types.Message.de_json(result) def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None, @@ -1222,7 +1253,7 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, - provider_data=None): + provider_data=None, timeout=None): """ Sends invoice :param chat_id: @@ -1248,19 +1279,19 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to :param provider_data: :return: """ - result = apihelper.send_invoice(self.token, chat_id, title, description, invoice_payload, provider_token, - currency, prices, start_parameter, photo_url, photo_size, photo_width, - photo_height, - need_name, need_phone_number, need_email, need_shipping_address, is_flexible, - disable_notification, reply_to_message_id, reply_markup, provider_data) + result = apihelper.send_invoice( + self.token, chat_id, title, description, invoice_payload, provider_token, + currency, prices, start_parameter, photo_url, photo_size, photo_width, + photo_height, need_name, need_phone_number, need_email, need_shipping_address, + is_flexible, disable_notification, reply_to_message_id, reply_markup, + provider_data, timeout) return types.Message.de_json(result) def send_poll( - self, chat_id, - question, options, + self, 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_notifications=False, reply_to_message_id=None, reply_markup=None): + disable_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None): """ Send polls :param chat_id: @@ -1290,7 +1321,7 @@ def send_poll( question, options, is_anonymous, type, allows_multiple_answers, correct_option_id, explanation, explanation_parse_mode, open_period, close_date, is_closed, - disable_notifications, reply_to_message_id, reply_markup)) + disable_notifications, reply_to_message_id, reply_markup, timeout)) def stop_poll(self, chat_id, message_id): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index abb6f3fa8..570a9d661 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -57,10 +57,13 @@ def _make_request(token, method_name, method='get', params=None, files=None): if files and format_header_param: fields.format_header_param = _no_encode(format_header_param) if params: - if 'timeout' in params: read_timeout = params['timeout'] + 10 - if 'connect-timeout' in params: connect_timeout = params['connect-timeout'] + 10 - result = _get_req_session().request(method, request_url, params=params, files=files, - timeout=(connect_timeout, read_timeout), proxies=proxy) + if 'timeout' in params: + read_timeout = params.pop('timeout') + 10 + if 'connect-timeout' in params: + connect_timeout = params.pop('connect-timeout') + 10 + result = _get_req_session().request( + method, request_url, params=params, files=files, + timeout=(connect_timeout, read_timeout), proxies=proxy) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) return _check_result(method_name, result)['result'] @@ -128,8 +131,10 @@ def download_file(token, file_path): return result.content -def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): +def send_message( + token, chat_id, text, + disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send text messages. On success, the sent Message is returned. :param token: @@ -140,11 +145,12 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m :param reply_markup: :param parse_mode: :param disable_notification: + :param timeout: :return: """ method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} - if disable_web_page_preview: + if disable_web_page_preview is not None: payload['disable_web_page_preview'] = disable_web_page_preview if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id @@ -152,7 +158,7 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: payload['parse_mode'] = parse_mode - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout @@ -251,30 +257,41 @@ def get_chat_member(token, chat_id, user_id): return _make_request(token, method_url, params=payload) -def forward_message(token, chat_id, from_chat_id, message_id, disable_notification=None): +def forward_message( + token, chat_id, from_chat_id, message_id, + disable_notification=None, timeout=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) -def send_dice(token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, reply_markup=None): +def send_dice( + token, chat_id, + emoji=None, disable_notification=None, reply_to_message_id=None, + reply_markup=None, timeout=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: payload['emoji'] = emoji - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) -def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None): +def send_photo( + token, chat_id, photo, + caption=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -290,25 +307,36 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: payload['parse_mode'] = parse_mode - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, files=files, method='post') -def send_media_group(token, chat_id, media, disable_notification=None, reply_to_message_id=None): +def send_media_group( + token, chat_id, media, + disable_notification=None, reply_to_message_id=None, + timeout=None): method_url = r'sendMediaGroup' media_json, files = _convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id - return _make_request(token, method_url, params=payload, method='post' if files else 'get', - files=files if files else None) + if timeout: + payload['connect-timeout'] = timeout + return _make_request( + token, method_url, params=payload, + method='post' if files else 'get', + files=files if files else None) -def send_location(token, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None): +def send_location( + token, chat_id, latitude, longitude, + live_period=None, reply_to_message_id=None, reply_markup=None, + disable_notification=None, timeout=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -317,13 +345,15 @@ def send_location(token, chat_id, latitude, longitude, live_period=None, reply_t payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) def edit_message_live_location(token, latitude, longitude, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None): + inline_message_id=None, reply_markup=None, timeout=None): method_url = r'editMessageLiveLocation' payload = {'latitude': latitude, 'longitude': longitude} if chat_id: @@ -334,11 +364,14 @@ def edit_message_live_location(token, latitude, longitude, chat_id=None, message payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) -def stop_message_live_location(token, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None): +def stop_message_live_location( + token, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None, timeout=None): method_url = r'stopMessageLiveLocation' payload = {} if chat_id: @@ -349,42 +382,54 @@ def stop_message_live_location(token, chat_id=None, message_id=None, payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) -def send_venue(token, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None): +def send_venue( + token, chat_id, latitude, longitude, title, address, + foursquare_id=None, disable_notification=None, + reply_to_message_id=None, reply_markup=None, timeout=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: payload['foursquare_id'] = foursquare_id - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) -def send_contact(token, chat_id, phone_number, first_name, last_name=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None): +def send_contact( + token, chat_id, phone_number, first_name, + last_name=None, disable_notification=None, + reply_to_message_id=None, reply_markup=None, timeout=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: payload['last_name'] = last_name - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) -def send_chat_action(token, chat_id, action): +def send_chat_action(token, chat_id, action, timeout=None): method_url = r'sendChatAction' payload = {'chat_id': chat_id, 'action': action} + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -407,9 +452,9 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: payload['parse_mode'] = parse_mode - if supports_streaming: + if supports_streaming is not None: payload['supports_streaming'] = supports_streaming - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout @@ -435,7 +480,7 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: payload['parse_mode'] = parse_mode - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout @@ -461,7 +506,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: payload['parse_mode'] = parse_mode - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout @@ -487,7 +532,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout @@ -517,7 +562,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: payload['parse_mode'] = parse_mode - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout @@ -539,7 +584,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode and data_type == 'document': payload['parse_mode'] = parse_mode - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout @@ -569,46 +614,56 @@ def unban_chat_member(token, chat_id, user_id): return _make_request(token, method_url, params=payload, method='post') -def restrict_chat_member(token, chat_id, user_id, until_date=None, can_send_messages=None, - can_send_media_messages=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_invite_users=None): +def restrict_chat_member( + token, chat_id, user_id, until_date=None, + can_send_messages=None, can_send_media_messages=None, + can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None): method_url = 'restrictChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} - if until_date: + if until_date is not None: payload['until_date'] = until_date - if can_send_messages: + if can_send_messages is not None: payload['can_send_messages'] = can_send_messages - if can_send_media_messages: + if can_send_media_messages is not None: payload['can_send_media_messages'] = can_send_media_messages - if can_send_other_messages: + if can_send_polls is not None: + payload['can_send_polls'] = can_send_polls + if can_send_other_messages is not None: payload['can_send_other_messages'] = can_send_other_messages - if can_add_web_page_previews: + if can_add_web_page_previews is not None: payload['can_add_web_page_previews'] = can_add_web_page_previews - if can_invite_users: + if can_change_info is not None: + payload['can_change_info'] = can_change_info + if can_invite_users is not None: payload['can_invite_users'] = can_invite_users + if can_pin_messages is not None: + payload['can_pin_messages'] = can_pin_messages return _make_request(token, method_url, params=payload, method='post') -def promote_chat_member(token, chat_id, user_id, can_change_info=None, can_post_messages=None, - can_edit_messages=None, can_delete_messages=None, can_invite_users=None, - can_restrict_members=None, can_pin_messages=None, can_promote_members=None): +def promote_chat_member( + token, chat_id, user_id, can_change_info=None, can_post_messages=None, + can_edit_messages=None, can_delete_messages=None, can_invite_users=None, + can_restrict_members=None, can_pin_messages=None, can_promote_members=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} - if can_change_info: + if can_change_info is not None: payload['can_change_info'] = can_change_info - if can_post_messages: + if can_post_messages is not None: payload['can_post_messages'] = can_post_messages - if can_edit_messages: + if can_edit_messages is not None: payload['can_edit_messages'] = can_edit_messages - if can_delete_messages: + if can_delete_messages is not None: payload['can_delete_messages'] = can_delete_messages - if can_invite_users: + if can_invite_users is not None: payload['can_invite_users'] = can_invite_users - if can_restrict_members: + if can_restrict_members is not None: payload['can_restrict_members'] = can_restrict_members - if can_pin_messages: + if can_pin_messages is not None: payload['can_pin_messages'] = can_pin_messages - if can_promote_members: + if can_promote_members is not None: payload['can_promote_members'] = can_promote_members return _make_request(token, method_url, params=payload, method='post') @@ -669,9 +724,11 @@ def set_chat_description(token, chat_id, description): return _make_request(token, method_url, params=payload, method='post') -def pin_chat_message(token, chat_id, message_id, disable_notification=False): +def pin_chat_message(token, chat_id, message_id, disable_notification=None): method_url = 'pinChatMessage' - payload = {'chat_id': chat_id, 'message_id': message_id, 'disable_notification': disable_notification} + payload = {'chat_id': chat_id, 'message_id': message_id} + if disable_notification is not None: + payload['disable_notification'] = disable_notification return _make_request(token, method_url, params=payload, method='post') @@ -695,7 +752,7 @@ 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 disable_web_page_preview: + if disable_web_page_preview is not None: payload['disable_web_page_preview'] = disable_web_page_preview if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) @@ -756,15 +813,19 @@ def delete_message(token, chat_id, message_id): # Game -def send_game(token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None): +def send_game( + token, chat_id, game_short_name, + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -785,7 +846,7 @@ def set_game_score(token, user_id, score, force=None, disable_edit_message=None, """ method_url = r'setGameScore' payload = {'user_id': user_id, 'score': score} - if force: + if force is not None: payload['force'] = force if chat_id: payload['chat_id'] = chat_id @@ -793,7 +854,7 @@ def set_game_score(token, user_id, score, force=None, disable_edit_message=None, payload['message_id'] = message_id if inline_message_id: payload['inline_message_id'] = inline_message_id - if disable_edit_message: + if disable_edit_message is not None: payload['disable_edit_message'] = disable_edit_message return _make_request(token, method_url, params=payload) @@ -823,10 +884,12 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m # Payments (https://core.telegram.org/bots/api#payments) -def send_invoice(token, chat_id, title, description, invoice_payload, provider_token, currency, prices, - start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, - need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None): +def send_invoice( + token, chat_id, title, description, invoice_payload, provider_token, currency, prices, + start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, + timeout=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -865,17 +928,17 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t payload['photo_width'] = photo_width if photo_height: payload['photo_height'] = photo_height - if need_name: + if need_name is not None: payload['need_name'] = need_name - if need_phone_number: + if need_phone_number is not None: payload['need_phone_number'] = need_phone_number - if need_email: + if need_email is not None: payload['need_email'] = need_email - if need_shipping_address: + if need_shipping_address is not None: payload['need_shipping_address'] = need_shipping_address - if is_flexible: + if is_flexible is not None: payload['is_flexible'] = is_flexible - if disable_notification: + if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id @@ -883,6 +946,8 @@ def send_invoice(token, chat_id, title, description, invoice_payload, provider_t payload['reply_markup'] = _convert_markup(reply_markup) if provider_data: payload['provider_data'] = provider_data + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -940,7 +1005,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None, payload = {'callback_query_id': callback_query_id} if text: payload['text'] = text - if show_alert: + if show_alert is not None: payload['show_alert'] = show_alert if url: payload['url'] = url @@ -955,7 +1020,7 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)} if cache_time is not None: payload['cache_time'] = cache_time - if is_personal: + if is_personal is not None: payload['is_personal'] = is_personal if next_offset is not None: payload['next_offset'] = next_offset @@ -978,7 +1043,9 @@ def upload_sticker_file(token, user_id, png_sticker): return _make_request(token, method_url, params=payload, files=files, method='post') -def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, contains_masks=None, mask_position=None): +def create_new_sticker_set( + token, user_id, name, title, png_sticker, emojis, + contains_masks=None, mask_position=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} files = None @@ -986,7 +1053,7 @@ def create_new_sticker_set(token, user_id, name, title, png_sticker, emojis, con files = {'png_sticker': png_sticker} else: payload['png_sticker'] = png_sticker - if contains_masks: + if contains_masks is not None: payload['contains_masks'] = contains_masks if mask_position: payload['mask_position'] = mask_position.to_json() @@ -1023,7 +1090,7 @@ 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_notifications=False, reply_to_message_id=None, reply_markup=None): + disable_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1055,6 +1122,8 @@ def send_poll( payload['reply_to_message_id'] = reply_to_message_id if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) From 03b02561a57d04c1c81e90473567c628d054baa0 Mon Sep 17 00:00:00 2001 From: oleg Date: Wed, 6 May 2020 13:23:39 +0500 Subject: [PATCH 0416/1808] Add request_poll attribute to KeyboardButton; Add KeyboardButtonPollType object --- telebot/types.py | 16 +++++++++++++--- tests/test_types.py | 8 ++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index defe601a1..9cc9162ad 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -117,7 +117,6 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.shipping_query = shipping_query self.pre_checkout_query = pre_checkout_query self.poll = poll - self.poll_answer = poll_answer class WebhookInfo(JsonDeserializable): @@ -223,7 +222,7 @@ def de_json(cls, json_string): def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, all_members_are_administrators=None, photo=None, description=None, invite_link=None, - pinned_message=None, permissions=None, slow_mode_delay=None, + pinned_message=None, permissions=None, slow_mode_delay=None, sticker_set_name=None, can_set_sticker_set=None): self.id = id self.type = type @@ -859,10 +858,11 @@ def to_json(self): class KeyboardButton(Dictionaryable, JsonSerializable): - def __init__(self, text, request_contact=None, request_location=None): + def __init__(self, text, request_contact=None, request_location=None, request_poll=None): self.text = text self.request_contact = request_contact self.request_location = request_location + self.request_poll = request_poll def to_json(self): return json.dumps(self.to_dict()) @@ -873,9 +873,19 @@ def to_dict(self): json_dict['request_contact'] = self.request_contact if self.request_location: json_dict['request_location'] = self.request_location + if self.request_poll: + json_dict['request_poll'] = self.request_poll.to_dict() return json_dict +class KeyboardButtonPollType(Dictionaryable): + def __init__(self, type=''): + self.type = type + + def to_dict(self): + return {'type': self.type} + + class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): def __init__(self, row_width=3): """ diff --git a/tests/test_types.py b/tests/test_types.py index 18337edc7..cc0c4cf2d 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -188,3 +188,11 @@ def test_json_poll_answer(): assert poll_answer.poll_id == '5895675970559410186' assert isinstance(poll_answer.user, types.User) assert poll_answer.options_ids == [1] + + +def test_KeyboardButtonPollType(): + markup = types.ReplyKeyboardMarkup() + markup.add(types.KeyboardButton('send me a poll', request_poll=types.KeyboardButtonPollType(type='quiz'))) + json_str = markup.to_json() + assert 'request_poll' in json_str + assert 'quiz' in json_str From 592dcbfedf7fc0c1f02b7fb630e4807bc98c61fc Mon Sep 17 00:00:00 2001 From: oleg Date: Tue, 12 May 2020 10:42:17 +0500 Subject: [PATCH 0417/1808] Add PollAnswer type; Add poll_answer_handler --- telebot/types.py | 1 + tests/test_types.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 9cc9162ad..cf3d7e9c4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -117,6 +117,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.shipping_query = shipping_query self.pre_checkout_query = pre_checkout_query self.poll = poll + self.poll_answer = poll_answer class WebhookInfo(JsonDeserializable): diff --git a/tests/test_types.py b/tests/test_types.py index cc0c4cf2d..1a4abe89d 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -190,6 +190,14 @@ def test_json_poll_answer(): assert poll_answer.options_ids == [1] +def test_json_poll_answer(): + json_string = r'{"poll_id": "qwerty963852741","user": {"id": 111,"is_bot": false,"first_name": "Tele","last_name": "Bot","username": "Telebot","language_code": "ru"},"option_ids": [1, 2]}' + msg = types.PollAnswer.de_json(json_string) + assert isinstance(msg.user, types.User) + assert msg.poll_id == 'qwerty963852741' + assert len(msg.option_ids) == 2 + + def test_KeyboardButtonPollType(): markup = types.ReplyKeyboardMarkup() markup.add(types.KeyboardButton('send me a poll', request_poll=types.KeyboardButtonPollType(type='quiz'))) From 27e2cbc7eab060ae953548875986a4ee654e95bb Mon Sep 17 00:00:00 2001 From: oleg Date: Mon, 18 May 2020 12:22:26 +0500 Subject: [PATCH 0418/1808] Remove unnecessary test --- tests/test_types.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index 1a4abe89d..cc0c4cf2d 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -190,14 +190,6 @@ def test_json_poll_answer(): assert poll_answer.options_ids == [1] -def test_json_poll_answer(): - json_string = r'{"poll_id": "qwerty963852741","user": {"id": 111,"is_bot": false,"first_name": "Tele","last_name": "Bot","username": "Telebot","language_code": "ru"},"option_ids": [1, 2]}' - msg = types.PollAnswer.de_json(json_string) - assert isinstance(msg.user, types.User) - assert msg.poll_id == 'qwerty963852741' - assert len(msg.option_ids) == 2 - - def test_KeyboardButtonPollType(): markup = types.ReplyKeyboardMarkup() markup.add(types.KeyboardButton('send me a poll', request_poll=types.KeyboardButtonPollType(type='quiz'))) From 32dc03ec44ec92ca59e186693394830c32b26348 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 20 May 2020 11:57:41 +0300 Subject: [PATCH 0419/1808] send_chat_permissions fix --- telebot/apihelper.py | 6 ++++-- tests/test_telebot.py | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 570a9d661..ac1c70f03 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -671,7 +671,8 @@ def promote_chat_member( def set_chat_administrator_custom_title(token, chat_id, user_id, custom_title): method_url = 'setChatAdministratorCustomTitle' payload = { - 'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title} + 'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title + } return _make_request(token, method_url, params=payload, method='post') @@ -679,7 +680,8 @@ def set_chat_permissions(token, chat_id, permissions): method_url = 'setChatPermissions' payload = { 'chat_id': chat_id, - 'permissions': _convert_list_json_serializable(permissions)} + 'permissions': permissions.to_json() + } return _make_request(token, method_url, params=payload, method='post') diff --git a/tests/test_telebot.py b/tests/test_telebot.py index e9f3c5a29..a3d877f1d 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -553,3 +553,8 @@ def command_handler(message): tb.process_new_updates([update]) time.sleep(1) assert update.message.text == 'got' * 2 + + def test_chat_permissions(self): + tb = telebot.TeleBot(TOKEN) + permissions = types.ChatPermissions(can_send_messages=True, can_send_polls=False) + msg = tb.set_chat_permissions(CHAT_ID, permissions) From 1494946d02ec7d8fbeb88a3539d32483a5a1fc42 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 29 May 2020 12:37:23 +0300 Subject: [PATCH 0420/1808] Update test_telebot.py Build fix. --- tests/test_telebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index a3d877f1d..6ca56fbb0 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -555,6 +555,7 @@ def command_handler(message): assert update.message.text == 'got' * 2 def test_chat_permissions(self): + return # CHAT_ID is private chat, no permissions can be set tb = telebot.TeleBot(TOKEN) permissions = types.ChatPermissions(can_send_messages=True, can_send_polls=False) msg = tb.set_chat_permissions(CHAT_ID, permissions) From c24d1e2d0b3692a0ac90c9d50720b562f52c7814 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Sun, 7 Jun 2020 02:50:30 +0800 Subject: [PATCH 0421/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f1bb9dcc3..bb2a9e47e 100644 --- a/README.md +++ b/README.md @@ -648,5 +648,6 @@ Get help. Discuss. Chat. * [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov. * [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram. * [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...) +* [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 44872ce87de1e7dbdf8159e0a2dcc86205cba4b6 Mon Sep 17 00:00:00 2001 From: Kwisatz Haderach <38349702+Otxoto@users.noreply.github.com> Date: Mon, 22 Jun 2020 13:18:13 +0200 Subject: [PATCH 0422/1808] Complete User Class Added following fields: can_join_groups can_read_all_group_messages supports_inline_queries --- telebot/types.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index cf3d7e9c4..37e06fad6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -157,15 +157,21 @@ def de_json(cls, json_string): last_name = obj.get('last_name') username = obj.get('username') language_code = obj.get('language_code') - return cls(id, is_bot, first_name, last_name, username, language_code) + can_join_groups = obj.get('can_join_groups') + can_read_all_group_messages = obj.get('can_read_all_group_messages') + supports_inline_queries = obj.get('supports_inline_queries') + return cls(id, is_bot, first_name, last_name, username, language_code, can_join_groups, can_read_all_group_messages, supports_inline_queries) - def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None): + 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): self.id = id self.is_bot = is_bot self.first_name = first_name self.username = username self.last_name = last_name self.language_code = language_code + self.can_join_groups = can_join_groups + self.can_read_all_group_messages = can_read_all_group_messages + self.supports_inline_queries = supports_inline_queries def to_json(self): return json.dumps(self.to_dict()) @@ -176,7 +182,10 @@ def to_dict(self): 'first_name': self.first_name, 'last_name': self.last_name, 'username': self.username, - 'language_code': self.language_code} + 'language_code': self.language_code, + 'can_join_groups': self.can_join_groups, + 'can_read_all_group_messages': self.can_read_all_group_messages, + 'supports_inline_queries': self.supports_inline_queries} class GroupChat(JsonDeserializable): From 4f4c0891d9c6da96e212036c03d220e325e41598 Mon Sep 17 00:00:00 2001 From: Kwisatz Haderach <38349702+Otxoto@users.noreply.github.com> Date: Tue, 23 Jun 2020 20:10:12 +0200 Subject: [PATCH 0423/1808] Added thumb support to send_audio --- telebot/apihelper.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8a6ae751d..93ba91a56 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -541,7 +541,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -567,6 +567,11 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if thumb: + if not util.is_string(thumb): + files['thumb'] = thumb + else: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') From 29b432e65ad6d7a77ddd19c77472a08af897f547 Mon Sep 17 00:00:00 2001 From: Kwisatz Haderach <38349702+Otxoto@users.noreply.github.com> Date: Tue, 23 Jun 2020 20:12:46 +0200 Subject: [PATCH 0424/1808] Added thumb to send_audio --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ffde7e3a5..38867add6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -695,7 +695,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, - timeout=None): + timeout=None, thumb=None): """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. :param chat_id:Unique identifier for the message recipient @@ -713,7 +713,7 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None """ return types.Message.de_json( apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout)) + reply_markup, parse_mode, disable_notification, timeout, thumb)) def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): From 53ccef5e5e3261468b19059f36c3a6319352c487 Mon Sep 17 00:00:00 2001 From: Kwisatz Haderach <38349702+Otxoto@users.noreply.github.com> Date: Tue, 23 Jun 2020 20:14:52 +0200 Subject: [PATCH 0425/1808] added thumb parameter to send_video --- telebot/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 38867add6..c182463e2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -709,6 +709,7 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None :param parse_mode :param disable_notification: :param timeout: + :param thumb: :return: Message """ return types.Message.de_json( @@ -771,7 +772,7 @@ def send_sticker( disable_notification, timeout)) def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None): """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -784,11 +785,12 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param reply_markup: :param disable_notification: :param timeout: + :param thumb: :return: """ return types.Message.de_json( apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, - parse_mode, supports_streaming, disable_notification, timeout)) + parse_mode, supports_streaming, disable_notification, timeout, thumb)) def send_animation(self, chat_id, animation, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): From 99de5490a05ffcbd63fefb1d97d696387231c7d0 Mon Sep 17 00:00:00 2001 From: Kwisatz Haderach <38349702+Otxoto@users.noreply.github.com> Date: Tue, 23 Jun 2020 20:17:21 +0200 Subject: [PATCH 0426/1808] Added thumb parameter to send_video --- telebot/apihelper.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 93ba91a56..c4ab90624 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -435,7 +435,7 @@ def send_chat_action(token, chat_id, action, timeout=None): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -459,6 +459,11 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if thumb: + if not util.is_string(thumb): + files['thumb'] = thumb + else: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') From b5a4276282803375f347bf588e1f5dbf0cd0f180 Mon Sep 17 00:00:00 2001 From: Finbarrs <16977112+0xnu@users.noreply.github.com> Date: Thu, 2 Jul 2020 13:27:51 +0100 Subject: [PATCH 0427/1808] Update README.md Link to Sports bot added. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bb2a9e47e..3b04f1622 100644 --- a/README.md +++ b/README.md @@ -649,5 +649,6 @@ Get help. Discuss. Chat. * [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram. * [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...) * [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering +* [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 4afde9f5577227d7e5cdfe5cf78f43f98cc0ea0c Mon Sep 17 00:00:00 2001 From: Aragroth <51192359+Aragroth@users.noreply.github.com> Date: Thu, 2 Jul 2020 17:46:41 +0300 Subject: [PATCH 0428/1808] Update apihelper.py --- telebot/apihelper.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c4ab90624..e7a3df554 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -53,8 +53,8 @@ def _make_request(token, method_name, method='get', params=None, files=None): request_url = API_URL.format(token, method_name) logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) - read_timeout = READ_TIMEOUT - connect_timeout = CONNECT_TIMEOUT + read_timeout = READ_TIMEOUT + 70 * 2 + connect_timeout = CONNECT_TIMEOUT + 70 * 2 if files and format_header_param: fields.format_header_param = _no_encode(format_header_param) if params: @@ -435,7 +435,7 @@ def send_chat_action(token, chat_id, action, timeout=None): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -464,6 +464,10 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa files['thumb'] = thumb else: payload['thumb'] = thumb + if width: + payload['width'] = width + if height: + payload['height'] = height return _make_request(token, method_url, params=payload, files=files, method='post') From a69a358ebd9e9f5616813bbce58bd213dfa2268c Mon Sep 17 00:00:00 2001 From: Aragroth <51192359+Aragroth@users.noreply.github.com> Date: Thu, 2 Jul 2020 17:47:38 +0300 Subject: [PATCH 0429/1808] Update __init__.py --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c182463e2..b24013ba9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -772,7 +772,7 @@ def send_sticker( disable_notification, timeout)) def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None): """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -790,7 +790,7 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag """ return types.Message.de_json( apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, - parse_mode, supports_streaming, disable_notification, timeout, thumb)) + parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height)) def send_animation(self, chat_id, animation, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None): From f83f69ed50f0c53bb8539fd5167c822f51284117 Mon Sep 17 00:00:00 2001 From: Aragroth <51192359+Aragroth@users.noreply.github.com> Date: Thu, 2 Jul 2020 17:50:14 +0300 Subject: [PATCH 0430/1808] Update apihelper.py --- telebot/apihelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e7a3df554..07ae8f854 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -53,8 +53,8 @@ def _make_request(token, method_name, method='get', params=None, files=None): request_url = API_URL.format(token, method_name) logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) - read_timeout = READ_TIMEOUT + 70 * 2 - connect_timeout = CONNECT_TIMEOUT + 70 * 2 + read_timeout = READ_TIMEOUT + connect_timeout = CONNECT_TIMEOUT if files and format_header_param: fields.format_header_param = _no_encode(format_header_param) if params: From f4d978cd980dd593eca08481754fb0769022ef9c Mon Sep 17 00:00:00 2001 From: Aragroth <51192359+Aragroth@users.noreply.github.com> Date: Thu, 2 Jul 2020 17:51:34 +0300 Subject: [PATCH 0431/1808] Update apihelper.py --- telebot/apihelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 07ae8f854..e7a3df554 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -53,8 +53,8 @@ def _make_request(token, method_name, method='get', params=None, files=None): request_url = API_URL.format(token, method_name) logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) - read_timeout = READ_TIMEOUT - connect_timeout = CONNECT_TIMEOUT + read_timeout = READ_TIMEOUT + 70 * 2 + connect_timeout = CONNECT_TIMEOUT + 70 * 2 if files and format_header_param: fields.format_header_param = _no_encode(format_header_param) if params: From cdffeba829c2223a1505608cd71bf4e6d064141e Mon Sep 17 00:00:00 2001 From: Timur Davletshin Date: Fri, 3 Jul 2020 22:05:47 +0500 Subject: [PATCH 0432/1808] added NST --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3b04f1622..fb12d59b3 100644 --- a/README.md +++ b/README.md @@ -650,5 +650,6 @@ Get help. Discuss. Chat. * [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...) * [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering * [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates. +* [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 0f387db8d2e8bf0df7d95f8c33a302bba9ca4b05 Mon Sep 17 00:00:00 2001 From: daveusa31 <41593484+daveusa31@users.noreply.github.com> Date: Sat, 4 Jul 2020 20:45:48 +0300 Subject: [PATCH 0433/1808] Fix tabs --- telebot/util.py | 122 ++++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 9e97ad3ac..6e581b63c 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -25,67 +25,67 @@ class WorkerThread(threading.Thread): - count = 0 - - def __init__(self, exception_callback=None, queue=None, name=None): - if not name: - name = "WorkerThread{0}".format(self.__class__.count + 1) - self.__class__.count += 1 - if not queue: - queue = Queue.Queue() - - threading.Thread.__init__(self, name=name) - self.queue = queue - self.daemon = True - - self.received_task_event = threading.Event() - self.done_event = threading.Event() - self.exception_event = threading.Event() - self.continue_event = threading.Event() - - self.exception_callback = exception_callback - self.exc_info = None - self._running = True - self.start() - - def run(self): - while self._running: - try: - task, args, kwargs = self.queue.get(block=True, timeout=.5) - self.continue_event.clear() - self.received_task_event.clear() - self.done_event.clear() - self.exception_event.clear() - logger.debug("Received task") - self.received_task_event.set() - - task(*args, **kwargs) - logger.debug("Task complete") - self.done_event.set() - except Queue.Empty: - pass - except Exception as e: - logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc()) - self.exc_info = sys.exc_info() - self.exception_event.set() - - if self.exception_callback: - self.exception_callback(self, self.exc_info) - self.continue_event.wait() - - def put(self, task, *args, **kwargs): - self.queue.put((task, args, kwargs)) - - def raise_exceptions(self): - if self.exception_event.is_set(): - six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2]) - - def clear_exceptions(self): - self.exception_event.clear() - self.continue_event.set() - - def stop(self): - self._running = False + count = 0 + + def __init__(self, exception_callback=None, queue=None, name=None): + if not name: + name = "WorkerThread{0}".format(self.__class__.count + 1) + self.__class__.count += 1 + if not queue: + queue = Queue.Queue() + + threading.Thread.__init__(self, name=name) + self.queue = queue + self.daemon = True + + self.received_task_event = threading.Event() + self.done_event = threading.Event() + self.exception_event = threading.Event() + self.continue_event = threading.Event() + + self.exception_callback = exception_callback + self.exc_info = None + self._running = True + self.start() + + def run(self): + while self._running: + try: + task, args, kwargs = self.queue.get(block=True, timeout=.5) + self.continue_event.clear() + self.received_task_event.clear() + self.done_event.clear() + self.exception_event.clear() + logger.debug("Received task") + self.received_task_event.set() + + task(*args, **kwargs) + logger.debug("Task complete") + self.done_event.set() + except Queue.Empty: + pass + except Exception as e: + logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc()) + self.exc_info = sys.exc_info() + self.exception_event.set() + + if self.exception_callback: + self.exception_callback(self, self.exc_info) + self.continue_event.wait() + + def put(self, task, *args, **kwargs): + self.queue.put((task, args, kwargs)) + + def raise_exceptions(self): + if self.exception_event.is_set(): + six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2]) + + def clear_exceptions(self): + self.exception_event.clear() + self.continue_event.set() + + def stop(self): + self._running = False class ThreadPool: From 0304e6507f34786731423de3eca36a8f55c59a2e Mon Sep 17 00:00:00 2001 From: daveusa31 <41593484+daveusa31@users.noreply.github.com> Date: Sat, 4 Jul 2020 21:07:42 +0300 Subject: [PATCH 0434/1808] Append default parse_mode --- telebot/__init__.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c182463e2..7e0bd0f3c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -85,15 +85,17 @@ class TeleBot: """ def __init__( - self, token, threaded=True, skip_pending=False, num_threads=2, + self, token, parse_mode=None, threaded=True, skip_pending=False, num_threads=2, next_step_backend=None, reply_backend=None ): """ :param token: bot API token + :param parse_mode: default parse_mode :return: Telebot object. """ self.token = token + self.parse_mode = parse_mode self.update_listener = [] self.skip_pending = skip_pending @@ -632,6 +634,8 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me :param timeout: :return: API reply. """ + parse_mode = self.parse_mode if not parse_mode else parse_mode + return types.Message.de_json( apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) @@ -689,6 +693,8 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep :param reply_markup: :return: API reply. """ + parse_mode = self.parse_mode if not parse_mode else parse_mode + return types.Message.de_json( apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) @@ -731,6 +737,8 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa :param timeout: :return: Message """ + parse_mode = self.parse_mode if not parse_mode else parse_mode + return types.Message.de_json( apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) @@ -749,6 +757,8 @@ def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, r :param timeout: :return: API reply. """ + parse_mode = self.parse_mode if not parse_mode 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=caption)) @@ -785,9 +795,11 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param reply_markup: :param disable_notification: :param timeout: - :param thumb: + :param thumb: :return: """ + parse_mode = self.parse_mode if not parse_mode else parse_mode + return types.Message.de_json( apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb)) @@ -807,6 +819,8 @@ def send_animation(self, chat_id, animation, duration=None, caption=None, reply_ :param timeout: :return: """ + parse_mode = self.parse_mode if not parse_mode else parse_mode + return types.Message.de_json( apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) @@ -1165,6 +1179,8 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ :param reply_markup: :return: """ + parse_mode = self.parse_mode if not parse_mode 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) if type(result) == bool: # if edit inline message return is bool not Message. @@ -1367,6 +1383,8 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me :param reply_markup: :return: """ + parse_mode = self.parse_mode if not parse_mode else parse_mode + result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, parse_mode, reply_markup) if type(result) == bool: From f42ec4fe0da3d690f6416ffd6ec393af0728dd85 Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 6 Jul 2020 00:37:17 +0800 Subject: [PATCH 0435/1808] Bump version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 05b07606f..dc85d337f 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(filename): return file.read() setup(name='pyTelegramBotAPI', - version='3.7.1', + version='3.7.2', description='Python Telegram bot api. ', long_description=read('README.md'), long_description_content_type="text/markdown", From 49398f5c613595929a47ce4bbc3cdbb08ca9fed7 Mon Sep 17 00:00:00 2001 From: Purya Jafari <58905883+iPurya@users.noreply.github.com> Date: Tue, 7 Jul 2020 01:31:33 +0430 Subject: [PATCH 0436/1808] Update types.py Sending stickers everytime have different file_id, so for detecting special stickers we need a unique parameter. --- telebot/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 37e06fad6..cb98b9fba 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2204,6 +2204,7 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] width = obj['width'] height = obj['height'] is_animated = obj['is_animated'] @@ -2212,10 +2213,11 @@ def de_json(cls, json_string): set_name = obj.get('set_name') mask_position = MaskPosition.de_json(obj.get('mask_position')) file_size = obj.get('file_size') - return cls(file_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated) + return cls(file_id, file_unique_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated) - def __init__(self, file_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated): + def __init__(self, file_id, file_unique_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated): self.file_id = file_id + self.file_unique_id = file_unique_id self.width = width self.height = height self.thumb = thumb From d5e9f73821a9a42d4183ef31636c519c83b1639d Mon Sep 17 00:00:00 2001 From: Purya Jafari <58905883+iPurya@users.noreply.github.com> Date: Tue, 7 Jul 2020 02:38:06 +0430 Subject: [PATCH 0437/1808] Update test_types.py tests updated for pull request #908. --- tests/test_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index cc0c4cf2d..45ffa1ba6 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -57,7 +57,7 @@ def test_json_Message_Audio(): def test_json_Message_Sticker(): - json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 320, "height": 320}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb.height == 60 @@ -65,7 +65,7 @@ def test_json_Message_Sticker(): def test_json_Message_Sticker_without_thumb(): - json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb is None From d2d7cc39be5c6e356d849bf9737411404ed1963d Mon Sep 17 00:00:00 2001 From: Purya Jafari <58905883+iPurya@users.noreply.github.com> Date: Tue, 7 Jul 2020 03:39:00 +0430 Subject: [PATCH 0438/1808] Update test_types.py --- tests/test_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_types.py b/tests/test_types.py index 45ffa1ba6..742c113d8 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -57,7 +57,7 @@ def test_json_Message_Audio(): def test_json_Message_Sticker(): - json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 320, "height": 320}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 60, "height": 60}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb.height == 60 From 7c94eee3a23f0edbca3ad83ebbbef1a3bd860f62 Mon Sep 17 00:00:00 2001 From: Aragroth <51192359+Aragroth@users.noreply.github.com> Date: Sat, 11 Jul 2020 20:09:48 +0300 Subject: [PATCH 0439/1808] Update apihelper.py --- telebot/apihelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e7a3df554..07ae8f854 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -53,8 +53,8 @@ def _make_request(token, method_name, method='get', params=None, files=None): request_url = API_URL.format(token, method_name) logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) - read_timeout = READ_TIMEOUT + 70 * 2 - connect_timeout = CONNECT_TIMEOUT + 70 * 2 + read_timeout = READ_TIMEOUT + connect_timeout = CONNECT_TIMEOUT if files and format_header_param: fields.format_header_param = _no_encode(format_header_param) if params: From 6049de435617a07237f9a8b7b6d80203951ae589 Mon Sep 17 00:00:00 2001 From: zeph1997 Date: Thu, 16 Jul 2020 19:07:39 +0800 Subject: [PATCH 0440/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fb12d59b3..530641d82 100644 --- a/README.md +++ b/README.md @@ -651,5 +651,6 @@ Get help. Discuss. Chat. * [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering * [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates. * [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. +* [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From bc5d9c8d6984c5565081cea601e9b2b4d8f6d3aa Mon Sep 17 00:00:00 2001 From: zeph1997 Date: Thu, 16 Jul 2020 19:09:37 +0800 Subject: [PATCH 0441/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 530641d82..fa335532b 100644 --- a/README.md +++ b/README.md @@ -651,6 +651,6 @@ Get help. Discuss. Chat. * [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering * [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates. * [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. -* [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. +* [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From b50eb1bafb31aeee6cff6e7781ba5e7c7bfbbf1a Mon Sep 17 00:00:00 2001 From: EskiSlav Date: Fri, 17 Jul 2020 13:43:45 +0300 Subject: [PATCH 0442/1808] Added nested entities from Bot API 4.5 --- telebot/types.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index cb98b9fba..7660b80cb 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -479,8 +479,11 @@ def __html_text(self, text, entities): "pre" : "

{text}
", "code" : "{text}", #"url" : "{text}", # @badiboy plain URLs have no text and do not need tags - "text_link": "{text}" - } + "text_link": "{text}", + "strikethrough": "{text}", + "underline": "{text}" + } + if hasattr(self, "custom_subs"): for key, value in self.custom_subs.items(): _subs[key] = value @@ -511,8 +514,7 @@ def func(upd_text, subst_type=None, url=None, user=None): html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) offset += entity.length else: - # TODO: process nested entities from Bot API 4.5 - # Now ignoring them + # For future entities pass if offset * 2 < len(utf16_text): html_text += func(utf16_text[offset * 2:]) From c533a52e39d1efd1cfd1752bec0d297e49dd17d3 Mon Sep 17 00:00:00 2001 From: EskiSlav Date: Sat, 18 Jul 2020 00:25:00 +0300 Subject: [PATCH 0443/1808] Restored necessary comment --- telebot/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 7660b80cb..a622d29a8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -514,7 +514,8 @@ def func(upd_text, subst_type=None, url=None, user=None): html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) offset += entity.length else: - # For future entities + # TODO: process nested entities from Bot API 4.5 + # Now ignoring them pass if offset * 2 < len(utf16_text): html_text += func(utf16_text[offset * 2:]) From a02f499a206f503f2d92bb814d57f46208761f5f Mon Sep 17 00:00:00 2001 From: daveusa31 <41593484+daveusa31@users.noreply.github.com> Date: Sat, 18 Jul 2020 19:02:07 +0300 Subject: [PATCH 0444/1808] Documented the default parse_mode installation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa335532b..b106d1d62 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Then, open the file and create an instance of the TeleBot class. ```python import telebot -bot = telebot.TeleBot("TOKEN") +bot = telebot.TeleBot("TOKEN", parse_mode=None) # You can set parse_mode by default. HTML or MARKDOWN ``` *Note: Make sure to actually replace TOKEN with your own API token.* From dbe9ce49dfe5c7c77a47576c77f343d599fb647a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 21 Jul 2020 01:20:01 +0300 Subject: [PATCH 0445/1808] Minor updates in code --- telebot/__init__.py | 56 +++++++++++++++++++++++--------------------- telebot/apihelper.py | 25 +++++++++++++------- telebot/types.py | 2 +- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ffde7e3a5..14366b537 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -648,14 +648,14 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio return types.Message.de_json( apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) - def delete_message(self, chat_id, message_id): + def delete_message(self, chat_id, message_id, timeout=None): """ Use this method to delete message. Returns True on success. :param chat_id: in which chat to delete :param message_id: which message to delete :return: API reply. """ - return apihelper.delete_message(self.token, chat_id, message_id) + return apihelper.delete_message(self.token, chat_id, message_id, timeout) def send_dice( self, chat_id, @@ -1252,39 +1252,41 @@ def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_me def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, - is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, - provider_data=None, timeout=None): + send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, timeout=None): """ Sends invoice - :param chat_id: - :param title: - :param description: - :param invoice_payload: - :param provider_token: - :param currency: - :param prices: - :param start_parameter: - :param photo_url: - :param photo_size: - :param photo_width: - :param photo_height: - :param need_name: - :param need_phone_number: - :param need_email: - :param need_shipping_address: - :param is_flexible: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param provider_data: + :param chat_id: Unique identifier for the target private chat + :param title: Product name + :param description: Product description + :param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :param provider_token: Payments provider token, obtained via @Botfather + :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for. + :param photo_size: Photo size + :param photo_width: Photo width + :param photo_height: Photo height + :param need_name: Pass True, if you require the user's full name to complete the order + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :param need_email: Pass True, if you require the user's email to complete the order + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :param is_flexible: Pass True, if the final price depends on the shipping method + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_markup: 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 + :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. :return: """ result = apihelper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, - is_flexible, disable_notification, reply_to_message_id, reply_markup, - provider_data, timeout) + send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, + reply_to_message_id, reply_markup, provider_data, timeout) return types.Message.de_json(result) def send_poll( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8a6ae751d..b3a6abf4a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -320,7 +320,7 @@ def send_media_group( disable_notification=None, reply_to_message_id=None, timeout=None): method_url = r'sendMediaGroup' - media_json, files = _convert_input_media_array(media) + media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} if disable_notification is not None: payload['disable_notification'] = disable_notification @@ -781,7 +781,7 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): method_url = r'editMessageMedia' - media_json, file = _convert_input_media(media) + media_json, file = convert_input_media(media) payload = {'media': media_json} if chat_id: payload['chat_id'] = chat_id @@ -808,9 +808,11 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa return _make_request(token, method_url, params=payload, method='post') -def delete_message(token, chat_id, message_id): +def delete_message(token, chat_id, message_id, timeout=None): method_url = r'deleteMessage' payload = {'chat_id': chat_id, 'message_id': message_id} + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, method='post') @@ -890,7 +892,8 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m def send_invoice( token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, - need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, is_flexible=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, + send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, timeout=None): """ @@ -913,10 +916,12 @@ def send_invoice( :param need_email: Pass True, if you require the user's email to complete the order :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order :param is_flexible: Pass True, if the final price depends on the shipping method + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :param send_email_to_provider: Pass True, if user's email address should be sent to provider :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: 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 - :param provider_data: + :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. :return: """ method_url = r'sendInvoice' @@ -939,6 +944,10 @@ def send_invoice( payload['need_email'] = need_email if need_shipping_address is not None: payload['need_shipping_address'] = need_shipping_address + if send_phone_number_to_provider is not None: + payload['send_phone_number_to_provider'] = send_phone_number_to_provider + if send_email_to_provider is not None: + payload['send_email_to_provider'] = send_email_to_provider if is_flexible is not None: payload['is_flexible'] = is_flexible if disable_notification is not None: @@ -1154,13 +1163,13 @@ def _convert_markup(markup): return markup -def _convert_input_media(media): +def convert_input_media(media): if isinstance(media, types.InputMedia): - return media._convert_input_media() + return media.convert_input_media() return None, None -def _convert_input_media_array(array): +def convert_input_media_array(array): media = [] files = {} for input_media in array: diff --git a/telebot/types.py b/telebot/types.py index cf3d7e9c4..19c6d35c8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2268,7 +2268,7 @@ def to_dict(self): json_dict['parse_mode'] = self.parse_mode return json_dict - def _convert_input_media(self): + def convert_input_media(self): if util.is_string(self.media): return self.to_json(), None From 0ac64469b07600b4ce79f5edbf793551afbfdd64 Mon Sep 17 00:00:00 2001 From: mrpes <68982655+mrpes@users.noreply.github.com> Date: Thu, 30 Jul 2020 09:34:51 +0500 Subject: [PATCH 0446/1808] Retry on requests error Added RETRY_ON_ERROR var. If its value is true, we'll try to get proper result MAX_RETRIES times, with RETRY_TIMEOUT delay between requests. Last request will be called outside of the try block, so it will throw an exception on failure P.S. I'm actually not sure if there are better ways to solve this problem, but this was my way of solving it --- telebot/apihelper.py | 45 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 138c4387d..a47976b43 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import time try: import ujson as json @@ -6,6 +7,7 @@ import json import requests +from requests.exceptions import HTTPError, ConnectionError, Timeout try: from requests.packages.urllib3 import fields @@ -28,6 +30,10 @@ CONNECT_TIMEOUT = 3.5 READ_TIMEOUT = 9999 +RETRY_ON_ERROR = False +RETRY_TIMEOUT = 2 +MAX_RETRIES = 15 + CUSTOM_SERIALIZER = None ENABLE_MIDDLEWARE = False @@ -62,9 +68,42 @@ def _make_request(token, method_name, method='get', params=None, files=None): read_timeout = params.pop('timeout') + 10 if 'connect-timeout' in params: connect_timeout = params.pop('connect-timeout') + 10 - result = _get_req_session().request( - method, request_url, params=params, files=files, - timeout=(connect_timeout, read_timeout), proxies=proxy) + + if RETRY_ON_ERROR: + got_result = False + current_try = 0 + + while not got_result and current_try Date: Fri, 31 Jul 2020 03:10:34 +0500 Subject: [PATCH 0447/1808] Exception classes redesign Replacing 1 exception class with 3 more specific classes: HTTP Exception (server returned http code != 200), Invalid JSON Exception (server returned invalid json), Telegram Expection (telegram returned ok != true) All 3 classes extend BaseApiException so we can handle all API exceptions at the same time --- telebot/apihelper.py | 63 +++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a47976b43..a1b91a159 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -116,27 +116,22 @@ def _check_result(method_name, result): - The content of the result is invalid JSON. - The method call was unsuccessful (The JSON 'ok' field equals False) - :raises ApiException: if one of the above listed cases is applicable + :raises BaseApiException: if one of the above listed cases is applicable :param method_name: The name of the method called :param result: The returned result of the method request :return: The result parsed to a JSON dictionary. """ if result.status_code != 200: - msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \ - .format(result.status_code, result.reason, result.text.encode('utf8')) - raise ApiException(msg, method_name, result) + raise ApiHTTPException(method_name, result) try: result_json = result.json() except: - msg = 'The server returned an invalid JSON response. Response body:\n[{0}]' \ - .format(result.text.encode('utf8')) - raise ApiException(msg, method_name, result) + raise ApiInvalidJSONException(method_name, result) if not result_json['ok']: - msg = 'Error code: {0} Description: {1}' \ - .format(result_json['error_code'], result_json['description']) - raise ApiException(msg, method_name, result) + raise ApiTelegramException(msg, method_name, result, result_json) + return result_json @@ -165,9 +160,8 @@ def download_file(token, file_path): result = _get_req_session().get(url, proxies=proxy) if result.status_code != 200: - msg = 'The server returned HTTP {0} {1}. Response body:\n[{2}]' \ - .format(result.status_code, result.reason, result.text) - raise ApiException(msg, 'Download file', result) + raise ApiHTTPException('Download file', result) + return result.content @@ -1245,15 +1239,52 @@ def wrapper(key, val): return wrapper -class ApiException(Exception): +class BaseApiException(Exception): """ - This class represents an Exception thrown when a call to the Telegram API fails. + This class represents a base Exception thrown when a call to the Telegram API fails. In addition to an informative message, it has a `function_name` and a `result` attribute, which respectively contain the name of the failed function and the returned result that made the function to be considered as failed. """ def __init__(self, msg, function_name, result): - super(ApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg)) + super(BaseApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg)) self.function_name = function_name self.result = result + +class ApiHTTPException(BaseApiException): + """ + This class represents an Exception thrown when a call to the + Telegram API server returns HTTP code that is not 200. + """ + def __init__(self, function_name, result): + super(ApiHTTPException, self).__init__( + "The server returned HTTP {0} {1}. Response body:\n[{2}]" \ + .format(result.status_code, result.reason, result.text.encode('utf8')), + function_name, + result) + +class ApiInvalidJSONException(BaseApiException): + """ + This class represents an Exception thrown when a call to the + Telegram API server returns invalid json. + """ + def __init__(self, function_name, result): + super(ApiInvalidJSONException, self).__init__( + "The server returned an invalid JSON response. Response body:\n[{0}]" \ + .format(result.text.encode('utf8')), + function_name, + result) + +class ApiTelegramException(BaseApiException): + """ + This class represents an Exception thrown when a Telegram API returns error code. + """ + def __init__(self, function_name, result, result_json): + super(ApiTelegramException, self).__init__( + "Error code: {0} Description: {1}" \ + .format(result_json['error_code'], result_json['description']), + function_name, + result) + self.result_json = result_json + From 67536d4eec7677764657c27f2431d1e31de539fe Mon Sep 17 00:00:00 2001 From: mrpes <68982655+mrpes@users.noreply.github.com> Date: Fri, 31 Jul 2020 03:30:03 +0500 Subject: [PATCH 0448/1808] Fixing backward compatibility issue Just realized that renaming ApiException to BaseApiException will cause backward compatibility issue --- telebot/apihelper.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a1b91a159..e654a72dd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -116,7 +116,7 @@ def _check_result(method_name, result): - The content of the result is invalid JSON. - The method call was unsuccessful (The JSON 'ok' field equals False) - :raises BaseApiException: if one of the above listed cases is applicable + :raises ApiException: if one of the above listed cases is applicable :param method_name: The name of the method called :param result: The returned result of the method request :return: The result parsed to a JSON dictionary. @@ -1239,7 +1239,7 @@ def wrapper(key, val): return wrapper -class BaseApiException(Exception): +class ApiException(Exception): """ This class represents a base Exception thrown when a call to the Telegram API fails. In addition to an informative message, it has a `function_name` and a `result` attribute, which respectively @@ -1248,11 +1248,11 @@ class BaseApiException(Exception): """ def __init__(self, msg, function_name, result): - super(BaseApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg)) + super(ApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg)) self.function_name = function_name self.result = result -class ApiHTTPException(BaseApiException): +class ApiHTTPException(ApiException): """ This class represents an Exception thrown when a call to the Telegram API server returns HTTP code that is not 200. @@ -1264,7 +1264,7 @@ def __init__(self, function_name, result): function_name, result) -class ApiInvalidJSONException(BaseApiException): +class ApiInvalidJSONException(ApiException): """ This class represents an Exception thrown when a call to the Telegram API server returns invalid json. @@ -1276,7 +1276,7 @@ def __init__(self, function_name, result): function_name, result) -class ApiTelegramException(BaseApiException): +class ApiTelegramException(ApiException): """ This class represents an Exception thrown when a Telegram API returns error code. """ From 0ab908705bef0ec46786eb545bb8ca09b52253b2 Mon Sep 17 00:00:00 2001 From: mrpes <68982655+mrpes@users.noreply.github.com> Date: Fri, 31 Jul 2020 10:39:04 +0500 Subject: [PATCH 0449/1808] Support for PIL images as photo argument Added autoconversion of PIL image to file-like object. PIL module is optional --- telebot/util.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index 6e581b63c..b4589d47e 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -19,6 +19,13 @@ import queue as Queue import logging +try: + import PIL + from io import BytesIO + pil_imported = True +except: + pil_imported = False + logger = logging.getLogger('TeleBot') thread_local = threading.local() @@ -159,6 +166,19 @@ def wrapper(*args, **kwargs): def is_string(var): return isinstance(var, string_types) +def is_pil_image(var): + return pil_imported and isinstance(var, PIL.Image.Image) + +def pil_image_to_file(image, extension='JPEG', quality='web_low'): + if pil_imported: + photoBuffer = BytesIO() + image.convert('RGB').save(photoBuffer, extension, quality=quality) + photoBuffer.seek(0) + + return photoBuffer + else: + raise RuntimeError('PIL module is not imported') + def is_command(text): """ Checks if `text` is a command. Telegram chat commands start with the '/' character. From 97aa9637cb4ee53fc323369150da2a226c40b744 Mon Sep 17 00:00:00 2001 From: mrpes <68982655+mrpes@users.noreply.github.com> Date: Fri, 31 Jul 2020 10:45:58 +0500 Subject: [PATCH 0450/1808] Update apihelper.py --- telebot/apihelper.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e654a72dd..7603d17b0 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -329,10 +329,12 @@ def send_photo( method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None - if not util.is_string(photo): - files = {'photo': photo} - else: + if util.is_string(photo): payload['photo'] = photo + elif util.is_pil_image(photo): + files = {'photo': util.pil_image_to_file(photo)} + else: + files = {'photo': photo} if caption: payload['caption'] = caption if reply_to_message_id: @@ -743,10 +745,12 @@ def set_chat_photo(token, chat_id, photo): method_url = 'setChatPhoto' payload = {'chat_id': chat_id} files = None - if not util.is_string(photo): - files = {'photo': photo} - else: + if util.is_string(photo): payload['photo'] = photo + elif util.is_pil_image(photo): + files = {'photo': util.pil_image_to_file(photo)} + else: + files = {'photo': photo} return _make_request(token, method_url, params=payload, files=files, method='post') From 5823ca5613e29e28b881693da19301961eeaafb8 Mon Sep 17 00:00:00 2001 From: mrpes <68982655+mrpes@users.noreply.github.com> Date: Sat, 1 Aug 2020 01:28:56 +0500 Subject: [PATCH 0451/1808] Minor keyboard code redesign --- telebot/types.py | 96 ++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index b2d250e30..4b2c8dfcc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -66,9 +66,9 @@ def check_json(json_type): :param json_type: :return: """ - if isinstance(json_type, dict): + if util.is_dict(json_type): return json_type - elif isinstance(json_type, str): + elif util.is_string(json_type): return json.loads(json_type) else: raise ValueError("json_type should be a json dict or string.") @@ -806,36 +806,44 @@ def to_json(self): class ReplyKeyboardMarkup(JsonSerializable): def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3): + if row_width>12: + raise ValueError('Telegram does not support reply keyboard row width over 12') + self.resize_keyboard = resize_keyboard self.one_time_keyboard = one_time_keyboard self.selective = selective self.row_width = row_width self.keyboard = [] - def add(self, *args): + def add(self, *args, row_width=None): """ This function adds strings to the keyboard, while not exceeding row_width. E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} when row_width is set to 1. When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} See https://core.telegram.org/bots/api#replykeyboardmarkup + :raises ValueError: If row_width > 12 :param args: KeyboardButton to append to the keyboard + :param row_width: width of row + :return: self, to allow function chaining. """ - i = 1 - row = [] - for button in args: - if util.is_string(button): - row.append({'text': button}) - elif isinstance(button, bytes): - row.append({'text': button.decode('utf-8')}) - else: - row.append(button.to_dict()) - if i % self.row_width == 0: - self.keyboard.append(row) - row = [] - i += 1 - if len(row) > 0: - self.keyboard.append(row) + row_width = row_width or self.row_width + + if row_width>12: + raise ValueError('Telegram does not support reply keyboard row width over 12') + + for row in util.chunks(args, row_width): + button_array = [] + for button in row: + if util.is_string(button): + button_array.append({'text': button}) + elif util.is_bytes(button): + button_array.append({'text': button.decode('utf-8')}) + else: + button_array.append(button.to_dict()) + self.keyboard.append(button_array) + + return self def row(self, *args): """ @@ -845,14 +853,8 @@ def row(self, *args): :param args: strings :return: self, to allow function chaining. """ - btn_array = [] - for button in args: - if util.is_string(button): - btn_array.append({'text': button}) - else: - btn_array.append(button.to_dict()) - self.keyboard.append(btn_array) - return self + + return self.add(args, 12) def to_json(self): """ @@ -904,13 +906,17 @@ def __init__(self, row_width=3): """ This object represents an inline keyboard that appears right next to the message it belongs to. - + + :raises ValueError: If row_width > 8 :return: """ + if row_width>8: + raise ValueError('Telegram does not support inline keyboard row width over 8') + self.row_width = row_width self.keyboard = [] - def add(self, *args): + def add(self, *args, row_width=None): """ This method adds buttons to the keyboard without exceeding row_width. @@ -920,20 +926,23 @@ def add(self, *args): When row_width is set to 2, the result: {keyboard: [["A", "B"], ["C"]]} See https://core.telegram.org/bots/api#inlinekeyboardmarkup - + + :raises ValueError: If row_width > 8 :param args: Array of InlineKeyboardButton to append to the keyboard + :param row_width: width of row + :return: self, to allow function chaining. """ - i = 1 - row = [] - for button in args: - row.append(button.to_dict()) - if i % self.row_width == 0: - self.keyboard.append(row) - row = [] - i += 1 - if len(row) > 0: - self.keyboard.append(row) - + row_width = row_width or self.row_width + + if row_width>8: + raise ValueError('Telegram does not support inline keyboard row width over 8') + + for row in util.chunks(args, row_width): + button_array = [button.to_dict() for button in row] + self.keyboard.append(button_array) + + return self + def row(self, *args): """ Adds a list of InlineKeyboardButton to the keyboard. @@ -942,13 +951,12 @@ def row(self, *args): InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: '{keyboard: [["A"], ["B", "C"]]}' See https://core.telegram.org/bots/api#inlinekeyboardmarkup - + :param args: Array of InlineKeyboardButton to append to the keyboard :return: self, to allow function chaining. """ - button_array = [button.to_dict() for button in args] - self.keyboard.append(button_array) - return self + + return self.add(args, 8) def to_json(self): """ From 317a490cf04ea5fe0a6a0ff675ee62c590b82ed6 Mon Sep 17 00:00:00 2001 From: mrpes <68982655+mrpes@users.noreply.github.com> Date: Sat, 1 Aug 2020 01:30:38 +0500 Subject: [PATCH 0452/1808] Type checking moved to utils --- telebot/util.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index b4589d47e..7612ec48c 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -166,6 +166,12 @@ def wrapper(*args, **kwargs): def is_string(var): return isinstance(var, string_types) +def is_dict(var): + return isinstance(var, dict) + +def is_bytes(var): + return isinstance(var, bytes) + def is_pil_image(var): return pil_imported and isinstance(var, PIL.Image.Image) @@ -278,6 +284,11 @@ def per_thread(key, construct_value, reset=False): return getattr(thread_local, key) +def chunks(lst, n): + """Yield successive n-sized chunks from lst.""" + # https://stackoverflow.com/a/312464/9935473 + for i in range(0, len(lst), n): + yield lst[i:i + n] def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) From 4e5fb59fc01dc50b64730f861ed5a21c06d57400 Mon Sep 17 00:00:00 2001 From: "Mr. Dog" <68982655+mrpes@users.noreply.github.com> Date: Sun, 2 Aug 2020 20:20:33 +0500 Subject: [PATCH 0453/1808] Replace exceptions with warnings Also further PIL support added --- telebot/types.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 4b2c8dfcc..4e3327739 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +import logging + try: import ujson as json except ImportError: @@ -9,6 +11,7 @@ from telebot import util +logger = logging.getLogger('TeleBot') class JsonSerializable(object): """ @@ -807,7 +810,8 @@ def to_json(self): class ReplyKeyboardMarkup(JsonSerializable): def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3): if row_width>12: - raise ValueError('Telegram does not support reply keyboard row width over 12') + logger.warning('Telegram does not support reply keyboard row width over 12') + row_width=12 self.resize_keyboard = resize_keyboard self.one_time_keyboard = one_time_keyboard @@ -822,15 +826,16 @@ def add(self, *args, row_width=None): when row_width is set to 1. When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} See https://core.telegram.org/bots/api#replykeyboardmarkup - :raises ValueError: If row_width > 12 :param args: KeyboardButton to append to the keyboard :param row_width: width of row :return: self, to allow function chaining. """ row_width = row_width or self.row_width + if row_width>12: - raise ValueError('Telegram does not support reply keyboard row width over 12') + logger.warning('Telegram does not support reply keyboard row width over 12') + row_width=12 for row in util.chunks(args, row_width): button_array = [] @@ -907,12 +912,12 @@ def __init__(self, row_width=3): This object represents an inline keyboard that appears right next to the message it belongs to. - :raises ValueError: If row_width > 8 :return: """ if row_width>8: - raise ValueError('Telegram does not support inline keyboard row width over 8') - + logger.warning('Telegram does not support inline keyboard row width over 8') + row_width=8 + self.row_width = row_width self.keyboard = [] @@ -927,7 +932,6 @@ def add(self, *args, row_width=None): {keyboard: [["A", "B"], ["C"]]} See https://core.telegram.org/bots/api#inlinekeyboardmarkup - :raises ValueError: If row_width > 8 :param args: Array of InlineKeyboardButton to append to the keyboard :param row_width: width of row :return: self, to allow function chaining. @@ -935,7 +939,8 @@ def add(self, *args, row_width=None): row_width = row_width or self.row_width if row_width>8: - raise ValueError('Telegram does not support inline keyboard row width over 8') + logger.warning('Telegram does not support inline keyboard row width over 8') + row_width=8 for row in util.chunks(args, row_width): button_array = [button.to_dict() for button in row] @@ -2299,6 +2304,9 @@ def convert_input_media(self): class InputMediaPhoto(InputMedia): def __init__(self, media, caption=None, parse_mode=None): + if util.is_pil_image(media): + media = util.pil_image_to_file(media) + super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) def to_dict(self): From 1ba093cb02f40168afe61161a05d1908620c5414 Mon Sep 17 00:00:00 2001 From: "Mr. Dog" <68982655+mrpes@users.noreply.github.com> Date: Sun, 2 Aug 2020 20:30:58 +0500 Subject: [PATCH 0454/1808] Change logger level to warning --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 44119badf..ce41c6dc9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -18,7 +18,7 @@ console_output_handler.setFormatter(formatter) logger.addHandler(console_output_handler) -logger.setLevel(logging.ERROR) +logger.setLevel(logging.WARNING) from telebot import apihelper, types, util from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend From cc36207992a6003c673a27e18ab7b48ffb5c87cf Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 2 Aug 2020 18:58:22 +0300 Subject: [PATCH 0455/1808] Minor keyboard update followup --- telebot/__init__.py | 2 +- telebot/types.py | 38 ++++++++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ce41c6dc9..44119badf 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -18,7 +18,7 @@ console_output_handler.setFormatter(formatter) logger.addHandler(console_output_handler) -logger.setLevel(logging.WARNING) +logger.setLevel(logging.ERROR) from telebot import apihelper, types, util from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend diff --git a/telebot/types.py b/telebot/types.py index 4e3327739..3ea5e6eb1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -808,10 +808,13 @@ def to_json(self): class ReplyKeyboardMarkup(JsonSerializable): + max_row_keys = 12 + def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3): - if row_width>12: - logger.warning('Telegram does not support reply keyboard row width over 12') - row_width=12 + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys self.resize_keyboard = resize_keyboard self.one_time_keyboard = one_time_keyboard @@ -830,12 +833,14 @@ def add(self, *args, row_width=None): :param row_width: width of row :return: self, to allow function chaining. """ - row_width = row_width or self.row_width + if row_width is None: + row_width = self.row_width - if row_width>12: - logger.warning('Telegram does not support reply keyboard row width over 12') - row_width=12 + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys for row in util.chunks(args, row_width): button_array = [] @@ -907,6 +912,8 @@ def to_dict(self): class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): + max_row_keys = 12 + def __init__(self, row_width=3): """ This object represents an inline keyboard that appears @@ -914,9 +921,10 @@ def __init__(self, row_width=3): :return: """ - if row_width>8: - logger.warning('Telegram does not support inline keyboard row width over 8') - row_width=8 + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys self.row_width = row_width self.keyboard = [] @@ -936,11 +944,13 @@ def add(self, *args, row_width=None): :param row_width: width of row :return: self, to allow function chaining. """ - row_width = row_width or self.row_width + if row_width is None: + row_width = self.row_width - if row_width>8: - logger.warning('Telegram does not support inline keyboard row width over 8') - row_width=8 + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys for row in util.chunks(args, row_width): button_array = [button.to_dict() for button in row] From c17a2379ba014d5c87352c9c0f2d48300f7f7398 Mon Sep 17 00:00:00 2001 From: "Mr. Dog" <68982655+mrpes@users.noreply.github.com> Date: Mon, 3 Aug 2020 06:39:12 +0500 Subject: [PATCH 0456/1808] Exceptions classes redesign followup --- telebot/apihelper.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 7603d17b0..3f3ddfd75 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -105,7 +105,10 @@ def _make_request(token, method_name, method='get', params=None, files=None): timeout=(connect_timeout, read_timeout), proxies=proxy) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) - return _check_result(method_name, result)['result'] + + json_result = _check_result(method_name, result) + if json_result: + return json_result['result'] def _check_result(method_name, result): @@ -121,18 +124,18 @@ def _check_result(method_name, result): :param result: The returned result of the method request :return: The result parsed to a JSON dictionary. """ - if result.status_code != 200: - raise ApiHTTPException(method_name, result) - try: result_json = result.json() except: raise ApiInvalidJSONException(method_name, result) - - if not result_json['ok']: - raise ApiTelegramException(msg, method_name, result, result_json) - - return result_json + else: + if not result_json['ok']: + raise ApiTelegramException(method_name, result, result_json) + + elif result.status_code != 200: + raise ApiHTTPException(method_name, result) + + return result_json def get_me(token): @@ -1291,4 +1294,5 @@ def __init__(self, function_name, result, result_json): function_name, result) self.result_json = result_json + self.error_code = result_json['error_code'] From 1bb98483c282591766fcfccf04ec85749e4db7cc Mon Sep 17 00:00:00 2001 From: "Mr. Dog" <68982655+mrpes@users.noreply.github.com> Date: Tue, 4 Aug 2020 05:34:13 +0500 Subject: [PATCH 0457/1808] Update apihelper.py --- telebot/apihelper.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3f3ddfd75..964104ae4 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -127,14 +127,15 @@ def _check_result(method_name, result): try: result_json = result.json() except: - raise ApiInvalidJSONException(method_name, result) + if result.status_code != 200: + raise ApiHTTPException(method_name, result) + else: + raise ApiInvalidJSONException(method_name, result) + else: if not result_json['ok']: raise ApiTelegramException(method_name, result, result_json) - elif result.status_code != 200: - raise ApiHTTPException(method_name, result) - return result_json From a5fd407eb6a46608db0286ab7650665f8953087c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 4 Aug 2020 12:29:56 +0300 Subject: [PATCH 0458/1808] Bugfix and DISABLE_KEYLEN_ERROR Bugfix and DISABLE_KEYLEN_ERROR to supress keyboard length errors. --- telebot/apihelper.py | 3 +-- telebot/types.py | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 7603d17b0..fa61fe655 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -11,7 +11,6 @@ try: from requests.packages.urllib3 import fields - format_header_param = fields.format_header_param except ImportError: format_header_param = None @@ -130,7 +129,7 @@ def _check_result(method_name, result): raise ApiInvalidJSONException(method_name, result) if not result_json['ok']: - raise ApiTelegramException(msg, method_name, result, result_json) + raise ApiTelegramException(method_name, result, result_json) return result_json diff --git a/telebot/types.py b/telebot/types.py index 3ea5e6eb1..4d35176f9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11,8 +11,11 @@ from telebot import util +DISABLE_KEYLEN_ERROR = False + logger = logging.getLogger('TeleBot') + class JsonSerializable(object): """ Subclasses of this class are guaranteed to be able to be converted to JSON format. @@ -813,7 +816,8 @@ class ReplyKeyboardMarkup(JsonSerializable): def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3): if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases - logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + if not DISABLE_KEYLEN_ERROR: + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys self.resize_keyboard = resize_keyboard @@ -839,7 +843,8 @@ def add(self, *args, row_width=None): if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases - logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + if not DISABLE_KEYLEN_ERROR: + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys for row in util.chunks(args, row_width): From c6f51f6c5581cc3f60b6a2ee5c2955e39ef7523b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 4 Aug 2020 18:28:35 +0300 Subject: [PATCH 0459/1808] CopyPaste bugfix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 4d35176f9..844620296 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -917,7 +917,7 @@ def to_dict(self): class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): - max_row_keys = 12 + max_row_keys = 8 def __init__(self, row_width=3): """ From ec79d1dc1ee1b5f0e377b58e2b97b45f64a46b6e Mon Sep 17 00:00:00 2001 From: "Mr. Dog" <68982655+mrpes@users.noreply.github.com> Date: Tue, 4 Aug 2020 23:45:33 +0500 Subject: [PATCH 0460/1808] Keyboard bugfix --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 844620296..4ef068d91 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -869,7 +869,7 @@ def row(self, *args): :return: self, to allow function chaining. """ - return self.add(args, 12) + return self.add(*args, row_width=self.max_row_keys) def to_json(self): """ @@ -976,7 +976,7 @@ def row(self, *args): :return: self, to allow function chaining. """ - return self.add(args, 8) + return self.add(*args, row_width=self.max_row_keys) def to_json(self): """ From 52511fce48810dbfe9ffc6eed2c05f35b0285d02 Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo <66796758+barbax7@users.noreply.github.com> Date: Thu, 13 Aug 2020 12:14:57 +0200 Subject: [PATCH 0461/1808] Update README.md Added my bot to the list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b106d1d62..5a4a7a243 100644 --- a/README.md +++ b/README.md @@ -652,5 +652,6 @@ Get help. Discuss. Chat. * [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates. * [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. * [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. +* [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 484c3a4c4829511173276959e5747681dd393be5 Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 14 Aug 2020 10:50:56 +0530 Subject: [PATCH 0462/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5a4a7a243..59fa784f0 100644 --- a/README.md +++ b/README.md @@ -653,5 +653,6 @@ Get help. Discuss. Chat. * [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. * [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. * [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read. +* [BlueProjects](https://t.me/BlueProjects) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Channel Where More Than 4 Wonderful Bots Are Listed Up!! Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 1cd36253f0872131f33be94f336a9fd4c21e6b44 Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 14 Aug 2020 11:36:04 +0530 Subject: [PATCH 0463/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 59fa784f0..5ca5b550b 100644 --- a/README.md +++ b/README.md @@ -653,6 +653,6 @@ Get help. Discuss. Chat. * [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. * [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. * [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read. -* [BlueProjects](https://t.me/BlueProjects) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Channel Where More Than 4 Wonderful Bots Are Listed Up!! +* [BlueProjects](https://t.me/BlueProjects) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Channel Where More Than 4 Wonderful Bots Are Listed Up. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 2aaff09c39b2b742dd6f55d7c6a5915a790d7f88 Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 14 Aug 2020 11:38:57 +0530 Subject: [PATCH 0464/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ca5b550b..66c9c09f2 100644 --- a/README.md +++ b/README.md @@ -653,6 +653,6 @@ Get help. Discuss. Chat. * [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. * [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. * [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read. -* [BlueProjects](https://t.me/BlueProjects) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Channel Where More Than 4 Wonderful Bots Are Listed Up. +* [Blue_CC_Bot](https://t.me/Blue_CC_Bot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Checks Your Given Credit Cards And Says Which Is A Real,Card And Which Is Fake. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 3b57c288b4ca52cceb29925851e349a9688d1f29 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 17 Aug 2020 14:08:20 +0530 Subject: [PATCH 0465/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 66c9c09f2..6c68f8440 100644 --- a/README.md +++ b/README.md @@ -654,5 +654,6 @@ Get help. Discuss. Chat. * [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. * [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read. * [Blue_CC_Bot](https://t.me/Blue_CC_Bot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Checks Your Given Credit Cards And Says Which Is A Real,Card And Which Is Fake. +* [RandomInfoBot](https://t.me/RandomInfoBot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Generates Random Information Of Humans Scraped From Over 13 Websites. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 18eb8eb605a08f1a6851e9524cfcba921d65159c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 19 Aug 2020 23:57:48 +0300 Subject: [PATCH 0466/1808] Two None checks --- telebot/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index 7612ec48c..67c6abfd9 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -191,6 +191,7 @@ def is_command(text): :param text: Text to check. :return: True if `text` is a command, else False. """ + if (text is None): return None return text.startswith('/') @@ -208,6 +209,7 @@ def extract_command(text): :param text: String to extract the command from :return: the command if `text` is a command (according to is_command), else None. """ + if (text is None): return None return text.split()[0].split('@')[0][1:] if is_command(text) else None From 8b50dc488b7131629ff4d6728a0075ee755c438f Mon Sep 17 00:00:00 2001 From: rf0x1d Date: Fri, 21 Aug 2020 11:09:43 +0300 Subject: [PATCH 0467/1808] added missing thumb params and more --- setup.py | 7 ++++++- telebot/__init__.py | 32 ++++++++++++++++++++------------ telebot/apihelper.py | 14 ++++++++++---- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/setup.py b/setup.py index dc85d337f..f176609f6 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,18 @@ #!/usr/bin/env python from setuptools import setup from io import open +import re def read(filename): with open(filename, encoding='utf-8') as file: return file.read() +with open('telebot/version.py', 'r', encoding='utf-8') as f: # Credits: LonamiWebs + version = re.search(r"^__version__\s*=\s*'(.*)'.*$", + f.read(), flags=re.MULTILINE).group(1) + setup(name='pyTelegramBotAPI', - version='3.7.2', + version=version, description='Python Telegram bot api. ', long_description=read('README.md'), long_description_content_type="text/markdown", diff --git a/telebot/__init__.py b/telebot/__init__.py index 44119badf..063163eeb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -743,8 +743,8 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) - def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None, thumb=None): """ Use this method to send general files. :param chat_id: @@ -755,13 +755,14 @@ def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, r :param parse_mode: :param disable_notification: :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent :return: API reply. """ parse_mode = self.parse_mode if not parse_mode else parse_mode - return types.Message.de_json( + 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=caption)) + parse_mode, disable_notification, timeout, caption, thumb)) def send_sticker( self, chat_id, data, reply_to_message_id=None, reply_markup=None, @@ -795,7 +796,9 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param reply_markup: :param disable_notification: :param timeout: - :param thumb: + :param thumb: InputFile or String : Thumbnail of the file sent + :param width: + :param height: :return: """ parse_mode = self.parse_mode if not parse_mode else parse_mode @@ -804,8 +807,10 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height)) - def send_animation(self, chat_id, animation, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + def send_animation(self, chat_id, animation, duration=None, + caption=None, reply_to_message_id=None, + reply_markup=None, parse_mode=None, + disable_notification=None, timeout=None, thumb=None): """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -817,18 +822,20 @@ def send_animation(self, chat_id, animation, duration=None, caption=None, reply_ :param reply_markup: :param disable_notification: :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent :return: """ parse_mode = self.parse_mode if not parse_mode else parse_mode return types.Message.de_json( apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout)) + parse_mode, disable_notification, timeout, thumb)) - def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + def send_video_note(self, chat_id, data, duration=None, length=None, + reply_to_message_id=None, reply_markup=None, + disable_notification=None, timeout=None, thumb=None): """ - Use this method to send video files, Telegram clients support mp4 videos. + As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds @@ -837,11 +844,12 @@ def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_me :param reply_markup: :param disable_notification: :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent :return: """ return types.Message.de_json( apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout)) + disable_notification, timeout, thumb)) def send_media_group( self, chat_id, media, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5d668ac69..5561d3bce 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -509,7 +509,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None, thumb=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -531,6 +531,8 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if thumb: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') @@ -561,7 +563,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + disable_notification=None, timeout=None, thumb=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -571,7 +573,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['video_note'] = data if duration: payload['duration'] = duration - if length: + if length and (str(length).isdigit() and int(length) <= 639): payload['length'] = length else: payload['length'] = 639 # seems like it is MAX length size @@ -583,6 +585,8 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if thumb: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') @@ -622,7 +626,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, - disable_notification=None, timeout=None, caption=None): + disable_notification=None, timeout=None, caption=None, thumb=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -642,6 +646,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['connect-timeout'] = timeout if caption: payload['caption'] = caption + if thumb: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') From 0ab4046a4f58bdbb2a91cc0995011b205d432098 Mon Sep 17 00:00:00 2001 From: rf0x1d Date: Fri, 21 Aug 2020 11:09:53 +0300 Subject: [PATCH 0468/1808] Create version.py --- telebot/version.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 telebot/version.py diff --git a/telebot/version.py b/telebot/version.py new file mode 100644 index 000000000..92aad920e --- /dev/null +++ b/telebot/version.py @@ -0,0 +1,3 @@ +# Versions should comply with PEP440. +# This line is parsed in setup.py: +__version__ = '3.7.3' From 9ca3c78c84496cae42ad665e6a2e65c6e859d91d Mon Sep 17 00:00:00 2001 From: rf0x1d Date: Fri, 21 Aug 2020 11:22:24 +0300 Subject: [PATCH 0469/1808] back version to 3.7.2 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 92aad920e..da7bcf9cc 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__ = '3.7.3' +__version__ = '3.7.2' From cab33ad0d90a3443806a8a861669543c3e080792 Mon Sep 17 00:00:00 2001 From: rf0x1d Date: Fri, 21 Aug 2020 14:09:38 +0300 Subject: [PATCH 0470/1808] fixed thumb processing --- telebot/apihelper.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5561d3bce..5d84addc5 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -532,7 +532,10 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m if timeout: payload['connect-timeout'] = timeout if thumb: - payload['thumb'] = thumb + if not util.is_string(thumb): + files['thumb'] = thumb + else: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') @@ -586,7 +589,10 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m if timeout: payload['connect-timeout'] = timeout if thumb: - payload['thumb'] = thumb + if not util.is_string(thumb): + files['thumb'] = thumb + else: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') @@ -647,7 +653,10 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m if caption: payload['caption'] = caption if thumb: - payload['thumb'] = thumb + if not util.is_string(thumb): + files['thumb'] = thumb + else: + payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') From 73487f96c4697fe3b88d4bed80a27d6386407ee4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 21 Aug 2020 17:36:08 +0300 Subject: [PATCH 0471/1808] Custom exception handler for poll mode Initial beta of custom exception handler for poll mode. Use ExceptionHandler class and bot.exception_handler to proceed unhandled exceptions in poll mode. --- telebot/__init__.py | 91 +++++++++++++++++++++++++++++++++++--------- telebot/apihelper.py | 3 +- 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 44119badf..f7ee2ae82 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -42,6 +42,15 @@ def __getitem__(self, item): return getattr(self, item) +class ExceptionHandler: + """ + Class for handling exceptions while Polling + """ + + def handle(self, exception): + return False + + class TeleBot: """ This is TeleBot Class Methods: @@ -86,7 +95,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 + next_step_backend=None, reply_backend=None, exception_handler=None ): """ :param token: bot API token @@ -111,6 +120,8 @@ def __init__( if not self.reply_backend: self.reply_backend = MemoryHandlerBackend() + self.exception_handler = exception_handler + self.message_handlers = [] self.edited_message_handlers = [] self.channel_post_handlers = [] @@ -452,20 +463,41 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout=3): error_interval = 0.25 except apihelper.ApiException as e: - logger.error(e) - if not none_stop: - self.__stop_polling.set() - logger.info("Exception occurred. Stopping.") + if self.exception_handler is not None: + handled = self.exception_handler.handle(e) + else: + handled = False + + if not handled: + logger.error(e) + if not none_stop: + self.__stop_polling.set() + logger.info("Exception occurred. Stopping.") + else: + polling_thread.clear_exceptions() + self.worker_pool.clear_exceptions() + logger.info("Waiting for {0} seconds until retry".format(error_interval)) + time.sleep(error_interval) + error_interval *= 2 else: polling_thread.clear_exceptions() self.worker_pool.clear_exceptions() - logger.info("Waiting for {0} seconds until retry".format(error_interval)) time.sleep(error_interval) - error_interval *= 2 except KeyboardInterrupt: logger.info("KeyboardInterrupt received.") self.__stop_polling.set() break + except Exception as e: + if self.exception_handler is not None: + handled = self.exception_handler.handle(e) + else: + handled = False + if not handled: + raise e + else: + polling_thread.clear_exceptions() + self.worker_pool.clear_exceptions() + time.sleep(error_interval) polling_thread.stop() logger.info('Stopped polling.') @@ -480,18 +512,35 @@ def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3): self.__retrieve_updates(timeout) error_interval = 0.25 except apihelper.ApiException as e: - logger.error(e) - if not none_stop: - self.__stop_polling.set() - logger.info("Exception occurred. Stopping.") + if self.exception_handler is not None: + handled = self.exception_handler.handle(e) + else: + handled = False + + if not handled: + logger.error(e) + if not none_stop: + self.__stop_polling.set() + logger.info("Exception occurred. Stopping.") + else: + logger.info("Waiting for {0} seconds until retry".format(error_interval)) + time.sleep(error_interval) + error_interval *= 2 else: - logger.info("Waiting for {0} seconds until retry".format(error_interval)) time.sleep(error_interval) - error_interval *= 2 except KeyboardInterrupt: logger.info("KeyboardInterrupt received.") self.__stop_polling.set() break + except Exception as e: + if self.exception_handler is not None: + handled = self.exception_handler.handle(e) + else: + handled = False + if not handled: + raise e + else: + time.sleep(error_interval) logger.info('Stopped polling.') @@ -805,7 +854,7 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height)) def send_animation(self, chat_id, animation, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None): """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -823,7 +872,7 @@ def send_animation(self, chat_id, animation, duration=None, caption=None, reply_ return types.Message.de_json( apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout)) + parse_mode, disable_notification, timeout)) def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None): @@ -1055,7 +1104,6 @@ def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title): """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - def set_chat_permissions(self, chat_id, permissions): """ Use this method to set default chat permissions for all members. @@ -1270,7 +1318,7 @@ def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_me def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, - send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, + send_phone_number_to_provider=None, send_email_to_provider=None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, timeout=None): """ Sends invoice @@ -1386,7 +1434,7 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me :return: """ parse_mode = self.parse_mode if not parse_mode else parse_mode - + result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, parse_mode, reply_markup) if type(result) == bool: @@ -1837,6 +1885,7 @@ def inline_handler(self, func, **kwargs): :param kwargs: :return: """ + def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_inline_handler(handler_dict) @@ -1859,6 +1908,7 @@ def chosen_inline_handler(self, func, **kwargs): :param kwargs: :return: """ + def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_chosen_inline_handler(handler_dict) @@ -1881,6 +1931,7 @@ def callback_query_handler(self, func, **kwargs): :param kwargs: :return: """ + def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_callback_query_handler(handler_dict) @@ -1903,6 +1954,7 @@ def shipping_query_handler(self, func, **kwargs): :param kwargs: :return: """ + def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_shipping_query_handler(handler_dict) @@ -1925,6 +1977,7 @@ def pre_checkout_query_handler(self, func, **kwargs): :param kwargs: :return: """ + def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_pre_checkout_query_handler(handler_dict) @@ -1947,6 +2000,7 @@ def poll_handler(self, func, **kwargs): :param kwargs: :return: """ + def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_poll_handler(handler_dict) @@ -1969,6 +2023,7 @@ def poll_answer_handler(self, func=None, **kwargs): :param kwargs: :return: """ + def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_poll_answer_handler(handler_dict) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5d668ac69..90c613403 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -975,6 +975,7 @@ def send_invoice( :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: 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 :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: :return: """ method_url = r'sendInvoice' @@ -1288,7 +1289,7 @@ class ApiTelegramException(ApiException): """ def __init__(self, function_name, result, result_json): super(ApiTelegramException, self).__init__( - "Error code: {0} Description: {1}" \ + "Error code: {0}. Description: {1}" \ .format(result_json['error_code'], result_json['description']), function_name, result) From 5b70980bda3d6414705a4040179440ff51ce648a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 21 Aug 2020 17:38:54 +0300 Subject: [PATCH 0472/1808] Resolve conflicts --- telebot/__init__.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f7ee2ae82..589d443e9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -792,8 +792,8 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) - def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None, thumb=None): """ Use this method to send general files. :param chat_id: @@ -804,13 +804,14 @@ def send_document(self, chat_id, data, reply_to_message_id=None, caption=None, r :param parse_mode: :param disable_notification: :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent :return: API reply. """ parse_mode = self.parse_mode if not parse_mode else parse_mode - return types.Message.de_json( + 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=caption)) + parse_mode, disable_notification, timeout, caption, thumb)) def send_sticker( self, chat_id, data, reply_to_message_id=None, reply_markup=None, @@ -844,7 +845,9 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param reply_markup: :param disable_notification: :param timeout: - :param thumb: + :param thumb: InputFile or String : Thumbnail of the file sent + :param width: + :param height: :return: """ parse_mode = self.parse_mode if not parse_mode else parse_mode @@ -853,8 +856,10 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height)) - def send_animation(self, chat_id, animation, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + def send_animation(self, chat_id, animation, duration=None, + caption=None, reply_to_message_id=None, + reply_markup=None, parse_mode=None, + disable_notification=None, timeout=None, thumb=None): """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -866,18 +871,20 @@ def send_animation(self, chat_id, animation, duration=None, caption=None, reply_ :param reply_markup: :param disable_notification: :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent :return: """ parse_mode = self.parse_mode if not parse_mode else parse_mode return types.Message.de_json( apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout)) + parse_mode, disable_notification, timeout, thumb)) - def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + def send_video_note(self, chat_id, data, duration=None, length=None, + reply_to_message_id=None, reply_markup=None, + disable_notification=None, timeout=None, thumb=None): """ - Use this method to send video files, Telegram clients support mp4 videos. + As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds @@ -886,11 +893,12 @@ def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_me :param reply_markup: :param disable_notification: :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent :return: """ return types.Message.de_json( apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout)) + disable_notification, timeout, thumb)) def send_media_group( self, chat_id, media, From 5e19965b0c42c55fe7561a3b94b42ca25c2d685f Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Sat, 22 Aug 2020 16:11:52 +0200 Subject: [PATCH 0473/1808] Fix 'NoneType' object assignment error from #892 and #954 --- telebot/apihelper.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5d84addc5..e75ed538e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -498,7 +498,10 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['connect-timeout'] = timeout if thumb: if not util.is_string(thumb): - files['thumb'] = thumb + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} else: payload['thumb'] = thumb if width: @@ -533,7 +536,10 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m payload['connect-timeout'] = timeout if thumb: if not util.is_string(thumb): - files['thumb'] = thumb + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} else: payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') @@ -590,7 +596,10 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['connect-timeout'] = timeout if thumb: if not util.is_string(thumb): - files['thumb'] = thumb + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} else: payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') @@ -625,7 +634,10 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['connect-timeout'] = timeout if thumb: if not util.is_string(thumb): - files['thumb'] = thumb + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} else: payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') @@ -654,7 +666,10 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['caption'] = caption if thumb: if not util.is_string(thumb): - files['thumb'] = thumb + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} else: payload['thumb'] = thumb return _make_request(token, method_url, params=payload, files=files, method='post') From cdd48c7aed0d2a9223e326c2e3ab0b9400f1d3f4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 24 Aug 2020 16:02:35 +0300 Subject: [PATCH 0474/1808] Empty list optimization, Py2 arteacts removed, Empty list optimization: None instead of []. Py2 arteacts removed: no more six moudle used. --- requirements.txt | 1 - setup.py | 2 +- telebot/__init__.py | 129 ++++++++++++++++++++---------------- telebot/handler_backends.py | 18 +++-- telebot/types.py | 6 +- telebot/util.py | 33 ++++----- telebot/version.py | 2 +- 7 files changed, 97 insertions(+), 94 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6e4ca4073..a38fc0933 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ py==1.4.29 pytest==3.0.2 requests==2.20.0 -six==1.9.0 wheel==0.24.0 diff --git a/setup.py b/setup.py index f176609f6..b3e8158cf 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def read(filename): packages=['telebot'], license='GPL2', keywords='telegram bot api tools', - install_requires=['requests', 'six'], + install_requires=['requests'], extras_require={ 'json': 'ujson', 'redis': 'redis>=3.4.1' diff --git a/telebot/__init__.py b/telebot/__init__.py index 181163377..3ca0cfe62 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7,8 +7,6 @@ import threading import time -import six - logger = logging.getLogger('TeleBot') formatter = logging.Formatter( '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' @@ -134,20 +132,20 @@ def __init__( self.poll_handlers = [] self.poll_answer_handlers = [] - self.typed_middleware_handlers = { - 'message': [], - 'edited_message': [], - 'channel_post': [], - 'edited_channel_post': [], - 'inline_query': [], - 'chosen_inline_result': [], - 'callback_query': [], - 'shipping_query': [], - 'pre_checkout_query': [], - 'poll': [], - } - - self.default_middleware_handlers = [] + if apihelper.ENABLE_MIDDLEWARE: + self.typed_middleware_handlers = { + 'message': [], + 'edited_message': [], + 'channel_post': [], + 'edited_channel_post': [], + 'inline_query': [], + 'chosen_inline_result': [], + 'callback_query': [], + 'shipping_query': [], + 'pre_checkout_query': [], + 'poll': [], + } + self.default_middleware_handlers = [] self.threaded = threaded if self.threaded: @@ -297,17 +295,22 @@ def __retrieve_updates(self, timeout=20): self.process_new_updates(updates) def process_new_updates(self, updates): - new_messages = [] - new_edited_messages = [] - new_channel_posts = [] - new_edited_channel_posts = [] - new_inline_querys = [] - new_chosen_inline_results = [] - new_callback_querys = [] - new_shipping_querys = [] - new_pre_checkout_querys = [] - new_polls = [] - new_poll_answers = [] + upd_count = len(updates) + logger.debug('Received {0} new updates'.format(upd_count)) + if (upd_count == 0): + return + + new_messages = None + new_edited_messages = None + new_channel_posts = None + new_edited_channel_posts = None + new_inline_queries = None + new_chosen_inline_results = None + new_callback_queries = None + new_shipping_queries = None + new_pre_checkout_queries = None + new_polls = None + new_poll_answers = None for update in updates: if apihelper.ENABLE_MIDDLEWARE: @@ -316,50 +319,60 @@ def process_new_updates(self, updates): if update.update_id > self.last_update_id: self.last_update_id = update.update_id if update.message: + if new_messages is None: new_messages = [] new_messages.append(update.message) if update.edited_message: + if new_edited_messages is None: new_edited_messages = [] new_edited_messages.append(update.edited_message) if update.channel_post: + if new_channel_posts is None: new_channel_posts = [] new_channel_posts.append(update.channel_post) if update.edited_channel_post: + if new_edited_channel_posts is None: new_edited_channel_posts = [] new_edited_channel_posts.append(update.edited_channel_post) if update.inline_query: - new_inline_querys.append(update.inline_query) + if new_inline_queries is None: new_inline_queries = [] + new_inline_queries.append(update.inline_query) if update.chosen_inline_result: + if new_chosen_inline_results is None: new_chosen_inline_results = [] new_chosen_inline_results.append(update.chosen_inline_result) if update.callback_query: - new_callback_querys.append(update.callback_query) + if new_callback_queries is None: new_callback_queries = [] + new_callback_queries.append(update.callback_query) if update.shipping_query: - new_shipping_querys.append(update.shipping_query) + if new_shipping_queries is None: new_shipping_queries = [] + new_shipping_queries.append(update.shipping_query) if update.pre_checkout_query: - new_pre_checkout_querys.append(update.pre_checkout_query) + if new_pre_checkout_queries is None: new_pre_checkout_queries = [] + new_pre_checkout_queries.append(update.pre_checkout_query) if update.poll: + if new_polls is None: new_polls = [] new_polls.append(update.poll) if update.poll_answer: + if new_poll_answers is None: new_poll_answers = [] new_poll_answers.append(update.poll_answer) - logger.debug('Received {0} new updates'.format(len(updates))) - if len(new_messages) > 0: + if new_messages: self.process_new_messages(new_messages) - if len(new_edited_messages) > 0: + if new_edited_messages: self.process_new_edited_messages(new_edited_messages) - if len(new_channel_posts) > 0: + if new_channel_posts: self.process_new_channel_posts(new_channel_posts) - if len(new_edited_channel_posts) > 0: + if new_edited_channel_posts: self.process_new_edited_channel_posts(new_edited_channel_posts) - if len(new_inline_querys) > 0: - self.process_new_inline_query(new_inline_querys) - if len(new_chosen_inline_results) > 0: + if new_inline_queries: + self.process_new_inline_query(new_inline_queries) + if new_chosen_inline_results: self.process_new_chosen_inline_query(new_chosen_inline_results) - if len(new_callback_querys) > 0: - self.process_new_callback_query(new_callback_querys) - if len(new_shipping_querys) > 0: - self.process_new_shipping_query(new_shipping_querys) - if len(new_pre_checkout_querys) > 0: - self.process_new_pre_checkout_query(new_pre_checkout_querys) - if len(new_polls) > 0: + if new_callback_queries: + self.process_new_callback_query(new_callback_queries) + if new_shipping_queries: + self.process_new_shipping_query(new_shipping_queries) + if new_pre_checkout_queries: + self.process_new_pre_checkout_query(new_pre_checkout_queries) + if new_polls: self.process_new_poll(new_polls) - if len(new_poll_answers) > 0: + if new_poll_answers: self.process_new_poll_answer(new_poll_answers) def process_new_messages(self, new_messages): @@ -409,6 +422,8 @@ def process_middlewares(self, update): default_middleware_handler(self, update) def __notify_update(self, new_messages): + if len(self.update_listener) == 0: + return for listener in self.update_listener: self._exec_task(listener, new_messages) @@ -1590,8 +1605,9 @@ def _notify_reply_handlers(self, new_messages): for message in new_messages: if hasattr(message, "reply_to_message") and message.reply_to_message is not None: handlers = self.reply_backend.get_handlers(message.reply_to_message.message_id) - for handler in handlers: - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + if handlers: + for handler in handlers: + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) def register_next_step_handler(self, message, callback, *args, **kwargs): """ @@ -1663,11 +1679,12 @@ def _notify_next_handlers(self, new_messages): for i, message in enumerate(new_messages): need_pop = False handlers = self.next_step_backend.get_handlers(message.chat.id) - for handler in handlers: - need_pop = True - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + if handlers: + for handler in handlers: + need_pop = True + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) if need_pop: - new_messages.pop(i) # removing message that detects with next_step_handler + new_messages.pop(i) # removing message that was detected with next_step_handler @staticmethod def _build_handler_dict(handler, **filters): @@ -1769,9 +1786,7 @@ def decorator(handler): func=func, content_types=content_types, **kwargs) - self.add_message_handler(handler_dict) - return handler return decorator @@ -2054,7 +2069,7 @@ def _test_message_handler(self, message_handler, message): :param message: :return: """ - for message_filter, filter_value in six.iteritems(message_handler['filters']): + for message_filter, filter_value in message_handler['filters'].items(): if filter_value is None: continue @@ -2088,6 +2103,8 @@ def _notify_command_handlers(self, handlers, new_messages): :param new_messages: :return: """ + if len(handlers) == 0: + return for message in new_messages: for message_handler in handlers: if self._test_message_handler(message_handler, message): diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index e71fb2454..a10d13c4a 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -32,10 +32,13 @@ def register_handler(self, handler_group_id, handler): self.handlers[handler_group_id] = [handler] def clear_handlers(self, handler_group_id): - self.handlers.pop(handler_group_id, []) + self.handlers.pop(handler_group_id, None) def get_handlers(self, handler_group_id): - return self.handlers.pop(handler_group_id, []) + return self.handlers.pop(handler_group_id, None) + + def load_handlers(self, filename, del_file_after_loading): + raise NotImplementedError() class FileHandlerBackend(HandlerBackend): @@ -50,19 +53,15 @@ def register_handler(self, handler_group_id, handler): self.handlers[handler_group_id].append(handler) else: self.handlers[handler_group_id] = [handler] - self.start_save_timer() def clear_handlers(self, handler_group_id): - self.handlers.pop(handler_group_id, []) - + self.handlers.pop(handler_group_id, None) self.start_save_timer() def get_handlers(self, handler_group_id): - handlers = self.handlers.pop(handler_group_id, []) - + handlers = self.handlers.pop(handler_group_id, None) self.start_save_timer() - return handlers def start_save_timer(self): @@ -136,10 +135,9 @@ def clear_handlers(self, handler_group_id): self.redis.delete(self._key(handler_group_id)) def get_handlers(self, handler_group_id): - handlers = [] + handlers = None value = self.redis.get(self._key(handler_group_id)) if value: handlers = pickle.loads(value) self.clear_handlers(handler_group_id) - return handlers diff --git a/telebot/types.py b/telebot/types.py index 4ef068d91..d00cb6d4f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7,8 +7,6 @@ except ImportError: import json -import six - from telebot import util DISABLE_KEYLEN_ERROR = False @@ -81,13 +79,13 @@ def check_json(json_type): def __str__(self): d = {} - for x, y in six.iteritems(self.__dict__): + for x, y in self.__dict__.items(): if hasattr(y, '__dict__'): d[x] = y.__dict__ else: d[x] = y - return six.text_type(d) + return str(d) class Update(JsonDeserializable): diff --git a/telebot/util.py b/telebot/util.py index 67c6abfd9..099828ac1 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -2,21 +2,12 @@ import random import re import string -import sys import threading import traceback import warnings import functools -import six -from six import string_types - -# Python3 queue support. - -try: - import Queue -except ImportError: - import queue as Queue +import queue as Queue import logging try: @@ -51,7 +42,7 @@ def __init__(self, exception_callback=None, queue=None, name=None): self.continue_event = threading.Event() self.exception_callback = exception_callback - self.exc_info = None + self.exception_info = None self._running = True self.start() @@ -73,11 +64,11 @@ def run(self): pass except Exception as e: logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc()) - self.exc_info = sys.exc_info() + self.exception_info = e self.exception_event.set() if self.exception_callback: - self.exception_callback(self, self.exc_info) + self.exception_callback(self, self.exception_info) self.continue_event.wait() def put(self, task, *args, **kwargs): @@ -85,7 +76,7 @@ def put(self, task, *args, **kwargs): def raise_exceptions(self): if self.exception_event.is_set(): - six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2]) + raise self.exception_info def clear_exceptions(self): self.exception_event.clear() @@ -103,19 +94,19 @@ def __init__(self, num_threads=2): self.num_threads = num_threads self.exception_event = threading.Event() - self.exc_info = None + self.exception_info = None def put(self, func, *args, **kwargs): self.tasks.put((func, args, kwargs)) def on_exception(self, worker_thread, exc_info): - self.exc_info = exc_info + self.exception_info = exc_info self.exception_event.set() worker_thread.continue_event.set() def raise_exceptions(self): if self.exception_event.is_set(): - six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2]) + raise self.exception_info def clear_exceptions(self): self.exception_event.clear() @@ -140,15 +131,15 @@ def __init__(self, target, *args, **kwargs): def _run(self): try: self.result = self.target(*self.args, **self.kwargs) - except: - self.result = sys.exc_info() + except Exception as e: + self.result = e self.done = True def wait(self): if not self.done: self.thread.join() if isinstance(self.result, BaseException): - six.reraise(self.result[0], self.result[1], self.result[2]) + raise self.result else: return self.result @@ -164,7 +155,7 @@ def wrapper(*args, **kwargs): def is_string(var): - return isinstance(var, string_types) + return isinstance(var, str) def is_dict(var): return isinstance(var, dict) diff --git a/telebot/version.py b/telebot/version.py index da7bcf9cc..92aad920e 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__ = '3.7.2' +__version__ = '3.7.3' From 48b53f6a8ec9585270ab78eed28d2ece28551e1d Mon Sep 17 00:00:00 2001 From: eternnoir Date: Mon, 24 Aug 2020 21:36:27 +0800 Subject: [PATCH 0475/1808] Update version.X --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index da7bcf9cc..92aad920e 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__ = '3.7.2' +__version__ = '3.7.3' From 8cd18945c506971e04f26c426e56c3fecb416a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20D=C3=A1vila=20Herrero?= Date: Tue, 25 Aug 2020 15:45:08 +0200 Subject: [PATCH 0476/1808] Append bots to list TasksListsBot MyElizaPsychologistBot --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6c68f8440..9285c4e3f 100644 --- a/README.md +++ b/README.md @@ -655,5 +655,7 @@ Get help. Discuss. Chat. * [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read. * [Blue_CC_Bot](https://t.me/Blue_CC_Bot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Checks Your Given Credit Cards And Says Which Is A Real,Card And Which Is Fake. * [RandomInfoBot](https://t.me/RandomInfoBot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Generates Random Information Of Humans Scraped From Over 13 Websites. +* [TasksListsBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/TasksListsBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - A (tasks) lists manager bot for Telegram. +* [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From bab9b4077d159fad6607e707ce30e8c574de31f2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 25 Aug 2020 18:18:51 +0300 Subject: [PATCH 0477/1808] Bot API support checked/updated up to 4.2 --- README.md | 7 +++++- telebot/__init__.py | 34 +++++++++++++++++-------- telebot/apihelper.py | 11 +++++--- telebot/types.py | 60 ++++++++++++++++++++++++++++++-------------- tests/test_types.py | 2 +- 5 files changed, 79 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 9285c4e3f..59a3dbfb6 100644 --- a/README.md +++ b/README.md @@ -539,8 +539,13 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} _Checking is in progress..._ -✅ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) _- To be checked..._ +✅ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019) _- To be checked..._ +* ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019) +* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support. +* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support. +* ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018) +* ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) * ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017) * ✔ [Bot API 3.3](https://core.telegram.org/bots/api-changelog#august-23-2017) * ✔ [Bot API 3.2](https://core.telegram.org/bots/api-changelog#july-21-2017) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3ca0cfe62..1c74d02e4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -711,6 +711,7 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio :param chat_id: which chat to forward :param from_chat_id: which chat message from :param message_id: message id + :param timeout: :return: API reply. """ return types.Message.de_json( @@ -721,6 +722,7 @@ def delete_message(self, chat_id, message_id, timeout=None): Use this method to delete message. Returns True on success. :param chat_id: in which chat to delete :param message_id: which message to delete + :param timeout: :return: API reply. """ return apihelper.delete_message(self.token, chat_id, message_id, timeout) @@ -736,6 +738,7 @@ def send_dice( :param disable_notification: :param reply_to_message_id: :param reply_markup: + :param timeout: :return: Message """ return types.Message.de_json( @@ -755,6 +758,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep :param parse_mode :param reply_to_message_id: :param reply_markup: + :param timeout: :return: API reply. """ parse_mode = self.parse_mode if not parse_mode else parse_mode @@ -924,6 +928,7 @@ def send_media_group( :param media: :param disable_notification: :param reply_to_message_id: + :param timeout: :return: """ result = apihelper.send_media_group( @@ -945,6 +950,7 @@ def send_location( :param reply_to_message_id: :param reply_markup: :param disable_notification: + :param timeout: :return: API reply. """ return types.Message.de_json( @@ -962,6 +968,7 @@ def edit_message_live_location(self, latitude, longitude, chat_id=None, message_ :param message_id: :param inline_message_id: :param reply_markup: + :param timeout: :return: """ return types.Message.de_json( @@ -979,6 +986,7 @@ def stop_message_live_location( :param message_id: :param inline_message_id: :param reply_markup: + :param timeout: :return: """ return types.Message.de_json( @@ -986,8 +994,8 @@ def stop_message_live_location( self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) def send_venue( - self, chat_id, latitude, longitude, title, address, foursquare_id=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None): + self, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): """ Use this method to send information about a venue. :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel @@ -996,25 +1004,26 @@ def send_venue( :param title: String : Name of the venue :param address: String : Address of the venue :param foursquare_id: String : Foursquare identifier of the venue + :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) :param disable_notification: :param reply_to_message_id: :param reply_markup: + :param timeout: :return: """ return types.Message.de_json( apihelper.send_venue( - self.token, chat_id, latitude, longitude, title, address, foursquare_id, + self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_to_message_id, reply_markup, timeout) ) def send_contact( - self, chat_id, phone_number, first_name, - last_name=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None): + self, chat_id, phone_number, first_name, last_name=None, vcard=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): return types.Message.de_json( apihelper.send_contact( - self.token, chat_id, phone_number, first_name, last_name, disable_notification, - reply_to_message_id, reply_markup, timeout) + self.token, chat_id, phone_number, first_name, last_name, vcard, + disable_notification, reply_to_message_id, reply_markup, timeout) ) def send_chat_action(self, chat_id, action, timeout=None): @@ -1025,6 +1034,7 @@ def send_chat_action(self, chat_id, action, timeout=None): :param chat_id: :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', 'upload_video_note'. + :param timeout: :return: API reply. :type: boolean """ return apihelper.send_chat_action(self.token, chat_id, action, timeout) @@ -1297,6 +1307,7 @@ def send_game( :param disable_notification: :param reply_to_message_id: :param reply_markup: + :param timeout: :return: """ result = apihelper.send_game( @@ -1368,6 +1379,7 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: 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 :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: :return: """ result = apihelper.send_invoice( @@ -1400,6 +1412,7 @@ def send_poll( :param disable_notifications: :param reply_to_message_id: :param reply_markup: + :param timeout: :return: """ @@ -1414,14 +1427,15 @@ def send_poll( explanation, explanation_parse_mode, open_period, close_date, is_closed, disable_notifications, reply_to_message_id, reply_markup, timeout)) - def stop_poll(self, chat_id, message_id): + def stop_poll(self, chat_id, message_id, reply_markup=None): """ Stops poll :param chat_id: :param message_id: + :param reply_markup: :return: """ - return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id)) + return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id, reply_markup)) def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 70c3f261c..b80a627df 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -427,12 +427,14 @@ def stop_message_live_location( def send_venue( token, chat_id, latitude, longitude, title, address, - foursquare_id=None, disable_notification=None, + foursquare_id=None, foursquare_type=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: payload['foursquare_id'] = foursquare_id + if foursquare_type: + payload['foursquare_type'] = foursquare_type if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: @@ -445,13 +447,14 @@ def send_venue( def send_contact( - token, chat_id, phone_number, first_name, - last_name=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None): + token, chat_id, phone_number, first_name, last_name=None, vcard=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: payload['last_name'] = last_name + if vcard: + payload['vcard'] = vcard if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_to_message_id: diff --git a/telebot/types.py b/telebot/types.py index d00cb6d4f..55689a69b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -274,6 +274,8 @@ def de_json(cls, json_string): opts['forward_from_message_id'] = obj.get('forward_from_message_id') if 'forward_signature' in obj: opts['forward_signature'] = obj.get('forward_signature') + if 'forward_sender_name' in obj: + opts['forward_sender_name'] = obj.get('forward_sender_name') if 'forward_date' in obj: opts['forward_date'] = obj.get('forward_date') if 'reply_to_message' in obj: @@ -416,6 +418,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.forward_from_chat = None self.forward_from_message_id = None self.forward_signature = None + self.forward_sender_name = None self.forward_date = None self.reply_to_message = None self.edit_date = None @@ -544,14 +547,16 @@ def de_json(cls, json_string): length = obj['length'] url = obj.get('url') user = User.de_json(obj.get('user')) - return cls(type, offset, length, url, user) + language = obj.get('language') + return cls(type, offset, length, url, user, language) - def __init__(self, type, offset, length, url=None, user=None): + def __init__(self, type, offset, length, url=None, user=None, language=None): self.type = type self.offset = offset self.length = length self.url = url self.user = user + self.language = language class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): @@ -711,13 +716,15 @@ def de_json(cls, json_string): first_name = obj['first_name'] last_name = obj.get('last_name') user_id = obj.get('user_id') - return cls(phone_number, first_name, last_name, user_id) + vcard = obj.get('vcard') + return cls(phone_number, first_name, last_name, user_id, vcard) - def __init__(self, phone_number, first_name, last_name=None, user_id=None): + def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None): self.phone_number = phone_number self.first_name = first_name self.last_name = last_name self.user_id = user_id + self.vcard = vcard class Location(JsonDeserializable): @@ -745,13 +752,15 @@ def de_json(cls, json_string): title = obj['title'] address = obj['address'] foursquare_id = obj.get('foursquare_id') - return cls(location, title, address, foursquare_id) + foursquare_type = obj.get('foursquare_type') + return cls(location, title, address, foursquare_id, foursquare_type) - def __init__(self, location, title, address, foursquare_id=None): + def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None): self.location = location self.title = title self.address = address self.foursquare_id = foursquare_id + self.foursquare_type = foursquare_type class UserProfilePhotos(JsonDeserializable): @@ -1281,12 +1290,13 @@ def to_dict(self): class InputVenueMessageContent(Dictionaryable): - def __init__(self, latitude, longitude, title, address, foursquare_id=None): + def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None): self.latitude = latitude self.longitude = longitude self.title = title self.address = address self.foursquare_id = foursquare_id + self.foursquare_type = foursquare_type def to_dict(self): json_dict = { @@ -1297,19 +1307,24 @@ def to_dict(self): } if self.foursquare_id: json_dict['foursquare_id'] = self.foursquare_id + if self.foursquare_type: + json_dict['foursquare_type'] = self.foursquare_type return json_dict class InputContactMessageContent(Dictionaryable): - def __init__(self, phone_number, first_name, last_name=None): + def __init__(self, phone_number, first_name, last_name=None, vcard=None): self.phone_number = phone_number self.first_name = first_name self.last_name = last_name + self.vcard = vcard def to_dict(self): json_dict = {'phone_numbe': 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 return json_dict @@ -1737,8 +1752,8 @@ def to_json(self): class InlineQueryResultVenue(JsonSerializable): - def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, reply_markup=None, - input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None): + 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): self.type = 'venue' self.id = id self.title = title @@ -1746,6 +1761,7 @@ def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, 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.thumb_url = thumb_url @@ -1757,6 +1773,8 @@ def to_json(self): 'longitude': self.longitude, '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: @@ -1771,13 +1789,15 @@ def to_json(self): class InlineQueryResultContact(JsonSerializable): - def __init__(self, id, phone_number, first_name, last_name=None, reply_markup=None, - input_message_content=None, thumb_url=None, thumb_width=None, thumb_height=None): + 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 @@ -1788,16 +1808,18 @@ 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 - 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) @@ -1976,9 +1998,10 @@ def de_json(cls, json_string): description = obj['description'] photo = Game.parse_photo(obj['photo']) text = obj.get('text') - text_entities = None if 'text_entities' in obj: text_entities = Game.parse_entities(obj['text_entities']) + else: + text_entities = None animation = Animation.de_json(obj.get('animation')) return cls(title, description, photo, text, text_entities, animation) @@ -2446,7 +2469,6 @@ def de_json(cls, json_string): explanation_entities = None open_period = obj.get('open_period') close_date = obj.get('close_date') - #poll = return cls( question, options, poll_id, total_voter_count, is_closed, is_anonymous, poll_type, @@ -2469,7 +2491,7 @@ def __init__( self.allows_multiple_answers = allows_multiple_answers self.correct_option_id = correct_option_id self.explanation = explanation - self.explanation_entities = explanation_entities if not(explanation_entities is None) else [] + self.explanation_entities = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] self.open_period = open_period self.close_date = close_date diff --git a/tests/test_types.py b/tests/test_types.py index 742c113d8..d8c14036a 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -110,7 +110,7 @@ def test_json_UserProfilePhotos(): def test_json_contact(): - json_string = r'{"phone_number":"00011111111","first_name":"dd","last_name":"ddl","user_id":8633}' + json_string = r'{"phone_number":"00011111111","first_name":"dd","last_name":"ddl","user_id":8633,"vcard":"SomeContactString"}' contact = types.Contact.de_json(json_string) assert contact.first_name == 'dd' assert contact.last_name == 'ddl' From c13f9a7f98f26d26748591c30d73d290c1a4effa Mon Sep 17 00:00:00 2001 From: Nikolay Korolev Date: Tue, 25 Aug 2020 21:26:28 +0300 Subject: [PATCH 0478/1808] Add last_update_id parameter for constructor --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3ca0cfe62..33c309443 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -92,7 +92,7 @@ class TeleBot: """ def __init__( - self, token, parse_mode=None, threaded=True, skip_pending=False, num_threads=2, + self, token, parse_mode=None, last_update_id=0, threaded=True, skip_pending=False, num_threads=2, next_step_backend=None, reply_backend=None, exception_handler=None ): """ @@ -107,7 +107,7 @@ def __init__( self.skip_pending = skip_pending self.__stop_polling = threading.Event() - self.last_update_id = 0 + self.last_update_id = last_update_id self.exc_info = None self.next_step_backend = next_step_backend From 5120650774ec3a321c5532c09256dfc1193cc28e Mon Sep 17 00:00:00 2001 From: Nikolay Korolev Date: Tue, 25 Aug 2020 21:45:30 +0300 Subject: [PATCH 0479/1808] Move parameter to the end of list --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 33c309443..2a98812ca 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -92,8 +92,8 @@ class TeleBot: """ def __init__( - self, token, parse_mode=None, last_update_id=0, threaded=True, skip_pending=False, num_threads=2, - next_step_backend=None, reply_backend=None, exception_handler=None + 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 ): """ :param token: bot API token From 309e55845cb47bf27837a04c560ddc01e5b8a6a6 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 26 Aug 2020 00:51:55 +0300 Subject: [PATCH 0480/1808] Minor readme update --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 59a3dbfb6..795371195 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) +[![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/) * [Getting started.](#getting-started) * [Writing your first bot](#writing-your-first-bot) @@ -39,7 +40,7 @@ ## Getting started. -This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and Pypy 3. +This API is tested with Python Python 3.6-3.8 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager)*: From e811163b5f402fac1eff549480658f41dddb6917 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Sat, 29 Aug 2020 04:29:02 +0800 Subject: [PATCH 0481/1808] UPG: Added the field `file_unique_id` Added the field file_unique_id to the objects Animation, Audio, Document, PassportFile, PhotoSize, Sticker, Video, VideoNote, Voice, File and the fields small_file_unique_id and big_file_unique_id to the object ChatPhoto. (Bot API 4.5) --- telebot/types.py | 56 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 55689a69b..85971013a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -586,13 +586,15 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] width = obj['width'] height = obj['height'] file_size = obj.get('file_size') - return cls(file_id, width, height, file_size) + return cls(file_id, file_unique_id, width, height, file_size) - def __init__(self, file_id, width, height, file_size=None): + def __init__(self, file_id, file_unique_id, width, height, file_size=None): self.file_size = file_size + self.file_unique_id = file_unique_id self.height = height self.width = width self.file_id = file_id @@ -604,15 +606,17 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] duration = obj['duration'] performer = obj.get('performer') title = obj.get('title') mime_type = obj.get('mime_type') file_size = obj.get('file_size') - return cls(file_id, duration, performer, title, mime_type, file_size) + return cls(file_id, file_unique_id, duration, performer, title, mime_type, file_size) - def __init__(self, file_id, duration, performer=None, title=None, mime_type=None, file_size=None): + def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, mime_type=None, file_size=None): self.file_id = file_id + self.file_unique_id = file_unique_id self.duration = duration self.performer = performer self.title = title @@ -626,13 +630,15 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] duration = obj['duration'] mime_type = obj.get('mime_type') file_size = obj.get('file_size') - return cls(file_id, duration, mime_type, file_size) + return cls(file_id, file_unique_id, duration, mime_type, file_size) - def __init__(self, file_id, duration, mime_type=None, file_size=None): + def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None): self.file_id = file_id + self.file_unique_id = file_unique_id self.duration = duration self.mime_type = mime_type self.file_size = file_size @@ -644,16 +650,18 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] thumb = None if 'thumb' in obj and 'file_id' in obj['thumb']: thumb = PhotoSize.de_json(obj['thumb']) file_name = obj.get('file_name') mime_type = obj.get('mime_type') file_size = obj.get('file_size') - return cls(file_id, thumb, file_name, mime_type, file_size) + return cls(file_id, file_unique_id, thumb, file_name, mime_type, file_size) - def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_size=None): + def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None): self.file_id = file_id + self.file_unique_id = file_unique_id self.thumb = thumb self.file_name = file_name self.mime_type = mime_type @@ -667,16 +675,18 @@ def de_json(cls, json_string): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] width = obj['width'] height = obj['height'] duration = obj['duration'] thumb = PhotoSize.de_json(obj.get('thumb')) mime_type = obj.get('mime_type') file_size = obj.get('file_size') - return cls(file_id, width, height, duration, thumb, mime_type, file_size) + return cls(file_id, file_unique_id, width, height, duration, thumb, mime_type, file_size) - def __init__(self, file_id, width, height, duration, thumb=None, mime_type=None, file_size=None): + def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, mime_type=None, file_size=None): self.file_id = file_id + self.file_unique_id = file_unique_id self.width = width self.height = height self.duration = duration @@ -692,14 +702,16 @@ def de_json(cls, json_string): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] length = obj['length'] duration = obj['duration'] thumb = PhotoSize.de_json(obj.get('thumb')) file_size = obj.get('file_size') - return cls(file_id, length, duration, thumb, file_size) + return cls(file_id, file_unique_id, length, duration, thumb, file_size) - def __init__(self, file_id, length, duration, thumb=None, file_size=None): + def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None): self.file_id = file_id + self.file_unique_id = file_unique_id self.length = length self.duration = duration self.thumb = thumb @@ -785,12 +797,14 @@ def de_json(cls, json_string): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] file_size = obj.get('file_size') file_path = obj.get('file_path') - return cls(file_id, file_size, file_path) + return cls(file_id, file_unique_id, file_size, file_path) - def __init__(self, file_id, file_size, file_path): + def __init__(self, file_id, file_unique_id, file_size, file_path): self.file_id = file_id + self.file_unique_id = file_unique_id self.file_size = file_size self.file_path = file_path @@ -1085,12 +1099,16 @@ def de_json(cls, json_string): return None obj = cls.check_json(json_string) small_file_id = obj['small_file_id'] + small_file_unique_id = obj['small_file_unique_id'] big_file_id = obj['big_file_id'] - return cls(small_file_id, big_file_id) + big_file_unique_id = obj['big_file_unique_id'] + return cls(small_file_id, small_file_unique_id, big_file_id, big_file_unique_id) - def __init__(self, small_file_id, big_file_id): + def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id): self.small_file_id = small_file_id + self.small_file_unique_id = small_file_unique_id self.big_file_id = big_file_id + self.big_file_unique_id = big_file_unique_id class ChatMember(JsonDeserializable): @@ -2034,14 +2052,16 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) file_id = obj['file_id'] + file_unique_id = obj['file_unique_id'] thumb = PhotoSize.de_json(obj.get('thumb')) file_name = obj.get('file_name') mime_type = obj.get('mime_type') file_size = obj.get('file_size') - return cls(file_id, thumb, file_name, mime_type, file_size) + return cls(file_id, file_unique_id, thumb, file_name, mime_type, file_size) - def __init__(self, file_id, thumb=None, file_name=None, mime_type=None, file_size=None): + def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None): self.file_id = file_id + self.file_unique_id = file_unique_id self.thumb = thumb self.file_name = file_name self.mime_type = mime_type From bdfb793e3421a9e35390937c57117bcd19730576 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Sat, 29 Aug 2020 12:07:38 +0800 Subject: [PATCH 0482/1808] test: Added file_unique_id from Bot API 4.5 --- tests/test_types.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index d8c14036a..2f1e6987e 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -41,14 +41,14 @@ def test_json_GroupChat(): def test_json_Document(): - json_string = r'{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}' + json_string = r'{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_unique_id": "AgADJQEAAqfhOEY","file_size":446}' doc = types.Document.de_json(json_string) assert doc.thumb is None assert doc.file_name == 'Text File' def test_json_Message_Audio(): - json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd","is_bot":true },"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}' + json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd","is_bot":true },"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_unique_id": "AgADawEAAn8VSFY","file_size":20096}}' msg = types.Message.de_json(json_string) assert msg.audio.duration == 1 assert msg.content_type == 'audio' @@ -73,21 +73,21 @@ def test_json_Message_Sticker_without_thumb(): def test_json_Message_Document(): - json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}' + json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":446}}' msg = types.Message.de_json(json_string) assert msg.document.file_name == 'Text File' assert msg.content_type == 'document' def test_json_Message_Photo(): - json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}' + json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":53013,"width":759,"height":570}]}' msg = types.Message.de_json(json_string) assert len(msg.photo) == 3 assert msg.content_type == 'photo' def test_json_Message_Video(): - json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}' + json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_unique_id": "AQADTeisa3QAAz1nAAI","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_unique_id": "AgADbgEAAn8VSFY","file_size":260699}}' msg = types.Message.de_json(json_string) assert msg.video assert msg.video.duration == 3 @@ -103,7 +103,7 @@ def test_json_Message_Location(): def test_json_UserProfilePhotos(): - json_string = r'{"total_count":1,"photos":[[{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAATZH_SpyZjzIwdVAAIC","file_size":6150,"width":160,"height":160},{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAATOiTNi_YoJMghVAAIC","file_size":13363,"width":320,"height":320},{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAAQW4DyFv0-lhglVAAIC","file_size":28347,"width":640,"height":640},{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAAT50RvJCg0GQApVAAIC","file_size":33953,"width":800,"height":800}]]}' + json_string = r'{"total_count":1,"photos":[[{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAATZH_SpyZjzIwdVAAIC","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":6150,"width":160,"height":160},{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAATOiTNi_YoJMghVAAIC","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":13363,"width":320,"height":320},{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAAQW4DyFv0-lhglVAAIC","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":28347,"width":640,"height":640},{"file_id":"AgADAgADqacxG6wpRwABvEB6fpeIcKS4HAIkAAT50RvJCg0GQApVAAIC","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":33953,"width":800,"height":800}]]}' upp = types.UserProfilePhotos.de_json(json_string) assert upp.photos[0][0].width == 160 assert upp.photos[0][-1].height == 800 @@ -117,7 +117,7 @@ def test_json_contact(): def test_json_voice(): - json_string = r'{"duration": 0,"mime_type": "audio/ogg","file_id": "AwcccccccDH1JaB7w_gyFjYQxVAg","file_size": 10481}' + json_string = r'{"duration": 0,"mime_type": "audio/ogg","file_id": "AwcccccccDH1JaB7w_gyFjYQxVAg","file_unique_id": "AgADbAEAAn8VSFY","file_size": 10481}' voice = types.Voice.de_json(json_string) assert voice.duration == 0 assert voice.file_size == 10481 From 81100f249cbec7f89f07c0fd62e02b397399bb96 Mon Sep 17 00:00:00 2001 From: Artem Frantsiian <35114937+ArtemFrantsiian@users.noreply.github.com> Date: Sat, 29 Aug 2020 21:57:41 +0300 Subject: [PATCH 0483/1808] Fix an error with the is_pil_image function When I've tried to send_photo as shown in detailed_example, I got an error: "AttributeError: module 'PIL' has no attribute 'Image'". This error was described well here: https://stackoverflow.com/a/11911536/9092263. So in accordance to prescriptions, I've made changes and It works fine for me. Steps to reproduce: 1. initiate bot via TeleBot constructor 2. call function bot.send_photo(call.message.chat.id, open("some_image.jpg", "rb")) P.S. Error Environment: - python==3.8.5 - pyTelegramBotAPI==3.7.3 - PIL==7.2.0 --- telebot/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 099828ac1..438e8c1ed 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -11,7 +11,7 @@ import logging try: - import PIL + from PIL import Image from io import BytesIO pil_imported = True except: @@ -164,7 +164,7 @@ def is_bytes(var): return isinstance(var, bytes) def is_pil_image(var): - return pil_imported and isinstance(var, PIL.Image.Image) + return pil_imported and isinstance(var, Image.Image) def pil_image_to_file(image, extension='JPEG', quality='web_low'): if pil_imported: From 6832c337333536f39a500c7bccc1b354633189c8 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Mon, 31 Aug 2020 12:00:56 +0000 Subject: [PATCH 0484/1808] feat: Added the field reply_markup to the Message Added the field `reply_markup` to the Message object --- telebot/types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 85971013a..e44d6b50a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -385,6 +385,9 @@ def de_json(cls, json_string): if 'passport_data' in obj: opts['passport_data'] = obj['passport_data'] content_type = 'passport_data' + if 'reply_markup' in obj: + opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) + content_type = 'reply_markup' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -455,6 +458,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.invoice = None self.successful_payment = None self.connected_website = None + self.reply_markup = None for key in options: setattr(self, key, options[key]) self.json = json_string From cdae65116b92e1d5dbace592588e9a6b436b66e4 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Tue, 1 Sep 2020 18:03:21 +0800 Subject: [PATCH 0485/1808] feat: make LoginUrl JsonDeserializable feat: make LoginUrl JsonDeserializable, add de_json func --- telebot/types.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e44d6b50a..7d88a0652 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1017,12 +1017,23 @@ def to_dict(self): return json_dict -class LoginUrl(Dictionaryable, JsonSerializable): +class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None): self.url = url self.forward_text = forward_text self.bot_username = bot_username self.request_write_access = request_write_access + + @classmethod + def de_json(cls, json_string): + if (json_string is None): + return None + obj = cls.check_json(json_string) + url = obj['url'] + forward_text = obj.get('forward_text') + bot_username = obj.get('bot_username') + request_write_access = obj.get('request_write_access') + return cls(url, forward_text, bot_username, request_write_access) def to_json(self): return json.dumps(self.to_dict()) From 630a9a5b2ca653193de5b3f31672d45995a53fa4 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Tue, 1 Sep 2020 18:07:45 +0800 Subject: [PATCH 0486/1808] feat: make InlineKeyboardButton JsonDeserializable feat: make InlineKeyboardButton JsonDeserializable, add de_json func to InlineKeyboardButton Object --- telebot/types.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 7d88a0652..adf7e1848 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1049,7 +1049,7 @@ def to_dict(self): return json_dict -class InlineKeyboardButton(Dictionaryable, JsonSerializable): +class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None): self.text = text @@ -1060,6 +1060,21 @@ def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, self.callback_game = callback_game self.pay = pay self.login_url = login_url + + @classmethod + def de_json(cls, json_string): + if (json_string is None): + return None + obj = cls.check_json(json_string) + text = obj['text'] + url = obj.get('url') + callback_data = obj.get('callback_data') + switch_inline_query = obj.get('switch_inline_query') + switch_inline_query_current_chat = obj.get('switch_inline_query_current_chat') + callback_game = obj.get('callback_game') + pay = obj.get('pay') + login_url = LoginUrl.de_json(obj.get('login_url')) + return cls(text, url, callback_data, switch_inline_query, switch_inline_query_current_chat, callback_game, pay, login_url) def to_json(self): return json.dumps(self.to_dict()) From decad450d0e1fa4433f36cfbd465c7d26aee551d Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Tue, 1 Sep 2020 18:13:22 +0800 Subject: [PATCH 0487/1808] feat: make InlineKeyboardMarkup JsonDeserializable feat: make InlineKeyboardMarkup JsonDeserializable, add de_json func to InlineKeyboardMarkup object --- telebot/types.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index adf7e1848..9ae4ccca5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -941,10 +941,18 @@ def to_dict(self): return {'type': self.type} -class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): +class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)): max_row_keys = 8 + + @classmethod + def de_json(cls, json_string): + if (json_string is None): + return None + obj = cls.check_json(json_string) + keyboard = [[button for button in row] for row in obj['inline_keyboard']] + return cls(keyboard) - def __init__(self, row_width=3): + def __init__(self, keyboard=[] ,row_width=3): """ This object represents an inline keyboard that appears right next to the message it belongs to. @@ -957,7 +965,7 @@ def __init__(self, row_width=3): row_width = self.max_row_keys self.row_width = row_width - self.keyboard = [] + self.keyboard = keyboard def add(self, *args, row_width=None): """ From 32a9e65ecca3781cf067c81251bd9c13a763b5d1 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Wed, 2 Sep 2020 09:12:49 +0800 Subject: [PATCH 0488/1808] fix: reply_markup does not change content_type --- telebot/types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 9ae4ccca5..45181f31e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -387,7 +387,6 @@ def de_json(cls, json_string): content_type = 'passport_data' if 'reply_markup' in obj: opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) - content_type = 'reply_markup' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod From a803edd09b70e3ff3636e19f2dbd98c6769c6e47 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Wed, 2 Sep 2020 09:25:23 +0800 Subject: [PATCH 0489/1808] fix: button in markup should be obj, not json text --- telebot/types.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 45181f31e..0c9b99ee9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -940,7 +940,7 @@ def to_dict(self): return {'type': self.type} -class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)): +class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable): max_row_keys = 8 @classmethod @@ -948,7 +948,7 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - keyboard = [[button for button in row] for row in obj['inline_keyboard']] + keyboard = [[InlineKeyboardButton.de_json(button) for button in row] for row in obj['inline_keyboard']] return cls(keyboard) def __init__(self, keyboard=[] ,row_width=3): @@ -990,7 +990,7 @@ def add(self, *args, row_width=None): row_width = self.max_row_keys for row in util.chunks(args, row_width): - button_array = [button.to_dict() for button in row] + button_array = [button for button in row] self.keyboard.append(button_array) return self @@ -1020,7 +1020,8 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - json_dict = {'inline_keyboard': self.keyboard} + json_dict = dict() + json_dict['inline_keyboard'] = [[json.loads(button.to_json()) for button in row] for row in self.keyboard] return json_dict From 698b4371e6d5aca396fe011de3d9b17ceffbe801 Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Wed, 2 Sep 2020 10:33:32 +0800 Subject: [PATCH 0490/1808] test: Add tests for InlineKeyboardMarkup and ... Add tests for InlineKeyboardMarkup and InlineKeyboardButton --- tests/test_types.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_types.py b/tests/test_types.py index 2f1e6987e..173cda95a 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -17,6 +17,28 @@ def test_json_message(): assert msg.text == 'HIHI' +def test_json_message_with_reply_markup(): + jsonstring = r'{"message_id":48,"from":{"id":153587469,"is_bot":false,"first_name":"Neko","username":"Neko"},"chat":{"id":153587469,"first_name":"Neko","username":"Neko","type":"private"},"date":1598879570,"text":"test","reply_markup":{"inline_keyboard":[[{"text":"Google","url":"http://www.google.com"},{"text":"Yahoo","url":"http://www.yahoo.com"}]]}}' + msg = types.Message.de_json(jsonstring) + assert msg.content_type == 'text' + assert msg.reply_markup.keyboard[0][0].text == 'Google' + + +def test_json_InlineKeyboardMarkup(): + jsonstring = r'{"inline_keyboard":[[{"text":"Google","url":"http://www.google.com"},{"text":"Yahoo","url":"http://www.yahoo.com"}]]}' + markup = types.InlineKeyboardMarkup.de_json(jsonstring) + assert markup.keyboard[0][0].text == 'Google' + assert markup.keyboard[0][1].url == 'http://www.yahoo.com' + + +def test_json_InlineKeyboardButton(): + jsonstring = r'{"text":"Google","url":"http://www.google.com"}' + button = types.InlineKeyboardButton.de_json(jsonstring) + assert button.text == 'Google' + assert button.url == 'http://www.google.com' + + + def test_json_message_with_dice(): jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value": 4, "emoji": "\ud83c\udfaf"}}' msg = types.Message.de_json(jsonstring) From 9ab906e60ccbcba7a445d9dac5d4be215cecdd3b Mon Sep 17 00:00:00 2001 From: meoww-bot <14239840+meoww-bot@users.noreply.github.com> Date: Wed, 2 Sep 2020 18:09:14 +0800 Subject: [PATCH 0491/1808] fix: simplify code json.loads(button.to_json()) equals to button.to_dict() --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 0c9b99ee9..f1b9af077 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1021,7 +1021,7 @@ def to_json(self): def to_dict(self): json_dict = dict() - json_dict['inline_keyboard'] = [[json.loads(button.to_json()) for button in row] for row in self.keyboard] + json_dict['inline_keyboard'] = [[button.to_dict() for button in row] for row in self.keyboard] return json_dict From 3ae145f206eae4ae1c9f3e7838e80fdff54379ff Mon Sep 17 00:00:00 2001 From: FrankWang Date: Thu, 10 Sep 2020 16:22:50 +0800 Subject: [PATCH 0492/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 795371195..fc6e8b070 100644 --- a/README.md +++ b/README.md @@ -663,5 +663,6 @@ Get help. Discuss. Chat. * [RandomInfoBot](https://t.me/RandomInfoBot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Generates Random Information Of Humans Scraped From Over 13 Websites. * [TasksListsBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/TasksListsBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - A (tasks) lists manager bot for Telegram. * [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot. +* [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 75a5dd14928a33470e8892b2d72e28d9260a1151 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 22 Sep 2020 01:34:49 +0300 Subject: [PATCH 0493/1808] Minor bugfix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 55689a69b..2d004fa35 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -945,7 +945,7 @@ def add(self, *args, row_width=None): """ This method adds buttons to the keyboard without exceeding row_width. - E.g. InlineKeyboardMarkup#add("A", "B", "C") yields the json result: + E.g. InlineKeyboardMarkup.add("A", "B", "C") yields the json result: {keyboard: [["A"], ["B"], ["C"]]} when row_width is set to 1. When row_width is set to 2, the result: From 00c2e9b51c1ac971848d48c28b3455a182c688a2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 22 Sep 2020 01:41:51 +0300 Subject: [PATCH 0494/1808] Piece death fix --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 3519533da..384553231 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -951,7 +951,7 @@ def de_json(cls, json_string): keyboard = [[InlineKeyboardButton.de_json(button) for button in row] for row in obj['inline_keyboard']] return cls(keyboard) - def __init__(self, keyboard=[] ,row_width=3): + def __init__(self, keyboard=None ,row_width=3): """ This object represents an inline keyboard that appears right next to the message it belongs to. @@ -964,7 +964,7 @@ def __init__(self, keyboard=[] ,row_width=3): row_width = self.max_row_keys self.row_width = row_width - self.keyboard = keyboard + self.keyboard = keyboard if keyboard else [] def add(self, *args, row_width=None): """ From 36a3ce62c453e8b9b56a5401bb76c7b5f3ae0acf Mon Sep 17 00:00:00 2001 From: andvch Date: Wed, 14 Oct 2020 12:06:49 +0300 Subject: [PATCH 0495/1808] Fix broken text_mention html formatting --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 384553231..1534da884 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -503,7 +503,7 @@ def __html_text(self, text, entities): def func(upd_text, subst_type=None, url=None, user=None): upd_text = upd_text.decode("utf-16-le") if subst_type == "text_mention": - subst_type = "url" + subst_type = "text_link" url = "tg://user?id={0}".format(user.id) elif subst_type == "mention": url = "https://t.me/{0}".format(upd_text[1:]) From 746c71665e47134f8d02e44f3f8633d3a6fab515 Mon Sep 17 00:00:00 2001 From: Pinguluk Date: Wed, 28 Oct 2020 12:31:57 +0200 Subject: [PATCH 0496/1808] Update README.md linked example/webhook_examples to the directory --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc6e8b070..9d2a3705c 100644 --- a/README.md +++ b/README.md @@ -505,7 +505,7 @@ bot.polling() ### Using web hooks When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages([update.message]) when you recieve it. -There are some examples using webhooks in the *examples/webhook_examples* directory. +There are some examples using webhooks in the [examples/webhook_examples](examples/webhook_examples) directory. ### Logging From afa88304d7cb59e9c639db6bcab767eb8a7e023f Mon Sep 17 00:00:00 2001 From: Mrsqd Date: Thu, 29 Oct 2020 02:09:47 +0300 Subject: [PATCH 0497/1808] Added Frcstbot I've made a weather forecast bot using your API. Can you approve my request to add it, please? --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9d2a3705c..1a227eb78 100644 --- a/README.md +++ b/README.md @@ -664,5 +664,6 @@ Get help. Discuss. Chat. * [TasksListsBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/TasksListsBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - A (tasks) lists manager bot for Telegram. * [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot. * [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want. +* [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast. Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From 27461c03af76226b95905046305ba51eeb81e817 Mon Sep 17 00:00:00 2001 From: diegop384 <67247633+diegop384@users.noreply.github.com> Date: Tue, 3 Nov 2020 09:28:31 -0500 Subject: [PATCH 0498/1808] Update README.md I added my bot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1a227eb78..78939e5f5 100644 --- a/README.md +++ b/README.md @@ -665,5 +665,6 @@ Get help. Discuss. Chat. * [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot. * [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want. * [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast. +* [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour) Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. From fa3ca84d2466a58fb243df0b4dc3bb69b62f865c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 3 Nov 2020 17:46:19 +0300 Subject: [PATCH 0499/1808] Animation content_type "When you send gif telegram gives you animation and document at same time in update and when you parse that first if is animation and second is document because of this the content_type set document not animation" --- telebot/types.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 1534da884..6dcf6c209 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -296,12 +296,13 @@ def de_json(cls, json_string): if 'audio' in obj: opts['audio'] = Audio.de_json(obj['audio']) content_type = 'audio' - if 'animation' in obj: - opts['animation'] = Animation.de_json(obj['animation']) - content_type = 'animation' if 'document' in obj: opts['document'] = Document.de_json(obj['document']) content_type = 'document' + if 'animation' in obj: + # Document content type accompanies "animation", so "animation" should be checked below "document" to override it + opts['animation'] = Animation.de_json(obj['animation']) + content_type = 'animation' if 'game' in obj: opts['game'] = Game.de_json(obj['game']) content_type = 'game' From 7a3fd30f6ac7c06e39673665f9ec2817ad64294c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 7 Nov 2020 12:52:51 +0300 Subject: [PATCH 0500/1808] Long polling updates and combo content types --- telebot/apihelper.py | 17 +++++++++++------ telebot/util.py | 10 +++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b80a627df..ec4ad9bdf 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -52,11 +52,11 @@ def _make_request(token, method_name, method='get', params=None, files=None): :param files: Optional files. :return: The result parsed to a JSON dictionary. """ - if API_URL is None: - request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) - else: + if API_URL: request_url = API_URL.format(token, method_name) - + else: + request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) + logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT @@ -67,7 +67,12 @@ def _make_request(token, method_name, method='get', params=None, files=None): read_timeout = params.pop('timeout') + 10 if 'connect-timeout' in params: connect_timeout = params.pop('connect-timeout') + 10 - + if 'long_polling_timeout' in params: + # For getUpdates: the only function with timeout on the BOT API side + params['timeout'] = params.pop('long_polling_timeout') + + + result = None if RETRY_ON_ERROR: got_result = False current_try = 0 @@ -235,7 +240,7 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No if limit: payload['limit'] = limit if timeout: - payload['timeout'] = timeout + payload['long_polling_timeout'] = timeout if allowed_updates: payload['allowed_updates'] = json.dumps(allowed_updates) return _make_request(token, method_url, params=payload) diff --git a/telebot/util.py b/telebot/util.py index 438e8c1ed..3ad4463e2 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -21,6 +21,15 @@ thread_local = threading.local() +content_type_media = [ + 'text', 'audio', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll', + 'venue', 'location' +] + +content_type_service = [ + 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', + 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message' +] class WorkerThread(threading.Thread): count = 0 @@ -286,7 +295,6 @@ def chunks(lst, n): def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) - def deprecated(func): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted From 03e1aef70e25d8ed5f218ff25c80eb57372d2eb9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 7 Nov 2020 14:02:11 +0300 Subject: [PATCH 0501/1808] long_polling_timeout update 1 --- .travis.yml | 1 + telebot/__init__.py | 36 +++++++++++++++++++----------------- telebot/apihelper.py | 6 ++++-- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2ce07115d..eaed67e02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "3.6" - "3.7" - "3.8" + - "3.9" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/telebot/__init__.py b/telebot/__init__.py index df59f39b9..3028c8c98 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -252,16 +252,17 @@ def get_webhook_info(self): def remove_webhook(self): return self.set_webhook() # No params resets webhook - def get_updates(self, offset=None, limit=None, timeout=20, allowed_updates=None): + def get_updates(self, offset=None, limit=None, timeout=20, allowed_updates=None, long_polling_timeout = 20): """ Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. :param allowed_updates: Array of string. List the types of updates you want your bot to receive. :param offset: Integer. Identifier of the first update to be returned. :param limit: Integer. Limits the number of updates to be retrieved. - :param timeout: Integer. Timeout in seconds for long polling. + :param timeout: Integer. Request connection timeout + :param long_polling_timeout. Timeout in seconds for long polling. :return: array of Updates """ - json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates) + json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates, long_polling_timeout) ret = [] for ju in json_updates: ret.append(types.Update.de_json(ju)) @@ -273,16 +274,16 @@ def __skip_updates(self): :return: total updates skipped """ total = 0 - updates = self.get_updates(offset=self.last_update_id, timeout=1) + updates = self.get_updates(offset=self.last_update_id, long_polling_timeout=1) while updates: total += len(updates) for update in updates: if update.update_id > self.last_update_id: self.last_update_id = update.update_id - updates = self.get_updates(offset=self.last_update_id + 1, timeout=1) + updates = self.get_updates(offset=self.last_update_id + 1, long_polling_timeout=1) return total - def __retrieve_updates(self, timeout=20): + def __retrieve_updates(self, timeout=20, long_polling_timeout=20): """ Retrieves any updates from the Telegram API. Registered listeners and applicable message handlers will be notified when a new message arrives. @@ -291,7 +292,7 @@ def __retrieve_updates(self, timeout=20): if self.skip_pending: logger.debug('Skipped {0} pending messages'.format(self.__skip_updates())) self.skip_pending = False - updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout) + updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout, long_polling_timeout = long_polling_timeout) self.process_new_updates(updates) def process_new_updates(self, updates): @@ -427,16 +428,16 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout=20, *args, **kwargs): + def infinity_polling(self, timeout=20, long_polling_timeout=20, *args, **kwargs): while not self.__stop_polling.is_set(): try: - self.polling(timeout=timeout, *args, **kwargs) + self.polling(timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) except Exception: time.sleep(timeout) pass logger.info("Break infinity polling") - def polling(self, none_stop=False, interval=0, timeout=20): + def polling(self, none_stop=False, interval=0, timeout=20, long_polling_timeout=20): """ 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. @@ -446,15 +447,16 @@ def polling(self, none_stop=False, interval=0, timeout=20): Always get updates. :param interval: :param none_stop: Do not stop polling when an ApiException occurs. - :param timeout: Timeout in seconds for long polling. + :param timeout: Integer. Request connection timeout + :param long_polling_timeout. Timeout in seconds for long polling. :return: """ if self.threaded: - self.__threaded_polling(none_stop, interval, timeout) + self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout) else: - self.__non_threaded_polling(none_stop, interval, timeout) + self.__non_threaded_polling(none_stop, interval, timeout, long_polling_timeout) - def __threaded_polling(self, none_stop=False, interval=0, timeout=3): + def __threaded_polling(self, none_stop=False, interval=0, timeout = None, long_polling_timeout = None): logger.info('Started polling.') self.__stop_polling.clear() error_interval = 0.25 @@ -469,7 +471,7 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout=3): while not self.__stop_polling.wait(interval): or_event.clear() try: - polling_thread.put(self.__retrieve_updates, timeout) + polling_thread.put(self.__retrieve_updates, timeout, long_polling_timeout) or_event.wait() # wait for polling thread finish, polling thread error or thread pool error @@ -517,14 +519,14 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout=3): polling_thread.stop() logger.info('Stopped polling.') - def __non_threaded_polling(self, none_stop=False, interval=0, timeout=3): + def __non_threaded_polling(self, none_stop=False, interval=0, timeout = None, long_polling_timeout = None): logger.info('Started polling.') self.__stop_polling.clear() error_interval = 0.25 while not self.__stop_polling.wait(interval): try: - self.__retrieve_updates(timeout) + self.__retrieve_updates(timeout, long_polling_timeout) error_interval = 0.25 except apihelper.ApiException as e: if self.exception_handler is not None: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ec4ad9bdf..40f83540f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -232,7 +232,7 @@ def get_webhook_info(token): return _make_request(token, method_url, params=payload) -def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=None): +def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=None, long_polling_timeout = None): method_url = r'getUpdates' payload = {} if offset: @@ -240,7 +240,9 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No if limit: payload['limit'] = limit if timeout: - payload['long_polling_timeout'] = timeout + payload['timeout'] = timeout + if long_polling_timeout: + payload['long_polling_timeout'] = long_polling_timeout if allowed_updates: payload['allowed_updates'] = json.dumps(allowed_updates) return _make_request(token, method_url, params=payload) From a548374a4db7d9c31a62d7a6887bbb1044cefcf5 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 7 Nov 2020 14:43:17 +0300 Subject: [PATCH 0502/1808] long_polling_timeout update 2 --- telebot/__init__.py | 10 +++++----- telebot/util.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3028c8c98..1a9f4795c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -433,7 +433,7 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, *args, **kwargs) try: self.polling(timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) except Exception: - time.sleep(timeout) + time.sleep(3) pass logger.info("Break infinity polling") @@ -456,7 +456,7 @@ def polling(self, none_stop=False, interval=0, timeout=20, long_polling_timeout= else: self.__non_threaded_polling(none_stop, interval, timeout, long_polling_timeout) - def __threaded_polling(self, none_stop=False, interval=0, timeout = None, long_polling_timeout = None): + def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None): logger.info('Started polling.') self.__stop_polling.clear() error_interval = 0.25 @@ -487,7 +487,7 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout = None, long_p if not handled: logger.error(e) - if not none_stop: + if not non_stop: self.__stop_polling.set() logger.info("Exception occurred. Stopping.") else: @@ -519,7 +519,7 @@ def __threaded_polling(self, none_stop=False, interval=0, timeout = None, long_p polling_thread.stop() logger.info('Stopped polling.') - def __non_threaded_polling(self, none_stop=False, interval=0, timeout = None, long_polling_timeout = None): + def __non_threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None): logger.info('Started polling.') self.__stop_polling.clear() error_interval = 0.25 @@ -536,7 +536,7 @@ def __non_threaded_polling(self, none_stop=False, interval=0, timeout = None, lo if not handled: logger.error(e) - if not none_stop: + if not non_stop: self.__stop_polling.set() logger.info("Exception occurred. Stopping.") else: diff --git a/telebot/util.py b/telebot/util.py index 3ad4463e2..4c5251c13 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -72,7 +72,7 @@ def run(self): except Queue.Empty: pass except Exception as e: - logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc()) + logger.debug(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc()) self.exception_info = e self.exception_event.set() From 00d125a298a0e24362d0950fed7c5ff3d040421c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 7 Nov 2020 14:59:45 +0300 Subject: [PATCH 0503/1808] long_polling_timeout update 3 --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1a9f4795c..ccc017a97 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -431,7 +431,7 @@ def __notify_update(self, new_messages): def infinity_polling(self, timeout=20, long_polling_timeout=20, *args, **kwargs): while not self.__stop_polling.is_set(): try: - self.polling(timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) + self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) except Exception: time.sleep(3) pass From bd276459656895489d606fe5f9d4d547dd232339 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 11 Nov 2020 00:32:34 +0300 Subject: [PATCH 0504/1808] set_webhook bugfinx set_webhook does not reset allowed_updates for empty list (to default) --- telebot/apihelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 40f83540f..8e817bd71 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -216,7 +216,7 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed files = {'certificate': certificate} if max_connections: payload['max_connections'] = max_connections - if allowed_updates: + if allowed_updates is not None: # Empty lists should pass payload['allowed_updates'] = json.dumps(allowed_updates) return _make_request(token, method_url, params=payload, files=files) @@ -243,7 +243,7 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No payload['timeout'] = timeout if long_polling_timeout: payload['long_polling_timeout'] = long_polling_timeout - if allowed_updates: + if allowed_updates is not None: # Empty lists should pass payload['allowed_updates'] = json.dumps(allowed_updates) return _make_request(token, method_url, params=payload) From 5824d47590cf77d36a0ebcaef1c184aa806e8b6c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Nov 2020 02:22:52 +0300 Subject: [PATCH 0505/1808] added only_if_banned to unban_chat_member --- telebot/__init__.py | 5 +++-- telebot/apihelper.py | 4 +++- telebot/types.py | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ccc017a97..20fdf4508 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1052,14 +1052,15 @@ def kick_chat_member(self, chat_id, user_id, until_date=None): """ return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date) - def unban_chat_member(self, chat_id, user_id): + def unban_chat_member(self, chat_id, user_id, only_if_banned = False): """ Removes member from the ban :param chat_id: :param user_id: + :param only_if_banned: :return: """ - return apihelper.unban_chat_member(self.token, chat_id, user_id) + return apihelper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) def restrict_chat_member( self, chat_id, user_id, until_date=None, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8e817bd71..3bc6d35d1 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -700,9 +700,11 @@ def kick_chat_member(token, chat_id, user_id, until_date=None): return _make_request(token, method_url, params=payload, method='post') -def unban_chat_member(token, chat_id, user_id): +def unban_chat_member(token, chat_id, user_id, only_if_banned): method_url = 'unbanChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} + if only_if_banned: + payload['only_if_banned'] = only_if_banned return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 6dcf6c209..5e6b59a5e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -413,6 +413,7 @@ def parse_entities(cls, message_entity_array): def __init__(self, message_id, from_user, date, chat, content_type, options, json_string): self.content_type = content_type + self.id = message_id # Lets fix the telegram usability ####up with ID in Message :) self.message_id = message_id self.from_user = from_user self.date = date From 640f3982622e9950a9ca472e39122568c1cf3834 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 20 Nov 2020 23:49:55 +0300 Subject: [PATCH 0506/1808] Version 3.7.4 release --- README.md | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 78939e5f5..3e3d109bb 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ## Getting started. -This API is tested with Python Python 3.6-3.8 and Pypy 3. +This API is tested with Python Python 3.6-3.9 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager)*: diff --git a/telebot/version.py b/telebot/version.py index 92aad920e..35ac6333b 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__ = '3.7.3' +__version__ = '3.7.4' From 0a2216a22bf89b8f3482462109270b6e9c048e04 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 29 Nov 2020 14:47:53 +0300 Subject: [PATCH 0507/1808] Bot API 5.0 pinning-unpinning logic update + add unpin_all_chat_messages() (former unpin_chat_message()) * update unpin_chat_message() (add message_id arg) --- telebot/__init__.py | 20 ++++++++++++++++++-- telebot/apihelper.py | 8 +++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 20fdf4508..f151195e5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1239,17 +1239,29 @@ def pin_chat_message(self, chat_id, message_id, disable_notification=False): """ return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) - def unpin_chat_message(self, chat_id): + def unpin_chat_message(self, chat_id, message_id): """ - Use this method to unpin a message in a supergroup chat. + Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param message_id: Int: Identifier of a message to unpin :return: """ return apihelper.unpin_chat_message(self.token, chat_id) + def unpin_all_chat_messages(self, chat_id): + """ + Use this method to unpin a all pinned messages in a supergroup chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: + """ + return apihelper.unpin_all_chat_messages(self.token, chat_id, message_id) + def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, disable_web_page_preview=None, reply_markup=None): """ @@ -2317,6 +2329,10 @@ def pin_chat_message(self, *args, **kwargs): def unpin_chat_message(self, *args): return TeleBot.unpin_chat_message(self, *args) + @util.async_dec() + def unpin_all_chat_messages(self, *args): + return TeleBot.unpin_all_chat_messages(self, *args) + @util.async_dec() def edit_message_text(self, *args, **kwargs): return TeleBot.edit_message_text(self, *args, **kwargs) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3bc6d35d1..b43d3412a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -830,8 +830,14 @@ def pin_chat_message(token, chat_id, message_id, disable_notification=None): return _make_request(token, method_url, params=payload, method='post') -def unpin_chat_message(token, chat_id): +def unpin_chat_message(token, chat_id, message_id): method_url = 'unpinChatMessage' + payload = {'chat_id': chat_id, 'message_id': message_id} + return _make_request(token, method_url, params=payload, method='post') + + +def unpin_all_chat_messages(token, chat_id): + method_url = 'unpinAllChatMessages' payload = {'chat_id': chat_id} return _make_request(token, method_url, params=payload, method='post') From 00c9351f83ddff17e0b4c88d380f8cbfcb90d6f0 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 29 Nov 2020 15:12:14 +0300 Subject: [PATCH 0508/1808] Hotfix 0a2216a22bf89b8f3482462109270b6e9c048e04 * message_id made optional as API declares --- telebot/__init__.py | 2 +- telebot/apihelper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f151195e5..4f59e040f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1239,7 +1239,7 @@ def pin_chat_message(self, chat_id, message_id, disable_notification=False): """ return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) - def unpin_chat_message(self, chat_id, message_id): + def unpin_chat_message(self, chat_id, message_id=None): """ Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b43d3412a..1cc57ab41 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -830,7 +830,7 @@ def pin_chat_message(token, chat_id, message_id, disable_notification=None): return _make_request(token, method_url, params=payload, method='post') -def unpin_chat_message(token, chat_id, message_id): +def unpin_chat_message(token, chat_id, message_id=None): method_url = 'unpinChatMessage' payload = {'chat_id': chat_id, 'message_id': message_id} return _make_request(token, method_url, params=payload, method='post') From b9898bbdda1ca783046acc9c976f63a56059cb82 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 29 Nov 2020 15:21:59 +0300 Subject: [PATCH 0509/1808] Fix 0a2216a22bf89b8f3482462109270b6e9c048e04 #2 + message_id arg of unpin_chat_message() passing to the helper - removed passing arg to unpin_all_chat_messages() --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4f59e040f..03b6c6cc2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1249,7 +1249,7 @@ def unpin_chat_message(self, chat_id, message_id=None): :param message_id: Int: Identifier of a message to unpin :return: """ - return apihelper.unpin_chat_message(self.token, chat_id) + return apihelper.unpin_chat_message(self.token, chat_id, message_id) def unpin_all_chat_messages(self, chat_id): """ @@ -1260,7 +1260,7 @@ def unpin_all_chat_messages(self, chat_id): (in the format @channelusername) :return: """ - return apihelper.unpin_all_chat_messages(self.token, chat_id, message_id) + return apihelper.unpin_all_chat_messages(self.token, chat_id) def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, disable_web_page_preview=None, reply_markup=None): From 6cc80f25d78149db849aaa83134d43ef4f10b52f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 29 Nov 2020 15:33:39 +0300 Subject: [PATCH 0510/1808] Bot API 5.0 pinning-unpinning logic post-fix. --- telebot/apihelper.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1cc57ab41..2fe3a7ddc 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -830,9 +830,11 @@ def pin_chat_message(token, chat_id, message_id, disable_notification=None): return _make_request(token, method_url, params=payload, method='post') -def unpin_chat_message(token, chat_id, message_id=None): +def unpin_chat_message(token, chat_id, message_id): method_url = 'unpinChatMessage' - payload = {'chat_id': chat_id, 'message_id': message_id} + payload = {'chat_id': chat_id} + if message_id: + payload['message_id'] = message_id return _make_request(token, method_url, params=payload, method='post') From 65c3ca58da9a787084cfaecbdaee9f97b9b709df Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 9 Dec 2020 01:41:07 +0300 Subject: [PATCH 0511/1808] Update __init__.py Allow parse_mode = "" to disable default parse mode. --- telebot/__init__.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 03b6c6cc2..dcb2cb392 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -687,8 +687,8 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me """ Use this method to send text messages. - Warning: Do not send more than about 5000 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 5000 characters, use the split_string function in apihelper.py. + Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. + If you must send more than 4000 characters, use the split_string function in apihelper.py. :param chat_id: :param text: @@ -700,7 +700,7 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me :param timeout: :return: API reply. """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, @@ -763,7 +763,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep :param timeout: :return: API reply. """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, @@ -788,6 +788,8 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None :param thumb: :return: Message """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + return types.Message.de_json( apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb)) @@ -807,7 +809,7 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa :param timeout: :return: Message """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, @@ -828,7 +830,7 @@ def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, re :param thumb: InputFile or String : Thumbnail of the file sent :return: API reply. """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + 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, @@ -871,7 +873,7 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param height: :return: """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, @@ -895,7 +897,7 @@ def send_animation(self, chat_id, animation, duration=None, :param thumb: InputFile or String : Thumbnail of the file sent :return: """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, @@ -1275,7 +1277,7 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ :param reply_markup: :return: """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + 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) @@ -1430,6 +1432,7 @@ def send_poll( :param timeout: :return: """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode if isinstance(question, types.Poll): raise Exception("The send_poll signature was changed, please see send_poll function details.") @@ -1485,7 +1488,7 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me :param reply_markup: :return: """ - parse_mode = self.parse_mode if not parse_mode else parse_mode + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, parse_mode, reply_markup) From 75a18e586991f40b0b54f0633fc1c7b548802506 Mon Sep 17 00:00:00 2001 From: vixfwis Date: Tue, 15 Dec 2020 14:38:50 +0300 Subject: [PATCH 0512/1808] add webhook example for Twisted framework --- .../webhook_twisted_echo_bot.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 examples/webhook_examples/webhook_twisted_echo_bot.py diff --git a/examples/webhook_examples/webhook_twisted_echo_bot.py b/examples/webhook_examples/webhook_twisted_echo_bot.py new file mode 100755 index 000000000..6db589310 --- /dev/null +++ b/examples/webhook_examples/webhook_twisted_echo_bot.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import logging +import telebot +import json +from twisted.internet import ssl, reactor +from twisted.web.resource import Resource, ErrorPage +from twisted.web.server import Site, Request + +API_TOKEN = '' + +WEBHOOK_HOST = '' +WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open') +WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr + +WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate +WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key + +# Quick'n'dirty SSL certificate generation: +# +# openssl genrsa -out webhook_pkey.pem 2048 +# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem +# +# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply +# with the same value in you put in WEBHOOK_HOST + +WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT) +WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN) + +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) +bot = telebot.TeleBot(API_TOKEN) + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +def send_welcome(message): + bot.reply_to(message, + ("Hi there, I am EchoBot.\n" + "I am here to echo your kind words back to you.")) + + +# Handle all other messages +@bot.message_handler(func=lambda message: True, content_types=['text']) +def echo_message(message): + bot.reply_to(message, message.text) + + +# Remove webhook, it fails sometimes the set if there is a previous webhook +bot.remove_webhook() + +# Set webhook +bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, + certificate=open(WEBHOOK_SSL_CERT, 'r')) + + +# Process webhook calls +class WebhookHandler(Resource): + isLeaf = True + def render_POST(self, request: Request): + request_body_dict = json.load(request.content) + update = telebot.types.Update.de_json(request_body_dict) + reactor.callInThread(lambda: bot.process_new_updates([update])) + return b'' + + +root = ErrorPage(403, 'Forbidden', '') +root.putChild(API_TOKEN.encode(), WebhookHandler()) +site = Site(root) +sslcontext = ssl.DefaultOpenSSLContextFactory(WEBHOOK_SSL_PRIV, WEBHOOK_SSL_CERT) +reactor.listenSSL(8443, site, sslcontext) +reactor.run() From 4658d2b8da94109a25cf9be714a3ebb69a2e2339 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 16 Dec 2020 01:57:30 +0300 Subject: [PATCH 0513/1808] Fix unban_chat_member in async --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index dcb2cb392..f5ee53a1a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2293,8 +2293,8 @@ def kick_chat_member(self, *args, **kwargs): return TeleBot.kick_chat_member(self, *args, **kwargs) @util.async_dec() - def unban_chat_member(self, *args): - return TeleBot.unban_chat_member(self, *args) + def unban_chat_member(self, *args, **kwargs): + return TeleBot.unban_chat_member(self, *args, **kwargs) @util.async_dec() def restrict_chat_member(self, *args, **kwargs): From 4e61bc3a8b3b4ee8cbd3365c59f826c5dc9d5ba3 Mon Sep 17 00:00:00 2001 From: vixfwis Date: Thu, 17 Dec 2020 15:34:36 +0300 Subject: [PATCH 0514/1808] add short description to example and readme files --- examples/webhook_examples/README.md | 13 +++++++++++-- .../webhook_examples/webhook_twisted_echo_bot.py | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) mode change 100755 => 100644 examples/webhook_examples/webhook_twisted_echo_bot.py diff --git a/examples/webhook_examples/README.md b/examples/webhook_examples/README.md index b1e435149..686a38b34 100644 --- a/examples/webhook_examples/README.md +++ b/examples/webhook_examples/README.md @@ -1,6 +1,6 @@ # Webhook examples using pyTelegramBotAPI -There are 4 examples in this directory using different libraries: +There are 5 examples in this directory using different libraries: * **Python (CPython):** *webhook_cpython_echo_bot.py* * **Pros:** @@ -42,4 +42,13 @@ There are 4 examples in this directory using different libraries: * **Cons:** * Requires Python 3.4.2+, don't work with Python 2 -*Latest update of this document: 2017-01-30* +* **Twisted (20.3.0):** *webhook_twisted_echo_bot.py* + * **Pros:** + * Asynchronous event-driven networking engine + * Very high performance + * Built-in support for many internet protocols + * **Cons:** + * Twisted is low-level, which may be good or bad depending on use case + * Considerable learning curve - reading docs is a must. + +*Latest update of this document: 2020-12-17* diff --git a/examples/webhook_examples/webhook_twisted_echo_bot.py b/examples/webhook_examples/webhook_twisted_echo_bot.py old mode 100755 new mode 100644 index 6db589310..9a15190ae --- a/examples/webhook_examples/webhook_twisted_echo_bot.py +++ b/examples/webhook_examples/webhook_twisted_echo_bot.py @@ -1,6 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +# This is an example echo bot using webhook with Twisted network framework. +# Updates are received with Twisted web server and processed in reactor thread pool. +# Relevant docs: +# https://twistedmatrix.com/documents/current/core/howto/reactor-basics.html +# https://twistedmatrix.com/documents/current/web/howto/using-twistedweb.html + import logging import telebot import json From 96686e522104564e14f43ada2ae9b9e249793d89 Mon Sep 17 00:00:00 2001 From: Mikhail Krostelev Date: Tue, 22 Dec 2020 21:38:38 +0300 Subject: [PATCH 0515/1808] fix restrict_chat_member method --- telebot/apihelper.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 2fe3a7ddc..d09b22de6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -715,25 +715,27 @@ def restrict_chat_member( can_add_web_page_previews=None, can_change_info=None, can_invite_users=None, can_pin_messages=None): method_url = 'restrictChatMember' - payload = {'chat_id': chat_id, 'user_id': user_id} + permissions = {} if until_date is not None: - payload['until_date'] = until_date + permissions['until_date'] = until_date if can_send_messages is not None: - payload['can_send_messages'] = can_send_messages + permissions['can_send_messages'] = can_send_messages if can_send_media_messages is not None: - payload['can_send_media_messages'] = can_send_media_messages + permissions['can_send_media_messages'] = can_send_media_messages if can_send_polls is not None: - payload['can_send_polls'] = can_send_polls + permissions['can_send_polls'] = can_send_polls if can_send_other_messages is not None: - payload['can_send_other_messages'] = can_send_other_messages + permissions['can_send_other_messages'] = can_send_other_messages if can_add_web_page_previews is not None: - payload['can_add_web_page_previews'] = can_add_web_page_previews + permissions['can_add_web_page_previews'] = can_add_web_page_previews if can_change_info is not None: - payload['can_change_info'] = can_change_info + permissions['can_change_info'] = can_change_info if can_invite_users is not None: - payload['can_invite_users'] = can_invite_users + permissions['can_invite_users'] = can_invite_users if can_pin_messages is not None: - payload['can_pin_messages'] = can_pin_messages + permissions['can_pin_messages'] = can_pin_messages + permissions_json = json.dumps(permissions) + payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} return _make_request(token, method_url, params=payload, method='post') From 2534dc5925b91b7f0ddfd54228e7007a8960fe73 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 24 Dec 2020 19:55:24 +0300 Subject: [PATCH 0516/1808] Exception if middleware is used but not enabled. --- examples/step_example.py | 2 +- telebot/__init__.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/step_example.py b/examples/step_example.py index 0fc17e538..0291c6672 100644 --- a/examples/step_example.py +++ b/examples/step_example.py @@ -68,7 +68,7 @@ def process_sex_step(message): if (sex == u'Male') or (sex == u'Female'): user.sex = sex else: - raise Exception() + raise Exception("Unknown sex") bot.send_message(chat_id, 'Nice to meet you ' + user.name + '\n Age:' + str(user.age) + '\n Sex:' + user.sex) except Exception as e: bot.reply_to(message, 'oooops') diff --git a/telebot/__init__.py b/telebot/__init__.py index f5ee53a1a..9f7674f47 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1432,10 +1432,9 @@ def send_poll( :param timeout: :return: """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode if isinstance(question, types.Poll): - raise Exception("The send_poll signature was changed, please see send_poll function details.") + raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") return types.Message.de_json( apihelper.send_poll( @@ -1759,7 +1758,6 @@ def print_channel_post_text(bot_instance, update): def decorator(handler): self.add_middleware_handler(handler, update_types) - return handler return decorator @@ -1771,6 +1769,9 @@ def add_middleware_handler(self, handler, update_types=None): :param update_types: :return: """ + if not apihelper.ENABLE_MIDDLEWARE: + raise RuntimeError("Middleware is not enabled. Use apihelper.ENABLE_MIDDLEWARE.") + if update_types: for update_type in update_types: self.typed_middleware_handlers[update_type].append(handler) From c4e624d99911a09e1b0d464dc31e5884c67a2bcd Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 24 Dec 2020 23:55:12 +0300 Subject: [PATCH 0517/1808] Avoid dead threads in treaded polling --- telebot/__init__.py | 19 +++++++++++-------- telebot/util.py | 1 - 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9f7674f47..5492666b0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -472,34 +472,32 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po or_event.clear() try: polling_thread.put(self.__retrieve_updates, timeout, long_polling_timeout) - or_event.wait() # wait for polling thread finish, polling thread error or thread pool error - polling_thread.raise_exceptions() self.worker_pool.raise_exceptions() - error_interval = 0.25 except apihelper.ApiException as e: if self.exception_handler is not None: handled = self.exception_handler.handle(e) else: handled = False - if not handled: logger.error(e) if not non_stop: self.__stop_polling.set() logger.info("Exception occurred. Stopping.") else: - polling_thread.clear_exceptions() - self.worker_pool.clear_exceptions() + # polling_thread.clear_exceptions() + # self.worker_pool.clear_exceptions() logger.info("Waiting for {0} seconds until retry".format(error_interval)) time.sleep(error_interval) error_interval *= 2 else: - polling_thread.clear_exceptions() - self.worker_pool.clear_exceptions() + # polling_thread.clear_exceptions() + # self.worker_pool.clear_exceptions() time.sleep(error_interval) + polling_thread.clear_exceptions() #* + self.worker_pool.clear_exceptions() #* except KeyboardInterrupt: logger.info("KeyboardInterrupt received.") self.__stop_polling.set() @@ -510,6 +508,9 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po else: handled = False if not handled: + polling_thread.stop() + polling_thread.clear_exceptions() #* + self.worker_pool.clear_exceptions() #* raise e else: polling_thread.clear_exceptions() @@ -517,6 +518,8 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po time.sleep(error_interval) polling_thread.stop() + polling_thread.clear_exceptions() #* + self.worker_pool.clear_exceptions() #* logger.info('Stopped polling.') def __non_threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None): diff --git a/telebot/util.py b/telebot/util.py index 4c5251c13..713cbc367 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -75,7 +75,6 @@ def run(self): logger.debug(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc()) self.exception_info = e self.exception_event.set() - if self.exception_callback: self.exception_callback(self, self.exception_info) self.continue_event.wait() From 93b86307d9ef49ad9f6f73521a9c47f60b5a1fec Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Fri, 25 Dec 2020 09:47:40 +1100 Subject: [PATCH 0518/1808] docs: fix simple typo, perfomance -> performance There is a small typo in examples/webhook_examples/README.md. Should read `performance` rather than `perfomance`. --- examples/webhook_examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/webhook_examples/README.md b/examples/webhook_examples/README.md index 686a38b34..221d9c5d4 100644 --- a/examples/webhook_examples/README.md +++ b/examples/webhook_examples/README.md @@ -37,7 +37,7 @@ There are 5 examples in this directory using different libraries: * **Pros:** * It's a web application framework * Python 3 compatible - * Asynchronous, excellent perfomance + * Asynchronous, excellent performance * Utilizes new async/await syntax * **Cons:** * Requires Python 3.4.2+, don't work with Python 2 From 6559f431b7ff808b40bf9500ac00ac43d4b4f36c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 29 Dec 2020 19:24:41 +0300 Subject: [PATCH 0519/1808] Bot API update Bot API conformance up to 4.4 Added webhook parameters from 5.0 --- README.md | 6 ++-- telebot/__init__.py | 78 ++++++++++++++++++++++++++++++-------------- telebot/apihelper.py | 17 +++++++--- telebot/types.py | 48 +++++++++++++++------------ 4 files changed, 97 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 3e3d109bb..a4285e0a8 100644 --- a/README.md +++ b/README.md @@ -540,11 +540,13 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} _Checking is in progress..._ -✅ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019) _- To be checked..._ +✅ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) _- To be checked..._ +* ✔ [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) * ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support. -* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support. +* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support. * ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018) * ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) * ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5492666b0..180211afe 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -235,17 +235,37 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a """ self.reply_backend.load_handlers(filename, del_file_after_loading) - def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None): - return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates) + def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None): + """ + Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an + update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update. + In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns True on success. + + :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration + :param certificate: Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details. + :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS + :return: + """ + return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address) - def delete_webhook(self): + def delete_webhook(self, drop_pending_updates=None): """ Use this method to remove webhook integration if you decide to switch back to getUpdates. + + :param drop_pending_updates: Pass True to drop all pending updates :return: bool """ - return apihelper.delete_webhook(self.token) + return apihelper.delete_webhook(self.token, drop_pending_updates) def get_webhook_info(self): + """ + Use this method to get current webhook status. Requires no parameters. + If the bot is using getUpdates, will return an object with the url field empty. + + :return: On success, returns a WebhookInfo object. + """ result = apihelper.get_webhook_info(self.token) return types.WebhookInfo.de_json(result) @@ -432,7 +452,8 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, *args, **kwargs) while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) - except Exception: + except Exception as e: + logger.error("Infinity polling exception: {}".format(e)) time.sleep(3) pass logger.info("Break infinity polling") @@ -1059,11 +1080,16 @@ def kick_chat_member(self, chat_id, user_id, until_date=None): def unban_chat_member(self, chat_id, user_id, only_if_banned = False): """ - Removes member from the ban - :param chat_id: - :param user_id: - :param only_if_banned: - :return: + Use this method to unban a previously kicked user in a supergroup or channel. + The user will not return to the group or channel automatically, but will be able to join via link, etc. + The bot must be an administrator for this to work. By default, this method guarantees that after the call + the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat + they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. + + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username) + :param user_id: Unique identifier of the target user + :param only_if_banned: Do nothing if the user is not banned + :return: True on success """ return apihelper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) @@ -1077,7 +1103,7 @@ def restrict_chat_member( Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. - Returns True on success. + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :param user_id: Int : Unique identifier of the target user @@ -1096,7 +1122,7 @@ def restrict_chat_member( :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, implies can_invite_users :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups - :return: types.Message + :return: True on success """ return apihelper.restrict_chat_member( self.token, chat_id, user_id, until_date, @@ -1111,7 +1137,8 @@ def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_m """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Pass False for all boolean parameters to demote a user. Returns True on success. + Pass False for all boolean parameters to demote a user. + :param chat_id: Unique identifier for the target chat or username of the target channel ( in the format @channelusername) :param user_id: Int : Unique identifier of the target user @@ -1125,7 +1152,7 @@ def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_m :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) - :return: + :return: True on success. """ return apihelper.promote_chat_member(self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, @@ -1134,26 +1161,27 @@ def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_m def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title): """ Use this method to set a custom title for an administrator - in a supergroup promoted by the bot. - Returns True on success. + in a supergroup promoted by the bot. + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param user_id: Unique identifier of the target user :param custom_title: New custom title for the administrator; 0-16 characters, emoji are not allowed - :return: + :return: True on success. """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) def set_chat_permissions(self, chat_id, permissions): """ Use this method to set default chat permissions for all members. - The bot must be an administrator in the group or a supergroup for this to work - and must have the can_restrict_members admin rights. + The bot must be an administrator in the group or a supergroup for this to work + and must have the can_restrict_members admin rights. + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param permissions: New default chat permissions - :return: + :return: True on success """ return apihelper.set_chat_permissions(self.token, chat_id, permissions) @@ -1161,10 +1189,10 @@ def export_chat_invite_link(self, chat_id): """ Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns exported invite link as String on success. + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :return: + :return: exported invite link as String on success. """ return apihelper.export_chat_invite_link(self.token, chat_id) @@ -1218,15 +1246,15 @@ def set_chat_title(self, chat_id, title): """ return apihelper.set_chat_title(self.token, chat_id, title) - def set_chat_description(self, chat_id, description): + def set_chat_description(self, chat_id, description=None): """ Use this method to change the description of a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param description: Str: New chat description, 0-255 characters - :return: + :return: True on success. """ return apihelper.set_chat_description(self.token, chat_id, description) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d09b22de6..207b3ae65 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -206,7 +206,7 @@ def send_message( return _make_request(token, method_url, params=payload, method='post') -def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None): +def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None): method_url = r'setWebhook' payload = { 'url': url if url else "", @@ -218,12 +218,17 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed payload['max_connections'] = max_connections if allowed_updates is not None: # Empty lists should pass payload['allowed_updates'] = json.dumps(allowed_updates) + if ip_address is not None: # Empty string should pass + payload['ip_address'] = ip_address return _make_request(token, method_url, params=payload, files=files) -def delete_webhook(token): +def delete_webhook(token, drop_pending_updates=None): method_url = r'deleteWebhook' - return _make_request(token, method_url) + payload = {} + if drop_pending_updates is not None: # None / True / False + payload['drop_pending_updates'] = drop_pending_updates + return _make_request(token, method_url, params=payload) def get_webhook_info(token): @@ -703,7 +708,7 @@ def kick_chat_member(token, chat_id, user_id, until_date=None): def unban_chat_member(token, chat_id, user_id, only_if_banned): method_url = 'unbanChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} - if only_if_banned: + if only_if_banned is not None: # None / True / False payload['only_if_banned'] = only_if_banned return _make_request(token, method_url, params=payload, method='post') @@ -820,7 +825,9 @@ def set_my_commands(token, commands): def set_chat_description(token, chat_id, description): method_url = 'setChatDescription' - payload = {'chat_id': chat_id, 'description': description} + payload = {'chat_id': chat_id} + if description is not None: # Allow empty strings + payload['description'] = description return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 5e6b59a5e..73e5db59a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -132,18 +132,20 @@ def de_json(cls, json_string): url = obj['url'] has_custom_certificate = obj['has_custom_certificate'] pending_update_count = obj['pending_update_count'] + ip_address = obj.get('ip_address') last_error_date = obj.get('last_error_date') last_error_message = obj.get('last_error_message') max_connections = obj.get('max_connections') allowed_updates = obj.get('allowed_updates') - return cls(url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, - max_connections, allowed_updates) + return cls(url, has_custom_certificate, pending_update_count, ip_address, last_error_date, + last_error_message, max_connections, allowed_updates) - def __init__(self, url, has_custom_certificate, pending_update_count, last_error_date, last_error_message, - max_connections, allowed_updates): + def __init__(self, url, has_custom_certificate, pending_update_count, ip_address, last_error_date, + last_error_message, max_connections, allowed_updates): self.url = url self.has_custom_certificate = has_custom_certificate self.pending_update_count = pending_update_count + self.ip_address = ip_address self.last_error_date = last_error_date self.last_error_message = last_error_message self.max_connections = max_connections @@ -218,8 +220,8 @@ def de_json(cls, json_string): username = obj.get('username') first_name = obj.get('first_name') last_name = obj.get('last_name') - all_members_are_administrators = obj.get('all_members_are_administrators') photo = ChatPhoto.de_json(obj.get('photo')) + bio = obj.get('bio') description = obj.get('description') invite_link = obj.get('invite_link') pinned_message = Message.de_json(obj.get('pinned_message')) @@ -227,25 +229,27 @@ def de_json(cls, json_string): slow_mode_delay = obj.get('slow_mode_delay') sticker_set_name = obj.get('sticker_set_name') can_set_sticker_set = obj.get('can_set_sticker_set') + linked_chat_id = obj.get('linked_chat_id') + location = None # Not implemented return cls( id, type, title, username, first_name, last_name, - all_members_are_administrators, photo, description, invite_link, + photo, bio, description, invite_link, pinned_message, permissions, slow_mode_delay, sticker_set_name, - can_set_sticker_set) + can_set_sticker_set, linked_chat_id, location) def __init__(self, id, type, title=None, username=None, first_name=None, - last_name=None, all_members_are_administrators=None, - photo=None, description=None, invite_link=None, + last_name=None, photo=None, bio=None, description=None, invite_link=None, pinned_message=None, permissions=None, slow_mode_delay=None, - sticker_set_name=None, can_set_sticker_set=None): + sticker_set_name=None, can_set_sticker_set=None, + linked_chat_id=None, location=None): self.id = id self.type = type self.title = title self.username = username self.first_name = first_name self.last_name = last_name - self.all_members_are_administrators = all_members_are_administrators self.photo = photo + self.bio = bio self.description = description self.invite_link = invite_link self.pinned_message = pinned_message @@ -253,6 +257,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.slow_mode_delay = slow_mode_delay self.sticker_set_name = sticker_set_name self.can_set_sticker_set = can_set_sticker_set + self.linked_chat_id = linked_chat_id + self.location = location class Message(JsonDeserializable): @@ -1054,7 +1060,7 @@ def to_dict(self): json_dict['forward_text'] = self.forward_text if self.bot_username: json_dict['bot_username'] = self.bot_username - if self.request_write_access: + if self.request_write_access is not None: json_dict['request_write_access'] = self.request_write_access return json_dict @@ -1160,7 +1166,6 @@ def de_json(cls, json_string): user = User.de_json(obj['user']) status = obj['status'] custom_title = obj.get('custom_title') - until_date = obj.get('until_date') can_be_edited = obj.get('can_be_edited') can_post_messages = obj.get('can_post_messages') can_edit_messages = obj.get('can_edit_messages') @@ -1176,23 +1181,23 @@ def de_json(cls, json_string): can_send_polls = obj.get('can_send_polls') can_send_other_messages = obj.get('can_send_other_messages') can_add_web_page_previews = obj.get('can_add_web_page_previews') + until_date = obj.get('until_date') return cls( - user, status, custom_title, until_date, can_be_edited, can_post_messages, + user, status, custom_title, can_be_edited, can_post_messages, can_edit_messages, can_delete_messages, can_restrict_members, can_promote_members, can_change_info, can_invite_users, can_pin_messages, is_member, can_send_messages, can_send_media_messages, can_send_polls, - can_send_other_messages, can_add_web_page_previews) + can_send_other_messages, can_add_web_page_previews, until_date) - def __init__(self, user, status, custom_title=None, until_date=None, can_be_edited=None, + def __init__(self, user, status, custom_title=None, can_be_edited=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_restrict_members=None, can_promote_members=None, can_change_info=None, can_invite_users=None, can_pin_messages=None, is_member=None, can_send_messages=None, can_send_media_messages=None, can_send_polls=None, - can_send_other_messages=None, can_add_web_page_previews=None): + can_send_other_messages=None, can_add_web_page_previews=None, until_date=None): self.user = user self.status = status self.custom_title = custom_title - self.until_date = until_date self.can_be_edited = can_be_edited self.can_post_messages = can_post_messages self.can_edit_messages = can_edit_messages @@ -1208,6 +1213,7 @@ def __init__(self, user, status, custom_title=None, until_date=None, can_be_edit self.can_send_polls = can_send_polls self.can_send_other_messages = can_send_other_messages self.can_add_web_page_previews = can_add_web_page_previews + self.until_date = until_date class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): @@ -2297,14 +2303,16 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) name = obj['name'] title = obj['title'] + is_animated = obj['is_animated'] contains_masks = obj['contains_masks'] stickers = [] for s in obj['stickers']: stickers.append(Sticker.de_json(s)) - return cls(name, title, contains_masks, stickers) + return cls(name, title, is_animated, contains_masks, stickers) - def __init__(self, name, title, contains_masks, stickers): + def __init__(self, name, title, is_animated, contains_masks, stickers): self.stickers = stickers + self.is_animated = is_animated self.contains_masks = contains_masks self.title = title self.name = name From eab36a99e981a618f5feafa485c21c9afb58daf6 Mon Sep 17 00:00:00 2001 From: FrankWang Date: Fri, 1 Jan 2021 23:14:00 +0800 Subject: [PATCH 0520/1808] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a4285e0a8..0d000c8c1 100644 --- a/README.md +++ b/README.md @@ -668,5 +668,6 @@ Get help. Discuss. Chat. * [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want. * [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast. * [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour) +* [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani) -Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh. +Want to have your bot listed here? Just make a pull requet. From 6b0484b9db21e05c96545253c3c484916ffa95ae Mon Sep 17 00:00:00 2001 From: Taras Date: Tue, 5 Jan 2021 13:06:14 +0200 Subject: [PATCH 0521/1808] Modify RedisHandlerBackend, add argument "password=None" to __init__() With argument "password=None" in method __init__(), and argument "password" in "self.redis = Redis(host, port, db, password)", will be able to use Redis with password protection, if password is set . --- telebot/handler_backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index a10d13c4a..9b54f7c72 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -114,11 +114,11 @@ def return_load_handlers(filename, del_file_after_loading=True): class RedisHandlerBackend(HandlerBackend): - def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot'): + def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot', password=None): super(RedisHandlerBackend, self).__init__(handlers) from redis import Redis self.prefix = prefix - self.redis = Redis(host, port, db) + self.redis = Redis(host, port, db, password) def _key(self, handle_group_id): return ':'.join((self.prefix, str(handle_group_id))) From 0126ba82a588839b26a07b3748de880f301e72af Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo <66796758+barbax7@users.noreply.github.com> Date: Tue, 5 Jan 2021 17:31:18 +0100 Subject: [PATCH 0522/1808] Added bot @donamazonbot to the list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0d000c8c1..20e25a1d7 100644 --- a/README.md +++ b/README.md @@ -669,5 +669,6 @@ Get help. Discuss. Chat. * [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast. * [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour) * [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani) +* [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user. Want to have your bot listed here? Just make a pull requet. From 5dc008a762d5fc80a7270dbf129436a73f88af35 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 7 Jan 2021 00:13:44 +0300 Subject: [PATCH 0523/1808] Added timeout to xxx_webhook --- telebot/__init__.py | 18 +++++++++++------- telebot/apihelper.py | 12 +++++++++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 180211afe..a6a20ff59 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -235,7 +235,7 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a """ self.reply_backend.load_handlers(filename, del_file_after_loading) - def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None): + def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, timeout=None): """ Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update. @@ -246,27 +246,30 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS + :param timeout: Integer. Request connection timeout :return: """ - return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address) + return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, timeout) - def delete_webhook(self, drop_pending_updates=None): + def delete_webhook(self, drop_pending_updates=None, timeout=None): """ Use this method to remove webhook integration if you decide to switch back to getUpdates. :param drop_pending_updates: Pass True to drop all pending updates + :param timeout: Integer. Request connection timeout :return: bool """ - return apihelper.delete_webhook(self.token, drop_pending_updates) + return apihelper.delete_webhook(self.token, drop_pending_updates, timeout) - def get_webhook_info(self): + def get_webhook_info(self, timeout=None): """ Use this method to get current webhook status. Requires no parameters. If the bot is using getUpdates, will return an object with the url field empty. + :param timeout: Integer. Request connection timeout :return: On success, returns a WebhookInfo object. """ - result = apihelper.get_webhook_info(self.token) + result = apihelper.get_webhook_info(self.token, timeout) return types.WebhookInfo.de_json(result) def remove_webhook(self): @@ -455,7 +458,8 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, *args, **kwargs) except Exception as e: logger.error("Infinity polling exception: {}".format(e)) time.sleep(3) - pass + continue + logger.info("Infinity polling: polling exited") logger.info("Break infinity polling") def polling(self, none_stop=False, interval=0, timeout=20, long_polling_timeout=20): diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 207b3ae65..f059cb414 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -206,7 +206,7 @@ def send_message( return _make_request(token, method_url, params=payload, method='post') -def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None): +def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, timeout=None): method_url = r'setWebhook' payload = { 'url': url if url else "", @@ -220,20 +220,26 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed payload['allowed_updates'] = json.dumps(allowed_updates) if ip_address is not None: # Empty string should pass payload['ip_address'] = ip_address + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, files=files) -def delete_webhook(token, drop_pending_updates=None): +def delete_webhook(token, drop_pending_updates=None, timeout=None): method_url = r'deleteWebhook' payload = {} if drop_pending_updates is not None: # None / True / False payload['drop_pending_updates'] = drop_pending_updates + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) -def get_webhook_info(token): +def get_webhook_info(token, timeout=None): method_url = r'getWebhookInfo' payload = {} + if timeout: + payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) From 0900acfae9cf9270fd04c918a009e15ec0730b06 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 7 Jan 2021 20:46:50 +0300 Subject: [PATCH 0524/1808] Release version 3.7.5 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 35ac6333b..31a8e244f 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__ = '3.7.4' +__version__ = '3.7.5' From 52ebb5a1a78039dcf12a839bd7975502952c5475 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 9 Jan 2021 21:22:49 +0300 Subject: [PATCH 0525/1808] drop_pending_updates in set_webhook --- telebot/__init__.py | 7 +++++-- telebot/apihelper.py | 11 +++++++---- tests/test_telebot.py | 16 +++++++++++----- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a6a20ff59..f166a27da 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -45,6 +45,7 @@ class ExceptionHandler: Class for handling exceptions while Polling """ + # noinspection PyMethodMayBeStatic,PyUnusedLocal def handle(self, exception): return False @@ -235,7 +236,8 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a """ self.reply_backend.load_handlers(filename, del_file_after_loading) - def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, timeout=None): + def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, + drop_pending_updates = None, timeout=None): """ Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update. @@ -246,10 +248,11 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS + :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout :return: """ - return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, timeout) + return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout) def delete_webhook(self, drop_pending_updates=None, timeout=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f059cb414..1cedbc807 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -206,7 +206,8 @@ def send_message( return _make_request(token, method_url, params=payload, method='post') -def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, timeout=None): +def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, + drop_pending_updates = None, timeout=None): method_url = r'setWebhook' payload = { 'url': url if url else "", @@ -216,10 +217,12 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed files = {'certificate': certificate} if max_connections: payload['max_connections'] = max_connections - if allowed_updates is not None: # Empty lists should pass + if allowed_updates is not None: # Empty lists should pass payload['allowed_updates'] = json.dumps(allowed_updates) - if ip_address is not None: # Empty string should pass + if ip_address is not None: # Empty string should pass payload['ip_address'] = ip_address + if drop_pending_updates is not None: # Any bool value should pass + payload['drop_pending_updates'] = drop_pending_updates if timeout: payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload, files=files) @@ -228,7 +231,7 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed def delete_webhook(token, drop_pending_updates=None, timeout=None): method_url = r'deleteWebhook' payload = {} - if drop_pending_updates is not None: # None / True / False + if drop_pending_updates is not None: # Any bool value should pass payload['drop_pending_updates'] = drop_pending_updates if timeout: payload['connect-timeout'] = timeout diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 6ca56fbb0..547d29d05 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -48,6 +48,7 @@ def test_message_handler_reg(self): bot = telebot.TeleBot('') msg = self.create_text_message(r'https://web.telegram.org/') + # noinspection PyUnusedLocal @bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') def command_url(message): msg.text = 'got' @@ -60,6 +61,7 @@ def test_message_handler_lambda(self): bot = telebot.TeleBot('') msg = self.create_text_message(r'lambda_text') + # noinspection PyUnusedLocal @bot.message_handler(func=lambda message: r'lambda' in message.text) def command_url(message): msg.text = 'got' @@ -72,6 +74,7 @@ def test_message_handler_lambda_fail(self): bot = telebot.TeleBot('') msg = self.create_text_message(r'text') + # noinspection PyUnusedLocal @bot.message_handler(func=lambda message: r'lambda' in message.text) def command_url(message): msg.text = 'got' @@ -84,6 +87,7 @@ def test_message_handler_reg_fail(self): bot = telebot.TeleBot('') msg = self.create_text_message(r'web.telegram.org/') + # noinspection PyUnusedLocal @bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)') def command_url(message): msg.text = 'got' @@ -522,6 +526,7 @@ def test_typed_middleware_handler(self): tb = telebot.TeleBot('') update = self.create_message_update('/help') + # noinspection PyUnusedLocal @tb.middleware_handler(update_types=['message']) def middleware(tb_instance, message): message.text = 'got' @@ -542,9 +547,10 @@ def test_default_middleware_handler(self): tb = telebot.TeleBot('') update = self.create_message_update('/help') + # noinspection PyUnusedLocal @tb.middleware_handler() - def middleware(tb_instance, update): - update.message.text = 'got' + def middleware(tb_instance, mw_update): + mw_update.message.text = 'got' @tb.message_handler(func=lambda m: m.text == 'got') def command_handler(message): @@ -556,6 +562,6 @@ def command_handler(message): def test_chat_permissions(self): return # CHAT_ID is private chat, no permissions can be set - tb = telebot.TeleBot(TOKEN) - permissions = types.ChatPermissions(can_send_messages=True, can_send_polls=False) - msg = tb.set_chat_permissions(CHAT_ID, permissions) + #tb = telebot.TeleBot(TOKEN) + #permissions = types.ChatPermissions(can_send_messages=True, can_send_polls=False) + #msg = tb.set_chat_permissions(CHAT_ID, permissions) From 58281f0a1000fe510d53ca541b4908754743e626 Mon Sep 17 00:00:00 2001 From: Alireza Date: Mon, 11 Jan 2021 02:50:17 +0330 Subject: [PATCH 0526/1808] Added copyMessage method --- telebot/__init__.py | 25 ++++++++++++++++++++++++- telebot/apihelper.py | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f166a27da..91cd33219 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -750,6 +750,24 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio return types.Message.de_json( apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) + def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, + reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None, + disable_notification=None, timeout=None): + # FIXME: rewrite the docstring + """ + Use this method to copy messages of any kind. + :param disable_notification: + :param chat_id: which chat to forward + :param from_chat_id: which chat message from + :param message_id: message id + :param timeout: + :return: API reply. + """ + return types.Message.de_json( + apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, + reply_to_message_id, allow_sending_without_reply, reply_markup, + disable_notification, timeout)) + def delete_message(self, chat_id, message_id, timeout=None): """ Use this method to delete message. Returns True on success. @@ -863,7 +881,7 @@ def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, re """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - return types.Message.de_json( + 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)) @@ -2267,6 +2285,11 @@ def send_dice(self, *args, **kwargs): def forward_message(self, *args, **kwargs): return TeleBot.forward_message(self, *args, **kwargs) + @util.async_dec() + def copy_message(self, *args, **kwargs): + return TeleBot.copy_message(self, *args, **kwargs) + + @util.async_dec() def delete_message(self, *args): return TeleBot.delete_message(self, *args) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1cedbc807..9ba0a1a31 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -326,6 +326,30 @@ def forward_message( return _make_request(token, method_url, params=payload) +def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, + reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None, + disable_notification=None, timeout=None): + method_url = r'copyMessage' + payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} + if caption is not None: + payload['caption'] = caption + if parse_mode is not None: + payload['parse_mode'] = parse_mode + if caption_entities is not None: + payload['caption_entities'] = caption_entities + if reply_to_message_id is not None: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup is not None: + payload['reply_markup'] = reply_markup + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['connect-timeout'] = timeout + return _make_request(token, method_url, params=payload) + + def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, From b684c4f60de4604d2cf959efcafbd33a3d654989 Mon Sep 17 00:00:00 2001 From: Alireza Date: Tue, 12 Jan 2021 11:17:53 +0330 Subject: [PATCH 0527/1808] Fix Things on copyMessage --- telebot/__init__.py | 17 ++++++++++++----- telebot/apihelper.py | 10 ++++++++-- telebot/types.py | 26 +++++++++++++++++++++++++- tests/test_telebot.py | 7 +++++++ 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 91cd33219..b8b679e29 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7,6 +7,8 @@ import threading import time +from telebot.types import MessageID + logger = logging.getLogger('TeleBot') formatter = logging.Formatter( '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' @@ -751,19 +753,24 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, - reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None, - disable_notification=None, timeout=None): - # FIXME: rewrite the docstring + disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None, + timeout=None): """ Use this method to copy messages of any kind. - :param disable_notification: :param chat_id: which chat to forward :param from_chat_id: which chat message from :param message_id: message id + :param caption: + :param parse_mode: + :param caption_entities: + :param disable_notification: + :param reply_to_message_id: + :param allow_sending_without_reply: + :param reply_markup: :param timeout: :return: API reply. """ - return types.Message.de_json( + return MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, reply_to_message_id, allow_sending_without_reply, reply_markup, disable_notification, timeout)) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9ba0a1a31..407ffcf30 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -336,11 +336,11 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m if parse_mode is not None: payload['parse_mode'] = parse_mode if caption_entities is not None: - payload['caption_entities'] = caption_entities + payload['caption_entities'] = _convert_entites(caption_entities) if reply_to_message_id is not None: payload['reply_to_message_id'] = reply_to_message_id if reply_markup is not None: - payload['reply_markup'] = reply_markup + payload['reply_markup'] = _convert_markup(reply_markup) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if disable_notification is not None: @@ -1309,6 +1309,12 @@ def _convert_markup(markup): return markup +def _convert_entites(entites): + if isinstance(entites[0], types.JsonSerializable): + return [entity.to_json() for entity in entites] + return entites + + 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 73e5db59a..94316c05f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -261,6 +261,19 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.location = location +class MessageID(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if(json_string is None): + return None + obj = cls.check_json(json_string) + message_id = obj['message_id'] + return cls(message_id) + + def __init__(self, message_id): + self.message_id = message_id + + class Message(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -548,7 +561,7 @@ def html_caption(self): return self.__html_text(self.caption, self.caption_entities) -class MessageEntity(JsonDeserializable): +class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -569,6 +582,17 @@ def __init__(self, type, offset, length, url=None, user=None, language=None): self.user = user self.language = language + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {"type": self.type, + "offset": self.offset, + "length": self.length, + "url": self.url, + "user": self.user, + "language": self.language} + class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): @classmethod diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 547d29d05..a70911ebc 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -293,6 +293,13 @@ def test_forward_message(self): ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id) assert ret_msg.forward_from + def test_copy_message(self): + text = 'CI copy_message Test Message' + tb = telebot.TeleBot(TOKEN) + msg = tb.send_message(CHAT_ID, text) + ret_msg = tb.copy_message(CHAT_ID, CHAT_ID, msg.message_id) + assert ret_msg + def test_forward_message_dis_noti(self): text = 'CI forward_message Test Message' tb = telebot.TeleBot(TOKEN) From b561e353303f6befc5d4a39e53ada27b070a41d8 Mon Sep 17 00:00:00 2001 From: Alireza Date: Tue, 12 Jan 2021 11:19:57 +0330 Subject: [PATCH 0528/1808] Update __init__.py --- telebot/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b8b679e29..8696dede2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7,8 +7,6 @@ import threading import time -from telebot.types import MessageID - logger = logging.getLogger('TeleBot') formatter = logging.Formatter( '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' @@ -770,7 +768,7 @@ def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mo :param timeout: :return: API reply. """ - return MessageID.de_json( + return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, reply_to_message_id, allow_sending_without_reply, reply_markup, disable_notification, timeout)) From 6e3e1591097774eaf130d99e18842a52411cc785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Coego?= <37184503+dgarcoe@users.noreply.github.com> Date: Wed, 13 Jan 2021 17:55:08 +0100 Subject: [PATCH 0529/1808] Update README.md I included my bot in the README. Thanks for the library! --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 20e25a1d7..ef044835e 100644 --- a/README.md +++ b/README.md @@ -670,5 +670,6 @@ Get help. Discuss. Chat. * [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour) * [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani) * [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user. +* [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources. Want to have your bot listed here? Just make a pull requet. From 82838e1d26bd26ff967fc5fbc4bdbf4f217fbb98 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 14 Jan 2021 03:44:37 +0300 Subject: [PATCH 0530/1808] Infinity polling fall down fixed --- telebot/__init__.py | 4 +++- telebot/apihelper.py | 2 +- telebot/util.py | 8 +++++--- telebot/version.py | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f166a27da..af5309d37 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6,6 +6,7 @@ import sys import threading import time +import traceback logger = logging.getLogger('TeleBot') formatter = logging.Formatter( @@ -459,7 +460,8 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, *args, **kwargs) try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) except Exception as e: - logger.error("Infinity polling exception: {}".format(e)) + logger.error("Infinity polling exception: %s", str(e)) + logger.debug("Exception traceback:\n%s", traceback.format_exc()) time.sleep(3) continue logger.info("Infinity polling: polling exited") diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1cedbc807..00763e0f7 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -254,7 +254,7 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No if limit: payload['limit'] = limit if timeout: - payload['timeout'] = timeout + payload['connect-timeout'] = timeout if long_polling_timeout: payload['long_polling_timeout'] = long_polling_timeout if allowed_updates is not None: # Empty lists should pass diff --git a/telebot/util.py b/telebot/util.py index 713cbc367..1226ecc5c 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -235,8 +235,10 @@ def or_clear(self): def orify(e, changed_callback): - e._set = e.set - e._clear = e.clear + if not hasattr(e, "_set"): + e._set = e.set + if not hasattr(e, "_clear"): + e._clear = e.clear e.changed = changed_callback e.set = lambda: or_set(e) e.clear = lambda: or_clear(e) @@ -244,7 +246,7 @@ def orify(e, changed_callback): def OrEvent(*events): or_event = threading.Event() def changed(): - bools = [e.is_set() for e in events] + bools = [ev.is_set() for ev in events] if any(bools): or_event.set() else: diff --git a/telebot/version.py b/telebot/version.py index 31a8e244f..2400619e9 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__ = '3.7.5' +__version__ = '3.7.5.u1' From f56da17741c3cd045d98087a31bf9404081892a8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 14 Jan 2021 15:45:47 +0300 Subject: [PATCH 0531/1808] Fix restrict_chat_member until_date bug --- telebot/apihelper.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 00763e0f7..41b647c34 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import time +import datetime try: import ujson as json @@ -709,7 +710,9 @@ def get_method_by_type(data_type): def kick_chat_member(token, chat_id, user_id, until_date=None): method_url = 'kickChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} - if until_date: + if isinstance(until_date, datetime.datetime): + payload['until_date'] = until_date.timestamp() + else: payload['until_date'] = until_date return _make_request(token, method_url, params=payload, method='post') @@ -730,8 +733,6 @@ def restrict_chat_member( can_invite_users=None, can_pin_messages=None): method_url = 'restrictChatMember' permissions = {} - if until_date is not None: - permissions['until_date'] = until_date if can_send_messages is not None: permissions['can_send_messages'] = can_send_messages if can_send_media_messages is not None: @@ -750,6 +751,11 @@ def restrict_chat_member( permissions['can_pin_messages'] = can_pin_messages permissions_json = json.dumps(permissions) payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} + if until_date is not None: + if isinstance(until_date, datetime.datetime): + payload['until_date'] = until_date.timestamp() + else: + payload['until_date'] = until_date return _make_request(token, method_url, params=payload, method='post') @@ -1246,7 +1252,10 @@ def send_poll( if open_period is not None: payload['open_period'] = open_period if close_date is not None: - payload['close_date'] = close_date + if isinstance(close_date, datetime.datetime): + payload['close_date'] = close_date.timestamp() + else: + payload['close_date'] = close_date if is_closed is not None: payload['is_closed'] = is_closed From 2e5250ec9844094a2047589b4278b1366307f10a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 14 Jan 2021 15:48:30 +0300 Subject: [PATCH 0532/1808] Version update to previous commit --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 2400619e9..60813d862 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__ = '3.7.5.u1' +__version__ = '3.7.5.u2' From e9ba2fd8bbf9f4683d98e8fc8256647b517e7545 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 16 Jan 2021 02:14:29 +0300 Subject: [PATCH 0533/1808] Polling timeout fix --- telebot/apihelper.py | 5 ++++- telebot/version.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 41b647c34..98ed5eb1e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -69,8 +69,11 @@ def _make_request(token, method_name, method='get', params=None, files=None): if 'connect-timeout' in params: connect_timeout = params.pop('connect-timeout') + 10 if 'long_polling_timeout' in params: - # For getUpdates: the only function with timeout on the BOT API side + # For getUpdates + # The only function with timeout on the BOT API side params['timeout'] = params.pop('long_polling_timeout') + # Long polling hangs for given time. Read timeout should be greater that long_polling_timeout + read_timeout = max(params['timeout'] + 10, read_timeout) result = None diff --git a/telebot/version.py b/telebot/version.py index 60813d862..308e7a377 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__ = '3.7.5.u2' +__version__ = '3.7.5.u3' From bc54a5379cd10ca52d0957c29a3a6908e11fc454 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 16 Jan 2021 23:50:25 +0300 Subject: [PATCH 0534/1808] Added short live sessions --- telebot/apihelper.py | 23 ++++++++++++++++++----- telebot/version.py | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 98ed5eb1e..849bf55c5 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import time -import datetime +from datetime import datetime try: import ujson as json @@ -29,6 +29,7 @@ CONNECT_TIMEOUT = 3.5 READ_TIMEOUT = 9999 +SESSION_TIME_TO_LIVE = None # In seconds. None - live forever, 0 - one-time RETRY_ON_ERROR = False RETRY_TIMEOUT = 2 @@ -40,7 +41,19 @@ def _get_req_session(reset=False): - return util.per_thread('req_session', lambda: session if session else requests.session(), reset) + if SESSION_TIME_TO_LIVE: + # If session TTL is set - check time passed + creation_date = util.per_thread('req_session_time', lambda: datetime(2021, 1, 1), reset) + if (datetime.now() - creation_date).total_seconds() > SESSION_TIME_TO_LIVE: + # Force session reset + reset = True + + if SESSION_TIME_TO_LIVE == 0: + # Session is one-time use + return requests.sessions.Session() + else: + # Session lives some time or forever once created. Default + return util.per_thread('req_session', lambda: session if session else requests.sessions.Session(), reset) def _make_request(token, method_name, method='get', params=None, files=None): @@ -713,7 +726,7 @@ def get_method_by_type(data_type): def kick_chat_member(token, chat_id, user_id, until_date=None): method_url = 'kickChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} - if isinstance(until_date, datetime.datetime): + if isinstance(until_date, datetime): payload['until_date'] = until_date.timestamp() else: payload['until_date'] = until_date @@ -755,7 +768,7 @@ def restrict_chat_member( permissions_json = json.dumps(permissions) payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} if until_date is not None: - if isinstance(until_date, datetime.datetime): + if isinstance(until_date, datetime): payload['until_date'] = until_date.timestamp() else: payload['until_date'] = until_date @@ -1255,7 +1268,7 @@ def send_poll( if open_period is not None: payload['open_period'] = open_period if close_date is not None: - if isinstance(close_date, datetime.datetime): + if isinstance(close_date, datetime): payload['close_date'] = close_date.timestamp() else: payload['close_date'] = close_date diff --git a/telebot/version.py b/telebot/version.py index 308e7a377..fdd20d337 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__ = '3.7.5.u3' +__version__ = '3.7.5.u4' From ec8714ad3a267276c45f376605b78af7a7078b44 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 17 Jan 2021 00:43:52 +0300 Subject: [PATCH 0535/1808] Short live sessions u1 --- telebot/apihelper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 849bf55c5..2a4231884 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -43,10 +43,12 @@ def _get_req_session(reset=False): if SESSION_TIME_TO_LIVE: # If session TTL is set - check time passed - creation_date = util.per_thread('req_session_time', lambda: datetime(2021, 1, 1), reset) + creation_date = util.per_thread('req_session_time', lambda: datetime.now(), reset) if (datetime.now() - creation_date).total_seconds() > SESSION_TIME_TO_LIVE: # Force session reset reset = True + # Save reset time + util.per_thread('req_session_time', lambda: datetime.now(), True) if SESSION_TIME_TO_LIVE == 0: # Session is one-time use From ea51b1e95e0430d7128bc055a02e19f543172267 Mon Sep 17 00:00:00 2001 From: Robin Modisch Date: Sun, 17 Jan 2021 01:06:47 +0100 Subject: [PATCH 0536/1808] hide token from debug logs prevent leaks of the bot token by hiding it from the log --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 2a4231884..27d3c959e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -73,7 +73,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): else: request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) - logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files)) + logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files).replace(token, "{TOKEN}")) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT if files and format_header_param: From 3109e35bb4f0d5f921a72ecb54eb1dce9f0bfa17 Mon Sep 17 00:00:00 2001 From: Robin Modisch Date: Sun, 17 Jan 2021 01:26:38 +0100 Subject: [PATCH 0537/1808] show bot id --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 27d3c959e..4f9f84c46 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -73,7 +73,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): else: request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) - logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files).replace(token, "{TOKEN}")) + logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files).replace(token, token.split(':')[0] + ":{TOKEN}")) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT if files and format_header_param: From d57aa04bfb77a29d94d5c055a08aee3297179d12 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 18 Jan 2021 01:02:19 +0300 Subject: [PATCH 0538/1808] Release v.3.7.6 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index fdd20d337..e3cfe4c6e 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__ = '3.7.5.u4' +__version__ = '3.7.6' From 80c9e17fd41123b17b336079707d3921342bc596 Mon Sep 17 00:00:00 2001 From: Robin Modisch Date: Sun, 17 Jan 2021 23:22:45 +0100 Subject: [PATCH 0539/1808] add apihelper.ENABLE_MIDDLEWARE = True to readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ef044835e..eb1cc3355 100644 --- a/README.md +++ b/README.md @@ -231,9 +231,11 @@ def test_callback(call): A middleware handler is a function that allows you to modify requests or the bot context as they pass through the Telegram to the bot. You can imagine middleware as a chain of logic connection handled before any other handlers are -executed. +executed. Middleware processing is disabled by default, enable it by setting `apihelper.ENABLE_MIDDLEWARE = True`. ```python +apihelper.ENABLE_MIDDLEWARE = True + @bot.middleware_handler(update_types=['message']) def modify_message(bot_instance, message): # modifying the message before it reaches any other handler From f93916372e22a015e0d5b1f25d26f9114f6d2c3b Mon Sep 17 00:00:00 2001 From: Robin Modisch Date: Sun, 17 Jan 2021 23:34:54 +0100 Subject: [PATCH 0540/1808] document edit_message_text in readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index eb1cc3355..57d49e48e 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,9 @@ updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout): # sendMessage tb.send_message(chat_id, text) +# editMessageText +tb.send_message(new_text, chat_id, message_id) + # forwardMessage tb.forward_message(to_chat_id, from_chat_id, message_id) From 6d7116d521df48c86b4c40d0383d018d8efb5db2 Mon Sep 17 00:00:00 2001 From: Robin Modisch Date: Sun, 17 Jan 2021 23:45:23 +0100 Subject: [PATCH 0541/1808] document SESSION_TIME_TO_LIVE --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 57d49e48e..975093c8a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ * [F.A.Q.](#faq) * [Bot 2.0](#bot-20) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) + * [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors) * [The Telegram Chat Group](#the-telegram-chat-group) * [More examples](#more-examples) * [Bots using this API](#bots-using-this-api) @@ -603,6 +604,10 @@ if message.chat.type == "channel": ``` +### How can I handle reocurring ConnectionResetErrors? + +Bot instances that were idle for a long time might be rejected by the server when sending a message due to a timeout of the last used session. Add `apihelper.SESSION_TIME_TO_LIVE = 5 * 60` to your initialisation to force recreation after 5 minutes without any activity. + ## The Telegram Chat Group Get help. Discuss. Chat. From cd31c8db5c9171515c0f22ab89a1a35e72ef615f Mon Sep 17 00:00:00 2001 From: Robin Modisch Date: Sun, 17 Jan 2021 23:47:03 +0100 Subject: [PATCH 0542/1808] fix example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 975093c8a..711e8a3f3 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout): tb.send_message(chat_id, text) # editMessageText -tb.send_message(new_text, chat_id, message_id) +tb.edit_message_text(new_text, chat_id, message_id) # forwardMessage tb.forward_message(to_chat_id, from_chat_id, message_id) From fdf2838669309d9fafb72c122a2705bd2f7a82aa Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 19 Jan 2021 01:27:39 +0300 Subject: [PATCH 0543/1808] Minor update to copyMessage --- telebot/__init__.py | 8 ++++---- telebot/apihelper.py | 21 +++++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 80f8c3538..5396ee7a2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -753,8 +753,8 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, - disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None, - timeout=None): + disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, + reply_markup=None, timeout=None): """ Use this method to copy messages of any kind. :param chat_id: which chat to forward @@ -772,8 +772,8 @@ def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mo """ return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, - reply_to_message_id, allow_sending_without_reply, reply_markup, - disable_notification, timeout)) + disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, + timeout)) def delete_message(self, chat_id, message_id, timeout=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 47255019f..140479f38 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -346,24 +346,24 @@ def forward_message( def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, - reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None, - disable_notification=None, timeout=None): + disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, + reply_markup=None, timeout=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: payload['caption'] = caption - if parse_mode is not None: + if parse_mode: payload['parse_mode'] = parse_mode if caption_entities is not None: payload['caption_entities'] = _convert_entites(caption_entities) - if reply_to_message_id is not None: + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply - if disable_notification is not None: - payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -1337,9 +1337,14 @@ def _convert_markup(markup): def _convert_entites(entites): - if isinstance(entites[0], types.JsonSerializable): + if entites is None: + return None + elif len(entites) == 0: + return [] + elif isinstance(entites[0], types.JsonSerializable): return [entity.to_json() for entity in entites] - return entites + else: + return entites def convert_input_media(media): From a7d1dbf0e9b7675aeabad198d84c4d68aff06b24 Mon Sep 17 00:00:00 2001 From: Robin Modisch Date: Wed, 20 Jan 2021 11:04:55 +0100 Subject: [PATCH 0544/1808] add MineGramBot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 711e8a3f3..7f9125118 100644 --- a/README.md +++ b/README.md @@ -681,5 +681,6 @@ Get help. Discuss. Chat. * [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani) * [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user. * [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources. +* [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server. Want to have your bot listed here? Just make a pull requet. From 20d0ab229f2e335509231011510c9fbb4ad4bfbc Mon Sep 17 00:00:00 2001 From: dexpiper <58036191+dexpiper@users.noreply.github.com> Date: Thu, 21 Jan 2021 17:19:54 +0300 Subject: [PATCH 0545/1808] README list of bots update Add another bot to the list below --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7f9125118..1ce71504a 100644 --- a/README.md +++ b/README.md @@ -682,5 +682,6 @@ Get help. Discuss. Chat. * [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user. * [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources. * [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server. +* [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls. Want to have your bot listed here? Just make a pull requet. From 8790f26e682ad2702e1bd65ca7833e42b7c6c34c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 30 Jan 2021 14:41:19 +0300 Subject: [PATCH 0546/1808] Custom logging level for infinity_polling --- telebot/__init__.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5396ee7a2..2a0c6cde1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -455,17 +455,28 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout=20, long_polling_timeout=20, *args, **kwargs): + def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=logging.ERROR, *args, **kwargs): + """ + Wrap polling with infinite loop and exception handling to avoid bot stops polling. + + :param timeout: Request connection timeout + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param logger_level: Custom logging level for infinity_polling logging. None/NOTSET = no error logging + """ while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) except Exception as e: - logger.error("Infinity polling exception: %s", str(e)) - logger.debug("Exception traceback:\n%s", traceback.format_exc()) + if logger_level and logger_level >= logging.ERROR: + logger.error("Infinity polling exception: %s", str(e)) + if logger_level and logger_level >= logging.DEBUG: + logger.error("Exception traceback:\n%s", traceback.format_exc()) time.sleep(3) continue - logger.info("Infinity polling: polling exited") - logger.info("Break infinity polling") + if logger_level and logger_level >= logging.INFO: + logger.error("Infinity polling: polling exited") + if logger_level and logger_level >= logging.INFO: + logger.error("Break infinity polling") def polling(self, none_stop=False, interval=0, timeout=20, long_polling_timeout=20): """ @@ -475,10 +486,10 @@ def polling(self, none_stop=False, interval=0, timeout=20, long_polling_timeout= Warning: Do not call this function more than once! Always get updates. - :param interval: + :param interval: Delay between two update retrivals :param none_stop: Do not stop polling when an ApiException occurs. - :param timeout: Integer. Request connection timeout - :param long_polling_timeout. Timeout in seconds for long polling. + :param timeout: Request connection timeout + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :return: """ if self.threaded: From b39244f827eed7b57992a3671b05d3c32e58c934 Mon Sep 17 00:00:00 2001 From: Mohammad Arab Anvari Date: Tue, 16 Feb 2021 13:41:56 +0330 Subject: [PATCH 0547/1808] Add my bot to Readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ce71504a..4d643aa1e 100644 --- a/README.md +++ b/README.md @@ -683,5 +683,5 @@ Get help. Discuss. Chat. * [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources. * [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server. * [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls. - +* [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calnedar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) Want to have your bot listed here? Just make a pull requet. From 07e93f95a12f2cd97760152d1869576709affa02 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Tue, 9 Mar 2021 02:05:44 +0300 Subject: [PATCH 0548/1808] Removed my bot from list The link is now dead, and so is that bot --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4d643aa1e..4fe7ece6a 100644 --- a/README.md +++ b/README.md @@ -627,7 +627,6 @@ Get help. Discuss. Chat. * [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files. * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you. * [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander. -* [Telegram Proxy Bot](https://bitbucket.org/master_groosha/telegram-proxy-bot) by *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as some kind of "proxy". * [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`. * [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot. * [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* From f23059d7ec9febd9d66d2891b9484ad529a75cda Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo <66796758+barbax7@users.noreply.github.com> Date: Thu, 18 Mar 2021 08:38:47 +0100 Subject: [PATCH 0549/1808] Added Price Tracker bot --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fe7ece6a..51a7a7ffa 100644 --- a/README.md +++ b/README.md @@ -682,5 +682,6 @@ Get help. Discuss. Chat. * [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources. * [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server. * [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls. -* [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calnedar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) +* [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) +* [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. Want to have your bot listed here? Just make a pull requet. From 96e0be89420608438d1ad2b5827ce8ffe673ced7 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 28 Mar 2021 11:54:46 +0300 Subject: [PATCH 0550/1808] Heroku example update --- examples/webhook_examples/webhook_flask_heroku_echo.py | 4 +++- telebot/__init__.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/webhook_examples/webhook_flask_heroku_echo.py b/examples/webhook_examples/webhook_flask_heroku_echo.py index 7bbf2bf7c..a465abc1d 100644 --- a/examples/webhook_examples/webhook_flask_heroku_echo.py +++ b/examples/webhook_examples/webhook_flask_heroku_echo.py @@ -21,7 +21,9 @@ def echo_message(message): @server.route('/' + TOKEN, methods=['POST']) def getMessage(): - bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))]) + json_string = request.get_data().decode('utf-8') + update = telebot.types.Update.de_json(json_string) + bot.process_new_updates([update]) return "!", 200 diff --git a/telebot/__init__.py b/telebot/__init__.py index 2a0c6cde1..7fcffc9de 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -461,7 +461,7 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log :param timeout: Request connection timeout :param long_polling_timeout: Timeout in seconds for long polling (see API docs) - :param logger_level: Custom logging level for infinity_polling logging. None/NOTSET = no error logging + :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging """ while not self.__stop_polling.is_set(): try: From 209d9b27b4396ead58f3bc447efae461b358f126 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 28 Mar 2021 11:57:05 +0300 Subject: [PATCH 0551/1808] Minor release --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index e3cfe4c6e..182ec1e22 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__ = '3.7.6' +__version__ = '3.7.7' From 6c90da793ec9e8b1021a0500a3598ee30009afae Mon Sep 17 00:00:00 2001 From: David256 Date: Thu, 1 Apr 2021 14:56:08 -0500 Subject: [PATCH 0552/1808] New property full_name --- telebot/types.py | 7 +++++++ tests/test_types.py | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 94316c05f..21c43aa1d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -179,6 +179,13 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.can_read_all_group_messages = can_read_all_group_messages self.supports_inline_queries = supports_inline_queries + @property + def full_name(self): + full_name = self.first_name + if self.last_name: + full_name += f' {self.last_name}' + return full_name + def to_json(self): return json.dumps(self.to_dict()) diff --git a/tests/test_types.py b/tests/test_types.py index 173cda95a..355f6909f 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -6,9 +6,10 @@ def test_json_user(): - jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot","is_bot":true}' + jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","last_name":")))","username":"rdss_bot","is_bot":true}' u = types.User.de_json(jsonstring) assert u.id == 101176298 + assert u.full_name == 'RDSSBOT )))' def test_json_message(): From 2f69917a8221ff0feb4baf84577dc814711c5d44 Mon Sep 17 00:00:00 2001 From: David256 Date: Thu, 1 Apr 2021 16:52:12 -0500 Subject: [PATCH 0553/1808] Change fstrings to string formatting --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 21c43aa1d..9cad69142 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -183,7 +183,7 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua def full_name(self): full_name = self.first_name if self.last_name: - full_name += f' {self.last_name}' + full_name += ' {0}'.format(self.last_name) return full_name def to_json(self): From a39fb1472685285af6389df9a41b44a3ef137be9 Mon Sep 17 00:00:00 2001 From: FosterToster Date: Sun, 18 Apr 2021 19:56:52 +0700 Subject: [PATCH 0554/1808] middleware handlers exception handling --- telebot/__init__.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7fcffc9de..597d486b4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -339,13 +339,19 @@ def process_new_updates(self, updates): new_pre_checkout_queries = None new_polls = None new_poll_answers = None - + for update in updates: if apihelper.ENABLE_MIDDLEWARE: - self.process_middlewares(update) - + try: + self.process_middlewares(update) + except Exception as e: + logger.error(str(e)) + update.middleware_error = e # for future handling if it needed + if update.update_id > self.last_update_id: self.last_update_id = update.update_id + if hasattr(update, 'middleware_error'): + continue if update.message: if new_messages is None: new_messages = [] new_messages.append(update.message) @@ -443,11 +449,19 @@ def process_middlewares(self, update): for update_type, middlewares in self.typed_middleware_handlers.items(): if getattr(update, update_type) is not None: for typed_middleware_handler in middlewares: - typed_middleware_handler(self, getattr(update, update_type)) + try: + typed_middleware_handler(self, getattr(update, update_type)) + except Exception as e: + e.args = (f'Typed middleware handler "{typed_middleware_handler.__qualname__}" raised exception: {str(e)}',) + raise if len(self.default_middleware_handlers) > 0: for default_middleware_handler in self.default_middleware_handlers: - default_middleware_handler(self, update) + try: + default_middleware_handler(self, update) + except Exception as e: + e.args = (f'Default middleware handler "{typed_middleware_handler.__qualname__}" raised exception: {str(e)}',) + raise def __notify_update(self, new_messages): if len(self.update_listener) == 0: From 042d8c17da814b2a5706f6f1d55474a1c8668178 Mon Sep 17 00:00:00 2001 From: FosterToster Date: Sun, 18 Apr 2021 22:31:24 +0700 Subject: [PATCH 0555/1808] suppress_middleware_excepions configuration. False by default. --- telebot/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 597d486b4..46c201aa7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -95,7 +95,8 @@ 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 + next_step_backend=None, reply_backend=None, exception_handler=None, last_update_id=0, + suppress_middleware_excepions=False ): """ :param token: bot API token @@ -107,6 +108,7 @@ def __init__( self.parse_mode = parse_mode self.update_listener = [] self.skip_pending = skip_pending + self.suppress_middleware_excepions = suppress_middleware_excepions self.__stop_polling = threading.Event() self.last_update_id = last_update_id @@ -346,6 +348,8 @@ def process_new_updates(self, updates): self.process_middlewares(update) except Exception as e: logger.error(str(e)) + if not self.suppress_middleware_excepions: + raise update.middleware_error = e # for future handling if it needed if update.update_id > self.last_update_id: From 855b838e912e87279b8fe845e70648f800dc6e81 Mon Sep 17 00:00:00 2001 From: FosterToster Date: Sun, 18 Apr 2021 22:41:28 +0700 Subject: [PATCH 0556/1808] more explict process_middleware exceptions suppressing --- telebot/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 46c201aa7..8638d941c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -350,12 +350,12 @@ def process_new_updates(self, updates): logger.error(str(e)) if not self.suppress_middleware_excepions: raise - update.middleware_error = e # for future handling if it needed + else: + if update.update_id > self.last_update_id: self.last_update_id = update.update_id + continue if update.update_id > self.last_update_id: self.last_update_id = update.update_id - if hasattr(update, 'middleware_error'): - continue if update.message: if new_messages is None: new_messages = [] new_messages.append(update.message) From 2565094897bada8bd2ae2f43cc322a0b8e37a250 Mon Sep 17 00:00:00 2001 From: FosterToster Date: Mon, 19 Apr 2021 22:20:42 +0700 Subject: [PATCH 0557/1808] fixed overwriting exception args --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8638d941c..64d283e7c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -456,7 +456,7 @@ def process_middlewares(self, update): try: typed_middleware_handler(self, getattr(update, update_type)) except Exception as e: - e.args = (f'Typed middleware handler "{typed_middleware_handler.__qualname__}" raised exception: {str(e)}',) + e.args = e.args + (f'Typed middleware handler "{typed_middleware_handler.__qualname__}"',) raise if len(self.default_middleware_handlers) > 0: @@ -464,7 +464,7 @@ def process_middlewares(self, update): try: default_middleware_handler(self, update) except Exception as e: - e.args = (f'Default middleware handler "{typed_middleware_handler.__qualname__}" raised exception: {str(e)}',) + e.args = e.args + (f'Default middleware handler "{default_middleware_handler.__qualname__}"',) raise def __notify_update(self, new_messages): From 990bb827bed0603f42cef6f08698673c7a6c3644 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 19 Apr 2021 18:45:49 +0300 Subject: [PATCH 0558/1808] Python 3.5 end-of-life --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eaed67e02..2f6ddfbd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "3.5" - "3.6" - "3.7" - "3.8" From b7a18bf0d95b8feda3b8799ab2ef0b904483c8ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Apr 2021 17:03:50 +0000 Subject: [PATCH 0559/1808] Bump py from 1.4.29 to 1.10.0 Bumps [py](https://github.com/pytest-dev/py) from 1.4.29 to 1.10.0. - [Release notes](https://github.com/pytest-dev/py/releases) - [Changelog](https://github.com/pytest-dev/py/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/py/compare/1.4.29...1.10.0) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a38fc0933..cf9236360 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -py==1.4.29 +py==1.10.0 pytest==3.0.2 requests==2.20.0 wheel==0.24.0 From 4a256750070309ff6aca0e50a5df7ac1131bf990 Mon Sep 17 00:00:00 2001 From: Hemanta Pokharel Date: Tue, 11 May 2021 07:51:13 +0545 Subject: [PATCH 0560/1808] Add Torrent Hunt bot in README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 51a7a7ffa..cbeb2007e 100644 --- a/README.md +++ b/README.md @@ -684,4 +684,6 @@ Get help. Discuss. Chat. * [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls. * [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) * [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. -Want to have your bot listed here? Just make a pull requet. +* [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. + +**Want to have your bot listed here? Just make a pull requet.** From 47cab4d63ee52cd8bd398594a7c9651107fbd731 Mon Sep 17 00:00:00 2001 From: Hemanta Pokharel Date: Tue, 11 May 2021 07:58:19 +0545 Subject: [PATCH 0561/1808] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cbeb2007e..f8fce3170 100644 --- a/README.md +++ b/README.md @@ -686,4 +686,4 @@ Get help. Discuss. Chat. * [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. * [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. -**Want to have your bot listed here? Just make a pull requet.** +**Want to have your bot listed here? Just make a pull request.** From 73fb18c193691f87b784bf280e8b4bc9e5e71952 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 11 May 2021 23:26:22 +0300 Subject: [PATCH 0562/1808] Change message handler filtering order Now content_type is checked first. --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7fcffc9de..f0b8da97f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1880,7 +1880,7 @@ def default_command(message): :param commands: Optional list of strings (commands to handle). :param regexp: Optional regular expression. :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. - :param content_types: This commands' supported content types. Must be a list. Defaults to ['text']. + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. """ if content_types is None: @@ -1888,10 +1888,10 @@ def default_command(message): def decorator(handler): handler_dict = self._build_handler_dict(handler, + content_types=content_types, commands=commands, regexp=regexp, func=func, - content_types=content_types, **kwargs) self.add_message_handler(handler_dict) return handler From 53c98328c1790b5cede6e36d76757652c7fef9d3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 12 May 2021 00:26:33 +0300 Subject: [PATCH 0563/1808] send_poll fix with PollOptions Now send_poll correctly operates with PollOptions passed as array of PollOption. --- telebot/apihelper.py | 16 +++++++++++++++- telebot/types.py | 11 ++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 140479f38..4cbf67039 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1277,7 +1277,7 @@ def send_poll( payload = { 'chat_id': str(chat_id), 'question': question, - 'options': json.dumps(options)} + 'options': json.dumps(_convert_poll_options(options))} if is_anonymous is not None: payload['is_anonymous'] = is_anonymous @@ -1347,6 +1347,20 @@ def _convert_entites(entites): return entites +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.JsonSerializable): + 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/types.py b/telebot/types.py index 9cad69142..5e3b33393 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2527,7 +2527,8 @@ def to_dict(self): return ret -class PollOption(JsonSerializable, JsonDeserializable): +class PollOption(JsonDeserializable): +#class PollOption(JsonSerializable, JsonDeserializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2539,10 +2540,10 @@ def de_json(cls, json_string): def __init__(self, text, voter_count = 0): self.text = text self.voter_count = voter_count - - def to_json(self): - # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll - return json.dumps(self.text) + # Converted in _convert_poll_options + # def to_json(self): + # # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll + # return json.dumps(self.text) class Poll(JsonDeserializable): From 90de2e4ad973da5ca553073c1108a395ea5f42c2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 15 May 2021 11:35:13 +0300 Subject: [PATCH 0564/1808] Release 3.7.8 Regular release with minor updates --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 182ec1e22..c5b41b7c2 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__ = '3.7.7' +__version__ = '3.7.8' From 7540a26fb944c6485def1bc35ef6f249e94c04ee Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 15 May 2021 20:08:51 +0300 Subject: [PATCH 0565/1808] send_poll fix of fix Previous update was inconsistent, sorry. --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4cbf67039..582e25ba9 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1355,7 +1355,7 @@ def _convert_poll_options(poll_options): 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.JsonSerializable): + elif isinstance(poll_options[0], types.PollOption): return [option.text for option in poll_options] else: return poll_options From 26e5f3d3a88e2e5d8e1d81958bb196a0acf194c9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 15 May 2021 20:27:52 +0300 Subject: [PATCH 0566/1808] Fix release 3.7.8u1 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index c5b41b7c2..0266440b8 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__ = '3.7.8' +__version__ = '3.7.8u1' From 59559199d5a13e7d350401f4b79bf90affa7f736 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 15 May 2021 20:29:58 +0300 Subject: [PATCH 0567/1808] Update version.py --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 0266440b8..54f9f841a 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__ = '3.7.8u1' +__version__ = '3.7.9' From ff54f194ad29301aae95cb2a163607fab48e77a9 Mon Sep 17 00:00:00 2001 From: Yaroslav Vorobev Date: Wed, 19 May 2021 02:24:07 +0300 Subject: [PATCH 0568/1808] Added: create_chat_invite_link, edit_chat_invite_link, revoke_chat_invite_link methods --- telebot/__init__.py | 40 ++++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8844546ac..4b2116dfe 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1246,6 +1246,46 @@ def set_chat_permissions(self, chat_id, permissions): """ return apihelper.set_chat_permissions(self.token, chat_id, permissions) + def create_chat_invite_link(self, chat_id, expire_date=None, member_limit=None): + """ + Use this method to create an additional invite link for a chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :expire_date: Point in time (Unix timestamp) when the link will expire + :member_limit: Maximum number of users that can be members of the chat simultaneously + :return: + """ + return apihelper.create_chat_invite_link(self.token, chat_id, expire_date, member_limit) + + def edit_chat_invite_link(self, chat_id, invite_link, expire_date=None, member_limit=None): + """ + Use this method to edit a non-primary invite link created by the bot. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :invite_link: The invite link to edit + :expire_date: Point in time (Unix timestamp) when the link will expire + :member_limit: Maximum number of users that can be members of the chat simultaneously + :return: + """ + return apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, expire_date, member_limit) + + def revoke_chat_invite_link(self, chat_id, invite_link): + """ + Use this method to revoke an invite link created by the bot. + Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :invite_link: The invite link to revoke + :return: + """ + return apihelper.revoke_chat_invite_link(self.token, chat_id, invite_link) + def export_chat_invite_link(self, chat_id): """ Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 582e25ba9..a2f04b29e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -843,6 +843,50 @@ def set_chat_permissions(token, chat_id, permissions): return _make_request(token, method_url, params=payload, method='post') +def create_chat_invite_link(token, chat_id, expire_date, member_limit): + method_url = 'createChatInviteLink' + payload = { + 'chat_id': chat_id + } + + if expire_date is not None: + payload['expire_date'] = expire_date + if isinstance(payload['expire_date'], datetime): + payload['expire_date'] = payload['expire_date'].timestamp() + + if member_limit is not None: + payload['member_limit'] = member_limit + + return _make_request(token, method_url, params=payload, method='post') + + +def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit): + method_url = 'editChatInviteLink' + payload = { + 'chat_id': chat_id, + 'invite_link': invite_link + } + + if expire_date is not None: + payload['expire_date'] = expire_date + if isinstance(payload['expire_date'], datetime): + payload['expire_date'] = payload['expire_date'].timestamp() + + if member_limit is not None: + payload['member_limit'] = member_limit + + return _make_request(token, method_url, params=payload, method='post') + + +def revoke_chat_invite_link(token, chat_id, invite_link): + method_url = 'revokeChatInviteLink' + payload = { + 'chat_id': chat_id, + 'invite_link': invite_link + } + return _make_request(token, method_url, params=payload, method='post') + + def export_chat_invite_link(token, chat_id): method_url = 'exportChatInviteLink' payload = {'chat_id': chat_id} From e22fcbe3c0d173c81c200c4e41602beb99195823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=88=ED=99=98=20Ye-Hwan=20Kim=20=28Sam=29?= <43813340+yehwankim23@users.noreply.github.com> Date: Thu, 20 May 2021 18:01:10 +0900 Subject: [PATCH 0569/1808] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8fce3170..e23d36b73 100644 --- a/README.md +++ b/README.md @@ -455,7 +455,7 @@ Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra deta ## Advanced use of the API ### Asynchronous delivery of messages -There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up you bot __significantly__, but it has unwanted side effects if used without caution. +There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up your bot __significantly__, but it has unwanted side effects if used without caution. To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot. ```python tb = telebot.AsyncTeleBot("TOKEN") From 1209281787c1073b24e48db8b526e565b3201c7b Mon Sep 17 00:00:00 2001 From: zora89 <42845567+zora89@users.noreply.github.com> Date: Wed, 26 May 2021 02:17:49 +0530 Subject: [PATCH 0570/1808] type corrected. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e23d36b73..df11c6430 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Outlined below are some general use cases of the API. #### Message handlers A message handler is a function that is decorated with the `message_handler` decorator of a TeleBot instance. Message handlers consist of one or multiple filters. -Each filter much return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot): +Each filter must return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot): ```python @bot.message_handler(filters) def function_name(message): From aea067f78946df8be3856a5af10211e02c630138 Mon Sep 17 00:00:00 2001 From: anvar Date: Tue, 1 Jun 2021 08:39:09 +0500 Subject: [PATCH 0571/1808] Bug fixed on set_game_score fixed wrong ordered argument error on calling apihelper.set_game_score method in set_game_score --- telebot/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4b2116dfe..b1c1093ed 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1465,7 +1465,7 @@ def send_game( return types.Message.de_json(result) def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None, - edit_message=None): + disable_edit_message=None): """ Sets the value of points in the game to a specific user :param user_id: @@ -1474,11 +1474,11 @@ def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=No :param chat_id: :param message_id: :param inline_message_id: - :param edit_message: + :param disable_edit_message: :return: """ - result = apihelper.set_game_score(self.token, user_id, score, force, chat_id, message_id, inline_message_id, - edit_message) + result = apihelper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, message_id, + inline_message_id) if type(result) == bool: return result return types.Message.de_json(result) From 20030f47af3154daf99599854242f2eadeb67754 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:51:32 +0200 Subject: [PATCH 0572/1808] Update util.py Added the function `smart_split` to split text into meaningful parts. --- telebot/util.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index 1226ecc5c..bb19831ad 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -17,6 +17,8 @@ except: pil_imported = False +MAX_MESSAGE_LENGTH = 4096 + logger = logging.getLogger('TeleBot') thread_local = threading.local() @@ -223,6 +225,38 @@ def split_string(text, chars_per_string): """ return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)] + +def smart_split(text, chars_per_string=MAX_MESSAGE_LENGTH): + f""" + Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. + This is very useful for splitting one giant message into multiples. + If `chars_per_string` > {MAX_MESSAGE_LENGTH}: `chars_per_string` = {MAX_MESSAGE_LENGTH}. + Splits by '\n', '. ' or ' ' in exactly this priority. + + :param text: The text to split + :param chars_per_string: The number of maximum characters per part the text is split to. + :return: The splitted text as a list of strings. + """ + def _text_before_last(substr): + return substr.join(part.split(substr)[:-1]) + substr + + if chars_per_string > MAX_MESSAGE_LENGTH: chars_per_string = MAX_MESSAGE_LENGTH + + parts = [] + while True: + if len(text) < chars_per_string: + parts.append(text) + return parts + + part = text[:chars_per_string] + + if ("\n" in part): part = _text_before_last("\n") + elif (". " in part): part = _text_before_last(". ") + elif (" " in part): part = _text_before_last(" ") + + parts.append(part) + text = text[len(part):] + # CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352 def or_set(self): self._set() From 9a6ddce8dfdb43c32207ca565ed1b4c42ef689f2 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Thu, 3 Jun 2021 19:06:53 +0200 Subject: [PATCH 0573/1808] Added the function `unix_time` --- telebot/util.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index bb19831ad..ea39b81bb 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -6,6 +6,7 @@ import traceback import warnings import functools +import time import queue as Queue import logging @@ -214,6 +215,25 @@ def extract_command(text): return text.split()[0].split('@')[0][1:] if is_command(text) else None +def unix_time(seconds=0, minutes=0, hours=0, days=0): + """ + Returns UNIX time + given paramenters + This is useful to restrict or kick a chat member. Just use it as parameter `until_date`. + + Examples: + bot.kick_chat_member(chat_id, user_id, until_date=unix_time(days=1)): bans a chat member for 1 day + bot.kick_chat_member(chat_id, user_id, until_date=unix_time(seconds=45)): bans a chat member for 45 seconds + + :param seconds: how many seconds from now + :param minutes: how many minutes from now + :param hours: how many hours from now + :param days: how many days from now + :return: UNIX time + """ + t = seconds + (60 * minutes) + (3600 * hours) + (86400 * days) + return (int(t + time.time())) + + def split_string(text, chars_per_string): """ Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. From ed5e5e507728be25d08083773d928b59aee867ad Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Thu, 3 Jun 2021 19:51:33 +0200 Subject: [PATCH 0574/1808] Update util.py - Removed function `unix_time` - Added function `escape` - Added function `user_link` - Added function `quick_markup` - Added some type hints --- telebot/util.py | 121 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 36 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index ea39b81bb..842a48da1 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -6,10 +6,11 @@ import traceback import warnings import functools -import time +from typing import Any, List, Dict import queue as Queue import logging +from telebot import types try: from PIL import Image @@ -187,7 +188,7 @@ def pil_image_to_file(image, extension='JPEG', quality='web_low'): else: raise RuntimeError('PIL module is not imported') -def is_command(text): +def is_command(text: str) -> bool: """ Checks if `text` is a command. Telegram chat commands start with the '/' character. :param text: Text to check. @@ -197,7 +198,7 @@ def is_command(text): return text.startswith('/') -def extract_command(text): +def extract_command(text: str) -> str: """ Extracts the command from `text` (minus the '/') if `text` is a command (see is_command). If `text` is not a command, this function returns None. @@ -215,26 +216,24 @@ def extract_command(text): return text.split()[0].split('@')[0][1:] if is_command(text) else None -def unix_time(seconds=0, minutes=0, hours=0, days=0): +def extract_arguments(text: str) -> str: """ - Returns UNIX time + given paramenters - This is useful to restrict or kick a chat member. Just use it as parameter `until_date`. - - Examples: - bot.kick_chat_member(chat_id, user_id, until_date=unix_time(days=1)): bans a chat member for 1 day - bot.kick_chat_member(chat_id, user_id, until_date=unix_time(seconds=45)): bans a chat member for 45 seconds + Returns the argument after the command. + + Examples: + extract_arguments("/get name"): 'name' + extract_arguments("/get"): '' + extract_arguments("/get@botName name"): 'name' - :param seconds: how many seconds from now - :param minutes: how many minutes from now - :param hours: how many hours from now - :param days: how many days from now - :return: UNIX time + :param text: String to extract the arguments from a command + :return: the arguments if `text` is a command (according to is_command), else None. """ - t = seconds + (60 * minutes) + (3600 * hours) + (86400 * days) - return (int(t + time.time())) + regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) + result = regexp.match(text) + return result.group(2) if is_command(text) else None -def split_string(text, chars_per_string): +def split_string(text: str, chars_per_string: int) -> List[str]: """ Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. This is very useful for splitting one giant message into multiples. @@ -246,7 +245,7 @@ def split_string(text, chars_per_string): return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)] -def smart_split(text, chars_per_string=MAX_MESSAGE_LENGTH): +def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]: f""" Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. This is very useful for splitting one giant message into multiples. @@ -257,7 +256,7 @@ def smart_split(text, chars_per_string=MAX_MESSAGE_LENGTH): :param chars_per_string: The number of maximum characters per part the text is split to. :return: The splitted text as a list of strings. """ - def _text_before_last(substr): + def _text_before_last(substr: str) -> str: return substr.join(part.split(substr)[:-1]) + substr if chars_per_string > MAX_MESSAGE_LENGTH: chars_per_string = MAX_MESSAGE_LENGTH @@ -277,6 +276,72 @@ def _text_before_last(substr): parts.append(part) text = text[len(part):] + +def escape(text: str) -> str: + """ + Replaces the following chars in `text` ('&' with '&', '<' with '<' and '>' with '>'). + + :param text: the text to escape + :return: the escaped text + """ + chars = {"&": "&", "<": "<", ">": ">"} + for old, new in chars.items(): text = text.replace(old, new) + return text + + +def user_link(user: types.User, include_id: bool=False) -> str: + """ + Returns an HTML user link. This is useful for reports. + Attention: Don't forget to set parse_mode to 'HTML'! + + Example: + bot.send_message(your_user_id, user_link(message.from_user) + ' startet the bot!', parse_mode='HTML') + + :param user: the user (not the user_id) + :param include_id: include the user_id + :return: HTML user link + """ + name = escape(user.first_name) + return (f"{name}" + + f" (
{user.id}
)" if include_id else "") + + +def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup: + """ + Returns a reply markup from a dict in this format: {'text': kwargs} + This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' + + Example: + quick_markup({ + 'Twitter': {'url': 'https://twitter.com'}, + 'Facebook': {'url': 'https://facebook.com'}, + 'Back': {'callback_data': 'whatever'} + }, row_width=2): + returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook + and a back button below + + kwargs can be: + { + 'url': None, + 'callback_data': None, + 'switch_inline_query': None, + 'switch_inline_query_current_chat': None, + 'callback_game': None, + 'pay': None, + 'login_url': None + } + + :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} + :return: InlineKeyboardMarkup + """ + markup = types.InlineKeyboardMarkup(row_width=row_width) + buttons = [] + for text, kwargs in values.items(): + buttons.append(types.InlineKeyboardButton(text=text, **kwargs)) + markup.add(*buttons) + return markup + + # CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352 def or_set(self): self._set() @@ -317,22 +382,6 @@ def busy_wait(): changed() return or_event -def extract_arguments(text): - """ - Returns the argument after the command. - - Examples: - extract_arguments("/get name"): 'name' - extract_arguments("/get"): '' - extract_arguments("/get@botName name"): 'name' - - :param text: String to extract the arguments from a command - :return: the arguments if `text` is a command (according to is_command), else None. - """ - regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) - result = regexp.match(text) - return result.group(2) if is_command(text) else None - def per_thread(key, construct_value, reset=False): if reset or not hasattr(thread_local, key): From afbc67795a58e35c3e1b050400c7ae0e328ade35 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 4 Jun 2021 12:11:37 +0300 Subject: [PATCH 0575/1808] Partial rollback for previous update --- telebot/util.py | 73 +++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 842a48da1..6b41801df 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -10,7 +10,7 @@ import queue as Queue import logging -from telebot import types +# from telebot import types try: from PIL import Image @@ -289,7 +289,7 @@ def escape(text: str) -> str: return text -def user_link(user: types.User, include_id: bool=False) -> str: +def user_link(user, include_id: bool=False) -> str: """ Returns an HTML user link. This is useful for reports. Attention: Don't forget to set parse_mode to 'HTML'! @@ -306,40 +306,41 @@ def user_link(user: types.User, include_id: bool=False) -> str: + f" (
{user.id}
)" if include_id else "") -def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup: - """ - Returns a reply markup from a dict in this format: {'text': kwargs} - This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' - - Example: - quick_markup({ - 'Twitter': {'url': 'https://twitter.com'}, - 'Facebook': {'url': 'https://facebook.com'}, - 'Back': {'callback_data': 'whatever'} - }, row_width=2): - returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook - and a back button below - - kwargs can be: - { - 'url': None, - 'callback_data': None, - 'switch_inline_query': None, - 'switch_inline_query_current_chat': None, - 'callback_game': None, - 'pay': None, - 'login_url': None - } - - :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} - :return: InlineKeyboardMarkup - """ - markup = types.InlineKeyboardMarkup(row_width=row_width) - buttons = [] - for text, kwargs in values.items(): - buttons.append(types.InlineKeyboardButton(text=text, **kwargs)) - markup.add(*buttons) - return markup +# def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2): +# """ +# Returns a reply markup from a dict in this format: {'text': kwargs} +# This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' +# +# Example: +# quick_markup({ +# 'Twitter': {'url': 'https://twitter.com'}, +# 'Facebook': {'url': 'https://facebook.com'}, +# 'Back': {'callback_data': 'whatever'} +# }, row_width=2): +# returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook +# and a back button below +# +# kwargs can be: +# { +# 'url': None, +# 'callback_data': None, +# 'switch_inline_query': None, +# 'switch_inline_query_current_chat': None, +# 'callback_game': None, +# 'pay': None, +# 'login_url': None +# } +# +# :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} +# :param row_width: +# :return: InlineKeyboardMarkup +# """ +# markup = types.InlineKeyboardMarkup(row_width=row_width) +# buttons = [] +# for text, kwargs in values.items(): +# buttons.append(types.InlineKeyboardButton(text=text, **kwargs)) +# markup.add(*buttons) +# return markup # CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352 From 2add34c70261949cfcd18df5b602a1a2f6eb4da4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 4 Jun 2021 12:28:33 +0300 Subject: [PATCH 0576/1808] Fix special case when content_type is None --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b1c1093ed..f6277aed5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2254,8 +2254,8 @@ def _test_filter(message_filter, filter_value, message): """ test_cases = { 'content_types': lambda msg: msg.content_type in filter_value, - 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), - 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, + 'regexp': lambda msg: msg.content_type and msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), + 'commands': lambda msg: msg.content_type and msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, 'func': lambda msg: filter_value(msg) } From 709eb8cf452b3ea49b80945710ff1e6aa9289bdd Mon Sep 17 00:00:00 2001 From: ZurMaD Date: Sun, 6 Jun 2021 14:30:55 -0500 Subject: [PATCH 0577/1808] Github Actions: setup for 3.6+ pypy3.6+ --- .github/workflows/setup_python.yml | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/setup_python.yml diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml new file mode 100644 index 000000000..06318976e --- /dev/null +++ b/.github/workflows/setup_python.yml @@ -0,0 +1,35 @@ +# This is a basic workflow to help you get started with Actions + +name: ActionsSetupPython + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + push: + branches: [ master ] + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + #workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + all-setups: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.6', 'pypy-3.7' ] #'pypy-3.8', 'pypy-3.9' NOT SUPPORTED NOW + name: Python ${{ matrix.python-version }} setup and tests + steps: + - uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + - run: | + pip3 install -r requirements.txt + python setup.py install + cd tests && py.test From ab05cb004502b4eceea43b8a7d1e24498760e89b Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 9 Jun 2021 16:20:42 +0200 Subject: [PATCH 0578/1808] Fixed a bug in `user_link` `user_link` returned an empty string if `include_id` was set to False --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 842a48da1..10c4fe3e2 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -303,7 +303,7 @@ def user_link(user: types.User, include_id: bool=False) -> str: """ name = escape(user.first_name) return (f"{name}" - + f" (
{user.id}
)" if include_id else "") + + (f" (
{user.id}
)" if include_id else "")) def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup: From 25bac683097583312f39e035f4ae1b29feb3e06f Mon Sep 17 00:00:00 2001 From: AREEG94FAHAD Date: Sat, 12 Jun 2021 18:18:16 +0300 Subject: [PATCH 0579/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index df11c6430..9cf23f11b 100644 --- a/README.md +++ b/README.md @@ -685,5 +685,6 @@ Get help. Discuss. Chat. * [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) * [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. * [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. +* Translator bot by [Areeg Fahad](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. **Want to have your bot listed here? Just make a pull request.** From 81299ff61399e0367519657f2029d2252910dd1d Mon Sep 17 00:00:00 2001 From: AREEG94FAHAD Date: Sat, 12 Jun 2021 18:18:51 +0300 Subject: [PATCH 0580/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cf23f11b..2afe0028a 100644 --- a/README.md +++ b/README.md @@ -685,6 +685,6 @@ Get help. Discuss. Chat. * [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) * [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. * [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. -* Translator bot by [Areeg Fahad](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. +* Translator bot by [Areeg Fahad source](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. **Want to have your bot listed here? Just make a pull request.** From d5c202abbd18440ee85ec8dfbef9fa14fe1101eb Mon Sep 17 00:00:00 2001 From: AREEG94FAHAD Date: Sat, 12 Jun 2021 18:19:18 +0300 Subject: [PATCH 0581/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2afe0028a..22e11201e 100644 --- a/README.md +++ b/README.md @@ -685,6 +685,6 @@ Get help. Discuss. Chat. * [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) * [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. * [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. -* Translator bot by [Areeg Fahad source](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. +* Translator bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. **Want to have your bot listed here? Just make a pull request.** From a01e59951a866aa84c5a15f998e91af1d247c546 Mon Sep 17 00:00:00 2001 From: AREEG94FAHAD Date: Sat, 12 Jun 2021 19:01:42 +0300 Subject: [PATCH 0582/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 22e11201e..c19529adc 100644 --- a/README.md +++ b/README.md @@ -686,5 +686,7 @@ Get help. Discuss. Chat. * [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. * [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. * Translator bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. +* Digital Cryptocurrency bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/currencies_bot). With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. + **Want to have your bot listed here? Just make a pull request.** From bbafdd1c1d1c118b416ca66d09ce3cd30e7f1fed Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Thu, 17 Jun 2021 20:28:53 +0200 Subject: [PATCH 0583/1808] Some Updates > Added lot of type hints to types.py > Added some new fields from TelegramBotAPI to pyTelegramBotAPI > fixed `circular import error in util.py > Added functions `log_out` and `close` to __init__.py and apihelper.py > And some more small changes --- .gitignore | 1 + telebot/__init__.py | 212 ++++--- telebot/apihelper.py | 7 + telebot/types.py | 1420 ++++++++++++++++++++---------------------- telebot/util.py | 79 +-- 5 files changed, 841 insertions(+), 878 deletions(-) diff --git a/.gitignore b/.gitignore index dd11917ad..c9919ab0a 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ testMain.py #VS Code .vscode/ +.DS_Store diff --git a/telebot/__init__.py b/telebot/__init__.py index f6277aed5..eaf846528 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7,6 +7,7 @@ import threading import time import traceback +from typing import List, Union logger = logging.getLogger('TeleBot') formatter = logging.Formatter( @@ -55,6 +56,8 @@ class TeleBot: """ This is TeleBot Class Methods: getMe + logOut + close sendMessage forwardMessage deleteMessage @@ -76,6 +79,9 @@ class TeleBot: unbanChatMember restrictChatMember promoteChatMember + createChatInviteLink + editChatInviteLink + revokeChatInviteLink exportChatInviteLink setChatPhoto deleteChatPhoto @@ -640,20 +646,50 @@ def stop_bot(self): def set_update_listener(self, listener): self.update_listener.append(listener) - def get_me(self): + def get_me(self) -> types.User: + """ + Returns basic information about the bot in form of a User object. + """ result = apihelper.get_me(self.token) return types.User.de_json(result) - def get_file(self, file_id): + def get_file(self, file_id) -> types.File: + """ + Use this method to get basic info about a file and prepare it for downloading. + For the moment, bots can download files of up to 20MB in size. + On success, a File object is returned. + It is guaranteed that the link will be valid for at least 1 hour. + When the link expires, a new one can be requested by calling get_file again. + """ return types.File.de_json(apihelper.get_file(self.token, file_id)) - def get_file_url(self, file_id): + def get_file_url(self, file_id) -> str: return apihelper.get_file_url(self.token, file_id) - def download_file(self, file_path): + def download_file(self, file_path) -> bytes: return apihelper.download_file(self.token, file_path) - def get_user_profile_photos(self, user_id, offset=None, limit=None): + def log_out(self) -> bool: + """ + Use this method to log out from the cloud Bot API server before launching the bot locally. + You MUST log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. + After a successful call, you can immediately log in on a local server, + but will not be able to log in back to the cloud Bot API server for 10 minutes. + Returns True on success. + """ + return apihelper.log_out(self.token) + + def close(self) -> bool: + """ + Use this method to close the bot instance before moving it from one local server to another. + You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. + The method will return error 429 in the first 10 minutes after the bot is launched. + Returns True on success. + """ + return apihelper.close(self.token) + + + def get_user_profile_photos(self, user_id, offset=None, limit=None) -> types.UserProfilePhotos: """ Retrieves the user profile photos of the person with 'user_id' See https://core.telegram.org/bots/api#getuserprofilephotos @@ -665,7 +701,7 @@ def get_user_profile_photos(self, user_id, offset=None, limit=None): result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit) return types.UserProfilePhotos.de_json(result) - def get_chat(self, chat_id): + def get_chat(self, chat_id) -> types.Chat: """ 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. @@ -675,7 +711,7 @@ def get_chat(self, chat_id): result = apihelper.get_chat(self.token, chat_id) return types.Chat.de_json(result) - def leave_chat(self, chat_id): + def leave_chat(self, chat_id) -> bool: """ Use this method for your bot to leave a group, supergroup or channel. Returns True on success. :param chat_id: @@ -684,7 +720,7 @@ def leave_chat(self, chat_id): result = apihelper.leave_chat(self.token, chat_id) return result - def get_chat_administrators(self, chat_id): + def get_chat_administrators(self, chat_id) -> List[types.ChatMember]: """ Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains @@ -699,7 +735,7 @@ def get_chat_administrators(self, chat_id): ret.append(types.ChatMember.de_json(r)) return ret - def get_chat_members_count(self, chat_id): + def get_chat_members_count(self, chat_id) -> int: """ Use this method to get the number of members in a chat. Returns Int on success. :param chat_id: @@ -708,7 +744,7 @@ def get_chat_members_count(self, chat_id): result = apihelper.get_chat_members_count(self.token, chat_id) return result - def set_chat_sticker_set(self, chat_id, sticker_set_name): + def set_chat_sticker_set(self, chat_id, sticker_set_name) -> types.StickerSet: """ Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -722,7 +758,7 @@ def set_chat_sticker_set(self, chat_id, sticker_set_name): result = apihelper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) return result - def delete_chat_sticker_set(self, chat_id): + def delete_chat_sticker_set(self, chat_id) -> bool: """ Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set @@ -734,7 +770,7 @@ def delete_chat_sticker_set(self, chat_id): result = apihelper.delete_chat_sticker_set(self.token, chat_id) return result - def get_chat_member(self, chat_id, user_id): + def get_chat_member(self, chat_id, user_id) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. :param chat_id: @@ -745,7 +781,7 @@ def get_chat_member(self, chat_id, user_id): return types.ChatMember.de_json(result) def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None) -> types.Message: """ Use this method to send text messages. @@ -768,7 +804,7 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) - def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None): + def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None) -> types.Message: """ Use this method to forward messages of any kind. :param disable_notification: @@ -783,7 +819,7 @@ def forward_message(self, chat_id, from_chat_id, message_id, disable_notificatio def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None): + reply_markup=None, timeout=None) -> int: """ Use this method to copy messages of any kind. :param chat_id: which chat to forward @@ -804,7 +840,7 @@ def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mo disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, timeout)) - def delete_message(self, chat_id, message_id, timeout=None): + def delete_message(self, chat_id, message_id, timeout=None) -> bool: """ Use this method to delete message. Returns True on success. :param chat_id: in which chat to delete @@ -817,7 +853,7 @@ def delete_message(self, chat_id, message_id, timeout=None): def send_dice( self, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None): + reply_markup=None, timeout=None) -> types.Message: """ Use this method to send dices. :param chat_id: @@ -835,7 +871,7 @@ def send_dice( ) def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None) -> types.Message: """ Use this method to send photos. :param disable_notification: @@ -856,7 +892,7 @@ def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, rep def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, - timeout=None, thumb=None): + timeout=None, thumb=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. :param chat_id:Unique identifier for the message recipient @@ -880,7 +916,7 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None reply_markup, parse_mode, disable_notification, timeout, thumb)) def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. :param chat_id:Unique identifier for the message recipient. @@ -901,7 +937,7 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa parse_mode, disable_notification, timeout)) def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None, thumb=None): + parse_mode=None, disable_notification=None, timeout=None, thumb=None) -> types.Message: """ Use this method to send general files. :param chat_id: @@ -923,7 +959,7 @@ def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, re def send_sticker( self, chat_id, data, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + disable_notification=None, timeout=None) -> types.Message: """ Use this method to send .webp stickers. :param chat_id: @@ -940,7 +976,8 @@ def send_sticker( disable_notification, timeout)) def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, + width=None, height=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -967,7 +1004,7 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag def send_animation(self, chat_id, animation, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, - disable_notification=None, timeout=None, thumb=None): + disable_notification=None, timeout=None, thumb=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -990,7 +1027,7 @@ def send_animation(self, chat_id, animation, duration=None, def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None): + disable_notification=None, timeout=None, thumb=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -1010,7 +1047,7 @@ def send_video_note(self, chat_id, data, duration=None, length=None, def send_media_group( self, chat_id, media, - disable_notification=None, reply_to_message_id=None, timeout=None): + disable_notification=None, reply_to_message_id=None, timeout=None) -> List[types.Message]: """ send a group of photos or videos as an album. On success, an array of the sent Messages is returned. :param chat_id: @@ -1029,7 +1066,7 @@ def send_media_group( def send_location( self, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, - reply_markup=None, disable_notification=None, timeout=None): + reply_markup=None, disable_notification=None, timeout=None) -> types.Message: """ Use this method to send point on the map. :param chat_id: @@ -1048,7 +1085,7 @@ def send_location( reply_markup, disable_notification, timeout)) def edit_message_live_location(self, latitude, longitude, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None): + inline_message_id=None, reply_markup=None, timeout=None) -> types.Message: """ Use this method to edit live location :param latitude: @@ -1067,7 +1104,7 @@ def edit_message_live_location(self, latitude, longitude, chat_id=None, message_ def stop_message_live_location( self, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None): + inline_message_id=None, reply_markup=None, timeout=None) -> types.Message: """ Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires @@ -1084,7 +1121,7 @@ def stop_message_live_location( def send_venue( self, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: """ Use this method to send information about a venue. :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel @@ -1108,14 +1145,14 @@ def send_venue( def send_contact( self, chat_id, phone_number, first_name, last_name=None, vcard=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: return types.Message.de_json( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_to_message_id, reply_markup, timeout) ) - def send_chat_action(self, chat_id, action, timeout=None): + def send_chat_action(self, chat_id, action, timeout=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear @@ -1128,7 +1165,7 @@ def send_chat_action(self, chat_id, action, timeout=None): """ return apihelper.send_chat_action(self.token, chat_id, action, timeout) - def kick_chat_member(self, chat_id, user_id, until_date=None): + def kick_chat_member(self, chat_id, user_id, until_date=None) -> bool: """ Use this method to kick a user from a group or a supergroup. :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup @@ -1139,7 +1176,7 @@ def kick_chat_member(self, chat_id, user_id, until_date=None): """ return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date) - def unban_chat_member(self, chat_id, user_id, only_if_banned = False): + def unban_chat_member(self, chat_id, user_id, only_if_banned = False) -> bool: """ Use this method to unban a previously kicked user in a supergroup or channel. The user will not return to the group or channel automatically, but will be able to join via link, etc. @@ -1159,7 +1196,7 @@ def restrict_chat_member( can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None): + can_invite_users=None, can_pin_messages=None) -> bool: """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -1194,7 +1231,7 @@ def restrict_chat_member( def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, - can_restrict_members=None, can_pin_messages=None, can_promote_members=None): + can_restrict_members=None, can_pin_messages=None, can_promote_members=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1219,7 +1256,7 @@ def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_m can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members) - def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title): + def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title) -> bool: """ Use this method to set a custom title for an administrator in a supergroup promoted by the bot. @@ -1233,7 +1270,7 @@ def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title): """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - def set_chat_permissions(self, chat_id, permissions): + def set_chat_permissions(self, chat_id, permissions) -> bool: """ Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work @@ -1246,7 +1283,7 @@ def set_chat_permissions(self, chat_id, permissions): """ return apihelper.set_chat_permissions(self.token, chat_id, permissions) - def create_chat_invite_link(self, chat_id, expire_date=None, member_limit=None): + def create_chat_invite_link(self, chat_id, expire_date=None, member_limit=None) -> types.ChatInviteLink: """ Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1257,7 +1294,9 @@ def create_chat_invite_link(self, chat_id, expire_date=None, member_limit=None): :member_limit: Maximum number of users that can be members of the chat simultaneously :return: """ - return apihelper.create_chat_invite_link(self.token, chat_id, expire_date, member_limit) + return types.ChatInviteLink.de_json( + apihelper.create_chat_invite_link(self.token, chat_id, expire_date, member_limit) + ) def edit_chat_invite_link(self, chat_id, invite_link, expire_date=None, member_limit=None): """ @@ -1271,9 +1310,11 @@ def edit_chat_invite_link(self, chat_id, invite_link, expire_date=None, member_l :member_limit: Maximum number of users that can be members of the chat simultaneously :return: """ - return apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, expire_date, member_limit) + return types.ChatInviteLink.de_json( + apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, expire_date, member_limit) + ) - def revoke_chat_invite_link(self, chat_id, invite_link): + def revoke_chat_invite_link(self, chat_id, invite_link) -> types.ChatInviteLink: """ Use this method to revoke an invite link created by the bot. Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator @@ -1284,9 +1325,11 @@ def revoke_chat_invite_link(self, chat_id, invite_link): :invite_link: The invite link to revoke :return: """ - return apihelper.revoke_chat_invite_link(self.token, chat_id, invite_link) + return types.ChatInviteLink.de_json( + apihelper.revoke_chat_invite_link(self.token, chat_id, invite_link) + ) - def export_chat_invite_link(self, chat_id): + def export_chat_invite_link(self, chat_id) -> str: """ Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1297,7 +1340,7 @@ def export_chat_invite_link(self, chat_id): """ return apihelper.export_chat_invite_link(self.token, chat_id) - def set_chat_photo(self, chat_id, photo): + def set_chat_photo(self, chat_id, photo) -> bool: """ Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1311,7 +1354,7 @@ def set_chat_photo(self, chat_id, photo): """ return apihelper.set_chat_photo(self.token, chat_id, photo) - def delete_chat_photo(self, chat_id): + def delete_chat_photo(self, chat_id) -> bool: """ Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1324,7 +1367,7 @@ def delete_chat_photo(self, chat_id): """ return apihelper.delete_chat_photo(self.token, chat_id) - def set_my_commands(self, commands): + def set_my_commands(self, commands) -> bool: """ Use this method to change the list of the bot's commands. :param commands: Array of BotCommand. A JSON-serialized list of bot commands @@ -1333,7 +1376,7 @@ def set_my_commands(self, commands): """ return apihelper.set_my_commands(self.token, commands) - def set_chat_title(self, chat_id, title): + def set_chat_title(self, chat_id, title) -> bool: """ Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1347,7 +1390,7 @@ def set_chat_title(self, chat_id, title): """ return apihelper.set_chat_title(self.token, chat_id, title) - def set_chat_description(self, chat_id, description=None): + def set_chat_description(self, chat_id, description=None) -> bool: """ Use this method to change the description of a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1359,7 +1402,7 @@ def set_chat_description(self, chat_id, description=None): """ return apihelper.set_chat_description(self.token, chat_id, description) - def pin_chat_message(self, chat_id, message_id, disable_notification=False): + def pin_chat_message(self, chat_id, message_id, disable_notification=False) -> bool: """ Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1373,7 +1416,7 @@ def pin_chat_message(self, chat_id, message_id, disable_notification=False): """ return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) - def unpin_chat_message(self, chat_id, message_id=None): + def unpin_chat_message(self, chat_id, message_id=None) -> bool: """ Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1385,7 +1428,7 @@ def unpin_chat_message(self, chat_id, message_id=None): """ return apihelper.unpin_chat_message(self.token, chat_id, message_id) - def unpin_all_chat_messages(self, chat_id): + def unpin_all_chat_messages(self, chat_id) -> bool: """ Use this method to unpin a all pinned messages in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1397,7 +1440,7 @@ def unpin_all_chat_messages(self, chat_id): return apihelper.unpin_all_chat_messages(self.token, chat_id) def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, - disable_web_page_preview=None, reply_markup=None): + disable_web_page_preview=None, reply_markup=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. :param text: @@ -1417,7 +1460,7 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ return result return types.Message.de_json(result) - def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. :param media: @@ -1432,7 +1475,7 @@ def edit_message_media(self, media, chat_id=None, message_id=None, inline_messag return result return types.Message.de_json(result) - def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. :param chat_id: @@ -1448,7 +1491,7 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_messag def send_game( self, chat_id, game_short_name, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None): + reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: """ Used to send the game :param chat_id: @@ -1465,7 +1508,7 @@ def send_game( return types.Message.de_json(result) def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None, - disable_edit_message=None): + disable_edit_message=None) -> Union[types.Message, bool]: """ Sets the value of points in the game to a specific user :param user_id: @@ -1483,7 +1526,7 @@ def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=No return result return types.Message.de_json(result) - def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None): + def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None) -> List[types.GameHighScore]: """ Gets top points and game play :param user_id: @@ -1502,7 +1545,8 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, send_email_to_provider=None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, timeout=None): + disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, + timeout=None) -> types.Message: """ Sends invoice :param chat_id: Unique identifier for the target private chat @@ -1543,7 +1587,7 @@ def send_poll( self, 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_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None): + disable_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: """ Send polls :param chat_id: @@ -1576,7 +1620,7 @@ def send_poll( explanation, explanation_parse_mode, open_period, close_date, is_closed, disable_notifications, reply_to_message_id, reply_markup, timeout)) - def stop_poll(self, chat_id, message_id, reply_markup=None): + def stop_poll(self, chat_id, message_id, reply_markup=None) -> types.Poll: """ Stops poll :param chat_id: @@ -1586,7 +1630,7 @@ def stop_poll(self, chat_id, message_id, reply_markup=None): """ return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id, reply_markup)) - def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None): + def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None) -> bool: """ Asks for an answer to a shipping question :param shipping_query_id: @@ -1597,7 +1641,7 @@ def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, er """ return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) - def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None): + def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None) -> bool: """ Response to a request for pre-inspection :param pre_checkout_query_id: @@ -1608,7 +1652,7 @@ def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=Non return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, reply_markup=None): + parse_mode=None, reply_markup=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages :param caption: @@ -1627,7 +1671,7 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me return result return types.Message.de_json(result) - def reply_to(self, message, text, **kwargs): + def reply_to(self, message, text, **kwargs) -> types.Message: """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` :param message: @@ -1638,7 +1682,7 @@ def reply_to(self, message, text, **kwargs): return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) def answer_inline_query(self, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, - switch_pm_text=None, switch_pm_parameter=None): + switch_pm_text=None, switch_pm_parameter=None) -> bool: """ Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. @@ -1655,7 +1699,7 @@ def answer_inline_query(self, inline_query_id, results, cache_time=None, is_pers return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, switch_pm_text, switch_pm_parameter) - def answer_callback_query(self, callback_query_id, text=None, show_alert=None, url=None, cache_time=None): + def answer_callback_query(self, callback_query_id, text=None, show_alert=None, url=None, cache_time=None) -> bool: """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. @@ -1668,7 +1712,7 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None, u """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - def get_sticker_set(self, name): + def get_sticker_set(self, name) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. :param name: @@ -1677,7 +1721,7 @@ def get_sticker_set(self, name): result = apihelper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) - def upload_sticker_file(self, user_id, png_sticker): + def upload_sticker_file(self, user_id, png_sticker) -> types.File: """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. @@ -1689,7 +1733,7 @@ def upload_sticker_file(self, user_id, png_sticker): return types.File.de_json(result) def create_new_sticker_set(self, user_id, name, title, png_sticker, emojis, contains_masks=None, - mask_position=None): + mask_position=None) -> bool: """ Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success. @@ -1705,7 +1749,7 @@ def create_new_sticker_set(self, user_id, name, title, png_sticker, emojis, cont return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, contains_masks, mask_position) - def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=None): + def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. Returns True on success. :param user_id: @@ -1717,7 +1761,7 @@ def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=N """ return apihelper.add_sticker_to_set(self.token, user_id, name, png_sticker, emojis, mask_position) - def set_sticker_position_in_set(self, sticker, position): + def set_sticker_position_in_set(self, sticker, position) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. :param sticker: @@ -1726,7 +1770,7 @@ def set_sticker_position_in_set(self, sticker, position): """ return apihelper.set_sticker_position_in_set(self.token, sticker, position) - def delete_sticker_from_set(self, sticker): + def delete_sticker_from_set(self, sticker) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. :param sticker: @@ -1734,7 +1778,7 @@ def delete_sticker_from_set(self, sticker): """ return apihelper.delete_sticker_from_set(self.token, sticker) - def register_for_reply(self, message, callback, *args, **kwargs): + def register_for_reply(self, message, callback, *args, **kwargs) -> None: """ Registers a callback function to be notified when a reply to `message` arrives. @@ -1747,7 +1791,7 @@ def register_for_reply(self, message, callback, *args, **kwargs): message_id = message.message_id self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs) - def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs): + def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs) -> None: """ Registers a callback function to be notified when a reply to `message` arrives. @@ -1759,7 +1803,7 @@ def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs """ self.reply_backend.register_handler(message_id, Handler(callback, *args, **kwargs)) - def _notify_reply_handlers(self, new_messages): + def _notify_reply_handlers(self, new_messages) -> None: """ Notify handlers of the answers :param new_messages: @@ -1772,7 +1816,7 @@ def _notify_reply_handlers(self, new_messages): for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - def register_next_step_handler(self, message, callback, *args, **kwargs): + def register_next_step_handler(self, message, callback, *args, **kwargs) -> None: """ Registers a callback function to be notified when new message arrives after `message`. @@ -1786,7 +1830,7 @@ def register_next_step_handler(self, message, callback, *args, **kwargs): chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) - def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwargs): + def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwargs) -> None: """ Registers a callback function to be notified when new message arrives after `message`. @@ -1799,7 +1843,7 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar """ self.next_step_backend.register_handler(chat_id, Handler(callback, *args, **kwargs)) - def clear_step_handler(self, message): + def clear_step_handler(self, message) -> None: """ Clears all callback functions registered by register_next_step_handler(). @@ -1808,7 +1852,7 @@ def clear_step_handler(self, message): chat_id = message.chat.id self.clear_step_handler_by_chat_id(chat_id) - def clear_step_handler_by_chat_id(self, chat_id): + def clear_step_handler_by_chat_id(self, chat_id) -> None: """ Clears all callback functions registered by register_next_step_handler(). @@ -1816,7 +1860,7 @@ def clear_step_handler_by_chat_id(self, chat_id): """ self.next_step_backend.clear_handlers(chat_id) - def clear_reply_handlers(self, message): + def clear_reply_handlers(self, message) -> None: """ Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). @@ -1825,7 +1869,7 @@ def clear_reply_handlers(self, message): message_id = message.message_id self.clear_reply_handlers_by_message_id(message_id) - def clear_reply_handlers_by_message_id(self, message_id): + def clear_reply_handlers_by_message_id(self, message_id) -> None: """ Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). @@ -2254,8 +2298,8 @@ def _test_filter(message_filter, filter_value, message): """ test_cases = { 'content_types': lambda msg: msg.content_type in filter_value, - 'regexp': lambda msg: msg.content_type and msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), - 'commands': lambda msg: msg.content_type and msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, + 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), + 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, 'func': lambda msg: filter_value(msg) } diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a2f04b29e..a6710e884 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -165,6 +165,13 @@ def get_me(token): method_url = r'getMe' return _make_request(token, method_url) +def log_out(token): + method_url = r'logOut' + return _make_request(token, method_url) + +def close(token): + method_url = r'close' + return _make_request(token, method_url) def get_file(token, file_id): method_url = r'getFile' diff --git a/telebot/types.py b/telebot/types.py index 5e3b33393..5b526764e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import logging +import types +from typing import Dict, List try: import ujson as json @@ -157,27 +159,19 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - id = obj['id'] - is_bot = obj['is_bot'] - first_name = obj['first_name'] - last_name = obj.get('last_name') - username = obj.get('username') - language_code = obj.get('language_code') - can_join_groups = obj.get('can_join_groups') - can_read_all_group_messages = obj.get('can_read_all_group_messages') - supports_inline_queries = obj.get('supports_inline_queries') - return cls(id, is_bot, first_name, last_name, username, language_code, can_join_groups, can_read_all_group_messages, supports_inline_queries) - - 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): - self.id = id - self.is_bot = is_bot - self.first_name = first_name - self.username = username - self.last_name = last_name - self.language_code = language_code - self.can_join_groups = can_join_groups - self.can_read_all_group_messages = can_read_all_group_messages - self.supports_inline_queries = supports_inline_queries + return cls(**obj) + + 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, **kwargs): + self.id: int = id + self.is_bot: bool = is_bot + self.first_name: str = first_name + self.username: str = username + self.last_name: str = last_name + self.language_code: str = language_code + self.can_join_groups: bool = can_join_groups + self.can_read_all_group_messages: bool = can_read_all_group_messages + self.supports_inline_queries: bool = supports_inline_queries @property def full_name(self): @@ -206,66 +200,51 @@ class GroupChat(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - id = obj['id'] - title = obj['title'] - return cls(id, title) + return cls(**obj) - def __init__(self, id, title): - self.id = id - self.title = title + def __init__(self, id, title, **kwargs): + self.id: int = id + self.title: str = title class Chat(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) - id = obj['id'] - type = obj['type'] - title = obj.get('title') - username = obj.get('username') - first_name = obj.get('first_name') - last_name = obj.get('last_name') - photo = ChatPhoto.de_json(obj.get('photo')) - bio = obj.get('bio') - description = obj.get('description') - invite_link = obj.get('invite_link') - pinned_message = Message.de_json(obj.get('pinned_message')) - permissions = ChatPermissions.de_json(obj.get('permissions')) - slow_mode_delay = obj.get('slow_mode_delay') - sticker_set_name = obj.get('sticker_set_name') - can_set_sticker_set = obj.get('can_set_sticker_set') - linked_chat_id = obj.get('linked_chat_id') - location = None # Not implemented - return cls( - id, type, title, username, first_name, last_name, - photo, bio, description, invite_link, - pinned_message, permissions, slow_mode_delay, sticker_set_name, - can_set_sticker_set, linked_chat_id, location) + if 'photo' in obj: + obj['photo'] = ChatPhoto.de_json(obj['photo']) + if 'pinned_message' in obj: + obj['pinned_message'] = Message.de_json(obj['pinned_message']) + if 'permissions' in obj: + obj['permissions'] = ChatPermissions.de_json(obj['permissions']) + if 'location' in obj: + obj['location'] = ChatLocation.de_json(obj['location']) + return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, photo=None, bio=None, description=None, invite_link=None, pinned_message=None, permissions=None, slow_mode_delay=None, - sticker_set_name=None, can_set_sticker_set=None, - linked_chat_id=None, location=None): - self.id = id - self.type = type - self.title = title - self.username = username - self.first_name = first_name - self.last_name = last_name - self.photo = photo - self.bio = bio - self.description = description - self.invite_link = invite_link - self.pinned_message = pinned_message - self.permissions = permissions - self.slow_mode_delay = slow_mode_delay - self.sticker_set_name = sticker_set_name - self.can_set_sticker_set = can_set_sticker_set - self.linked_chat_id = linked_chat_id - self.location = location + message_auto_delete_time=None, sticker_set_name=None, can_set_sticker_set=None, + linked_chat_id=None, location=None, **kwargs): + self.id: int = id + self.type: str = type + self.title: str = title + self.username: str = username + self.first_name: str = first_name + self.last_name: str = last_name + self.photo: ChatPhoto = photo + self.bio: str = bio + self.description: str = description + self.invite_link: str = invite_link + self.pinned_message: Message = pinned_message + self.permissions: ChatPermissions = permissions + self.slow_mode_delay: int = slow_mode_delay + self.message_auto_delete_time = message_auto_delete_time + self.sticker_set_name: str = sticker_set_name + self.can_set_sticker_set: bool = can_set_sticker_set + self.linked_chat_id: int = linked_chat_id + self.location: ChatLocation = location class MessageID(JsonDeserializable): @@ -306,6 +285,8 @@ def de_json(cls, json_string): opts['forward_date'] = obj.get('forward_date') if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) + if 'via_bot' in obj: + opts['via_bot'] = User.de_json(obj['via_bot']) if 'edit_date' in obj: opts['edit_date'] = obj.get('edit_date') if 'media_group_id' in obj: @@ -438,54 +419,55 @@ def parse_entities(cls, message_entity_array): return ret def __init__(self, message_id, from_user, date, chat, content_type, options, json_string): - self.content_type = content_type - self.id = message_id # Lets fix the telegram usability ####up with ID in Message :) - self.message_id = message_id - self.from_user = from_user - self.date = date - self.chat = chat - self.forward_from = None - self.forward_from_chat = None - self.forward_from_message_id = None - self.forward_signature = None - self.forward_sender_name = None - self.forward_date = None - self.reply_to_message = None - self.edit_date = None - self.media_group_id = None - self.author_signature = None - self.text = None - self.entities = None - self.caption_entities = None - self.audio = None - self.document = None - self.photo = None - self.sticker = None - self.video = None - self.video_note = None - self.voice = None - self.caption = None - self.contact = None - self.location = None - self.venue = None - self.animation = None - self.dice = None - self.new_chat_member = None # Deprecated since Bot API 3.0. Not processed anymore - self.new_chat_members = None - self.left_chat_member = None - self.new_chat_title = None - self.new_chat_photo = None - self.delete_chat_photo = None - self.group_chat_created = None - self.supergroup_chat_created = None - self.channel_chat_created = None - self.migrate_to_chat_id = None - self.migrate_from_chat_id = None - self.pinned_message = None - self.invoice = None - self.successful_payment = None - self.connected_website = None - self.reply_markup = None + self.content_type: str = content_type + self.id: int = message_id # Lets fix the telegram usability ####up with ID in Message :) + self.message_id: int = message_id + self.from_user: User = from_user + self.date: int = date + self.chat: Chat = chat + self.forward_from: User = None + self.forward_from_chat: Chat = None + self.forward_from_message_id: int = None + self.forward_signature: str = None + self.forward_sender_name: str = None + self.forward_date: int = None + self.reply_to_message: Message = None + self.via_bot: User = None + self.edit_date: int = None + self.media_group_id: str = None + self.author_signature: str = None + self.text: str = None + self.entities: List[MessageEntity] = None + self.caption_entities: List[MessageEntity] = None + self.audio: Audio = None + self.document: Document = None + self.photo: List[PhotoSize] = None + self.sticker: Sticker = None + self.video: Video = None + self.video_note: VideoNote = None + self.voice: Voice = None + self.caption: str = None + self.contact: Contact = None + self.location: Location = None + self.venue: Venue = None + self.animation: Animation = None + self.dice: Dice = None + self.new_chat_member: User = None # Deprecated since Bot API 3.0. Not processed anymore + self.new_chat_members: List[User] = None + self.left_chat_member: User = None + self.new_chat_title: str = None + self.new_chat_photo: List[PhotoSize] = None + self.delete_chat_photo: bool = None + self.group_chat_created: bool = None + self.supergroup_chat_created: bool = None + self.channel_chat_created: bool = None + self.migrate_to_chat_id: int = None + self.migrate_from_chat_id: int = None + self.pinned_message: Message = None + self.invoice: Invoice = None + self.successful_payment: SuccessfulPayment = None + self.connected_website: str = None + self.reply_markup: InlineKeyboardMarkup = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -569,25 +551,27 @@ def html_caption(self): class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): + @staticmethod + def to_list_of_dicts(entity_list) -> List[Dict]: + res = [] + for e in entity_list: + res.append(MessageEntity.to_dict(e)) + return res or None @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) - type = obj['type'] - offset = obj['offset'] - length = obj['length'] - url = obj.get('url') - user = User.de_json(obj.get('user')) - language = obj.get('language') - return cls(type, offset, length, url, user, language) - - def __init__(self, type, offset, length, url=None, user=None, language=None): - self.type = type - self.offset = offset - self.length = length - self.url = url - self.user = user - self.language = language + if 'user' in obj: + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs): + self.type: str = type + self.offset: int = offset + self.length: int = length + self.url: str = url + self.user: User = user + self.language: str = language def to_json(self): return json.dumps(self.to_dict()) @@ -606,13 +590,11 @@ class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - value = obj['value'] - emoji = obj['emoji'] - return cls(value, emoji) + return cls(**obj) - def __init__(self, value, emoji): - self.value = value - self.emoji = emoji + def __init__(self, value, emoji, **kwargs): + self.value: int = value + self.emoji: str = emoji def to_json(self): return json.dumps(self.to_dict()) @@ -627,19 +609,14 @@ class PhotoSize(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - width = obj['width'] - height = obj['height'] - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, width, height, file_size) - - def __init__(self, file_id, file_unique_id, width, height, file_size=None): - self.file_size = file_size - self.file_unique_id = file_unique_id - self.height = height - self.width = width - self.file_id = file_id + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwargs): + self.file_size: int = file_size + self.file_unique_id: str = file_unique_id + self.height: int = height + self.width: int = width + self.file_id: str = file_id class Audio(JsonDeserializable): @@ -647,23 +624,21 @@ class Audio(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - duration = obj['duration'] - performer = obj.get('performer') - title = obj.get('title') - mime_type = obj.get('mime_type') - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, duration, performer, title, mime_type, file_size) - - def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, mime_type=None, file_size=None): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.duration = duration - self.performer = performer - self.title = title - self.mime_type = mime_type - self.file_size = file_size + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, + file_size=None, thumb=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.duration: int = duration + self.performer: str = performer + self.title: str = title + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + self.thumb: PhotoSize = thumb class Voice(JsonDeserializable): @@ -671,189 +646,168 @@ class Voice(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - duration = obj['duration'] - mime_type = obj.get('mime_type') - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, duration, mime_type, file_size) - - def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.duration = duration - self.mime_type = mime_type - self.file_size = file_size + return cls(**obj) + + def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.duration: int = duration + self.mime_type: str = mime_type + self.file_size: int = file_size class Document(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - thumb = None if 'thumb' in obj and 'file_id' in obj['thumb']: - thumb = PhotoSize.de_json(obj['thumb']) - file_name = obj.get('file_name') - mime_type = obj.get('mime_type') - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, thumb, file_name, mime_type, file_size) - - def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size class Video(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - width = obj['width'] - height = obj['height'] - duration = obj['duration'] - thumb = PhotoSize.de_json(obj.get('thumb')) - mime_type = obj.get('mime_type') - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, width, height, duration, thumb, mime_type, file_size) - - def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, mime_type=None, file_size=None): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.width = width - self.height = height - self.duration = duration - self.thumb = thumb - self.mime_type = mime_type - self.file_size = file_size + if ('thumb' in obj and 'file_id' in obj['thumb']): + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size class VideoNote(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - length = obj['length'] - duration = obj['duration'] - thumb = PhotoSize.de_json(obj.get('thumb')) - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, length, duration, thumb, file_size) - - def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.length = length - self.duration = duration - self.thumb = thumb - self.file_size = file_size + if ('thumb' in obj and 'file_id' in obj['thumb']): + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.length: int = length + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_size: int = file_size class Contact(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - phone_number = obj['phone_number'] - first_name = obj['first_name'] - last_name = obj.get('last_name') - user_id = obj.get('user_id') - vcard = obj.get('vcard') - return cls(phone_number, first_name, last_name, user_id, vcard) - - def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None): - self.phone_number = phone_number - self.first_name = first_name - self.last_name = last_name - self.user_id = user_id - self.vcard = vcard + return cls(**obj) + + def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None, **kwargs): + self.phone_number: str = phone_number + self.first_name: str = first_name + self.last_name: str = last_name + self.user_id: int = user_id + self.vcard: str = vcard -class Location(JsonDeserializable): +class Location(JsonDeserializable, JsonSerializable, Dictionaryable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - longitude = obj['longitude'] - latitude = obj['latitude'] - return cls(longitude, latitude) - - def __init__(self, longitude, latitude): - self.longitude = longitude - self.latitude = latitude + return cls(**obj) + + def __init__(self, longitude: float, latitude: float, horizontal_accuracy:float=None, + live_period: int=None, heading: int=None, proximity_alert_radius: int=None, **kwargs): + self.longitude: float = longitude + self.latitude: float = latitude + self.horizontal_accuracy: float = horizontal_accuracy + self.live_period: int = live_period + self.heading: int = heading + self.proximity_alert_radius: int = proximity_alert_radius + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return { + "longitude": self.longitude, + "latitude": self.latitude, + "horizontal_accuracy": self.horizontal_accuracy, + "live_period": self.live_period, + "heading": self.heading, + "proximity_alert_radius": self.proximity_alert_radius, + } class Venue(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - location = Location.de_json(obj['location']) - title = obj['title'] - address = obj['address'] - foursquare_id = obj.get('foursquare_id') - foursquare_type = obj.get('foursquare_type') - return cls(location, title, address, foursquare_id, foursquare_type) - - def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None): - self.location = location - self.title = title - self.address = address - self.foursquare_id = foursquare_id - self.foursquare_type = foursquare_type + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, + google_place_id=None, google_place_type=None, **kwargs): + self.location: Location = location + self.title: str = title + self.address: str = address + self.foursquare_id: str = foursquare_id + self.foursquare_type: str = foursquare_type + self.google_place_id: str = google_place_id + self.google_place_type: str = google_place_type class UserProfilePhotos(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - total_count = obj['total_count'] photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] - return cls(total_count, photos) + obj['photos'] = photos + return cls(**obj) - def __init__(self, total_count, photos): - self.total_count = total_count - self.photos = photos + def __init__(self, total_count, photos, **kwargs): + self.total_count: int = total_count + self.photos: List[PhotoSize] = photos class File(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - file_size = obj.get('file_size') - file_path = obj.get('file_path') - return cls(file_id, file_unique_id, file_size, file_path) + return cls(**obj) - def __init__(self, file_id, file_unique_id, file_size, file_path): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.file_size = file_size - self.file_path = file_path + def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.file_size: int = file_size + self.file_path: str = file_path class ForceReply(JsonSerializable): def __init__(self, selective=None): - self.selective = selective + self.selective: bool = selective def to_json(self): json_dict = {'force_reply': True} @@ -864,7 +818,7 @@ def to_json(self): class ReplyKeyboardRemove(JsonSerializable): def __init__(self, selective=None): - self.selective = selective + self.selective: bool = selective def to_json(self): json_dict = {'remove_keyboard': True} @@ -883,11 +837,11 @@ def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - self.resize_keyboard = resize_keyboard - self.one_time_keyboard = one_time_keyboard - self.selective = selective - self.row_width = row_width - self.keyboard = [] + self.resize_keyboard: bool = resize_keyboard + self.one_time_keyboard: bool = one_time_keyboard + self.selective: bool = selective + self.row_width: int = row_width + self.keyboard: List[List[KeyboardButton]] = [] def add(self, *args, row_width=None): """ @@ -952,10 +906,10 @@ def to_json(self): class KeyboardButton(Dictionaryable, JsonSerializable): def __init__(self, text, request_contact=None, request_location=None, request_poll=None): - self.text = text - self.request_contact = request_contact - self.request_location = request_location - self.request_poll = request_poll + self.text: str = text + self.request_contact: bool = request_contact + self.request_location: bool = request_location + self.request_poll: KeyboardButtonPollType = request_poll def to_json(self): return json.dumps(self.to_dict()) @@ -973,7 +927,7 @@ def to_dict(self): class KeyboardButtonPollType(Dictionaryable): def __init__(self, type=''): - self.type = type + self.type: str = type def to_dict(self): return {'type': self.type} @@ -984,8 +938,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) keyboard = [[InlineKeyboardButton.de_json(button) for button in row] for row in obj['inline_keyboard']] return cls(keyboard) @@ -1002,8 +955,8 @@ def __init__(self, keyboard=None ,row_width=3): logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - self.row_width = row_width - self.keyboard = keyboard if keyboard else [] + self.row_width: int = row_width + self.keyboard: List[List[InlineKeyboardButton]] = keyboard or [] def add(self, *args, row_width=None): """ @@ -1064,64 +1017,25 @@ def to_dict(self): return json_dict -class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): - def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None): - self.url = url - self.forward_text = forward_text - self.bot_username = bot_username - self.request_write_access = request_write_access - +class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - url = obj['url'] - forward_text = obj.get('forward_text') - bot_username = obj.get('bot_username') - request_write_access = obj.get('request_write_access') - return cls(url, forward_text, bot_username, request_write_access) - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'url': self.url} - if self.forward_text: - json_dict['forward_text'] = self.forward_text - if self.bot_username: - json_dict['bot_username'] = self.bot_username - if self.request_write_access is not None: - json_dict['request_write_access'] = self.request_write_access - return json_dict - + if 'login_url' in obj: + obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) + return cls(**obj) -class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, - switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None): - self.text = text - self.url = url - self.callback_data = callback_data - self.switch_inline_query = switch_inline_query - self.switch_inline_query_current_chat = switch_inline_query_current_chat - self.callback_game = callback_game - self.pay = pay - self.login_url = login_url - - @classmethod - def de_json(cls, json_string): - if (json_string is None): - return None - obj = cls.check_json(json_string) - text = obj['text'] - url = obj.get('url') - callback_data = obj.get('callback_data') - switch_inline_query = obj.get('switch_inline_query') - switch_inline_query_current_chat = obj.get('switch_inline_query_current_chat') - callback_game = obj.get('callback_game') - pay = obj.get('pay') - login_url = LoginUrl.de_json(obj.get('login_url')) - return cls(text, url, callback_data, switch_inline_query, switch_inline_query_current_chat, callback_game, pay, login_url) + switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): + self.text: str = text + self.url: str = url + self.callback_data: str = callback_data + self.switch_inline_query: str = switch_inline_query + self.switch_inline_query_current_chat: str = switch_inline_query_current_chat + self.callback_game = callback_game # Not Implemented + self.pay: bool = pay + self.login_url: LoginUrl = login_url def to_json(self): return json.dumps(self.to_dict()) @@ -1145,139 +1059,122 @@ def to_dict(self): return json_dict -class CallbackQuery(JsonDeserializable): +class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) - id = obj['id'] - from_user = User.de_json(obj['from']) - message = Message.de_json(obj.get('message')) - inline_message_id = obj.get('inline_message_id') - chat_instance = obj['chat_instance'] - data = obj.get('data') - game_short_name = obj.get('game_short_name') - return cls(id, from_user, data, chat_instance, message, inline_message_id, game_short_name) + return cls(**obj) - def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None): - self.game_short_name = game_short_name - self.chat_instance = chat_instance - self.id = id - self.from_user = from_user - self.message = message - self.data = data - self.inline_message_id = inline_message_id + def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None, **kwargs): + self.url: str = url + self.forward_text: str = forward_text + self.bot_username: str = bot_username + self.request_write_access: bool = request_write_access + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'url': self.url} + if self.forward_text: + json_dict['forward_text'] = self.forward_text + if self.bot_username: + json_dict['bot_username'] = self.bot_username + if self.request_write_access is not None: + json_dict['request_write_access'] = self.request_write_access + return json_dict +class CallbackQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + if 'message' in obj: + obj['message'] = Message.de_json(obj.get('message')) + return cls(**obj) + + def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None, **kwargs): + self.id: int = id + self.from_user: User = from_user + self.message: Message = message + self.inline_message_id: str = inline_message_id + self.chat_instance: str = chat_instance + self.data: str = data + self.game_short_name: str = game_short_name + + class ChatPhoto(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - small_file_id = obj['small_file_id'] - small_file_unique_id = obj['small_file_unique_id'] - big_file_id = obj['big_file_id'] - big_file_unique_id = obj['big_file_unique_id'] - return cls(small_file_id, small_file_unique_id, big_file_id, big_file_unique_id) + return cls(**obj) - def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id): - self.small_file_id = small_file_id - self.small_file_unique_id = small_file_unique_id - self.big_file_id = big_file_id - self.big_file_unique_id = big_file_unique_id + def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id, **kwargs): + self.small_file_id: str = small_file_id + self.small_file_unique_id: str = small_file_unique_id + self.big_file_id: str = big_file_id + self.big_file_unique_id: str = big_file_unique_id class ChatMember(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) - user = User.de_json(obj['user']) - status = obj['status'] - custom_title = obj.get('custom_title') - can_be_edited = obj.get('can_be_edited') - can_post_messages = obj.get('can_post_messages') - can_edit_messages = obj.get('can_edit_messages') - can_delete_messages = obj.get('can_delete_messages') - can_restrict_members = obj.get('can_restrict_members') - can_promote_members = obj.get('can_promote_members') - can_change_info = obj.get('can_change_info') - can_invite_users = obj.get('can_invite_users') - can_pin_messages = obj.get('can_pin_messages') - is_member = obj.get('is_member') - can_send_messages = obj.get('can_send_messages') - can_send_media_messages = obj.get('can_send_media_messages') - can_send_polls = obj.get('can_send_polls') - can_send_other_messages = obj.get('can_send_other_messages') - can_add_web_page_previews = obj.get('can_add_web_page_previews') - until_date = obj.get('until_date') - return cls( - user, status, custom_title, can_be_edited, can_post_messages, - can_edit_messages, can_delete_messages, can_restrict_members, - can_promote_members, can_change_info, can_invite_users, can_pin_messages, - is_member, can_send_messages, can_send_media_messages, can_send_polls, - can_send_other_messages, can_add_web_page_previews, until_date) - - def __init__(self, user, status, custom_title=None, can_be_edited=None, + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_restrict_members=None, can_promote_members=None, can_change_info=None, can_invite_users=None, can_pin_messages=None, is_member=None, can_send_messages=None, can_send_media_messages=None, can_send_polls=None, - can_send_other_messages=None, can_add_web_page_previews=None, until_date=None): - self.user = user - self.status = status - self.custom_title = custom_title - self.can_be_edited = can_be_edited - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_delete_messages = can_delete_messages - self.can_restrict_members = can_restrict_members - self.can_promote_members = can_promote_members - self.can_change_info = can_change_info - self.can_invite_users = can_invite_users - self.can_pin_messages = can_pin_messages - self.is_member = is_member - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_polls = can_send_polls - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.until_date = until_date + can_send_other_messages=None, can_add_web_page_previews=None, until_date=None, **kwargs): + self.user: User = user + self.status: str = status + self.custom_title: str = custom_title + self.is_anonymous: bool = is_anonymous + self.can_be_edited: bool = can_be_edited + self.can_post_messages: bool = can_post_messages + self.can_edit_messages: bool = can_edit_messages + self.can_delete_messages: bool = can_delete_messages + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + self.is_member: bool = is_member + self.can_send_messages: bool = can_send_messages + self.can_send_media_messages: bool = can_send_media_messages + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.until_date: int = until_date class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): - def __init__(self, can_send_messages=None, can_send_media_messages=None, - can_send_polls=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None): - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_polls = can_send_polls - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.can_change_info = can_change_info - self.can_invite_users = can_invite_users - self.can_pin_messages = can_pin_messages - @classmethod def de_json(cls, json_string): - if json_string is None: - return json_string + if json_string is None: return json_string obj = cls.check_json(json_string) - can_send_messages = obj.get('can_send_messages') - can_send_media_messages = obj.get('can_send_media_messages') - can_send_polls = obj.get('can_send_polls') - can_send_other_messages = obj.get('can_send_other_messages') - can_add_web_page_previews = obj.get('can_add_web_page_previews') - can_change_info = obj.get('can_change_info') - can_invite_users = obj.get('can_invite_users') - can_pin_messages = obj.get('can_pin_messages') - return cls( - can_send_messages, can_send_media_messages, can_send_polls, - can_send_other_messages, can_add_web_page_previews, - can_change_info, can_invite_users, can_pin_messages) + return cls(**obj) + + def __init__(self, can_send_messages=None, can_send_media_messages=None, + can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None, **kwargs): + self.can_send_messages: bool = can_send_messages + self.can_send_media_messages: bool = can_send_media_messages + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages def to_json(self): return json.dumps(self.to_dict()) @@ -1312,8 +1209,8 @@ def __init__(self, command, description): :param description: Description of the command, 3-256 characters. :return: """ - self.command = command - self.description = description + self.command: str = command + self.description: str = description def to_json(self): return json.dumps(self.to_dict()) @@ -1327,71 +1224,89 @@ def to_dict(self): class InlineQuery(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - id = obj['id'] - from_user = User.de_json(obj['from']) - location = Location.de_json(obj.get('location')) - query = obj['query'] - offset = obj['offset'] - return cls(id, from_user, location, query, offset) - - def __init__(self, id, from_user, location, query, offset): + obj['from_user'] = User.de_json(obj.pop('from')) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, id, from_user, query, offset, chat_type=None, location=None, **kwargs): """ This object represents an incoming inline query. When the user sends an empty query, your bot could return some default or trending results. :param id: string Unique identifier for this query :param from_user: User Sender - :param location: Sender location, only for bots that request user location :param query: String Text of the query + :param chat_type: String Type of the chat, from which the inline query was sent. + Can be either “sender” for a private chat with the inline query sender, + “private”, “group”, “supergroup”, or “channel”. :param offset: String Offset of the results to be returned, can be controlled by the bot + :param location: Sender location, only for bots that request user location :return: InlineQuery Object """ - self.id = id - self.from_user = from_user - self.location = location - self.query = query - self.offset = offset + self.id: int = id + self.from_user: User = from_user + self.query: str = query + self.offset: str = offset + self.chat_type: str = chat_type + self.location: Location = location + + class InputTextMessageContent(Dictionaryable): - def __init__(self, message_text, parse_mode=None, disable_web_page_preview=None): - self.message_text = message_text - self.parse_mode = parse_mode - self.disable_web_page_preview = disable_web_page_preview + def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None): + self.message_text: str = message_text + self.parse_mode: str = parse_mode + self.entities: List[MessageEntity] = entities + self.disable_web_page_preview: bool = disable_web_page_preview def to_dict(self): - json_dic = {'message_text': self.message_text} + json_dict = {'message_text': self.message_text} if self.parse_mode: - json_dic['parse_mode'] = self.parse_mode + json_dict['parse_mode'] = self.parse_mode + if self.entities: + json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) if self.disable_web_page_preview: - json_dic['disable_web_page_preview'] = self.disable_web_page_preview - return json_dic + json_dict['disable_web_page_preview'] = self.disable_web_page_preview + return json_dict class InputLocationMessageContent(Dictionaryable): - def __init__(self, latitude, longitude, live_period=None): - self.latitude = latitude - self.longitude = longitude - self.live_period = live_period + def __init__(self, latitude, longitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None): + self.latitude: float = latitude + self.longitude: float = longitude + self.horizontal_accuracy: float = horizontal_accuracy + self.live_period: int = live_period + self.heading: int = heading + self.proximity_alert_radius: int = proximity_alert_radius def to_dict(self): - json_dic = {'latitude': self.latitude, 'longitude': self.longitude} + json_dict = {'latitude': self.latitude, 'longitude': self.longitude} + if self.horizontal_accuracy: + json_dict['horizontal_accuracy'] = self.horizontal_accuracy if self.live_period: - json_dic['live_period'] = self.live_period - return json_dic + 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 + return json_dict class InputVenueMessageContent(Dictionaryable): - def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None): - self.latitude = latitude - self.longitude = longitude - self.title = title - self.address = address - self.foursquare_id = foursquare_id - self.foursquare_type = foursquare_type + def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, + google_place_id=None, google_place_type=None): + self.latitude: float = latitude + self.longitude: float = longitude + self.title: str = title + self.address: str = address + self.foursquare_id: str = foursquare_id + self.foursquare_type: str = foursquare_type + self.google_place_id: str = google_place_id + self.google_place_type: str = google_place_type def to_dict(self): json_dict = { @@ -1404,15 +1319,19 @@ def to_dict(self): json_dict['foursquare_id'] = self.foursquare_id if self.foursquare_type: json_dict['foursquare_type'] = self.foursquare_type + 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_dict class InputContactMessageContent(Dictionaryable): def __init__(self, phone_number, first_name, last_name=None, vcard=None): - self.phone_number = phone_number - self.first_name = first_name - self.last_name = last_name - self.vcard = vcard + self.phone_number: str = phone_number + self.first_name: str = first_name + self.last_name: str = last_name + self.vcard: str = vcard def to_dict(self): json_dict = {'phone_numbe': self.phone_number, 'first_name': self.first_name} @@ -1426,17 +1345,14 @@ def to_dict(self): class ChosenInlineResult(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) - result_id = obj['result_id'] - from_user = User.de_json(obj['from']) - query = obj['query'] - location = Location.de_json(obj.get('location')) - inline_message_id = obj.get('inline_message_id') - return cls(result_id, from_user, query, location, inline_message_id) - - def __init__(self, result_id, from_user, query, location=None, inline_message_id=None): + obj['from_user'] = User.de_json(obj.pop('from')) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, result_id, from_user, query, location=None, inline_message_id=None, **kwargs): """ This object represents a result of an inline query that was chosen by the user and sent to their chat partner. @@ -1445,11 +1361,11 @@ def __init__(self, result_id, from_user, query, location=None, inline_message_id :param query: String The query that was used to obtain the result. :return: ChosenInlineResult Object. """ - self.result_id = result_id - self.from_user = from_user - self.query = query - self.location = location - self.inline_message_id = inline_message_id + self.result_id: str = result_id + self.from_user: User = from_user + self.location: Location = location + self.inline_message_id: str = inline_message_id + self.query: str = query class InlineQueryResultArticle(JsonSerializable): @@ -2089,16 +2005,12 @@ class Game(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - title = obj['title'] - description = obj['description'] - photo = Game.parse_photo(obj['photo']) - text = obj.get('text') + obj['photo'] = Game.parse_photo(obj['photo']) if 'text_entities' in obj: - text_entities = Game.parse_entities(obj['text_entities']) - else: - text_entities = None - animation = Animation.de_json(obj.get('animation')) - return cls(title, description, photo, text, text_entities, animation) + obj['text_entities'] = Game.parse_entities(obj['text_entities']) + if 'animation' in obj: + obj['animation'] = Animation.de_json(obj['animation']) + return cls(**obj) @classmethod def parse_photo(cls, photo_size_array): @@ -2114,13 +2026,13 @@ def parse_entities(cls, message_entity_array): ret.append(MessageEntity.de_json(me)) return ret - def __init__(self, title, description, photo, text=None, text_entities=None, animation=None): - self.title = title - self.description = description - self.photo = photo - self.text = text - self.text_entities = text_entities - self.animation = animation + def __init__(self, title, description, photo, text=None, text_entities=None, animation=None, **kwargs): + self.title: str = title + self.description: str = description + self.photo: List[PhotoSize] = photo + self.text: str = text + self.text_entities: List[MessageEntity] = text_entities + self.animation: Animation = animation class Animation(JsonDeserializable): @@ -2128,21 +2040,20 @@ class Animation(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - thumb = PhotoSize.de_json(obj.get('thumb')) - file_name = obj.get('file_name') - mime_type = obj.get('mime_type') - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, thumb, file_name, mime_type, file_size) - - def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size + obj["thumb"] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, + thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size class GameHighScore(JsonDeserializable): @@ -2150,23 +2061,21 @@ class GameHighScore(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - position = obj['position'] - user = User.de_json(obj['user']) - score = obj['score'] - return cls(position, user, score) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) - def __init__(self, position, user, score): - self.position = position - self.user = user - self.score = score + def __init__(self, position, user, score, **kwargs): + self.position: int = position + self.user: User = user + self.score: int = score # Payments class LabeledPrice(JsonSerializable): def __init__(self, label, amount): - self.label = label - self.amount = amount + self.label: str = label + self.amount: int = amount def to_json(self): return json.dumps({ @@ -2179,19 +2088,14 @@ class Invoice(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - title = obj['title'] - description = obj['description'] - start_parameter = obj['start_parameter'] - currency = obj['currency'] - total_amount = obj['total_amount'] - return cls(title, description, start_parameter, currency, total_amount) - - def __init__(self, title, description, start_parameter, currency, total_amount): - self.title = title - self.description = description - self.start_parameter = start_parameter - self.currency = currency - self.total_amount = total_amount + return cls(**obj) + + def __init__(self, title, description, start_parameter, currency, total_amount, **kwargs): + self.title: str = title + self.description: str = description + self.start_parameter: str = start_parameter + self.currency: str = currency + self.total_amount: int = total_amount class ShippingAddress(JsonDeserializable): @@ -2199,21 +2103,15 @@ class ShippingAddress(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - country_code = obj['country_code'] - state = obj['state'] - city = obj['city'] - street_line1 = obj['street_line1'] - street_line2 = obj['street_line2'] - post_code = obj['post_code'] - return cls(country_code, state, city, street_line1, street_line2, post_code) - - def __init__(self, country_code, state, city, street_line1, street_line2, post_code): - self.country_code = country_code - self.state = state - self.city = city - self.street_line1 = street_line1 - self.street_line2 = street_line2 - self.post_code = post_code + return cls(**obj) + + def __init__(self, country_code, state, city, street_line1, street_line2, post_code, **kwargs): + self.country_code: str = country_code + self.state: str = state + self.city: str = city + self.street_line1: str = street_line1 + self.street_line2: str = street_line2 + self.post_code: str = post_code class OrderInfo(JsonDeserializable): @@ -2221,24 +2119,21 @@ class OrderInfo(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - name = obj.get('name') - phone_number = obj.get('phone_number') - email = obj.get('email') - shipping_address = ShippingAddress.de_json(obj.get('shipping_address')) - return cls(name, phone_number, email, shipping_address) - - def __init__(self, name, phone_number, email, shipping_address): - self.name = name - self.phone_number = phone_number - self.email = email - self.shipping_address = shipping_address + obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) + return cls(**obj) + + def __init__(self, name, phone_number, email, shipping_address, **kwargs): + self.name: str = name + self.phone_number: str = phone_number + self.email: str = email + self.shipping_address: ShippingAddress = shipping_address class ShippingOption(JsonSerializable): def __init__(self, id, title): - self.id = id - self.title = title - self.prices = [] + self.id: str = id + self.title: str = title + self.prices: List[LabeledPrice] = [] def add_price(self, *args): """ @@ -2262,25 +2157,19 @@ class SuccessfulPayment(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - currency = obj['currency'] - total_amount = obj['total_amount'] - invoice_payload = obj['invoice_payload'] - shipping_option_id = obj.get('shipping_option_id') - order_info = OrderInfo.de_json(obj.get('order_info')) - telegram_payment_charge_id = obj['telegram_payment_charge_id'] - provider_payment_charge_id = obj['provider_payment_charge_id'] - return cls(currency, total_amount, invoice_payload, shipping_option_id, order_info, - telegram_payment_charge_id, provider_payment_charge_id) - - def __init__(self, currency, total_amount, invoice_payload, shipping_option_id, order_info, - telegram_payment_charge_id, provider_payment_charge_id): - self.currency = currency - self.total_amount = total_amount - self.invoice_payload = invoice_payload - self.shipping_option_id = shipping_option_id - self.order_info = order_info - self.telegram_payment_charge_id = telegram_payment_charge_id - self.provider_payment_charge_id = provider_payment_charge_id + if 'order_info' in obj: + obj['order_info'] = OrderInfo.de_json(obj['order_info']) + return cls(**obj) + + def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, + telegram_payment_charge_id=None, provider_payment_charge_id=None, **kwargs): + self.currency: str = currency + self.total_amount: int = total_amount + self.invoice_payload: str = invoice_payload + self.shipping_option_id: str = shipping_option_id + self.order_info: OrderInfo = order_info + self.telegram_payment_charge_id: str = telegram_payment_charge_id + self.provider_payment_charge_id: str = provider_payment_charge_id class ShippingQuery(JsonDeserializable): @@ -2288,17 +2177,15 @@ class ShippingQuery(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - id = obj['id'] - from_user = User.de_json(obj['from']) - invoice_payload = obj['invoice_payload'] - shipping_address = ShippingAddress.de_json(obj['shipping_address']) - return cls(id, from_user, invoice_payload, shipping_address) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) + return cls(**obj) - def __init__(self, id, from_user, invoice_payload, shipping_address): - self.id = id - self.from_user = from_user - self.invoice_payload = invoice_payload - self.shipping_address = shipping_address + def __init__(self, id, from_user, invoice_payload, shipping_address, **kwargs): + self.id: str = id + self.from_user: User = from_user + self.invoice_payload: str = invoice_payload + self.shipping_address: ShippingAddress = shipping_address class PreCheckoutQuery(JsonDeserializable): @@ -2306,23 +2193,19 @@ class PreCheckoutQuery(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - id = obj['id'] - from_user = User.de_json(obj['from']) - currency = obj['currency'] - total_amount = obj['total_amount'] - invoice_payload = obj['invoice_payload'] - shipping_option_id = obj.get('shipping_option_id') - order_info = OrderInfo.de_json(obj.get('order_info')) - return cls(id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info) - - def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id, order_info): - self.id = id - self.from_user = from_user - self.currency = currency - self.total_amount = total_amount - self.invoice_payload = invoice_payload - self.shipping_option_id = shipping_option_id - self.order_info = order_info + obj['from_user'] = User.de_json(obj.pop('from')) + if 'order_info' in obj: + obj['order_info'] = OrderInfo.de_json(obj['order_info']) + return cls(**obj) + + def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs): + self.id: str = id + self.from_user: User = from_user + self.currency: str = currency + self.total_amount: int = total_amount + self.invoice_payload: str = invoice_payload + self.shipping_option_id: str = shipping_option_id + self.order_info: OrderInfo = order_info # Stickers @@ -2332,21 +2215,21 @@ class StickerSet(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - name = obj['name'] - title = obj['title'] - is_animated = obj['is_animated'] - contains_masks = obj['contains_masks'] stickers = [] for s in obj['stickers']: stickers.append(Sticker.de_json(s)) - return cls(name, title, is_animated, contains_masks, stickers) + obj['stickers'] = stickers + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) - def __init__(self, name, title, is_animated, contains_masks, stickers): - self.stickers = stickers - self.is_animated = is_animated - self.contains_masks = contains_masks - self.title = title - self.name = name + def __init__(self, name, title, is_animated, contains_masks, stickers, thumb=None, **kwargs): + self.name: str = name + self.title: str = title + self.is_animated: bool = is_animated + self.contains_masks: bool = contains_masks + self.stickers: List[Sticker] = stickers + self.thumb: PhotoSize = thumb class Sticker(JsonDeserializable): @@ -2354,29 +2237,25 @@ class Sticker(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - file_id = obj['file_id'] - file_unique_id = obj['file_unique_id'] - width = obj['width'] - height = obj['height'] - is_animated = obj['is_animated'] - thumb = PhotoSize.de_json(obj.get('thumb')) - emoji = obj.get('emoji') - set_name = obj.get('set_name') - mask_position = MaskPosition.de_json(obj.get('mask_position')) - file_size = obj.get('file_size') - return cls(file_id, file_unique_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated) - - def __init__(self, file_id, file_unique_id, width, height, thumb, emoji, set_name, mask_position, file_size, is_animated): - self.file_id = file_id - self.file_unique_id = file_unique_id - self.width = width - self.height = height - self.thumb = thumb - self.emoji = emoji - self.set_name = set_name - self.mask_position = mask_position - self.file_size = file_size - self.is_animated = is_animated + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + if 'mask_position' in obj: + obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width, height, is_animated, + thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.is_animated: bool = is_animated + self.thumb: PhotoSize = thumb + self.emoji: str = emoji + self.set_name: str = set_name + self.mask_position: MaskPosition = mask_position + self.file_size: int = file_size + class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): @@ -2384,17 +2263,13 @@ class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - point = obj['point'] - x_shift = obj['x_shift'] - y_shift = obj['y_shift'] - scale = obj['scale'] - return cls(point, x_shift, y_shift, scale) - - def __init__(self, point, x_shift, y_shift, scale): - self.point = point - self.x_shift = x_shift - self.y_shift = y_shift - self.scale = scale + return cls(**obj) + + def __init__(self, point, x_shift, y_shift, scale, **kwargs): + self.point: str = point + self.x_shift: float = x_shift + self.y_shift: float = y_shift + self.scale: float = scale def to_json(self): return json.dumps(self.to_dict()) @@ -2407,10 +2282,10 @@ def to_dict(self): class InputMedia(Dictionaryable, JsonSerializable): def __init__(self, type, media, caption=None, parse_mode=None): - self.type = type - self.media = media - self.caption = caption - self.parse_mode = parse_mode + self.type: str = type + self.media: str = media + self.caption: str = caption + self.parse_mode: str = parse_mode if util.is_string(self.media): self._media_name = '' @@ -2533,13 +2408,11 @@ class PollOption(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - text = obj['text'] - voter_count = int(obj['voter_count']) - return cls(text, voter_count) + return cls(**obj) - def __init__(self, text, voter_count = 0): - self.text = text - self.voter_count = voter_count + def __init__(self, text, voter_count = 0, **kwargs): + self.text: str = text + self.voter_count: int = voter_count # Converted in _convert_poll_options # def to_json(self): # # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll @@ -2551,49 +2424,33 @@ class Poll(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - poll_id = obj['id'] - question = obj['question'] options = [] for opt in obj['options']: options.append(PollOption.de_json(opt)) - total_voter_count = obj['total_voter_count'] - is_closed = obj['is_closed'] - is_anonymous = obj['is_anonymous'] - poll_type = obj['type'] - allows_multiple_answers = obj['allows_multiple_answers'] - correct_option_id = obj.get('correct_option_id') - explanation = obj.get('explanation') + obj['options'] = options or None if 'explanation_entities' in obj: - explanation_entities = Message.parse_entities(obj['explanation_entities']) - else: - explanation_entities = None - open_period = obj.get('open_period') - close_date = obj.get('close_date') - return cls( - question, options, - poll_id, total_voter_count, is_closed, is_anonymous, poll_type, - allows_multiple_answers, correct_option_id, explanation, explanation_entities, - open_period, close_date) + obj['explanation_entities'] = Message.parse_entities(obj['explanation_entities']) + return cls(**obj) def __init__( self, question, options, poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, - open_period=None, close_date=None): - self.id = poll_id - self.question = question - self.options = options - self.total_voter_count = total_voter_count - self.is_closed = is_closed - self.is_anonymous = is_anonymous - self.type = poll_type - self.allows_multiple_answers = allows_multiple_answers - self.correct_option_id = correct_option_id - self.explanation = explanation - self.explanation_entities = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] - self.open_period = open_period - self.close_date = close_date + open_period=None, close_date=None, **kwargs): + self.id: str = poll_id + self.question: str = question + self.options: List[PollOption] = options + self.total_voter_count: int = total_voter_count + self.is_closed: bool = is_closed + self.is_anonymous: bool = is_anonymous + self.type: str = poll_type + self.allows_multiple_answers: bool = allows_multiple_answers + self.correct_option_id: int = correct_option_id + self.explanation: str = explanation + self.explanation_entities: List[MessageEntity] = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] + self.open_period: int = open_period + self.close_date: int = close_date def add(self, option): if type(option) is PollOption: @@ -2607,15 +2464,13 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - poll_id = obj['poll_id'] - user = User.de_json(obj['user']) - options_ids = obj['option_ids'] - return cls(poll_id, user, options_ids) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) - def __init__(self, poll_id, user, options_ids): - self.poll_id = poll_id - self.user = user - self.options_ids = options_ids + def __init__(self, poll_id, user, options_ids, **kwargs): + self.poll_id: str = poll_id + self.user: User = user + self.options_ids: List[int] = options_ids def to_json(self): return json.dumps(self.to_dict()) @@ -2624,3 +2479,56 @@ def to_dict(self): return {'poll_id': self.poll_id, 'user': self.user.to_dict(), 'options_ids': self.options_ids} + + +class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return json_string + obj = cls.check_json(json_string) + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, location: Location, address: str, **kwargs): + self.location: Location = location + self.address: str = address + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return { + "location": self.location.to_dict(), + "address": self.address + } + + +class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['creator'] = User.de_json(obj['creator']) + return cls(**obj) + + def __init__(self, invite_link: str, creator: User, is_primary: bool, is_revoked: bool, + expire_date: int=None, member_limit: int=None, **kwargs): + self.invite_link: str = invite_link + self.creator: User = creator + self.is_primary: bool = is_primary + self.is_revoked: bool = is_revoked + self.expire_date: int = expire_date + self.member_limit: int = member_limit + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return { + "invite_link": self.invite_link, + "creator": self.creator.to_dict(), + "is_primary": self.is_primary, + "is_revoked": self.is_revoked, + "expire_date": self.expire_date, + "member_limit": self.member_limit + } \ No newline at end of file diff --git a/telebot/util.py b/telebot/util.py index de9c1ce25..72a4b897b 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +# credits: https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/ +from __future__ import annotations import random import re import string @@ -6,11 +8,13 @@ import traceback import warnings import functools -from typing import Any, List, Dict +from typing import Any, List, Dict, TYPE_CHECKING import queue as Queue import logging -# from telebot import types +# credits: https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/ +if TYPE_CHECKING: + from telebot import types try: from PIL import Image @@ -289,7 +293,7 @@ def escape(text: str) -> str: return text -def user_link(user, include_id: bool=False) -> str: +def user_link(user: types.User, include_id: bool=False) -> str: """ Returns an HTML user link. This is useful for reports. Attention: Don't forget to set parse_mode to 'HTML'! @@ -306,41 +310,40 @@ def user_link(user, include_id: bool=False) -> str: + (f" (
{user.id}
)" if include_id else "")) -# def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2): -# """ -# Returns a reply markup from a dict in this format: {'text': kwargs} -# This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' -# -# Example: -# quick_markup({ -# 'Twitter': {'url': 'https://twitter.com'}, -# 'Facebook': {'url': 'https://facebook.com'}, -# 'Back': {'callback_data': 'whatever'} -# }, row_width=2): -# returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook -# and a back button below -# -# kwargs can be: -# { -# 'url': None, -# 'callback_data': None, -# 'switch_inline_query': None, -# 'switch_inline_query_current_chat': None, -# 'callback_game': None, -# 'pay': None, -# 'login_url': None -# } -# -# :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} -# :param row_width: -# :return: InlineKeyboardMarkup -# """ -# markup = types.InlineKeyboardMarkup(row_width=row_width) -# buttons = [] -# for text, kwargs in values.items(): -# buttons.append(types.InlineKeyboardButton(text=text, **kwargs)) -# markup.add(*buttons) -# return markup +def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup: + """ + Returns a reply markup from a dict in this format: {'text': kwargs} + This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' + + Example: + quick_markup({ + 'Twitter': {'url': 'https://twitter.com'}, + 'Facebook': {'url': 'https://facebook.com'}, + 'Back': {'callback_data': 'whatever'} + }, row_width=2): + returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook + and a back button below + + kwargs can be: + { + 'url': None, + 'callback_data': None, + 'switch_inline_query': None, + 'switch_inline_query_current_chat': None, + 'callback_game': None, + 'pay': None, + 'login_url': None + } + + :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} + :return: InlineKeyboardMarkup + """ + markup = types.InlineKeyboardMarkup(row_width=row_width) + buttons = [] + for text, kwargs in values.items(): + buttons.append(types.InlineKeyboardButton(text=text, **kwargs)) + markup.add(*buttons) + return markup # CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352 From 63fe6e01d18360e6e9b8cf12b398dfc523bc3575 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Fri, 18 Jun 2021 22:35:49 +0200 Subject: [PATCH 0584/1808] Fixed the errors from my last PRs I testet all using pytest and python versions 3.6-3.9 on macOS --- telebot/__init__.py | 3 +++ telebot/types.py | 12 +++++++++++- telebot/util.py | 11 ++++------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index eaf846528..cb582dc4e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -9,6 +9,9 @@ import traceback from typing import List, Union +import telebot.util +import telebot.types + logger = logging.getLogger('TeleBot') formatter = logging.Formatter( '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' diff --git a/telebot/types.py b/telebot/types.py index 5b526764e..79ffcf2d7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import logging -import types from typing import Dict, List try: @@ -159,6 +158,7 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) + print (obj) return cls(**obj) def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None, @@ -626,6 +626,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'thumb' in obj and 'file_id' in obj['thumb']: obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None return cls(**obj) def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, @@ -663,6 +665,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'thumb' in obj and 'file_id' in obj['thumb']: obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None return cls(**obj) def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): @@ -2221,6 +2225,8 @@ def de_json(cls, json_string): obj['stickers'] = stickers if 'thumb' in obj and 'file_id' in obj['thumb']: obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None return cls(**obj) def __init__(self, name, title, is_animated, contains_masks, stickers, thumb=None, **kwargs): @@ -2239,6 +2245,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'thumb' in obj and 'file_id' in obj['thumb']: obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None if 'mask_position' in obj: obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) return cls(**obj) @@ -2424,6 +2432,7 @@ class Poll(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) + obj['poll_id'] = obj.pop('id') options = [] for opt in obj['options']: options.append(PollOption.de_json(opt)) @@ -2465,6 +2474,7 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) + obj['options_ids'] = obj.pop('option_ids') return cls(**obj) def __init__(self, poll_id, user, options_ids, **kwargs): diff --git a/telebot/util.py b/telebot/util.py index 72a4b897b..beb9e9091 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -# credits: https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/ -from __future__ import annotations import random import re import string @@ -8,13 +6,12 @@ import traceback import warnings import functools -from typing import Any, List, Dict, TYPE_CHECKING +from typing import Any, List, Dict import queue as Queue import logging -# credits: https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/ -if TYPE_CHECKING: - from telebot import types + +from telebot import types try: from PIL import Image @@ -198,7 +195,7 @@ def is_command(text: str) -> bool: :param text: Text to check. :return: True if `text` is a command, else False. """ - if (text is None): return None + if (text is None): return False return text.startswith('/') From a9ae070256bf65c64bc78547d6f57c2aa339177a Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Fri, 18 Jun 2021 22:37:31 +0200 Subject: [PATCH 0585/1808] Update types.py --- telebot/types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 79ffcf2d7..6ecb75da1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -158,7 +158,6 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - print (obj) return cls(**obj) def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None, From d26923e167e4ce324134bbdb117039890c874007 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 19 Jun 2021 15:09:52 +0300 Subject: [PATCH 0586/1808] Raise exception if no token passed --- telebot/apihelper.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a2f04b29e..7a238e3db 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -68,6 +68,8 @@ def _make_request(token, method_name, method='get', params=None, files=None): :param files: Optional files. :return: The result parsed to a JSON dictionary. """ + if not token: + raise Exception('Bot token is not defined') if API_URL: request_url = API_URL.format(token, method_name) else: From 795f7fff7fb41e6cc74b88f1229fee56032d8872 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Sat, 19 Jun 2021 17:59:55 +0200 Subject: [PATCH 0587/1808] Some small changes * Fixed type warnings in some editors by changing `var: Type = None` to `var: Union[Type, None] = None` * changed some args from `obj['arg']` to `obj.get('arg')` if arg is optional * better PEP-8 compliance for less weak warnings * added tests for the new type `ChatInviteLink` --- telebot/__init__.py | 157 +++++++++++++++++++++++------------- telebot/types.py | 180 +++++++++++++++++++++--------------------- telebot/util.py | 35 +++++--- tests/test_telebot.py | 18 +++++ tests/test_types.py | 11 +++ 5 files changed, 245 insertions(+), 156 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index cb582dc4e..d5d2e1ac3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -9,6 +9,7 @@ import traceback from typing import List, Union +# this imports are used to avoid circular import error import telebot.util import telebot.types @@ -23,7 +24,7 @@ logger.setLevel(logging.ERROR) -from telebot import apihelper, types, util +from telebot import apihelper, util, types from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend """ @@ -252,19 +253,30 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ drop_pending_updates = None, timeout=None): """ Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an - update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update. - In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns True on success. + update for the bot, we will send an HTTPS POST request to the specified url, + containing a JSON-serialized Update. + In case of an unsuccessful request, we will give up after a reasonable amount of attempts. + Returns True on success. :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration - :param certificate: Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details. - :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. - :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. - :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS + :param certificate: Upload your public key certificate so that the root certificate in use can be checked. + See our self-signed guide for details. + :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook + for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, + and higher values to increase your bot's throughput. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates + of these types. See Update for a complete list of available update types. + Specify an empty list to receive all updates regardless of type (default). + If not specified, the previous setting will be used. + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address + resolved through DNS :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout :return: """ - return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout) + return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, + drop_pending_updates, timeout) def delete_webhook(self, drop_pending_updates=None, timeout=None): """ @@ -330,14 +342,14 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20): if self.skip_pending: logger.debug('Skipped {0} pending messages'.format(self.__skip_updates())) self.skip_pending = False - updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout, long_polling_timeout = long_polling_timeout) + updates = self.get_updates(offset=(self.last_update_id + 1), + timeout=timeout, long_polling_timeout=long_polling_timeout) self.process_new_updates(updates) def process_new_updates(self, updates): upd_count = len(updates) logger.debug('Received {0} new updates'.format(upd_count)) - if (upd_count == 0): - return + if upd_count == 0: return new_messages = None new_edited_messages = None @@ -488,11 +500,13 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log :param timeout: Request connection timeout :param long_polling_timeout: Timeout in seconds for long polling (see API docs) - :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging + :param logger_level: Custom logging level for infinity_polling logging. + Use logger levels from logging as a value. None/NOTSET = no error logging """ while not self.__stop_polling.is_set(): try: - self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs) + self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, + *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) @@ -590,7 +604,7 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po self.worker_pool.clear_exceptions() #* logger.info('Stopped polling.') - def __non_threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None): + def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None): logger.info('Started polling.') self.__stop_polling.clear() error_interval = 0.25 @@ -675,7 +689,8 @@ def download_file(self, file_path) -> bytes: def log_out(self) -> bool: """ Use this method to log out from the cloud Bot API server before launching the bot locally. - You MUST log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. + You MUST log out the bot before running it locally, otherwise there is no guarantee + that the bot will receive updates. After a successful call, you can immediately log in on a local server, but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns True on success. @@ -685,13 +700,13 @@ def log_out(self) -> bool: def close(self) -> bool: """ Use this method to close the bot instance before moving it from one local server to another. - You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. + You need to delete the webhook before calling this method to ensure that the bot isn't launched again + after server restart. The method will return error 429 in the first 10 minutes after the bot is launched. Returns True on success. """ return apihelper.close(self.token) - def get_user_profile_photos(self, user_id, offset=None, limit=None) -> types.UserProfilePhotos: """ Retrieves the user profile photos of the person with 'user_id' @@ -807,7 +822,8 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout)) - def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None) -> types.Message: + def forward_message(self, chat_id, from_chat_id, message_id, + disable_notification=None, timeout=None) -> types.Message: """ Use this method to forward messages of any kind. :param disable_notification: @@ -897,7 +913,8 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None) -> types.Message: """ - Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. + Use this method to send audio files, if you want Telegram clients to display them in the music player. + Your audio must be in the .mp3 format. :param chat_id:Unique identifier for the message recipient :param audio:Audio file to send. :param caption: @@ -909,7 +926,7 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None :param parse_mode :param disable_notification: :param timeout: - :param thumb: + :param thumb: :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -921,7 +938,8 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None) -> types.Message: """ - Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. + Use this method to send audio files, if you want Telegram clients to display the file + as a playable voice message. :param chat_id:Unique identifier for the message recipient. :param voice: :param caption: @@ -984,7 +1002,8 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram server + :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend + a video that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds :param caption: String : Video caption (may also be used when resending videos by file_id). :param parse_mode: @@ -993,7 +1012,7 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param reply_markup: :param disable_notification: :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent + :param thumb: InputFile or String : Thumbnail of the file sent :param width: :param height: :return: @@ -1011,7 +1030,8 @@ def send_animation(self, chat_id, animation, duration=None, """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server + :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an + animation that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds :param caption: String : Animation caption (may also be used when resending animation by file_id). :param parse_mode: @@ -1025,16 +1045,18 @@ def send_animation(self, chat_id, animation, duration=None, parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( - apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout, thumb)) + apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, thumb)) def send_video_note(self, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None, thumb=None) -> types.Message: """ - As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. + As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send + video messages. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server + :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend + a video that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) :param reply_to_message_id: @@ -1133,7 +1155,8 @@ def send_venue( :param title: String : Name of the venue :param address: String : Address of the venue :param foursquare_id: String : Foursquare identifier of the venue - :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) + :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, + “arts_entertainment/aquarium” or “food/icecream”.) :param disable_notification: :param reply_to_message_id: :param reply_markup: @@ -1162,7 +1185,8 @@ def send_chat_action(self, chat_id, action, timeout=None) -> bool: its typing status). :param chat_id: :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', - 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', 'upload_video_note'. + 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', + 'upload_video_note'. :param timeout: :return: API reply. :type: boolean """ @@ -1187,7 +1211,8 @@ def unban_chat_member(self, chat_id, user_id, only_if_banned = False) -> bool: the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. - :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username) + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel + (in the format @username) :param user_id: Unique identifier of the target user :param only_if_banned: Do nothing if the user is not banned :return: True on success @@ -1219,9 +1244,10 @@ def restrict_chat_member( use inline bots, implies can_send_media_messages :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, implies can_send_media_messages - :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups - :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, - implies can_invite_users + :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. + Ignored in public supergroups + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, + implies can_invite_users :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups :return: True on success """ @@ -1463,9 +1489,13 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ return result return types.Message.de_json(result) - def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: + def edit_message_media(self, media, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: """ - Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. + Use this method to edit animation, audio, document, photo, or video messages. + If a message is a part of a message album, then it can be edited only to a photo or a video. + Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. + Use previously uploaded file via its file_id or specify a URL. :param media: :param chat_id: :param message_id: @@ -1478,7 +1508,8 @@ def edit_message_media(self, media, chat_id=None, message_id=None, inline_messag return result return types.Message.de_json(result) - def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: + def edit_message_reply_markup(self, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. :param chat_id: @@ -1523,13 +1554,14 @@ def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=No :param disable_edit_message: :return: """ - result = apihelper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, message_id, - inline_message_id) + result = apihelper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, + message_id, inline_message_id) if type(result) == bool: return result return types.Message.de_json(result) - def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None) -> List[types.GameHighScore]: + def get_game_high_scores(self, user_id, chat_id=None, + message_id=None, inline_message_id=None) -> List[types.GameHighScore]: """ Gets top points and game play :param user_id: @@ -1555,12 +1587,17 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to :param chat_id: Unique identifier for the target private chat :param title: Product name :param description: Product description - :param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, + use for your internal processes. :param provider_token: Payments provider token, obtained via @Botfather - :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies - :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) - :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter - :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for. + :param currency: Three-letter ISO 4217 currency code, + see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components + (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice + when used as a start parameter + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods + or a marketing image for a service. People like it better when they see what they are paying for. :param photo_size: Photo size :param photo_width: Photo width :param photo_height: Photo height @@ -1573,8 +1610,10 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to :param send_email_to_provider: Pass True, if user's email address should be sent to provider :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: 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 - :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 reply_markup: 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 + :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: :return: """ @@ -1651,7 +1690,7 @@ def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=Non :param ok: :param error_message: :return: - """ + """ return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, @@ -1677,7 +1716,7 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me def reply_to(self, message, text, **kwargs) -> types.Message: """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` - :param message: + :param message: :param text: :param kwargs: :return: @@ -1691,11 +1730,14 @@ def answer_inline_query(self, inline_query_id, results, cache_time=None, is_pers No more than 50 results per query are allowed. :param inline_query_id: Unique identifier for the answered query :param results: Array of results for the inline query - :param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server. - :param is_personal: Pass True, if results may be cached on the server side only for the user that sent the query. - :param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results. + :param cache_time: The maximum amount of time in seconds that the result of the inline query + may be cached on the server. + :param is_personal: Pass True, if results may be cached on the server side only for + the user that sent the query. + :param next_offset: Pass the offset that a client should send in the next query with the same text + to receive more results. :param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user - to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter + to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button :return: True means success. """ @@ -1973,18 +2015,21 @@ def command_help(message): bot.send_message(message.chat.id, 'Did someone call for help?') # Handle all sent documents of type 'text/plain'. - @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document']) + @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', + content_types=['document']) def command_handle_document(message): bot.send_message(message.chat.id, 'Document received, sir!') # Handle all other messages. - @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', 'text', 'location', 'contact', 'sticker']) + @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', + 'text', 'location', 'contact', 'sticker']) def default_command(message): bot.send_message(message.chat.id, "This is the default command handler.") :param commands: Optional list of strings (commands to handle). :param regexp: Optional regular expression. - :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. + :param func: Optional lambda function. The lambda receives the message to test as the first parameter. + It must return True if the command should handle the message. :param content_types: Supported message content types. Must be a list. Defaults to ['text']. """ diff --git a/telebot/types.py b/telebot/types.py index 6ecb75da1..91393742a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import logging -from typing import Dict, List +from typing import Dict, List, Union try: import ujson as json @@ -92,7 +92,7 @@ def __str__(self): class Update(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) update_id = obj['update_id'] message = Message.de_json(obj.get('message')) @@ -128,7 +128,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan class WebhookInfo(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) url = obj['url'] has_custom_certificate = obj['has_custom_certificate'] @@ -156,7 +156,7 @@ def __init__(self, url, has_custom_certificate, pending_update_count, ip_address class User(JsonDeserializable, Dictionaryable, JsonSerializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -197,7 +197,7 @@ def to_dict(self): class GroupChat(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -249,8 +249,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, class MessageID(JsonDeserializable): @classmethod def de_json(cls, json_string): - if(json_string is None): - return None + if json_string is None: return None obj = cls.check_json(json_string) message_id = obj['message_id'] return cls(message_id) @@ -262,7 +261,7 @@ def __init__(self, message_id): class Message(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) message_id = obj['message_id'] from_user = User.de_json(obj.get('from')) @@ -306,7 +305,8 @@ def de_json(cls, json_string): opts['document'] = Document.de_json(obj['document']) content_type = 'document' if 'animation' in obj: - # Document content type accompanies "animation", so "animation" should be checked below "document" to override it + # Document content type accompanies "animation", + # so "animation" should be checked below "document" to override it opts['animation'] = Animation.de_json(obj['animation']) content_type = 'animation' if 'game' in obj: @@ -424,49 +424,49 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.from_user: User = from_user self.date: int = date self.chat: Chat = chat - self.forward_from: User = None - self.forward_from_chat: Chat = None - self.forward_from_message_id: int = None - self.forward_signature: str = None - self.forward_sender_name: str = None - self.forward_date: int = None - self.reply_to_message: Message = None - self.via_bot: User = None - self.edit_date: int = None - self.media_group_id: str = None - self.author_signature: str = None - self.text: str = None - self.entities: List[MessageEntity] = None - self.caption_entities: List[MessageEntity] = None - self.audio: Audio = None - self.document: Document = None - self.photo: List[PhotoSize] = None - self.sticker: Sticker = None - self.video: Video = None - self.video_note: VideoNote = None - self.voice: Voice = None - self.caption: str = None - self.contact: Contact = None - self.location: Location = None - self.venue: Venue = None - self.animation: Animation = None - self.dice: Dice = None - self.new_chat_member: User = None # Deprecated since Bot API 3.0. Not processed anymore - self.new_chat_members: List[User] = None - self.left_chat_member: User = None - self.new_chat_title: str = None - self.new_chat_photo: List[PhotoSize] = None - self.delete_chat_photo: bool = None - self.group_chat_created: bool = None - self.supergroup_chat_created: bool = None - self.channel_chat_created: bool = None - self.migrate_to_chat_id: int = None - self.migrate_from_chat_id: int = None - self.pinned_message: Message = None - self.invoice: Invoice = None - self.successful_payment: SuccessfulPayment = None - self.connected_website: str = None - self.reply_markup: InlineKeyboardMarkup = None + self.forward_from: Union[User, None] = None + self.forward_from_chat: Union[Chat, None] = None + self.forward_from_message_id: Union[int, None] = None + self.forward_signature: Union[str, None] = None + self.forward_sender_name: Union[str, None] = None + self.forward_date: Union[int, None] = None + self.reply_to_message: Union[Message, None] = None + self.via_bot: Union[User, None] = None + self.edit_date: Union[int, None] = None + self.media_group_id: Union[str, None] = None + self.author_signature: Union[str, None] = None + self.text: Union[str, None] = None + self.entities: Union[List[MessageEntity], None] = None + self.caption_entities: Union[List[MessageEntity], None] = None + self.audio: Union[Audio, None] = None + self.document: Union[Document, None] = None + self.photo: Union[List[PhotoSize], None] = None + self.sticker: Union[Sticker, None] = None + self.video: Union[Video, None] = None + self.video_note: Union[VideoNote, None] = None + self.voice: Union[Voice, None] = None + self.caption: Union[str, None] = None + self.contact: Union[Contact, None] = None + self.location: Union[Location, None] = None + self.venue: Union[Venue, None] = None + self.animation: Union[Animation, None] = None + self.dice: Union[Dice, None] = None + self.new_chat_member: Union[User, None] = None # Deprecated since Bot API 3.0. Not processed anymore + self.new_chat_members: Union[List[User], None] = None + self.left_chat_member: Union[User, None] = None + self.new_chat_title: Union[str, None] = None + self.new_chat_photo: Union[List[PhotoSize], None] = None + self.delete_chat_photo: Union[bool, None] = None + self.group_chat_created: Union[bool, None] = None + self.supergroup_chat_created: Union[bool, None] = None + self.channel_chat_created: Union[bool, None] = None + self.migrate_to_chat_id: Union[int, None] = None + self.migrate_from_chat_id: Union[int, None] = None + self.pinned_message: Union[Message, None] = None + self.invoice: Union[Invoice, None] = None + self.successful_payment: Union[SuccessfulPayment, None] = None + self.connected_website: Union[str, None] = None + self.reply_markup: Union[InlineKeyboardMarkup, None] = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -481,7 +481,7 @@ def __html_text(self, text, entities): message.html_text >> "Test parse formatting, url, text_mention and mention @username" - Cusom subs: + Custom subs: You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. Example: message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} @@ -493,15 +493,15 @@ def __html_text(self, text, entities): return text _subs = { - "bold" : "{text}", - "italic" : "{text}", - "pre" : "
{text}
", - "code" : "{text}", - #"url" : "{text}", # @badiboy plain URLs have no text and do not need tags + "bold": "{text}", + "italic": "{text}", + "pre": "
{text}
", + "code": "{text}", + # "url": "{text}", # @badiboy plain URLs have no text and do not need tags "text_link": "{text}", "strikethrough": "{text}", "underline": "{text}" - } + } if hasattr(self, "custom_subs"): for key, value in self.custom_subs.items(): @@ -551,11 +551,12 @@ def html_caption(self): class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): @staticmethod - def to_list_of_dicts(entity_list) -> List[Dict]: + def to_list_of_dicts(entity_list) -> Union[List[Dict], None]: res = [] for e in entity_list: res.append(MessageEntity.to_dict(e)) return res or None + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -587,7 +588,7 @@ def to_dict(self): class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -606,7 +607,7 @@ def to_dict(self): class PhotoSize(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -621,7 +622,7 @@ def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwa class Audio(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) if 'thumb' in obj and 'file_id' in obj['thumb']: obj['thumb'] = PhotoSize.de_json(obj['thumb']) @@ -645,7 +646,7 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None class Voice(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -682,7 +683,7 @@ class Video(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if ('thumb' in obj and 'file_id' in obj['thumb']): + if 'thumb' in obj and 'file_id' in obj['thumb']: obj['thumb'] = PhotoSize.de_json(obj['thumb']) return cls(**obj) @@ -703,7 +704,7 @@ class VideoNote(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if ('thumb' in obj and 'file_id' in obj['thumb']): + if 'thumb' in obj and 'file_id' in obj['thumb']: obj['thumb'] = PhotoSize.de_json(obj['thumb']) return cls(**obj) @@ -738,8 +739,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, longitude: float, latitude: float, horizontal_accuracy:float=None, - live_period: int=None, heading: int=None, proximity_alert_radius: int=None, **kwargs): + def __init__(self, longitude, latitude, horizontal_accuracy=None, + live_period=None, heading=None, proximity_alert_radius=None, **kwargs): self.longitude: float = longitude self.latitude: float = latitude self.horizontal_accuracy: float = horizontal_accuracy @@ -766,7 +767,7 @@ class Venue(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['location'] = Location.de_json(obj['location']) + obj['location'] = Location.de_json(obj.get('location')) return cls(**obj) def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, @@ -785,11 +786,12 @@ class UserProfilePhotos(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] - obj['photos'] = photos + if 'photos' in obj: + photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] + obj['photos'] = photos return cls(**obj) - def __init__(self, total_count, photos, **kwargs): + def __init__(self, total_count, photos=None, **kwargs): self.total_count: int = total_count self.photos: List[PhotoSize] = photos @@ -859,8 +861,7 @@ def add(self, *args, row_width=None): """ if row_width is None: row_width = self.row_width - - + if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases if not DISABLE_KEYLEN_ERROR: @@ -946,7 +947,7 @@ def de_json(cls, json_string): keyboard = [[InlineKeyboardButton.de_json(button) for button in row] for row in obj['inline_keyboard']] return cls(keyboard) - def __init__(self, keyboard=None ,row_width=3): + def __init__(self, keyboard=None, row_width=3): """ This object represents an inline keyboard that appears right next to the message it belongs to. @@ -993,7 +994,7 @@ def add(self, *args, row_width=None): def row(self, *args): """ Adds a list of InlineKeyboardButton to the keyboard. - This metod does not consider row_width. + This method does not consider row_width. InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: '{keyboard: [["A"], ["B", "C"]]}' @@ -1255,8 +1256,6 @@ def __init__(self, id, from_user, query, offset, chat_type=None, location=None, self.offset: str = offset self.chat_type: str = chat_type self.location: Location = location - - class InputTextMessageContent(Dictionaryable): @@ -1337,7 +1336,7 @@ def __init__(self, phone_number, first_name, last_name=None, vcard=None): self.vcard: str = vcard def to_dict(self): - json_dict = {'phone_numbe': self.phone_number, 'first_name': self.first_name} + json_dict = {'phone_number': self.phone_number, 'first_name': self.first_name} if self.last_name: json_dict['last_name'] = self.last_name if self.vcard: @@ -2043,7 +2042,10 @@ class Animation(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - obj["thumb"] = PhotoSize.de_json(obj['thumb']) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj["thumb"] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None return cls(**obj) def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, @@ -2122,10 +2124,10 @@ class OrderInfo(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) + obj['shipping_address'] = ShippingAddress.de_json(obj.get('shipping_address')) return cls(**obj) - def __init__(self, name, phone_number, email, shipping_address, **kwargs): + def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs): self.name: str = name self.phone_number: str = phone_number self.email: str = email @@ -2160,8 +2162,7 @@ class SuccessfulPayment(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - if 'order_info' in obj: - obj['order_info'] = OrderInfo.de_json(obj['order_info']) + obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) return cls(**obj) def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, @@ -2197,8 +2198,7 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) obj['from_user'] = User.de_json(obj.pop('from')) - if 'order_info' in obj: - obj['order_info'] = OrderInfo.de_json(obj['order_info']) + obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) return cls(**obj) def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs): @@ -2473,6 +2473,7 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) + # Strange name, i think it should be `option_ids` not `options_ids` maybe replace that obj['options_ids'] = obj.pop('option_ids') return cls(**obj) @@ -2487,6 +2488,7 @@ def to_json(self): def to_dict(self): return {'poll_id': self.poll_id, 'user': self.user.to_dict(), + #should be `option_ids` not `options_ids` could cause problems here 'options_ids': self.options_ids} @@ -2498,7 +2500,7 @@ def de_json(cls, json_string): obj['location'] = Location.de_json(obj['location']) return cls(**obj) - def __init__(self, location: Location, address: str, **kwargs): + def __init__(self, location, address, **kwargs): self.location: Location = location self.address: str = address @@ -2520,8 +2522,8 @@ def de_json(cls, json_string): obj['creator'] = User.de_json(obj['creator']) return cls(**obj) - def __init__(self, invite_link: str, creator: User, is_primary: bool, is_revoked: bool, - expire_date: int=None, member_limit: int=None, **kwargs): + def __init__(self, invite_link, creator, is_primary, is_revoked, + expire_date=None, member_limit=None, **kwargs): self.invite_link: str = invite_link self.creator: User = creator self.is_primary: bool = is_primary diff --git a/telebot/util.py b/telebot/util.py index beb9e9091..7b879598e 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -6,7 +6,7 @@ import traceback import warnings import functools -from typing import Any, List, Dict +from typing import Any, List, Dict, Union import queue as Queue import logging @@ -36,6 +36,7 @@ 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message' ] + class WorkerThread(threading.Thread): count = 0 @@ -170,15 +171,19 @@ def wrapper(*args, **kwargs): def is_string(var): return isinstance(var, str) + def is_dict(var): return isinstance(var, dict) + def is_bytes(var): return isinstance(var, bytes) + def is_pil_image(var): return pil_imported and isinstance(var, Image.Image) + def pil_image_to_file(image, extension='JPEG', quality='web_low'): if pil_imported: photoBuffer = BytesIO() @@ -189,17 +194,18 @@ def pil_image_to_file(image, extension='JPEG', quality='web_low'): else: raise RuntimeError('PIL module is not imported') + def is_command(text: str) -> bool: """ Checks if `text` is a command. Telegram chat commands start with the '/' character. :param text: Text to check. :return: True if `text` is a command, else False. """ - if (text is None): return False + if text is None: return False return text.startswith('/') -def extract_command(text: str) -> str: +def extract_command(text: str) -> Union[str, None]: """ Extracts the command from `text` (minus the '/') if `text` is a command (see is_command). If `text` is not a command, this function returns None. @@ -213,7 +219,7 @@ def extract_command(text: str) -> str: :param text: String to extract the command from :return: the command if `text` is a command (according to is_command), else None. """ - if (text is None): return None + if text is None: return None return text.split()[0].split('@')[0][1:] if is_command(text) else None @@ -229,7 +235,7 @@ def extract_arguments(text: str) -> str: :param text: String to extract the arguments from a command :return: the arguments if `text` is a command (according to is_command), else None. """ - regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE) + regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)", re.IGNORECASE) result = regexp.match(text) return result.group(2) if is_command(text) else None @@ -247,16 +253,17 @@ def split_string(text: str, chars_per_string: int) -> List[str]: def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]: - f""" + """ Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. This is very useful for splitting one giant message into multiples. - If `chars_per_string` > {MAX_MESSAGE_LENGTH}: `chars_per_string` = {MAX_MESSAGE_LENGTH}. + If `chars_per_string` > 4096: `chars_per_string` = 4096. Splits by '\n', '. ' or ' ' in exactly this priority. :param text: The text to split :param chars_per_string: The number of maximum characters per part the text is split to. :return: The splitted text as a list of strings. """ + def _text_before_last(substr: str) -> str: return substr.join(part.split(substr)[:-1]) + substr @@ -270,9 +277,9 @@ def _text_before_last(substr: str) -> str: part = text[:chars_per_string] - if ("\n" in part): part = _text_before_last("\n") - elif (". " in part): part = _text_before_last(". ") - elif (" " in part): part = _text_before_last(" ") + if "\n" in part: part = _text_before_last("\n") + elif ". " in part: part = _text_before_last(". ") + elif " " in part: part = _text_before_last(" ") parts.append(part) text = text[len(part):] @@ -296,7 +303,7 @@ def user_link(user: types.User, include_id: bool=False) -> str: Attention: Don't forget to set parse_mode to 'HTML'! Example: - bot.send_message(your_user_id, user_link(message.from_user) + ' startet the bot!', parse_mode='HTML') + bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML') :param user: the user (not the user_id) :param include_id: include the user_id @@ -333,6 +340,7 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I } :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} + :param row_width: int row width :return: InlineKeyboardMarkup """ markup = types.InlineKeyboardMarkup(row_width=row_width) @@ -363,8 +371,10 @@ def orify(e, changed_callback): e.set = lambda: or_set(e) e.clear = lambda: or_clear(e) + def OrEvent(*events): or_event = threading.Event() + def changed(): bools = [ev.is_set() for ev in events] if any(bools): @@ -391,15 +401,18 @@ def per_thread(key, construct_value, reset=False): return getattr(thread_local, key) + def chunks(lst, n): """Yield successive n-sized chunks from lst.""" # https://stackoverflow.com/a/312464/9935473 for i in range(0, len(lst), n): yield lst[i:i + n] + def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) + def deprecated(func): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted diff --git a/tests/test_telebot.py b/tests/test_telebot.py index a70911ebc..e8f57a9ea 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -6,6 +6,7 @@ import time import pytest import os +from datetime import datetime, timedelta import telebot from telebot import types @@ -407,6 +408,23 @@ def test_get_chat_members_count(self): cn = tb.get_chat_members_count(GROUP_ID) assert cn > 1 + def test_export_chat_invite_link(self): + tb = telebot.TeleBot(TOKEN) + il = tb.export_chat_invite_link(GROUP_ID) + assert isinstance(il, str) + + def test_create_revoke_detailed_chat_invite_link(self): + tb = telebot.TeleBot(TOKEN) + cil = tb.create_chat_invite_link(GROUP_ID, + (datetime.now() + timedelta(minutes=1)).timestamp(), member_limit=5) + assert isinstance(cil.invite_link, str) + assert cil.creator.id == tb.get_me().id + assert isinstance(cil.expire_date, (float, int)) + assert cil.member_limit == 5 + assert not cil.is_revoked + rcil = tb.revoke_chat_invite_link(GROUP_ID, cil.invite_link) + assert rcil.is_revoked + def test_edit_markup(self): text = 'CI Test Message' tb = telebot.TeleBot(TOKEN) diff --git a/tests/test_types.py b/tests/test_types.py index 355f6909f..9ca73c91a 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -219,3 +219,14 @@ def test_KeyboardButtonPollType(): json_str = markup.to_json() assert 'request_poll' in json_str assert 'quiz' in json_str + + +def test_json_chat_invite_link(): + json_string = r'{"invite_link": "https://t.me/joinchat/z-abCdEFghijKlMn", "creator": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "is_primary": false, "is_revoked": false, "expire_date": 1624119999, "member_limit": 10}' + invite_link = types.ChatInviteLink.de_json(json_string) + assert invite_link.invite_link == 'https://t.me/joinchat/z-abCdEFghijKlMn' + assert isinstance(invite_link.creator, types.User) + assert not invite_link.is_primary + assert not invite_link.is_revoked + assert invite_link.expire_date == 1624119999 + assert invite_link.member_limit == 10 \ No newline at end of file From 0370a9f277552f2416e23b9894e08f78e02ba298 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Sat, 19 Jun 2021 20:13:53 +0200 Subject: [PATCH 0588/1808] Added class ChatMemberUpdated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added class `ChatMemberUpdated` to types * Simplified `de_json` functions in `WebhookInfo` and `Update` classes (for overall more consistent code) * changed `options_ids` to ´option_id` in class `PollAnswer` * Added test for `ChatMemberUpdated` class in `test_types.py` and added the fields `my_chat_member` and `chat_member` to the `Update` class and its tests --- telebot/types.py | 83 ++++++++++++++++++++-------------- tests/test_handler_backends.py | 11 +++-- tests/test_telebot.py | 5 +- tests/test_types.py | 14 +++++- 4 files changed, 72 insertions(+), 41 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 91393742a..289849393 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -94,23 +94,24 @@ class Update(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - update_id = obj['update_id'] - message = Message.de_json(obj.get('message')) - edited_message = Message.de_json(obj.get('edited_message')) - channel_post = Message.de_json(obj.get('channel_post')) - edited_channel_post = Message.de_json(obj.get('edited_channel_post')) - inline_query = InlineQuery.de_json(obj.get('inline_query')) - chosen_inline_result = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) - callback_query = CallbackQuery.de_json(obj.get('callback_query')) - shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) - pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) - poll = Poll.de_json(obj.get('poll')) - poll_answer = PollAnswer.de_json(obj.get('poll_answer')) - 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) + obj['message'] = Message.de_json(obj.get('message')) + obj['edited_message'] = Message.de_json(obj.get('edited_message')) + obj['channel_post'] = Message.de_json(obj.get('channel_post')) + obj['edited_channel_post'] = Message.de_json(obj.get('edited_channel_post')) + obj['inline_query'] = InlineQuery.de_json(obj.get('inline_query')) + obj['chosen_inline_result'] = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) + obj['callback_query'] = CallbackQuery.de_json(obj.get('callback_query')) + obj['shipping_query'] = ShippingQuery.de_json(obj.get('shipping_query')) + obj['pre_checkout_query'] = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) + obj['poll'] = Poll.de_json(obj.get('poll')) + obj['poll_answer'] = PollAnswer.de_json(obj.get('poll_answer')) + obj['my_chat_member'] = ChatMemberUpdated.de_json(obj.get('my_chat_member')) + obj['chat_member'] = ChatMemberUpdated.de_json(obj.get('chat_member')) + return cls(**obj) 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): + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer, + my_chat_member, chat_member, **kwargs): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -123,6 +124,29 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.pre_checkout_query = pre_checkout_query self.poll = poll self.poll_answer = poll_answer + self.my_chat_member = my_chat_member + self.chat_member = chat_member + + +class ChatMemberUpdated(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['old_chat_member'] = ChatMember.de_json(obj['old_chat_member']) + obj['new_chat_member'] = ChatMember.de_json(obj['new_chat_member']) + 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, **kwargs): + self.chat: Chat = chat + self.from_user: User = from_user + self.date: int = date + self.old_chat_member: ChatMember = old_chat_member + self.new_chat_member: ChatMember = new_chat_member + self.invite_link: Union[ChatInviteLink, None] = invite_link class WebhookInfo(JsonDeserializable): @@ -130,19 +154,11 @@ class WebhookInfo(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - url = obj['url'] - has_custom_certificate = obj['has_custom_certificate'] - pending_update_count = obj['pending_update_count'] - ip_address = obj.get('ip_address') - last_error_date = obj.get('last_error_date') - last_error_message = obj.get('last_error_message') - max_connections = obj.get('max_connections') - allowed_updates = obj.get('allowed_updates') - return cls(url, has_custom_certificate, pending_update_count, ip_address, last_error_date, - last_error_message, max_connections, allowed_updates) - - def __init__(self, url, has_custom_certificate, pending_update_count, ip_address, last_error_date, - last_error_message, max_connections, allowed_updates): + return cls(**obj) + + def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, + last_error_date=None, last_error_message=None, max_connections=None, + allowed_updates=None, **kwargs): self.url = url self.has_custom_certificate = has_custom_certificate self.pending_update_count = pending_update_count @@ -767,7 +783,7 @@ class Venue(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['location'] = Location.de_json(obj.get('location')) + obj['location'] = Location.de_json(obj['location']) return cls(**obj) def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, @@ -2473,14 +2489,12 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) - # Strange name, i think it should be `option_ids` not `options_ids` maybe replace that - obj['options_ids'] = obj.pop('option_ids') return cls(**obj) - def __init__(self, poll_id, user, options_ids, **kwargs): + def __init__(self, poll_id, user, option_ids, **kwargs): self.poll_id: str = poll_id self.user: User = user - self.options_ids: List[int] = options_ids + self.option_ids: List[int] = option_ids def to_json(self): return json.dumps(self.to_dict()) @@ -2488,8 +2502,7 @@ def to_json(self): def to_dict(self): return {'poll_id': self.poll_id, 'user': self.user.to_dict(), - #should be `option_ids` not `options_ids` could cause problems here - 'options_ids': self.options_ids} + 'option_ids': self.option_ids} class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index 9605900da..638cb2758 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -62,8 +62,11 @@ def update_type(message): pre_checkout_query = None poll = None poll_answer = None + my_chat_member = None + chat_member = None return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer, + my_chat_member, chat_member) @pytest.fixture() @@ -78,9 +81,11 @@ def reply_to_message_update_type(reply_to_message): pre_checkout_query = None poll = None poll_answer = None + my_chat_member = None + chat_member = None return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post, - inline_query, - chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer) + inline_query, chosen_inline_result, callback_query, shipping_query, pre_checkout_query, + poll, poll_answer, my_chat_member, chat_member) def next_handler(message): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index e8f57a9ea..a22adcda0 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -458,8 +458,11 @@ def create_message_update(text): pre_checkout_query = None poll = None poll_answer = None + my_chat_member = None + chat_member = None return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query, - chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer) + chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer, + my_chat_member, chat_member) def test_is_string_unicode(self): s1 = u'string' diff --git a/tests/test_types.py b/tests/test_types.py index 9ca73c91a..417a67827 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -210,7 +210,7 @@ def test_json_poll_answer(): poll_answer = types.PollAnswer.de_json(jsonstring) assert poll_answer.poll_id == '5895675970559410186' assert isinstance(poll_answer.user, types.User) - assert poll_answer.options_ids == [1] + assert poll_answer.option_ids == [1] def test_KeyboardButtonPollType(): @@ -229,4 +229,14 @@ def test_json_chat_invite_link(): assert not invite_link.is_primary assert not invite_link.is_revoked assert invite_link.expire_date == 1624119999 - assert invite_link.member_limit == 10 \ No newline at end of file + assert invite_link.member_limit == 10 + +def test_chat_member_updated(): + json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "administrator"}}' + cm_updated = types.ChatMemberUpdated.de_json(json_string) + assert cm_updated.chat.id == -1234567890123 + assert cm_updated.from_user.id == 133869498 + assert cm_updated.date == 1624119999 + assert cm_updated.old_chat_member.status == "member" + assert cm_updated.new_chat_member.status == "administrator" + From 18f1fd42b034f76125036e07fc84c7b92525a2de Mon Sep 17 00:00:00 2001 From: Leon Heess Date: Sun, 20 Jun 2021 13:14:55 +0200 Subject: [PATCH 0589/1808] Add Anti-Tracking Bot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c19529adc..cfbe1496e 100644 --- a/README.md +++ b/README.md @@ -687,6 +687,6 @@ Get help. Discuss. Chat. * [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. * Translator bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. * Digital Cryptocurrency bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/currencies_bot). With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. - +* [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by [Leon Heess (source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. **Want to have your bot listed here? Just make a pull request.** From 4146b5038464c9f878fcb07b9a48cc386c96c95d Mon Sep 17 00:00:00 2001 From: Vishal Singh <38159691+vishal2376@users.noreply.github.com> Date: Mon, 21 Jun 2021 13:16:22 +0530 Subject: [PATCH 0590/1808] Add developer bot --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index cfbe1496e..466d69d65 100644 --- a/README.md +++ b/README.md @@ -688,5 +688,10 @@ Get help. Discuss. Chat. * Translator bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. * Digital Cryptocurrency bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/currencies_bot). With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. * [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by [Leon Heess (source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. +* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot is specially created for 💻 coders and it can do following tasks + - Provide c++ learning resources 📚 + - GitHub(search,clone) + - Stackoverflow search 🔍 + - Codeforces(profile visualizer,random problems) **Want to have your bot listed here? Just make a pull request.** From 66598e39fe9ae79cd2e82b68fe94ba095d3f51c4 Mon Sep 17 00:00:00 2001 From: Vishal Singh <38159691+vishal2376@users.noreply.github.com> Date: Mon, 21 Jun 2021 16:27:32 +0530 Subject: [PATCH 0591/1808] Change description of developer bot --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 466d69d65..305537b56 100644 --- a/README.md +++ b/README.md @@ -688,10 +688,6 @@ Get help. Discuss. Chat. * Translator bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. * Digital Cryptocurrency bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/currencies_bot). With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. * [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by [Leon Heess (source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. -* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot is specially created for 💻 coders and it can do following tasks - - Provide c++ learning resources 📚 - - GitHub(search,clone) - - Stackoverflow search 🔍 - - Codeforces(profile visualizer,random problems) +* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub(search,clone),provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems) **Want to have your bot listed here? Just make a pull request.** From f11bf08ba1660e87572e37ca9cbb54a95ff1288b Mon Sep 17 00:00:00 2001 From: Vishal Singh <38159691+vishal2376@users.noreply.github.com> Date: Mon, 21 Jun 2021 16:30:17 +0530 Subject: [PATCH 0592/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 305537b56..7048b6cdd 100644 --- a/README.md +++ b/README.md @@ -688,6 +688,6 @@ Get help. Discuss. Chat. * Translator bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. * Digital Cryptocurrency bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/currencies_bot). With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. * [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by [Leon Heess (source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. -* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub(search,clone),provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems) +* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems) **Want to have your bot listed here? Just make a pull request.** From 7118613ef7ffeb6e196f046a61fd907092e8623d Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 21 Jun 2021 17:39:13 +0200 Subject: [PATCH 0593/1808] Added missing features * added some missing features of TelegramBotAPI 4.6-5.2 to pyTelegramBotAPI * added type hints to (almost) all public TeleBot functions --- telebot/__init__.py | 695 ++++++++++++++++++++++++++++++++----------- telebot/apihelper.py | 159 ++++++++-- telebot/types.py | 116 +++++++- telebot/util.py | 4 +- 4 files changed, 768 insertions(+), 206 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d5d2e1ac3..484b208b2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import print_function +from datetime import datetime import logging import re @@ -7,12 +8,14 @@ import threading import time import traceback -from typing import List, Union +from typing import Any, Callable, List, Optional, Union # this imports are used to avoid circular import error import telebot.util import telebot.types + + logger = logging.getLogger('TeleBot') formatter = logging.Formatter( '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' @@ -27,6 +30,12 @@ from telebot import apihelper, util, types from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend + +REPLY_MARKUP_TYPES = Union[ + types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, types.ForceReply] + + """ Module : telebot """ @@ -99,6 +108,7 @@ class TeleBot: getChatMembersCount getChatMember answerCallbackQuery + getMyCommands setMyCommands answerInlineQuery """ @@ -106,7 +116,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 + suppress_middleware_excepions=False # <- Typo in exceptions ): """ :param token: bot API token @@ -302,7 +312,9 @@ def get_webhook_info(self, timeout=None): def remove_webhook(self): return self.set_webhook() # No params resets webhook - def get_updates(self, offset=None, limit=None, timeout=20, allowed_updates=None, long_polling_timeout = 20): + def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, + timeout: Optional[int]=20, allowed_updates: Optional[List[str]]=None, + long_polling_timeout: int=20) -> List[types.Update]: """ Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. :param allowed_updates: Array of string. List the types of updates you want your bot to receive. @@ -670,7 +682,7 @@ def get_me(self) -> types.User: result = apihelper.get_me(self.token) return types.User.de_json(result) - def get_file(self, file_id) -> types.File: + def get_file(self, file_id: str) -> types.File: """ Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. @@ -680,10 +692,10 @@ def get_file(self, file_id) -> types.File: """ return types.File.de_json(apihelper.get_file(self.token, file_id)) - def get_file_url(self, file_id) -> str: + def get_file_url(self, file_id: str) -> str: return apihelper.get_file_url(self.token, file_id) - def download_file(self, file_path) -> bytes: + def download_file(self, file_path: str) -> bytes: return apihelper.download_file(self.token, file_path) def log_out(self) -> bool: @@ -707,7 +719,8 @@ def close(self) -> bool: """ return apihelper.close(self.token) - def get_user_profile_photos(self, user_id, offset=None, limit=None) -> types.UserProfilePhotos: + def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, + limit: Optional[int]=None) -> types.UserProfilePhotos: """ Retrieves the user profile photos of the person with 'user_id' See https://core.telegram.org/bots/api#getuserprofilephotos @@ -719,7 +732,7 @@ def get_user_profile_photos(self, user_id, offset=None, limit=None) -> types.Use result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit) return types.UserProfilePhotos.de_json(result) - def get_chat(self, chat_id) -> types.Chat: + def get_chat(self, chat_id: Union[int, str]) -> types.Chat: """ 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. @@ -729,7 +742,7 @@ def get_chat(self, chat_id) -> types.Chat: result = apihelper.get_chat(self.token, chat_id) return types.Chat.de_json(result) - def leave_chat(self, chat_id) -> bool: + def leave_chat(self, chat_id: Union[int, str]) -> bool: """ Use this method for your bot to leave a group, supergroup or channel. Returns True on success. :param chat_id: @@ -738,7 +751,7 @@ def leave_chat(self, chat_id) -> bool: result = apihelper.leave_chat(self.token, chat_id) return result - def get_chat_administrators(self, chat_id) -> List[types.ChatMember]: + def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMember]: """ Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains @@ -753,7 +766,7 @@ def get_chat_administrators(self, chat_id) -> List[types.ChatMember]: ret.append(types.ChatMember.de_json(r)) return ret - def get_chat_members_count(self, chat_id) -> int: + def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ Use this method to get the number of members in a chat. Returns Int on success. :param chat_id: @@ -762,7 +775,7 @@ def get_chat_members_count(self, chat_id) -> int: result = apihelper.get_chat_members_count(self.token, chat_id) return result - def set_chat_sticker_set(self, chat_id, sticker_set_name) -> types.StickerSet: + def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: """ Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -776,7 +789,7 @@ def set_chat_sticker_set(self, chat_id, sticker_set_name) -> types.StickerSet: result = apihelper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) return result - def delete_chat_sticker_set(self, chat_id) -> bool: + def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: """ Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set @@ -788,7 +801,7 @@ def delete_chat_sticker_set(self, chat_id) -> bool: result = apihelper.delete_chat_sticker_set(self.token, chat_id) return result - def get_chat_member(self, chat_id, user_id) -> types.ChatMember: + def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. :param chat_id: @@ -798,13 +811,22 @@ def get_chat_member(self, chat_id, user_id) -> types.ChatMember: result = apihelper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) - def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None) -> types.Message: + def send_message( + self, chat_id: Union[int, str], text: str, + disable_web_page_preview: Optional[bool]=None, + reply_to_message_id: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send text messages. Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 4000 characters, use the split_string function in apihelper.py. + If you must send more than 4000 characters, + use the `split_string` or `smart_split` function in util.py. :param chat_id: :param text: @@ -814,16 +836,22 @@ def send_message(self, chat_id, text, disable_web_page_preview=None, reply_to_me :param parse_mode: :param disable_notification: Boolean, Optional. Sends the message silently. :param timeout: + :param entities: + :param allow_sending_without_reply: :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( - apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout)) + apihelper.send_message( + self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, + entities, allow_sending_without_reply)) - def forward_message(self, chat_id, from_chat_id, message_id, - disable_notification=None, timeout=None) -> types.Message: + def forward_message( + self, chat_id: Union[int, str], from_chat_id: Union[int, str], + message_id: int, disable_notification: Optional[bool]=None, + timeout: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. :param disable_notification: @@ -836,9 +864,18 @@ def forward_message(self, chat_id, from_chat_id, message_id, return types.Message.de_json( apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) - def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, - disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None) -> int: + def copy_message( + self, chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None) -> int: """ Use this method to copy messages of any kind. :param chat_id: which chat to forward @@ -859,7 +896,8 @@ def copy_message(self, chat_id, from_chat_id, message_id, caption=None, parse_mo disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, timeout)) - def delete_message(self, chat_id, message_id, timeout=None) -> bool: + def delete_message(self, chat_id: Union[int, str], message_id: int, + timeout: Optional[int]=None) -> bool: """ Use this method to delete message. Returns True on success. :param chat_id: in which chat to delete @@ -870,9 +908,12 @@ def delete_message(self, chat_id, message_id, timeout=None) -> bool: return apihelper.delete_message(self.token, chat_id, message_id, timeout) def send_dice( - self, chat_id, - emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None) -> types.Message: + self, chat_id: Union[int, str], + emoji: Optional[str]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send dices. :param chat_id: @@ -881,37 +922,57 @@ def send_dice( :param reply_to_message_id: :param reply_markup: :param timeout: + :param allow_sending_without_reply: :return: Message """ return types.Message.de_json( apihelper.send_dice( self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout) + reply_markup, timeout, allow_sending_without_reply) ) - def send_photo(self, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None) -> types.Message: + def send_photo( + self, chat_id: Union[int, str], photo: Union[Any, str], + caption: Optional[str]=None, reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send photos. - :param disable_notification: :param chat_id: :param photo: :param caption: - :param parse_mode + :param parse_mode: + :param disable_notification: :param reply_to_message_id: :param reply_markup: :param timeout: + :param caption_entities: + :param allow_sending_without_reply: :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( - apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout)) - - def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None, title=None, - reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, - timeout=None, thumb=None) -> types.Message: + apihelper.send_photo( + self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, + parse_mode, disable_notification, timeout, caption_entities, + allow_sending_without_reply)) + + def send_audio( + self, chat_id: Union[int, str], audio: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, + performer: Optional[str]=None, title: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -927,16 +988,28 @@ def send_audio(self, chat_id, audio, caption=None, duration=None, performer=None :param disable_notification: :param timeout: :param thumb: + :param caption_entities: + :param allow_sending_without_reply: :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( - apihelper.send_audio(self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb)) - - def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None) -> types.Message: + apihelper.send_audio( + self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, thumb, + caption_entities, allow_sending_without_reply)) + + def send_voice( + self, chat_id: Union[int, str], voice: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -949,16 +1022,29 @@ def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_messa :param parse_mode :param disable_notification: :param timeout: + :param caption_entities: + :param allow_sending_without_reply: :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( - apihelper.send_voice(self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout)) - - def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None, thumb=None) -> types.Message: + apihelper.send_voice( + self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, + parse_mode, disable_notification, timeout, caption_entities, + allow_sending_without_reply)) + + def send_document( + self, chat_id: Union[int, str], data: Union[Any, str], + reply_to_message_id: Optional[int]=None, + caption: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send general files. :param chat_id: @@ -970,17 +1056,25 @@ def send_document(self, chat_id, data,reply_to_message_id=None, caption=None, re :param disable_notification: :param timeout: :param thumb: InputFile or String : Thumbnail of the file sent + :param caption_entities: + :param allow_sending_without_reply: :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)) + 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)) def send_sticker( - self, chat_id, data, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None) -> types.Message: + self, chat_id: Union[int, str], data: Union[Any, str], + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send .webp stickers. :param chat_id: @@ -989,16 +1083,31 @@ def send_sticker( :param reply_markup: :param disable_notification: to disable the notification :param timeout: timeout + :param allow_sending_without_reply: :return: API reply. """ return types.Message.de_json( apihelper.send_data( - self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup, - disable_notification, timeout)) - - def send_video(self, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, - width=None, height=None) -> types.Message: + self.token, chat_id=chat_id, data=data, data_type='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)) + + def send_video( + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, + caption: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + supports_streaming: Optional[bool]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + width: Optional[int]=None, + height: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -1015,18 +1124,30 @@ def send_video(self, chat_id, data, duration=None, caption=None, reply_to_messag :param thumb: InputFile or String : Thumbnail of the file sent :param width: :param height: + :param caption_entities: + :param allow_sending_without_reply: :return: """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( - apihelper.send_video(self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, - parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height)) - - def send_animation(self, chat_id, animation, duration=None, - caption=None, reply_to_message_id=None, - reply_markup=None, parse_mode=None, - disable_notification=None, timeout=None, thumb=None) -> types.Message: + apihelper.send_video( + self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, + parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, + caption_entities, allow_sending_without_reply)) + + def send_animation( + self, chat_id: Union[int, str], animation: Union[Any, str], + duration: Optional[int]=None, + caption: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -1040,17 +1161,28 @@ def send_animation(self, chat_id, animation, duration=None, :param disable_notification: :param timeout: :param thumb: InputFile or String : Thumbnail of the file sent + :param caption_entities: + :param allow_sending_without_reply: :return: """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode return types.Message.de_json( - apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb)) - - def send_video_note(self, chat_id, data, duration=None, length=None, - reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None) -> types.Message: + apihelper.send_animation( + self.token, chat_id, animation, duration, caption, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, thumb, + caption_entities, allow_sending_without_reply)) + + def send_video_note( + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, + length: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. @@ -1064,15 +1196,23 @@ def send_video_note(self, chat_id, data, duration=None, length=None, :param disable_notification: :param timeout: :param thumb: InputFile or String : Thumbnail of the file sent + :param allow_sending_without_reply: :return: """ return types.Message.de_json( - apihelper.send_video_note(self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb)) + apihelper.send_video_note( + self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, + disable_notification, timeout, thumb, allow_sending_without_reply)) def send_media_group( - self, chat_id, media, - disable_notification=None, reply_to_message_id=None, timeout=None) -> List[types.Message]: + self, chat_id: Union[int, str], + media: List[Union[ + types.InputMediaAudio, types.InputMediaDocument, + types.InputMediaPhoto, types.InputMediaVideo]], + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: """ send a group of photos or videos as an album. On success, an array of the sent Messages is returned. :param chat_id: @@ -1080,56 +1220,90 @@ def send_media_group( :param disable_notification: :param reply_to_message_id: :param timeout: + :param allow_sending_without_reply: :return: """ result = apihelper.send_media_group( - self.token, chat_id, media, disable_notification, reply_to_message_id, timeout) + self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, + allow_sending_without_reply) ret = [] for msg in result: ret.append(types.Message.de_json(msg)) return ret def send_location( - self, chat_id, latitude, longitude, live_period=None, reply_to_message_id=None, - reply_markup=None, disable_notification=None, timeout=None) -> types.Message: + self, chat_id: Union[int, str], + latitude: float, longitude: float, + live_period: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, + proximity_alert_radius: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + + """ Use this method to send point on the map. :param chat_id: :param latitude: :param longitude: - :param live_period + :param live_period: :param reply_to_message_id: :param reply_markup: :param disable_notification: :param timeout: + :param horizontal_accuracy: + :param heading: + :param proximity_alert_radius: + :param allow_sending_without_reply: :return: API reply. """ return types.Message.de_json( apihelper.send_location( - self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, - reply_markup, disable_notification, timeout)) - - def edit_message_live_location(self, latitude, longitude, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None) -> types.Message: + self.token, chat_id, latitude, longitude, live_period, + reply_to_message_id, reply_markup, disable_notification, timeout, + horizontal_accuracy, heading, proximity_alert_radius, + allow_sending_without_reply)) + + def edit_message_live_location( + self, latitude: float, longitude: float, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, + proximity_alert_radius: Optional[int]=None) -> types.Message: """ Use this method to edit live location :param latitude: :param longitude: :param chat_id: :param message_id: - :param inline_message_id: :param reply_markup: :param timeout: + :param inline_message_id: + :param horizontal_accuracy: + :param heading: + :param proximity_alert_radius: :return: """ return types.Message.de_json( apihelper.edit_message_live_location( self.token, latitude, longitude, chat_id, message_id, - inline_message_id, reply_markup, timeout)) + inline_message_id, reply_markup, timeout, + horizontal_accuracy, heading, proximity_alert_radius)) def stop_message_live_location( - self, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None) -> types.Message: + self, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None) -> types.Message: """ Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires @@ -1145,8 +1319,18 @@ def stop_message_live_location( self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) def send_venue( - self, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: + self, chat_id: Union[int, str], + latitude: float, longitude: float, + title: str, address: str, + foursquare_id: Optional[str]=None, + foursquare_type: Optional[str]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + google_place_id: Optional[str]=None, + google_place_type: Optional[str]=None) -> types.Message: """ Use this method to send information about a venue. :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel @@ -1161,24 +1345,36 @@ def send_venue( :param reply_to_message_id: :param reply_markup: :param timeout: + :param allow_sending_without_reply: + :param google_place_id: + :param google_place_type: :return: """ return types.Message.de_json( apihelper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, - disable_notification, reply_to_message_id, reply_markup, timeout) + disable_notification, reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply, google_place_id, google_place_type) ) def send_contact( - self, chat_id, phone_number, first_name, last_name=None, vcard=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: + self, chat_id: Union[int, str], phone_number: str, + first_name: str, last_name: Optional[str]=None, + vcard: Optional[str]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: return types.Message.de_json( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, - disable_notification, reply_to_message_id, reply_markup, timeout) + disable_notification, reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply) ) - def send_chat_action(self, chat_id, action, timeout=None) -> bool: + def send_chat_action( + self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear @@ -1192,18 +1388,26 @@ def send_chat_action(self, chat_id, action, timeout=None) -> bool: """ return apihelper.send_chat_action(self.token, chat_id, action, timeout) - def kick_chat_member(self, chat_id, user_id, until_date=None) -> bool: + def kick_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, + revoke_messages: Optional[bool]=None) -> bool: """ Use this method to kick a user from a group or a supergroup. :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup :param user_id: Int : Unique identifier of the target user :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever + :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. + If False, the user will be able to see messages in the group that were sent before the user was removed. + Always True for supergroups and channels. :return: boolean """ - return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date) + return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) - def unban_chat_member(self, chat_id, user_id, only_if_banned = False) -> bool: + def unban_chat_member( + self, chat_id: Union[int, str], user_id: int, + only_if_banned: Optional[bool]=False) -> bool: """ Use this method to unban a previously kicked user in a supergroup or channel. The user will not return to the group or channel automatically, but will be able to join via link, etc. @@ -1220,11 +1424,16 @@ def unban_chat_member(self, chat_id, user_id, only_if_banned = False) -> bool: return apihelper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) def restrict_chat_member( - self, chat_id, user_id, until_date=None, - can_send_messages=None, can_send_media_messages=None, - can_send_polls=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None) -> bool: + self, chat_id: Union[int, str], user_id: int, + until_date: Optional[Union[int, datetime]]=None, + can_send_messages: Optional[bool]=None, + can_send_media_messages: Optional[bool]=None, + can_send_polls: Optional[bool]=None, + can_send_other_messages: Optional[bool]=None, + can_add_web_page_previews: Optional[bool]=None, + can_change_info: Optional[bool]=None, + can_invite_users: Optional[bool]=None, + can_pin_messages: Optional[bool]=None) -> bool: """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -1258,9 +1467,19 @@ def restrict_chat_member( can_add_web_page_previews, can_change_info, can_invite_users, can_pin_messages) - def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_messages=None, - can_edit_messages=None, can_delete_messages=None, can_invite_users=None, - can_restrict_members=None, can_pin_messages=None, can_promote_members=None) -> bool: + def promote_chat_member( + self, chat_id: Union[int, str], user_id: int, + can_change_info: Optional[bool]=None, + can_post_messages: Optional[bool]=None, + can_edit_messages: Optional[bool]=None, + can_delete_messages: Optional[bool]=None, + can_invite_users: Optional[bool]=None, + can_restrict_members: Optional[bool]=None, + can_pin_messages: Optional[bool]=None, + can_promote_members: Optional[bool]=None, + is_anonymous: Optional[bool]=None, + can_manage_chat: Optional[bool]=None, + can_manage_voice_chats: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1279,13 +1498,23 @@ def promote_chat_member(self, chat_id, user_id, can_change_info=None, can_post_m :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) + :param is_anonymous: Bool: Pass True, if the administrator's presence in the chat is hidden + :param can_manage_chat: Bool: Pass True, if the administrator can access the chat event log, chat statistics, + message statistics in channels, see channel members, + see anonymous administrators in supergroups and ignore slow mode. + Implied by any other administrator privilege + :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats + For now, bots can use this privilege only for passing to other administrators. :return: True on success. """ - return apihelper.promote_chat_member(self.token, chat_id, user_id, can_change_info, can_post_messages, - can_edit_messages, can_delete_messages, can_invite_users, - can_restrict_members, can_pin_messages, can_promote_members) + return apihelper.promote_chat_member( + self.token, chat_id, user_id, can_change_info, can_post_messages, + can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members, + is_anonymous, can_manage_chat, can_manage_voice_chats) - def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title) -> bool: + def set_chat_administrator_custom_title( + self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: """ Use this method to set a custom title for an administrator in a supergroup promoted by the bot. @@ -1299,7 +1528,8 @@ def set_chat_administrator_custom_title(self, chat_id, user_id, custom_title) -> """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - def set_chat_permissions(self, chat_id, permissions) -> bool: + def set_chat_permissions( + self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: """ Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work @@ -1312,7 +1542,10 @@ def set_chat_permissions(self, chat_id, permissions) -> bool: """ return apihelper.set_chat_permissions(self.token, chat_id, permissions) - def create_chat_invite_link(self, chat_id, expire_date=None, member_limit=None) -> types.ChatInviteLink: + def create_chat_invite_link( + self, chat_id: Union[int, str], + expire_date: Optional[Union[int, datetime]]=None, + member_limit: Optional[int]=None) -> types.ChatInviteLink: """ Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1327,7 +1560,10 @@ def create_chat_invite_link(self, chat_id, expire_date=None, member_limit=None) apihelper.create_chat_invite_link(self.token, chat_id, expire_date, member_limit) ) - def edit_chat_invite_link(self, chat_id, invite_link, expire_date=None, member_limit=None): + def edit_chat_invite_link( + self, chat_id: Union[int, str], invite_link: str, + expire_date: Optional[Union[int, datetime]]=None, + member_limit: Optional[int]=None) -> types.ChatInviteLink: """ Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1343,7 +1579,8 @@ def edit_chat_invite_link(self, chat_id, invite_link, expire_date=None, member_l apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, expire_date, member_limit) ) - def revoke_chat_invite_link(self, chat_id, invite_link) -> types.ChatInviteLink: + def revoke_chat_invite_link( + self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: """ Use this method to revoke an invite link created by the bot. Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator @@ -1358,7 +1595,7 @@ def revoke_chat_invite_link(self, chat_id, invite_link) -> types.ChatInviteLink: apihelper.revoke_chat_invite_link(self.token, chat_id, invite_link) ) - def export_chat_invite_link(self, chat_id) -> str: + def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: """ Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1369,7 +1606,7 @@ def export_chat_invite_link(self, chat_id) -> str: """ return apihelper.export_chat_invite_link(self.token, chat_id) - def set_chat_photo(self, chat_id, photo) -> bool: + def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: """ Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1383,7 +1620,7 @@ def set_chat_photo(self, chat_id, photo) -> bool: """ return apihelper.set_chat_photo(self.token, chat_id, photo) - def delete_chat_photo(self, chat_id) -> bool: + def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1395,17 +1632,24 @@ def delete_chat_photo(self, chat_id) -> bool: :return: """ return apihelper.delete_chat_photo(self.token, chat_id) + + def get_my_commands(self) -> List[types.BotCommand]: + """ + Use this method to get the current list of the bot's commands. + Returns List of BotCommand on success. + """ + result = apihelper.get_my_commands(self.token) + return [types.BotCommand.de_json(cmd) for cmd in result] - def set_my_commands(self, commands) -> bool: + def set_my_commands(self, commands: List[types.BotCommand]) -> bool: """ Use this method to change the list of the bot's commands. - :param commands: Array of BotCommand. A JSON-serialized list of bot commands - to be set as the list of the bot's commands. At most 100 commands can be specified. + :param commands: List of BotCommand. At most 100 commands can be specified. :return: """ return apihelper.set_my_commands(self.token, commands) - def set_chat_title(self, chat_id, title) -> bool: + def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: """ Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1419,7 +1663,7 @@ def set_chat_title(self, chat_id, title) -> bool: """ return apihelper.set_chat_title(self.token, chat_id, title) - def set_chat_description(self, chat_id, description=None) -> bool: + def set_chat_description(self, chat_id: Union[int, str], description: Optional[str]=None) -> bool: """ Use this method to change the description of a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1431,7 +1675,9 @@ def set_chat_description(self, chat_id, description=None) -> bool: """ return apihelper.set_chat_description(self.token, chat_id, description) - def pin_chat_message(self, chat_id, message_id, disable_notification=False) -> bool: + def pin_chat_message( + self, chat_id: Union[int, str], message_id: int, + disable_notification: Optional[bool]=False) -> bool: """ Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1445,7 +1691,7 @@ def pin_chat_message(self, chat_id, message_id, disable_notification=False) -> b """ return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) - def unpin_chat_message(self, chat_id, message_id=None) -> bool: + def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: """ Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1457,7 +1703,7 @@ def unpin_chat_message(self, chat_id, message_id=None) -> bool: """ return apihelper.unpin_chat_message(self.token, chat_id, message_id) - def unpin_all_chat_messages(self, chat_id) -> bool: + def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: """ Use this method to unpin a all pinned messages in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1468,8 +1714,14 @@ def unpin_all_chat_messages(self, chat_id) -> bool: """ return apihelper.unpin_all_chat_messages(self.token, chat_id) - def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, - disable_web_page_preview=None, reply_markup=None) -> Union[types.Message, bool]: + def edit_message_text( + self, text: str, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + parse_mode: Optional[str]=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. :param text: @@ -1489,8 +1741,11 @@ def edit_message_text(self, text, chat_id=None, message_id=None, inline_message_ return result return types.Message.de_json(result) - def edit_message_media(self, media, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: + def edit_message_media( + self, media: Any, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -1508,8 +1763,11 @@ def edit_message_media(self, media, chat_id=None, message_id=None, return result return types.Message.de_json(result) - def edit_message_reply_markup(self, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]: + def edit_message_reply_markup( + self, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. :param chat_id: @@ -1524,8 +1782,12 @@ def edit_message_reply_markup(self, chat_id=None, message_id=None, return types.Message.de_json(result) def send_game( - self, chat_id, game_short_name, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: + self, chat_id: Union[int, str], game_short_name: str, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Used to send the game :param chat_id: @@ -1534,15 +1796,22 @@ def send_game( :param reply_to_message_id: :param reply_markup: :param timeout: + :param allow_sending_without_reply: :return: """ result = apihelper.send_game( self.token, chat_id, game_short_name, disable_notification, - reply_to_message_id, reply_markup, timeout) + reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply) return types.Message.de_json(result) - def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=None, inline_message_id=None, - disable_edit_message=None) -> Union[types.Message, bool]: + def set_game_score( + self, user_id: Union[int, str], score: int, + force: Optional[bool]=None, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: """ Sets the value of points in the game to a specific user :param user_id: @@ -1560,8 +1829,10 @@ def set_game_score(self, user_id, score, force=None, chat_id=None, message_id=No return result return types.Message.de_json(result) - def get_game_high_scores(self, user_id, chat_id=None, - message_id=None, inline_message_id=None) -> List[types.GameHighScore]: + def get_game_high_scores( + self, user_id: int, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ Gets top points and game play :param user_id: @@ -1576,12 +1847,23 @@ def get_game_high_scores(self, user_id, chat_id=None, ret.append(types.GameHighScore.de_json(r)) return ret - def send_invoice(self, chat_id, title, description, invoice_payload, provider_token, currency, prices, - start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, - need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, - send_phone_number_to_provider=None, send_email_to_provider=None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None) -> types.Message: + def send_invoice( + self, chat_id: Union[int, str], title: str, description: str, + invoice_payload: str, provider_token: str, currency: str, + prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, + photo_url: Optional[str]=None, photo_size: Optional[int]=None, + photo_width: Optional[int]=None, photo_height: Optional[int]=None, + need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, + need_email: Optional[bool]=None, need_shipping_address: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, + is_flexible: Optional[bool]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + provider_data: Optional[str]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: """ Sends invoice :param chat_id: Unique identifier for the target private chat @@ -1615,6 +1897,7 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to :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 allow_sending_without_reply: :return: """ result = apihelper.send_invoice( @@ -1622,14 +1905,25 @@ def send_invoice(self, chat_id, title, description, invoice_payload, provider_to currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, - reply_to_message_id, reply_markup, provider_data, timeout) + reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply) return types.Message.de_json(result) def send_poll( - self, 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_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None) -> types.Message: + self, chat_id: Union[int, str], question: str, options: List[str], + is_anonymous: Optional[bool]=None, type: Optional[str]=None, + allows_multiple_answers: Optional[bool]=None, + correct_option_id: Optional[int]=None, + explanation: Optional[str]=None, + explanation_parse_mode: Optional[str]=None, + open_period: Optional[int]=None, + close_date: Optional[Union[int, datetime]]=None, + is_closed: Optional[bool]=None, + disable_notifications: Optional[bool]=False, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + allow_sending_without_reply: Optional[bool]=None, + timeout: Optional[int]=None, + explanation_entities: Optional[List[types.MessageEntity]]=None) -> types.Message: """ Send polls :param chat_id: @@ -1646,8 +1940,10 @@ def send_poll( :param is_closed: :param disable_notifications: :param reply_to_message_id: + :param allow_sending_without_reply: :param reply_markup: :param timeout: + :param explanation_entities: :return: """ @@ -1660,9 +1956,12 @@ def send_poll( question, options, is_anonymous, type, allows_multiple_answers, correct_option_id, explanation, explanation_parse_mode, open_period, close_date, is_closed, - disable_notifications, reply_to_message_id, reply_markup, timeout)) + disable_notifications, reply_to_message_id, allow_sending_without_reply, + reply_markup, timeout, explanation_entities)) - def stop_poll(self, chat_id, message_id, reply_markup=None) -> types.Poll: + def stop_poll( + self, chat_id: Union[int, str], message_id: int, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: """ Stops poll :param chat_id: @@ -1672,7 +1971,10 @@ def stop_poll(self, chat_id, message_id, reply_markup=None) -> types.Poll: """ return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id, reply_markup)) - def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, error_message=None) -> bool: + def answer_shipping_query( + self, shipping_query_id: str, ok: bool, + shipping_options: Optional[List[types.ShippingOption]]=None, + error_message: Optional[str]=None) -> bool: """ Asks for an answer to a shipping question :param shipping_query_id: @@ -1683,7 +1985,9 @@ def answer_shipping_query(self, shipping_query_id, ok, shipping_options=None, er """ return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) - def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=None) -> bool: + def answer_pre_checkout_query( + self, pre_checkout_query_id: int, ok: bool, + error_message: Optional[str]=None) -> bool: """ Response to a request for pre-inspection :param pre_checkout_query_id: @@ -1693,8 +1997,12 @@ def answer_pre_checkout_query(self, pre_checkout_query_id, ok, error_message=Non """ return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) - def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, reply_markup=None) -> Union[types.Message, bool]: + def edit_message_caption( + self, caption: str, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + parse_mode: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages :param caption: @@ -1713,7 +2021,7 @@ def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_me return result return types.Message.de_json(result) - def reply_to(self, message, text, **kwargs) -> types.Message: + def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` :param message: @@ -1723,8 +2031,14 @@ def reply_to(self, message, text, **kwargs) -> types.Message: """ return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) - def answer_inline_query(self, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, - switch_pm_text=None, switch_pm_parameter=None) -> bool: + def answer_inline_query( + self, inline_query_id: str, + results: List[Any], + cache_time: Optional[int]=None, + is_personal: Optional[bool]=None, + next_offset: Optional[str]=None, + switch_pm_text: Optional[str]=None, + switch_pm_parameter: Optional[str]=None) -> bool: """ Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. @@ -1744,7 +2058,10 @@ def answer_inline_query(self, inline_query_id, results, cache_time=None, is_pers return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, switch_pm_text, switch_pm_parameter) - def answer_callback_query(self, callback_query_id, text=None, show_alert=None, url=None, cache_time=None) -> bool: + def answer_callback_query( + self, callback_query_id: str, + text: Optional[str]=None, show_alert: Optional[bool]=None, + url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. @@ -1757,7 +2074,15 @@ def answer_callback_query(self, callback_query_id, text=None, show_alert=None, u """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - def get_sticker_set(self, name) -> types.StickerSet: + def set_sticker_set_thumb( + self, name: str, user_id: int, thumb: Union[Any, str]=None): + """ + Use this method to set the thumbnail of a sticker set. + Animated thumbnails can be set for animated sticker sets only. Returns True on success. + """ + return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) + + def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. :param name: @@ -1766,7 +2091,7 @@ def get_sticker_set(self, name) -> types.StickerSet: result = apihelper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) - def upload_sticker_file(self, user_id, png_sticker) -> types.File: + def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. @@ -1777,24 +2102,32 @@ def upload_sticker_file(self, user_id, png_sticker) -> types.File: result = apihelper.upload_sticker_file(self.token, user_id, png_sticker) return types.File.de_json(result) - def create_new_sticker_set(self, user_id, name, title, png_sticker, emojis, contains_masks=None, - mask_position=None) -> bool: + def create_new_sticker_set( + self, user_id: int, name: str, title: str, + png_sticker: Union[Any, str], + emojis: str, + contains_masks: Optional[bool]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: """ - Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. + Use this method to create new sticker set owned by a user. + The bot will be able to edit the created sticker set. Returns True on success. :param user_id: :param name: :param title: - :param png_sticker: + :param png_sticker: :param emojis: :param contains_masks: :param mask_position: :return: """ - return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, contains_masks, - mask_position) + return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, + contains_masks, mask_position) + - def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=None) -> bool: + def add_sticker_to_set( + self, user_id: int, name: str, png_sticker: Union[Any, str], + emojis: str, mask_position: Optional[types.MaskPosition]=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. Returns True on success. :param user_id: @@ -1806,7 +2139,7 @@ def add_sticker_to_set(self, user_id, name, png_sticker, emojis, mask_position=N """ return apihelper.add_sticker_to_set(self.token, user_id, name, png_sticker, emojis, mask_position) - def set_sticker_position_in_set(self, sticker, position) -> bool: + def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. :param sticker: @@ -1815,7 +2148,7 @@ def set_sticker_position_in_set(self, sticker, position) -> bool: """ return apihelper.set_sticker_position_in_set(self.token, sticker, position) - def delete_sticker_from_set(self, sticker) -> bool: + def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. :param sticker: @@ -1823,7 +2156,8 @@ def delete_sticker_from_set(self, sticker) -> bool: """ return apihelper.delete_sticker_from_set(self.token, sticker) - def register_for_reply(self, message, callback, *args, **kwargs) -> None: + def register_for_reply( + self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when a reply to `message` arrives. @@ -1836,7 +2170,8 @@ def register_for_reply(self, message, callback, *args, **kwargs) -> None: message_id = message.message_id self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs) - def register_for_reply_by_message_id(self, message_id, callback, *args, **kwargs) -> None: + def register_for_reply_by_message_id( + self, message_id: int, callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when a reply to `message` arrives. @@ -1861,7 +2196,8 @@ def _notify_reply_handlers(self, new_messages) -> None: for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - def register_next_step_handler(self, message, callback, *args, **kwargs) -> None: + def register_next_step_handler( + self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when new message arrives after `message`. @@ -1875,7 +2211,8 @@ def register_next_step_handler(self, message, callback, *args, **kwargs) -> None chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) - def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwargs) -> None: + def register_next_step_handler_by_chat_id( + self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when new message arrives after `message`. @@ -1888,7 +2225,7 @@ def register_next_step_handler_by_chat_id(self, chat_id, callback, *args, **kwar """ self.next_step_backend.register_handler(chat_id, Handler(callback, *args, **kwargs)) - def clear_step_handler(self, message) -> None: + def clear_step_handler(self, message: types.Message) -> None: """ Clears all callback functions registered by register_next_step_handler(). @@ -1897,7 +2234,7 @@ def clear_step_handler(self, message) -> None: chat_id = message.chat.id self.clear_step_handler_by_chat_id(chat_id) - def clear_step_handler_by_chat_id(self, chat_id) -> None: + def clear_step_handler_by_chat_id(self, chat_id: Union[int, str]) -> None: """ Clears all callback functions registered by register_next_step_handler(). @@ -1905,7 +2242,7 @@ def clear_step_handler_by_chat_id(self, chat_id) -> None: """ self.next_step_backend.clear_handlers(chat_id) - def clear_reply_handlers(self, message) -> None: + def clear_reply_handlers(self, message: types.Message) -> None: """ Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). @@ -1914,7 +2251,7 @@ def clear_reply_handlers(self, message) -> None: message_id = message.message_id self.clear_reply_handlers_by_message_id(message_id) - def clear_reply_handlers_by_message_id(self, message_id) -> None: + def clear_reply_handlers_by_message_id(self, message_id: int) -> None: """ Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e9e15cf42..49a125a71 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import time from datetime import datetime +from typing import Dict try: import ujson as json @@ -167,14 +168,22 @@ def get_me(token): method_url = r'getMe' return _make_request(token, method_url) + +def get_my_commands(token): + method_url = r'getMyCommands' + return _make_request(token, method_url) + + def log_out(token): method_url = r'logOut' return _make_request(token, method_url) + def close(token): method_url = r'close' return _make_request(token, method_url) + def get_file(token, file_id): method_url = r'getFile' return _make_request(token, method_url, params={'file_id': file_id}) @@ -203,7 +212,8 @@ def download_file(token, file_path): def send_message( token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None, + entities=None, allow_sending_without_reply=None): """ Use this method to send text messages. On success, the sent Message is returned. :param token: @@ -215,6 +225,8 @@ def send_message( :param parse_mode: :param disable_notification: :param timeout: + :param entities: + :param allow_sending_without_reply: :return: """ method_url = r'sendMessage' @@ -231,6 +243,10 @@ def send_message( payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if entities: + payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, method='post') @@ -324,6 +340,18 @@ def get_chat_members_count(token, chat_id): return _make_request(token, method_url, params=payload) +def set_sticker_set_thumb(token, name, user_id, thumb): + method_url = r'setStickerSetThumb' + payload = {'name': name, 'user_id': user_id} + files = {} + if thumb: + if not isinstance(thumb, str): + files['thumb'] = thumb + else: + payload['thumb'] = thumb + return _make_request(token, method_url, params=payload, files=files or None) + + def set_chat_sticker_set(token, chat_id, sticker_set_name): method_url = r'setChatStickerSet' payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name} @@ -381,7 +409,7 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None): + reply_markup=None, timeout=None, allow_sending_without_reply=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -394,13 +422,16 @@ def send_dice( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout + if 'allow_sending_without_reply': + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) def send_photo( token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None, + caption_entities=None, allow_sending_without_reply=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -422,13 +453,17 @@ def send_photo( payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') def send_media_group( token, chat_id, media, disable_notification=None, reply_to_message_id=None, - timeout=None): + timeout=None, allow_sending_without_reply=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -438,6 +473,8 @@ def send_media_group( payload['reply_to_message_id'] = reply_to_message_id if timeout: payload['connect-timeout'] = timeout + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -446,14 +483,24 @@ def send_media_group( def send_location( token, chat_id, latitude, longitude, - live_period=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None): + live_period=None, reply_to_message_id=None, + reply_markup=None, disable_notification=None, + timeout=None, horizontal_accuracy=None, heading=None, + proximity_alert_radius=None, allow_sending_without_reply=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: payload['live_period'] = live_period + if horizontal_accuracy: + payload['horizontal_accuracy'] = horizontal_accuracy + if heading: + payload['heading'] = heading + if proximity_alert_radius: + payload['proximity_alert_radius'] = proximity_alert_radius if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if disable_notification is not None: @@ -463,14 +510,22 @@ def send_location( return _make_request(token, method_url, params=payload) -def edit_message_live_location(token, latitude, longitude, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None): +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): method_url = r'editMessageLiveLocation' payload = {'latitude': latitude, 'longitude': longitude} if chat_id: payload['chat_id'] = chat_id if message_id: payload['message_id'] = message_id + if horizontal_accuracy: + payload['horizontal_accuracy'] = horizontal_accuracy + if heading: + payload['heading'] = heading + if proximity_alert_radius: + payload['proximity_alert_radius'] = proximity_alert_radius if inline_message_id: payload['inline_message_id'] = inline_message_id if reply_markup: @@ -501,7 +556,9 @@ def stop_message_live_location( def send_venue( token, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None): + reply_to_message_id=None, reply_markup=None, timeout=None, + allow_sending_without_reply=None, google_place_id=None, + google_place_type=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -516,12 +573,19 @@ def send_venue( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if google_place_id: + payload['google_place_id'] = google_place_id + if google_place_type: + payload['google_place_type'] = google_place_type return _make_request(token, method_url, params=payload) def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, + allow_sending_without_reply=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -536,6 +600,8 @@ def send_contact( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -548,7 +614,8 @@ def send_chat_action(token, chat_id, action, timeout=None): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, + thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -584,11 +651,17 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['width'] = width if height: payload['height'] = height + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') -def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None, thumb=None): +def send_animation( + token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, + allow_sending_without_reply=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -618,11 +691,16 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m files = {'thumb': thumb} else: payload['thumb'] = thumb + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None): + parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, + allow_sending_without_reply=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -644,11 +722,15 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['disable_notification'] = disable_notification if timeout: payload['connect-timeout'] = timeout + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None): + disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -678,11 +760,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m files = {'thumb': thumb} else: payload['thumb'] = thumb + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None): + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, + caption_entities=None, allow_sending_without_reply=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -716,11 +801,16 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non files = {'thumb': thumb} else: payload['thumb'] = thumb + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, - disable_notification=None, timeout=None, caption=None, thumb=None): + disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, + allow_sending_without_reply=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -748,6 +838,10 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m files = {'thumb': thumb} else: payload['thumb'] = thumb + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -758,13 +852,15 @@ def get_method_by_type(data_type): return r'sendSticker' -def kick_chat_member(token, chat_id, user_id, until_date=None): +def kick_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None): method_url = 'kickChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if isinstance(until_date, datetime): payload['until_date'] = until_date.timestamp() else: payload['until_date'] = until_date + if revoke_messages is not None: + payload['revoke_messages'] = revoke_messages return _make_request(token, method_url, params=payload, method='post') @@ -813,7 +909,8 @@ def restrict_chat_member( def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, - can_restrict_members=None, can_pin_messages=None, can_promote_members=None): + can_restrict_members=None, can_pin_messages=None, can_promote_members=None, + is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -832,6 +929,12 @@ def promote_chat_member( payload['can_pin_messages'] = can_pin_messages if can_promote_members is not None: payload['can_promote_members'] = can_promote_members + if is_anonymous is not None: + payload['is_anonymous'] = is_anonymous + if can_manage_chat is not None: + payload['can_manage_chat'] = can_manage_chat + if can_manage_voice_chats is not None: + payload['can_manage_voice_chats'] = can_manage_voice_chats return _make_request(token, method_url, params=payload, method='post') @@ -1042,7 +1145,8 @@ def delete_message(token, chat_id, message_id, timeout=None): def send_game( token, chat_id, game_short_name, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None): + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, + allow_sending_without_reply=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1053,6 +1157,8 @@ def send_game( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1117,7 +1223,7 @@ def send_invoice( need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None): + timeout=None, allow_sending_without_reply=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1145,6 +1251,7 @@ def send_invoice( :param reply_markup: 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 :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 allow_sending_without_reply: :return: """ method_url = r'sendInvoice' @@ -1183,6 +1290,8 @@ def send_invoice( payload['provider_data'] = provider_data if timeout: payload['connect-timeout'] = timeout + if allow_sending_without_reply: + payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1325,7 +1434,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_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None): + disable_notifications=False, reply_to_message_id=None, allow_sending_without_reply=None, + reply_markup=None, timeout=None, explanation_entities=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1358,10 +1468,15 @@ def send_poll( payload['disable_notification'] = disable_notifications if reply_to_message_id is not None: payload['reply_to_message_id'] = reply_to_message_id + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout + if explanation_entities: + payload['explanation_entities'] = json.dumps( + types.MessageEntity.to_list_of_dicts(explanation_entities)) return _make_request(token, method_url, params=payload) diff --git a/telebot/types.py b/telebot/types.py index 289849393..a134c5a9c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -408,6 +408,27 @@ def de_json(cls, json_string): if 'passport_data' in obj: opts['passport_data'] = obj['passport_data'] content_type = 'passport_data' + if 'proximity_alert_triggered' in obj: + opts['proximity_alert_triggered'] = ProximityAlertTriggered.de_json(obj[ + 'proximity_alert_triggered']) + content_type = 'proximity_alert_triggered' + if 'voice_chat_scheduled' in obj: + opts['voice_chat_scheduled'] = VoiceChatScheduled.de_json(obj['voice_chat_scheduled']) + content_type = 'voice_chat_scheduled' + if 'voice_chat_started' in obj: + opts['voice_chat_started'] = VoiceChatStarted.de_json(obj['voice_chat_started']) + content_type = 'voice_chat_started' + if 'voice_chat_ended' in obj: + opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) + content_type = 'voice_chat_ended' + if 'voice_chat_participants_invited' in obj: + opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json( + obj['voice_chat_participants_invited']) + content_type = 'voice_chat_participants_invited' + if 'message_auto_delete_timer_changed' in obj: + opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json( + obj['message_auto_delete_timer_changed']) + content_type = 'message_auto_delete_timer_changed' if 'reply_markup' in obj: opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1220,7 +1241,13 @@ def to_dict(self): return json_dict -class BotCommand(JsonSerializable): +class BotCommand(JsonSerializable, JsonDeserializable): + @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, command, description): """ This object represents a bot command. @@ -1748,13 +1775,14 @@ def to_json(self): class InlineQueryResultLocation(JsonSerializable): - def __init__(self, id, title, latitude, longitude, live_period=None, reply_markup=None, + 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 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 @@ -1765,6 +1793,8 @@ def __init__(self, id, title, latitude, longitude, live_period=None, reply_marku def to_json(self): json_dict = {'type': self.type, 'id': self.id, 'latitude': self.latitude, 'longitude': self.longitude, 'title': self.title} + if self.horizontal_accuracy: + json_dict['horizontal_accuracy'] = self.horizontal_accuracy if self.live_period: json_dict['live_period'] = self.live_period if self.thumb_url: @@ -1782,7 +1812,8 @@ def to_json(self): class InlineQueryResultVenue(JsonSerializable): 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): + 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 @@ -1796,6 +1827,8 @@ def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, 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, @@ -1814,6 +1847,10 @@ def to_json(self): 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) @@ -2555,4 +2592,75 @@ def to_dict(self): "is_revoked": self.is_revoked, "expire_date": self.expire_date, "member_limit": self.member_limit - } \ No newline at end of file + } + +class ProximityAlertTriggered(JsonDeserializable): + @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, traveler, watcher, distance, **kwargs): + self.traveler: User = traveler + self.watcher: User = watcher + self.distance: int = distance + + +class VoiceChatStarted(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + return cls() + + def __init__(self): + """ + This object represents a service message about a voice chat started in the chat. + Currently holds no information. + """ + pass + +class VoiceChatScheduled(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(obj['start_date']) + + def __init__(self, start_date): + self.start_date: int = start_date + + +class VoiceChatEnded(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(obj['duration']) + + def __init__(self, duration): + self.duration: int = duration + + +class VoiceChatParticipantsInvited(JsonDeserializable): + @classmethod + 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) + + def __init__(self, users=None): + self.users: List[User] = users + + +class MessageAutoDeleteTimerChanged(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(obj['message_auto_delete_time']) + + def __init__(self, message_auto_delete_time): + self.message_auto_delete_time = message_auto_delete_time diff --git a/telebot/util.py b/telebot/util.py index 7b879598e..cb1257721 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -33,7 +33,9 @@ content_type_service = [ 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', - 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message' + 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', + 'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', + 'voice_chat_participants_invited', 'message_auto_delete_timer_changed' ] From d3369245c4b6a64e3a3f4a72fec2a158c8d016b6 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 21 Jun 2021 17:49:03 +0200 Subject: [PATCH 0594/1808] fixed wrong type hint --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 484b208b2..c8c4f8453 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -814,7 +814,7 @@ def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatM def send_message( self, chat_id: Union[int, str], text: str, disable_web_page_preview: Optional[bool]=None, - reply_to_message_id: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, From 3f46ce3b7b3043276f8b8cd6e5bc0545d95060bd Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 21 Jun 2021 19:59:39 +0200 Subject: [PATCH 0595/1808] added InputInvoiceMessageContent and tgs_sticker support and some small changes --- telebot/__init__.py | 49 ++++++++++++- telebot/apihelper.py | 50 ++++++------- telebot/types.py | 168 ++++++++++++++++++++++++++++++------------- 3 files changed, 191 insertions(+), 76 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c8c4f8453..a751be315 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2121,8 +2121,33 @@ def create_new_sticker_set( :param mask_position: :return: """ - return apihelper.create_new_sticker_set(self.token, user_id, name, title, png_sticker, emojis, - contains_masks, mask_position) + return apihelper.create_new_sticker_set( + self.token, user_id, name, title, png_sticker, emojis, + contains_masks, mask_position, animated=False) + + + def create_new_animated_sticker_set( + self, user_id: int, name: str, title: str, + tgs_sticker: Union[Any, str], + emojis: str, + contains_masks: Optional[bool]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to create new sticker set owned by a user. + The bot will be able to edit the created sticker set. + Returns True on success. + :param user_id: + :param name: + :param title: + :param tgs_sticker: + :param emojis: + :param contains_masks: + :param mask_position: + :return: + """ + return apihelper.create_new_sticker_set( + self.token, user_id, name, title, tgs_sticker, emojis, + contains_masks, mask_position, animated=True) def add_sticker_to_set( @@ -2137,7 +2162,25 @@ def add_sticker_to_set( :param mask_position: :return: """ - return apihelper.add_sticker_to_set(self.token, user_id, name, png_sticker, emojis, mask_position) + return apihelper.add_sticker_to_set( + self.token, user_id, name, png_sticker, emojis, mask_position, animated=False) + + + def add_sticker_to_animated_set( + self, user_id: int, name: str, tgs_sticker: Union[Any, str], + emojis: str, mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to add a new sticker to a set created by the bot. Returns True on success. + :param user_id: + :param name: + :param tgs_sticker: + :param emojis: + :param mask_position: + :return: + """ + return apihelper.add_sticker_to_set( + self.token, user_id, name, tgs_sticker, emojis, mask_position, animated=True) + def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 49a125a71..106702e16 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -245,7 +245,7 @@ def send_message( payload['connect-timeout'] = timeout if entities: payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, method='post') @@ -422,7 +422,7 @@ def send_dice( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if 'allow_sending_without_reply': + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -455,7 +455,7 @@ def send_photo( payload['connect-timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -473,7 +473,7 @@ def send_media_group( payload['reply_to_message_id'] = reply_to_message_id if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request( token, method_url, params=payload, @@ -499,7 +499,7 @@ def send_location( payload['proximity_alert_radius'] = proximity_alert_radius if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) @@ -573,7 +573,7 @@ def send_venue( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if google_place_id: payload['google_place_id'] = google_place_id @@ -600,7 +600,7 @@ def send_contact( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -653,7 +653,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['height'] = height if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -693,7 +693,7 @@ def send_animation( payload['thumb'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -724,7 +724,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['connect-timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -760,7 +760,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m files = {'thumb': thumb} else: payload['thumb'] = thumb - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -803,7 +803,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['thumb'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -840,7 +840,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['thumb'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1157,7 +1157,7 @@ def send_game( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1290,7 +1290,7 @@ def send_invoice( payload['provider_data'] = provider_data if timeout: payload['connect-timeout'] = timeout - if allow_sending_without_reply: + if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1388,15 +1388,16 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( - token, user_id, name, title, png_sticker, emojis, - contains_masks=None, mask_position=None): + token, user_id, name, title, sticker, emojis, + contains_masks=None, mask_position=None, animated=False): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} files = None - if not util.is_string(png_sticker): - files = {'png_sticker': png_sticker} + stype = 'tgs_sticker' if animated else 'png_sticker' + if not util.is_string(sticker): + files = {stype: sticker} else: - payload['png_sticker'] = png_sticker + payload[stype] = sticker if contains_masks is not None: payload['contains_masks'] = contains_masks if mask_position: @@ -1404,14 +1405,15 @@ def create_new_sticker_set( return _make_request(token, method_url, params=payload, files=files, method='post') -def add_sticker_to_set(token, user_id, name, png_sticker, emojis, mask_position): +def add_sticker_to_set(token, user_id, name, sticker, emojis, mask_position, animated=False): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} files = None - if not util.is_string(png_sticker): - files = {'png_sticker': png_sticker} + stype = 'tgs_sticker' if animated else 'png_sticker' + if not util.is_string(sticker): + files = {stype: sticker} else: - payload['png_sticker'] = png_sticker + payload[stype] = sticker if mask_position: payload['mask_position'] = mask_position.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/types.py b/telebot/types.py index a134c5a9c..29e2b9d9e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import logging -from typing import Dict, List, Union +from typing import Dict, List, Optional, Union try: import ujson as json @@ -146,7 +146,7 @@ def __init__(self, chat, from_user, date, old_chat_member, new_chat_member, invi self.date: int = date self.old_chat_member: ChatMember = old_chat_member self.new_chat_member: ChatMember = new_chat_member - self.invite_link: Union[ChatInviteLink, None] = invite_link + self.invite_link: Optional[ChatInviteLink] = invite_link class WebhookInfo(JsonDeserializable): @@ -461,49 +461,49 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.from_user: User = from_user self.date: int = date self.chat: Chat = chat - self.forward_from: Union[User, None] = None - self.forward_from_chat: Union[Chat, None] = None - self.forward_from_message_id: Union[int, None] = None - self.forward_signature: Union[str, None] = None - self.forward_sender_name: Union[str, None] = None - self.forward_date: Union[int, None] = None - self.reply_to_message: Union[Message, None] = None - self.via_bot: Union[User, None] = None - self.edit_date: Union[int, None] = None - self.media_group_id: Union[str, None] = None - self.author_signature: Union[str, None] = None - self.text: Union[str, None] = None - self.entities: Union[List[MessageEntity], None] = None - self.caption_entities: Union[List[MessageEntity], None] = None - self.audio: Union[Audio, None] = None - self.document: Union[Document, None] = None - self.photo: Union[List[PhotoSize], None] = None - self.sticker: Union[Sticker, None] = None - self.video: Union[Video, None] = None - self.video_note: Union[VideoNote, None] = None - self.voice: Union[Voice, None] = None - self.caption: Union[str, None] = None - self.contact: Union[Contact, None] = None - self.location: Union[Location, None] = None - self.venue: Union[Venue, None] = None - self.animation: Union[Animation, None] = None - self.dice: Union[Dice, None] = None - self.new_chat_member: Union[User, None] = None # Deprecated since Bot API 3.0. Not processed anymore - self.new_chat_members: Union[List[User], None] = None - self.left_chat_member: Union[User, None] = None - self.new_chat_title: Union[str, None] = None - self.new_chat_photo: Union[List[PhotoSize], None] = None - self.delete_chat_photo: Union[bool, None] = None - self.group_chat_created: Union[bool, None] = None - self.supergroup_chat_created: Union[bool, None] = None - self.channel_chat_created: Union[bool, None] = None - self.migrate_to_chat_id: Union[int, None] = None - self.migrate_from_chat_id: Union[int, None] = None - self.pinned_message: Union[Message, None] = None - self.invoice: Union[Invoice, None] = None - self.successful_payment: Union[SuccessfulPayment, None] = None - self.connected_website: Union[str, None] = None - self.reply_markup: Union[InlineKeyboardMarkup, None] = None + self.forward_from: Optional[User] = None + self.forward_from_chat: Optional[Chat] = None + self.forward_from_message_id: Optional[int] = None + self.forward_signature: Optional[str] = None + self.forward_sender_name: Optional[str] = None + self.forward_date: Optional[int] = None + self.reply_to_message: Optional[Message] = None + self.via_bot: Optional[User] = None + self.edit_date: Optional[int] = None + self.media_group_id: Optional[str] = None + self.author_signature: Optional[str] = None + self.text: Optional[str] = None + self.entities: Optional[List[MessageEntity]] = None + self.caption_entities: Optional[List[MessageEntity]] = None + self.audio: Optional[Audio] = None + self.document: Optional[Document] = None + self.photo: Optional[List[PhotoSize]] = None + self.sticker: Optional[Sticker] = None + self.video: Optional[Video] = None + self.video_note: Optional[VideoNote] = None + self.voice: Optional[Voice] = None + self.caption: Optional[str] = None + self.contact: Optional[Contact] = None + self.location: Optional[Location] = None + self.venue: Optional[Venue] = None + self.animation: Optional[Animation] = None + self.dice: Optional[Dice] = None + self.new_chat_member: Optional[User] = None # Deprecated since Bot API 3.0. Not processed anymore + self.new_chat_members: Optional[List[User]] = None + self.left_chat_member: Optional[User] = None + self.new_chat_title: Optional[str] = None + self.new_chat_photo: Optional[List[PhotoSize]] = None + self.delete_chat_photo: Optional[bool] = None + self.group_chat_created: Optional[bool] = None + self.supergroup_chat_created: Optional[bool] = None + self.channel_chat_created: Optional[bool] = None + self.migrate_to_chat_id: Optional[int] = None + self.migrate_from_chat_id: Optional[int] = None + self.pinned_message: Optional[Message] = None + self.invoice: Optional[Invoice] = None + self.successful_payment: Optional[SuccessfulPayment] = None + self.connected_website: Optional[str] = None + self.reply_markup: Optional[InlineKeyboardMarkup] = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -1314,7 +1314,7 @@ def to_dict(self): json_dict['parse_mode'] = self.parse_mode if self.entities: json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) - if self.disable_web_page_preview: + if self.disable_web_page_preview is not None: json_dict['disable_web_page_preview'] = self.disable_web_page_preview return json_dict @@ -1387,6 +1387,73 @@ def to_dict(self): return json_dict +class InputInvoiceMessageContent(Dictionaryable): + def __init__(self, title, description, payload, provider_token, currency, prices, + max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, + photo_url=None, photo_size=None, photo_width=None, photo_height=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, + send_phone_number_to_provider=None, send_email_to_provider=None, + is_flexible=None): + self.title: str = title + self.description: str = description + self.payload: str = payload + self.provider_token: str = provider_token + self.currency: str = currency + self.prices: List[LabeledPrice] = prices + self.max_tip_amount: Optional[int] = max_tip_amount + self.suggested_tip_amounts: Optional[List[int]] = suggested_tip_amounts + self.provider_data: Optional[str] = provider_data + self.photo_url: Optional[str] = photo_url + self.photo_size: Optional[int] = photo_size + self.photo_width: Optional[int] = photo_width + self.photo_height: Optional[int] = photo_height + self.need_name: Optional[bool] = need_name + self.need_phone_number: Optional[bool] = need_phone_number + self.need_email: Optional[bool] = need_email + self.need_shipping_address: Optional[bool] = need_shipping_address + self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider + self.send_email_to_provider: Optional[bool] = send_email_to_provider + self.is_flexible: Optional[bool] = is_flexible + + def to_dict(self): + json_dict = { + 'title': self.title, + 'description': self.description, + 'payload': self.payload, + 'provider_token': self.provider_token, + 'currency': self.currency, + 'prices': [LabeledPrice.to_dict(lp) for lp in self.prices] + } + if self.max_tip_amount: + json_dict['max_tip_amount'] = self.max_tip_amount + if self.suggested_tip_amounts: + json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts + if self.provider_data: + json_dict['provider_data'] = self.provider_data + if self.photo_url: + json_dict['photo_url'] = self.photo_url + if self.photo_size: + json_dict['photo_size'] = self.photo_size + if self.photo_width: + json_dict['photo_width'] = self.photo_width + if self.photo_height: + json_dict['photo_height'] = self.photo_height + if self.need_name is not None: + json_dict['need_name'] = self.need_name + if self.need_phone_number is not None: + json_dict['need_phone_number'] = self.need_phone_number + if self.need_email is not None: + json_dict['need_email'] = self.need_email + if self.need_shipping_address is not None: + json_dict['need_shipping_address'] = self.need_shipping_address + if self.send_phone_number_to_provider is not None: + json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider + if self.send_email_to_provider is not None: + json_dict['send_email_to_provider'] = self.send_email_to_provider + if self.is_flexible is not None: + json_dict['is_flexible'] = self.is_flexible + + class ChosenInlineResult(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -2135,10 +2202,13 @@ def __init__(self, label, amount): self.label: str = label self.amount: int = amount - def to_json(self): - return json.dumps({ + def to_dict(self): + return { 'label': self.label, 'amount': self.amount - }) + } + + def to_json(self): + return json.dumps(self.to_dict()) class Invoice(JsonDeserializable): From 558eef78b412901cf8ab195d46bf2bac57f891df Mon Sep 17 00:00:00 2001 From: Pablo Date: Mon, 21 Jun 2021 17:27:35 -0500 Subject: [PATCH 0596/1808] Fix long string blocking version of python on github actions setup --- .github/workflows/setup_python.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 06318976e..d2bcdf786 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -1,6 +1,6 @@ # This is a basic workflow to help you get started with Actions -name: ActionsSetupPython +name: Setup # Controls when the action will run. on: @@ -21,7 +21,7 @@ jobs: strategy: matrix: python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.6', 'pypy-3.7' ] #'pypy-3.8', 'pypy-3.9' NOT SUPPORTED NOW - name: Python ${{ matrix.python-version }} setup and tests + name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 - name: Setup python From c00595e212d2b510a8708d7e96bcc1bdd6a550bd Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Tue, 22 Jun 2021 15:55:14 +0200 Subject: [PATCH 0597/1808] Update types.py * Added Parameter `caption_entities` to `InputMedia` class * Added Parameter `disable_content_type_detection` to `InputMediaDocument` class --- telebot/types.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 29e2b9d9e..d3384c0fc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -649,11 +649,11 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwargs): - self.file_size: int = file_size + self.file_id: str = file_id self.file_unique_id: str = file_unique_id - self.height: int = height self.width: int = width - self.file_id: str = file_id + self.height: int = height + self.file_size: int = file_size class Audio(JsonDeserializable): @@ -2411,11 +2411,12 @@ def to_dict(self): # InputMedia class InputMedia(Dictionaryable, JsonSerializable): - def __init__(self, type, media, caption=None, parse_mode=None): + def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None): self.type: str = type self.media: str = media - self.caption: str = caption - self.parse_mode: str = parse_mode + self.caption: Optional[str] = caption + self.parse_mode: Optional[str] = parse_mode + self.caption_entities: Optional[List[MessageEntity]] = caption_entities if util.is_string(self.media): self._media_name = '' @@ -2433,6 +2434,8 @@ def to_dict(self): json_dict['caption'] = self.caption 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] return json_dict def convert_input_media(self): @@ -2521,14 +2524,17 @@ def to_dict(self): class InputMediaDocument(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None): super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb + self.disable_content_type_detection = disable_content_type_detection def to_dict(self): ret = super(InputMediaDocument, self).to_dict() if self.thumb: ret['thumb'] = self.thumb + if self.disable_content_type_detection is not None: + ret['disable_content_type_detection'] = self.disable_content_type_detection return ret From bffbe764e5805e8de8fe43ab8188a74978e379a2 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Tue, 22 Jun 2021 15:57:34 +0200 Subject: [PATCH 0598/1808] Update tgs_sticker support * Updated `create_new_sticker_set` and `add_sticker_to_set` functions * Removed `create_new_animated_sticker_set` and `add_sticker_to_animated_sticker_set` functions --- telebot/__init__.py | 65 +++++++++++--------------------------------- telebot/apihelper.py | 16 +++++++---- 2 files changed, 26 insertions(+), 55 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a751be315..8bb417e34 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2104,32 +2104,9 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> typ def create_new_sticker_set( self, user_id: int, name: str, title: str, - png_sticker: Union[Any, str], emojis: str, - contains_masks: Optional[bool]=None, - mask_position: Optional[types.MaskPosition]=None) -> bool: - """ - Use this method to create new sticker set owned by a user. - The bot will be able to edit the created sticker set. - Returns True on success. - :param user_id: - :param name: - :param title: - :param png_sticker: - :param emojis: - :param contains_masks: - :param mask_position: - :return: - """ - return apihelper.create_new_sticker_set( - self.token, user_id, name, title, png_sticker, emojis, - contains_masks, mask_position, animated=False) - - - def create_new_animated_sticker_set( - self, user_id: int, name: str, title: str, + png_sticker: Union[Any, str], tgs_sticker: Union[Any, str], - emojis: str, contains_masks: Optional[bool]=None, mask_position: Optional[types.MaskPosition]=None) -> bool: """ @@ -2139,47 +2116,37 @@ def create_new_animated_sticker_set( :param user_id: :param name: :param title: - :param tgs_sticker: :param emojis: + :param png_sticker: + :param tgs_sticker: :param contains_masks: :param mask_position: :return: """ return apihelper.create_new_sticker_set( - self.token, user_id, name, title, tgs_sticker, emojis, - contains_masks, mask_position, animated=True) - - - def add_sticker_to_set( - self, user_id: int, name: str, png_sticker: Union[Any, str], - emojis: str, mask_position: Optional[types.MaskPosition]=None) -> bool: - """ - Use this method to add a new sticker to a set created by the bot. Returns True on success. - :param user_id: - :param name: - :param png_sticker: - :param emojis: - :param mask_position: - :return: - """ - return apihelper.add_sticker_to_set( - self.token, user_id, name, png_sticker, emojis, mask_position, animated=False) + self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, + contains_masks, mask_position) - def add_sticker_to_animated_set( - self, user_id: int, name: str, tgs_sticker: Union[Any, str], - emojis: str, mask_position: Optional[types.MaskPosition]=None) -> bool: + def add_sticker_to_set( + self, user_id: int, name: str, emojis: str, + png_sticker: Optional[Union[Any, str]]=None, + tgs_sticker: Optional[Union[Any, str]]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: """ - Use this method to add a new sticker to a set created by the bot. Returns True on success. + Use this method to add a new sticker to a set created by the bot. + It's required to pass `png_sticker` or `tgs_sticker`. + Returns True on success. :param user_id: :param name: - :param tgs_sticker: :param emojis: + :param png_sticker: Required if `tgs_sticker` is None + :param tgs_sticker: Required if `png_sticker` is None :param mask_position: :return: """ return apihelper.add_sticker_to_set( - self.token, user_id, name, tgs_sticker, emojis, mask_position, animated=True) + self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position) def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 106702e16..867eef874 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -810,7 +810,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, disable_content_type_detection=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -842,6 +842,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if method_url == 'sendDocument' and disable_content_type_detection is not None: + payload['disable_content_type_detection'] = disable_content_type_detection return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1388,12 +1390,13 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( - token, user_id, name, title, sticker, emojis, - contains_masks=None, mask_position=None, animated=False): + token, user_id, name, title, emojis, png_sticker, tgs_sticker, + contains_masks=None, mask_position=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} + stype = 'png_sticker' if png_sticker else 'tgs_sticker' + sticker = png_sticker or tgs_sticker files = None - stype = 'tgs_sticker' if animated else 'png_sticker' if not util.is_string(sticker): files = {stype: sticker} else: @@ -1405,11 +1408,12 @@ def create_new_sticker_set( return _make_request(token, method_url, params=payload, files=files, method='post') -def add_sticker_to_set(token, user_id, name, sticker, emojis, mask_position, animated=False): +def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} + stype = 'png_sticker' if png_sticker else 'tgs_sticker' + sticker = png_sticker or tgs_sticker files = None - stype = 'tgs_sticker' if animated else 'png_sticker' if not util.is_string(sticker): files = {stype: sticker} else: From 65cf8410155cd3b9cb6d4d1ca8499bea9f492cc4 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 23 Jun 2021 16:09:40 +0200 Subject: [PATCH 0599/1808] Update util.py added `allowed_updates` list (used by `_init_._retrieve_all_updates` because `chat_member` is not requested by default) --- telebot/util.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index cb1257721..8af740c7a 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -38,6 +38,10 @@ 'voice_chat_participants_invited', 'message_auto_delete_timer_changed' ] +allowed_updates = ["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" +] class WorkerThread(threading.Thread): count = 0 From 4554cb969f4e7c49ce87006485880f4caa7091b3 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 23 Jun 2021 16:10:48 +0200 Subject: [PATCH 0600/1808] Update __init__.py added handlers for `my_chat_member` and `chat_member` --- telebot/__init__.py | 73 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8bb417e34..d95958270 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -155,6 +155,8 @@ def __init__( self.pre_checkout_query_handlers = [] self.poll_handlers = [] self.poll_answer_handlers = [] + self.my_chat_member_handlers = [] + self.chat_member_handlers = [] if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { @@ -168,6 +170,9 @@ def __init__( 'shipping_query': [], 'pre_checkout_query': [], 'poll': [], + 'poll_answer': [], + 'my_chat_member': [], + 'chat_member': [] } self.default_middleware_handlers = [] @@ -354,7 +359,8 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20): if self.skip_pending: logger.debug('Skipped {0} pending messages'.format(self.__skip_updates())) self.skip_pending = False - updates = self.get_updates(offset=(self.last_update_id + 1), + updates = self.get_updates(offset=(self.last_update_id + 1), + allowed_updates=util.allowed_updates, timeout=timeout, long_polling_timeout=long_polling_timeout) self.process_new_updates(updates) @@ -374,6 +380,8 @@ def process_new_updates(self, updates): new_pre_checkout_queries = None new_polls = None new_poll_answers = None + new_my_chat_members = None + new_chat_members = None for update in updates: if apihelper.ENABLE_MIDDLEWARE: @@ -422,6 +430,12 @@ def process_new_updates(self, updates): if update.poll_answer: if new_poll_answers is None: new_poll_answers = [] new_poll_answers.append(update.poll_answer) + if update.my_chat_member: + if new_my_chat_members is None: new_my_chat_members = [] + new_my_chat_members.append(update.my_chat_member) + if update.chat_member: + if new_chat_members is None: new_chat_members = [] + new_chat_members.append(update.chat_member) if new_messages: self.process_new_messages(new_messages) @@ -445,6 +459,10 @@ def process_new_updates(self, updates): self.process_new_poll(new_polls) if new_poll_answers: self.process_new_poll_answer(new_poll_answers) + if new_my_chat_members: + self.process_new_my_chat_member(new_my_chat_members) + if new_chat_members: + self.process_new_chat_member(new_chat_members) def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) @@ -481,6 +499,12 @@ def process_new_poll(self, polls): def process_new_poll_answer(self, poll_answers): self._notify_command_handlers(self.poll_answer_handlers, poll_answers) + + def process_new_my_chat_member(self, my_chat_members): + self._notify_command_handlers(self.my_chat_member_handlers, my_chat_members) + + def process_new_chat_member(self, chat_members): + self._notify_command_handlers(self.chat_member_handlers, chat_members) def process_middlewares(self, update): for update_type, middlewares in self.typed_middleware_handlers.items(): @@ -2665,6 +2689,53 @@ def add_poll_answer_handler(self, handler_dict): :return: """ self.poll_answer_handlers.append(handler_dict) + + def my_chat_member_handler(self, func=None, **kwargs): + """ + my_chat_member handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_my_chat_member_handler(handler_dict) + return handler + + return decorator + + def add_my_chat_member_handler(self, handler_dict): + """ + Adds a my_chat_member handler + :param handler_dict: + :return: + """ + self.my_chat_member_handlers.append(handler_dict) + + def chat_member_handler(self, func=None, **kwargs): + """ + chat_member handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_member_handler(handler_dict) + return handler + + return decorator + + def add_chat_member_handler(self, handler_dict): + """ + Adds a chat_member handler + :param handler_dict: + :return: + """ + self.chat_member_handlers.append(handler_dict) + def _test_message_handler(self, message_handler, message): """ From 506464e6370183e4047d09bcd87c18c573832168 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 23 Jun 2021 19:29:36 +0200 Subject: [PATCH 0601/1808] Update __init__.py Added the parameter `allowed_updates` to polling and infinity_polling functions --- telebot/__init__.py | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d95958270..967f986c9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -350,7 +350,7 @@ def __skip_updates(self): updates = self.get_updates(offset=self.last_update_id + 1, long_polling_timeout=1) return total - def __retrieve_updates(self, timeout=20, long_polling_timeout=20): + def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_updates=None): """ Retrieves any updates from the Telegram API. Registered listeners and applicable message handlers will be notified when a new message arrives. @@ -360,7 +360,7 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20): logger.debug('Skipped {0} pending messages'.format(self.__skip_updates())) self.skip_pending = False updates = self.get_updates(offset=(self.last_update_id + 1), - allowed_updates=util.allowed_updates, + allowed_updates=allowed_updates, timeout=timeout, long_polling_timeout=long_polling_timeout) self.process_new_updates(updates) @@ -530,7 +530,8 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=logging.ERROR, *args, **kwargs): + def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=logging.ERROR, + allowed_updates=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -538,11 +539,19 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging + :param allowed_updates: A list of the update types you want your bot to receive. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.allowed_updates for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + 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. """ while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, - *args, **kwargs) + allowed_updates=allowed_updates *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) @@ -555,7 +564,8 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log if logger_level and logger_level >= logging.INFO: logger.error("Break infinity polling") - def polling(self, none_stop=False, interval=0, timeout=20, long_polling_timeout=20): + def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, + long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=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. @@ -567,14 +577,22 @@ def polling(self, none_stop=False, interval=0, timeout=20, long_polling_timeout= :param none_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. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.allowed_updates for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + 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. :return: """ if self.threaded: - self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout) + self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) else: - self.__non_threaded_polling(none_stop, interval, timeout, long_polling_timeout) + self.__non_threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) - def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None): + def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None, allowed_updates=None): logger.info('Started polling.') self.__stop_polling.clear() error_interval = 0.25 @@ -589,7 +607,7 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po while not self.__stop_polling.wait(interval): or_event.clear() try: - polling_thread.put(self.__retrieve_updates, timeout, long_polling_timeout) + polling_thread.put(self.__retrieve_updates, timeout, long_polling_timeout, allowed_updates=allowed_updates) or_event.wait() # wait for polling thread finish, polling thread error or thread pool error polling_thread.raise_exceptions() self.worker_pool.raise_exceptions() @@ -640,14 +658,14 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po self.worker_pool.clear_exceptions() #* logger.info('Stopped polling.') - def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None): + def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None, allowed_updates=None): logger.info('Started polling.') self.__stop_polling.clear() error_interval = 0.25 while not self.__stop_polling.wait(interval): try: - self.__retrieve_updates(timeout, long_polling_timeout) + self.__retrieve_updates(timeout, long_polling_timeout, allowed_updates=allowed_updates) error_interval = 0.25 except apihelper.ApiException as e: if self.exception_handler is not None: From 0bfefdf15deb305bb46ac76d4ad4c05b64ace3ea Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 23 Jun 2021 19:57:44 +0200 Subject: [PATCH 0602/1808] changed allowed_updates in util to update_types i think its more clear name --- telebot/__init__.py | 4 ++-- telebot/util.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 967f986c9..86f65c65c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -541,7 +541,7 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log Use logger levels from logging as a value. None/NOTSET = no error logging :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.allowed_updates for a complete list of available update types. + See util.update_types for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. @@ -579,7 +579,7 @@ def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, :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. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.allowed_updates for a complete list of available update types. + See util.update_types for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. diff --git a/telebot/util.py b/telebot/util.py index 8af740c7a..3dd71dbd9 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -38,9 +38,10 @@ 'voice_chat_participants_invited', 'message_auto_delete_timer_changed' ] -allowed_updates = ["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" +update_types = [ + "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" ] class WorkerThread(threading.Thread): From 3d5415433e1749a99925e781fd4f1bd6c4218f7d Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 23 Jun 2021 22:51:17 +0200 Subject: [PATCH 0603/1808] Update __init__.py Updated TeleBot doc string and added the missing functions to AsyncTeleBot --- telebot/__init__.py | 75 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 86f65c65c..f06ee7323 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -73,29 +73,55 @@ class TeleBot: close sendMessage forwardMessage + copyMessage deleteMessage sendPhoto sendAudio sendDocument sendSticker sendVideo + sendVenue sendAnimation sendVideoNote sendLocation sendChatAction sendDice + sendContact + sendInvoice + sendMediaGroup getUserProfilePhotos getUpdates getFile sendPoll + stopPoll + sendGame + setGameScore + getGameHighScores + editMessageText + editMessageCaption + editMessageMedia + editMessageReplyMarkup + editMessageLiveLocation + stopMessageLiveLocation kickChatMember unbanChatMember restrictChatMember promoteChatMember + setChatAdministratorCustomTitle + setChatPermissions createChatInviteLink editChatInviteLink revokeChatInviteLink exportChatInviteLink + setChatStickerSet + deleteChatStickerSet + createNewStickerSet + addStickerToSet + deleteStickerFromSet + setStickerPositionInSet + uploadStickerFile + setStickerSetThumb + getStickerSet setChatPhoto deleteChatPhoto setChatTitle @@ -111,6 +137,8 @@ class TeleBot: getMyCommands setMyCommands answerInlineQuery + answerShippingQuery + answerPreCheckoutQuery """ def __init__( @@ -2808,6 +2836,8 @@ def _notify_command_handlers(self, handlers, new_messages): class AsyncTeleBot(TeleBot): def __init__(self, *args, **kwargs): TeleBot.__init__(self, *args, **kwargs) + + # I'm not sure if `get_updates` should be added here too @util.async_dec() def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): @@ -2837,6 +2867,22 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a def get_me(self): return TeleBot.get_me(self) + @util.async_dec() + def log_out(self): + return TeleBot.log_out(self) + + @util.async_dec() + def close(self): + return TeleBot.close(self) + + @util.async_dec() + def get_my_commands(self): + return TeleBot.get_my_commands(self) + + @util.async_dec() + def set_my_commands(self, *args, **kwargs): + return TeleBot.set_my_commands(self, *args, **kwargs) + @util.async_dec() def get_file(self, *args): return TeleBot.get_file(self, *args) @@ -2885,6 +2931,10 @@ def send_message(self, *args, **kwargs): def send_dice(self, *args, **kwargs): return TeleBot.send_dice(self, *args, **kwargs) + @util.async_dec() + def send_animation(self, *args, **kwargs): + return TeleBot.send_animation(self, *args, **kwargs) + @util.async_dec() def forward_message(self, *args, **kwargs): return TeleBot.forward_message(self, *args, **kwargs) @@ -2893,7 +2943,6 @@ def forward_message(self, *args, **kwargs): def copy_message(self, *args, **kwargs): return TeleBot.copy_message(self, *args, **kwargs) - @util.async_dec() def delete_message(self, *args): return TeleBot.delete_message(self, *args) @@ -2969,7 +3018,27 @@ def restrict_chat_member(self, *args, **kwargs): @util.async_dec() def promote_chat_member(self, *args, **kwargs): return TeleBot.promote_chat_member(self, *args, **kwargs) + + @util.async_dec() + def set_chat_administrator_custom_title(self, *args, **kwargs): + return TeleBot.set_chat_administrator_custom_title(self, *args, **kwargs) + + @util.async_dec() + def set_chat_permissions(self, *args, **kwargs): + return TeleBot.set_chat_permissions(self, *args, **kwargs) + @util.async_dec() + def create_chat_invite_link(self, *args, **kwargs): + return TeleBot.create_chat_invite_link(self, *args, **kwargs) + + @util.async_dec() + def edit_chat_invite_link(self, *args, **kwargs): + return TeleBot.edit_chat_invite_link(self, *args, **kwargs) + + @util.async_dec() + def revoke_chat_invite_link(self, *args, **kwargs): + return TeleBot.revoke_chat_invite_link(self, *args, **kwargs) + @util.async_dec() def export_chat_invite_link(self, *args): return TeleBot.export_chat_invite_link(self, *args) @@ -3073,6 +3142,10 @@ def set_sticker_position_in_set(self, *args, **kwargs): @util.async_dec() def delete_sticker_from_set(self, *args, **kwargs): return TeleBot.delete_sticker_from_set(self, *args, **kwargs) + + @util.async_dec() + def set_sticker_set_thumb(self, *args, **kwargs): + return TeleBot.set_sticker_set_thumb(self, *args, **kwargs) @util.async_dec() def send_poll(self, *args, **kwargs): From ce991e9ac3799b9bff72bd99feb76951ded8c446 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 23 Jun 2021 22:52:24 +0200 Subject: [PATCH 0604/1808] Update types.py added the missing attributes `can_manage_chat` and `can_manage_voice_chats` to ChatMember class --- telebot/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index d3384c0fc..ad7e4d302 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1174,7 +1174,9 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed can_restrict_members=None, can_promote_members=None, can_change_info=None, can_invite_users=None, can_pin_messages=None, is_member=None, can_send_messages=None, can_send_media_messages=None, can_send_polls=None, - can_send_other_messages=None, can_add_web_page_previews=None, until_date=None, **kwargs): + can_send_other_messages=None, can_add_web_page_previews=None, + can_manage_chat=None, can_manage_voice_chats=None, + until_date=None, **kwargs): self.user: User = user self.status: str = status self.custom_title: str = custom_title @@ -1194,6 +1196,8 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_send_polls: bool = can_send_polls self.can_send_other_messages: bool = can_send_other_messages self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_manage_chat: bool = can_manage_chat + self.can_manage_voice_chats: bool = can_manage_voice_chats self.until_date: int = until_date From 3e33b7f1cb7990e28104ac2a0454a72f7adb439d Mon Sep 17 00:00:00 2001 From: MAIKS1900 Date: Sat, 26 Jun 2021 14:36:14 +0300 Subject: [PATCH 0605/1808] Bot API 5.3 changes - Personalized Commands for different chats - Custom Placeholders of input field for ReplyKeyboardMarkup and ForceReply. --- telebot/__init__.py | 43 ++++++++++++++++++++++++---- telebot/apihelper.py | 31 +++++++++++++++++---- telebot/types.py | 65 +++++++++++++++++++++++++++++++++++++++++-- tests/test_telebot.py | 18 ++++++++++++ 4 files changed, 142 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f06ee7323..753e7d3fd 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1703,21 +1703,52 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ return apihelper.delete_chat_photo(self.token, chat_id) - def get_my_commands(self) -> List[types.BotCommand]: - """ - Use this method to get the current list of the bot's commands. + def get_my_commands(self, + scope: Optional[Union[ + types.BotCommandScopeDefault, types.BotCommandScopeAllPrivateChats, + types.BotCommandScopeAllGroupChats, types.BotCommandScopeAllChatAdministrators, + types.BotCommandScopeChat, + types.BotCommandScopeChatAdministrators, types.BotCommandScopeChatMember]]=None, + language_code: Optional[str]=None) -> List[types.BotCommand]: + """ + Use this method to get the current list of the bot's commands for the given scope and user language + :param scope: scope of users for which the commands are relevant + :param language_code: A two-letter ISO 639-1 language code Returns List of BotCommand on success. """ - result = apihelper.get_my_commands(self.token) + result = apihelper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] - def set_my_commands(self, commands: List[types.BotCommand]) -> bool: + def set_my_commands(self, commands: List[types.BotCommand], + scope: Optional[Union[ + types.BotCommandScopeDefault, types.BotCommandScopeAllPrivateChats, + types.BotCommandScopeAllGroupChats, types.BotCommandScopeAllChatAdministrators, + types.BotCommandScopeChat, + types.BotCommandScopeChatAdministrators, types.BotCommandScopeChatMember]] = None, + language_code: Optional[str]=None) -> bool: """ Use this method to change the list of the bot's commands. :param commands: List of BotCommand. At most 100 commands can be specified. + :param scope: scope of users for which the commands are relevant + :param language_code: A two-letter ISO 639-1 language code + :return: + """ + return apihelper.set_my_commands(self.token, commands, scope, language_code) + + def delete_my_commands(self, + scope: Optional[Union[ + types.BotCommandScopeDefault, types.BotCommandScopeAllPrivateChats, + types.BotCommandScopeAllGroupChats, types.BotCommandScopeAllChatAdministrators, + types.BotCommandScopeChat, + types.BotCommandScopeChatAdministrators, types.BotCommandScopeChatMember]]=None, + language_code: Optional[str]=None) -> bool: + """ + Use this method to delete the list of the bot's commands for the given scope and user language. + :param scope: scope of users for which the commands are relevant + :param language_code: A two-letter ISO 639-1 language code :return: """ - return apihelper.set_my_commands(self.token, commands) + return apihelper.delete_my_commands(self.token, scope, language_code) def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 867eef874..384c0bc1e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -169,11 +169,6 @@ def get_me(token): return _make_request(token, method_url) -def get_my_commands(token): - method_url = r'getMyCommands' - return _make_request(token, method_url) - - def log_out(token): method_url = r'logOut' return _make_request(token, method_url) @@ -1032,9 +1027,33 @@ def set_chat_title(token, chat_id, title): return _make_request(token, method_url, params=payload, method='post') -def set_my_commands(token, commands): +def get_my_commands(token, scope, language_code): + method_url = r'getMyCommands' + payload = {} + if scope is not None: + payload['scope'] = scope.to_json() + if language_code is not None: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload, method='post') + + +def set_my_commands(token, commands, scope, language_code): method_url = r'setMyCommands' payload = {'commands': _convert_list_json_serializable(commands)} + if scope is not None: + payload['scope'] = scope.to_json() + if language_code is not None: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload, method='post') + + +def delete_my_commands(token, scope, language_code): + method_url = r'deleteMyCommands' + payload = {} + if scope is not None: + payload['scope'] = scope.to_json() + if language_code is not None: + payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index ad7e4d302..19a6e0fe7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -848,13 +848,16 @@ def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): class ForceReply(JsonSerializable): - def __init__(self, selective=None): + def __init__(self, selective=None, input_field_placeholder=None): self.selective: bool = selective + self.input_field_placeholder = input_field_placeholder def to_json(self): json_dict = {'force_reply': True} if self.selective: json_dict['selective'] = True + if self.input_field_placeholder: + json_dict['input_field_placeholder'] = self.input_field_placeholder return json.dumps(json_dict) @@ -872,7 +875,8 @@ def to_json(self): class ReplyKeyboardMarkup(JsonSerializable): max_row_keys = 12 - def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3): + def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3, + input_field_placeholder=None): if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases if not DISABLE_KEYLEN_ERROR: @@ -883,6 +887,7 @@ def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, self.one_time_keyboard: bool = one_time_keyboard self.selective: bool = selective self.row_width: int = row_width + self.input_field_placeholder = input_field_placeholder self.keyboard: List[List[KeyboardButton]] = [] def add(self, *args, row_width=None): @@ -926,7 +931,7 @@ def row(self, *args): :param args: strings :return: self, to allow function chaining. """ - + return self.add(*args, row_width=self.max_row_keys) def to_json(self): @@ -942,6 +947,8 @@ def to_json(self): json_dict['resize_keyboard'] = True if self.selective: json_dict['selective'] = True + if self.input_field_placeholder: + json_dict['input_field_placeholder'] = self.input_field_placeholder return json.dumps(json_dict) @@ -1270,6 +1277,58 @@ def to_dict(self): return {'command': self.command, 'description': self.description} +# BotCommandScopes + +class BotCommandScope(JsonSerializable): + def __init__(self, type='default', chat_id=None, user_id=None): + self.type: str = type + self.chat_id: Optional[Union[int, str]] = chat_id + self.user_id: Optional[Union[int, str]] = user_id + + def to_json(self): + json_dict = {'type': self.type} + if self.chat_id: + json_dict['chat_id'] = self.chat_id + if self.user_id: + json_dict['user_id'] = self.user_id + return json.dumps(json_dict) + + +class BotCommandScopeDefault(BotCommandScope): + def __init__(self): + super(BotCommandScopeDefault, self).__init__(type='default') + + +class BotCommandScopeAllPrivateChats(BotCommandScope): + def __init__(self): + super(BotCommandScopeAllPrivateChats, self).__init__(type='all_private_chats') + + +class BotCommandScopeAllGroupChats(BotCommandScope): + def __init__(self): + super(BotCommandScopeAllGroupChats, self).__init__(type='all_group_chats') + + +class BotCommandScopeAllChatAdministrators(BotCommandScope): + def __init__(self): + super(BotCommandScopeAllChatAdministrators, self).__init__(type='all_chat_administrators') + + +class BotCommandScopeChat(BotCommandScope): + def __init__(self, chat_id=None): + super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) + + +class BotCommandScopeChatAdministrators(BotCommandScope): + def __init__(self, chat_id=None): + super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) + + +class BotCommandScopeChatMember(BotCommandScope): + def __init__(self, chat_id=None, user_id=None): + super(BotCommandScopeChatMember, self).__init__(type='chat_administrators', chat_id=chat_id, user_id=user_id) + + # InlineQuery class InlineQuery(JsonDeserializable): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index a22adcda0..a2f3d36e8 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -546,6 +546,24 @@ def test_send_document_formating_caption(self): ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown') assert ret_msg.caption_entities[0].type == 'italic' + def test_chat_commands(self): + tb = telebot.TeleBot(TOKEN) + command, description, lang = 'command_1', 'description of command 1', 'en' + scope = telebot.types.BotCommandScopeChat(CHAT_ID) + ret_msg = tb.set_my_commands([telebot.types.BotCommand(command, description)], scope, lang) + assert ret_msg is True + + ret_msg = tb.get_my_commands(scope, lang) + assert ret_msg[0].command == command + assert ret_msg[0].description == description + + ret_msg = tb.delete_my_commands(scope, lang) + assert ret_msg is True + + ret_msg = tb.get_my_commands(scope, lang) + assert ret_msg == [] + + def test_typed_middleware_handler(self): from telebot import apihelper From 38c4c21030f31d3e901adcb2c44e76e3a8dca79c Mon Sep 17 00:00:00 2001 From: Vlad Galatskiy Date: Sun, 27 Jun 2021 11:37:27 +0300 Subject: [PATCH 0606/1808] Add file_name argument to send_data method --- telebot/apihelper.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 867eef874..9566444e2 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -810,12 +810,15 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None): + allow_sending_without_reply=None, disable_content_type_detection=None, file_name=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None if not util.is_string(data): - files = {data_type: data} + file_data = data + if file_name is not None: + file_data = (file_name, data) + files = {data_type: file_data} else: payload[data_type] = data if reply_to_message_id: From e56f134a7c27ce01dd61905909af06ecd2115a13 Mon Sep 17 00:00:00 2001 From: Vlad Galatskiy Date: Sun, 27 Jun 2021 11:38:45 +0300 Subject: [PATCH 0607/1808] Add file_name support to send_document method --- telebot/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f06ee7323..dbe27650d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1114,7 +1114,8 @@ def send_document( timeout: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + file_name: Optional[str]=None) -> types.Message: """ Use this method to send general files. :param chat_id: @@ -1128,6 +1129,7 @@ def send_document( :param thumb: InputFile or String : Thumbnail of the file sent :param caption_entities: :param allow_sending_without_reply: + :param file_name: :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1136,7 +1138,7 @@ def send_document( 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)) + allow_sending_without_reply, file_name)) def send_sticker( self, chat_id: Union[int, str], data: Union[Any, str], From a791ff4e46c2496920863fc5f678f1e4df8352ad Mon Sep 17 00:00:00 2001 From: Vlad Galatskiy Date: Sun, 27 Jun 2021 11:58:33 +0300 Subject: [PATCH 0608/1808] Add tests for file sending with name --- tests/test_telebot.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index a22adcda0..377bd7211 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -126,6 +126,16 @@ def test_send_file(self): ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id) assert ret_msg.message_id + def test_send_file_with_filename(self): + file_data = open('../examples/detailed_example/kitten.jpg', 'rb') + tb = telebot.TeleBot(TOKEN) + + ret_msg = tb.send_document(CHAT_ID, file_data) + assert ret_msg.message_id + + ret_msg = tb.send_document(CHAT_ID, file_data, file_name="test.jpg") + assert ret_msg.message_id + def test_send_file_dis_noti(self): file_data = open('../examples/detailed_example/kitten.jpg', 'rb') tb = telebot.TeleBot(TOKEN) From c088fabe6c6d8527386649996d0749fd9d32e0d1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 27 Jun 2021 13:09:08 +0300 Subject: [PATCH 0609/1808] Release version 3.8.0 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 54f9f841a..54c4c5d23 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__ = '3.7.9' +__version__ = '3.8.0' From 491cc05a95c3b590e9a0ec6a9fdee42ffca74a97 Mon Sep 17 00:00:00 2001 From: MAIKS1900 Date: Sun, 27 Jun 2021 17:28:11 +0300 Subject: [PATCH 0610/1808] - Set BotCommandScope as abstract class. - Docstrings from telegram API Scope types --- telebot/types.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 19a6e0fe7..8688132a1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2,6 +2,7 @@ import logging from typing import Dict, List, Optional, Union +from abc import ABC try: import ujson as json @@ -1279,8 +1280,19 @@ def to_dict(self): # BotCommandScopes -class BotCommandScope(JsonSerializable): +class BotCommandScope(ABC, JsonSerializable): def __init__(self, type='default', chat_id=None, user_id=None): + """ + Abstract class. + Use BotCommandScopeX classes to set a specific scope type: + BotCommandScopeDefault + BotCommandScopeAllPrivateChats + BotCommandScopeAllGroupChats + BotCommandScopeAllChatAdministrators + BotCommandScopeChat + BotCommandScopeChatAdministrators + BotCommandScopeChatMember + """ self.type: str = type self.chat_id: Optional[Union[int, str]] = chat_id self.user_id: Optional[Union[int, str]] = user_id @@ -1296,21 +1308,34 @@ def to_json(self): class BotCommandScopeDefault(BotCommandScope): def __init__(self): + """ + Represents the default scope of bot commands. + Default commands are used if no commands with a narrower scope are specified for the user. + """ super(BotCommandScopeDefault, self).__init__(type='default') class BotCommandScopeAllPrivateChats(BotCommandScope): def __init__(self): + """ + Represents the scope of bot commands, covering all private chats. + """ super(BotCommandScopeAllPrivateChats, self).__init__(type='all_private_chats') class BotCommandScopeAllGroupChats(BotCommandScope): def __init__(self): + """ + Represents the scope of bot commands, covering all group and supergroup chats. + """ super(BotCommandScopeAllGroupChats, self).__init__(type='all_group_chats') class BotCommandScopeAllChatAdministrators(BotCommandScope): def __init__(self): + """ + Represents the scope of bot commands, covering all group and supergroup chat administrators. + """ super(BotCommandScopeAllChatAdministrators, self).__init__(type='all_chat_administrators') @@ -1321,11 +1346,20 @@ def __init__(self, chat_id=None): class BotCommandScopeChatAdministrators(BotCommandScope): def __init__(self, chat_id=None): + """ + Represents the scope of bot commands, covering a specific chat. + @param chat_id: Unique identifier for the target chat + """ super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) class BotCommandScopeChatMember(BotCommandScope): def __init__(self, chat_id=None, user_id=None): + """ + Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat + @param chat_id: Unique identifier for the target chat + @param user_id: Unique identifier of the target user + """ super(BotCommandScopeChatMember, self).__init__(type='chat_administrators', chat_id=chat_id, user_id=user_id) From a29c4af2ee14dfcfa008e1e99972d54a9df465f2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 27 Jun 2021 20:40:16 +0300 Subject: [PATCH 0611/1808] Post-release fix for infinity_polling --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f06ee7323..cce73fe4d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -579,7 +579,7 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, - allowed_updates=allowed_updates *args, **kwargs) + allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) From 0aa7a8a8f694459bee66dfb7652430bdfab26ec1 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 28 Jun 2021 09:31:06 +0200 Subject: [PATCH 0612/1808] new 5.3 function names added the new function names (the previous names are still working) from 5.3 and some other small changes --- telebot/__init__.py | 109 +++++++++++++++++++++++++++++-------------- telebot/apihelper.py | 39 ++++++++++++---- telebot/types.py | 27 +++++------ 3 files changed, 117 insertions(+), 58 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9864b59ab..7adeeb188 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -104,6 +104,7 @@ class TeleBot: editMessageLiveLocation stopMessageLiveLocation kickChatMember + banChatMember unbanChatMember restrictChatMember promoteChatMember @@ -132,10 +133,12 @@ class TeleBot: getChat getChatAdministrators getChatMembersCount + getChatMemberCount getChatMember answerCallbackQuery getMyCommands setMyCommands + deleteMyCommands answerInlineQuery answerShippingQuery answerPreCheckoutQuery @@ -844,6 +847,15 @@ def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ result = apihelper.get_chat_members_count(self.token, chat_id) return result + + def get_chat_member_count(self, chat_id: Union[int, str]) -> int: + """ + Use this method to get the number of members in a chat. Returns Int on success. + :param chat_id: + :return: + """ + result = apihelper.get_chat_member_count(self.token, chat_id) + return result def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: """ @@ -1475,6 +1487,26 @@ def kick_chat_member( """ return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + def ban_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, + revoke_messages: Optional[bool]=None) -> bool: + """ + Use this method to ban a user in a group, a supergroup or a channel. + In the case of supergroups and channels, the user will not be able to return to the chat on their + own using invite links, etc., unless unbanned first. + Returns True on success. + :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup + :param user_id: Int : Unique identifier of the target user + :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or + less than 30 seconds from the current time they are considered to be banned forever + :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. + If False, the user will be able to see messages in the group that were sent before the user was removed. + Always True for supergroups and channels. + :return: boolean + """ + return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + def unban_chat_member( self, chat_id: Union[int, str], user_id: int, only_if_banned: Optional[bool]=False) -> bool: @@ -1699,54 +1731,49 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: setting is off in the target group. :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :return: """ return apihelper.delete_chat_photo(self.token, chat_id) - def get_my_commands(self, - scope: Optional[Union[ - types.BotCommandScopeDefault, types.BotCommandScopeAllPrivateChats, - types.BotCommandScopeAllGroupChats, types.BotCommandScopeAllChatAdministrators, - types.BotCommandScopeChat, - types.BotCommandScopeChatAdministrators, types.BotCommandScopeChatMember]]=None, - language_code: Optional[str]=None) -> List[types.BotCommand]: - """ - Use this method to get the current list of the bot's commands for the given scope and user language - :param scope: scope of users for which the commands are relevant - :param language_code: A two-letter ISO 639-1 language code + def get_my_commands(self, scope: Optional[types.BotCommandScope], + language_code: Optional[str]) -> List[types.BotCommand]: + """ + Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. + :param scope: The scope of users for which the commands are relevant. + Defaults to BotCommandScopeDefault. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands """ result = apihelper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] - def set_my_commands(self, commands: List[types.BotCommand], - scope: Optional[Union[ - types.BotCommandScopeDefault, types.BotCommandScopeAllPrivateChats, - types.BotCommandScopeAllGroupChats, types.BotCommandScopeAllChatAdministrators, - types.BotCommandScopeChat, - types.BotCommandScopeChatAdministrators, types.BotCommandScopeChatMember]] = None, - language_code: Optional[str]=None) -> bool: + def set_my_commands(self, commands: List[types.BotCommand], + scope: Optional[types.BotCommandScope]=None, + language_code: Optional[str]=None) -> bool: """ Use this method to change the list of the bot's commands. :param commands: List of BotCommand. At most 100 commands can be specified. - :param scope: scope of users for which the commands are relevant - :param language_code: A two-letter ISO 639-1 language code + :param scope: The scope of users for which the commands are relevant. + Defaults to BotCommandScopeDefault. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands :return: """ return apihelper.set_my_commands(self.token, commands, scope, language_code) - - def delete_my_commands(self, - scope: Optional[Union[ - types.BotCommandScopeDefault, types.BotCommandScopeAllPrivateChats, - types.BotCommandScopeAllGroupChats, types.BotCommandScopeAllChatAdministrators, - types.BotCommandScopeChat, - types.BotCommandScopeChatAdministrators, types.BotCommandScopeChatMember]]=None, - language_code: Optional[str]=None) -> bool: + + def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, + language_code: Optional[int]=None) -> bool: """ - Use this method to delete the list of the bot's commands for the given scope and user language. - :param scope: scope of users for which the commands are relevant - :param language_code: A two-letter ISO 639-1 language code - :return: + Use this method to delete the list of the bot's commands for the given scope and user language. + After deletion, higher level commands will be shown to affected users. + Returns True on success. + :param scope: The scope of users for which the commands are relevant. + Defaults to BotCommandScopeDefault. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands """ return apihelper.delete_my_commands(self.token, scope, language_code) @@ -2907,12 +2934,16 @@ def close(self): return TeleBot.close(self) @util.async_dec() - def get_my_commands(self): - return TeleBot.get_my_commands(self) + def get_my_commands(self, *args, **kwargs): # needed args because new scope and language_code + return TeleBot.get_my_commands(self, *args, **kwargs) @util.async_dec() def set_my_commands(self, *args, **kwargs): return TeleBot.set_my_commands(self, *args, **kwargs) + + @util.async_dec() + def delete_my_commands(self, *args, **kwargs): + return TeleBot.delete_my_commands(self, *args, **kwargs) @util.async_dec() def get_file(self, *args): @@ -2941,6 +2972,10 @@ def get_chat_administrators(self, *args): @util.async_dec() def get_chat_members_count(self, *args): return TeleBot.get_chat_members_count(self, *args) + + @util.async_dec() + def get_chat_member_count(self, *args): + return TeleBot.get_chat_member_count(self, *args) @util.async_dec() def set_chat_sticker_set(self, *args): @@ -3037,6 +3072,10 @@ def send_chat_action(self, *args, **kwargs): @util.async_dec() def kick_chat_member(self, *args, **kwargs): return TeleBot.kick_chat_member(self, *args, **kwargs) + + @util.async_dec() + def ban_chat_member(self, *args, **kwargs): + return TeleBot.ban_chat_member(self, *args, **kwargs) @util.async_dec() def unban_chat_member(self, *args, **kwargs): diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 384c0bc1e..7b1779a8c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -93,6 +93,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): # Long polling hangs for given time. Read timeout should be greater that long_polling_timeout read_timeout = max(params['timeout'] + 10, read_timeout) + params = params or None #set params to None if empty result = None if RETRY_ON_ERROR: @@ -335,6 +336,12 @@ def get_chat_members_count(token, chat_id): return _make_request(token, method_url, params=payload) +def get_chat_member_count(token, chat_id): + method_url = r'getChatMemberCount' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + + def set_sticker_set_thumb(token, name, user_id, thumb): method_url = r'setStickerSetThumb' payload = {'name': name, 'user_id': user_id} @@ -861,6 +868,18 @@ def kick_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=N return _make_request(token, method_url, params=payload, method='post') +def ban_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None): + method_url = 'banChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id} + if isinstance(until_date, datetime): + payload['until_date'] = until_date.timestamp() + else: + payload['until_date'] = until_date + if revoke_messages is not None: + payload['revoke_messages'] = revoke_messages + return _make_request(token, method_url, params=payload, method='post') + + def unban_chat_member(token, chat_id, user_id, only_if_banned): method_url = 'unbanChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} @@ -1027,32 +1046,32 @@ def set_chat_title(token, chat_id, title): return _make_request(token, method_url, params=payload, method='post') -def get_my_commands(token, scope, language_code): +def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' payload = {} - if scope is not None: + if scope: payload['scope'] = scope.to_json() - if language_code is not None: + if language_code: payload['language_code'] = language_code - return _make_request(token, method_url, params=payload, method='post') + return _make_request(token, method_url, params=payload) -def set_my_commands(token, commands, scope, language_code): +def set_my_commands(token, commands, scope=None, language_code=None): method_url = r'setMyCommands' payload = {'commands': _convert_list_json_serializable(commands)} - if scope is not None: + if scope: payload['scope'] = scope.to_json() - if language_code is not None: + if language_code: payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') -def delete_my_commands(token, scope, language_code): +def delete_my_commands(token, scope=None, language_code=None): method_url = r'deleteMyCommands' payload = {} - if scope is not None: + if scope: payload['scope'] = scope.to_json() - if language_code is not None: + if language_code: payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 8688132a1..aed2e920f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -849,13 +849,13 @@ def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): class ForceReply(JsonSerializable): - def __init__(self, selective=None, input_field_placeholder=None): + def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Optional[str]=None): self.selective: bool = selective - self.input_field_placeholder = input_field_placeholder + self.input_field_placeholder: str = input_field_placeholder def to_json(self): json_dict = {'force_reply': True} - if self.selective: + if self.selective is not None: json_dict['selective'] = True if self.input_field_placeholder: json_dict['input_field_placeholder'] = self.input_field_placeholder @@ -876,8 +876,8 @@ def to_json(self): class ReplyKeyboardMarkup(JsonSerializable): max_row_keys = 12 - def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3, - input_field_placeholder=None): + def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, + selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None): if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases if not DISABLE_KEYLEN_ERROR: @@ -888,7 +888,7 @@ def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, self.one_time_keyboard: bool = one_time_keyboard self.selective: bool = selective self.row_width: int = row_width - self.input_field_placeholder = input_field_placeholder + self.input_field_placeholder: str = input_field_placeholder self.keyboard: List[List[KeyboardButton]] = [] def add(self, *args, row_width=None): @@ -942,11 +942,11 @@ def to_json(self): :return: """ json_dict = {'keyboard': self.keyboard} - if self.one_time_keyboard: + if self.one_time_keyboard is not None: json_dict['one_time_keyboard'] = True - if self.resize_keyboard: + if self.resize_keyboard is not None: json_dict['resize_keyboard'] = True - if self.selective: + if self.selective is not None: json_dict['selective'] = True if self.input_field_placeholder: json_dict['input_field_placeholder'] = self.input_field_placeholder @@ -954,7 +954,8 @@ def to_json(self): class KeyboardButton(Dictionaryable, JsonSerializable): - def __init__(self, text, request_contact=None, request_location=None, request_poll=None): + def __init__(self, text: str, request_contact: Optional[bool]=None, + request_location: Optional[bool]=None, request_poll: Optional[bool]=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location @@ -965,11 +966,11 @@ def to_json(self): def to_dict(self): json_dict = {'text': self.text} - if self.request_contact: + if self.request_contact is not None: json_dict['request_contact'] = self.request_contact - if self.request_location: + if self.request_location is not None: json_dict['request_location'] = self.request_location - if self.request_poll: + if self.request_poll is not None: json_dict['request_poll'] = self.request_poll.to_dict() return json_dict From 60bb63ab2b48ae084349eedab854bf62c61d02c4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 28 Jun 2021 12:41:15 +0300 Subject: [PATCH 0613/1808] Release 3.8.1 - bugfix --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 54c4c5d23..b74c7d354 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__ = '3.8.0' +__version__ = '3.8.1' From 0b383498eb8181709c79cd214da38ec6457455f2 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 28 Jun 2021 11:59:21 +0200 Subject: [PATCH 0614/1808] addded logger info for deprecated funcs --- telebot/__init__.py | 14 ++++++++------ telebot/apihelper.py | 18 ------------------ 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7adeeb188..3c14456df 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -103,7 +103,6 @@ class TeleBot: editMessageReplyMarkup editMessageLiveLocation stopMessageLiveLocation - kickChatMember banChatMember unbanChatMember restrictChatMember @@ -132,7 +131,6 @@ class TeleBot: leaveChat getChat getChatAdministrators - getChatMembersCount getChatMemberCount getChatMember answerCallbackQuery @@ -845,7 +843,8 @@ def get_chat_members_count(self, chat_id: Union[int, str]) -> int: :param chat_id: :return: """ - result = apihelper.get_chat_members_count(self.token, chat_id) + logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead.') + result = apihelper.get_chat_member_count(self.token, chat_id) return result def get_chat_member_count(self, chat_id: Union[int, str]) -> int: @@ -1485,7 +1484,8 @@ def kick_chat_member( Always True for supergroups and channels. :return: boolean """ - return apihelper.kick_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') + return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) def ban_chat_member( self, chat_id: Union[int, str], user_id: int, @@ -2971,7 +2971,8 @@ def get_chat_administrators(self, *args): @util.async_dec() def get_chat_members_count(self, *args): - return TeleBot.get_chat_members_count(self, *args) + logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead') + return TeleBot.get_chat_member_count(self, *args) @util.async_dec() def get_chat_member_count(self, *args): @@ -3071,7 +3072,8 @@ def send_chat_action(self, *args, **kwargs): @util.async_dec() def kick_chat_member(self, *args, **kwargs): - return TeleBot.kick_chat_member(self, *args, **kwargs) + logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') + return TeleBot.ban_chat_member(self, *args, **kwargs) @util.async_dec() def ban_chat_member(self, *args, **kwargs): diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 7b1779a8c..ca8ec8ca6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -330,12 +330,6 @@ def get_chat_administrators(token, chat_id): return _make_request(token, method_url, params=payload) -def get_chat_members_count(token, chat_id): - method_url = r'getChatMembersCount' - payload = {'chat_id': chat_id} - return _make_request(token, method_url, params=payload) - - def get_chat_member_count(token, chat_id): method_url = r'getChatMemberCount' payload = {'chat_id': chat_id} @@ -856,18 +850,6 @@ def get_method_by_type(data_type): return r'sendSticker' -def kick_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None): - method_url = 'kickChatMember' - payload = {'chat_id': chat_id, 'user_id': user_id} - if isinstance(until_date, datetime): - payload['until_date'] = until_date.timestamp() - else: - payload['until_date'] = until_date - if revoke_messages is not None: - payload['revoke_messages'] = revoke_messages - return _make_request(token, method_url, params=payload, method='post') - - def ban_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None): method_url = 'banChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} From b48a445e9f64e1d5602e928f09ace4c397c5d5f5 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 28 Jun 2021 12:02:40 +0200 Subject: [PATCH 0615/1808] Update __init__.py updated docstrings --- telebot/__init__.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3c14456df..e05cc8aff 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -839,9 +839,7 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ - Use this method to get the number of members in a chat. Returns Int on success. - :param chat_id: - :return: + This function is deprecated. Use `get_chat_member_count` instead """ logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead.') result = apihelper.get_chat_member_count(self.token, chat_id) @@ -1474,15 +1472,7 @@ def kick_chat_member( until_date:Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ - Use this method to kick a user from a group or a supergroup. - :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup - :param user_id: Int : Unique identifier of the target user - :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or - less than 30 seconds from the current time they are considered to be banned forever - :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. - If False, the user will be able to see messages in the group that were sent before the user was removed. - Always True for supergroups and channels. - :return: boolean + This function is deprecated. Use `ban_chat_member` instead """ logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) From f8110cd04669540d7dc8c2e82b68fd47516756c1 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 28 Jun 2021 15:17:53 +0200 Subject: [PATCH 0616/1808] Update README.md * Added the new message_handlers * Added some information about local Bot API Server * Replaced the split_string with the smart_split function --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 7048b6cdd..26aeee829 100644 --- a/README.md +++ b/README.md @@ -208,26 +208,59 @@ def send_something(message): **Important: all handlers are tested in the order in which they were declared** #### Edited Message handlers - -@bot.edited_message_handler(filters) +Handle edited messages +`@bot.edited_message_handler(filters) # <- passes a Message type object to your function` #### channel_post_handler - -@bot.channel_post_handler(filters) +Handle channel post messages +`@bot.channel_post_handler(filters) # <- passes a Message type object to your function` #### edited_channel_post_handler - -@bot.edited_channel_post_handler(filters) +Handle edited channel post messages +`@bot.edited_channel_post_handler(filters) # <- passes a Message type object to your function` #### Callback Query Handler - -In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback queries. - +Handle callback queries ```python @bot.callback_query_handler(func=lambda call: True) -def test_callback(call): +def test_callback(call): # <- passes a CallbackQuery type object to your function logger.info(call) ``` + +#### Inline Handler +Handle inline queries +`@bot.inline_handler() # <- passes a InlineQuery type object to your function` + +#### Chosen Inline Handler +Handle chosen inline results +`@bot.chosen_inline_handler() # <- passes a ChosenInlineResult type object to your function` + +#### Shipping Query Handler +Handle shipping queries +`@bot.shipping_query_handeler() # <- passes a ShippingQuery type object to your function` + +#### Pre Checkout Query Handler +Handle pre checkoupt queries +`@bot.pre_checkout_query_handler() # <- passes a PreCheckoutQuery type object to your function` + +#### Poll Handler +Handle poll updates +`@bot.poll_handler() # <- passes a Poll type object to your function` + +#### Poll Answer Handler +Handle poll answers +`@bot.poll_answer_handler() # <- passes a PollAnswer type object to your function` + +#### My Chat Member Handler +Handle updates of a the bot's member status in a chat +`@bot.my_chat_member_handler() # <- passes a ChatMemberUpdated type object to your function` + +#### Chat Member Handler +Handle updates of a chat member's status in a chat +`@bot.chat_member_handler() # <- passes a ChatMemberUpdated type object to your function` +*Note: "chat_member" updates are not requested by default. If you want to allow all update types, set `allowed_updates` in `bot.polling()` / `bot.infinity_polling()` to `util.update_types`* + + #### Middleware Handler A middleware handler is a function that allows you to modify requests or the bot context as they pass through the @@ -261,6 +294,7 @@ tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object # - interval: True/False (default False) - The interval between polling requests # Note: Editing this parameter harms the bot's response time # - timeout: integer (default 20) - Timeout in seconds for long polling. +# - allowed_updates: List of Strings (default None) - List of update types to request tb.polling(none_stop=False, interval=0, timeout=20) # getMe @@ -454,6 +488,18 @@ Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra deta ## Advanced use of the API +### Using local Bot API Sever +Since version 5.0 of the Bot API, you have the possibility to run your own [Local Bot API Server](https://core.telegram.org/bots/api#using-a-local-bot-api-server). +pyTelegramBotAPI also supports this feature. +```python +from telebot import apihelper + +apihelper.API_URL = "http://localhost:4200/bot{0}/{1}" +``` +**Important: Like described [here](https://core.telegram.org/bots/api#logout), you have to log out your bot from the Telegram server before switching to your local API server. in pyTelegramBotAPI use `bot.log_out()`** + +*Note: 4200 is an example port* + ### Asynchronous delivery of messages There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up your bot __significantly__, but it has unwanted side effects if used without caution. To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot. @@ -481,9 +527,10 @@ Sometimes you must send messages that exceed 5000 characters. The Telegram API c from telebot import util large_text = open("large_text.txt", "rb").read() -# Split the text each 3000 characters. -# split_string returns a list with the splitted text. -splitted_text = util.split_string(large_text, 3000) +# Splits one string into multiple strings, with a maximum amount of `chars_per_string` (max. 4096) +# Splits by last '\n', '. ' or ' ' in exactly this priority. +# smart_split returns a list with the splitted text. +splitted_text = util.smart_split(large_text, chars_per_string=3000) for text in splitted_text: tb.send_message(chat_id, text) ``` From b222416fd8fcdf19faf7010afd8ab3169a1811a8 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 28 Jun 2021 15:44:49 +0200 Subject: [PATCH 0617/1808] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 26aeee829..27ac29d16 100644 --- a/README.md +++ b/README.md @@ -527,6 +527,18 @@ Sometimes you must send messages that exceed 5000 characters. The Telegram API c from telebot import util large_text = open("large_text.txt", "rb").read() +# Split the text each 3000 characters. +# split_string returns a list with the splitted text. +splitted_text = util.split_string(large_text, 3000) + +for text in splitted_text: + tb.send_message(chat_id, text) +``` + +Or you can use the new `smart_split` function to get more meaningful substrings: +```python +from telebot import util +large_text = open("large_text.txt", "rb").read() # Splits one string into multiple strings, with a maximum amount of `chars_per_string` (max. 4096) # Splits by last '\n', '. ' or ' ' in exactly this priority. # smart_split returns a list with the splitted text. From a4e73a05c6ea2696483b756c8e135f690f369235 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 29 Jun 2021 13:30:01 +0300 Subject: [PATCH 0618/1808] Update file_name to visible_file_name in send_document --- telebot/__init__.py | 6 +++--- telebot/apihelper.py | 6 +++--- tests/test_telebot.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f1a1e6d07..fbd9545cf 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1124,7 +1124,7 @@ def send_document( thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, - file_name: Optional[str]=None) -> types.Message: + visible_file_name: Optional[str]=None) -> types.Message: """ Use this method to send general files. :param chat_id: @@ -1138,7 +1138,7 @@ def send_document( :param thumb: InputFile or String : Thumbnail of the file sent :param caption_entities: :param allow_sending_without_reply: - :param file_name: + :param visible_file_name: allows to define file name that will be visible in the Telegram instead of original file name :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1147,7 +1147,7 @@ def send_document( 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, file_name)) + allow_sending_without_reply, visible_file_name)) def send_sticker( self, chat_id: Union[int, str], data: Union[Any, str], diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 82f43e49b..177279f51 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -806,14 +806,14 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None, file_name=None): + allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None if not util.is_string(data): file_data = data - if file_name is not None: - file_data = (file_name, data) + if visible_file_name: + file_data = (visible_file_name, data) files = {data_type: file_data} else: payload[data_type] = data diff --git a/tests/test_telebot.py b/tests/test_telebot.py index c50c25a1d..1a37af849 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -133,7 +133,7 @@ def test_send_file_with_filename(self): ret_msg = tb.send_document(CHAT_ID, file_data) assert ret_msg.message_id - ret_msg = tb.send_document(CHAT_ID, file_data, file_name="test.jpg") + ret_msg = tb.send_document(CHAT_ID, file_data, visible_file_name="test.jpg") assert ret_msg.message_id def test_send_file_dis_noti(self): From a6668397e10ec6203293f2582af567a8903b0a6a Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:08:05 +0200 Subject: [PATCH 0619/1808] new deprecated decorator added a new deprecated decorator to util --- telebot/util.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 3dd71dbd9..50ca45081 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -6,7 +6,7 @@ import traceback import warnings import functools -from typing import Any, List, Dict, Union +from typing import Any, Callable, List, Dict, Optional, Union import queue as Queue import logging @@ -420,6 +420,26 @@ def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) +def deprecated_dec(warn: Optional[bool]=False, alternative: Optional[Callable]=None): + """ + Use this decorator to mark functions as deprecated. + When the function is used, an info (or warning if `warn` is True) is logged. + :param warn: If True a warning is logged else an info + :param alternative: The new function to use instead + """ + def decorator(function): + def wrapper(*args, **kwargs): + if not warn: + logger.info(f"`{function.__name__}` is deprecated." + + (f" Use `{alternative.__name__}` instead" if alternative else "")) + else: + logger.warn(f"`{function.__name__}` is deprecated." + + (f" Use `{alternative.__name__}` instead" if alternative else "")) + return function(*args, **kwargs) + return wrapper + return decorator + + def deprecated(func): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted From 073d7fb6a733ee26c1d6985fed422fd28fcbe34c Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:11:48 +0200 Subject: [PATCH 0620/1808] Update util.py whoops warn is not optional --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 50ca45081..49ea25dd4 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -420,7 +420,7 @@ def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) -def deprecated_dec(warn: Optional[bool]=False, alternative: Optional[Callable]=None): +def deprecated_dec(warn: bool=False, alternative: Optional[Callable]=None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. From 791d65e95a9ba60ac8b798c7d865f9d887d9880c Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:47:39 +0200 Subject: [PATCH 0621/1808] replaced old deprecated decorator --- telebot/util.py | 17 +---------------- tests/test_telebot.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 49ea25dd4..711f003c5 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -420,7 +420,7 @@ def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) -def deprecated_dec(warn: bool=False, alternative: Optional[Callable]=None): +def deprecated(warn: bool=False, alternative: Optional[Callable]=None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. @@ -439,18 +439,3 @@ def wrapper(*args, **kwargs): return wrapper return decorator - -def deprecated(func): - """This is a decorator which can be used to mark functions - as deprecated. It will result in a warning being emitted - when the function is used.""" - # https://stackoverflow.com/a/30253848/441814 - @functools.wraps(func) - def new_func(*args, **kwargs): - warnings.simplefilter('always', DeprecationWarning) # turn off filter - warnings.warn("Call to deprecated function {}.".format(func.__name__), - category=DeprecationWarning, - stacklevel=2) - warnings.simplefilter('default', DeprecationWarning) # reset filter - return func(*args, **kwargs) - return new_func diff --git a/tests/test_telebot.py b/tests/test_telebot.py index a2f3d36e8..0421033c7 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -19,6 +19,14 @@ CHAT_ID = os.environ['CHAT_ID'] GROUP_ID = os.environ['GROUP_ID'] +def _new_test(): + pass + +@util.deprecated(alternative=_new_test) +def _test(): + pass + + @pytest.mark.skipif(should_skip, reason="No environment variables configured") class TestTeleBot: @@ -605,6 +613,9 @@ def command_handler(message): tb.process_new_updates([update]) time.sleep(1) assert update.message.text == 'got' * 2 + + def test_deprecated_dec(self): + _test() def test_chat_permissions(self): return # CHAT_ID is private chat, no permissions can be set From 56e4f68a839f6d647f4a9d21109abd65f4e69017 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Wed, 30 Jun 2021 14:16:38 +0200 Subject: [PATCH 0622/1808] added the property `difference` to ChatMemberUpdated --- telebot/types.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index aed2e920f..61ac414dc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -148,6 +148,23 @@ 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 + + @property + def difference(self) -> Dict[str, List]: + """ + Get the difference between `old_chat_member` and `new_chat_member` + as a dict in the following format {'parameter': [old_value, new_value]} + E.g {'status': ['member', 'kicked'], 'until_date': [None, 1625055092]} + """ + old: Dict = self.old_chat_member.__dict__ + new: Dict = self.new_chat_member.__dict__ + old.pop('user') # User should always be the same + new.pop('user') # No need to include + dif = {} + for key in new: + if new[key] != old[key]: + dif[key] = [old[key], new[key]] + return dif class WebhookInfo(JsonDeserializable): From c7b360e98203b79366301c519127c7377d5f57ae Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Thu, 1 Jul 2021 18:54:39 +0200 Subject: [PATCH 0623/1808] fixed bug --- telebot/types.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 61ac414dc..cae490903 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -158,10 +158,9 @@ def difference(self) -> Dict[str, List]: """ old: Dict = self.old_chat_member.__dict__ new: Dict = self.new_chat_member.__dict__ - old.pop('user') # User should always be the same - new.pop('user') # No need to include dif = {} for key in new: + if key == 'user': continue if new[key] != old[key]: dif[key] = [old[key], new[key]] return dif From a15016d7d9b7dc2cc451138d58b8bc18701e6650 Mon Sep 17 00:00:00 2001 From: Andy Kluger Date: Wed, 7 Jul 2021 13:00:32 -0400 Subject: [PATCH 0624/1808] mention colorcodebot as a project using this library --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 27ac29d16..c392958e6 100644 --- a/README.md +++ b/README.md @@ -691,6 +691,7 @@ Get help. Discuss. Chat. * [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* * [NeoBot](https://github.com/neoranger/NeoBot) by [@NeoRanger](https://github.com/neoranger) * [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi* +* [ColorCodeBot](https://t.me/colorcodebot) ([source](https://github.com/andydecleyre/colorcodebot)) - Share code snippets as beautifully syntax-highlighted HTML and/or images. * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. * [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin. * [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 From beb5a456eb282c01e3af98d41ea08a3fd583e838 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 8 Jul 2021 09:35:48 +0300 Subject: [PATCH 0625/1808] Preserve dict change in Update --- telebot/types.py | 98 ++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index cae490903..a0efe89f9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -65,15 +65,16 @@ def de_json(cls, json_string): raise NotImplementedError @staticmethod - def check_json(json_type): + def check_json(json_type, dict_copy = True): """ Checks whether json_type is a dict or a string. If it is already a dict, it is returned as-is. If it is not, it is converted to a dict by means of json.loads(json_type) - :param json_type: - :return: + :param json_type: input json or parsed dict + :param dict_copy: if dict is passed and it is changed outside - should be True! + :return: Dictionary parsed from json or original dict """ if util.is_dict(json_type): - return json_type + return json_type.copy() elif util.is_string(json_type): return json.loads(json_type) else: @@ -94,25 +95,28 @@ class Update(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) - obj['message'] = Message.de_json(obj.get('message')) - obj['edited_message'] = Message.de_json(obj.get('edited_message')) - obj['channel_post'] = Message.de_json(obj.get('channel_post')) - obj['edited_channel_post'] = Message.de_json(obj.get('edited_channel_post')) - obj['inline_query'] = InlineQuery.de_json(obj.get('inline_query')) - obj['chosen_inline_result'] = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) - obj['callback_query'] = CallbackQuery.de_json(obj.get('callback_query')) - obj['shipping_query'] = ShippingQuery.de_json(obj.get('shipping_query')) - obj['pre_checkout_query'] = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) - obj['poll'] = Poll.de_json(obj.get('poll')) - obj['poll_answer'] = PollAnswer.de_json(obj.get('poll_answer')) - obj['my_chat_member'] = ChatMemberUpdated.de_json(obj.get('my_chat_member')) - obj['chat_member'] = ChatMemberUpdated.de_json(obj.get('chat_member')) - return cls(**obj) + obj = cls.check_json(json_string, dict_copy=False) + update_id = obj['update_id'] + message = Message.de_json(obj.get('message')) + edited_message = Message.de_json(obj.get('edited_message')) + channel_post = Message.de_json(obj.get('channel_post')) + edited_channel_post = Message.de_json(obj.get('edited_channel_post')) + inline_query = InlineQuery.de_json(obj.get('inline_query')) + chosen_inline_result = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) + callback_query = CallbackQuery.de_json(obj.get('callback_query')) + shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) + pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) + poll = Poll.de_json(obj.get('poll')) + poll_answer = PollAnswer.de_json(obj.get('poll_answer')) + my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member')) + chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) + 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) 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, **kwargs): + my_chat_member, chat_member): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -170,7 +174,7 @@ class WebhookInfo(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, @@ -190,7 +194,7 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(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, @@ -231,7 +235,7 @@ class GroupChat(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, id, title, **kwargs): @@ -283,11 +287,10 @@ class MessageID(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) - message_id = obj['message_id'] - return cls(message_id) + obj = cls.check_json(json_string, dict_copy=False) + return cls(**obj) - def __init__(self, message_id): + def __init__(self, message_id, **kwargs): self.message_id = message_id @@ -295,7 +298,7 @@ class Message(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) message_id = obj['message_id'] from_user = User.de_json(obj.get('from')) date = obj['date'] @@ -643,7 +646,7 @@ class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, value, emoji, **kwargs): @@ -662,7 +665,7 @@ class PhotoSize(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwargs): @@ -701,7 +704,7 @@ class Voice(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None, **kwargs): @@ -775,7 +778,7 @@ class Contact(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None, **kwargs): @@ -790,7 +793,7 @@ class Location(JsonDeserializable, JsonSerializable, Dictionaryable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, longitude, latitude, horizontal_accuracy=None, @@ -854,7 +857,7 @@ class File(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): @@ -1005,7 +1008,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + 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) @@ -1129,7 +1132,7 @@ class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None, **kwargs): @@ -1176,7 +1179,7 @@ class ChatPhoto(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id, **kwargs): @@ -1230,7 +1233,7 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): @classmethod def de_json(cls, json_string): if json_string is None: return json_string - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, can_send_messages=None, can_send_media_messages=None, @@ -1274,7 +1277,7 @@ class BotCommand(JsonSerializable, JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, command, description): @@ -2329,7 +2332,7 @@ class Invoice(JsonDeserializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, title, description, start_parameter, currency, total_amount, **kwargs): @@ -2344,7 +2347,7 @@ class ShippingAddress(JsonDeserializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, country_code, state, city, street_line1, street_line2, post_code, **kwargs): @@ -2506,7 +2509,7 @@ class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, point, x_shift, y_shift, scale, **kwargs): @@ -2653,11 +2656,10 @@ def to_dict(self): class PollOption(JsonDeserializable): -#class PollOption(JsonSerializable, JsonDeserializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, text, voter_count = 0, **kwargs): @@ -2788,7 +2790,7 @@ class ProximityAlertTriggered(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) def __init__(self, traveler, watcher, distance, **kwargs): @@ -2813,7 +2815,7 @@ class VoiceChatScheduled(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(obj['start_date']) def __init__(self, start_date): @@ -2824,7 +2826,7 @@ class VoiceChatEnded(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(obj['duration']) def __init__(self, duration): @@ -2849,7 +2851,7 @@ class MessageAutoDeleteTimerChanged(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string, dict_copy=False) return cls(obj['message_auto_delete_time']) def __init__(self, message_auto_delete_time): From 2578e481349dddce0e095cf27567d8784fc5b1b3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 9 Jul 2021 10:42:56 +0300 Subject: [PATCH 0626/1808] Timeouts in making requests are rethought --- telebot/apihelper.py | 93 +++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 177279f51..5ee003e81 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import time from datetime import datetime -from typing import Dict try: import ujson as json @@ -12,6 +11,7 @@ from requests.exceptions import HTTPError, ConnectionError, Timeout try: + # noinspection PyUnresolvedReferences from requests.packages.urllib3 import fields format_header_param = fields.format_header_param except ImportError: @@ -28,8 +28,11 @@ API_URL = None FILE_URL = None -CONNECT_TIMEOUT = 3.5 -READ_TIMEOUT = 9999 +CONNECT_TIMEOUT = 15 +READ_TIMEOUT = 30 + +LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates) + SESSION_TIME_TO_LIVE = None # In seconds. None - live forever, 0 - one-time RETRY_ON_ERROR = False @@ -45,6 +48,7 @@ def _get_req_session(reset=False): if SESSION_TIME_TO_LIVE: # If session TTL is set - check time passed creation_date = util.per_thread('req_session_time', lambda: datetime.now(), reset) + # noinspection PyTypeChecker if (datetime.now() - creation_date).total_seconds() > SESSION_TIME_TO_LIVE: # Force session reset reset = True @@ -72,6 +76,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): if not token: raise Exception('Bot token is not defined') if API_URL: + # noinspection PyUnresolvedReferences request_url = API_URL.format(token, method_name) else: request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name) @@ -83,17 +88,21 @@ def _make_request(token, method_name, method='get', params=None, files=None): fields.format_header_param = _no_encode(format_header_param) if params: if 'timeout' in params: - read_timeout = params.pop('timeout') + 10 - if 'connect-timeout' in params: - connect_timeout = params.pop('connect-timeout') + 10 + read_timeout = params.pop('timeout') + connect_timeout = read_timeout +# if 'connect-timeout' in params: +# connect_timeout = params.pop('connect-timeout') + 10 if 'long_polling_timeout' in params: - # For getUpdates - # The only function with timeout on the BOT API side - params['timeout'] = params.pop('long_polling_timeout') - # Long polling hangs for given time. Read timeout should be greater that long_polling_timeout - read_timeout = max(params['timeout'] + 10, read_timeout) + # For getUpdates: it's the only function with timeout parameter on the BOT API side + long_polling_timeout = params.pop('long_polling_timeout') + params['timeout'] = long_polling_timeout + # Long polling hangs for a given time. Read timeout should be greater that long_polling_timeout + read_timeout = max(long_polling_timeout + 5, read_timeout) + # Lets stop suppose that user is stupid and assume that he knows what he do... + # read_timeout = read_timeout + 10 + # connect_timeout = connect_timeout + 10 - params = params or None #set params to None if empty + params = params or None # Set params to None if empty result = None if RETRY_ON_ERROR: @@ -189,13 +198,15 @@ def get_file_url(token, file_id): if FILE_URL is None: return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id)['file_path']) else: + # noinspection PyUnresolvedReferences return FILE_URL.format(token, get_file(token, file_id)['file_path']) - + def download_file(token, file_path): if FILE_URL is None: url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path) else: + # noinspection PyUnresolvedReferences url = FILE_URL.format(token, file_path) result = _get_req_session().get(url, proxies=proxy) @@ -238,7 +249,7 @@ def send_message( if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if entities: payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) if allow_sending_without_reply is not None: @@ -264,7 +275,7 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed if drop_pending_updates is not None: # Any bool value should pass payload['drop_pending_updates'] = drop_pending_updates if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload, files=files) @@ -274,7 +285,7 @@ def delete_webhook(token, drop_pending_updates=None, timeout=None): if drop_pending_updates is not None: # Any bool value should pass payload['drop_pending_updates'] = drop_pending_updates if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -282,7 +293,7 @@ def get_webhook_info(token, timeout=None): method_url = r'getWebhookInfo' payload = {} if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -294,9 +305,8 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No if limit: payload['limit'] = limit if timeout: - payload['connect-timeout'] = timeout - if long_polling_timeout: - payload['long_polling_timeout'] = long_polling_timeout + payload['timeout'] = timeout + payload['long_polling_timeout'] = long_polling_timeout if long_polling_timeout else LONG_POLLING_TIMEOUT if allowed_updates is not None: # Empty lists should pass payload['allowed_updates'] = json.dumps(allowed_updates) return _make_request(token, method_url, params=payload) @@ -374,7 +384,7 @@ def forward_message( if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -398,7 +408,7 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -417,7 +427,7 @@ def send_dice( if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -448,7 +458,7 @@ def send_photo( if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -468,7 +478,7 @@ def send_media_group( if reply_to_message_id: payload['reply_to_message_id'] = reply_to_message_id if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request( @@ -502,7 +512,7 @@ def send_location( if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -527,7 +537,7 @@ def edit_message_live_location( if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -545,7 +555,7 @@ def stop_message_live_location( if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -568,7 +578,7 @@ def send_venue( if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if google_place_id: @@ -595,7 +605,7 @@ def send_contact( if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -605,7 +615,7 @@ def send_chat_action(token, chat_id, action, timeout=None): method_url = r'sendChatAction' payload = {'chat_id': chat_id, 'action': action} if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload) @@ -634,7 +644,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if thumb: if not util.is_string(thumb): if files: @@ -678,7 +688,7 @@ def send_animation( if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if thumb: if not util.is_string(thumb): if files: @@ -717,7 +727,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -747,7 +757,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if thumb: if not util.is_string(thumb): if files: @@ -788,7 +798,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if thumb: if not util.is_string(thumb): if files: @@ -826,7 +836,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if caption: payload['caption'] = caption if thumb: @@ -1162,7 +1172,7 @@ def delete_message(token, chat_id, message_id, timeout=None): method_url = r'deleteMessage' payload = {'chat_id': chat_id, 'message_id': message_id} if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout return _make_request(token, method_url, params=payload, method='post') @@ -1181,7 +1191,7 @@ def send_game( if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1314,7 +1324,7 @@ def send_invoice( if provider_data: payload['provider_data'] = provider_data if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply return _make_request(token, method_url, params=payload) @@ -1458,6 +1468,7 @@ def delete_sticker_from_set(token, sticker): return _make_request(token, method_url, params=payload, method='post') +# noinspection PyShadowingBuiltins def send_poll( token, chat_id, question, options, @@ -1502,7 +1513,7 @@ def send_poll( if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: - payload['connect-timeout'] = timeout + payload['timeout'] = timeout if explanation_entities: payload['explanation_entities'] = json.dumps( types.MessageEntity.to_list_of_dicts(explanation_entities)) From 2d0b092ea4b2e0c367f28b583919cdb0c3b33741 Mon Sep 17 00:00:00 2001 From: dannkunt <32395839+dannkunt@users.noreply.github.com> Date: Sat, 10 Jul 2021 22:03:31 +0300 Subject: [PATCH 0627/1808] Fix wrong type hint call.id gives int --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fbd9545cf..ecaf438ad 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2179,7 +2179,7 @@ def answer_inline_query( switch_pm_text, switch_pm_parameter) def answer_callback_query( - self, callback_query_id: str, + self, callback_query_id: int, text: Optional[str]=None, show_alert: Optional[bool]=None, url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: """ From 6fb10e92e4fea41e78d86301c96c20b0e267a7e8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 13 Jul 2021 20:11:47 +0300 Subject: [PATCH 0628/1808] Fix CallbackQuery issue for games --- telebot/types.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index a0efe89f9..b0ccae95d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1160,6 +1160,9 @@ class CallbackQuery(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + if not "data" in obj: + # "data" field is Optional in the API, but historically is mandatory in the class constructor + obj['data'] = None obj['from_user'] = User.de_json(obj.pop('from')) if 'message' in obj: obj['message'] = Message.de_json(obj.get('message')) From f52ea635e5fd9d8c74699b267cf4f3b7dabd36c0 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 13 Jul 2021 22:09:56 +0300 Subject: [PATCH 0629/1808] Fix worker_pool issue --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ecaf438ad..faf1baa0d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -740,7 +740,7 @@ def stop_polling(self): def stop_bot(self): self.stop_polling() - if self.worker_pool: + if self.threaded and self.worker_pool: self.worker_pool.close() def set_update_listener(self, listener): From fa80b1dba07a563a445614212c78e3a850900a21 Mon Sep 17 00:00:00 2001 From: Vladislav Nahorniy Date: Thu, 15 Jul 2021 08:56:04 +0300 Subject: [PATCH 0630/1808] Added tip for invoice --- telebot/__init__.py | 10 ++++++++-- telebot/apihelper.py | 9 ++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index faf1baa0d..36b5e2e13 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1983,7 +1983,8 @@ def send_invoice( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, provider_data: Optional[str]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[list]=None) -> types.Message: """ Sends invoice :param chat_id: Unique identifier for the target private chat @@ -2018,6 +2019,10 @@ def send_invoice( A detailed description of required fields should be provided by the payment provider. :param timeout: :param allow_sending_without_reply: + :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. :return: """ result = apihelper.send_invoice( @@ -2025,7 +2030,8 @@ def send_invoice( currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, - reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply) + reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, + max_tip_amount, suggested_tip_amounts) return types.Message.de_json(result) def send_poll( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5ee003e81..76684fca4 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1258,7 +1258,7 @@ def send_invoice( need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None): + timeout=None, allow_sending_without_reply=None,max_tip_amount=None, suggested_tip_amounts=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1287,6 +1287,9 @@ 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 allow_sending_without_reply: + :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. :return: """ method_url = r'sendInvoice' @@ -1327,6 +1330,10 @@ def send_invoice( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if max_tip_amount is not None: + payload['max_tip_amount'] = max_tip_amount + if suggested_tip_amounts is not None: + payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) return _make_request(token, method_url, params=payload) From 29c98b02302dc84add7f01cfbdaf111aa34dbab1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 15 Jul 2021 09:27:07 +0300 Subject: [PATCH 0631/1808] Invoice tips typo fix --- telebot/__init__.py | 3 ++- telebot/apihelper.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 36b5e2e13..bb4812354 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1984,7 +1984,8 @@ def send_invoice( provider_data: Optional[str]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[list]=None) -> types.Message: + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[list]=None) -> types.Message: """ Sends invoice :param chat_id: Unique identifier for the target private chat diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 76684fca4..2e7ee2afa 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1258,7 +1258,7 @@ def send_invoice( need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None,max_tip_amount=None, suggested_tip_amounts=None): + timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) From 097ba9fec2ff3692c03061288f3454c2058b9112 Mon Sep 17 00:00:00 2001 From: monosans Date: Mon, 19 Jul 2021 20:01:37 +0300 Subject: [PATCH 0632/1808] Replace for loops with comprehensions --- telebot/__init__.py | 20 ++++---------------- telebot/types.py | 11 ++++------- telebot/util.py | 7 ++++--- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index bb4812354..b13c39633 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -359,10 +359,7 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, :return: array of Updates """ json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates, long_polling_timeout) - ret = [] - for ju in json_updates: - ret.append(types.Update.de_json(ju)) - return ret + return [types.Update.de_json(ju) for ju in json_updates] def __skip_updates(self): """ @@ -832,10 +829,7 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe :return: """ result = apihelper.get_chat_administrators(self.token, chat_id) - ret = [] - for r in result: - ret.append(types.ChatMember.de_json(r)) - return ret + return [types.ChatMember.de_json(r) for r in result] def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ @@ -1307,10 +1301,7 @@ def send_media_group( result = apihelper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, allow_sending_without_reply) - ret = [] - for msg in result: - ret.append(types.Message.de_json(msg)) - return ret + return [types.Message.de_json(msg) for msg in result] def send_location( self, chat_id: Union[int, str], @@ -1962,10 +1953,7 @@ def get_game_high_scores( :return: """ result = apihelper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) - ret = [] - for r in result: - ret.append(types.GameHighScore.de_json(r)) - return ret + return [types.GameHighScore.de_json(r) for r in result] def send_invoice( self, chat_id: Union[int, str], title: str, description: str, diff --git a/telebot/types.py b/telebot/types.py index b0ccae95d..8dba5cc00 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -81,13 +81,10 @@ def check_json(json_type, dict_copy = True): raise ValueError("json_type should be a json dict or string.") def __str__(self): - d = {} - for x, y in self.__dict__.items(): - if hasattr(y, '__dict__'): - d[x] = y.__dict__ - else: - d[x] = y - + d = { + x: y.__dict__ if hasattr(y, '__dict__') else y + for x, y in self.__dict__.items() + } return str(d) diff --git a/telebot/util.py b/telebot/util.py index 711f003c5..34d08ed64 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -351,9 +351,10 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I :return: InlineKeyboardMarkup """ markup = types.InlineKeyboardMarkup(row_width=row_width) - buttons = [] - for text, kwargs in values.items(): - buttons.append(types.InlineKeyboardButton(text=text, **kwargs)) + buttons = [ + types.InlineKeyboardButton(text=text, **kwargs) + for text, kwargs in values.items() + ] markup.add(*buttons) return markup From ae8c3252df52770aa77142e6e5e8419ca5e4e6f2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 21 Jul 2021 21:53:56 +0300 Subject: [PATCH 0633/1808] Release version 3.8.2 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index b74c7d354..80f0f16f0 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__ = '3.8.1' +__version__ = '3.8.2' From 02b886465e3b6d7bfa05067beceaf8e38a0d4c8f Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 25 Jul 2021 15:46:53 +0500 Subject: [PATCH 0634/1808] new filters --- telebot/__init__.py | 56 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b13c39633..b5ce7cf3b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2461,7 +2461,7 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, supergroup=None, user_id=None, chat_id=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -2487,12 +2487,32 @@ def command_handle_document(message): 'text', 'location', 'contact', 'sticker']) def default_command(message): bot.send_message(message.chat.id, "This is the default command handler.") + # Handle all messages in private chat. + @bot.message_handler(is_private=True) + def default_command(message): + bot.send_message(message.chat.id, "You wrote message in private chat") + # Handle all messages in group/supergroup. + @bot.message_handler(supergroup=True) + def default_command(message): + bot.send_message(message.chat.id, "You wrote message in supergroup") + # Handle all messages from user 11111 + @bot.message_handler(user_id=[11111]) + def default_command(message): + bot.send_message(message.chat.id, "You wrote me message in special chat")# This doesn't work for other users than '11111' + # Handle all messages in specific group/supergroup. + @bot.message_handler(chat_id=[1111]]) + def default_command(message): + bot.send_message(message.chat.id, "You wrote message in special supergroup") :param commands: Optional list of strings (commands to handle). :param regexp: Optional regular expression. :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :param is_private: True/False, only private chats. + :param supergroup: True/False, only supergroups/groups. + :param user_id: List of user ids, which can use handler. + :param chat_id: List of chat_ids where handler is executed. """ if content_types is None: @@ -2503,6 +2523,10 @@ def decorator(handler): content_types=content_types, commands=commands, regexp=regexp, + is_private=is_private, + supergroup=supergroup, + user_id=user_id, + chat_id = chat_id, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2518,13 +2542,17 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, supergroup=None, user_id=None, chat_id=None, **kwargs): """ Edit message handler decorator :param commands: :param regexp: :param func: :param content_types: + :param is_private: + :param supergroup: + :param user_id: + :param chat_id: :param kwargs: :return: """ @@ -2534,10 +2562,14 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ def decorator(handler): handler_dict = self._build_handler_dict(handler, + content_types=content_types, commands=commands, regexp=regexp, + is_private=is_private, + supergroup=supergroup, + user_id=user_id, + chat_id = chat_id, func=func, - content_types=content_types, **kwargs) self.add_edited_message_handler(handler_dict) return handler @@ -2552,13 +2584,14 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_id=None, **kwargs): """ Channel post handler decorator :param commands: :param regexp: :param func: :param content_types: + :param chat_id: :param kwargs: :return: """ @@ -2568,10 +2601,11 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty def decorator(handler): handler_dict = self._build_handler_dict(handler, + content_types=content_types, commands=commands, regexp=regexp, + chat_id = chat_id, func=func, - content_types=content_types, **kwargs) self.add_channel_post_handler(handler_dict) return handler @@ -2586,7 +2620,7 @@ def add_channel_post_handler(self, handler_dict): """ self.channel_post_handlers.append(handler_dict) - def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, supergroup=None, user_id=None, chat_id=None, **kwargs): """ Edit channel post handler decorator :param commands: @@ -2602,10 +2636,14 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con def decorator(handler): handler_dict = self._build_handler_dict(handler, + content_types=content_types, commands=commands, regexp=regexp, + is_private=is_private, + supergroup=supergroup, + user_id=user_id, + chat_id = chat_id, func=func, - content_types=content_types, **kwargs) self.add_edited_channel_post_handler(handler_dict) return handler @@ -2857,6 +2895,10 @@ def _test_filter(message_filter, filter_value, message): 'content_types': lambda msg: msg.content_type in filter_value, 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, + 'is_private': lambda msg: msg.chat.type == 'private', + 'supergroup': lambda msg: msg.chat.type == "supergroup", + 'user_id': lambda msg: msg.from_user.id in filter_value, + 'chat_id': lambda msg: msg.chat.id in filter_value, 'func': lambda msg: filter_value(msg) } From 9c1b19a9e49e601bbf83bf9b059caf2157cfa0dc Mon Sep 17 00:00:00 2001 From: coder2020official Date: Wed, 28 Jul 2021 23:06:31 +0500 Subject: [PATCH 0635/1808] upd --- telebot/__init__.py | 3 ++- telebot/apihelper.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b5ce7cf3b..f9418d840 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2116,7 +2116,8 @@ def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - parse_mode: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 2e7ee2afa..1f0c187f7 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1123,7 +1123,7 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, reply_markup=None): + parse_mode=None, caption_entities=None,reply_markup=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -1134,6 +1134,8 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m payload['inline_message_id'] = inline_message_id if parse_mode: payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = caption_entities if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload, method='post') From 7ebe589b46ba0d1218f9954730573b5ab86d011d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Wed, 28 Jul 2021 23:10:15 +0500 Subject: [PATCH 0636/1808] Update __init__.py --- telebot/__init__.py | 58 +++++++-------------------------------------- 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f9418d840..59637dcae 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2116,7 +2116,7 @@ def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - parse_mode: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ @@ -2462,7 +2462,7 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, supergroup=None, user_id=None, chat_id=None, **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -2488,32 +2488,12 @@ def command_handle_document(message): 'text', 'location', 'contact', 'sticker']) def default_command(message): bot.send_message(message.chat.id, "This is the default command handler.") - # Handle all messages in private chat. - @bot.message_handler(is_private=True) - def default_command(message): - bot.send_message(message.chat.id, "You wrote message in private chat") - # Handle all messages in group/supergroup. - @bot.message_handler(supergroup=True) - def default_command(message): - bot.send_message(message.chat.id, "You wrote message in supergroup") - # Handle all messages from user 11111 - @bot.message_handler(user_id=[11111]) - def default_command(message): - bot.send_message(message.chat.id, "You wrote me message in special chat")# This doesn't work for other users than '11111' - # Handle all messages in specific group/supergroup. - @bot.message_handler(chat_id=[1111]]) - def default_command(message): - bot.send_message(message.chat.id, "You wrote message in special supergroup") :param commands: Optional list of strings (commands to handle). :param regexp: Optional regular expression. :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param content_types: Supported message content types. Must be a list. Defaults to ['text']. - :param is_private: True/False, only private chats. - :param supergroup: True/False, only supergroups/groups. - :param user_id: List of user ids, which can use handler. - :param chat_id: List of chat_ids where handler is executed. """ if content_types is None: @@ -2524,10 +2504,6 @@ def decorator(handler): content_types=content_types, commands=commands, regexp=regexp, - is_private=is_private, - supergroup=supergroup, - user_id=user_id, - chat_id = chat_id, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2543,17 +2519,13 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, supergroup=None, user_id=None, chat_id=None, **kwargs): + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Edit message handler decorator :param commands: :param regexp: :param func: :param content_types: - :param is_private: - :param supergroup: - :param user_id: - :param chat_id: :param kwargs: :return: """ @@ -2563,14 +2535,10 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ def decorator(handler): handler_dict = self._build_handler_dict(handler, - content_types=content_types, commands=commands, regexp=regexp, - is_private=is_private, - supergroup=supergroup, - user_id=user_id, - chat_id = chat_id, func=func, + content_types=content_types, **kwargs) self.add_edited_message_handler(handler_dict) return handler @@ -2585,14 +2553,13 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_id=None, **kwargs): + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Channel post handler decorator :param commands: :param regexp: :param func: :param content_types: - :param chat_id: :param kwargs: :return: """ @@ -2602,11 +2569,10 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty def decorator(handler): handler_dict = self._build_handler_dict(handler, - content_types=content_types, commands=commands, regexp=regexp, - chat_id = chat_id, func=func, + content_types=content_types, **kwargs) self.add_channel_post_handler(handler_dict) return handler @@ -2621,7 +2587,7 @@ def add_channel_post_handler(self, handler_dict): """ self.channel_post_handlers.append(handler_dict) - def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, supergroup=None, user_id=None, chat_id=None, **kwargs): + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Edit channel post handler decorator :param commands: @@ -2637,14 +2603,10 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con def decorator(handler): handler_dict = self._build_handler_dict(handler, - content_types=content_types, commands=commands, regexp=regexp, - is_private=is_private, - supergroup=supergroup, - user_id=user_id, - chat_id = chat_id, func=func, + content_types=content_types, **kwargs) self.add_edited_channel_post_handler(handler_dict) return handler @@ -2896,10 +2858,6 @@ def _test_filter(message_filter, filter_value, message): 'content_types': lambda msg: msg.content_type in filter_value, 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, - 'is_private': lambda msg: msg.chat.type == 'private', - 'supergroup': lambda msg: msg.chat.type == "supergroup", - 'user_id': lambda msg: msg.from_user.id in filter_value, - 'chat_id': lambda msg: msg.chat.id in filter_value, 'func': lambda msg: filter_value(msg) } From 81adfd335e85637011632aac89ed8602681fdb33 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Jul 2021 19:15:37 +0500 Subject: [PATCH 0637/1808] UPD --- telebot/__init__.py | 2 +- telebot/apihelper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 59637dcae..9fd86a2f7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2132,7 +2132,7 @@ def edit_message_caption( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, reply_markup) + parse_mode, caption_entities, reply_markup) if type(result) == bool: return result return types.Message.de_json(result) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1f0c187f7..7647e1278 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1135,7 +1135,7 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m if parse_mode: payload['parse_mode'] = parse_mode if caption_entities: - payload['caption_entities'] = caption_entities + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload, method='post') From c117ff2d50e052b0d39a7d105769cb8d0711c66f Mon Sep 17 00:00:00 2001 From: snikidev Date: Tue, 3 Aug 2021 17:34:29 +0100 Subject: [PATCH 0638/1808] Add return statement to to_dict() method inside InputInvoiceMessageContent --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 8dba5cc00..ceddfdb6e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1569,7 +1569,7 @@ def to_dict(self): json_dict['send_email_to_provider'] = self.send_email_to_provider if self.is_flexible is not None: json_dict['is_flexible'] = self.is_flexible - + return json_dict class ChosenInlineResult(JsonDeserializable): @classmethod From 4ba4bc18cff1d5324e58d5f8a0e1989ac2cdcb41 Mon Sep 17 00:00:00 2001 From: snikidev Date: Tue, 3 Aug 2021 17:35:59 +0100 Subject: [PATCH 0639/1808] add extra space --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index ceddfdb6e..60e5c0245 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1571,6 +1571,7 @@ def to_dict(self): json_dict['is_flexible'] = self.is_flexible return json_dict + class ChosenInlineResult(JsonDeserializable): @classmethod def de_json(cls, json_string): From ea16f3543253304b020a54566cf04f5e3c69459d Mon Sep 17 00:00:00 2001 From: Amol Soans Date: Fri, 6 Aug 2021 12:29:00 +0530 Subject: [PATCH 0640/1808] Add IPO bot Listed oneIPO bot created using pyTelegramBotAPI in section : Bpts using this API --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c392958e6..dbd517837 100644 --- a/README.md +++ b/README.md @@ -749,5 +749,6 @@ Get help. Discuss. Chat. * Digital Cryptocurrency bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/currencies_bot). With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. * [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by [Leon Heess (source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. * [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems) - +* [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings) + **Want to have your bot listed here? Just make a pull request.** From 911e35693007a1e7c45c884fc2c3dc8afa697caa Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 12 Aug 2021 15:16:04 +0300 Subject: [PATCH 0641/1808] BotCommandScopeChatMember fix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 60e5c0245..6d17c9f96 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1380,7 +1380,7 @@ def __init__(self, chat_id=None, user_id=None): @param chat_id: Unique identifier for the target chat @param user_id: Unique identifier of the target user """ - super(BotCommandScopeChatMember, self).__init__(type='chat_administrators', chat_id=chat_id, user_id=user_id) + super(BotCommandScopeChatMember, self).__init__(type='chat_member', chat_id=chat_id, user_id=user_id) # InlineQuery From 3e4a6cd702acd8f21c4ade98881da927275f4c15 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 14 Aug 2021 18:46:45 +0400 Subject: [PATCH 0642/1808] Create chat_member_example.py --- examples/chat_member_example.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 examples/chat_member_example.py diff --git a/examples/chat_member_example.py b/examples/chat_member_example.py new file mode 100644 index 000000000..b6fca73f6 --- /dev/null +++ b/examples/chat_member_example.py @@ -0,0 +1,33 @@ +import telebot +from telebot import types,util + +bot = telebot.TeleBot("token") + +#chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member. +@bot.chat_member_handler() +def chat_m(message: types.ChatMemberUpdated): + old = message.old_chat_member + new = message.new_chat_member + if new.status == "member": + bot.send_message(message.chat.id,"Hello {name}!".format(name=new.user.first_name)) # Welcome message + +#if bot is added to group, this handler will work +@bot.my_chat_member_handler() +def my_chat_m(message: types.ChatMemberUpdated): + old = message.old_chat_member + new = message.new_chat_member + if new.status == "member": + bot.send_message(message.chat.id,"Somebody added me to group") # Welcome message, if bot was added to group + bot.leave_chat(message.chat.id) + +#content_Type_service is: +#'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', +#'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', +#'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', +#'voice_chat_participants_invited', 'message_auto_delete_timer_changed' +# this handler deletes service messages + +@bot.message_handler(content_types=util.content_type_service) +def delall(message: types.Message): + bot.delete_message(message.chat.id,message.message_id) +bot.polling(allowed_updates=util.update_types) \ No newline at end of file From beeb60aab8171b9a426f9d239f9b3a3da41ca3fa Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 15 Aug 2021 11:40:13 +0400 Subject: [PATCH 0643/1808] skip_updates --- examples/skip_updates_example.py | 15 +++++++++++++++ telebot/__init__.py | 10 ++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 examples/skip_updates_example.py diff --git a/examples/skip_updates_example.py b/examples/skip_updates_example.py new file mode 100644 index 000000000..8474e1681 --- /dev/null +++ b/examples/skip_updates_example.py @@ -0,0 +1,15 @@ +import telebot + +bot = telebot.TeleBot("TOKEN") + +@bot.message_handler(commands=['start', 'help']) +def send_welcome(message): + bot.reply_to(message, "Howdy, how are you doing?") + +@bot.message_handler(func=lambda message: True) +def echo_all(message): + bot.reply_to(message, message.text) + +bot.polling(skip_updates=True) # Will skip old messages when skip_updates is set + +# Also, you can use skip_updates in infinity_polling() \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index 9fd86a2f7..26354b098 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -556,7 +556,7 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=logging.ERROR, + def infinity_polling(self, timeout=20, skip_updates=False, long_polling_timeout=20, logger_level=logging.ERROR, allowed_updates=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -565,6 +565,7 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging + :param skip_updates: Skip previous updates :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -574,6 +575,8 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log 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. """ + if skip_updates: + apihelper.get_updates(self.token, -1) while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, @@ -590,7 +593,7 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log 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, + def polling(self, none_stop: bool=False, skip_updates=False, interval: int=0, timeout: int=20, long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None): """ This function creates a new Thread that calls an internal __retrieve_updates function. @@ -601,6 +604,7 @@ def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, Always get updates. :param interval: Delay between two update retrivals :param none_stop: Do not stop polling when an ApiException occurs. + :param skip_updates: Skip previous updates :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. @@ -613,6 +617,8 @@ def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, so unwanted updates may be received for a short period of time. :return: """ + if skip_updates: + apihelper.get_updates(self.token, -1) if self.threaded: self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) else: From 1e4a6e2125e1a2fd2f6fc60a26d09dc509ee0a64 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 15 Aug 2021 13:32:11 +0400 Subject: [PATCH 0644/1808] Update __init__.py --- telebot/__init__.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 26354b098..8c6e5efea 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -143,7 +143,7 @@ class TeleBot: """ def __init__( - self, token, parse_mode=None, threaded=True, skip_pending=False, num_threads=2, + self, token, parse_mode=None, threaded=True, num_threads=2, next_step_backend=None, reply_backend=None, exception_handler=None, last_update_id=0, suppress_middleware_excepions=False # <- Typo in exceptions ): @@ -156,7 +156,6 @@ def __init__( self.token = token self.parse_mode = parse_mode self.update_listener = [] - self.skip_pending = skip_pending self.suppress_middleware_excepions = suppress_middleware_excepions self.__stop_polling = threading.Event() @@ -364,17 +363,10 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, def __skip_updates(self): """ Get and discard all pending updates before first poll of the bot - :return: total updates skipped - """ - total = 0 - updates = self.get_updates(offset=self.last_update_id, long_polling_timeout=1) - while updates: - total += len(updates) - for update in updates: - if update.update_id > self.last_update_id: - self.last_update_id = update.update_id - updates = self.get_updates(offset=self.last_update_id + 1, long_polling_timeout=1) - return total + :return: + """ + logger.debug('Skipped all pending messages') + self.get_updates(offset=-1, long_polling_timeout=1) def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_updates=None): """ @@ -382,9 +374,6 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update Registered listeners and applicable message handlers will be notified when a new message arrives. :raises ApiException when a call has failed. """ - if self.skip_pending: - logger.debug('Skipped {0} pending messages'.format(self.__skip_updates())) - self.skip_pending = False updates = self.get_updates(offset=(self.last_update_id + 1), allowed_updates=allowed_updates, timeout=timeout, long_polling_timeout=long_polling_timeout) @@ -576,7 +565,7 @@ def infinity_polling(self, timeout=20, skip_updates=False, long_polling_timeout= so unwanted updates may be received for a short period of time. """ if skip_updates: - apihelper.get_updates(self.token, -1) + self.__skip_updates() while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, @@ -618,7 +607,7 @@ def polling(self, none_stop: bool=False, skip_updates=False, interval: int=0, ti :return: """ if skip_updates: - apihelper.get_updates(self.token, -1) + self.__skip_updates() if self.threaded: self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) else: From 2c0f42b363b0149ed28c19ac304f8ae7e6ace497 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 16 Aug 2021 14:48:21 +0400 Subject: [PATCH 0645/1808] Update __init__.py --- telebot/__init__.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8c6e5efea..249fcabaf 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -143,7 +143,7 @@ class TeleBot: """ def __init__( - self, token, parse_mode=None, threaded=True, num_threads=2, + 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 ): @@ -156,6 +156,7 @@ def __init__( self.token = token self.parse_mode = parse_mode self.update_listener = [] + self.skip_pending = skip_pending self.suppress_middleware_excepions = suppress_middleware_excepions self.__stop_polling = threading.Event() @@ -365,8 +366,7 @@ def __skip_updates(self): Get and discard all pending updates before first poll of the bot :return: """ - logger.debug('Skipped all pending messages') - self.get_updates(offset=-1, long_polling_timeout=1) + self.get_updates(offset=-1) def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_updates=None): """ @@ -374,6 +374,10 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update Registered listeners and applicable message handlers will be notified when a new message arrives. :raises ApiException when a call has failed. """ + if self.skip_pending: + self.__skip_updates() + logger.debug('Skipped all pending messages') + self.skip_pending = False updates = self.get_updates(offset=(self.last_update_id + 1), allowed_updates=allowed_updates, timeout=timeout, long_polling_timeout=long_polling_timeout) @@ -545,7 +549,7 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout=20, skip_updates=False, long_polling_timeout=20, logger_level=logging.ERROR, + def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=logging.ERROR, allowed_updates=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -554,7 +558,6 @@ def infinity_polling(self, timeout=20, skip_updates=False, long_polling_timeout= :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging - :param skip_updates: Skip previous updates :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -564,8 +567,6 @@ def infinity_polling(self, timeout=20, skip_updates=False, long_polling_timeout= 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. """ - if skip_updates: - self.__skip_updates() while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, @@ -582,7 +583,7 @@ def infinity_polling(self, timeout=20, skip_updates=False, long_polling_timeout= if logger_level and logger_level >= logging.INFO: logger.error("Break infinity polling") - def polling(self, none_stop: bool=False, skip_updates=False, interval: int=0, timeout: int=20, + def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None): """ This function creates a new Thread that calls an internal __retrieve_updates function. @@ -593,7 +594,6 @@ def polling(self, none_stop: bool=False, skip_updates=False, interval: int=0, ti Always get updates. :param interval: Delay between two update retrivals :param none_stop: Do not stop polling when an ApiException occurs. - :param skip_updates: Skip previous updates :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. @@ -606,8 +606,6 @@ def polling(self, none_stop: bool=False, skip_updates=False, interval: int=0, ti so unwanted updates may be received for a short period of time. :return: """ - if skip_updates: - self.__skip_updates() if self.threaded: self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) else: @@ -2112,7 +2110,6 @@ def edit_message_caption( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages @@ -2127,7 +2124,7 @@ def edit_message_caption( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, caption_entities, reply_markup) + parse_mode, reply_markup) if type(result) == bool: return result return types.Message.de_json(result) From 3e7da0fd1825fdac5783f1dfeb8b080d81b778b5 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 16 Aug 2021 14:49:45 +0400 Subject: [PATCH 0646/1808] Update skip_updates_example.py --- examples/skip_updates_example.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/skip_updates_example.py b/examples/skip_updates_example.py index 8474e1681..805c9e491 100644 --- a/examples/skip_updates_example.py +++ b/examples/skip_updates_example.py @@ -1,6 +1,6 @@ import telebot -bot = telebot.TeleBot("TOKEN") +bot = telebot.TeleBot("TOKEN",skip_pending=True)# Skip pending skips old updates @bot.message_handler(commands=['start', 'help']) def send_welcome(message): @@ -10,6 +10,4 @@ def send_welcome(message): def echo_all(message): bot.reply_to(message, message.text) -bot.polling(skip_updates=True) # Will skip old messages when skip_updates is set - -# Also, you can use skip_updates in infinity_polling() \ No newline at end of file +bot.polling() \ No newline at end of file From 24ef64456b941b84177d3fad43b5c87095b1300c Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 16 Aug 2021 14:53:00 +0400 Subject: [PATCH 0647/1808] Update __init__.py --- telebot/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 249fcabaf..30b36b925 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2110,6 +2110,7 @@ def edit_message_caption( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages @@ -2124,7 +2125,7 @@ def edit_message_caption( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, reply_markup) + parse_mode, caption_entities, reply_markup) if type(result) == bool: return result return types.Message.de_json(result) From f553960096e1b6c8bab79d6c9eb4313936c1181f Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 16 Aug 2021 22:00:08 +0400 Subject: [PATCH 0648/1808] Update __init__.py --- telebot/__init__.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 30b36b925..5048863be 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -549,13 +549,14 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=logging.ERROR, + def infinity_polling(self, timeout=20, skip_pending=False, long_polling_timeout=20, logger_level=logging.ERROR, allowed_updates=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. :param timeout: Request connection timeout :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param skip_pending: skip old updates :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging :param allowed_updates: A list of the update types you want your bot to receive. @@ -567,6 +568,9 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log 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. """ + if skip_pending: + self.__skip_updates() + while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, @@ -583,7 +587,7 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log 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, + def polling(self, none_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None): """ This function creates a new Thread that calls an internal __retrieve_updates function. @@ -595,6 +599,7 @@ def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, :param interval: Delay between two update retrivals :param none_stop: Do not stop polling when an ApiException occurs. :param timeout: Request connection timeout + :param skip_pending: skip old updates :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. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. @@ -606,6 +611,9 @@ def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, so unwanted updates may be received for a short period of time. :return: """ + if skip_pending: + self.__skip_updates() + if self.threaded: self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout, allowed_updates) else: From f4ef2366b6d73ed60cd00f616bd727546266265f Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 16 Aug 2021 22:03:17 +0400 Subject: [PATCH 0649/1808] Update skip_updates_example.py --- examples/skip_updates_example.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/skip_updates_example.py b/examples/skip_updates_example.py index 805c9e491..0bd631b9e 100644 --- a/examples/skip_updates_example.py +++ b/examples/skip_updates_example.py @@ -1,6 +1,6 @@ import telebot -bot = telebot.TeleBot("TOKEN",skip_pending=True)# Skip pending skips old updates +bot = telebot.TeleBot("TOKEN") @bot.message_handler(commands=['start', 'help']) def send_welcome(message): @@ -10,4 +10,4 @@ def send_welcome(message): def echo_all(message): bot.reply_to(message, message.text) -bot.polling() \ No newline at end of file +bot.polling(skip_pending=True)# Skip pending skips old updates From 56cd3971dc406b0952aaa6cc7bb5ce8312b56cc1 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 16 Aug 2021 22:41:27 +0400 Subject: [PATCH 0650/1808] Update __init__.py --- telebot/__init__.py | 183 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5048863be..a3e6c0c13 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -594,7 +594,7 @@ def polling(self, none_stop: bool=False, skip_pending=False, interval: int=0, ti This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly. Warning: Do not call this function more than once! - + Always get updates. :param interval: Delay between two update retrivals :param none_stop: Do not stop polling when an ApiException occurs. @@ -2520,6 +2520,23 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_message_handler(handler_dict) def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Edit message handler decorator @@ -2554,6 +2571,23 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers edited message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_message_handler(handler_dict) def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Channel post handler decorator @@ -2587,7 +2621,24 @@ def add_channel_post_handler(self, handler_dict): :return: """ self.channel_post_handlers.append(handler_dict) - + + def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers channel post message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_channel_post_handler(handler_dict) def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Edit channel post handler decorator @@ -2622,6 +2673,24 @@ def add_edited_channel_post_handler(self, handler_dict): """ self.edited_channel_post_handlers.append(handler_dict) + def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers edited channel post message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_channel_post_handler(handler_dict) + def inline_handler(self, func, **kwargs): """ Inline call handler decorator @@ -2645,6 +2714,18 @@ def add_inline_handler(self, handler_dict): """ self.inline_handlers.append(handler_dict) + def register_inline_handler(self, callback, func, **kwargs): + """ + Registers inline handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_inline_handler(handler_dict) + def chosen_inline_handler(self, func, **kwargs): """ Description: TBD @@ -2668,6 +2749,19 @@ def add_chosen_inline_handler(self, handler_dict): """ self.chosen_inline_handlers.append(handler_dict) + def register_chosen_inline_handler(self, callback, func, **kwargs): + """ + Registers chosen inline handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_chosen_inline_handler(handler_dict) + + def callback_query_handler(self, func, **kwargs): """ Callback request handler decorator @@ -2691,6 +2785,18 @@ def add_callback_query_handler(self, handler_dict): """ self.callback_query_handlers.append(handler_dict) + def register_callback_query_handler(self, callback, func, **kwargs): + """ + Registers callback query handler.. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_callback_query_handler(handler_dict) + def shipping_query_handler(self, func, **kwargs): """ Shipping request handler @@ -2714,6 +2820,18 @@ def add_shipping_query_handler(self, handler_dict): """ self.shipping_query_handlers.append(handler_dict) + def register_shipping_query_handler(self, callback, func, **kwargs): + """ + Registers shipping query handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_shipping_query_handler(handler_dict) + def pre_checkout_query_handler(self, func, **kwargs): """ Pre-checkout request handler @@ -2736,6 +2854,18 @@ def add_pre_checkout_query_handler(self, handler_dict): :return: """ self.pre_checkout_query_handlers.append(handler_dict) + + def register_pre_checkout_query_handler(self, callback, func, **kwargs): + """ + Registers pre-checkout request handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_pre_checkout_query_handler(handler_dict) def poll_handler(self, func, **kwargs): """ @@ -2760,6 +2890,18 @@ def add_poll_handler(self, handler_dict): """ self.poll_handlers.append(handler_dict) + def register_poll_handler(self, callback, func, **kwargs): + """ + Registers poll handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_poll_handler(handler_dict) + def poll_answer_handler(self, func=None, **kwargs): """ Poll_answer request handler @@ -2782,7 +2924,19 @@ def add_poll_answer_handler(self, handler_dict): :return: """ self.poll_answer_handlers.append(handler_dict) - + + def register_poll_answer_handler(self, callback, func, **kwargs): + """ + Registers poll answer handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_poll_answer_handler(handler_dict) + def my_chat_member_handler(self, func=None, **kwargs): """ my_chat_member handler @@ -2806,6 +2960,18 @@ def add_my_chat_member_handler(self, handler_dict): """ self.my_chat_member_handlers.append(handler_dict) + def register_my_chat_member_handler(self, callback, func=None, **kwargs): + """ + Registers my chat member handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_my_chat_member_handler(handler_dict) + def chat_member_handler(self, func=None, **kwargs): """ chat_member handler @@ -2829,6 +2995,17 @@ def add_chat_member_handler(self, handler_dict): """ self.chat_member_handlers.append(handler_dict) + def register_chat_member_handler(self, callback, func=None, **kwargs): + """ + Registers chat member handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, + func=func, + **kwargs) + self.add_chat_member_handler(handler_dict) def _test_message_handler(self, message_handler, message): """ From d6501ddc0e90c32b5f1ec89725399ea98cd85056 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 18:47:38 +0300 Subject: [PATCH 0651/1808] 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 | 529 ++++++++++++++++++++----------------------- 4 files changed, 286 insertions(+), 309 deletions(-) diff --git a/README.md b/README.md index dbd517837..76fe3276a 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 9fd86a2f7..f50b9325a 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 @@ def infinity_polling(self, timeout=20, long_polling_timeout=20, logger_level=log 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 @@ def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, 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 @@ def polling(self, none_stop: bool=False, interval: int=0, timeout: int=20, 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 @@ def send_document( 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 @@ def send_sticker( """ 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 @@ def edit_message_text( 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 @@ def edit_message_text( :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 @@ def edit_message_text( 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 @@ def edit_message_caption( :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 7647e1278..a3ef5c21e 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 6d17c9f96..04b0d795b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -74,7 +74,7 @@ def check_json(json_type, dict_copy = True): :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 @@ def de_json(cls, json_string): 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 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso 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 @@ def to_json(self): 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 @@ def to_dict(self): 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 @@ def de_json(cls, json_string): 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 @@ def __init__(self, result_id, from_user, query, location=None, inline_message_id 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 @@ def __init__(self, id, title, input_message_content, reply_markup=None, url=None :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 @@ def __init__(self, id, title, input_message_content, reply_markup=None, url=None 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 @@ def to_json(self): 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 @@ def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None :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 @@ def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, titl :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 @@ def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None :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 @@ def __init__(self, id, video_url, mime_type, thumb_url, title, :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 @@ def to_json(self): 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.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 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) + return json_dict -class InlineQueryResultContact(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): - self.type = 'contact' - self.id = id + 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.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} + 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.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 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 @@ def __init__(self): 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 @@ def to_json(self): 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 @@ def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content 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 @@ def __init__(self, id, audio_file_id, caption=None, parse_mode=None, reply_marku # 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 @@ def to_dict(self): 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 From b2b7d90888f260a8e8000fda058bdf221441d017 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 19:32:43 +0300 Subject: [PATCH 0652/1808] API update fix 01 --- telebot/types.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 04b0d795b..ec9635e38 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1723,7 +1723,8 @@ def to_dict(self): 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): + reply_markup=None, input_message_content=None, gif_duration=None, parse_mode=None, + thumb_mime_type=None): """ Represents a link to an animated GIF file. :param id: Unique identifier for this result, 1-64 bytes. @@ -1745,6 +1746,7 @@ def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, self.gif_height = gif_height self.thumb_url = thumb_url self.gif_duration = gif_duration + self.thumb_mime_type = thumb_mime_type def to_dict(self): json_dict = super().to_dict() @@ -1756,13 +1758,16 @@ def to_dict(self): json_dict['thumb_url'] = self.thumb_url if self.gif_duration: json_dict['gif_duration'] = self.gif_duration + if self.thumb_mime_type: + json_dict['thumb_mime_type'] = self.thumb_mime_type return json_dict 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): + parse_mode=None, reply_markup=None, input_message_content=None, mpeg4_duration=None, + thumb_mime_type=None): """ Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). :param id: Unique identifier for this result, 1-64 bytes @@ -1786,6 +1791,7 @@ def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None self.mpeg4_height = mpeg4_height self.thumb_url = thumb_url self.mpeg4_duration = mpeg4_duration + self.thumb_mime_type = thumb_mime_type def to_dict(self): json_dict = super().to_dict() @@ -1797,6 +1803,8 @@ def to_dict(self): json_dict['thumb_url'] = self.thumb_url if self.mpeg4_duration: json_dict['mpeg4_duration '] = self.mpeg4_duration + if self.thumb_mime_type: + json_dict['thumb_mime_type'] = self.thumb_mime_type return json_dict From 8053183cb5e5d72172295857d494ab4b98f53c6a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 19:36:48 +0300 Subject: [PATCH 0653/1808] API update fix 02 --- telebot/__init__.py | 6 +++--- telebot/apihelper.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 88d6fc724..35cad2bd2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2047,7 +2047,7 @@ def send_poll( open_period: Optional[int]=None, close_date: Optional[Union[int, datetime]]=None, is_closed: Optional[bool]=None, - disable_notifications: Optional[bool]=False, + disable_notification: Optional[bool]=False, reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, allow_sending_without_reply: Optional[bool]=None, @@ -2067,7 +2067,7 @@ def send_poll( :param open_period: :param close_date: :param is_closed: - :param disable_notifications: + :param disable_notification: :param reply_to_message_id: :param allow_sending_without_reply: :param reply_markup: @@ -2085,7 +2085,7 @@ def send_poll( question, options, is_anonymous, type, allows_multiple_answers, correct_option_id, explanation, explanation_parse_mode, open_period, close_date, is_closed, - disable_notifications, reply_to_message_id, allow_sending_without_reply, + disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, timeout, explanation_entities)) def stop_poll( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a3ef5c21e..4b19f241c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1485,7 +1485,7 @@ 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_notifications=False, reply_to_message_id=None, allow_sending_without_reply=None, + disable_notification=False, reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None, timeout=None, explanation_entities=None): method_url = r'sendPoll' payload = { @@ -1515,8 +1515,8 @@ def send_poll( if is_closed is not None: payload['is_closed'] = is_closed - if disable_notifications: - payload['disable_notification'] = disable_notifications + if disable_notification: + payload['disable_notification'] = disable_notification if reply_to_message_id is not None: payload['reply_to_message_id'] = reply_to_message_id if allow_sending_without_reply is not None: From fabcd93dd7a197ce3fcbd0ce08b16d30d0f50fbb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 21:57:56 +0300 Subject: [PATCH 0654/1808] API update fix 03 --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index ec9635e38..dfcfb8ff8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -273,7 +273,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.pinned_message: Message = pinned_message self.permissions: ChatPermissions = permissions self.slow_mode_delay: int = slow_mode_delay - self.message_auto_delete_time = message_auto_delete_time + self.message_auto_delete_time: int = message_auto_delete_time self.sticker_set_name: str = sticker_set_name self.can_set_sticker_set: bool = can_set_sticker_set self.linked_chat_id: int = linked_chat_id From 022ef6a64cf4a0ef957a35ee6932fa4866de567d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 22:16:30 +0300 Subject: [PATCH 0655/1808] Dependecies clearing --- requirements.txt | 1 - setup.py | 1 + telebot/__init__.py | 1 - telebot/util.py | 4 ++-- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index cf9236360..3a014e7d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -py==1.10.0 pytest==3.0.2 requests==2.20.0 wheel==0.24.0 diff --git a/setup.py b/setup.py index b3e8158cf..7b6277638 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ def read(filename): install_requires=['requests'], extras_require={ 'json': 'ujson', + 'PIL': 'Pillow', 'redis': 'redis>=3.4.1' }, classifiers=[ diff --git a/telebot/__init__.py b/telebot/__init__.py index 35cad2bd2..57ed008bb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function from datetime import datetime import logging diff --git a/telebot/util.py b/telebot/util.py index 34d08ed64..d0a58b011 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -4,16 +4,16 @@ import string import threading import traceback -import warnings -import functools from typing import Any, Callable, List, Dict, Optional, Union +# noinspection PyPep8Naming import queue as Queue import logging from telebot import types try: + # noinspection PyPackageRequirements from PIL import Image from io import BytesIO pil_imported = True From 2bc052ad5a4a0d716e33ab36d32b7e94975dcf55 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 23:27:28 +0300 Subject: [PATCH 0656/1808] Check and update for full compatibility to Bot API up to 5.3 Pre-release of 4.0.0 --- README.md | 24 ++++++++++++++---------- telebot/__init__.py | 2 +- telebot/apihelper.py | 15 +++++++++------ telebot/types.py | 42 ++++++++++++++++++++++++++++++++++-------- telebot/version.py | 2 +- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 76fe3276a..93e188c80 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ -#

pyTelegramBotAPI - -

A simple, but extensible Python implementation for the Telegram Bot API. [![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) [![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/) +#

pyTelegramBotAPI

+ +

A simple, but extensible Python implementation for the Telegram Bot API.

+ +##

Supported Bot API version: 5.3!

+ +##Contents + * [Getting started.](#getting-started) * [Writing your first bot](#writing-your-first-bot) * [Prerequisites](#prerequisites) @@ -603,21 +608,20 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} ## API conformance -_Checking is in progress..._ - -✅ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021) _- To be checked..._ - +* ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMemberXXX classes are full copies of ChatMember +* ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021) +* ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021) * ✔ [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.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) -* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support. -* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support. +* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support +* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support * ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018) * ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) * ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017) diff --git a/telebot/__init__.py b/telebot/__init__.py index 57ed008bb..e9aaff69f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1985,7 +1985,7 @@ def send_invoice( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, max_tip_amount: Optional[int] = None, - suggested_tip_amounts: Optional[list]=None) -> types.Message: + suggested_tip_amounts: Optional[List[int]]=None) -> types.Message: """ Sends invoice :param chat_id: Unique identifier for the target private chat diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4b19f241c..0d3820fa1 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -973,11 +973,11 @@ def create_chat_invite_link(token, chat_id, expire_date, member_limit): } if expire_date is not None: - payload['expire_date'] = expire_date if isinstance(payload['expire_date'], datetime): payload['expire_date'] = payload['expire_date'].timestamp() - - if member_limit is not None: + else: + payload['expire_date'] = expire_date + if member_limit: payload['member_limit'] = member_limit return _make_request(token, method_url, params=payload, method='post') @@ -991,9 +991,10 @@ def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit } if expire_date is not None: - payload['expire_date'] = expire_date if isinstance(payload['expire_date'], datetime): payload['expire_date'] = payload['expire_date'].timestamp() + else: + payload['expire_date'] = expire_date if member_limit is not None: payload['member_limit'] = member_limit @@ -1258,7 +1259,7 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m def send_invoice( token, chat_id, title, description, invoice_payload, provider_token, currency, prices, - start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None, + start_parameter = None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, @@ -1298,8 +1299,10 @@ def send_invoice( """ method_url = r'sendInvoice' payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, - 'provider_token': provider_token, 'start_parameter': start_parameter, 'currency': currency, + 'provider_token': provider_token, 'currency': currency, 'prices': _convert_list_json_serializable(prices)} + if start_parameter: + payload['start_parameter'] = start_parameter if photo_url: payload['photo_url'] = photo_url if photo_size: diff --git a/telebot/types.py b/telebot/types.py index dfcfb8ff8..1d8bdc060 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -441,12 +441,10 @@ def de_json(cls, json_string): opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) content_type = 'voice_chat_ended' if 'voice_chat_participants_invited' in obj: - opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json( - obj['voice_chat_participants_invited']) + opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['voice_chat_participants_invited']) content_type = 'voice_chat_participants_invited' if 'message_auto_delete_timer_changed' in obj: - opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json( - obj['message_auto_delete_timer_changed']) + opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(obj['message_auto_delete_timer_changed']) content_type = 'message_auto_delete_timer_changed' if 'reply_markup' in obj: opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) @@ -1232,6 +1230,29 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.until_date: int = until_date +class ChatMemberOwner(ChatMember): + pass + +class ChatMemberAdministrator(ChatMember): + pass + + +class ChatMemberMember(ChatMember): + pass + + +class ChatMemberRestricted(ChatMember): + pass + + +class ChatMemberLeft(ChatMember): + pass + + +class ChatMemberBanned(ChatMember): + pass + + class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): @classmethod def de_json(cls, json_string): @@ -2744,14 +2765,18 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - return { + json_dict = { "invite_link": self.invite_link, "creator": self.creator.to_dict(), "is_primary": self.is_primary, - "is_revoked": self.is_revoked, - "expire_date": self.expire_date, - "member_limit": self.member_limit + "is_revoked": self.is_revoked } + if self.expire_date: + json_dict["expire_date"] = self.expire_date + if self.member_limit: + json_dict["member_limit"] = self.member_limit + return json_dict + class ProximityAlertTriggered(JsonDeserializable): @classmethod @@ -2778,6 +2803,7 @@ def __init__(self): """ pass + class VoiceChatScheduled(JsonDeserializable): @classmethod def de_json(cls, json_string): diff --git a/telebot/version.py b/telebot/version.py index 80f0f16f0..afeff5596 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__ = '3.8.2' +__version__ = '3.8.3' From f6359bc32c12a49db9a35cbddb1591f393edda90 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 18 Aug 2021 23:29:40 +0300 Subject: [PATCH 0657/1808] Readme fix --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 93e188c80..d1baef754 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) [![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/) -#

pyTelegramBotAPI

+#

pyTelegramBotAPI -

A simple, but extensible Python implementation for the Telegram Bot API.

+

A simple, but extensible Python implementation for the Telegram Bot API. -##

Supported Bot API version: 5.3!

+##

Supported Bot API version: 5.3! ##Contents From f5de0eeacfc6cd8244ff1c0446dd875d6f6d49e5 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 19 Aug 2021 22:46:12 +0300 Subject: [PATCH 0658/1808] Simplify and speedup _test_filter --- README.md | 18 +----------------- telebot/__init__.py | 35 ++++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index d1baef754..09e45a7a1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ##

Supported Bot API version: 5.3! -##Contents +## Contents * [Getting started.](#getting-started) * [Writing your first bot](#writing-your-first-bot) @@ -636,23 +636,8 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} * ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016) -## Change log - -27.04.2020 - Poll and Dice are up to date. -Python2 conformance is not checked any more due to EOL. - -11.04.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking. - -06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll - ## F.A.Q. -### Bot 2.0 - -April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision especially for the change of method's interface.If you want to update to the latest version, please make sure you've switched bot's code to bot 2.0 method interface. - -[More information about pyTelegramBotAPI support bot2.0](https://github.com/eternnoir/pyTelegramBotAPI/issues/130) - ### How can I distinguish a User and a GroupChat in message.chat? Telegram Bot API support new type Chat for message.chat. @@ -682,7 +667,6 @@ Bot instances that were idle for a long time might be rejected by the server whe Get help. Discuss. Chat. * Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A) -* We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi). ## More examples diff --git a/telebot/__init__.py b/telebot/__init__.py index e9aaff69f..29fabe028 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3040,19 +3040,28 @@ def _test_message_handler(self, message_handler, message): def _test_filter(message_filter, filter_value, message): """ Test filters - :param message_filter: - :param filter_value: - :param message: - :return: - """ - test_cases = { - 'content_types': lambda msg: msg.content_type in filter_value, - 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), - 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, - 'func': lambda msg: filter_value(msg) - } - - return test_cases.get(message_filter, lambda msg: False)(message) + :param message_filter: Filter type passed in handler + :param filter_value: Filter value passed in handler + :param message: Message to test + :return: True if filter conforms + """ + # test_cases = { + # 'content_types': lambda msg: msg.content_type in filter_value, + # 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), + # 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, + # 'func': lambda msg: filter_value(msg) + # } + # return test_cases.get(message_filter, lambda msg: False)(message) + if message_filter == 'content_types': + return message.content_type in filter_value + elif message_filter == 'regexp': + return message.content_type == 'text' and re.search(filter_value, message.text, re.IGNORECASE) + elif message_filter == 'commands': + return message.content_type == 'text' and util.extract_command(message.text) in filter_value + elif message_filter == 'func': + return filter_value(message) + else: + return False def _notify_command_handlers(self, handlers, new_messages): """ From 3efc2cf86902318cf4e37a78f4ce2e0a1ba352cf Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 19 Aug 2021 23:36:37 +0300 Subject: [PATCH 0659/1808] Typo --- telebot/__init__.py | 64 ++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 29fabe028..49a24ca75 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2544,11 +2544,11 @@ def register_message_handler(self, callback, content_types=None, commands=None, :return: decorated function """ handler_dict = self._build_handler_dict(callback, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) self.add_message_handler(handler_dict) def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ @@ -2595,11 +2595,11 @@ def register_edited_message_handler(self, callback, content_types=None, commands :return: decorated function """ handler_dict = self._build_handler_dict(callback, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) self.add_edited_message_handler(handler_dict) def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ @@ -2646,11 +2646,11 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N :return: decorated function """ handler_dict = self._build_handler_dict(callback, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) self.add_channel_post_handler(handler_dict) def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ @@ -2697,11 +2697,11 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com :return: decorated function """ handler_dict = self._build_handler_dict(callback, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) self.add_edited_channel_post_handler(handler_dict) def inline_handler(self, func, **kwargs): @@ -2734,9 +2734,7 @@ def register_inline_handler(self, callback, func, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_inline_handler(handler_dict) def chosen_inline_handler(self, func, **kwargs): @@ -2769,9 +2767,7 @@ def register_chosen_inline_handler(self, callback, func, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_chosen_inline_handler(handler_dict) @@ -2805,9 +2801,7 @@ def register_callback_query_handler(self, callback, func, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_callback_query_handler(handler_dict) def shipping_query_handler(self, func, **kwargs): @@ -2840,9 +2834,7 @@ def register_shipping_query_handler(self, callback, func, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_shipping_query_handler(handler_dict) def pre_checkout_query_handler(self, func, **kwargs): @@ -2875,9 +2867,7 @@ def register_pre_checkout_query_handler(self, callback, func, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_pre_checkout_query_handler(handler_dict) def poll_handler(self, func, **kwargs): @@ -3015,9 +3005,7 @@ def register_chat_member_handler(self, callback, func=None, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_chat_member_handler(handler_dict) def _test_message_handler(self, message_handler, message): From bd3a9bc350be88f6b49f3d1fdcd031af9dd4129d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 22 Aug 2021 22:16:03 +0300 Subject: [PATCH 0660/1808] chat_invite_link bugfix --- telebot/apihelper.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0d3820fa1..609805360 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -973,8 +973,8 @@ def create_chat_invite_link(token, chat_id, expire_date, member_limit): } if expire_date is not None: - if isinstance(payload['expire_date'], datetime): - payload['expire_date'] = payload['expire_date'].timestamp() + if isinstance(expire_date, datetime): + payload['expire_date'] = expire_date.timestamp() else: payload['expire_date'] = expire_date if member_limit: @@ -991,8 +991,8 @@ def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit } if expire_date is not None: - if isinstance(payload['expire_date'], datetime): - payload['expire_date'] = payload['expire_date'].timestamp() + if isinstance(expire_date, datetime): + payload['expire_date'] = expire_date.timestamp() else: payload['expire_date'] = expire_date From 4eb28df1ab4d09e326eb426982f20c9ba5ee3277 Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Tue, 24 Aug 2021 13:01:10 +0200 Subject: [PATCH 0661/1808] A Google Cloud Functions webhook --- telebot/util.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index d0a58b011..8c338c3c2 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -440,3 +440,18 @@ def wrapper(*args, **kwargs): return wrapper return decorator + +# Cloud helpers +def webhook_functions(bot, request): + """A webhook endpoint for Google Cloud Functions FaaS.""" + if request.is_json: + try: + request_json = request.get_json() + update = types.Update.de_json(request_json) + bot.process_new_updates([update]) + return '' + except Exception as e: + print(e) + return 'Bot FAIL', 400 + else: + return 'Bot ON' From b4f0a6d54666b5f2e7740c061ee7a2ac482f529e Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Wed, 25 Aug 2021 14:17:25 +0200 Subject: [PATCH 0662/1808] add Google in the name --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 8c338c3c2..535ffb26f 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -442,7 +442,7 @@ def wrapper(*args, **kwargs): # Cloud helpers -def webhook_functions(bot, request): +def webhook_google_functions(bot, request): """A webhook endpoint for Google Cloud Functions FaaS.""" if request.is_json: try: From d9e638a7df3052c93fe67cd7361d1c415a0ff91e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 30 Aug 2021 13:49:28 +0300 Subject: [PATCH 0663/1808] Bump version to 4.0 release --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index afeff5596..8676c8dd7 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__ = '3.8.3' +__version__ = '4.0.0' From 07ebdeab255ce848306a8fc0a8be806038397f23 Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Thu, 2 Sep 2021 19:46:01 +0200 Subject: [PATCH 0664/1808] Added missing content_type "animation" --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 535ffb26f..f871f09e7 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -27,7 +27,7 @@ thread_local = threading.local() content_type_media = [ - 'text', 'audio', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll', + 'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll', 'venue', 'location' ] From 644c6b90823327242e6951604a9e2f558d612bc2 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 10 Sep 2021 17:30:17 +0500 Subject: [PATCH 0665/1808] is_private --- telebot/__init__.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 49a24ca75..b596916fb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2476,7 +2476,7 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -2491,6 +2491,11 @@ def message_handler(self, commands=None, regexp=None, func=None, content_types=N def command_help(message): bot.send_message(message.chat.id, 'Did someone call for help?') + # Handles messages in private chat + @bot.message_handler(is_private=True) + def command_help(message): + bot.send_message(message.chat.id, 'Private chat detected, sir!') + # Handle all sent documents of type 'text/plain'. @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document']) @@ -2508,6 +2513,7 @@ def default_command(message): :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :param is_private: True for private chat """ if content_types is None: @@ -2518,6 +2524,7 @@ def decorator(handler): content_types=content_types, commands=commands, regexp=regexp, + is_private=is_private, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2533,7 +2540,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, is_private=None, **kwargs): """ Registers message handler. :param callback: function to be called @@ -2541,6 +2548,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param commands: list of commands :param regexp: :param func: + :param is_private: True for private chat :return: decorated function """ handler_dict = self._build_handler_dict(callback, @@ -2548,15 +2556,17 @@ def register_message_handler(self, callback, content_types=None, commands=None, commands=commands, regexp=regexp, func=func, + is_private=is_private, **kwargs) self.add_message_handler(handler_dict) - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, **kwargs): """ Edit message handler decorator :param commands: :param regexp: :param func: :param content_types: + :param is_private: True for private chat :param kwargs: :return: """ @@ -2570,6 +2580,7 @@ def decorator(handler): regexp=regexp, func=func, content_types=content_types, + is_private=is_private, **kwargs) self.add_edited_message_handler(handler_dict) return handler @@ -2584,7 +2595,7 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, is_private=None, **kwargs): """ Registers edited message handler. :param callback: function to be called @@ -2592,6 +2603,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param commands: list of commands :param regexp: :param func: + :param is_private: True for private chat :return: decorated function """ handler_dict = self._build_handler_dict(callback, @@ -2599,6 +2611,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands commands=commands, regexp=regexp, func=func, + is_private=is_private, **kwargs) self.add_edited_message_handler(handler_dict) def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): @@ -3046,6 +3059,8 @@ def _test_filter(message_filter, filter_value, message): return message.content_type == 'text' and re.search(filter_value, message.text, re.IGNORECASE) elif message_filter == 'commands': return message.content_type == 'text' and util.extract_command(message.text) in filter_value + elif message_filter == 'is_private': + return message.chat.type == 'private' elif message_filter == 'func': return filter_value(message) else: From 944b077c65415b7489fc65f1f569e0cb904f800d Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 10 Sep 2021 17:30:58 +0500 Subject: [PATCH 0666/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 09e45a7a1..5c3968733 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ TeleBot supports the following filters: |content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.| |regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))| |commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.| +|is_private|Private chat|`True` if chat is private |func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` Here are some examples of using the filters and message handlers: From 4035a385078648afd3fb576a62c25ac5021b6a6a Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 10 Sep 2021 17:56:44 +0500 Subject: [PATCH 0667/1808] Update __init__.py --- telebot/__init__.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b596916fb..6d5ea259c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2476,7 +2476,7 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, only_private=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -2492,7 +2492,7 @@ def command_help(message): bot.send_message(message.chat.id, 'Did someone call for help?') # Handles messages in private chat - @bot.message_handler(is_private=True) + @bot.message_handler(only_private=True) def command_help(message): bot.send_message(message.chat.id, 'Private chat detected, sir!') @@ -2513,7 +2513,7 @@ def default_command(message): :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param content_types: Supported message content types. Must be a list. Defaults to ['text']. - :param is_private: True for private chat + :param only_private: True for private chat """ if content_types is None: @@ -2524,7 +2524,7 @@ def decorator(handler): content_types=content_types, commands=commands, regexp=regexp, - is_private=is_private, + only_private=only_private, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2540,7 +2540,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, is_private=None, **kwargs): + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, only_private=None, **kwargs): """ Registers message handler. :param callback: function to be called @@ -2548,7 +2548,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param commands: list of commands :param regexp: :param func: - :param is_private: True for private chat + :param only_private: True for private chat :return: decorated function """ handler_dict = self._build_handler_dict(callback, @@ -2556,17 +2556,17 @@ def register_message_handler(self, callback, content_types=None, commands=None, commands=commands, regexp=regexp, func=func, - is_private=is_private, + only_private=only_private, **kwargs) self.add_message_handler(handler_dict) - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, is_private=None, **kwargs): + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, only_private=None, **kwargs): """ Edit message handler decorator :param commands: :param regexp: :param func: :param content_types: - :param is_private: True for private chat + :param only_private: True for private chat :param kwargs: :return: """ @@ -2580,7 +2580,7 @@ def decorator(handler): regexp=regexp, func=func, content_types=content_types, - is_private=is_private, + only_private=only_private, **kwargs) self.add_edited_message_handler(handler_dict) return handler @@ -2595,7 +2595,7 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, is_private=None, **kwargs): + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, only_private=None, **kwargs): """ Registers edited message handler. :param callback: function to be called @@ -2603,7 +2603,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param commands: list of commands :param regexp: :param func: - :param is_private: True for private chat + :param only_private: True for private chat :return: decorated function """ handler_dict = self._build_handler_dict(callback, @@ -2611,7 +2611,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands commands=commands, regexp=regexp, func=func, - is_private=is_private, + only_private=only_private, **kwargs) self.add_edited_message_handler(handler_dict) def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): @@ -3059,7 +3059,7 @@ def _test_filter(message_filter, filter_value, message): return message.content_type == 'text' and re.search(filter_value, message.text, re.IGNORECASE) elif message_filter == 'commands': return message.content_type == 'text' and util.extract_command(message.text) in filter_value - elif message_filter == 'is_private': + elif message_filter == 'only_private': return message.chat.type == 'private' elif message_filter == 'func': return filter_value(message) From de6f339cdfe27d5564cb813f77d7b4b59cda0166 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 10 Sep 2021 17:57:19 +0500 Subject: [PATCH 0668/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c3968733..7fa98012c 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ TeleBot supports the following filters: |content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.| |regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))| |commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.| -|is_private|Private chat|`True` if chat is private +|only_private|Private chat|`True` if chat is private |func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` Here are some examples of using the filters and message handlers: From 0f3a6393fc22b9ca675138ec26b75e2b72ecfb53 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 10 Sep 2021 20:42:43 +0500 Subject: [PATCH 0669/1808] Update __init__.py --- telebot/__init__.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6d5ea259c..504e5150a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2476,7 +2476,7 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, only_private=None, **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -2492,7 +2492,7 @@ def command_help(message): bot.send_message(message.chat.id, 'Did someone call for help?') # Handles messages in private chat - @bot.message_handler(only_private=True) + @bot.message_handler(chat_types=['private']) # You can add more chat types def command_help(message): bot.send_message(message.chat.id, 'Private chat detected, sir!') @@ -2513,7 +2513,7 @@ def default_command(message): :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param content_types: Supported message content types. Must be a list. Defaults to ['text']. - :param only_private: True for private chat + :param chat_types: list of chat types """ if content_types is None: @@ -2524,7 +2524,7 @@ def decorator(handler): content_types=content_types, commands=commands, regexp=regexp, - only_private=only_private, + chat_types=chat_types, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2540,7 +2540,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, only_private=None, **kwargs): + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): """ Registers message handler. :param callback: function to be called @@ -2548,7 +2548,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param commands: list of commands :param regexp: :param func: - :param only_private: True for private chat + :param chat_types: True for private chat :return: decorated function """ handler_dict = self._build_handler_dict(callback, @@ -2556,17 +2556,17 @@ def register_message_handler(self, callback, content_types=None, commands=None, commands=commands, regexp=regexp, func=func, - only_private=only_private, + chat_types=chat_types, **kwargs) self.add_message_handler(handler_dict) - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, only_private=None, **kwargs): + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Edit message handler decorator :param commands: :param regexp: :param func: :param content_types: - :param only_private: True for private chat + :param chat_types: list of chat types :param kwargs: :return: """ @@ -2580,7 +2580,7 @@ def decorator(handler): regexp=regexp, func=func, content_types=content_types, - only_private=only_private, + chat_types=chat_types, **kwargs) self.add_edited_message_handler(handler_dict) return handler @@ -2595,7 +2595,7 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, only_private=None, **kwargs): + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): """ Registers edited message handler. :param callback: function to be called @@ -2603,7 +2603,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param commands: list of commands :param regexp: :param func: - :param only_private: True for private chat + :param chat_types: True for private chat :return: decorated function """ handler_dict = self._build_handler_dict(callback, @@ -2611,7 +2611,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands commands=commands, regexp=regexp, func=func, - only_private=only_private, + chat_types=chat_types, **kwargs) self.add_edited_message_handler(handler_dict) def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): @@ -3059,8 +3059,8 @@ def _test_filter(message_filter, filter_value, message): return message.content_type == 'text' and re.search(filter_value, message.text, re.IGNORECASE) elif message_filter == 'commands': return message.content_type == 'text' and util.extract_command(message.text) in filter_value - elif message_filter == 'only_private': - return message.chat.type == 'private' + elif message_filter == 'chat_types': + return message.chat.type in filter_value elif message_filter == 'func': return filter_value(message) else: From 0f7eb1571e79af4a558668d2da98aabf8eac939c Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 10 Sep 2021 20:42:48 +0500 Subject: [PATCH 0670/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fa98012c..a68a95762 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ TeleBot supports the following filters: |content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.| |regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))| |commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.| -|only_private|Private chat|`True` if chat is private +|chat_types|list of chat types|`True` if `message.chat.type` in your filter |func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` Here are some examples of using the filters and message handlers: From f70b1353599eadf5a00dda6c4d5b5b34faed3294 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Sep 2021 17:02:40 +0300 Subject: [PATCH 0671/1808] Filter clearance 1. Filter optimization: should not store empty filters 2. Filter order: chat_type, content, others 3. Default session timeout set to 600 instead of "forever". 4. Type --- telebot/__init__.py | 34 ++++++++++++++++------------------ telebot/apihelper.py | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 504e5150a..125f8ceec 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2425,7 +2425,9 @@ def _build_handler_dict(handler, **filters): """ return { 'function': handler, - 'filters': filters + 'filters': {ftype: fvalue for ftype, fvalue in filters.items() if fvalue is not None} + # Remove None values, they are skipped in _test_filter anyway + #'filters': filters } def middleware_handler(self, update_types=None): @@ -2521,10 +2523,10 @@ def default_command(message): def decorator(handler): handler_dict = self._build_handler_dict(handler, + chat_types=chat_types, content_types=content_types, commands=commands, regexp=regexp, - chat_types=chat_types, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2552,13 +2554,14 @@ def register_message_handler(self, callback, content_types=None, commands=None, :return: decorated function """ handler_dict = self._build_handler_dict(callback, + chat_types=chat_types, content_types=content_types, commands=commands, regexp=regexp, func=func, - chat_types=chat_types, **kwargs) self.add_message_handler(handler_dict) + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Edit message handler decorator @@ -2576,11 +2579,11 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ def decorator(handler): handler_dict = self._build_handler_dict(handler, + chat_types=chat_types, + content_types=content_types, commands=commands, regexp=regexp, func=func, - content_types=content_types, - chat_types=chat_types, **kwargs) self.add_edited_message_handler(handler_dict) return handler @@ -2607,13 +2610,14 @@ def register_edited_message_handler(self, callback, content_types=None, commands :return: decorated function """ handler_dict = self._build_handler_dict(callback, + chat_types=chat_types, content_types=content_types, commands=commands, regexp=regexp, func=func, - chat_types=chat_types, **kwargs) self.add_edited_message_handler(handler_dict) + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Channel post handler decorator @@ -2630,10 +2634,10 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty def decorator(handler): handler_dict = self._build_handler_dict(handler, + content_types=content_types, commands=commands, regexp=regexp, func=func, - content_types=content_types, **kwargs) self.add_channel_post_handler(handler_dict) return handler @@ -2665,6 +2669,7 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N func=func, **kwargs) self.add_channel_post_handler(handler_dict) + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Edit channel post handler decorator @@ -2681,10 +2686,10 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con def decorator(handler): handler_dict = self._build_handler_dict(handler, + content_types=content_types, commands=commands, regexp=regexp, func=func, - content_types=content_types, **kwargs) self.add_edited_channel_post_handler(handler_dict) return handler @@ -2783,7 +2788,6 @@ def register_chosen_inline_handler(self, callback, func, **kwargs): handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_chosen_inline_handler(handler_dict) - def callback_query_handler(self, func, **kwargs): """ Callback request handler decorator @@ -2913,9 +2917,7 @@ def register_poll_handler(self, callback, func, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_poll_handler(handler_dict) def poll_answer_handler(self, func=None, **kwargs): @@ -2948,9 +2950,7 @@ def register_poll_answer_handler(self, callback, func, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_poll_answer_handler(handler_dict) def my_chat_member_handler(self, func=None, **kwargs): @@ -2983,9 +2983,7 @@ def register_my_chat_member_handler(self, callback, func=None, **kwargs): :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, - func=func, - **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_my_chat_member_handler(handler_dict) def chat_member_handler(self, func=None, **kwargs): diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 609805360..9588c4e23 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -33,7 +33,7 @@ LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates) -SESSION_TIME_TO_LIVE = None # In seconds. None - live forever, 0 - one-time +SESSION_TIME_TO_LIVE = 600 # In seconds. None - live forever, 0 - one-time RETRY_ON_ERROR = False RETRY_TIMEOUT = 2 From 16edfbb9dcdffafb8f9f10abef07e99376493afb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Sep 2021 19:26:55 +0300 Subject: [PATCH 0672/1808] Warning if commands or content_types filters are strings --- telebot/__init__.py | 66 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 125f8ceec..ae23416cf 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2521,6 +2521,14 @@ def default_command(message): if content_types is None: content_types = ["text"] + if isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + def decorator(handler): handler_dict = self._build_handler_dict(handler, chat_types=chat_types, @@ -2553,6 +2561,14 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param chat_types: True for private chat :return: decorated function """ + if isinstance(commands, str): + logger.warning("register_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + handler_dict = self._build_handler_dict(callback, chat_types=chat_types, content_types=content_types, @@ -2577,6 +2593,14 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ if content_types is None: content_types = ["text"] + if isinstance(commands, str): + logger.warning("edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + def decorator(handler): handler_dict = self._build_handler_dict(handler, chat_types=chat_types, @@ -2609,6 +2633,14 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param chat_types: True for private chat :return: decorated function """ + if isinstance(commands, str): + logger.warning("register_edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + handler_dict = self._build_handler_dict(callback, chat_types=chat_types, content_types=content_types, @@ -2628,10 +2660,17 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty :param kwargs: :return: """ - if content_types is None: content_types = ["text"] + if isinstance(commands, str): + logger.warning("channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + def decorator(handler): handler_dict = self._build_handler_dict(handler, content_types=content_types, @@ -2662,6 +2701,14 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N :param func: :return: decorated function """ + if isinstance(commands, str): + logger.warning("register_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, @@ -2680,10 +2727,17 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con :param kwargs: :return: """ - if content_types is None: content_types = ["text"] + if isinstance(commands, str): + logger.warning("edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + def decorator(handler): handler_dict = self._build_handler_dict(handler, content_types=content_types, @@ -2714,6 +2768,14 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com :param func: :return: decorated function """ + if isinstance(commands, str): + logger.warning("register_edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, From ec8975c9e3791d43793ba80b29ee88d45ab96ac4 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Sep 2021 21:47:59 +0500 Subject: [PATCH 0673/1808] Custom filters Added new feature - from now you can create your own custom filters --- telebot/__init__.py | 24 +++++++++++++++++++++--- telebot/util.py | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 504e5150a..def898356 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -185,6 +185,7 @@ def __init__( self.poll_answer_handlers = [] self.my_chat_member_handlers = [] self.chat_member_handlers = [] + self.custom_filters = {} if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { @@ -3037,8 +3038,16 @@ def _test_message_handler(self, message_handler, message): return True - @staticmethod - def _test_filter(message_filter, filter_value, message): + def create_filter(self, custom_filter): + """ + Create custom filter. + :params: + custom_filter: Class with check(message) method.""" + self.custom_filters[custom_filter.key] = custom_filter + + + + def _test_filter(self, message_filter, filter_value, message): """ Test filters :param message_filter: Filter type passed in handler @@ -3053,6 +3062,7 @@ def _test_filter(message_filter, filter_value, message): # 'func': lambda msg: filter_value(msg) # } # return test_cases.get(message_filter, lambda msg: False)(message) + if message_filter == 'content_types': return message.content_type in filter_value elif message_filter == 'regexp': @@ -3064,7 +3074,15 @@ def _test_filter(message_filter, filter_value, message): elif message_filter == 'func': return filter_value(message) else: - return False + if message_filter in self.custom_filters: + if message_filter in self.custom_filters: + filter_check = self.custom_filters.get(message_filter) + if filter_value == filter_check.check(message): return True + else: return False + else: + return False + + def _notify_command_handlers(self, handlers, new_messages): """ diff --git a/telebot/util.py b/telebot/util.py index f871f09e7..310f4c5e9 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -455,3 +455,4 @@ def webhook_google_functions(bot, request): return 'Bot FAIL', 400 else: return 'Bot ON' + From 8f3371dcd5a42de1be085bfdcaa6de94d97ed06e Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Sep 2021 21:59:28 +0500 Subject: [PATCH 0674/1808] Update __init__.py --- telebot/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index def898356..d40bdd756 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3075,10 +3075,9 @@ def _test_filter(self, message_filter, filter_value, message): return filter_value(message) else: if message_filter in self.custom_filters: - if message_filter in self.custom_filters: - filter_check = self.custom_filters.get(message_filter) - if filter_value == filter_check.check(message): return True - else: return False + filter_check = self.custom_filters.get(message_filter) + if filter_value == filter_check.check(message): return True + else: return False else: return False From 87fb30d57bb20add71b28d34a0e769f4aec0db57 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Sep 2021 22:03:37 +0500 Subject: [PATCH 0675/1808] Update __init__.py --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d40bdd756..95b32061f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3038,7 +3038,7 @@ def _test_message_handler(self, message_handler, message): return True - def create_filter(self, custom_filter): + def add_custom_filter(self, custom_filter): """ Create custom filter. :params: From 8e4d70b9c6161f6692cabb0cf802ab51ca5defa5 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Sep 2021 22:30:53 +0500 Subject: [PATCH 0676/1808] Update __init__.py --- telebot/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 95b32061f..0b5a5032f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3076,8 +3076,13 @@ def _test_filter(self, message_filter, filter_value, message): else: if message_filter in self.custom_filters: filter_check = self.custom_filters.get(message_filter) - if filter_value == filter_check.check(message): return True - else: return False + if type(filter_value) is bool: + + if filter_value == filter_check.check(message): return True + else: return False + else: + if filter_check.check(message,filter_value) is True: return True + else: return False else: return False From 9d37503442fb73eb2be24e7d8ede2853a846fe4a Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Sep 2021 23:02:56 +0500 Subject: [PATCH 0677/1808] reupdated --- telebot/__init__.py | 23 +++++++++++++---------- telebot/util.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0b5a5032f..c90e12b93 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3074,17 +3074,20 @@ def _test_filter(self, message_filter, filter_value, message): elif message_filter == 'func': return filter_value(message) else: - if message_filter in self.custom_filters: - filter_check = self.custom_filters.get(message_filter) - if type(filter_value) is bool: - - if filter_value == filter_check.check(message): return True - else: return False - else: - if filter_check.check(message,filter_value) is True: return True - else: return False + return self._check_filter(message_filter,filter_value,message) + + def _check_filter(self, message_filter, filter_value, message): + if message_filter in self.custom_filters: + filter_check = self.custom_filters.get(message_filter) + if isinstance(filter_value, util.SimpleCustomFilter): + + if filter_value == filter_check.check(message): return True + else: return False else: - return False + if filter_check.check(message,filter_value) is True: return True + else: return False + else: + return False diff --git a/telebot/util.py b/telebot/util.py index 310f4c5e9..88019ad86 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -456,3 +456,31 @@ def webhook_google_functions(bot, request): else: return 'Bot ON' + +class SimpleCustomFilter: + """ + Simple Custom Filter base class. + Create child class with check() method. + Accepts only bool. + """ + + def check(message): + """ + Perform a check. + """ + pass + +class AdvancedCustomFilter: + """ + Simple Custom Filter base class. + Create child class with check() method. + Can accept to parameters. + message: Message class + text: Filter value given in handler + """ + + def check(message, text): + """ + Perform a check. + """ + pass \ No newline at end of file From 14be2b8c183e5887335621f8a3f8f28d39c13c0f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Sep 2021 21:10:21 +0300 Subject: [PATCH 0678/1808] Custom filters upd --- telebot/__init__.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 441665776..53ea22dab 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3101,11 +3101,9 @@ def _test_message_handler(self, message_handler, message): def add_custom_filter(self, custom_filter): """ Create custom filter. - :params: - custom_filter: Class with check(message) method.""" + custom_filter: Class with check(message) method. + """ self.custom_filters[custom_filter.key] = custom_filter - - def _test_filter(self, message_filter, filter_value, message): """ @@ -3133,24 +3131,23 @@ def _test_filter(self, message_filter, filter_value, message): return message.chat.type in filter_value elif message_filter == 'func': return filter_value(message) - else: + elif self.custom_filters and message_filter in self.custom_filters: return self._check_filter(message_filter,filter_value,message) + else: + return False def _check_filter(self, message_filter, filter_value, message): - if message_filter in self.custom_filters: - filter_check = self.custom_filters.get(message_filter) - if isinstance(filter_value, util.SimpleCustomFilter): - - if filter_value == filter_check.check(message): return True - else: return False - else: - if filter_check.check(message,filter_value) is True: return True - else: return False + filter_check = self.custom_filters.get(message_filter) + if not filter_check: + return False + elif isinstance(filter_value, util.SimpleCustomFilter): + return filter_value == filter_check.check(message) + elif isinstance(filter_value, util.AdvancedCustomFilter): + return filter_check.check(message,filter_value) else: + logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") return False - - def _notify_command_handlers(self, handlers, new_messages): """ Notifies command handlers From 2da48c0adc6b004b149b6d7de5b4c9df5efc4139 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 11 Sep 2021 21:49:51 +0300 Subject: [PATCH 0679/1808] Custom filters upd --- telebot/__init__.py | 6 +++--- telebot/util.py | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 53ea22dab..e37943574 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3140,10 +3140,10 @@ def _check_filter(self, message_filter, filter_value, message): filter_check = self.custom_filters.get(message_filter) if not filter_check: return False - elif isinstance(filter_value, util.SimpleCustomFilter): + elif isinstance(filter_check, util.SimpleCustomFilter): return filter_value == filter_check.check(message) - elif isinstance(filter_value, util.AdvancedCustomFilter): - return filter_check.check(message,filter_value) + elif isinstance(filter_check, util.AdvancedCustomFilter): + return filter_check.check(message, filter_value) else: logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") return False diff --git a/telebot/util.py b/telebot/util.py index 88019ad86..8607ff2be 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -5,6 +5,7 @@ import threading import traceback from typing import Any, Callable, List, Dict, Optional, Union +from abc import ABC # noinspection PyPep8Naming import queue as Queue @@ -457,20 +458,20 @@ def webhook_google_functions(bot, request): return 'Bot ON' -class SimpleCustomFilter: +class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. Create child class with check() method. Accepts only bool. """ - def check(message): + def check(self, message): """ Perform a check. """ pass -class AdvancedCustomFilter: +class AdvancedCustomFilter(ABC): """ Simple Custom Filter base class. Create child class with check() method. @@ -479,8 +480,8 @@ class AdvancedCustomFilter: text: Filter value given in handler """ - def check(message, text): + def check(self, message, text): """ Perform a check. """ - pass \ No newline at end of file + pass From 5c80f112612e6d1701e554da36d87963811983b7 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 00:21:35 +0500 Subject: [PATCH 0680/1808] Updated __init__.py --- examples/custom_filters.py | 46 ++++++++++++++++++++++++++++++++++++++ telebot/__init__.py | 4 ++-- 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 examples/custom_filters.py diff --git a/examples/custom_filters.py b/examples/custom_filters.py new file mode 100644 index 000000000..23cebe3bb --- /dev/null +++ b/examples/custom_filters.py @@ -0,0 +1,46 @@ +import telebot +from telebot import util + +bot = telebot.TeleBot('TOKEN') + + +# AdvancedCustomFilter is for list, string filter values +class MainFilter(util.AdvancedCustomFilter): + key='text' + @staticmethod + def check(message, text): + return message.text in text + +# SimpleCustomFilter is for boolean values, such as is_admin=True +class IsAdmin(util.SimpleCustomFilter): + key='is_admin' + @staticmethod + def check(message: telebot.types.Message): + if bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']: + return True + else: + return False + + +@bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin +def admin_rep(message): + bot.send_message(message.chat.id, "Hi admin") + +@bot.message_handler(is_admin=False, commands=['admin']) # If user is not admin +def not_admin(message): + bot.send_message(message.chat.id, "You are not admin") + +@bot.message_handler(text=['hi']) # Response to hi message +def welcome_hi(message): + bot.send_message(message.chat.id, 'You said hi') + +@bot.message_handler(text=['bye']) # Response to bye message +def bye_user(message): + bot.send_message(message.chat.id, 'You said bye') + + +# Do not forget to register filters +bot.add_custom_filter(MainFilter) +bot.add_custom_filter(IsAdmin) + +bot.polling(skip_pending=True) # Skip old updates \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index e37943574..5816cdf5f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3140,9 +3140,9 @@ def _check_filter(self, message_filter, filter_value, message): filter_check = self.custom_filters.get(message_filter) if not filter_check: return False - elif isinstance(filter_check, util.SimpleCustomFilter): + elif isinstance(filter_check(), util.SimpleCustomFilter): return filter_value == filter_check.check(message) - elif isinstance(filter_check, util.AdvancedCustomFilter): + elif isinstance(filter_check(), util.AdvancedCustomFilter): return filter_check.check(message, filter_value) else: logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") From 5d611ea7f3ce34f22626ce4aa91d3c7400a2fa23 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 00:24:52 +0500 Subject: [PATCH 0681/1808] upd --- examples/custom_filters.py | 2 +- examples/deep_linking.py | 3 +-- examples/echo_bot.py | 2 +- examples/inline_example.py | 2 +- examples/inline_keyboard_example.py | 2 +- examples/payments_example.py | 3 +-- examples/skip_updates_example.py | 2 +- 7 files changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/custom_filters.py b/examples/custom_filters.py index 23cebe3bb..02944680d 100644 --- a/examples/custom_filters.py +++ b/examples/custom_filters.py @@ -43,4 +43,4 @@ def bye_user(message): bot.add_custom_filter(MainFilter) bot.add_custom_filter(IsAdmin) -bot.polling(skip_pending=True) # Skip old updates \ No newline at end of file +bot.polling(skip_pending=True,non_stop=True) # Skip old updates \ No newline at end of file diff --git a/examples/deep_linking.py b/examples/deep_linking.py index f5ea506c2..e0a7e946e 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -73,5 +73,4 @@ def send_welcome(message): reply = "Please visit me via a provided URL from the website." bot.reply_to(message, reply) - -bot.polling() +bot.polling(skip_pending=True,non_stop=True) # Skip old updates diff --git a/examples/echo_bot.py b/examples/echo_bot.py index b66eb3442..ec71f7050 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -25,4 +25,4 @@ def echo_message(message): bot.reply_to(message, message.text) -bot.polling() +bot.polling(skip_pending=True,non_stop=True) # Skip old updates diff --git a/examples/inline_example.py b/examples/inline_example.py index 21f05eb6f..8bb064d00 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -61,7 +61,7 @@ def default_query(inline_query): def main_loop(): - bot.polling(True) + bot.polling(skip_pending=True) # Skip old updates while 1: time.sleep(3) diff --git a/examples/inline_keyboard_example.py b/examples/inline_keyboard_example.py index f2b3fcef7..56b2ae5b0 100644 --- a/examples/inline_keyboard_example.py +++ b/examples/inline_keyboard_example.py @@ -24,4 +24,4 @@ def callback_query(call): def message_handler(message): bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup()) -bot.polling(none_stop=True) +bot.polling(skip_pending=True,non_stop=True) # Skip old updates diff --git a/examples/payments_example.py b/examples/payments_example.py index d0f52d4c2..efdddaf97 100644 --- a/examples/payments_example.py +++ b/examples/payments_example.py @@ -78,5 +78,4 @@ def got_payment(message): parse_mode='Markdown') -bot.skip_pending = True -bot.polling(none_stop=True, interval=0) +bot.polling(skip_pending=True,non_stop=True) # Skip old updates diff --git a/examples/skip_updates_example.py b/examples/skip_updates_example.py index 0bd631b9e..01aa41429 100644 --- a/examples/skip_updates_example.py +++ b/examples/skip_updates_example.py @@ -10,4 +10,4 @@ def send_welcome(message): def echo_all(message): bot.reply_to(message, message.text) -bot.polling(skip_pending=True)# Skip pending skips old updates +bot.polling(skip_pending=True,non_stop=True) # Skip old updates From e89acc8ba635fd4e7fdac41c9e0acdcfef6e0242 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 00:27:04 +0500 Subject: [PATCH 0682/1808] Update custom_filters.py --- examples/custom_filters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/custom_filters.py b/examples/custom_filters.py index 02944680d..e9e40e80c 100644 --- a/examples/custom_filters.py +++ b/examples/custom_filters.py @@ -40,7 +40,7 @@ def bye_user(message): # Do not forget to register filters -bot.add_custom_filter(MainFilter) -bot.add_custom_filter(IsAdmin) +bot.add_custom_filter(MainFilter()) +bot.add_custom_filter(IsAdmin()) bot.polling(skip_pending=True,non_stop=True) # Skip old updates \ No newline at end of file From 88f91518c7518dc8648f7fd6be2f96714df444d4 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 00:27:51 +0500 Subject: [PATCH 0683/1808] Update __init__.py --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5816cdf5f..e37943574 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3140,9 +3140,9 @@ def _check_filter(self, message_filter, filter_value, message): filter_check = self.custom_filters.get(message_filter) if not filter_check: return False - elif isinstance(filter_check(), util.SimpleCustomFilter): + elif isinstance(filter_check, util.SimpleCustomFilter): return filter_value == filter_check.check(message) - elif isinstance(filter_check(), util.AdvancedCustomFilter): + elif isinstance(filter_check, util.AdvancedCustomFilter): return filter_check.check(message, filter_value) else: logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") From 4e37662ab3048128277a2f85c96bdeaead5c5386 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 00:30:56 +0500 Subject: [PATCH 0684/1808] upd --- examples/deep_linking.py | 3 ++- examples/echo_bot.py | 2 +- examples/inline_example.py | 2 +- examples/inline_keyboard_example.py | 2 +- examples/payments_example.py | 3 ++- examples/skip_updates_example.py | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/deep_linking.py b/examples/deep_linking.py index e0a7e946e..f5ea506c2 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -73,4 +73,5 @@ def send_welcome(message): reply = "Please visit me via a provided URL from the website." bot.reply_to(message, reply) -bot.polling(skip_pending=True,non_stop=True) # Skip old updates + +bot.polling() diff --git a/examples/echo_bot.py b/examples/echo_bot.py index ec71f7050..b66eb3442 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -25,4 +25,4 @@ def echo_message(message): bot.reply_to(message, message.text) -bot.polling(skip_pending=True,non_stop=True) # Skip old updates +bot.polling() diff --git a/examples/inline_example.py b/examples/inline_example.py index 8bb064d00..21f05eb6f 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -61,7 +61,7 @@ def default_query(inline_query): def main_loop(): - bot.polling(skip_pending=True) # Skip old updates + bot.polling(True) while 1: time.sleep(3) diff --git a/examples/inline_keyboard_example.py b/examples/inline_keyboard_example.py index 56b2ae5b0..f2b3fcef7 100644 --- a/examples/inline_keyboard_example.py +++ b/examples/inline_keyboard_example.py @@ -24,4 +24,4 @@ def callback_query(call): def message_handler(message): bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup()) -bot.polling(skip_pending=True,non_stop=True) # Skip old updates +bot.polling(none_stop=True) diff --git a/examples/payments_example.py b/examples/payments_example.py index efdddaf97..d0f52d4c2 100644 --- a/examples/payments_example.py +++ b/examples/payments_example.py @@ -78,4 +78,5 @@ def got_payment(message): parse_mode='Markdown') -bot.polling(skip_pending=True,non_stop=True) # Skip old updates +bot.skip_pending = True +bot.polling(none_stop=True, interval=0) diff --git a/examples/skip_updates_example.py b/examples/skip_updates_example.py index 01aa41429..0bd631b9e 100644 --- a/examples/skip_updates_example.py +++ b/examples/skip_updates_example.py @@ -10,4 +10,4 @@ def send_welcome(message): def echo_all(message): bot.reply_to(message, message.text) -bot.polling(skip_pending=True,non_stop=True) # Skip old updates +bot.polling(skip_pending=True)# Skip pending skips old updates From 5f8c75816ebb8489224a63c8b561d24bb05dfd35 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 19:34:43 +0500 Subject: [PATCH 0685/1808] Some useful filters Created useful filters that can be used in message handlers. Created some examples on using them. --- examples/id_filter_example.py | 16 +++++++++ examples/text_filter_example.py | 17 ++++++++++ telebot/__init__.py | 2 +- telebot/util.py | 60 +++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 examples/id_filter_example.py create mode 100644 examples/text_filter_example.py diff --git a/examples/id_filter_example.py b/examples/id_filter_example.py new file mode 100644 index 000000000..dbd621ba0 --- /dev/null +++ b/examples/id_filter_example.py @@ -0,0 +1,16 @@ +import telebot + +bot = telebot.TeleBot('TOKEN') + + +# Chat id can be private or supergroups. +@bot.message_handler(chat_id=[12345678], commands=['admin']) # chat_id checks id corresponds to your list or not. +def admin_rep(message): + bot.send_message(message.chat.id, "You are allowed to use this command.") + +@bot.message_handler(commands=['admin']) +def not_admin(message): + bot.send_message(message.chat.id, "You are not allowed to use this command") + + +bot.polling(non_stop=True) \ No newline at end of file diff --git a/examples/text_filter_example.py b/examples/text_filter_example.py new file mode 100644 index 000000000..da0d3801a --- /dev/null +++ b/examples/text_filter_example.py @@ -0,0 +1,17 @@ +import telebot + +bot = telebot.TeleBot('TOKEN') + + +# Check if message starts with @admin tag +@bot.message_handler(text_startswith="@admin") +def start_filter(message): + bot.send_message(message.chat.id, "Looks like you are calling admin, wait...") + +# Check if text is hi or hello +@bot.message_handler(text=['hi','hello']) +def text_filter(message): + bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name)) + + +bot.polling(non_stop=True) \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index e37943574..5c85f28e8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -185,7 +185,7 @@ def __init__( self.poll_answer_handlers = [] self.my_chat_member_handlers = [] self.chat_member_handlers = [] - self.custom_filters = {} + self.custom_filters = {'text': util.TextFilter(), 'text_contains': util.TextContains(), 'chat_id': util.UserFilter(), 'text_startswith': util.TextStarts()} if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { diff --git a/telebot/util.py b/telebot/util.py index 8607ff2be..56fa63f0b 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -485,3 +485,63 @@ def check(self, message, text): Perform a check. """ pass + +class TextFilter(AdvancedCustomFilter): + """ + Filter to check Text message. + key: text + + Example: + @bot.message_handler(text=['account']) + """ + + key = 'text' + + def check(self, message, text): + if type(text) is list:return message.text in text + else: return text == message.text + +class TextContains(AdvancedCustomFilter): + """ + Filter to check Text message. + key: text + + Example: + # Will respond if any message.text contains word 'account' + @bot.message_handler(text_contains=['account']) + """ + + key = 'text_contains' + + def check(self, message, text): + return text in message.text + +class UserFilter(AdvancedCustomFilter): + """ + Check whether chat_id corresponds to given chat_id. + + Example: + @bot.message_handler(chat_id=[99999]) + + """ + + key = 'chat_id' + def check(self, message, text): + return message.chat.id in text + + +class TextStarts(AdvancedCustomFilter): + """ + Filter to check whether message starts with some text. + + Example: + # Will work if message.text starts with 'Sir'. + @bot.message_handler(text_startswith='Sir') + + """ + + key = 'text_startswith' + def check(self, message, text): + return message.text.startswith(text) + + \ No newline at end of file From 1ceec3cb54593a5613c804e64d08116d45723e1b Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 19:38:54 +0500 Subject: [PATCH 0686/1808] Update custom_filters.py --- examples/custom_filters.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/custom_filters.py b/examples/custom_filters.py index e9e40e80c..18c8c2633 100644 --- a/examples/custom_filters.py +++ b/examples/custom_filters.py @@ -16,10 +16,7 @@ class IsAdmin(util.SimpleCustomFilter): key='is_admin' @staticmethod def check(message: telebot.types.Message): - if bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']: - return True - else: - return False + return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator'] @bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin From 7d5e9e5111379c1de3afae2074a80bcc145ef093 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 12 Sep 2021 20:22:26 +0500 Subject: [PATCH 0687/1808] Added file custom_filters Added file with custom filters. Updated the examples --- examples/id_filter_example.py | 7 +++- examples/text_filter_example.py | 4 +++ telebot/__init__.py | 2 +- telebot/custom_filters.py | 61 +++++++++++++++++++++++++++++++++ telebot/util.py | 58 ------------------------------- 5 files changed, 72 insertions(+), 60 deletions(-) create mode 100644 telebot/custom_filters.py diff --git a/examples/id_filter_example.py b/examples/id_filter_example.py index dbd621ba0..3c8384174 100644 --- a/examples/id_filter_example.py +++ b/examples/id_filter_example.py @@ -1,6 +1,7 @@ import telebot +from telebot import custom_filters -bot = telebot.TeleBot('TOKEN') +bot = telebot.TeleBot('token') # Chat id can be private or supergroups. @@ -13,4 +14,8 @@ def not_admin(message): bot.send_message(message.chat.id, "You are not allowed to use this command") +# Do not forget to register +bot.add_custom_filter(custom_filters.UserFilter()) + + bot.polling(non_stop=True) \ No newline at end of file diff --git a/examples/text_filter_example.py b/examples/text_filter_example.py index da0d3801a..12b62f1e8 100644 --- a/examples/text_filter_example.py +++ b/examples/text_filter_example.py @@ -1,4 +1,5 @@ import telebot +from telebot import custom_filters bot = telebot.TeleBot('TOKEN') @@ -13,5 +14,8 @@ def start_filter(message): def text_filter(message): bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name)) +# Do not forget to register filters +bot.add_custom_filter(custom_filters.TextFilter()) +bot.add_custom_filter(custom_filters.TextStarts()) bot.polling(non_stop=True) \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index 5c85f28e8..e37943574 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -185,7 +185,7 @@ def __init__( self.poll_answer_handlers = [] self.my_chat_member_handlers = [] self.chat_member_handlers = [] - self.custom_filters = {'text': util.TextFilter(), 'text_contains': util.TextContains(), 'chat_id': util.UserFilter(), 'text_startswith': util.TextStarts()} + self.custom_filters = {} if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py new file mode 100644 index 000000000..00b0f7561 --- /dev/null +++ b/telebot/custom_filters.py @@ -0,0 +1,61 @@ +from telebot import util + + + +class TextFilter(util.AdvancedCustomFilter): + """ + Filter to check Text message. + key: text + + Example: + @bot.message_handler(text=['account']) + """ + + key = 'text' + + def check(self, message, text): + if type(text) is list:return message.text in text + else: return text == message.text + +class TextContains(util.AdvancedCustomFilter): + """ + Filter to check Text message. + key: text + + Example: + # Will respond if any message.text contains word 'account' + @bot.message_handler(text_contains=['account']) + """ + + key = 'text_contains' + + def check(self, message, text): + return text in message.text + +class UserFilter(util.AdvancedCustomFilter): + """ + Check whether chat_id corresponds to given chat_id. + + Example: + @bot.message_handler(chat_id=[99999]) + + """ + + key = 'chat_id' + def check(self, message, text): + return message.chat.id in text + + +class TextStarts(util.AdvancedCustomFilter): + """ + Filter to check whether message starts with some text. + + Example: + # Will work if message.text starts with 'Sir'. + @bot.message_handler(text_startswith='Sir') + + """ + + key = 'text_startswith' + def check(self, message, text): + return message.text.startswith(text) diff --git a/telebot/util.py b/telebot/util.py index 56fa63f0b..7bcb257fb 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -486,62 +486,4 @@ def check(self, message, text): """ pass -class TextFilter(AdvancedCustomFilter): - """ - Filter to check Text message. - key: text - - Example: - @bot.message_handler(text=['account']) - """ - - key = 'text' - - def check(self, message, text): - if type(text) is list:return message.text in text - else: return text == message.text - -class TextContains(AdvancedCustomFilter): - """ - Filter to check Text message. - key: text - - Example: - # Will respond if any message.text contains word 'account' - @bot.message_handler(text_contains=['account']) - """ - - key = 'text_contains' - - def check(self, message, text): - return text in message.text - -class UserFilter(AdvancedCustomFilter): - """ - Check whether chat_id corresponds to given chat_id. - - Example: - @bot.message_handler(chat_id=[99999]) - - """ - - key = 'chat_id' - def check(self, message, text): - return message.chat.id in text - - -class TextStarts(AdvancedCustomFilter): - """ - Filter to check whether message starts with some text. - - Example: - # Will work if message.text starts with 'Sir'. - @bot.message_handler(text_startswith='Sir') - - """ - - key = 'text_startswith' - def check(self, message, text): - return message.text.startswith(text) - \ No newline at end of file From cf75e76e5c7034921c1fb4d9ab71561cc6a5ca18 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:27:01 +0500 Subject: [PATCH 0688/1808] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index a68a95762..bcfc517e4 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,15 @@ TeleBot supports the following filters: |chat_types|list of chat types|`True` if `message.chat.type` in your filter |func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` +### Custom filters +Also, you can use built-in custom filters. Or, you can create your own filter. + +[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters.py) + +You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py) +Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/id_filter_example.py) +Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/text_filter_example.py) + Here are some examples of using the filters and message handlers: ```python From 8534804c0c5d55ac80a05492520a2060e68008bf Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:28:01 +0500 Subject: [PATCH 0689/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bcfc517e4..a2d80d5ba 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,9 @@ Also, you can use built-in custom filters. Or, you can create your own filter. [Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters.py) You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py) + Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/id_filter_example.py) + Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/text_filter_example.py) Here are some examples of using the filters and message handlers: From 5f4cd09490841c4a38ead7633a7057f051eb5d0d Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:28:46 +0500 Subject: [PATCH 0690/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a2d80d5ba..e92cef9de 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,8 @@ Also, you can use built-in custom filters. Or, you can create your own filter. [Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters.py) +Also, we have examples on them. Check this links: + You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py) Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/id_filter_example.py) From cf78234e3a36da72e55e60364c4bebcadf1ca1cd Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:30:32 +0500 Subject: [PATCH 0691/1808] Update README.md --- README.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e92cef9de..2204efd85 100644 --- a/README.md +++ b/README.md @@ -169,19 +169,6 @@ TeleBot supports the following filters: |commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.| |chat_types|list of chat types|`True` if `message.chat.type` in your filter |func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` - -### Custom filters -Also, you can use built-in custom filters. Or, you can create your own filter. - -[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters.py) - -Also, we have examples on them. Check this links: - -You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py) - -Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/id_filter_example.py) - -Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/text_filter_example.py) Here are some examples of using the filters and message handlers: @@ -300,6 +287,21 @@ def start(message): assert message.another_text == message.text + ':changed' ``` There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory. + + +### Custom filters +Also, you can use built-in custom filters. Or, you can create your own filter. + +[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters.py) + +Also, we have examples on them. Check this links: + +You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py) + +Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/id_filter_example.py) + +Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/text_filter_example.py) + #### TeleBot ```python From 43d2d8583e7f7cd70d91189ecfbf160534a52c77 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:32:16 +0500 Subject: [PATCH 0692/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2204efd85..52524b484 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,8 @@ Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/ Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/text_filter_example.py) +If you want to add some built-in filter, you are welcome to add it in custom_filters.py file. + #### TeleBot ```python From 5c715dabc3e7bb1bee22982276c2c7e6494d2495 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:40:31 +0500 Subject: [PATCH 0693/1808] Update README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 52524b484..1adc74337 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,28 @@ Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blo If you want to add some built-in filter, you are welcome to add it in custom_filters.py file. +Here is example of creating filter-class: + +``` +class IsAdmin(util.SimpleCustomFilter): + # Class will check whether the user is admin or creator in group or not + key='is_admin' + @staticmethod + def check(message: telebot.types.Message): + return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator'] + + + # To register filter, you need to use method add_custom_filter. + + bot.add_custom_filter(IsAdmin()) + + + # Now, you can use it in handler. + + @bot.message_handler(is_admin=True) + +``` + #### TeleBot ```python From 4071ab91244d793d4e8b5dd8c6ccbfc9fd92fd51 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:41:26 +0500 Subject: [PATCH 0694/1808] Update README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 1adc74337..a40e5ff06 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,7 @@ If you want to add some built-in filter, you are welcome to add it in custom_fil Here is example of creating filter-class: -``` +```python class IsAdmin(util.SimpleCustomFilter): # Class will check whether the user is admin or creator in group or not key='is_admin' @@ -314,14 +314,10 @@ class IsAdmin(util.SimpleCustomFilter): def check(message: telebot.types.Message): return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator'] - # To register filter, you need to use method add_custom_filter. - bot.add_custom_filter(IsAdmin()) - # Now, you can use it in handler. - @bot.message_handler(is_admin=True) ``` From c86af0496bf15befe81a56ff6b4beadbc0dc734f Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Sep 2021 20:43:09 +0500 Subject: [PATCH 0695/1808] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a40e5ff06..849efadad 100644 --- a/README.md +++ b/README.md @@ -314,11 +314,13 @@ class IsAdmin(util.SimpleCustomFilter): def check(message: telebot.types.Message): return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator'] - # To register filter, you need to use method add_custom_filter. - bot.add_custom_filter(IsAdmin()) +# To register filter, you need to use method add_custom_filter. +bot.add_custom_filter(IsAdmin()) - # Now, you can use it in handler. - @bot.message_handler(is_admin=True) +# Now, you can use it in handler. +@bot.message_handler(is_admin=True) +def admin_of_group(message): + bot.send_message(message.chat.id, 'You are admin of this group'!) ``` From 4ced4d29f5c006be34b612ca27310d240218e9fe Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 12 Sep 2021 19:36:23 +0300 Subject: [PATCH 0696/1808] Update custom filters readme and examples --- README.md | 10 ++-- .../general_custom_filters.py} | 5 +- .../{ => custom_filters}/id_filter_example.py | 5 +- .../text_filter_example.py | 4 +- telebot/__init__.py | 6 +- telebot/custom_filters.py | 59 +++++++++++++------ telebot/util.py | 32 ---------- 7 files changed, 57 insertions(+), 64 deletions(-) rename examples/{custom_filters.py => custom_filters/general_custom_filters.py} (91%) rename examples/{ => custom_filters}/id_filter_example.py (86%) rename examples/{ => custom_filters}/text_filter_example.py (82%) diff --git a/README.md b/README.md index 849efadad..919ff7444 100644 --- a/README.md +++ b/README.md @@ -289,25 +289,25 @@ def start(message): There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory. -### Custom filters +#### Custom filters Also, you can use built-in custom filters. Or, you can create your own filter. -[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters.py) +[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/general_custom_filters.py) Also, we have examples on them. Check this links: You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py) -Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/id_filter_example.py) +Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/id_filter_example.py) -Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/text_filter_example.py) +Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/text_filter_example.py) If you want to add some built-in filter, you are welcome to add it in custom_filters.py file. Here is example of creating filter-class: ```python -class IsAdmin(util.SimpleCustomFilter): +class IsAdmin(telebot.custom_filters.SimpleCustomFilter): # Class will check whether the user is admin or creator in group or not key='is_admin' @staticmethod diff --git a/examples/custom_filters.py b/examples/custom_filters/general_custom_filters.py similarity index 91% rename from examples/custom_filters.py rename to examples/custom_filters/general_custom_filters.py index 18c8c2633..382b68c6b 100644 --- a/examples/custom_filters.py +++ b/examples/custom_filters/general_custom_filters.py @@ -1,18 +1,17 @@ import telebot -from telebot import util bot = telebot.TeleBot('TOKEN') # AdvancedCustomFilter is for list, string filter values -class MainFilter(util.AdvancedCustomFilter): +class MainFilter(telebot.custom_filters.AdvancedCustomFilter): key='text' @staticmethod def check(message, text): return message.text in text # SimpleCustomFilter is for boolean values, such as is_admin=True -class IsAdmin(util.SimpleCustomFilter): +class IsAdmin(telebot.custom_filters.SimpleCustomFilter): key='is_admin' @staticmethod def check(message: telebot.types.Message): diff --git a/examples/id_filter_example.py b/examples/custom_filters/id_filter_example.py similarity index 86% rename from examples/id_filter_example.py rename to examples/custom_filters/id_filter_example.py index 3c8384174..06a6ce3ca 100644 --- a/examples/id_filter_example.py +++ b/examples/custom_filters/id_filter_example.py @@ -15,7 +15,8 @@ def not_admin(message): # Do not forget to register -bot.add_custom_filter(custom_filters.UserFilter()) +bot.add_custom_filter(custom_filters.ChatFilter()) -bot.polling(non_stop=True) \ No newline at end of file +bot.polling(non_stop=True) + diff --git a/examples/text_filter_example.py b/examples/custom_filters/text_filter_example.py similarity index 82% rename from examples/text_filter_example.py rename to examples/custom_filters/text_filter_example.py index 12b62f1e8..247609708 100644 --- a/examples/text_filter_example.py +++ b/examples/custom_filters/text_filter_example.py @@ -15,7 +15,7 @@ def text_filter(message): bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name)) # Do not forget to register filters -bot.add_custom_filter(custom_filters.TextFilter()) -bot.add_custom_filter(custom_filters.TextStarts()) +bot.add_custom_filter(custom_filters.TextMatchFilter()) +bot.add_custom_filter(custom_filters.TextStartsFilter()) bot.polling(non_stop=True) \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index e37943574..790fff0cb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -12,7 +12,7 @@ # this imports are used to avoid circular import error import telebot.util import telebot.types - +from custom_filters import SimpleCustomFilter, AdvancedCustomFilter logger = logging.getLogger('TeleBot') @@ -3140,9 +3140,9 @@ def _check_filter(self, message_filter, filter_value, message): filter_check = self.custom_filters.get(message_filter) if not filter_check: return False - elif isinstance(filter_check, util.SimpleCustomFilter): + elif isinstance(filter_check, SimpleCustomFilter): return filter_value == filter_check.check(message) - elif isinstance(filter_check, util.AdvancedCustomFilter): + elif isinstance(filter_check, AdvancedCustomFilter): return filter_check.check(message, filter_value) else: logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 00b0f7561..2491185cf 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -1,8 +1,36 @@ -from telebot import util +from abc import ABC +class SimpleCustomFilter(ABC): + """ + Simple Custom Filter base class. + Create child class with check() method. + Accepts only message, returns bool value, that is compared with given in handler. + """ + def check(self, message): + """ + Perform a check. + """ + pass -class TextFilter(util.AdvancedCustomFilter): + +class AdvancedCustomFilter(ABC): + """ + Simple Custom Filter base class. + Create child class with check() method. + Accepts two parameters, returns bool: True - filter passed, False - filter failed. + message: Message class + text: Filter value given in handler + """ + + def check(self, message, text): + """ + Perform a check. + """ + pass + + +class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. key: text @@ -17,7 +45,7 @@ def check(self, message, text): if type(text) is list:return message.text in text else: return text == message.text -class TextContains(util.AdvancedCustomFilter): +class TextContainsFilter(AdvancedCustomFilter): """ Filter to check Text message. key: text @@ -32,30 +60,27 @@ class TextContains(util.AdvancedCustomFilter): def check(self, message, text): return text in message.text -class UserFilter(util.AdvancedCustomFilter): +class TextStartsFilter(AdvancedCustomFilter): """ - Check whether chat_id corresponds to given chat_id. + Filter to check whether message starts with some text. Example: - @bot.message_handler(chat_id=[99999]) - + # Will work if message.text starts with 'Sir'. + @bot.message_handler(text_startswith='Sir') """ - key = 'chat_id' + key = 'text_startswith' def check(self, message, text): - return message.chat.id in text - + return message.text.startswith(text) -class TextStarts(util.AdvancedCustomFilter): +class ChatFilter(AdvancedCustomFilter): """ - Filter to check whether message starts with some text. + Check whether chat_id corresponds to given chat_id. Example: - # Will work if message.text starts with 'Sir'. - @bot.message_handler(text_startswith='Sir') - + @bot.message_handler(chat_id=[99999]) """ - key = 'text_startswith' + key = 'chat_id' def check(self, message, text): - return message.text.startswith(text) + return message.chat.id in text diff --git a/telebot/util.py b/telebot/util.py index 7bcb257fb..f871f09e7 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -5,7 +5,6 @@ import threading import traceback from typing import Any, Callable, List, Dict, Optional, Union -from abc import ABC # noinspection PyPep8Naming import queue as Queue @@ -456,34 +455,3 @@ def webhook_google_functions(bot, request): return 'Bot FAIL', 400 else: return 'Bot ON' - - -class SimpleCustomFilter(ABC): - """ - Simple Custom Filter base class. - Create child class with check() method. - Accepts only bool. - """ - - def check(self, message): - """ - Perform a check. - """ - pass - -class AdvancedCustomFilter(ABC): - """ - Simple Custom Filter base class. - Create child class with check() method. - Can accept to parameters. - message: Message class - text: Filter value given in handler - """ - - def check(self, message, text): - """ - Perform a check. - """ - pass - - \ No newline at end of file From 97e99b491030cde3f1e3a0ddb4cd31e38ec1bf7f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 12 Sep 2021 19:39:26 +0300 Subject: [PATCH 0697/1808] Fix --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 790fff0cb..78cf29d07 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -12,7 +12,7 @@ # this imports are used to avoid circular import error import telebot.util import telebot.types -from custom_filters import SimpleCustomFilter, AdvancedCustomFilter +from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter logger = logging.getLogger('TeleBot') From 38851bce220cc4af32bb0b08633712d1fe6d637d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 12 Sep 2021 20:02:49 +0300 Subject: [PATCH 0698/1808] README contents update --- README.md | 125 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 66 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 919ff7444..ad8c43474 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,27 @@ * [Methods](#methods) * [General use of the API](#general-use-of-the-api) * [Message handlers](#message-handlers) + * [Edited Message handler](#edited-message-handler) + * [Channel Post handler](#channel-post-handler) + * [Edited Channel Post handler](#edited-channel-post-handler) * [Callback Query handlers](#callback-query-handler) - * [Middleware handlers](#middleware-handler) + * [Shipping Query Handler](#shipping-query-handler) + * [Pre Checkout Query Handler](#pre-checkout-query-handler) + * [Poll Handler](#poll-handler) + * [Poll Answer Handler](#poll-answer-handler) + * [My Chat Member Handler](#my-chat-member-handler) + * [Chat Member Handler](#chat-member-handler) + * [Inline Mode](#inline-mode) + * [Inline handler](#inline-handler) + * [Chosen Inline handler](#chosen-inline-handler) + * [Answer Inline Query](#answer-inline-query) + * [Additional API features](#additional-api-features) + * [Middleware handlers](#middleware-handlers) + * [Custom filters](#custom-filters) * [TeleBot](#telebot) * [Reply markup](#reply-markup) - * [Inline Mode](#inline-mode) * [Advanced use of the API](#advanced-use-of-the-api) + * [Using local Bot API Server](#using-local-bot-api-sever) * [Asynchronous delivery of messages](#asynchronous-delivery-of-messages) * [Sending large text messages](#sending-large-text-messages) * [Controlling the amount of Threads used by TeleBot](#controlling-the-amount-of-threads-used-by-telebot) @@ -35,9 +50,7 @@ * [Logging](#logging) * [Proxy](#proxy) * [API conformance](#api-conformance) - * [Change log](#change-log) * [F.A.Q.](#faq) - * [Bot 2.0](#bot-20) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) * [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors) * [The Telegram Chat Group](#the-telegram-chat-group) @@ -46,7 +59,7 @@ ## Getting started. -This API is tested with Python Python 3.6-3.9 and Pypy 3. +This API is tested with Python 3.6-3.9 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager)*: @@ -213,15 +226,15 @@ def send_something(message): ``` **Important: all handlers are tested in the order in which they were declared** -#### Edited Message handlers +#### Edited Message handler Handle edited messages `@bot.edited_message_handler(filters) # <- passes a Message type object to your function` -#### channel_post_handler +#### Channel Post handler Handle channel post messages `@bot.channel_post_handler(filters) # <- passes a Message type object to your function` -#### edited_channel_post_handler +#### Edited Channel Post handler Handle edited channel post messages `@bot.edited_channel_post_handler(filters) # <- passes a Message type object to your function` @@ -233,14 +246,6 @@ def test_callback(call): # <- passes a CallbackQuery type object to your functi logger.info(call) ``` -#### Inline Handler -Handle inline queries -`@bot.inline_handler() # <- passes a InlineQuery type object to your function` - -#### Chosen Inline Handler -Handle chosen inline results -`@bot.chosen_inline_handler() # <- passes a ChosenInlineResult type object to your function` - #### Shipping Query Handler Handle shipping queries `@bot.shipping_query_handeler() # <- passes a ShippingQuery type object to your function` @@ -266,8 +271,51 @@ Handle updates of a chat member's status in a chat `@bot.chat_member_handler() # <- passes a ChatMemberUpdated type object to your function` *Note: "chat_member" updates are not requested by default. If you want to allow all update types, set `allowed_updates` in `bot.polling()` / `bot.infinity_polling()` to `util.update_types`* +### Inline Mode + +More information about [Inline mode](https://core.telegram.org/bots/inline). + +#### Inline handler + +Now, you can use inline_handler to get inline queries in telebot. + +```python + +@bot.inline_handler(lambda query: query.query == 'text') +def query_text(inline_query): + # Query message is text +``` + +#### Chosen Inline handler + +Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback +command for @Botfather. + +More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback) + +```python +@bot.chosen_inline_handler(func=lambda chosen_inline_result: True) +def test_chosen(chosen_inline_result): + # Process all chosen_inline_result. +``` + +#### Answer Inline Query + +```python +@bot.inline_handler(lambda query: query.query == 'text') +def query_text(inline_query): + try: + r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.')) + r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.')) + bot.answer_inline_query(inline_query.id, [r, r2]) + except Exception as e: + print(e) + +``` -#### Middleware Handler +### Additional API features + +#### Middleware Handlers A middleware handler is a function that allows you to modify requests or the bot context as they pass through the Telegram to the bot. You can imagine middleware as a chain of logic connection handled before any other handlers are @@ -471,49 +519,8 @@ ForceReply: ![ForceReply](https://farm4.staticflickr.com/3809/32418726814_d1baec0fc2_o_d.jpg "ForceReply") -### Inline Mode - -More information about [Inline mode](https://core.telegram.org/bots/inline). - -#### inline_handler - -Now, you can use inline_handler to get inline queries in telebot. - -```python - -@bot.inline_handler(lambda query: query.query == 'text') -def query_text(inline_query): - # Query message is text -``` - -#### chosen_inline_handler - -Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback -command for @Botfather. - -More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback) - -```python -@bot.chosen_inline_handler(func=lambda chosen_inline_result: True) -def test_chosen(chosen_inline_result): - # Process all chosen_inline_result. -``` - -#### answer_inline_query - -```python -@bot.inline_handler(lambda query: query.query == 'text') -def query_text(inline_query): - try: - r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.')) - r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.')) - bot.answer_inline_query(inline_query.id, [r, r2]) - except Exception as e: - print(e) - -``` -### Working with entities: +### Working with entities This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. Attributes: * `type` From 7913e25be21130ca9db01875b73647702c1a2260 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 12 Sep 2021 21:12:19 +0300 Subject: [PATCH 0699/1808] 4.0.1 beta release --- README.md | 43 +++++++------------------------------------ telebot/version.py | 2 +- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index ad8c43474..257b744ce 100644 --- a/README.md +++ b/README.md @@ -722,72 +722,43 @@ Get help. Discuss. Chat. ## Bots using this API * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* -* [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files. -* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you. -* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander. -* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`. +* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. +* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* * [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot. * [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* * [NeoBot](https://github.com/neoranger/NeoBot) by [@NeoRanger](https://github.com/neoranger) -* [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi* * [ColorCodeBot](https://t.me/colorcodebot) ([source](https://github.com/andydecleyre/colorcodebot)) - Share code snippets as beautifully syntax-highlighted HTML and/or images. * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. -* [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin. -* [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025 * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash -* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch +* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot) - DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch * [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev. -* [dailypepebot](https://telegram.me/dailypepebot) by [*Jaime*](https://github.com/jiwidi/Dailypepe) - Get's you random pepe images and gives you their id, then you can call this image with the number. -* [DailyQwertee](https://t.me/DailyQwertee) by [*Jaime*](https://github.com/jiwidi/DailyQwertee) - Bot that manages a channel that sends qwertee daily tshirts every day at 00:00 * [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram -* [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz) -* [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful. * [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic -* [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video. * [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service. * [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service. -* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram)) * [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students. * [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students. -* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free. * [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary. -* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audio samples and try to name the performer of the song. * [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon) -* [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm). -* [ManjaroBot](https://t.me/ManjaroBot) by [@NeoRanger](https://github.com/neoranger) - Bot for Manjaro Linux Spanish group with a lot of info for Manjaro Newbies. * [VigoBusTelegramBot](https://t.me/vigobusbot) ([GitHub](https://github.com/Pythoneiro/VigoBus-TelegramBot)) - Bot that provides buses coming to a certain stop and their remaining time for the city of Vigo (Galicia - Spain) * [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students. -* [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given ID’s -* [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe. -* [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian). * [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers. * [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov. * [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram. * [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...) * [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering * [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates. -* [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network. * [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications. -* [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read. -* [Blue_CC_Bot](https://t.me/Blue_CC_Bot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Checks Your Given Credit Cards And Says Which Is A Real,Card And Which Is Fake. -* [RandomInfoBot](https://t.me/RandomInfoBot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Generates Random Information Of Humans Scraped From Over 13 Websites. * [TasksListsBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/TasksListsBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - A (tasks) lists manager bot for Telegram. * [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot. -* [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want. * [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast. -* [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour) -* [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani) -* [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user. -* [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources. * [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server. * [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls. * [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon) -* [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down. -* [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to. -* Translator bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/translate_text_bot). This bot can be use to translate texts. -* Digital Cryptocurrency bot by [Areeg Fahad (source)](https://github.com/AREEG94FAHAD/currencies_bot). With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. -* [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by [Leon Heess (source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. +* [Translator bot](https://github.com/AREEG94FAHAD/translate_text_bot) by Areeg Fahad. This bot can be used to translate texts. +* [Digital Cryptocurrency bot](https://github.com/AREEG94FAHAD/currencies_bot) by Areeg Fahad. With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency. +* [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by Leon Heess [(source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. * [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems) * [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings) -**Want to have your bot listed here? Just make a pull request.** +**Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** diff --git a/telebot/version.py b/telebot/version.py index 8676c8dd7..9001d305c 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.0.0' +__version__ = '4.0.1' From b95ab104e38775c9c214d945cc0ca01cb3528a55 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 13 Sep 2021 23:09:06 +0500 Subject: [PATCH 0700/1808] Update custom_filters.py --- telebot/custom_filters.py | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 2491185cf..dd1f1a2c1 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -84,3 +84,49 @@ class ChatFilter(AdvancedCustomFilter): key = 'chat_id' def check(self, message, text): return message.chat.id in text + +class ForwardFilter(SimpleCustomFilter): + """ + Check whether message was forwarded. + + Example: + + @bot.message_handler(is_forwarded=True) + """ + + key = 'is_forwarded' + + def check(self, message): + return message.forward_from_chat is not None + +class IsReplyFilter(SimpleCustomFilter): + """ + Check whether message is a reply. + + Example: + + @bot.message_handler(is_reply=True) + """ + + key = 'is_reply' + + def check(self, message): + return message.reply_to_message is not None + + + +class LanguageFilter(AdvancedCustomFilter): + """ + Check users language_code. + + Example: + + @bot.message_handler(language_code=['ru']) + """ + + key = 'language_code' + + def check(self, message, text): + if type(text) is list:return message.from_user.language_code in text + else: return message.from_user.language_code == text + From 86a0a8cd6850f6f35dfe147ae54ae33e27a11702 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 14 Sep 2021 15:00:27 +0500 Subject: [PATCH 0701/1808] Little fixes and example Fixed is_forwarded custom filter & created example --- examples/custom_filters/is_filter_example.py | 21 ++++++++++++++++++++ telebot/custom_filters.py | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 examples/custom_filters/is_filter_example.py diff --git a/examples/custom_filters/is_filter_example.py b/examples/custom_filters/is_filter_example.py new file mode 100644 index 000000000..46d769ca1 --- /dev/null +++ b/examples/custom_filters/is_filter_example.py @@ -0,0 +1,21 @@ +import telebot +from telebot import custom_filters + +bot = telebot.TeleBot('TOKEN') + + +# Check if message is a reply +@bot.message_handler(is_reply=True) +def start_filter(message): + bot.send_message(message.chat.id, "Looks like you replied to my message.") + +# Check if message was forwarded +@bot.message_handler(is_forwarded=True) +def text_filter(message): + bot.send_message(message.chat.id, "I do not accept forwarded messages!") + +# Do not forget to register filters +bot.add_custom_filter(custom_filters.IsReplyFilter()) +bot.add_custom_filter(custom_filters.ForwardFilter()) + +bot.polling(non_stop=True) \ No newline at end of file diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index dd1f1a2c1..9a3c233c9 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -97,7 +97,7 @@ class ForwardFilter(SimpleCustomFilter): key = 'is_forwarded' def check(self, message): - return message.forward_from_chat is not None + return message.forward_from is not None class IsReplyFilter(SimpleCustomFilter): """ From fc31a2d466e5d4e252fb9668f2929e7f3a43c98c Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 14 Sep 2021 15:02:54 +0500 Subject: [PATCH 0702/1808] Update custom_filters.py --- telebot/custom_filters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 9a3c233c9..7fc18662d 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -87,7 +87,7 @@ def check(self, message, text): class ForwardFilter(SimpleCustomFilter): """ - Check whether message was forwarded. + Check whether message was forwarded from channel or group. Example: @@ -97,7 +97,7 @@ class ForwardFilter(SimpleCustomFilter): key = 'is_forwarded' def check(self, message): - return message.forward_from is not None + return message.forward_from_chat is not None class IsReplyFilter(SimpleCustomFilter): """ From c5c4d081ea42c2330a4f86eb910cbc54a2dfd0c4 Mon Sep 17 00:00:00 2001 From: bim-ba Date: Sat, 18 Sep 2021 19:46:53 +0300 Subject: [PATCH 0703/1808] Added new example: anonymous chat-bot --- examples/anonymous_bot.py | 150 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 examples/anonymous_bot.py diff --git a/examples/anonymous_bot.py b/examples/anonymous_bot.py new file mode 100644 index 000000000..f83bd42a8 --- /dev/null +++ b/examples/anonymous_bot.py @@ -0,0 +1,150 @@ +# This bot is needed to connect two people and their subsequent anonymous communication +# +# Avaiable commands: +# `/start` - Just send you a messsage how to start +# `/find` - Find a person you can contact +# `/stop` - Stop active conversation + +import telebot +from telebot import types + +# Initialize bot with your token +bot = telebot.TeleBot(TOKEN) + +# The `users` variable is needed to contain chat ids that are either in the search or in the active dialog, like {chat_id, chat_id} +users = {} +# The `freeid` variable is needed to contain chat id, that want to start conversation +# Or, in other words: chat id of user in the search +freeid = None + +# `/start` command handler +# +# That command only sends you 'Just use /find command!' +# +@bot.message_handler(commands=['start']) +def start(message: types.Message): + bot.send_message(message.chat.id, 'Just use /find command!') + +# `/find` command handler +# +# That command finds opponent for you +# +# That command according to the following principle: +# 1. You have written `/find` command +# 2. If you are already in the search or have an active dialog, bot sends you 'Shut up!' +# 3. If not: +# 3.1. Bot sends you 'Finding...' +# 3.2. If there is no user in the search: +# 3.2.2. `freeid` updated with `your_chat_id` +# 3.3. If there is user in the search: +# 3.3.1. Both you and the user in the search recieve the message 'Founded!' +# 3.3.2. `users` updated with a {user_in_the_search_chat_id, your_chat_id} +# 3.3.3. `users` updated with a {your_chat_id, user_in_the_search_id} +# 3.3.4. `freeid` updated with `None` +# +@bot.message_handler(commands=['find']) +def find(message: types.Message): + global freeid + + if message.chat.id not in users: + bot.send_message(message.chat.id, 'Finding...') + + if freeid == None: + freeid = message.chat.id + else: + # Question: + # Is there any way to simplify this like `bot.send_message([message.chat.id, freeid], 'Founded!')`? + bot.send_message(message.chat.id, 'Founded!') + bot.send_message(freeid, 'Founded!') + + users[freeid] = message.chat.id + users[message.chat.id] = freeid + freeid = None + + print(users, freeid) # Debug purpose, you can remove that line + else: + bot.send_message(message.chat.id, 'Shut up!') + +# `/stop` command handler +# +# That command stops your current conversation (if it exist) +# +# That command according to the following principle: +# 1. You have written `/stop` command +# 2. If you are not have active dialog or you are not in search, bot sends you 'You are not in search!' +# 3. If you are in active dialog: +# 3.1. Bot sends you 'Stopping...' +# 3.2. Bot sends 'Your opponent is leavin`...' to your opponent +# 3.3. {your_opponent_chat_id, your_chat_id} removes from `users` +# 3.4. {your_chat_id, your_opponent_chat_id} removes from `users` +# 4. If you are only in search: +# 4.1. Bot sends you 'Stopping...' +# 4.2. `freeid` updated with `None` +# +@bot.message_handler(commands=['stop']) +def stop(message: types.Message): + global freeid + + if message.chat.id in users: + bot.send_message(message.chat.id, 'Stopping...') + bot.send_message(users[message.chat.id], 'Your opponent is leavin`...') + + del users[users[message.chat.id]] + del users[message.chat.id] + + print(users, freeid) # Debug purpose, you can remove that line + elif message.chat.id == freeid: + bot.send_message(message.chat.id, 'Stopping...') + freeid = None + + print(users, freeid) # Debug purpose, you can remove that line + else: + bot.send_message(message.chat.id, 'You are not in search!') + +# message handler for conversation +# +# That handler needed to send message from one opponent to another +# If you are not in `users`, you will recieve a message 'No one can hear you...' +# Otherwise all your messages are sent to your opponent +# +# Questions: +# Is there any way to improve readability like `content_types=['all']`? +# Is there any way to improve "elif-zone"? Like: +# `bot.send_message(users[message.chat.id], message.data)` +# +@bot.message_handler(content_types=['animation', 'audio', 'contact', 'dice', 'document', 'location', 'photo', 'sticker', 'text', 'venue', 'video', 'video_note', 'voice']) +def chatting(message: types.Message): + if message.chat.id in users and users[message.chat.id] != None: + + if message.content_type == 'animation': + bot.send_animation(users[message.chat.id], message.animation.file_id) + elif message.content_type == 'audio': + bot.send_audio(users[message.chat.id], message.audio.file_id) + elif message.content_type == 'contact': + bot.send_contact(users[message.chat.id], message.contact.phone_number, message.contact.first_name) + elif message.content_type == 'dice': + bot.send_dice(users[message.chat.id], message.dice.emoji) + elif message.content_type == 'document': + bot.send_document(users[message.chat.id], message.document.file_id) + elif message.content_type == 'location': + bot.send_location(users[message.chat.id], message.location.latitude, message.location.longitude) + elif message.content_type == 'photo': + bot.send_photo(users[message.chat.id], message.photo) + elif message.content_type == 'sticker': + bot.send_sticker(users[message.chat.id], message.sticker.file_id) + elif message.content_type == 'text': + bot.send_message(users[message.chat.id], message.text) + elif message.content_type == 'venue': + bot.send_venue(users[message.chat.id], message.venue.location.latitude, message.venue.location.longitude, message.venue.title, message.venue.address) + elif message.content_type == 'video': + bot.send_video(users[message.chat.id], message.video.file_id) + elif message.content_type == 'video_note': + bot.send_video_note(users[message.chat.id], message.video_note.file_id) + elif message.content_type == 'voice': + bot.send_voice(users[message.chat.id], message.voice.file_id) + + else: + bot.send_message(message.chat.id, 'No one can hear you...') + +# Start retrieving updates +bot.polling() \ No newline at end of file From aba2a9e1792fc07addd3b1133c5ee4fffd1b93c1 Mon Sep 17 00:00:00 2001 From: bim-ba <81964109+bim-ba@users.noreply.github.com> Date: Sun, 19 Sep 2021 17:41:07 +0300 Subject: [PATCH 0704/1808] Improve readabilty of "elif-zone" --- examples/anonymous_bot.py | 52 +++++++++++---------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/examples/anonymous_bot.py b/examples/anonymous_bot.py index f83bd42a8..da0c00e7d 100644 --- a/examples/anonymous_bot.py +++ b/examples/anonymous_bot.py @@ -20,7 +20,6 @@ # `/start` command handler # # That command only sends you 'Just use /find command!' -# @bot.message_handler(commands=['start']) def start(message: types.Message): bot.send_message(message.chat.id, 'Just use /find command!') @@ -41,7 +40,6 @@ def start(message: types.Message): # 3.3.2. `users` updated with a {user_in_the_search_chat_id, your_chat_id} # 3.3.3. `users` updated with a {your_chat_id, user_in_the_search_id} # 3.3.4. `freeid` updated with `None` -# @bot.message_handler(commands=['find']) def find(message: types.Message): global freeid @@ -80,7 +78,6 @@ def find(message: types.Message): # 4. If you are only in search: # 4.1. Bot sends you 'Stopping...' # 4.2. `freeid` updated with `None` -# @bot.message_handler(commands=['stop']) def stop(message: types.Message): global freeid @@ -108,43 +105,22 @@ def stop(message: types.Message): # Otherwise all your messages are sent to your opponent # # Questions: -# Is there any way to improve readability like `content_types=['all']`? -# Is there any way to improve "elif-zone"? Like: -# `bot.send_message(users[message.chat.id], message.data)` -# -@bot.message_handler(content_types=['animation', 'audio', 'contact', 'dice', 'document', 'location', 'photo', 'sticker', 'text', 'venue', 'video', 'video_note', 'voice']) +# 1. Is there any way to improve readability like `content_types=['all']`? +# 2. Is there any way to register this message handler only when i found the opponent? +@bot.message_handler(content_types=['animation', 'audio', 'contact', 'dice', 'document', 'location', 'photo', 'poll', 'sticker', 'text', 'venue', 'video', 'video_note', 'voice']) def chatting(message: types.Message): - if message.chat.id in users and users[message.chat.id] != None: - - if message.content_type == 'animation': - bot.send_animation(users[message.chat.id], message.animation.file_id) - elif message.content_type == 'audio': - bot.send_audio(users[message.chat.id], message.audio.file_id) - elif message.content_type == 'contact': - bot.send_contact(users[message.chat.id], message.contact.phone_number, message.contact.first_name) - elif message.content_type == 'dice': - bot.send_dice(users[message.chat.id], message.dice.emoji) - elif message.content_type == 'document': - bot.send_document(users[message.chat.id], message.document.file_id) - elif message.content_type == 'location': - bot.send_location(users[message.chat.id], message.location.latitude, message.location.longitude) - elif message.content_type == 'photo': - bot.send_photo(users[message.chat.id], message.photo) - elif message.content_type == 'sticker': - bot.send_sticker(users[message.chat.id], message.sticker.file_id) - elif message.content_type == 'text': - bot.send_message(users[message.chat.id], message.text) - elif message.content_type == 'venue': - bot.send_venue(users[message.chat.id], message.venue.location.latitude, message.venue.location.longitude, message.venue.title, message.venue.address) - elif message.content_type == 'video': - bot.send_video(users[message.chat.id], message.video.file_id) - elif message.content_type == 'video_note': - bot.send_video_note(users[message.chat.id], message.video_note.file_id) - elif message.content_type == 'voice': - bot.send_voice(users[message.chat.id], message.voice.file_id) - + if message.chat.id in users: + bot.copy_message(users[message.chat.id], users[users[message.chat.id]], message.id) else: bot.send_message(message.chat.id, 'No one can hear you...') # Start retrieving updates -bot.polling() \ No newline at end of file +# Questions: +# 1. Is there any way not to process messages sent earlier? +# +# For example: +# If the bot is turned off, and i tried to type `/find` nothing will happen, but... +# When i start the bot, `/find` command will processed, and i will be added to search +# +# I tried `skip_pending=True`, but thats was not helpful +bot.polling() From 38cc96d0f3de26ac341365e76a63f30ddd468c0f Mon Sep 17 00:00:00 2001 From: SwissCorePy <51398037+SwissCorePy@users.noreply.github.com> Date: Mon, 20 Sep 2021 14:31:00 +0200 Subject: [PATCH 0705/1808] added property `user` to TeleBot class Added property `user` to TeleBot class. The idea is to have easy access to the user object representing the bot without doing an API call every time. --- telebot/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 49a24ca75..cdcbec29c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -207,6 +207,16 @@ def __init__( self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(num_threads=num_threads) + + @property + def user(self) -> types.User: + """ + The User object representing this bot. + Equivalent to bot.get_me() but the result is cached so only one API call is needed + """ + if not hasattr(self, "_user"): + self._user = types.User.de_json(self.get_me()) + return self._user def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): """ From 9c86ed623d8233bb1453061771dc538b1b9391b0 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 22 Sep 2021 22:37:18 +0500 Subject: [PATCH 0706/1808] Update custom_filters.py --- telebot/custom_filters.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 7fc18662d..2f3b9b18c 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -130,3 +130,19 @@ def check(self, message, text): if type(text) is list:return message.from_user.language_code in text else: return message.from_user.language_code == text +class IsAdminFilter(SimpleCustomFilter): + """ + Check whether the user is administrator / owner of the chat. + + Example: + @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) + """ + + key = 'is_chat_admin' + + def __init__(self, bot): + self._bot = bot + + def check(self, message): + return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] + From cd92d95f9126a61ec9a67111ad1a8af8ca331074 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 22 Sep 2021 22:37:57 +0500 Subject: [PATCH 0707/1808] Create admin_filter_example.py --- examples/custom_filters/admin_filter_example.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 examples/custom_filters/admin_filter_example.py diff --git a/examples/custom_filters/admin_filter_example.py b/examples/custom_filters/admin_filter_example.py new file mode 100644 index 000000000..98993e139 --- /dev/null +++ b/examples/custom_filters/admin_filter_example.py @@ -0,0 +1,12 @@ +import telebot +from telebot import custom_filters +bot = telebot.TeleBot('TOKEN') + +# Handler +@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) +def answer_for_admin(message): + bot.send_message(message.chat.id,"hello my admin") + +# Register filter +bot.add_custom_filter(custom_filters.IsAdminFilter(bot)) +bot.polling() \ No newline at end of file From 716323e56adb08c7a6acf5bc66b78fea1218a41b Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 22 Sep 2021 22:46:19 +0500 Subject: [PATCH 0708/1808] Register_XXX_Handler --- examples/register_handler/config.py | 5 +++++ examples/register_handler/handlers.py | 9 +++++++++ examples/register_handler/main.py | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 examples/register_handler/config.py create mode 100644 examples/register_handler/handlers.py create mode 100644 examples/register_handler/main.py diff --git a/examples/register_handler/config.py b/examples/register_handler/config.py new file mode 100644 index 000000000..50f8a6288 --- /dev/null +++ b/examples/register_handler/config.py @@ -0,0 +1,5 @@ +import telebot + +api_token = '' + +bot = telebot.TeleBot(api_token) \ No newline at end of file diff --git a/examples/register_handler/handlers.py b/examples/register_handler/handlers.py new file mode 100644 index 000000000..5d65b35fd --- /dev/null +++ b/examples/register_handler/handlers.py @@ -0,0 +1,9 @@ +# All handlers can be written in this file +from config import bot + +def start_executor(message): + bot.send_message(message.chat.id, 'Hello!') + +# Write more handlers here if you wish. You don't need a decorator + +# Just create function and register in main file. \ No newline at end of file diff --git a/examples/register_handler/main.py b/examples/register_handler/main.py new file mode 100644 index 000000000..1d670e9e0 --- /dev/null +++ b/examples/register_handler/main.py @@ -0,0 +1,19 @@ +import telebot +from telebot import custom_filters +import config +bot = telebot.TeleBot(config.api_token) + +import handlers + +bot.register_message_handler(handlers.start_executor, commands=['start']) # Start command executor + +# See also +# bot.register_callback_query_handler(*args, **kwargs) +# bot.register_channel_post_handler(*args, **kwargs) +# bot.register_chat_member_handler(*args, **kwargs) +# bot.register_inline_handler(*args, **kwargs) +# bot.register_my_chat_member_handler(*args, **kwargs) +# bot.register_edited_message_handler(*args, **kwargs) +# And other functions.. + +bot.polling() \ No newline at end of file From 92ac5a41666e80bd273bc5ba3f96d3febc1b8d72 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 17:12:32 +0500 Subject: [PATCH 0709/1808] States, and some minor improvements --- examples/custom_states.py | 34 +++++++ .../main.py => register_handler.py} | 12 +-- examples/register_handler/config.py | 5 -- examples/register_handler/handlers.py | 9 -- telebot/__init__.py | 89 ++++++++++++++++++- telebot/handler_backends.py | 76 ++++++++++++++++ 6 files changed, 205 insertions(+), 20 deletions(-) create mode 100644 examples/custom_states.py rename examples/{register_handler/main.py => register_handler.py} (59%) delete mode 100644 examples/register_handler/config.py delete mode 100644 examples/register_handler/handlers.py diff --git a/examples/custom_states.py b/examples/custom_states.py new file mode 100644 index 000000000..9ad9b1cfc --- /dev/null +++ b/examples/custom_states.py @@ -0,0 +1,34 @@ +import telebot + +from telebot.handler_backends import State + +bot = telebot.TeleBot("") + +@bot.message_handler(commands=['start']) +def start_ex(message): + bot.set_state(message.chat.id, 1) + bot.send_message(message.chat.id, 'Hi, write me a name') + + +@bot.state_handler(state=1) +def name_get(message, state:State): + bot.send_message(message.chat.id, f'Now write me a surname') + state.set(message.chat.id, 2) + with state.retrieve_data(message.chat.id) as data: + data['name'] = message.text + + +@bot.state_handler(state=2) +def ask_age(message, state:State): + bot.send_message(message.chat.id, "What is your age?") + state.set(message.chat.id, 3) + with state.retrieve_data(message.chat.id) as data: + data['surname'] = message.text + +@bot.state_handler(state=3) +def ready_for_answer(message, state: State): + with state.retrieve_data(message.chat.id) as data: + bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") + state.finish(message.chat.id) + +bot.polling() \ No newline at end of file diff --git a/examples/register_handler/main.py b/examples/register_handler.py similarity index 59% rename from examples/register_handler/main.py rename to examples/register_handler.py index 1d670e9e0..f733a3b89 100644 --- a/examples/register_handler/main.py +++ b/examples/register_handler.py @@ -1,11 +1,13 @@ import telebot -from telebot import custom_filters -import config -bot = telebot.TeleBot(config.api_token) -import handlers +api_token = '1297441208:AAH5THRzLXvY5breGFzkrEOIj7zwCGzbQ-Y' -bot.register_message_handler(handlers.start_executor, commands=['start']) # Start command executor +bot = telebot.TeleBot(api_token) + +def start_executor(message): + bot.send_message(message.chat.id, 'Hello!') + +bot.register_message_handler(start_executor, commands=['start']) # Start command executor # See also # bot.register_callback_query_handler(*args, **kwargs) diff --git a/examples/register_handler/config.py b/examples/register_handler/config.py deleted file mode 100644 index 50f8a6288..000000000 --- a/examples/register_handler/config.py +++ /dev/null @@ -1,5 +0,0 @@ -import telebot - -api_token = '' - -bot = telebot.TeleBot(api_token) \ No newline at end of file diff --git a/examples/register_handler/handlers.py b/examples/register_handler/handlers.py deleted file mode 100644 index 5d65b35fd..000000000 --- a/examples/register_handler/handlers.py +++ /dev/null @@ -1,9 +0,0 @@ -# All handlers can be written in this file -from config import bot - -def start_executor(message): - bot.send_message(message.chat.id, 'Hello!') - -# Write more handlers here if you wish. You don't need a decorator - -# Just create function and register in main file. \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index 32f7b1d64..fe0d25613 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -27,7 +27,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMachine, State REPLY_MARKUP_TYPES = Union[ @@ -186,6 +186,9 @@ def __init__( self.my_chat_member_handlers = [] self.chat_member_handlers = [] self.custom_filters = {} + self.state_handlers = [] + + self.current_states = StateMachine() if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { @@ -495,6 +498,7 @@ def process_new_updates(self, updates): def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) + self._notify_state_handlers(new_messages) self._notify_reply_handlers(new_messages) self.__notify_update(new_messages) self._notify_command_handlers(self.message_handlers, new_messages) @@ -2362,6 +2366,31 @@ def register_next_step_handler( chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) + def set_state(self, chat_id, state): + """ + Sets a new state of a user. + :param chat_id: + :param state: new state. can be string or integer. + """ + self.current_states.add_state(chat_id, state) + + def delete_state(self, chat_id): + """ + Delete the current state of a user. + :param chat_id: + :return: + """ + self.current_states.delete_state(chat_id) + + def get_state(self, chat_id): + """ + Get current state of a user. + :param chat_id: + :return: state of a user + """ + return self.current_states.current_state(chat_id) + + def register_next_step_handler_by_chat_id( self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: """ @@ -2426,6 +2455,26 @@ def _notify_next_handlers(self, new_messages): if need_pop: new_messages.pop(i) # removing message that was detected with next_step_handler + + def _notify_state_handlers(self, new_messages): + """ + Description: TBD + :param new_messages: + :return: + """ + for i, message in enumerate(new_messages): + need_pop = False + user_state = self.current_states.current_state(message.from_user.id) + if user_state: + for handler in self.state_handlers: + if handler['filters']['state'] == user_state: + need_pop = True + state = State(self.current_states) + self._exec_task(handler["function"], message, state) + if need_pop: + new_messages.pop(i) # removing message that was detected by states + + @staticmethod def _build_handler_dict(handler, **filters): """ @@ -2661,6 +2710,44 @@ def register_edited_message_handler(self, callback, content_types=None, commands **kwargs) self.add_edited_message_handler(handler_dict) + + def state_handler(self, state=None, content_types=None, func=None,**kwargs): + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + state=state, + content_types=content_types, + func=func, + **kwargs) + self.add_state_handler(handler_dict) + return handler + + return decorator + + def add_state_handler(self, handler_dict): + """ + Adds the edit message handler + :param handler_dict: + :return: + """ + self.state_handlers.append(handler_dict) + + def register_state_handler(self, callback, state=None, content_types=None, func=None, **kwargs): + """ + Register a state handler. + :param callback: function to be called + :param state: state to be checked + :param content_types: + :param func: + """ + handler_dict = self._build_handler_dict(callback, + state=state, + content_types=content_types, + func=func, + **kwargs) + self.add_state_handler(handler_dict) + + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Channel post handler decorator diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 9b54f7c72..721aba62c 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -141,3 +141,79 @@ def get_handlers(self, handler_group_id): handlers = pickle.loads(value) self.clear_handlers(handler_group_id) return handlers + + + +class StateMachine: + def __init__(self): + self._states = {} + + def add_state(self, chat_id, state): + """ + Add a state. + """ + if chat_id in self._states: + + self._states[int(chat_id)]['state'] = state + else: + self._states[chat_id] = {'state': state,'data': {}} + + def current_state(self, chat_id): + """Current state""" + if chat_id in self._states: return self._states[chat_id]['state'] + else: return False + + def delete_state(self, chat_id): + """Delete a state""" + return self._states.pop(chat_id) + + +class State: + """ + Base class for state managing. + """ + def __init__(self, obj: StateMachine) -> None: + self.obj = obj + + def set(self, chat_id, new_state): + """ + Set a new state for a user. + :param chat_id: + :param new_state: new_state of a user + """ + self.obj._states[chat_id]['state'] = new_state + + def finish(self, chat_id): + """ + Finish(delete) state of a user. + :param chat_id: + """ + self.obj._states.pop(chat_id) + + def retrieve_data(self, chat_id): + """ + Save input text. + + Usage: + with state.retrieve_data(message.chat.id) as data: + data['name'] = message.text + + Also, at the end of your 'Form' you can get the name: + data['name'] + """ + return StateContext(self.obj, chat_id) + +class StateContext: + """ + Class for data. + """ + def __init__(self , obj: StateMachine, chat_id) -> None: + self.obj = obj + self.chat_id = chat_id + self.data = obj._states[chat_id]['data'] + + def __enter__(self): + return self.data + + def __exit__(self, exc_type, exc_val, exc_tb): + return \ No newline at end of file From beb4f8df44799df78f629fc3bbf56c84b8ff5a3c Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 17:15:33 +0500 Subject: [PATCH 0710/1808] Update register_handler.py --- examples/register_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/register_handler.py b/examples/register_handler.py index f733a3b89..2ca423a7e 100644 --- a/examples/register_handler.py +++ b/examples/register_handler.py @@ -1,6 +1,6 @@ import telebot -api_token = '1297441208:AAH5THRzLXvY5breGFzkrEOIj7zwCGzbQ-Y' +api_token = 'token' bot = telebot.TeleBot(api_token) From 2df6f00ba558804891dbbb226312890f31ea7c8e Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 18:22:54 +0500 Subject: [PATCH 0711/1808] Optimization Optimized code, added filters support --- telebot/__init__.py | 26 ++++++++++++++++++++++---- telebot/handler_backends.py | 5 ++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fe0d25613..3b9bc652a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2468,6 +2468,11 @@ def _notify_state_handlers(self, new_messages): if user_state: for handler in self.state_handlers: if handler['filters']['state'] == user_state: + for message_filter, filter_value in handler['filters'].items(): + if filter_value is None: + continue + if not self._test_filter(message_filter, filter_value, message): + return False need_pop = True state = State(self.current_states) self._exec_task(handler["function"], message, state) @@ -2711,12 +2716,21 @@ def register_edited_message_handler(self, callback, content_types=None, commands self.add_edited_message_handler(handler_dict) - def state_handler(self, state=None, content_types=None, func=None,**kwargs): - + def state_handler(self, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs): + """ + State handler for getting input from a user. + :param state: state of a user + :param content_types: + :param regexp: + :param func: + :param chat_types: + """ def decorator(handler): handler_dict = self._build_handler_dict(handler, state=state, content_types=content_types, + regexp=regexp, + chat_types=chat_types, func=func, **kwargs) self.add_state_handler(handler_dict) @@ -2732,7 +2746,7 @@ def add_state_handler(self, handler_dict): """ self.state_handlers.append(handler_dict) - def register_state_handler(self, callback, state=None, content_types=None, func=None, **kwargs): + def register_state_handler(self, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs): """ Register a state handler. :param callback: function to be called @@ -2740,9 +2754,11 @@ def register_state_handler(self, callback, state=None, content_types=None, func= :param content_types: :param func: """ - handler_dict = self._build_handler_dict(callback, + handler_dict = self._build_handler_dict(handler, state=state, content_types=content_types, + regexp=regexp, + chat_types=chat_types, func=func, **kwargs) self.add_state_handler(handler_dict) @@ -3226,6 +3242,8 @@ def _test_filter(self, message_filter, filter_value, message): return message.content_type == 'text' and util.extract_command(message.text) in filter_value elif message_filter == 'chat_types': return message.chat.type in filter_value + elif message_filter == 'state': + return True elif message_filter == 'func': return filter_value(message) elif self.custom_filters and message_filter in self.custom_filters: diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 721aba62c..a3b8944d1 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -167,6 +167,9 @@ def delete_state(self, chat_id): """Delete a state""" return self._states.pop(chat_id) + def _get_data(self, chat_id): + return self._states[chat_id]['data'] + class State: """ @@ -210,7 +213,7 @@ class StateContext: def __init__(self , obj: StateMachine, chat_id) -> None: self.obj = obj self.chat_id = chat_id - self.data = obj._states[chat_id]['data'] + self.data = obj._get_data(chat_id) def __enter__(self): return self.data From 967b94b14f621e2277dd1f14440f1049ee4426df Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 20:27:03 +0500 Subject: [PATCH 0712/1808] Update handler_backends.py --- telebot/handler_backends.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index a3b8944d1..673bb46d6 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -151,6 +151,8 @@ def __init__(self): def add_state(self, chat_id, state): """ Add a state. + :param chat_id: + :param state: new state """ if chat_id in self._states: From 9287eced495e9cd49dba9548277b31d8514629f7 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 21:10:29 +0500 Subject: [PATCH 0713/1808] Update a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 257b744ce..34d955bd5 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ Handle edited channel post messages Handle callback queries ```python @bot.callback_query_handler(func=lambda call: True) -def test_callback(call): # <- passes a CallbackQuery type object to your function +def test_callback(call): # <- passes a CallbackQuery type object to your function logger.info(call) ``` From e721910c0c5c3c38517d4dca55aec68c71f26f10 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 22:19:07 +0500 Subject: [PATCH 0714/1808] Update __init__.py --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3b9bc652a..3e90279b8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2746,7 +2746,7 @@ def add_state_handler(self, handler_dict): """ self.state_handlers.append(handler_dict) - def register_state_handler(self, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs): + def register_state_handler(self, callback, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs): """ Register a state handler. :param callback: function to be called @@ -2754,7 +2754,7 @@ def register_state_handler(self, state, content_types=None, regexp=None, func=No :param content_types: :param func: """ - handler_dict = self._build_handler_dict(handler, + handler_dict = self._build_handler_dict(callback=callback, state=state, content_types=content_types, regexp=regexp, From 8149551a154b97d5dd7fc30aa67dbdccf5604d8e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Sep 2021 20:33:32 +0300 Subject: [PATCH 0715/1808] Release 4.1.0 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 9001d305c..c48449f13 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.0.1' +__version__ = '4.1.0' From 39e875c1eab56d8f959940fb9d490080c62f5c0a Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 22:49:32 +0500 Subject: [PATCH 0716/1808] Update handler_backends.py --- telebot/handler_backends.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 673bb46d6..a6a2dc6be 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -156,7 +156,7 @@ def add_state(self, chat_id, state): """ if chat_id in self._states: - self._states[int(chat_id)]['state'] = state + self._states[chat_id]['state'] = state else: self._states[chat_id] = {'state': state,'data': {}} @@ -186,14 +186,14 @@ def set(self, chat_id, new_state): :param chat_id: :param new_state: new_state of a user """ - self.obj._states[chat_id]['state'] = new_state + self.obj.add_state(chat_id,new_state) def finish(self, chat_id): """ Finish(delete) state of a user. :param chat_id: """ - self.obj._states.pop(chat_id) + self.obj.delete_state(chat_id) def retrieve_data(self, chat_id): """ From 44b44ac2c55d697ee2ade34b67f4e2d746c1c84f Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Sep 2021 23:05:36 +0500 Subject: [PATCH 0717/1808] Optimization --- telebot/__init__.py | 6 +++--- telebot/handler_backends.py | 18 +++++------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3e90279b8..ce2525022 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -27,7 +27,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMachine, State +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, State REPLY_MARKUP_TYPES = Union[ @@ -188,7 +188,7 @@ def __init__( self.custom_filters = {} self.state_handlers = [] - self.current_states = StateMachine() + self.current_states = State() if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { @@ -2474,7 +2474,7 @@ def _notify_state_handlers(self, new_messages): if not self._test_filter(message_filter, filter_value, message): return False need_pop = True - state = State(self.current_states) + state = self.current_states self._exec_task(handler["function"], message, state) if need_pop: new_messages.pop(i) # removing message that was detected by states diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index a6a2dc6be..fd55384c9 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -144,7 +144,7 @@ def get_handlers(self, handler_group_id): -class StateMachine: +class State: def __init__(self): self._states = {} @@ -172,28 +172,20 @@ def delete_state(self, chat_id): def _get_data(self, chat_id): return self._states[chat_id]['data'] - -class State: - """ - Base class for state managing. - """ - def __init__(self, obj: StateMachine) -> None: - self.obj = obj - def set(self, chat_id, new_state): """ Set a new state for a user. :param chat_id: :param new_state: new_state of a user """ - self.obj.add_state(chat_id,new_state) + self.add_state(chat_id,new_state) def finish(self, chat_id): """ Finish(delete) state of a user. :param chat_id: """ - self.obj.delete_state(chat_id) + self.delete_state(chat_id) def retrieve_data(self, chat_id): """ @@ -206,13 +198,13 @@ def retrieve_data(self, chat_id): Also, at the end of your 'Form' you can get the name: data['name'] """ - return StateContext(self.obj, chat_id) + return StateContext(self, chat_id) class StateContext: """ Class for data. """ - def __init__(self , obj: StateMachine, chat_id) -> None: + def __init__(self , obj: State, chat_id) -> None: self.obj = obj self.chat_id = chat_id self.data = obj._get_data(chat_id) From b35f17124f4b77b6b2d4c1416d913ea647574c82 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Sep 2021 21:15:24 +0300 Subject: [PATCH 0718/1808] States minor update --- telebot/__init__.py | 9 +++++---- telebot/handler_backends.py | 4 +--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3e90279b8..a9ce65c1d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -498,8 +498,8 @@ def process_new_updates(self, updates): def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) - self._notify_state_handlers(new_messages) self._notify_reply_handlers(new_messages) + self._notify_state_handlers(new_messages) self.__notify_update(new_messages) self._notify_command_handlers(self.message_handlers, new_messages) @@ -2390,7 +2390,6 @@ def get_state(self, chat_id): """ return self.current_states.current_state(chat_id) - def register_next_step_handler_by_chat_id( self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: """ @@ -2462,6 +2461,8 @@ def _notify_state_handlers(self, new_messages): :param new_messages: :return: """ + if not self.current_states: return + for i, message in enumerate(new_messages): need_pop = False user_state = self.current_states.current_state(message.from_user.id) @@ -3242,12 +3243,12 @@ def _test_filter(self, message_filter, filter_value, message): return message.content_type == 'text' and util.extract_command(message.text) in filter_value elif message_filter == 'chat_types': return message.chat.type in filter_value - elif message_filter == 'state': - return True elif message_filter == 'func': return filter_value(message) elif self.custom_filters and message_filter in self.custom_filters: return self._check_filter(message_filter,filter_value,message) + elif message_filter == 'state': + return True else: return False diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 673bb46d6..a4d2f0886 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -143,7 +143,6 @@ def get_handlers(self, handler_group_id): return handlers - class StateMachine: def __init__(self): self._states = {} @@ -172,7 +171,6 @@ def delete_state(self, chat_id): def _get_data(self, chat_id): return self._states[chat_id]['data'] - class State: """ Base class for state managing. @@ -221,4 +219,4 @@ def __enter__(self): return self.data def __exit__(self, exc_type, exc_val, exc_tb): - return \ No newline at end of file + return From 062fababf2d3e7b7728355abf891ba5cba01cf6e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 28 Sep 2021 19:17:09 +0300 Subject: [PATCH 0719/1808] polling should leave our world. :) --- README.md | 15 +++++++-------- examples/anonymous_bot.py | 11 +---------- examples/chat_member_example.py | 2 +- examples/custom_filters/admin_filter_example.py | 2 +- examples/custom_filters/general_custom_filters.py | 2 +- examples/custom_filters/id_filter_example.py | 5 +---- examples/custom_filters/is_filter_example.py | 2 +- examples/custom_filters/text_filter_example.py | 2 +- examples/custom_states.py | 2 +- examples/deep_linking.py | 2 +- examples/detailed_example/detailed_example.py | 2 +- examples/echo_bot.py | 2 +- examples/inline_example.py | 2 +- examples/inline_keyboard_example.py | 2 +- examples/middleware/i18n.py | 2 +- examples/middleware/session.py | 2 +- examples/payments_example.py | 3 +-- examples/register_handler.py | 2 +- examples/skip_updates_example.py | 2 +- examples/step_example.py | 2 +- examples/telebot_bot/telebot_bot.py | 2 +- telebot/__init__.py | 10 +++++++--- 22 files changed, 34 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 34d955bd5..01f1bbdf6 100644 --- a/README.md +++ b/README.md @@ -121,13 +121,13 @@ This one echoes all incoming text messages back to the sender. It uses a lambda We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file: ```python -bot.polling() +bot.infinity_polling() ``` Alright, that's it! Our source file now looks like this: ```python import telebot -bot = telebot.TeleBot("TOKEN") +bot = telebot.TeleBot("YOUR_BOT_TOKEN") @bot.message_handler(commands=['start', 'help']) def send_welcome(message): @@ -137,7 +137,7 @@ def send_welcome(message): def echo_all(message): bot.reply_to(message, message.text) -bot.polling() +bot.infinity_polling() ``` To start the bot, simply open up a terminal and enter `python echo_bot.py` to run the bot! Test it by sending commands ('/start' and '/help') and arbitrary text messages. @@ -381,12 +381,10 @@ TOKEN = '' tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object # Upon calling this function, TeleBot starts polling the Telegram servers for new messages. -# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers -# - interval: True/False (default False) - The interval between polling requests -# Note: Editing this parameter harms the bot's response time +# - interval: int (default 0) - The interval between polling requests # - timeout: integer (default 20) - Timeout in seconds for long polling. # - allowed_updates: List of Strings (default None) - List of update types to request -tb.polling(none_stop=False, interval=0, timeout=20) +tb.infinity_polling(interval=0, timeout=20) # getMe user = tb.get_me() @@ -398,6 +396,7 @@ tb.remove_webhook() # getUpdates updates = tb.get_updates() +# or updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout): # sendMessage @@ -614,7 +613,7 @@ def handle_messages(messages): bot.reply_to(message, 'Hi') bot.set_update_listener(handle_messages) -bot.polling() +bot.infinity_polling() ``` ### Using web hooks diff --git a/examples/anonymous_bot.py b/examples/anonymous_bot.py index da0c00e7d..db7eaf72b 100644 --- a/examples/anonymous_bot.py +++ b/examples/anonymous_bot.py @@ -114,13 +114,4 @@ def chatting(message: types.Message): else: bot.send_message(message.chat.id, 'No one can hear you...') -# Start retrieving updates -# Questions: -# 1. Is there any way not to process messages sent earlier? -# -# For example: -# If the bot is turned off, and i tried to type `/find` nothing will happen, but... -# When i start the bot, `/find` command will processed, and i will be added to search -# -# I tried `skip_pending=True`, but thats was not helpful -bot.polling() +bot.infinity_polling(skip_pending=True) diff --git a/examples/chat_member_example.py b/examples/chat_member_example.py index b6fca73f6..36dbfb2b1 100644 --- a/examples/chat_member_example.py +++ b/examples/chat_member_example.py @@ -30,4 +30,4 @@ def my_chat_m(message: types.ChatMemberUpdated): @bot.message_handler(content_types=util.content_type_service) def delall(message: types.Message): bot.delete_message(message.chat.id,message.message_id) -bot.polling(allowed_updates=util.update_types) \ No newline at end of file +bot.infinity_polling(allowed_updates=util.update_types) diff --git a/examples/custom_filters/admin_filter_example.py b/examples/custom_filters/admin_filter_example.py index 98993e139..2aa683e4a 100644 --- a/examples/custom_filters/admin_filter_example.py +++ b/examples/custom_filters/admin_filter_example.py @@ -9,4 +9,4 @@ def answer_for_admin(message): # Register filter bot.add_custom_filter(custom_filters.IsAdminFilter(bot)) -bot.polling() \ No newline at end of file +bot.infinity_polling() diff --git a/examples/custom_filters/general_custom_filters.py b/examples/custom_filters/general_custom_filters.py index 382b68c6b..5eab56971 100644 --- a/examples/custom_filters/general_custom_filters.py +++ b/examples/custom_filters/general_custom_filters.py @@ -39,4 +39,4 @@ def bye_user(message): bot.add_custom_filter(MainFilter()) bot.add_custom_filter(IsAdmin()) -bot.polling(skip_pending=True,non_stop=True) # Skip old updates \ No newline at end of file +bot.infinity_polling(skip_pending=True) # Skip old updates diff --git a/examples/custom_filters/id_filter_example.py b/examples/custom_filters/id_filter_example.py index 06a6ce3ca..4a5b8834d 100644 --- a/examples/custom_filters/id_filter_example.py +++ b/examples/custom_filters/id_filter_example.py @@ -13,10 +13,7 @@ def admin_rep(message): def not_admin(message): bot.send_message(message.chat.id, "You are not allowed to use this command") - # Do not forget to register bot.add_custom_filter(custom_filters.ChatFilter()) - -bot.polling(non_stop=True) - +bot.infinity_polling() diff --git a/examples/custom_filters/is_filter_example.py b/examples/custom_filters/is_filter_example.py index 46d769ca1..414ac0db1 100644 --- a/examples/custom_filters/is_filter_example.py +++ b/examples/custom_filters/is_filter_example.py @@ -18,4 +18,4 @@ def text_filter(message): bot.add_custom_filter(custom_filters.IsReplyFilter()) bot.add_custom_filter(custom_filters.ForwardFilter()) -bot.polling(non_stop=True) \ No newline at end of file +bot.infinity_polling() diff --git a/examples/custom_filters/text_filter_example.py b/examples/custom_filters/text_filter_example.py index 247609708..73b6682e3 100644 --- a/examples/custom_filters/text_filter_example.py +++ b/examples/custom_filters/text_filter_example.py @@ -18,4 +18,4 @@ def text_filter(message): bot.add_custom_filter(custom_filters.TextMatchFilter()) bot.add_custom_filter(custom_filters.TextStartsFilter()) -bot.polling(non_stop=True) \ No newline at end of file +bot.infinity_polling() diff --git a/examples/custom_states.py b/examples/custom_states.py index 9ad9b1cfc..309fa6cd0 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -31,4 +31,4 @@ def ready_for_answer(message, state: State): bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") state.finish(message.chat.id) -bot.polling() \ No newline at end of file +bot.infinity_polling() diff --git a/examples/deep_linking.py b/examples/deep_linking.py index f5ea506c2..039e3a76a 100644 --- a/examples/deep_linking.py +++ b/examples/deep_linking.py @@ -74,4 +74,4 @@ def send_welcome(message): bot.reply_to(message, reply) -bot.polling() +bot.infinity_polling() diff --git a/examples/detailed_example/detailed_example.py b/examples/detailed_example/detailed_example.py index 8f2878ae3..76359cfb9 100644 --- a/examples/detailed_example/detailed_example.py +++ b/examples/detailed_example/detailed_example.py @@ -130,4 +130,4 @@ def command_default(m): bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help") -bot.polling() +bot.infinity_polling() diff --git a/examples/echo_bot.py b/examples/echo_bot.py index b66eb3442..c55a0045b 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -25,4 +25,4 @@ def echo_message(message): bot.reply_to(message, message.text) -bot.polling() +bot.infinity_polling() diff --git a/examples/inline_example.py b/examples/inline_example.py index 21f05eb6f..b0dc10671 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -61,7 +61,7 @@ def default_query(inline_query): def main_loop(): - bot.polling(True) + bot.infinity_polling() while 1: time.sleep(3) diff --git a/examples/inline_keyboard_example.py b/examples/inline_keyboard_example.py index f2b3fcef7..41f088adc 100644 --- a/examples/inline_keyboard_example.py +++ b/examples/inline_keyboard_example.py @@ -24,4 +24,4 @@ def callback_query(call): def message_handler(message): bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup()) -bot.polling(none_stop=True) +bot.infinity_polling() diff --git a/examples/middleware/i18n.py b/examples/middleware/i18n.py index 3cea87544..aafbdd064 100644 --- a/examples/middleware/i18n.py +++ b/examples/middleware/i18n.py @@ -50,4 +50,4 @@ def start(message): bot.send_message(message.chat.id, _('hello')) -bot.polling() +bot.infinity_polling() diff --git a/examples/middleware/session.py b/examples/middleware/session.py index a1a30e5bc..ccda6fc83 100644 --- a/examples/middleware/session.py +++ b/examples/middleware/session.py @@ -58,4 +58,4 @@ def start(message): bot.send_message(message.chat.id, bot.session['state']) -bot.polling() +bot.infinity_polling() diff --git a/examples/payments_example.py b/examples/payments_example.py index d0f52d4c2..c8dbfc594 100644 --- a/examples/payments_example.py +++ b/examples/payments_example.py @@ -78,5 +78,4 @@ def got_payment(message): parse_mode='Markdown') -bot.skip_pending = True -bot.polling(none_stop=True, interval=0) +bot.infinity_polling(skip_pending = True) diff --git a/examples/register_handler.py b/examples/register_handler.py index 2ca423a7e..9ee074e73 100644 --- a/examples/register_handler.py +++ b/examples/register_handler.py @@ -18,4 +18,4 @@ def start_executor(message): # bot.register_edited_message_handler(*args, **kwargs) # And other functions.. -bot.polling() \ No newline at end of file +bot.infinity_polling() diff --git a/examples/skip_updates_example.py b/examples/skip_updates_example.py index 0bd631b9e..dee5dd2bb 100644 --- a/examples/skip_updates_example.py +++ b/examples/skip_updates_example.py @@ -10,4 +10,4 @@ def send_welcome(message): def echo_all(message): bot.reply_to(message, message.text) -bot.polling(skip_pending=True)# Skip pending skips old updates +bot.infinity_polling(skip_pending=True)# Skip pending skips old updates diff --git a/examples/step_example.py b/examples/step_example.py index 0291c6672..7c35bad71 100644 --- a/examples/step_example.py +++ b/examples/step_example.py @@ -83,4 +83,4 @@ def process_sex_step(message): # WARNING It will work only if enable_save_next_step_handlers was called! bot.load_next_step_handlers() -bot.polling() +bot.infinity_polling() diff --git a/examples/telebot_bot/telebot_bot.py b/examples/telebot_bot/telebot_bot.py index ac6b63cbf..112070755 100644 --- a/examples/telebot_bot/telebot_bot.py +++ b/examples/telebot_bot/telebot_bot.py @@ -81,4 +81,4 @@ def listener(messages): bot.set_update_listener(listener) -bot.polling() +bot.infinity_polling() diff --git a/telebot/__init__.py b/telebot/__init__.py index a9ce65c1d..5f96ee838 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -563,8 +563,9 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout=20, skip_pending=False, long_polling_timeout=20, logger_level=logging.ERROR, - allowed_updates=None, *args, **kwargs): + + def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, logger_level=logging.ERROR, + allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -673,7 +674,10 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po # self.worker_pool.clear_exceptions() logger.info("Waiting for {0} seconds until retry".format(error_interval)) time.sleep(error_interval) - error_interval *= 2 + if error_interval * 2 < 60: + error_interval *= 2 + else: + error_interval = 60 else: # polling_thread.clear_exceptions() # self.worker_pool.clear_exceptions() From a28af3903d7ccc07b8c9e4a4678e18b489407f71 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 30 Sep 2021 11:56:36 +0300 Subject: [PATCH 0720/1808] Bugfix with one_time_keyboard = False --- telebot/types.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 1d8bdc060..8f6821824 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -873,7 +873,7 @@ def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Opti def to_json(self): json_dict = {'force_reply': True} if self.selective is not None: - json_dict['selective'] = True + json_dict['selective'] = self.selective if self.input_field_placeholder: json_dict['input_field_placeholder'] = self.input_field_placeholder return json.dumps(json_dict) @@ -886,7 +886,7 @@ def __init__(self, selective=None): def to_json(self): json_dict = {'remove_keyboard': True} if self.selective: - json_dict['selective'] = True + json_dict['selective'] = self.selective return json.dumps(json_dict) @@ -960,11 +960,11 @@ def to_json(self): """ json_dict = {'keyboard': self.keyboard} if self.one_time_keyboard is not None: - json_dict['one_time_keyboard'] = True + json_dict['one_time_keyboard'] = self.one_time_keyboard if self.resize_keyboard is not None: - json_dict['resize_keyboard'] = True + json_dict['resize_keyboard'] = self.resize_keyboard if self.selective is not None: - json_dict['selective'] = True + json_dict['selective'] = self.selective if self.input_field_placeholder: json_dict['input_field_placeholder'] = self.input_field_placeholder return json.dumps(json_dict) From 2e4280a94747f2572d3e78b2edef7929c79c1e71 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Oct 2021 15:56:54 +0500 Subject: [PATCH 0721/1808] Update of state handlers No need to create state handlers --- examples/custom_states.py | 42 ++++++++++------ telebot/__init__.py | 97 +++++++------------------------------ telebot/custom_filters.py | 27 +++++++++++ telebot/handler_backends.py | 4 ++ 4 files changed, 77 insertions(+), 93 deletions(-) diff --git a/examples/custom_states.py b/examples/custom_states.py index 309fa6cd0..1d46f6c19 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -1,34 +1,48 @@ import telebot -from telebot.handler_backends import State +from telebot import custom_filters bot = telebot.TeleBot("") + + @bot.message_handler(commands=['start']) def start_ex(message): bot.set_state(message.chat.id, 1) bot.send_message(message.chat.id, 'Hi, write me a name') + -@bot.state_handler(state=1) -def name_get(message, state:State): +@bot.message_handler(state="*", commands='cancel') +def any_state(message): + bot.send_message(message.chat.id, "Your state was cancelled.") + bot.delete_state(message.chat.id) + +@bot.message_handler(state=1) +def name_get(message): bot.send_message(message.chat.id, f'Now write me a surname') - state.set(message.chat.id, 2) - with state.retrieve_data(message.chat.id) as data: + bot.set_state(message.chat.id, 2) + with bot.retrieve_data(message.chat.id) as data: data['name'] = message.text -@bot.state_handler(state=2) -def ask_age(message, state:State): +@bot.message_handler(state=2) +def ask_age(message): bot.send_message(message.chat.id, "What is your age?") - state.set(message.chat.id, 3) - with state.retrieve_data(message.chat.id) as data: + bot.set_state(message.chat.id, 3) + with bot.retrieve_data(message.chat.id) as data: data['surname'] = message.text -@bot.state_handler(state=3) -def ready_for_answer(message, state: State): - with state.retrieve_data(message.chat.id) as data: +@bot.message_handler(state=3, is_digit=True) +def ready_for_answer(message): + with bot.retrieve_data(message.chat.id) as data: bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") - state.finish(message.chat.id) + bot.delete_state(message.chat.id) + +@bot.message_handler(state=3, is_digit=False) +def age_incorrect(message): + bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') -bot.infinity_polling() +bot.add_custom_filter(custom_filters.StateFilter(bot)) +bot.add_custom_filter(custom_filters.IsDigitFilter()) +bot.infinity_polling(skip_pending=True) \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index bfefd9273..d3cc2e500 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -499,7 +499,6 @@ def process_new_updates(self, updates): def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) self._notify_reply_handlers(new_messages) - self._notify_state_handlers(new_messages) self.__notify_update(new_messages) self._notify_command_handlers(self.message_handlers, new_messages) @@ -2386,6 +2385,9 @@ def delete_state(self, chat_id): """ self.current_states.delete_state(chat_id) + def retrieve_data(self, chat_id): + return self.current_states.retrieve_data(chat_id) + def get_state(self, chat_id): """ Get current state of a user. @@ -2394,6 +2396,14 @@ def get_state(self, chat_id): """ return self.current_states.current_state(chat_id) + def add_data(self, chat_id, **kwargs): + """ + Add data to states. + :param chat_id: + """ + for key, value in kwargs.items(): + self.current_states._add_data(chat_id, key, value) + def register_next_step_handler_by_chat_id( self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: """ @@ -2459,32 +2469,6 @@ def _notify_next_handlers(self, new_messages): new_messages.pop(i) # removing message that was detected with next_step_handler - def _notify_state_handlers(self, new_messages): - """ - Description: TBD - :param new_messages: - :return: - """ - if not self.current_states: return - - for i, message in enumerate(new_messages): - need_pop = False - user_state = self.current_states.current_state(message.from_user.id) - if user_state: - for handler in self.state_handlers: - if handler['filters']['state'] == user_state: - for message_filter, filter_value in handler['filters'].items(): - if filter_value is None: - continue - if not self._test_filter(message_filter, filter_value, message): - return False - need_pop = True - state = self.current_states - self._exec_task(handler["function"], message, state) - if need_pop: - new_messages.pop(i) # removing message that was detected by states - - @staticmethod def _build_handler_dict(handler, **filters): """ @@ -2548,7 +2532,7 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, state=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -2591,6 +2575,9 @@ def default_command(message): if content_types is None: content_types = ["text"] + if type(state) is not list and state is not None: + state = [state] + if isinstance(commands, str): logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") commands = [commands] @@ -2605,6 +2592,7 @@ def decorator(handler): content_types=content_types, commands=commands, regexp=regexp, + state=state, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2620,7 +2608,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, state=None, **kwargs): """ Registers message handler. :param callback: function to be called @@ -2644,6 +2632,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, content_types=content_types, commands=commands, regexp=regexp, + state=state, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2721,54 +2710,6 @@ def register_edited_message_handler(self, callback, content_types=None, commands self.add_edited_message_handler(handler_dict) - def state_handler(self, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs): - """ - State handler for getting input from a user. - :param state: state of a user - :param content_types: - :param regexp: - :param func: - :param chat_types: - """ - def decorator(handler): - handler_dict = self._build_handler_dict(handler, - state=state, - content_types=content_types, - regexp=regexp, - chat_types=chat_types, - func=func, - **kwargs) - self.add_state_handler(handler_dict) - return handler - - return decorator - - def add_state_handler(self, handler_dict): - """ - Adds the edit message handler - :param handler_dict: - :return: - """ - self.state_handlers.append(handler_dict) - - def register_state_handler(self, callback, state, content_types=None, regexp=None, func=None, chat_types=None, **kwargs): - """ - Register a state handler. - :param callback: function to be called - :param state: state to be checked - :param content_types: - :param func: - """ - handler_dict = self._build_handler_dict(callback=callback, - state=state, - content_types=content_types, - regexp=regexp, - chat_types=chat_types, - func=func, - **kwargs) - self.add_state_handler(handler_dict) - - def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Channel post handler decorator @@ -3251,8 +3192,6 @@ def _test_filter(self, message_filter, filter_value, message): return filter_value(message) elif self.custom_filters and message_filter in self.custom_filters: return self._check_filter(message_filter,filter_value,message) - elif message_filter == 'state': - return True else: return False diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 2f3b9b18c..20b42c382 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -146,3 +146,30 @@ def __init__(self, bot): def check(self, message): return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] +class StateFilter(AdvancedCustomFilter): + """ + Filter to check state. + + Example: + @bot.message_handler(state=1) + """ + def __init__(self, bot): + self.bot = bot + key = 'state' + + def check(self, message, text): + if self.bot.current_states.current_state(message.from_user.id) is False:return False + elif '*' in text:return True + return self.bot.current_states.current_state(message.from_user.id) in text + +class IsDigitFilter(SimpleCustomFilter): + """ + Filter to check whether the string is made up of only digits. + + Example: + @bot.message_handler(is_digit=True) + """ + key = 'is_digit' + + def check(self, message): + return message.text.isdigit() diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 9a37fad6d..d695b8212 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -179,6 +179,10 @@ def set(self, chat_id, new_state): """ self.add_state(chat_id,new_state) + def _add_data(self, chat_id, key, value): + result = self._states[chat_id]['data'][key] = value + return result + def finish(self, chat_id): """ Finish(delete) state of a user. From ff35f2521161651631876f1b766111d5b893fe6f Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Oct 2021 16:08:01 +0500 Subject: [PATCH 0722/1808] Update __init__.py --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d3cc2e500..de6d76a82 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2532,7 +2532,7 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, state=None, **kwargs): + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. From f337abe06eb20cf1938eebac4245af8835bf0d79 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Oct 2021 16:09:20 +0500 Subject: [PATCH 0723/1808] Update __init__.py --- telebot/__init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index de6d76a82..8f46afc85 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2575,9 +2575,6 @@ def default_command(message): if content_types is None: content_types = ["text"] - if type(state) is not list and state is not None: - state = [state] - if isinstance(commands, str): logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") commands = [commands] @@ -2592,7 +2589,6 @@ def decorator(handler): content_types=content_types, commands=commands, regexp=regexp, - state=state, func=func, **kwargs) self.add_message_handler(handler_dict) @@ -2608,7 +2604,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, state=None, **kwargs): + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): """ Registers message handler. :param callback: function to be called @@ -2632,7 +2628,6 @@ def register_message_handler(self, callback, content_types=None, commands=None, content_types=content_types, commands=commands, regexp=regexp, - state=state, func=func, **kwargs) self.add_message_handler(handler_dict) From bf8736e17e432f67464614fd3f9446b15c9580a6 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Oct 2021 23:29:59 +0500 Subject: [PATCH 0724/1808] Critical fix --- examples/custom_states.py | 16 ++++++++++++++++ telebot/custom_filters.py | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/custom_states.py b/examples/custom_states.py index 1d46f6c19..ac70bb96e 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -8,6 +8,9 @@ @bot.message_handler(commands=['start']) def start_ex(message): + """ + Start command. Here we are starting state + """ bot.set_state(message.chat.id, 1) bot.send_message(message.chat.id, 'Hi, write me a name') @@ -15,11 +18,17 @@ def start_ex(message): @bot.message_handler(state="*", commands='cancel') def any_state(message): + """ + Cancel state + """ bot.send_message(message.chat.id, "Your state was cancelled.") bot.delete_state(message.chat.id) @bot.message_handler(state=1) def name_get(message): + """ + State 1. Will process when user's state is 1. + """ bot.send_message(message.chat.id, f'Now write me a surname') bot.set_state(message.chat.id, 2) with bot.retrieve_data(message.chat.id) as data: @@ -28,21 +37,28 @@ def name_get(message): @bot.message_handler(state=2) def ask_age(message): + """ + State 2. Will process when user's state is 2. + """ bot.send_message(message.chat.id, "What is your age?") bot.set_state(message.chat.id, 3) with bot.retrieve_data(message.chat.id) as data: data['surname'] = message.text +# result @bot.message_handler(state=3, is_digit=True) def ready_for_answer(message): with bot.retrieve_data(message.chat.id) as data: bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") bot.delete_state(message.chat.id) +#incorrect number @bot.message_handler(state=3, is_digit=False) def age_incorrect(message): bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') +# register filters + bot.add_custom_filter(custom_filters.StateFilter(bot)) bot.add_custom_filter(custom_filters.IsDigitFilter()) bot.infinity_polling(skip_pending=True) \ No newline at end of file diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 20b42c382..bce2399af 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -159,8 +159,9 @@ def __init__(self, bot): def check(self, message, text): if self.bot.current_states.current_state(message.from_user.id) is False:return False - elif '*' in text:return True - return self.bot.current_states.current_state(message.from_user.id) in text + elif text == '*':return True + elif type(text) is list:return self.bot.current_states.current_state(message.from_user.id) in text + return self.bot.current_states.current_state(message.from_user.id) == text class IsDigitFilter(SimpleCustomFilter): """ From eead303d473c654372fde0891df54beb4645bd04 Mon Sep 17 00:00:00 2001 From: TrevorWinstral Date: Wed, 6 Oct 2021 14:13:05 +0200 Subject: [PATCH 0725/1808] Updated README with my bots --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 01f1bbdf6..efeb2970b 100644 --- a/README.md +++ b/README.md @@ -719,6 +719,8 @@ Get help. Discuss. Chat. * [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py) ## Bots using this API +* [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user +* [ETHLecture Bot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded to the video portal * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. From 585f627e1fed3d84fac86ea60c8ffbef4d10e183 Mon Sep 17 00:00:00 2001 From: TrevorWinstral Date: Wed, 6 Oct 2021 14:14:05 +0200 Subject: [PATCH 0726/1808] Updated README with my bots --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index efeb2970b..df13db9e5 100644 --- a/README.md +++ b/README.md @@ -720,7 +720,7 @@ Get help. Discuss. Chat. ## Bots using this API * [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user -* [ETHLecture Bot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded to the video portal +* [ETHLecture Bot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. From dadfdc2f1328f84b14a29b06c418ae9268bdc15e Mon Sep 17 00:00:00 2001 From: TrevorWinstral Date: Wed, 6 Oct 2021 14:15:30 +0200 Subject: [PATCH 0727/1808] Updated README with my bots --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df13db9e5..b9769446b 100644 --- a/README.md +++ b/README.md @@ -720,7 +720,7 @@ Get help. Discuss. Chat. ## Bots using this API * [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user -* [ETHLecture Bot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded +* [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. From bf79e8341eaf0983b46e5d934510b0ba0b0b51f5 Mon Sep 17 00:00:00 2001 From: TrevorWinstral <31442534+TrevorWinstral@users.noreply.github.com> Date: Fri, 8 Oct 2021 10:39:59 +0000 Subject: [PATCH 0728/1808] Moved bots to bottom of list --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b9769446b..39406d4c1 100644 --- a/README.md +++ b/README.md @@ -719,8 +719,6 @@ Get help. Discuss. Chat. * [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py) ## Bots using this API -* [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user -* [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. @@ -761,5 +759,9 @@ Get help. Discuss. Chat. * [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by Leon Heess [(source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent. * [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems) * [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings) +* [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user +* [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded + + **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 5c9d4edca964bd16fd8fe98127e992765874f752 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 9 Oct 2021 22:31:34 +0300 Subject: [PATCH 0729/1808] Bump version 4.1.1 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index c48449f13..205c523d8 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.1.0' +__version__ = '4.1.1' From 98044d6faa8f8b34df46f596e4442b57d98c9434 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 13 Oct 2021 18:34:36 +0500 Subject: [PATCH 0730/1808] File support for states File support. Now states can be saved in pickle file --- examples/custom_states.py | 4 ++ telebot/__init__.py | 16 ++++- telebot/custom_filters.py | 6 +- telebot/handler_backends.py | 131 ++++++++++++++++++++++++++++++++++-- 4 files changed, 148 insertions(+), 9 deletions(-) diff --git a/examples/custom_states.py b/examples/custom_states.py index ac70bb96e..c7acfe558 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -61,4 +61,8 @@ def age_incorrect(message): bot.add_custom_filter(custom_filters.StateFilter(bot)) bot.add_custom_filter(custom_filters.IsDigitFilter()) + +# set saving states into file. +bot.enable_saving_states() # you can delete this if you do not need to save states + bot.infinity_polling(skip_pending=True) \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index 8f46afc85..b8b0c6e43 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -27,7 +27,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, State +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMemory, StateFile REPLY_MARKUP_TYPES = Union[ @@ -188,7 +188,8 @@ def __init__( self.custom_filters = {} self.state_handlers = [] - self.current_states = State() + self.current_states = StateMemory() + if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { @@ -237,6 +238,17 @@ def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/s """ self.next_step_backend = FileHandlerBackend(self.next_step_backend.handlers, filename, delay) + def enable_saving_states(self, filename="./.state-save/states.pkl"): + """ + Enable saving states (by default saving disabled) + + :param filename: Filename of saving file + + """ + + self.current_states = StateFile(filename=filename) + self.current_states._create_dir() + def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): """ Enable saving reply handlers (by default saving disable) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index bce2399af..0b9552359 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -158,9 +158,9 @@ def __init__(self, bot): key = 'state' def check(self, message, text): - if self.bot.current_states.current_state(message.from_user.id) is False:return False - elif text == '*':return True - elif type(text) is list:return self.bot.current_states.current_state(message.from_user.id) in text + if self.bot.current_states.current_state(message.from_user.id) is False: return False + elif text == '*': return True + elif type(text) is list: return self.bot.current_states.current_state(message.from_user.id) in text return self.bot.current_states.current_state(message.from_user.id) == text class IsDigitFilter(SimpleCustomFilter): diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index d695b8212..45b903beb 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -143,7 +143,7 @@ def get_handlers(self, handler_group_id): return handlers -class State: +class StateMemory: def __init__(self): self._states = {} @@ -166,7 +166,7 @@ def current_state(self, chat_id): def delete_state(self, chat_id): """Delete a state""" - return self._states.pop(chat_id) + self._states.pop(chat_id) def _get_data(self, chat_id): return self._states[chat_id]['data'] @@ -195,7 +195,7 @@ def retrieve_data(self, chat_id): Save input text. Usage: - with state.retrieve_data(message.chat.id) as data: + with bot.retrieve_data(message.chat.id) as data: data['name'] = message.text Also, at the end of your 'Form' you can get the name: @@ -203,11 +203,114 @@ def retrieve_data(self, chat_id): """ return StateContext(self, chat_id) + +class StateFile: + """ + Class to save states in a file. + """ + def __init__(self, filename): + self.file_path = filename + + def add_state(self, chat_id, state): + """ + Add a state. + :param chat_id: + :param state: new state + """ + states_data = self._read_data() + if chat_id in states_data: + states_data[chat_id]['state'] = state + return self._save_data(states_data) + else: + new_data = states_data[chat_id] = {'state': state,'data': {}} + return self._save_data(states_data) + + + def current_state(self, chat_id): + """Current state.""" + states_data = self._read_data() + if chat_id in states_data: return states_data[chat_id]['state'] + else: return False + + def delete_state(self, chat_id): + """Delete a state""" + states_data = self._read_data() + states_data.pop(chat_id) + self._save_data(states_data) + + def _read_data(self): + """ + Read the data from file. + """ + file = open(self.file_path, 'rb') + states_data = pickle.load(file) + file.close() + return states_data + + def _create_dir(self): + """ + Create directory .save-handlers. + """ + dirs = self.file_path.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + if not os.path.isfile(self.file_path): + with open(self.file_path,'wb') as file: + pickle.dump({}, file) + + def _save_data(self, new_data): + """ + Save data after editing. + :param new_data: + """ + with open(self.file_path, 'wb+') as state_file: + pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL) + return True + + def _get_data(self, chat_id): + return self._read_data()[chat_id]['data'] + + def set(self, chat_id, new_state): + """ + Set a new state for a user. + :param chat_id: + :param new_state: new_state of a user + + """ + self.add_state(chat_id,new_state) + + def _add_data(self, chat_id, key, value): + states_data = self._read_data() + result = states_data[chat_id]['data'][key] = value + self._save_data(result) + + return result + + def finish(self, chat_id): + """ + Finish(delete) state of a user. + :param chat_id: + """ + self.delete_state(chat_id) + + def retrieve_data(self, chat_id): + """ + Save input text. + + Usage: + with bot.retrieve_data(message.chat.id) as data: + data['name'] = message.text + + Also, at the end of your 'Form' you can get the name: + data['name'] + """ + return StateFileContext(self, chat_id) + + class StateContext: """ Class for data. """ - def __init__(self , obj: State, chat_id) -> None: + def __init__(self , obj: StateMemory, chat_id) -> None: self.obj = obj self.chat_id = chat_id self.data = obj._get_data(chat_id) @@ -217,3 +320,23 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): return + +class StateFileContext: + """ + Class for data. + """ + def __init__(self , obj: StateFile, chat_id) -> None: + self.obj = obj + self.chat_id = chat_id + self.data = self.obj._get_data(self.chat_id) + + def __enter__(self): + return self.data + + def __exit__(self, exc_type, exc_val, exc_tb): + old_data = self.obj._read_data() + for i in self.data: + old_data[self.chat_id]['data'][i] = self.data.get(i) + self.obj._save_data(old_data) + + return From b6625baec63038cb2733c1ba5a951a3275713457 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 13 Oct 2021 19:02:17 +0500 Subject: [PATCH 0731/1808] Update __init__.py --- telebot/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b8b0c6e43..64cf2c75e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -357,7 +357,7 @@ def delete_webhook(self, drop_pending_updates=None, timeout=None): """ return apihelper.delete_webhook(self.token, drop_pending_updates, timeout) - def get_webhook_info(self, timeout=None): + def get_webhook_info(self, timeout: Optional[int]=None): """ Use this method to get current webhook status. Requires no parameters. If the bot is using getUpdates, will return an object with the url field empty. @@ -2381,7 +2381,7 @@ def register_next_step_handler( chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) - def set_state(self, chat_id, state): + def set_state(self, chat_id: int, state: Union[int, str]): """ Sets a new state of a user. :param chat_id: @@ -2389,7 +2389,7 @@ def set_state(self, chat_id, state): """ self.current_states.add_state(chat_id, state) - def delete_state(self, chat_id): + def delete_state(self, chat_id: int): """ Delete the current state of a user. :param chat_id: @@ -2397,10 +2397,10 @@ def delete_state(self, chat_id): """ self.current_states.delete_state(chat_id) - def retrieve_data(self, chat_id): + def retrieve_data(self, chat_id: int): return self.current_states.retrieve_data(chat_id) - def get_state(self, chat_id): + def get_state(self, chat_id: int): """ Get current state of a user. :param chat_id: @@ -2408,7 +2408,7 @@ def get_state(self, chat_id): """ return self.current_states.current_state(chat_id) - def add_data(self, chat_id, **kwargs): + def add_data(self, chat_id: int, **kwargs): """ Add data to states. :param chat_id: From 5fb48e68a05324b2acda4733a4f792d5136f61e2 Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo Date: Sat, 16 Oct 2021 17:45:15 +0200 Subject: [PATCH 0732/1808] Added description of the ApiTelegramException as attribute of the class --- .gitignore | 1 + telebot/apihelper.py | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c9919ab0a..e2bc744b6 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ var/ .idea/ venv/ +.venv/ # PyInstaller # Usually these files are written by a python script from a template diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9588c4e23..07e052842 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1661,4 +1661,5 @@ def __init__(self, function_name, result, result_json): result) self.result_json = result_json self.error_code = result_json['error_code'] + self.description = result_json['description'] From bb58d3fead6b399cf1d2784ecb01b7874fac6971 Mon Sep 17 00:00:00 2001 From: resinprotein2333 Date: Sun, 24 Oct 2021 16:45:49 +0800 Subject: [PATCH 0733/1808] Update README.md Add my bot into the bot list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 39406d4c1..e870e7ef6 100644 --- a/README.md +++ b/README.md @@ -761,6 +761,7 @@ Get help. Discuss. Chat. * [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings) * [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user * [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded +* [Vlun Finder Bot](https://github.com/resinprotein2333/Vlun-Finder-bot) by [Resinprotein2333](https://github.com/resinprotein2333). This bot can help you to find The information of CVE vulnerabilities. From 1a351bc8c76756792c79cf0fc5aeb1eb42def157 Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Sun, 24 Oct 2021 20:38:15 +0530 Subject: [PATCH 0734/1808] Added A New Bot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e870e7ef6..10682a71e 100644 --- a/README.md +++ b/README.md @@ -762,6 +762,7 @@ Get help. Discuss. Chat. * [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user * [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded * [Vlun Finder Bot](https://github.com/resinprotein2333/Vlun-Finder-bot) by [Resinprotein2333](https://github.com/resinprotein2333). This bot can help you to find The information of CVE vulnerabilities. +* [ETHGasFeeTrackerBot](https://t.me/ETHGasFeeTrackerBot) ([Source](https://github.com/DevAdvik/ETHGasFeeTrackerBot]) by *DevAdvik* - Get Live Ethereum Gas Fees in GWEI From 558b37b1c3b0beba02e15d24db0ed6b3bf6a91f4 Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo Date: Wed, 3 Nov 2021 15:30:10 +0100 Subject: [PATCH 0735/1808] New antiflood function --- telebot/util.py | 22 ++++++++++++++++++++++ tests/test_telebot.py | 7 +++++++ 2 files changed, 29 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index f871f09e7..b33aaa1ba 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -455,3 +455,25 @@ def webhook_google_functions(bot, request): return 'Bot FAIL', 400 else: return 'Bot ON' + +def antiflood(function, *args, **kwargs): + """ + Use this function inside loops in order to avoid getting TooManyRequests error. + Example: + + from telebot.util import antiflood + for chat_id in chat_id_list: + msg = antiflood(bot.send_message, chat_id, text) + + You want get the + """ + from telebot.apihelper import ApiTelegramException + from time import sleep + try: + msg = function(*args, **kwargs) + except ApiTelegramException as ex: + if ex.error_code == 429: + sleep(ex.result_json['parameters']['retry_after']) + msg = function(*args, **kwargs) + finally: + return msg \ No newline at end of file diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 6e8f341e1..2426b0f71 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -455,6 +455,13 @@ def test_edit_markup(self): new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup) assert new_msg.message_id + def test_antiflood(self): + text = "Flooding" + tb = telebot.TeleBot(TOKEN) + for _ in range(0,100): + util.antiflood(tb.send_message, CHAT_ID, text) + assert _ + @staticmethod def create_text_message(text): params = {'text': text} From 4a274ba4403433bd6db098eee6ee801b9da2ed56 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Nov 2021 18:48:46 +0300 Subject: [PATCH 0736/1808] Custom request sender Added apihelper.CUSTOM_REQUEST_SENDER option. It allows to substitute requests.request to your own function. --- README.md | 34 ++++++++++++++++++++++++++++++---- telebot/apihelper.py | 13 ++++++++++--- telebot/util.py | 15 +++++++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 39406d4c1..62e77c14f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ## Contents - * [Getting started.](#getting-started) + * [Getting started](#getting-started) * [Writing your first bot](#writing-your-first-bot) * [Prerequisites](#prerequisites) * [A simple echo bot](#a-simple-echo-bot) @@ -49,6 +49,7 @@ * [Using web hooks](#using-web-hooks) * [Logging](#logging) * [Proxy](#proxy) + * [Testing](#testing) * [API conformance](#api-conformance) * [F.A.Q.](#faq) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) @@ -57,7 +58,7 @@ * [More examples](#more-examples) * [Bots using this API](#bots-using-this-api) -## Getting started. +## Getting started This API is tested with Python 3.6-3.9 and Pypy 3. There are two ways to install the library: @@ -622,7 +623,6 @@ When using webhooks telegram sends one Update per call, for processing it you sh There are some examples using webhooks in the [examples/webhook_examples](examples/webhook_examples) directory. ### Logging - You can use the Telebot module logger to log debug info about Telebot. Use `telebot.logger` to get the logger of the TeleBot module. It is possible to add custom logging Handlers to the logger. Refer to the [Python logging module page](https://docs.python.org/2/library/logging.html) for more info. @@ -634,7 +634,6 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. ``` ### Proxy - You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument. ```python @@ -649,6 +648,33 @@ If you want to use socket5 proxy you need install dependency `pip install reques apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} ``` +### Testing +You can disable or change the interaction with real Telegram server by using +```python +apihelper.CUSTOM_REQUEST_SENDER = your_handler +``` +parameter. You can pass there your own function that will be called instead of _requests.request_. + +For example: +```python +def custom_sender(method, url, **kwargs): + print("custom_sender. method: {}, url: {}, params: {}".format(method, url, kwargs.get("params"))) + result = util.CustomRequestResponse('{"ok":true,"result":{"message_id": 1, "date": 1, "chat": {"id": 1, "type": "private"}}}') + return result +``` + +Then you can use API and proceed requests in your handler code. +```python +apihelper.CUSTOM_REQUEST_SENDER = custom_sender +tb = TeleBot("test") +res = tb.send_message(123, "Test") +``` + +Result will be: + +`custom_sender. method: post, url: https://api.telegram.org/botololo/sendMessage, params: {'chat_id': '123', 'text': 'Test'}` + + ## API conformance diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9588c4e23..e9162ce49 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -40,10 +40,12 @@ MAX_RETRIES = 15 CUSTOM_SERIALIZER = None +CUSTOM_REQUEST_SENDER = None ENABLE_MIDDLEWARE = False + def _get_req_session(reset=False): if SESSION_TIME_TO_LIVE: # If session TTL is set - check time passed @@ -136,9 +138,14 @@ def _make_request(token, method_name, method='get', params=None, files=None): method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout), proxies=proxy) else: - result = _get_req_session().request( - method, request_url, params=params, files=files, - timeout=(connect_timeout, read_timeout), proxies=proxy) + if CUSTOM_REQUEST_SENDER: + result = CUSTOM_REQUEST_SENDER( + method, request_url, params=params, files=files, + timeout=(connect_timeout, read_timeout), proxies=proxy) + else: + result = _get_req_session().request( + method, request_url, params=params, files=files, + timeout=(connect_timeout, read_timeout), proxies=proxy) logger.debug("The server returned: '{0}'".format(result.text.encode('utf8'))) diff --git a/telebot/util.py b/telebot/util.py index f871f09e7..05105efc4 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -12,6 +12,11 @@ from telebot import types +try: + import ujson as json +except ImportError: + import json + try: # noinspection PyPackageRequirements from PIL import Image @@ -165,6 +170,16 @@ def wait(self): return self.result +class CustomRequestResponse(): + def __init__(self, json_text, status_code = 200, reason = ""): + self.status_code = status_code + self.text = json_text + self.reason = reason + + def json(self): + return json.loads(self.text) + + def async_dec(): def decorator(fn): def wrapper(*args, **kwargs): From 06c878212704cc5e41622d9d18a2faaa2028da83 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Nov 2021 23:22:03 +0500 Subject: [PATCH 0737/1808] Little update Allowed other handlers, checked methods and other things --- telebot/__init__.py | 63 +++++++++++++++++++++++++++++++++++++++----- telebot/apihelper.py | 12 +++++++-- telebot/types.py | 31 +++++++++++++++++++--- 3 files changed, 94 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 64cf2c75e..185d1ee98 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -185,6 +185,7 @@ def __init__( self.poll_answer_handlers = [] self.my_chat_member_handlers = [] self.chat_member_handlers = [] + self.chat_join_request_handlers = [] self.custom_filters = {} self.state_handlers = [] @@ -205,7 +206,8 @@ def __init__( 'poll': [], 'poll_answer': [], 'my_chat_member': [], - 'chat_member': [] + 'chat_member': [], + 'chat_join_request': [] } self.default_middleware_handlers = [] @@ -426,6 +428,7 @@ def process_new_updates(self, updates): new_poll_answers = None new_my_chat_members = None new_chat_members = None + chat_join_request = None for update in updates: if apihelper.ENABLE_MIDDLEWARE: @@ -480,6 +483,9 @@ def process_new_updates(self, updates): if update.chat_member: if new_chat_members is None: new_chat_members = [] new_chat_members.append(update.chat_member) + if update.chat_join_request: + if chat_join_request is None: chat_join_request = [] + chat_join_request.append(update.chat_join_request) if new_messages: self.process_new_messages(new_messages) @@ -507,6 +513,9 @@ def process_new_updates(self, updates): self.process_new_my_chat_member(new_my_chat_members) if new_chat_members: self.process_new_chat_member(new_chat_members) + if chat_join_request: + self.process_chat_join_request(chat_join_request) + def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) @@ -550,6 +559,9 @@ def process_new_my_chat_member(self, my_chat_members): def process_new_chat_member(self, chat_members): self._notify_command_handlers(self.chat_member_handlers, chat_members) + def process_chat_join_request(self, chat_join_request): + self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request) + def process_middlewares(self, update): for update_type, middlewares in self.typed_middleware_handlers.items(): if getattr(update, update_type) is not None: @@ -1667,9 +1679,11 @@ def set_chat_permissions( return apihelper.set_chat_permissions(self.token, chat_id, permissions) def create_chat_invite_link( - self, chat_id: Union[int, str], + self, chat_id: Union[int, str], + name: Optional[str]=None, expire_date: Optional[Union[int, datetime]]=None, - member_limit: Optional[int]=None) -> types.ChatInviteLink: + member_limit: Optional[int]=None, + creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1681,13 +1695,15 @@ def create_chat_invite_link( :return: """ return types.ChatInviteLink.de_json( - apihelper.create_chat_invite_link(self.token, chat_id, expire_date, member_limit) + apihelper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request) ) def edit_chat_invite_link( - self, chat_id: Union[int, str], invite_link: str, + self, chat_id: Union[int, str], name: Optional[str]=None, + invite_link: Optional[str] = None, expire_date: Optional[Union[int, datetime]]=None, - member_limit: Optional[int]=None) -> types.ChatInviteLink: + member_limit: Optional[int]=None , + creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1701,7 +1717,7 @@ def edit_chat_invite_link( :return: """ return types.ChatInviteLink.de_json( - apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, expire_date, member_limit) + apihelper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) ) def revoke_chat_invite_link( @@ -3148,6 +3164,39 @@ def register_chat_member_handler(self, callback, func=None, **kwargs): handler_dict = self._build_handler_dict(callback, func=func, **kwargs) self.add_chat_member_handler(handler_dict) + def chat_join_request_handler(self, func=None, **kwargs): + """ + chat_join_request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_join_request_handler(handler_dict) + return handler + + return decorator + + def add_chat_join_request_handler(self, handler_dict): + """ + Adds a chat_join_request handler + :param handler_dict: + :return: + """ + self.chat_join_request_handlers.append(handler_dict) + + def register_chat_join_request_handler(self, callback, func=None, **kwargs): + """ + Registers chat join request handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_chat_join_request_handler(handler_dict) + def _test_message_handler(self, message_handler, message): """ Test message handler diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 7b7ee9671..4d4f919dc 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -973,7 +973,7 @@ def set_chat_permissions(token, chat_id, permissions): return _make_request(token, method_url, params=payload, method='post') -def create_chat_invite_link(token, chat_id, expire_date, member_limit): +def create_chat_invite_link(token, chat_id, name, expire_date, member_limit, creates_join_request): method_url = 'createChatInviteLink' payload = { 'chat_id': chat_id @@ -986,11 +986,15 @@ def create_chat_invite_link(token, chat_id, expire_date, member_limit): payload['expire_date'] = expire_date if member_limit: payload['member_limit'] = member_limit + if creates_join_request: + payload['creates_join_request'] = creates_join_request + if name: + payload['name'] = name return _make_request(token, method_url, params=payload, method='post') -def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit): +def edit_chat_invite_link(token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request): method_url = 'editChatInviteLink' payload = { 'chat_id': chat_id, @@ -1005,6 +1009,10 @@ def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit if member_limit is not None: payload['member_limit'] = member_limit + if name: + payload['name'] = name + if creates_join_request: + payload['creates_join_request'] = creates_join_request return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 8f6821824..99b25591a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -107,6 +107,7 @@ def de_json(cls, json_string): poll_answer = PollAnswer.de_json(obj.get('poll_answer')) my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member')) chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) + chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) 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) @@ -166,6 +167,22 @@ def difference(self) -> Dict[str, List]: dif[key] = [old[key], new[key]] return dif +class ChatJoinRequest(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['from'] = User.de_json(obj['from']) + + return cls(**obj) + + def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs): + self.chat = Chat = chat + self.from_user: User = from_user + self.date: int = date + self.bio: Optional[str] = bio + self.invite_link: Optional[ChatInviteLink] = invite_link class WebhookInfo(JsonDeserializable): @classmethod @@ -2752,14 +2769,17 @@ def de_json(cls, json_string): obj['creator'] = User.de_json(obj['creator']) return cls(**obj) - def __init__(self, invite_link, creator, is_primary, is_revoked, - expire_date=None, member_limit=None, **kwargs): + def __init__(self, invite_link, creator, creates_join_request , is_primary, is_revoked, + name=None, expire_date=None, member_limit=None, pending_join_request_count=None, **kwargs): self.invite_link: str = invite_link self.creator: User = creator + self.creates_join_request: bool = creates_join_request self.is_primary: bool = is_primary self.is_revoked: bool = is_revoked + self.name: str = name self.expire_date: int = expire_date self.member_limit: int = member_limit + self.pending_join_request_count: int = pending_join_request_count def to_json(self): return json.dumps(self.to_dict()) @@ -2769,12 +2789,17 @@ def to_dict(self): "invite_link": self.invite_link, "creator": self.creator.to_dict(), "is_primary": self.is_primary, - "is_revoked": self.is_revoked + "is_revoked": self.is_revoked, + "creates_join_request": self.creates_join_request } if self.expire_date: json_dict["expire_date"] = self.expire_date if self.member_limit: json_dict["member_limit"] = self.member_limit + if self.pending_join_request_count: + json_dict["pending_join_request_count"] = self.pending_join_request_count + if self.name: + json_dict["name"] = self.name return json_dict From 953e2286b854a84364f3b50ae2793841e408f8a9 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 12:15:28 +0500 Subject: [PATCH 0738/1808] Bot API 5.4 --- examples/chat_join_request.py | 11 +++++++++++ telebot/__init__.py | 28 ++++++++++++++++++++++++++++ telebot/apihelper.py | 15 ++++++++++++++- telebot/types.py | 7 ++++--- telebot/util.py | 2 +- 5 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 examples/chat_join_request.py diff --git a/examples/chat_join_request.py b/examples/chat_join_request.py new file mode 100644 index 000000000..6ab29ed9f --- /dev/null +++ b/examples/chat_join_request.py @@ -0,0 +1,11 @@ +import telebot + + +bot = telebot.TeleBot('TOKEN') + +@bot.chat_join_request_handler() +def make_some(message: telebot.types.ChatJoinRequest): + bot.send_message(message.chat.id, 'I accepted a new user!') + bot.approve_chat_join_request(message.chat.id, message.from_user.id) + +bot.infinity_polling(allowed_updates=telebot.util.update_types) \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index 185d1ee98..4aba9f98a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -484,6 +484,7 @@ def process_new_updates(self, updates): if new_chat_members is None: new_chat_members = [] new_chat_members.append(update.chat_member) if update.chat_join_request: + print('we received1') if chat_join_request is None: chat_join_request = [] chat_join_request.append(update.chat_join_request) @@ -514,6 +515,7 @@ def process_new_updates(self, updates): if new_chat_members: self.process_new_chat_member(new_chat_members) if chat_join_request: + print('we received2') self.process_chat_join_request(chat_join_request) @@ -1747,6 +1749,32 @@ def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: """ return apihelper.export_chat_invite_link(self.token, chat_id) + def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: + """ + Use this method to approve a chat join request. + The bot must be an administrator in the chat for this to work and must have + the can_invite_users administrator right. Returns True on success. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :return: True on success. + """ + return apihelper.approve_chat_join_request(self.token, chat_id, user_id) + + def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: + """ + Use this method to decline a chat join request. + The bot must be an administrator in the chat for this to work and must have + the can_invite_users administrator right. Returns True on success. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :return: True on success. + """ + return apihelper.decline_chat_join_request(self.token, chat_id, user_id) + def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: """ Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4d4f919dc..f66db8af6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1031,7 +1031,20 @@ def export_chat_invite_link(token, chat_id): payload = {'chat_id': chat_id} return _make_request(token, method_url, params=payload, method='post') - +def approve_chat_join_request(token, chat_id, user_id): + method_url = 'approveChatJoinRequest' + payload = { + 'chat_id': chat_id, + 'user_id': user_id + } + return _make_request(token, method_url, params=payload, method='post') +def decline_chat_join_request(token, chat_id, user_id): + method_url = 'declineChatJoinRequest' + payload = { + 'chat_id': chat_id, + 'user_id': user_id + } + return _make_request(token, method_url, params=payload, method='post') def set_chat_photo(token, chat_id, photo): method_url = 'setChatPhoto' payload = {'chat_id': chat_id} diff --git a/telebot/types.py b/telebot/types.py index 99b25591a..972e2fd11 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -110,11 +110,11 @@ def de_json(cls, json_string): chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) 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) + my_chat_member, chat_member, chat_join_request) 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): + my_chat_member, chat_member, chat_join_request): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -129,6 +129,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.poll_answer = poll_answer self.my_chat_member = my_chat_member self.chat_member = chat_member + self.chat_join_request = chat_join_request class ChatMemberUpdated(JsonDeserializable): @@ -173,7 +174,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) - obj['from'] = User.de_json(obj['from']) + obj['from_user'] = User.de_json(obj['from']) return cls(**obj) diff --git a/telebot/util.py b/telebot/util.py index 5eb99bcbd..1ab620128 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -46,7 +46,7 @@ update_types = [ "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" + "my_chat_member", "chat_member", "chat_join_request" ] class WorkerThread(threading.Thread): From ed6616e4c72aba45ed9a53c52de57acbf8dda29f Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 12:21:02 +0500 Subject: [PATCH 0739/1808] Bot API 5.4 --- telebot/__init__.py | 2 -- telebot/types.py | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4aba9f98a..0919f6d33 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -484,7 +484,6 @@ def process_new_updates(self, updates): if new_chat_members is None: new_chat_members = [] new_chat_members.append(update.chat_member) if update.chat_join_request: - print('we received1') if chat_join_request is None: chat_join_request = [] chat_join_request.append(update.chat_join_request) @@ -515,7 +514,6 @@ def process_new_updates(self, updates): if new_chat_members: self.process_new_chat_member(new_chat_members) if chat_join_request: - print('we received2') self.process_chat_join_request(chat_join_request) diff --git a/telebot/types.py b/telebot/types.py index 972e2fd11..fdf64672c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -175,6 +175,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) obj['from_user'] = User.de_json(obj['from']) + obj['invite_link'] = ChatInviteLink.de_json(obj['invite_link']) return cls(**obj) From d49c57699eb470f5c4a299ee23e5b42cd2b168b9 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 12:27:19 +0500 Subject: [PATCH 0740/1808] Tests --- tests/test_handler_backends.py | 6 ++++-- tests/test_telebot.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index 638cb2758..21cf8f9b2 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -64,9 +64,10 @@ def update_type(message): poll_answer = None my_chat_member = None chat_member = None + chat_join_request = None return types.Update(1001234038283, 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) + my_chat_member, chat_member, chat_join_request) @pytest.fixture() @@ -83,9 +84,10 @@ def reply_to_message_update_type(reply_to_message): poll_answer = None my_chat_member = None chat_member = None + chat_join_request = None return types.Update(1001234038284, reply_to_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) + poll, poll_answer, my_chat_member, chat_member, chat_join_request) def next_handler(message): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 2426b0f71..2976a9ab9 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -485,9 +485,10 @@ def create_message_update(text): poll_answer = None my_chat_member = None chat_member = None + chat_join_request = None return types.Update(-1001234038283, 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) + my_chat_member, chat_member, chat_join_request) def test_is_string_unicode(self): s1 = u'string' From 31097c5380cb8fde8b6999685ce82246be94d720 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 12:34:49 +0500 Subject: [PATCH 0741/1808] Update test_types.py --- tests/test_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_types.py b/tests/test_types.py index 417a67827..4669f8264 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -222,7 +222,7 @@ def test_KeyboardButtonPollType(): def test_json_chat_invite_link(): - json_string = r'{"invite_link": "https://t.me/joinchat/z-abCdEFghijKlMn", "creator": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "is_primary": false, "is_revoked": false, "expire_date": 1624119999, "member_limit": 10}' + json_string = r'{"invite_link":{"invite_link":"https://t.me/joinchat/MeASP-Wi...","creator":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"ru"},"pending_join_request_count":1,"creates_join_request":true,"is_primary":false,"is_revoked":false }}' invite_link = types.ChatInviteLink.de_json(json_string) assert invite_link.invite_link == 'https://t.me/joinchat/z-abCdEFghijKlMn' assert isinstance(invite_link.creator, types.User) From 6808ab3ebeb2da79e0b4bffc6f6bb6e6a375878d Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 12:42:48 +0500 Subject: [PATCH 0742/1808] Update test_types.py --- tests/test_types.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index 4669f8264..d23f8fa3f 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -222,14 +222,18 @@ def test_KeyboardButtonPollType(): def test_json_chat_invite_link(): - json_string = r'{"invite_link":{"invite_link":"https://t.me/joinchat/MeASP-Wi...","creator":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"ru"},"pending_join_request_count":1,"creates_join_request":true,"is_primary":false,"is_revoked":false }}' + json_string = r'{"invite_link":"https://t.me/joinchat/MeASP-Wi...","creator":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"ru"},"pending_join_request_count":1,"creates_join_request":true,"is_primary":false,"is_revoked":false}' invite_link = types.ChatInviteLink.de_json(json_string) - assert invite_link.invite_link == 'https://t.me/joinchat/z-abCdEFghijKlMn' + assert invite_link.invite_link == 'https://t.me/joinchat/MeASP-Wi...' assert isinstance(invite_link.creator, types.User) assert not invite_link.is_primary assert not invite_link.is_revoked - assert invite_link.expire_date == 1624119999 - assert invite_link.member_limit == 10 + assert invite_link.expire_date is None + assert invite_link.member_limit is None + assert invite_link.name is None + assert invite_link.creator.id == 927266710 + assert invite_link.pending_join_request_count == 1 + assert invite_link.creates_join_request def test_chat_member_updated(): json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "administrator"}}' From 8dcfa0c2826caa210f625ae918d011b4a092970f Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 12:52:41 +0500 Subject: [PATCH 0743/1808] Little fix for states --- examples/custom_states.py | 14 ++++++++++---- tests/.state-save/states.pkl | 1 + tests/test_types.py | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 tests/.state-save/states.pkl diff --git a/examples/custom_states.py b/examples/custom_states.py index c7acfe558..3d16b5ad8 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -5,6 +5,12 @@ bot = telebot.TeleBot("") +class MyStates: + name = 1 + surname = 2 + age = 3 + + @bot.message_handler(commands=['start']) def start_ex(message): @@ -24,7 +30,7 @@ def any_state(message): bot.send_message(message.chat.id, "Your state was cancelled.") bot.delete_state(message.chat.id) -@bot.message_handler(state=1) +@bot.message_handler(state=MyStates.name) def name_get(message): """ State 1. Will process when user's state is 1. @@ -35,7 +41,7 @@ def name_get(message): data['name'] = message.text -@bot.message_handler(state=2) +@bot.message_handler(state=MyStates.surname) def ask_age(message): """ State 2. Will process when user's state is 2. @@ -46,14 +52,14 @@ def ask_age(message): data['surname'] = message.text # result -@bot.message_handler(state=3, is_digit=True) +@bot.message_handler(state=MyStates.age, is_digit=True) def ready_for_answer(message): with bot.retrieve_data(message.chat.id) as data: bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") bot.delete_state(message.chat.id) #incorrect number -@bot.message_handler(state=3, is_digit=False) +@bot.message_handler(state=MyStates.age, is_digit=False) def age_incorrect(message): bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') diff --git a/tests/.state-save/states.pkl b/tests/.state-save/states.pkl new file mode 100644 index 000000000..e2ecf720d --- /dev/null +++ b/tests/.state-save/states.pkl @@ -0,0 +1 @@ +�}�. \ No newline at end of file diff --git a/tests/test_types.py b/tests/test_types.py index d23f8fa3f..7f9b32fed 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -235,6 +235,7 @@ def test_json_chat_invite_link(): assert invite_link.pending_join_request_count == 1 assert invite_link.creates_join_request + def test_chat_member_updated(): json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "administrator"}}' cm_updated = types.ChatMemberUpdated.de_json(json_string) From fc347ae166f2c9aed80601a21383150f38b7384e Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 13:06:43 +0500 Subject: [PATCH 0744/1808] Update custom_states.py --- examples/custom_states.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/custom_states.py b/examples/custom_states.py index 3d16b5ad8..22691b244 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -17,7 +17,7 @@ def start_ex(message): """ Start command. Here we are starting state """ - bot.set_state(message.chat.id, 1) + bot.set_state(message.from_user.id, 1) bot.send_message(message.chat.id, 'Hi, write me a name') @@ -28,7 +28,7 @@ def any_state(message): Cancel state """ bot.send_message(message.chat.id, "Your state was cancelled.") - bot.delete_state(message.chat.id) + bot.delete_state(message.from_user.id) @bot.message_handler(state=MyStates.name) def name_get(message): @@ -36,8 +36,8 @@ def name_get(message): State 1. Will process when user's state is 1. """ bot.send_message(message.chat.id, f'Now write me a surname') - bot.set_state(message.chat.id, 2) - with bot.retrieve_data(message.chat.id) as data: + bot.set_state(message.from_user.id, 2) + with bot.retrieve_data(message.from_user.id) as data: data['name'] = message.text @@ -47,16 +47,16 @@ def ask_age(message): State 2. Will process when user's state is 2. """ bot.send_message(message.chat.id, "What is your age?") - bot.set_state(message.chat.id, 3) - with bot.retrieve_data(message.chat.id) as data: + bot.set_state(message.from_user.id, 3) + with bot.retrieve_data(message.from_user.id) as data: data['surname'] = message.text # result @bot.message_handler(state=MyStates.age, is_digit=True) def ready_for_answer(message): - with bot.retrieve_data(message.chat.id) as data: + with bot.retrieve_data(message.from_user.id) as data: bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") - bot.delete_state(message.chat.id) + bot.delete_state(message.from_user.id) #incorrect number @bot.message_handler(state=MyStates.age, is_digit=False) From 3a6073e3a0bcf9779eeab53a69aa49baf5de7392 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 13:08:49 +0500 Subject: [PATCH 0745/1808] Update custom_states.py --- examples/custom_states.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/custom_states.py b/examples/custom_states.py index 22691b244..5acc8f2fd 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -17,7 +17,7 @@ def start_ex(message): """ Start command. Here we are starting state """ - bot.set_state(message.from_user.id, 1) + bot.set_state(message.from_user.id, MyStates.name) bot.send_message(message.chat.id, 'Hi, write me a name') @@ -36,7 +36,7 @@ def name_get(message): State 1. Will process when user's state is 1. """ bot.send_message(message.chat.id, f'Now write me a surname') - bot.set_state(message.from_user.id, 2) + bot.set_state(message.from_user.id, MyStates.surname) with bot.retrieve_data(message.from_user.id) as data: data['name'] = message.text @@ -47,7 +47,7 @@ def ask_age(message): State 2. Will process when user's state is 2. """ bot.send_message(message.chat.id, "What is your age?") - bot.set_state(message.from_user.id, 3) + bot.set_state(message.from_user.id, MyStates.age) with bot.retrieve_data(message.from_user.id) as data: data['surname'] = message.text From becce1f580d5d1bb1ca6c8ef84e497f420781223 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 19:51:05 +0500 Subject: [PATCH 0746/1808] Update apihelper.py --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f66db8af6..3ae004d14 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -986,7 +986,7 @@ def create_chat_invite_link(token, chat_id, name, expire_date, member_limit, cre payload['expire_date'] = expire_date if member_limit: payload['member_limit'] = member_limit - if creates_join_request: + if creates_join_request is not None: payload['creates_join_request'] = creates_join_request if name: payload['name'] = name From 8003ff5e5937d1946a5f241c3763aa0d26c744aa Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 19:51:29 +0500 Subject: [PATCH 0747/1808] Delete states.pkl --- tests/.state-save/states.pkl | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/.state-save/states.pkl diff --git a/tests/.state-save/states.pkl b/tests/.state-save/states.pkl deleted file mode 100644 index e2ecf720d..000000000 --- a/tests/.state-save/states.pkl +++ /dev/null @@ -1 +0,0 @@ -�}�. \ No newline at end of file From e412d2f08402da0b3e414419300f15a9dd2023a1 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 19:56:10 +0500 Subject: [PATCH 0748/1808] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 54cc769cb..41ce353df 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ * [Poll Answer Handler](#poll-answer-handler) * [My Chat Member Handler](#my-chat-member-handler) * [Chat Member Handler](#chat-member-handler) + * [Chat Join request handler](#chat-join-request-handler) * [Inline Mode](#inline-mode) * [Inline handler](#inline-handler) * [Chosen Inline handler](#chosen-inline-handler) @@ -272,6 +273,10 @@ Handle updates of a chat member's status in a chat `@bot.chat_member_handler() # <- passes a ChatMemberUpdated type object to your function` *Note: "chat_member" updates are not requested by default. If you want to allow all update types, set `allowed_updates` in `bot.polling()` / `bot.infinity_polling()` to `util.update_types`* +#### Chat Join Request Handler +Handle chat join requests using: +`@bot.chat_join_request_handler() # <- passes ChatInviteLink type object to your function` + ### Inline Mode More information about [Inline mode](https://core.telegram.org/bots/inline). From 62b1ec04ab47c232697835e5567a9ab7b2c16b78 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 6 Nov 2021 19:59:44 +0500 Subject: [PATCH 0749/1808] Update __init__.py --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0919f6d33..80b38188c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -514,7 +514,7 @@ def process_new_updates(self, updates): if new_chat_members: self.process_new_chat_member(new_chat_members) if chat_join_request: - self.process_chat_join_request(chat_join_request) + self.process_new_chat_join_request(chat_join_request) def process_new_messages(self, new_messages): @@ -559,7 +559,7 @@ def process_new_my_chat_member(self, my_chat_members): def process_new_chat_member(self, chat_members): self._notify_command_handlers(self.chat_member_handlers, chat_members) - def process_chat_join_request(self, chat_join_request): + def process_new_chat_join_request(self, chat_join_request): self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request) def process_middlewares(self, update): From 5ac71baafed01bd7fa65eea7b101260134b8b6ae Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 7 Nov 2021 23:02:23 +0300 Subject: [PATCH 0750/1808] RETRY_ENGINE Added RETRY_ENGINE var to api_helper. Added RETRY_ENGINE = 2 based on native "requests" retry mechanism. --- telebot/apihelper.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index e9162ce49..e2367238f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -9,6 +9,7 @@ import requests from requests.exceptions import HTTPError, ConnectionError, Timeout +from requests.adapters import HTTPAdapter try: # noinspection PyUnresolvedReferences @@ -38,6 +39,7 @@ RETRY_ON_ERROR = False RETRY_TIMEOUT = 2 MAX_RETRIES = 15 +RETRY_ENGINE = 1 CUSTOM_SERIALIZER = None CUSTOM_REQUEST_SENDER = None @@ -107,45 +109,48 @@ def _make_request(token, method_name, method='get', params=None, files=None): params = params or None # Set params to None if empty result = None - if RETRY_ON_ERROR: + if RETRY_ON_ERROR and RETRY_ENGINE == 1: got_result = False current_try = 0 - while not got_result and current_try Date: Mon, 8 Nov 2021 18:35:55 +0300 Subject: [PATCH 0751/1808] Bump to version 4.2.0 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 205c523d8..24f8550f0 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.1.1' +__version__ = '4.2.0' From 9b99bb5f217e41facc930c5e2758cd3f8b288407 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 8 Nov 2021 18:51:42 +0300 Subject: [PATCH 0752/1808] Update readme and typo --- README.md | 1 + telebot/__init__.py | 15 +++++++++------ telebot/apihelper.py | 7 ++++++- telebot/types.py | 13 ++++++------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 41ce353df..41081dab8 100644 --- a/README.md +++ b/README.md @@ -683,6 +683,7 @@ Result will be: ## API conformance +* ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021) * ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMemberXXX classes are full copies of ChatMember * ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021) * ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021) diff --git a/telebot/__init__.py b/telebot/__init__.py index 80b38188c..cab3714ff 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -245,7 +245,6 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): Enable saving states (by default saving disabled) :param filename: Filename of saving file - """ self.current_states = StateFile(filename=filename) @@ -1690,8 +1689,10 @@ def create_chat_invite_link( :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param name: Invite link name; 0-32 characters :param expire_date: Point in time (Unix timestamp) when the link will expire :param member_limit: Maximum number of users that can be members of the chat simultaneously + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified :return: """ return types.ChatInviteLink.de_json( @@ -1699,21 +1700,23 @@ def create_chat_invite_link( ) def edit_chat_invite_link( - self, chat_id: Union[int, str], name: Optional[str]=None, - invite_link: Optional[str] = None, - expire_date: Optional[Union[int, datetime]]=None, - member_limit: Optional[int]=None , + self, chat_id: Union[int, str], + invite_link: Optional[str] = None, + name: Optional[str]=None, + expire_date: Optional[Union[int, datetime]]=None, + member_limit: Optional[int]=None, creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - :param invite_link: :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param name: Invite link name; 0-32 characters :param invite_link: The invite link to edit :param expire_date: Point in time (Unix timestamp) when the link will expire :param member_limit: Maximum number of users that can be members of the chat simultaneously + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified :return: """ return types.ChatInviteLink.de_json( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 37c752027..0ca982ef3 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1016,7 +1016,7 @@ def edit_chat_invite_link(token, chat_id, invite_link, name, expire_date, member payload['member_limit'] = member_limit if name: payload['name'] = name - if creates_join_request: + if creates_join_request is not None: payload['creates_join_request'] = creates_join_request return _make_request(token, method_url, params=payload, method='post') @@ -1036,6 +1036,7 @@ def export_chat_invite_link(token, chat_id): payload = {'chat_id': chat_id} return _make_request(token, method_url, params=payload, method='post') + def approve_chat_join_request(token, chat_id, user_id): method_url = 'approveChatJoinRequest' payload = { @@ -1043,6 +1044,8 @@ def approve_chat_join_request(token, chat_id, user_id): 'user_id': user_id } return _make_request(token, method_url, params=payload, method='post') + + def decline_chat_join_request(token, chat_id, user_id): method_url = 'declineChatJoinRequest' payload = { @@ -1050,6 +1053,8 @@ def decline_chat_join_request(token, chat_id, user_id): 'user_id': user_id } return _make_request(token, method_url, params=payload, method='post') + + def set_chat_photo(token, chat_id, photo): method_url = 'setChatPhoto' payload = {'chat_id': chat_id} diff --git a/telebot/types.py b/telebot/types.py index fdf64672c..263b327e6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -175,16 +175,15 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) obj['from_user'] = User.de_json(obj['from']) - obj['invite_link'] = ChatInviteLink.de_json(obj['invite_link']) - + obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link')) return cls(**obj) def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs): - self.chat = Chat = chat - self.from_user: User = from_user - self.date: int = date - self.bio: Optional[str] = bio - self.invite_link: Optional[ChatInviteLink] = invite_link + self.chat = chat + self.from_user = from_user + self.date = date + self.bio = bio + self.invite_link = invite_link class WebhookInfo(JsonDeserializable): @classmethod From e22a7fecea7501540c8efc4f2d97d7aefb942162 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 8 Nov 2021 18:53:10 +0300 Subject: [PATCH 0753/1808] One more readme update... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41081dab8..849a24935 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

A simple, but extensible Python implementation for the Telegram Bot API. -##

Supported Bot API version: 5.3! +##

Supported Bot API version: 5.4! ## Contents From 7925bdc6c98197fafe0928ac75bebd6471038592 Mon Sep 17 00:00:00 2001 From: JoachimStanislaus Date: Thu, 11 Nov 2021 17:21:56 +0800 Subject: [PATCH 0754/1808] added Google Sheet bot to list of bot examples. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 849a24935..37d56c792 100644 --- a/README.md +++ b/README.md @@ -795,6 +795,7 @@ Get help. Discuss. Chat. * [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded * [Vlun Finder Bot](https://github.com/resinprotein2333/Vlun-Finder-bot) by [Resinprotein2333](https://github.com/resinprotein2333). This bot can help you to find The information of CVE vulnerabilities. * [ETHGasFeeTrackerBot](https://t.me/ETHGasFeeTrackerBot) ([Source](https://github.com/DevAdvik/ETHGasFeeTrackerBot]) by *DevAdvik* - Get Live Ethereum Gas Fees in GWEI +* [Google Sheet Bot](https://github.com/JoachimStanislaus/Tele_Sheet_bot) by [JoachimStanislaus](https://github.com/JoachimStanislaus). This bot can help you to track your expenses by uploading your bot entries to your google sheet. From 1f05b47ad663c50dba6f7cc60ffa0b0f8394b377 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 20 Nov 2021 15:47:55 +0500 Subject: [PATCH 0755/1808] Asynchronous Telebot --- telebot/__init__.py | 3095 ++++++++++++++++++++++++--- telebot/asyncio_filters.py | 176 ++ telebot/asyncio_handler_backends.py | 343 +++ telebot/asyncio_helper.py | 1607 ++++++++++++++ 4 files changed, 4948 insertions(+), 273 deletions(-) create mode 100644 telebot/asyncio_filters.py create mode 100644 telebot/asyncio_handler_backends.py create mode 100644 telebot/asyncio_helper.py diff --git a/telebot/__init__.py b/telebot/__init__.py index cab3714ff..65906f2ba 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -12,10 +12,9 @@ # this imports are used to avoid circular import error import telebot.util import telebot.types -from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter - logger = logging.getLogger('TeleBot') + formatter = logging.Formatter( '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' ) @@ -26,8 +25,13 @@ logger.setLevel(logging.ERROR) -from telebot import apihelper, util, types +from telebot import apihelper, util, types, asyncio_helper from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMemory, StateFile +from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter +import asyncio +from telebot import asyncio_handler_backends +from telebot import asyncio_filters + REPLY_MARKUP_TYPES = Union[ @@ -35,6 +39,7 @@ types.ReplyKeyboardRemove, types.ForceReply] + """ Module : telebot """ @@ -3308,338 +3313,2882 @@ def _notify_command_handlers(self, handlers, new_messages): break -class AsyncTeleBot(TeleBot): - def __init__(self, *args, **kwargs): - TeleBot.__init__(self, *args, **kwargs) +class AsyncTeleBot: - # I'm not sure if `get_updates` should be added here too - - @util.async_dec() - def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): - return TeleBot.enable_save_next_step_handlers(self, delay, filename) - @util.async_dec() - def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): - return TeleBot.enable_save_reply_handlers(self, delay, filename) - - @util.async_dec() - def disable_save_next_step_handlers(self): - return TeleBot.disable_save_next_step_handlers(self) - - @util.async_dec() - def disable_save_reply_handlers(self): - return TeleBot.enable_save_reply_handlers(self) + def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, + exception_handler=None,suppress_middleware_excepions=False) -> None: # TODO: ADD TYPEHINTS + self.token = token + logger.info('creating some objects..') + self.loop = asyncio.get_event_loop() + self.offset = offset + self.token = token + self.parse_mode = parse_mode + self.update_listener = [] + self.suppress_middleware_excepions = suppress_middleware_excepions - @util.async_dec() - def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): - return TeleBot.load_next_step_handlers(self, filename, del_file_after_loading) + self.exc_info = None - @util.async_dec() - def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): - return TeleBot.load_reply_handlers(self, filename, del_file_after_loading) + self.exception_handler = exception_handler - @util.async_dec() - def get_me(self): - return TeleBot.get_me(self) + self.message_handlers = [] + self.edited_message_handlers = [] + self.channel_post_handlers = [] + self.edited_channel_post_handlers = [] + self.inline_handlers = [] + self.chosen_inline_handlers = [] + self.callback_query_handlers = [] + self.shipping_query_handlers = [] + self.pre_checkout_query_handlers = [] + self.poll_handlers = [] + self.poll_answer_handlers = [] + self.my_chat_member_handlers = [] + self.chat_member_handlers = [] + self.chat_join_request_handlers = [] + self.custom_filters = {} + self.state_handlers = [] - @util.async_dec() - def log_out(self): - return TeleBot.log_out(self) + self.current_states = asyncio_handler_backends.StateMemory() - @util.async_dec() - def close(self): - return TeleBot.close(self) - @util.async_dec() - def get_my_commands(self, *args, **kwargs): # needed args because new scope and language_code - return TeleBot.get_my_commands(self, *args, **kwargs) + if asyncio_helper.ENABLE_MIDDLEWARE: + self.typed_middleware_handlers = { + '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': [] + } + self.default_middleware_handlers = [] - @util.async_dec() - def set_my_commands(self, *args, **kwargs): - return TeleBot.set_my_commands(self, *args, **kwargs) - - @util.async_dec() - def delete_my_commands(self, *args, **kwargs): - return TeleBot.delete_my_commands(self, *args, **kwargs) - - @util.async_dec() - def get_file(self, *args): - return TeleBot.get_file(self, *args) - - @util.async_dec() - def download_file(self, *args): - return TeleBot.download_file(self, *args) - - @util.async_dec() - def get_user_profile_photos(self, *args, **kwargs): - return TeleBot.get_user_profile_photos(self, *args, **kwargs) - - @util.async_dec() - def get_chat(self, *args): - return TeleBot.get_chat(self, *args) - - @util.async_dec() - def leave_chat(self, *args): - return TeleBot.leave_chat(self, *args) - - @util.async_dec() - def get_chat_administrators(self, *args): - return TeleBot.get_chat_administrators(self, *args) - - @util.async_dec() - def get_chat_members_count(self, *args): - logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead') - return TeleBot.get_chat_member_count(self, *args) - - @util.async_dec() - def get_chat_member_count(self, *args): - return TeleBot.get_chat_member_count(self, *args) - @util.async_dec() - def set_chat_sticker_set(self, *args): - return TeleBot.set_chat_sticker_set(self, *args) + async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, + timeout: Optional[int]=None, allowed_updates: Optional[List]=None) -> types.Update: + json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates) + return [types.Update.de_json(ju) for ju in json_updates] - @util.async_dec() - def delete_chat_sticker_set(self, *args): - return TeleBot.delete_chat_sticker_set(self, *args) + async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, + long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None, + none_stop: Optional[bool]=None): + """ + This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. - @util.async_dec() - def get_chat_member(self, *args): - return TeleBot.get_chat_member(self, *args) + Warning: Do not call this function more than once! + + Always get updates. + :param interval: Delay between two update retrivals + :param non_stop: Do not stop polling when an ApiException occurs. + :param timeout: Request connection timeout + :param skip_pending: skip old updates + :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. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + 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 - @util.async_dec() - def send_message(self, *args, **kwargs): - return TeleBot.send_message(self, *args, **kwargs) + if skip_pending: + await self.skip_updates() + await self._process_polling(non_stop, interval, timeout, long_polling_timeout, allowed_updates) - @util.async_dec() - def send_dice(self, *args, **kwargs): - return TeleBot.send_dice(self, *args, **kwargs) + async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, logger_level=logging.ERROR, + allowed_updates: Optional[List[str]]=None, *args, **kwargs): + """ + Wrap polling with infinite loop and exception handling to avoid bot stops polling. - @util.async_dec() - def send_animation(self, *args, **kwargs): - return TeleBot.send_animation(self, *args, **kwargs) + :param timeout: Request connection timeout + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param skip_pending: skip old updates + :param logger_level: Custom logging level for infinity_polling logging. + Use logger levels from logging as a value. None/NOTSET = no error logging + :param allowed_updates: A list of the update types you want your bot to receive. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + 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. + """ + if skip_pending: + await self.skip_updates() + self._polling = True + while self._polling: + try: + await self._process_polling(non_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, + allowed_updates=allowed_updates, *args, **kwargs) + except Exception as e: + if logger_level and logger_level >= logging.ERROR: + logger.error("Infinity polling exception: %s", str(e)) + if logger_level and logger_level >= logging.DEBUG: + logger.error("Exception traceback:\n%s", traceback.format_exc()) + time.sleep(3) + continue + if logger_level and logger_level >= logging.INFO: + logger.error("Infinity polling: polling exited") + if logger_level and logger_level >= logging.INFO: + logger.error("Break infinity polling") - @util.async_dec() - def forward_message(self, *args, **kwargs): - return TeleBot.forward_message(self, *args, **kwargs) + async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, + long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None): + """ + Function to process polling. + :param non_stop: Do not stop polling when an ApiException occurs. + :param interval: Delay between two update retrivals + :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. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. - @util.async_dec() - def copy_message(self, *args, **kwargs): - return TeleBot.copy_message(self, *args, **kwargs) + 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. + :return: - @util.async_dec() - def delete_message(self, *args): - return TeleBot.delete_message(self, *args) + """ + self._polling = True - @util.async_dec() - def send_photo(self, *args, **kwargs): - return TeleBot.send_photo(self, *args, **kwargs) + try: + while self._polling: + try: + + updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout) + + if updates: + logger.debug(f"Received {len(updates)} updates.") + + await self.process_new_updates(updates) + if interval: await asyncio.sleep(interval) + except KeyboardInterrupt: + logger.info("KeyboardInterrupt received.") + break + except asyncio.CancelledError: + break + except asyncio_helper.ApiTelegramException as e: + logger.info(str(e)) - @util.async_dec() - def send_audio(self, *args, **kwargs): - return TeleBot.send_audio(self, *args, **kwargs) + continue + except Exception as e: + print('Cause exception while getting updates.') + logger.critical(str(e)) + await asyncio.sleep(3) + continue - @util.async_dec() - def send_voice(self, *args, **kwargs): - return TeleBot.send_voice(self, *args, **kwargs) + finally: + self._polling = False + logger.warning('Polling is stopped.') - @util.async_dec() - def send_document(self, *args, **kwargs): - return TeleBot.send_document(self, *args, **kwargs) - @util.async_dec() - def send_sticker(self, *args, **kwargs): - return TeleBot.send_sticker(self, *args, **kwargs) + async def _loop_create_task(self, coro): + return asyncio.create_task(coro) - @util.async_dec() - def send_video(self, *args, **kwargs): - return TeleBot.send_video(self, *args, **kwargs) + async def _process_updates(self, handlers, messages): + for message in messages: + for message_handler in handlers: + process_update = await self._test_message_handler(message_handler, message) + if not process_update: + continue + elif process_update: + try: + await self._loop_create_task(message_handler['function'](message)) + break + except Exception as e: + print(str(e)) - @util.async_dec() - def send_video_note(self, *args, **kwargs): - return TeleBot.send_video_note(self, *args, **kwargs) + # update handling + async def process_new_updates(self, updates): + upd_count = len(updates) + logger.info('Received {0} new updates'.format(upd_count)) + if upd_count == 0: return - @util.async_dec() - def send_media_group(self, *args, **kwargs): - return TeleBot.send_media_group(self, *args, **kwargs) + new_messages = None + new_edited_messages = None + new_channel_posts = None + new_edited_channel_posts = None + new_inline_queries = None + new_chosen_inline_results = None + new_callback_queries = None + new_shipping_queries = None + new_pre_checkout_queries = None + new_polls = None + new_poll_answers = None + new_my_chat_members = None + new_chat_members = None + chat_join_request = None + for update in updates: + if asyncio_helper.ENABLE_MIDDLEWARE: + try: + self.process_middlewares(update) + except Exception as e: + logger.error(str(e)) + if not self.suppress_middleware_excepions: + raise + else: + if update.update_id > self.offset: self.offset = update.update_id + continue + logger.debug('Processing updates: {0}'.format(update)) + if update.update_id: + self.offset = update.update_id + 1 + if update.message: + logger.info('Processing message') + if new_messages is None: new_messages = [] + new_messages.append(update.message) + if update.edited_message: + if new_edited_messages is None: new_edited_messages = [] + new_edited_messages.append(update.edited_message) + if update.channel_post: + if new_channel_posts is None: new_channel_posts = [] + new_channel_posts.append(update.channel_post) + if update.edited_channel_post: + if new_edited_channel_posts is None: new_edited_channel_posts = [] + new_edited_channel_posts.append(update.edited_channel_post) + if update.inline_query: + if new_inline_queries is None: new_inline_queries = [] + new_inline_queries.append(update.inline_query) + if update.chosen_inline_result: + if new_chosen_inline_results is None: new_chosen_inline_results = [] + new_chosen_inline_results.append(update.chosen_inline_result) + if update.callback_query: + if new_callback_queries is None: new_callback_queries = [] + new_callback_queries.append(update.callback_query) + if update.shipping_query: + if new_shipping_queries is None: new_shipping_queries = [] + new_shipping_queries.append(update.shipping_query) + if update.pre_checkout_query: + if new_pre_checkout_queries is None: new_pre_checkout_queries = [] + new_pre_checkout_queries.append(update.pre_checkout_query) + if update.poll: + if new_polls is None: new_polls = [] + new_polls.append(update.poll) + if update.poll_answer: + if new_poll_answers is None: new_poll_answers = [] + new_poll_answers.append(update.poll_answer) + if update.my_chat_member: + if new_my_chat_members is None: new_my_chat_members = [] + new_my_chat_members.append(update.my_chat_member) + if update.chat_member: + if new_chat_members is None: new_chat_members = [] + new_chat_members.append(update.chat_member) + if update.chat_join_request: + if chat_join_request is None: chat_join_request = [] + chat_join_request.append(update.chat_join_request) - @util.async_dec() - def send_location(self, *args, **kwargs): - return TeleBot.send_location(self, *args, **kwargs) + if new_messages: + await self.process_new_messages(new_messages) + if new_edited_messages: + await self.process_new_edited_messages(new_edited_messages) + if new_channel_posts: + await self.process_new_channel_posts(new_channel_posts) + if new_edited_channel_posts: + await self.process_new_edited_channel_posts(new_edited_channel_posts) + if new_inline_queries: + await self.process_new_inline_query(new_inline_queries) + if new_chosen_inline_results: + await self.process_new_chosen_inline_query(new_chosen_inline_results) + if new_callback_queries: + await self.process_new_callback_query(new_callback_queries) + if new_shipping_queries: + await self.process_new_shipping_query(new_shipping_queries) + if new_pre_checkout_queries: + await self.process_new_pre_checkout_query(new_pre_checkout_queries) + if new_polls: + await self.process_new_poll(new_polls) + if new_poll_answers: + await self.process_new_poll_answer(new_poll_answers) + if new_my_chat_members: + await self.process_new_my_chat_member(new_my_chat_members) + if new_chat_members: + await self.process_new_chat_member(new_chat_members) + if chat_join_request: + await self.process_chat_join_request(chat_join_request) + - @util.async_dec() - def edit_message_live_location(self, *args, **kwargs): - return TeleBot.edit_message_live_location(self, *args, **kwargs) + async def process_new_messages(self, new_messages): + await self.__notify_update(new_messages) + await self._process_updates(self.message_handlers, new_messages) - @util.async_dec() - def stop_message_live_location(self, *args, **kwargs): - return TeleBot.stop_message_live_location(self, *args, **kwargs) + async def process_new_edited_messages(self, edited_message): + await self._process_updates(self.edited_message_handlers, edited_message) - @util.async_dec() - def send_venue(self, *args, **kwargs): - return TeleBot.send_venue(self, *args, **kwargs) + async def process_new_channel_posts(self, channel_post): + await self._process_updates(self.channel_post_handlers, channel_post) - @util.async_dec() - def send_contact(self, *args, **kwargs): - return TeleBot.send_contact(self, *args, **kwargs) + async def process_new_edited_channel_posts(self, edited_channel_post): + await self._process_updates(self.edited_channel_post_handlers, edited_channel_post) - @util.async_dec() - def send_chat_action(self, *args, **kwargs): - return TeleBot.send_chat_action(self, *args, **kwargs) + async def process_new_inline_query(self, new_inline_querys): + await self._process_updates(self.inline_handlers, new_inline_querys) - @util.async_dec() - def kick_chat_member(self, *args, **kwargs): - logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') - return TeleBot.ban_chat_member(self, *args, **kwargs) - - @util.async_dec() - def ban_chat_member(self, *args, **kwargs): - return TeleBot.ban_chat_member(self, *args, **kwargs) + async def process_new_chosen_inline_query(self, new_chosen_inline_querys): + await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_querys) - @util.async_dec() - def unban_chat_member(self, *args, **kwargs): - return TeleBot.unban_chat_member(self, *args, **kwargs) + async def process_new_callback_query(self, new_callback_querys): + await self._process_updates(self.callback_query_handlers, new_callback_querys) - @util.async_dec() - def restrict_chat_member(self, *args, **kwargs): - return TeleBot.restrict_chat_member(self, *args, **kwargs) + async def process_new_shipping_query(self, new_shipping_querys): + await self._process_updates(self.shipping_query_handlers, new_shipping_querys) - @util.async_dec() - def promote_chat_member(self, *args, **kwargs): - return TeleBot.promote_chat_member(self, *args, **kwargs) - - @util.async_dec() - def set_chat_administrator_custom_title(self, *args, **kwargs): - return TeleBot.set_chat_administrator_custom_title(self, *args, **kwargs) + async def process_new_pre_checkout_query(self, pre_checkout_querys): + await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_querys) - @util.async_dec() - def set_chat_permissions(self, *args, **kwargs): - return TeleBot.set_chat_permissions(self, *args, **kwargs) + async def process_new_poll(self, polls): + await self._process_updates(self.poll_handlers, polls) - @util.async_dec() - def create_chat_invite_link(self, *args, **kwargs): - return TeleBot.create_chat_invite_link(self, *args, **kwargs) - - @util.async_dec() - def edit_chat_invite_link(self, *args, **kwargs): - return TeleBot.edit_chat_invite_link(self, *args, **kwargs) + async def process_new_poll_answer(self, poll_answers): + await self._process_updates(self.poll_answer_handlers, poll_answers) - @util.async_dec() - def revoke_chat_invite_link(self, *args, **kwargs): - return TeleBot.revoke_chat_invite_link(self, *args, **kwargs) - - @util.async_dec() - def export_chat_invite_link(self, *args): - return TeleBot.export_chat_invite_link(self, *args) - - @util.async_dec() - def set_chat_photo(self, *args): - return TeleBot.set_chat_photo(self, *args) + async def process_new_my_chat_member(self, my_chat_members): + await self._process_updates(self.my_chat_member_handlers, my_chat_members) - @util.async_dec() - def delete_chat_photo(self, *args): - return TeleBot.delete_chat_photo(self, *args) + async def process_new_chat_member(self, chat_members): + await self._process_updates(self.chat_member_handlers, chat_members) - @util.async_dec() - def set_chat_title(self, *args): - return TeleBot.set_chat_title(self, *args) + async def process_chat_join_request(self, chat_join_request): + await self._process_updates(self.chat_join_request_handlers, chat_join_request) - @util.async_dec() - def set_chat_description(self, *args): - return TeleBot.set_chat_description(self, *args) + async def process_middlewares(self, update): + for update_type, middlewares in self.typed_middleware_handlers.items(): + if getattr(update, update_type) is not None: + for typed_middleware_handler in middlewares: + try: + typed_middleware_handler(self, getattr(update, update_type)) + except Exception as e: + e.args = e.args + (f'Typed middleware handler "{typed_middleware_handler.__qualname__}"',) + raise - @util.async_dec() - def pin_chat_message(self, *args, **kwargs): - return TeleBot.pin_chat_message(self, *args, **kwargs) + if len(self.default_middleware_handlers) > 0: + for default_middleware_handler in self.default_middleware_handlers: + try: + default_middleware_handler(self, update) + except Exception as e: + e.args = e.args + (f'Default middleware handler "{default_middleware_handler.__qualname__}"',) + raise - @util.async_dec() - def unpin_chat_message(self, *args): - return TeleBot.unpin_chat_message(self, *args) + + async def __notify_update(self, new_messages): + if len(self.update_listener) == 0: + return + for listener in self.update_listener: + self._loop_create_task(listener, new_messages) - @util.async_dec() - def unpin_all_chat_messages(self, *args): - return TeleBot.unpin_all_chat_messages(self, *args) + async def _test_message_handler(self, message_handler, message): + """ + Test message handler + :param message_handler: + :param message: + :return: + """ + for message_filter, filter_value in message_handler['filters'].items(): + if filter_value is None: + continue - @util.async_dec() - def edit_message_text(self, *args, **kwargs): - return TeleBot.edit_message_text(self, *args, **kwargs) + if not await self._test_filter(message_filter, filter_value, message): + return False - @util.async_dec() - def edit_message_media(self, *args, **kwargs): - return TeleBot.edit_message_media(self, *args, **kwargs) + return True - @util.async_dec() - def edit_message_reply_markup(self, *args, **kwargs): - return TeleBot.edit_message_reply_markup(self, *args, **kwargs) + def add_custom_filter(self, custom_filter): + """ + Create custom filter. + custom_filter: Class with check(message) method. + """ + self.custom_filters[custom_filter.key] = custom_filter - @util.async_dec() - def send_game(self, *args, **kwargs): - return TeleBot.send_game(self, *args, **kwargs) + async def _test_filter(self, message_filter, filter_value, message): + """ + Test filters + :param message_filter: Filter type passed in handler + :param filter_value: Filter value passed in handler + :param message: Message to test + :return: True if filter conforms + """ + # test_cases = { + # 'content_types': lambda msg: msg.content_type in filter_value, + # 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), + # 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, + # 'func': lambda msg: filter_value(msg) + # } + # return test_cases.get(message_filter, lambda msg: False)(message) + if message_filter == 'content_types': + return message.content_type in filter_value + elif message_filter == 'regexp': + return message.content_type == 'text' and re.search(filter_value, message.text, re.IGNORECASE) + elif message_filter == 'commands': + return message.content_type == 'text' and util.extract_command(message.text) in filter_value + elif message_filter == 'chat_types': + return message.chat.type in filter_value + elif message_filter == 'func': + return filter_value(message) + elif self.custom_filters and message_filter in self.custom_filters: + return await self._check_filter(message_filter,filter_value,message) + else: + return False + + async def _check_filter(self, message_filter, filter_value, message): + """ + Check up the filter + :param message_filter: + :param filter_value: + :param message: + :return: + """ + filter_check = self.custom_filters.get(message_filter) + if not filter_check: + return False + elif isinstance(filter_check, asyncio_filters.SimpleCustomFilter): + return filter_value == await filter_check.check(message) + elif isinstance(filter_check, asyncio_filters.AdvancedCustomFilter): + return await filter_check.check(message, filter_value) + else: + logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter!") + return False + + def middleware_handler(self, update_types=None): + """ + Middleware handler decorator. + + This decorator can be used to decorate functions that must be handled as middlewares before entering any other + message handlers + But, be careful and check type of the update inside the handler if more than one update_type is given + + Example: + + bot = TeleBot('TOKEN') + + # Print post message text before entering to any post_channel handlers + @bot.middleware_handler(update_types=['channel_post', 'edited_channel_post']) + def print_channel_post_text(bot_instance, channel_post): + print(channel_post.text) + + # Print update id before entering to any handlers + @bot.middleware_handler() + def print_channel_post_text(bot_instance, update): + print(update.update_id) + + :param update_types: Optional list of update types that can be passed into the middleware handler. + + """ + + def decorator(handler): + self.add_middleware_handler(handler, update_types) + return handler + + return decorator + + def add_middleware_handler(self, handler, update_types=None): + """ + Add middleware handler + :param handler: + :param update_types: + :return: + """ + if not asyncio_helper.ENABLE_MIDDLEWARE: + raise RuntimeError("Middleware is not enabled. Use asyncio_helper.ENABLE_MIDDLEWARE.") + + if update_types: + for update_type in update_types: + self.typed_middleware_handlers[update_type].append(handler) + else: + self.default_middleware_handlers.append(handler) + + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): + """ + Message handler decorator. + This decorator can be used to decorate functions that must handle certain types of messages. + All message handlers are tested in the order they were added. + + Example: + + bot = TeleBot('TOKEN') + + # Handles all messages which text matches regexp. + @bot.message_handler(regexp='someregexp') + async def command_help(message): + bot.send_message(message.chat.id, 'Did someone call for help?') + + # Handles messages in private chat + @bot.message_handler(chat_types=['private']) # You can add more chat types + async def command_help(message): + bot.send_message(message.chat.id, 'Private chat detected, sir!') + + # Handle all sent documents of type 'text/plain'. + @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', + content_types=['document']) + async def command_handle_document(message): + bot.send_message(message.chat.id, 'Document received, sir!') + + # Handle all other messages. + @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', + 'text', 'location', 'contact', 'sticker']) + async def async default_command(message): + bot.send_message(message.chat.id, "This is the async default command handler.") + + :param commands: Optional list of strings (commands to handle). + :param regexp: Optional regular expression. + :param func: Optional lambda function. The lambda receives the message to test as the first parameter. + It must return True if the command should handle the message. + :param content_types: Supported message content types. Must be a list. async defaults to ['text']. + :param chat_types: list of chat types + """ + + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_message_handler(handler_dict) + return handler + + return decorator + + def add_message_handler(self, handler_dict): + """ + Adds a message handler + :param handler_dict: + :return: + """ + self.message_handlers.append(handler_dict) + + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + """ + Registers message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :param chat_types: True for private chat + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_message_handler(handler_dict) + + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): + """ + Edit message handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param chat_types: list of chat types + :param kwargs: + :return: + """ + + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_message_handler(handler_dict) + return handler + + return decorator + + def add_edited_message_handler(self, handler_dict): + """ + Adds the edit message handler + :param handler_dict: + :return: + """ + self.edited_message_handlers.append(handler_dict) + + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + """ + Registers edited message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :param chat_types: True for private chat + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_message_handler(handler_dict) + + + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Channel post handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param kwargs: + :return: + """ + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_channel_post_handler(handler_dict) + return handler + + return decorator + + def add_channel_post_handler(self, handler_dict): + """ + Adds channel post handler + :param handler_dict: + :return: + """ + self.channel_post_handlers.append(handler_dict) + + def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers channel post message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_channel_post_handler(handler_dict) + + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Edit channel post handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param kwargs: + :return: + """ + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_channel_post_handler(handler_dict) + return handler + + return decorator + + def add_edited_channel_post_handler(self, handler_dict): + """ + Adds the edit channel post handler + :param handler_dict: + :return: + """ + self.edited_channel_post_handlers.append(handler_dict) + + def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers edited channel post message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_channel_post_handler(handler_dict) + + def inline_handler(self, func, **kwargs): + """ + Inline call handler decorator + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_inline_handler(handler_dict) + return handler + + return decorator + + def add_inline_handler(self, handler_dict): + """ + Adds inline call handler + :param handler_dict: + :return: + """ + self.inline_handlers.append(handler_dict) + + def register_inline_handler(self, callback, func, **kwargs): + """ + Registers inline handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_inline_handler(handler_dict) + + def chosen_inline_handler(self, func, **kwargs): + """ + Description: TBD + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chosen_inline_handler(handler_dict) + return handler + + return decorator + + def add_chosen_inline_handler(self, handler_dict): + """ + Description: TBD + :param handler_dict: + :return: + """ + self.chosen_inline_handlers.append(handler_dict) + + def register_chosen_inline_handler(self, callback, func, **kwargs): + """ + Registers chosen inline handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_chosen_inline_handler(handler_dict) + + def callback_query_handler(self, func, **kwargs): + """ + Callback request handler decorator + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_callback_query_handler(handler_dict) + return handler + + return decorator + + def add_callback_query_handler(self, handler_dict): + """ + Adds a callback request handler + :param handler_dict: + :return: + """ + self.callback_query_handlers.append(handler_dict) + + def register_callback_query_handler(self, callback, func, **kwargs): + """ + Registers callback query handler.. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_callback_query_handler(handler_dict) + + def shipping_query_handler(self, func, **kwargs): + """ + Shipping request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_shipping_query_handler(handler_dict) + return handler + + return decorator + + def add_shipping_query_handler(self, handler_dict): + """ + Adds a shipping request handler + :param handler_dict: + :return: + """ + self.shipping_query_handlers.append(handler_dict) + + def register_shipping_query_handler(self, callback, func, **kwargs): + """ + Registers shipping query handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_shipping_query_handler(handler_dict) + + def pre_checkout_query_handler(self, func, **kwargs): + """ + Pre-checkout request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_pre_checkout_query_handler(handler_dict) + return handler + + return decorator + + def add_pre_checkout_query_handler(self, handler_dict): + """ + Adds a pre-checkout request handler + :param handler_dict: + :return: + """ + self.pre_checkout_query_handlers.append(handler_dict) + + def register_pre_checkout_query_handler(self, callback, func, **kwargs): + """ + Registers pre-checkout request handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_pre_checkout_query_handler(handler_dict) + + def poll_handler(self, func, **kwargs): + """ + Poll request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_poll_handler(handler_dict) + return handler + + return decorator + + def add_poll_handler(self, handler_dict): + """ + Adds a poll request handler + :param handler_dict: + :return: + """ + self.poll_handlers.append(handler_dict) + + def register_poll_handler(self, callback, func, **kwargs): + """ + Registers poll handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_poll_handler(handler_dict) + + def poll_answer_handler(self, func=None, **kwargs): + """ + Poll_answer request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_poll_answer_handler(handler_dict) + return handler + + return decorator + + def add_poll_answer_handler(self, handler_dict): + """ + Adds a poll_answer request handler + :param handler_dict: + :return: + """ + self.poll_answer_handlers.append(handler_dict) + + def register_poll_answer_handler(self, callback, func, **kwargs): + """ + Registers poll answer handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_poll_answer_handler(handler_dict) + + def my_chat_member_handler(self, func=None, **kwargs): + """ + my_chat_member handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_my_chat_member_handler(handler_dict) + return handler + + return decorator + + def add_my_chat_member_handler(self, handler_dict): + """ + Adds a my_chat_member handler + :param handler_dict: + :return: + """ + self.my_chat_member_handlers.append(handler_dict) + + def register_my_chat_member_handler(self, callback, func=None, **kwargs): + """ + Registers my chat member handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_my_chat_member_handler(handler_dict) + + def chat_member_handler(self, func=None, **kwargs): + """ + chat_member handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_member_handler(handler_dict) + return handler + + return decorator + + def add_chat_member_handler(self, handler_dict): + """ + Adds a chat_member handler + :param handler_dict: + :return: + """ + self.chat_member_handlers.append(handler_dict) + + def register_chat_member_handler(self, callback, func=None, **kwargs): + """ + Registers chat member handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_chat_member_handler(handler_dict) + + def chat_join_request_handler(self, func=None, **kwargs): + """ + chat_join_request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_join_request_handler(handler_dict) + return handler + + return decorator + + def add_chat_join_request_handler(self, handler_dict): + """ + Adds a chat_join_request handler + :param handler_dict: + :return: + """ + self.chat_join_request_handlers.append(handler_dict) + + def register_chat_join_request_handler(self, callback, func=None, **kwargs): + """ + Registers chat join request handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_chat_join_request_handler(handler_dict) + + @staticmethod + def _build_handler_dict(handler, **filters): + """ + Builds a dictionary for a handler + :param handler: + :param filters: + :return: + """ + return { + 'function': handler, + 'filters': {ftype: fvalue for ftype, fvalue in filters.items() if fvalue is not None} + # Remove None values, they are skipped in _test_filter anyway + #'filters': filters + } + + async def skip_updates(self): + await self.get_updates(-1) + return True + + # all methods begin here + + async def get_me(self) -> types.User: + """ + Returns basic information about the bot in form of a User object. + """ + result = await asyncio_helper.get_me(self.token) + return types.User.de_json(result) + + async def get_file(self, file_id: str) -> types.File: + """ + Use this method to get basic info about a file and prepare it for downloading. + For the moment, bots can download files of up to 20MB in size. + On success, a File object is returned. + It is guaranteed that the link will be valid for at least 1 hour. + When the link expires, a new one can be requested by calling get_file again. + """ + return types.File.de_json(await asyncio_helper.get_file(self.token, file_id)) + + async def get_file_url(self, file_id: str) -> str: + return await asyncio_helper.get_file_url(self.token, file_id) + + async def download_file(self, file_path: str) -> bytes: + return await asyncio_helper.download_file(self.token, file_path) + + async def log_out(self) -> bool: + """ + Use this method to log out from the cloud Bot API server before launching the bot locally. + You MUST log out the bot before running it locally, otherwise there is no guarantee + that the bot will receive updates. + After a successful call, you can immediately log in on a local server, + but will not be able to log in back to the cloud Bot API server for 10 minutes. + Returns True on success. + """ + return await asyncio_helper.log_out(self.token) + + async def close(self) -> bool: + """ + Use this method to close the bot instance before moving it from one local server to another. + You need to delete the webhook before calling this method to ensure that the bot isn't launched again + after server restart. + The method will return error 429 in the first 10 minutes after the bot is launched. + Returns True on success. + """ + return await asyncio_helper.close(self.token) + + async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, + drop_pending_updates = None, timeout=None): + """ + Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an + update for the bot, we will send an HTTPS POST request to the specified url, + containing a JSON-serialized Update. + In case of an unsuccessful request, we will give up after a reasonable amount of attempts. + Returns True on success. + + :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration + :param certificate: Upload your public key certificate so that the root certificate in use can be checked. + See our self-signed guide for details. + :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook + for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, + and higher values to increase your bot's throughput. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates + of these types. See Update for a complete list of available update types. + Specify an empty list to receive all updates regardless of type (default). + If not specified, the previous setting will be used. + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address + resolved through DNS + :param drop_pending_updates: Pass True to drop all pending updates + :param timeout: Integer. Request connection timeout + :return: + """ + return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, + drop_pending_updates, timeout) + + async def delete_webhook(self, drop_pending_updates=None, timeout=None): + """ + Use this method to remove webhook integration if you decide to switch back to getUpdates. + + :param drop_pending_updates: Pass True to drop all pending updates + :param timeout: Integer. Request connection timeout + :return: bool + """ + return await asyncio_helper.delete_webhook(self.token, drop_pending_updates, timeout) + + async def get_webhook_info(self, timeout=None): + """ + Use this method to get current webhook status. Requires no parameters. + If the bot is using getUpdates, will return an object with the url field empty. + + :param timeout: Integer. Request connection timeout + :return: On success, returns a WebhookInfo object. + """ + result = await asyncio_helper.get_webhook_info(self.token, timeout) + return types.WebhookInfo.de_json(result) + + async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, + limit: Optional[int]=None) -> types.UserProfilePhotos: + """ + Retrieves the user profile photos of the person with 'user_id' + See https://core.telegram.org/bots/api#getuserprofilephotos + :param user_id: + :param offset: + :param limit: + :return: API reply. + """ + 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: + """ + 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. + :param chat_id: + :return: + """ + result = await asyncio_helper.get_chat(self.token, chat_id) + return types.Chat.de_json(result) + + async def leave_chat(self, chat_id: Union[int, str]) -> bool: + """ + Use this method for your bot to leave a group, supergroup or channel. Returns True on success. + :param chat_id: + :return: + """ + result = await asyncio_helper.leave_chat(self.token, chat_id) + return result + + async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMember]: + """ + Use this method to get a list of administrators in a chat. + On success, returns an Array of ChatMember objects that contains + information about all chat administrators except other bots. + :param chat_id: Unique identifier for the target chat or username + of the target supergroup or channel (in the format @channelusername) + :return: + """ + result = await asyncio_helper.get_chat_administrators(self.token, chat_id) + return [types.ChatMember.de_json(r) for r in result] + + async def get_chat_members_count(self, chat_id: Union[int, str]) -> int: + """ + This function is deprecated. Use `get_chat_member_count` instead + """ + logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead.') + result = await asyncio_helper.get_chat_member_count(self.token, chat_id) + return result + + async def get_chat_member_count(self, chat_id: Union[int, str]) -> int: + """ + Use this method to get the number of members in a chat. Returns Int on success. + :param chat_id: + :return: + """ + result = await asyncio_helper.get_chat_member_count(self.token, chat_id) + return result + + async def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: + """ + Use this method to set a new group sticker set for a supergroup. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + Use the field can_set_sticker_set optionally returned in getChat requests to check + if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param sticker_set_name: Name of the sticker set to be set as the group sticker set + :return: + """ + result = await asyncio_helper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) + return result + + async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat + for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set + optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :return: + """ + result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) + return result + + async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: + """ + Use this method to get information about a member of a chat. Returns a ChatMember object on success. + :param chat_id: + :param user_id: + :return: + """ + result = await asyncio_helper.get_chat_member(self.token, chat_id, user_id) + return types.ChatMember.de_json(result) + + async def send_message( + self, chat_id: Union[int, str], text: str, + disable_web_page_preview: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send text messages. + + Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. + If you must send more than 4000 characters, + use the `split_string` or `smart_split` function in util.py. + + :param chat_id: + :param text: + :param disable_web_page_preview: + :param reply_to_message_id: + :param reply_markup: + :param parse_mode: + :param disable_notification: Boolean, Optional. Sends the message silently. + :param timeout: + :param entities: + :param allow_sending_without_reply: + :return: API reply. + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_message( + self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, + entities, allow_sending_without_reply)) + + async def forward_message( + self, chat_id: Union[int, str], from_chat_id: Union[int, str], + message_id: int, disable_notification: Optional[bool]=None, + timeout: Optional[int]=None) -> types.Message: + """ + Use this method to forward messages of any kind. + :param disable_notification: + :param chat_id: which chat to forward + :param from_chat_id: which chat message from + :param message_id: message id + :param timeout: + :return: API reply. + """ + return types.Message.de_json( + await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) + + async def copy_message( + self, chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None) -> int: + """ + Use this method to copy messages of any kind. + :param chat_id: which chat to forward + :param from_chat_id: which chat message from + :param message_id: message id + :param caption: + :param parse_mode: + :param caption_entities: + :param disable_notification: + :param reply_to_message_id: + :param allow_sending_without_reply: + :param reply_markup: + :param timeout: + :return: API reply. + """ + return types.MessageID.de_json( + await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, + disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, + timeout)) + + async def delete_message(self, chat_id: Union[int, str], message_id: int, + timeout: Optional[int]=None) -> bool: + """ + Use this method to delete message. Returns True on success. + :param chat_id: in which chat to delete + :param message_id: which message to delete + :param timeout: + :return: API reply. + """ + return await asyncio_helper.delete_message(self.token, chat_id, message_id, timeout) + + async def send_dice( + self, chat_id: Union[int, str], + emoji: Optional[str]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send dices. + :param chat_id: + :param emoji: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :return: Message + """ + return types.Message.de_json( + await asyncio_helper.send_dice( + self.token, chat_id, emoji, disable_notification, reply_to_message_id, + reply_markup, timeout, allow_sending_without_reply) + ) + + async def send_photo( + self, chat_id: Union[int, str], photo: Union[Any, str], + caption: Optional[str]=None, reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send photos. + :param chat_id: + :param photo: + :param caption: + :param parse_mode: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param caption_entities: + :param allow_sending_without_reply: + :return: API reply. + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_photo( + self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, + parse_mode, disable_notification, timeout, caption_entities, + allow_sending_without_reply)) + + async def send_audio( + self, chat_id: Union[int, str], audio: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, + performer: Optional[str]=None, title: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send audio files, if you want Telegram clients to display them in the music player. + Your audio must be in the .mp3 format. + :param chat_id:Unique identifier for the message recipient + :param audio:Audio file to send. + :param caption: + :param duration:Duration of the audio in seconds + :param performer:Performer + :param title:Track name + :param reply_to_message_id:If the message is a reply, ID of the original message + :param reply_markup: + :param parse_mode + :param disable_notification: + :param timeout: + :param thumb: + :param caption_entities: + :param allow_sending_without_reply: + :return: Message + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_audio( + self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, thumb, + caption_entities, allow_sending_without_reply)) + + async def send_voice( + self, chat_id: Union[int, str], voice: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send audio files, if you want Telegram clients to display the file + as a playable voice message. + :param chat_id:Unique identifier for the message recipient. + :param voice: + :param caption: + :param duration:Duration of sent audio in seconds + :param reply_to_message_id: + :param reply_markup: + :param parse_mode + :param disable_notification: + :param timeout: + :param caption_entities: + :param allow_sending_without_reply: + :return: Message + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_voice( + self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, + parse_mode, disable_notification, timeout, caption_entities, + allow_sending_without_reply)) + + async def send_document( + self, chat_id: Union[int, str], data: Union[Any, str], + reply_to_message_id: Optional[int]=None, + caption: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + 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, + disable_content_type_detection: Optional[bool]=None) -> types.Message: + """ + Use this method to send general files. + :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: 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; 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 async 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( + await asyncio_helper.send_data( + 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)) + + async def send_sticker( + self, chat_id: Union[int, str], data: Union[Any, str], + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send .webp stickers. + :param chat_id: + :param data: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: to disable the notification + :param timeout: timeout + :param allow_sending_without_reply: + :return: API reply. + """ + return types.Message.de_json( + await asyncio_helper.send_data( + 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)) + + async def send_video( + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, + caption: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + supports_streaming: Optional[bool]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + width: Optional[int]=None, + height: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send video files, Telegram clients support mp4 videos. + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend + a video that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param caption: String : Video caption (may also be used when resending videos by file_id). + :param parse_mode: + :param supports_streaming: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent + :param width: + :param height: + :param caption_entities: + :param allow_sending_without_reply: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_video( + self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, + parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, + caption_entities, allow_sending_without_reply)) + + async def send_animation( + self, chat_id: Union[int, str], animation: Union[Any, str], + duration: Optional[int]=None, + caption: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an + animation that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param caption: String : Animation caption (may also be used when resending animation by file_id). + :param parse_mode: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent + :param caption_entities: + :param allow_sending_without_reply: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_animation( + self.token, chat_id, animation, duration, caption, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, thumb, + caption_entities, allow_sending_without_reply)) + + async def send_video_note( + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, + length: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send + video messages. + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend + a video that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent + :param allow_sending_without_reply: + :return: + """ + return types.Message.de_json( + await asyncio_helper.send_video_note( + self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, + disable_notification, timeout, thumb, allow_sending_without_reply)) + + async def send_media_group( + self, chat_id: Union[int, str], + media: List[Union[ + types.InputMediaAudio, types.InputMediaDocument, + types.InputMediaPhoto, types.InputMediaVideo]], + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: + """ + send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + :param chat_id: + :param media: + :param disable_notification: + :param reply_to_message_id: + :param timeout: + :param allow_sending_without_reply: + :return: + """ + result = await asyncio_helper.send_media_group( + self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, + allow_sending_without_reply) + return [types.Message.de_json(msg) for msg in result] + + async def send_location( + self, chat_id: Union[int, str], + latitude: float, longitude: float, + live_period: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, + proximity_alert_radius: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + + + """ + Use this method to send point on the map. + :param chat_id: + :param latitude: + :param longitude: + :param live_period: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param horizontal_accuracy: + :param heading: + :param proximity_alert_radius: + :param allow_sending_without_reply: + :return: API reply. + """ + return types.Message.de_json( + await asyncio_helper.send_location( + self.token, chat_id, latitude, longitude, live_period, + reply_to_message_id, reply_markup, disable_notification, timeout, + horizontal_accuracy, heading, proximity_alert_radius, + allow_sending_without_reply)) + + async def edit_message_live_location( + self, latitude: float, longitude: float, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, + proximity_alert_radius: Optional[int]=None) -> types.Message: + """ + Use this method to edit live location + :param latitude: + :param longitude: + :param chat_id: + :param message_id: + :param reply_markup: + :param timeout: + :param inline_message_id: + :param horizontal_accuracy: + :param heading: + :param proximity_alert_radius: + :return: + """ + return types.Message.de_json( + 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)) + + async def stop_message_live_location( + self, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None) -> types.Message: + """ + Use this method to stop updating a live location message sent by the bot + or via the bot (for inline bots) before live_period expires + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :param timeout: + :return: + """ + return types.Message.de_json( + await asyncio_helper.stop_message_live_location( + self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) + + async def send_venue( + self, chat_id: Union[int, str], + latitude: float, longitude: float, + title: str, address: str, + foursquare_id: Optional[str]=None, + foursquare_type: Optional[str]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + google_place_id: Optional[str]=None, + google_place_type: Optional[str]=None) -> types.Message: + """ + Use this method to send information about a venue. + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel + :param latitude: Float : Latitude of the venue + :param longitude: Float : Longitude of the venue + :param title: String : Name of the venue + :param address: String : Address of the venue + :param foursquare_id: String : Foursquare identifier of the venue + :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/async default”, + “arts_entertainment/aquarium” or “food/icecream”.) + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :param google_place_id: + :param google_place_type: + :return: + """ + return types.Message.de_json( + await asyncio_helper.send_venue( + self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, + disable_notification, reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply, google_place_id, google_place_type) + ) + + async def send_contact( + self, chat_id: Union[int, str], phone_number: str, + first_name: str, last_name: Optional[str]=None, + vcard: Optional[str]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + return types.Message.de_json( + await asyncio_helper.send_contact( + self.token, chat_id, phone_number, first_name, last_name, vcard, + disable_notification, reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply) + ) + + async def send_chat_action( + self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: + """ + Use this method when you need to tell the user that something is happening on the bot's side. + The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear + its typing status). + :param chat_id: + :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', + 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', + 'upload_video_note'. + :param timeout: + :return: API reply. :type: boolean + """ + return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout) + + async def kick_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, + revoke_messages: Optional[bool]=None) -> bool: + """ + This function is deprecated. Use `ban_chat_member` instead + """ + logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') + return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + + async def ban_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, + revoke_messages: Optional[bool]=None) -> bool: + """ + Use this method to ban a user in a group, a supergroup or a channel. + In the case of supergroups and channels, the user will not be able to return to the chat on their + own using invite links, etc., unless unbanned first. + Returns True on success. + :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup + :param user_id: Int : Unique identifier of the target user + :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or + less than 30 seconds from the current time they are considered to be banned forever + :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. + If False, the user will be able to see messages in the group that were sent before the user was removed. + Always True for supergroups and channels. + :return: boolean + """ + return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + + async def unban_chat_member( + self, chat_id: Union[int, str], user_id: int, + only_if_banned: Optional[bool]=False) -> bool: + """ + Use this method to unban a previously kicked user in a supergroup or channel. + The user will not return to the group or channel automatically, but will be able to join via link, etc. + The bot must be an administrator for this to work. By async default, this method guarantees that after the call + the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat + they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. + + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel + (in the format @username) + :param user_id: Unique identifier of the target user + :param only_if_banned: Do nothing if the user is not banned + :return: True on success + """ + return await asyncio_helper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) + + async def restrict_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date: Optional[Union[int, datetime]]=None, + can_send_messages: Optional[bool]=None, + can_send_media_messages: Optional[bool]=None, + can_send_polls: Optional[bool]=None, + can_send_other_messages: Optional[bool]=None, + can_add_web_page_previews: Optional[bool]=None, + can_change_info: Optional[bool]=None, + can_invite_users: Optional[bool]=None, + can_pin_messages: Optional[bool]=None) -> bool: + """ + Use this method to restrict a user in a supergroup. + The bot must be an administrator in the supergroup for this to work and must have + the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. + + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup + or channel (in the format @channelusername) + :param user_id: Int : Unique identifier of the target user + :param until_date: Date when restrictions will be lifted for the user, unix time. + If user is restricted for more than 366 days or less than 30 seconds from the current time, + they are considered to be restricted forever + :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues + :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes + and voice notes, implies can_send_messages + :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages + :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and + use inline bots, implies can_send_media_messages + :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, + implies can_send_media_messages + :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. + Ignored in public supergroups + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, + implies can_invite_users + :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups + :return: True on success + """ + return await asyncio_helper.restrict_chat_member( + self.token, chat_id, user_id, until_date, + can_send_messages, can_send_media_messages, + can_send_polls, can_send_other_messages, + can_add_web_page_previews, can_change_info, + can_invite_users, can_pin_messages) + + async def promote_chat_member( + self, chat_id: Union[int, str], user_id: int, + can_change_info: Optional[bool]=None, + can_post_messages: Optional[bool]=None, + can_edit_messages: Optional[bool]=None, + can_delete_messages: Optional[bool]=None, + can_invite_users: Optional[bool]=None, + can_restrict_members: Optional[bool]=None, + can_pin_messages: Optional[bool]=None, + can_promote_members: Optional[bool]=None, + is_anonymous: Optional[bool]=None, + can_manage_chat: Optional[bool]=None, + can_manage_voice_chats: Optional[bool]=None) -> bool: + """ + Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + Pass False for all boolean parameters to demote a user. + + :param chat_id: Unique identifier for the target chat or username of the target channel ( + in the format @channelusername) + :param user_id: Int : Unique identifier of the target user + :param can_change_info: Bool: Pass True, if the administrator can change chat title, photo and other settings + :param can_post_messages: Bool : Pass True, if the administrator can create channel posts, channels only + :param can_edit_messages: Bool : Pass True, if the administrator can edit messages of other users, channels only + :param can_delete_messages: Bool : Pass True, if the administrator can delete messages of other users + :param can_invite_users: Bool : Pass True, if the administrator can invite new users to the chat + :param can_restrict_members: Bool: Pass True, if the administrator can restrict, ban or unban chat members + :param can_pin_messages: Bool: Pass True, if the administrator can pin messages, supergroups only + :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset + of his own privileges or demote administrators that he has promoted, directly or indirectly + (promoted by administrators that were appointed by him) + :param is_anonymous: Bool: Pass True, if the administrator's presence in the chat is hidden + :param can_manage_chat: Bool: Pass True, if the administrator can access the chat event log, chat statistics, + message statistics in channels, see channel members, + see anonymous administrators in supergroups and ignore slow mode. + Implied by any other administrator privilege + :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats + For now, bots can use this privilege only for passing to other administrators. + :return: True on success. + """ + return await asyncio_helper.promote_chat_member( + self.token, chat_id, user_id, can_change_info, can_post_messages, + can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members, + is_anonymous, can_manage_chat, can_manage_voice_chats) + + async def set_chat_administrator_custom_title( + self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: + """ + Use this method to set a custom title for an administrator + in a supergroup promoted by the bot. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :param custom_title: New custom title for the administrator; + 0-16 characters, emoji are not allowed + :return: True on success. + """ + return await asyncio_helper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) + + async def set_chat_permissions( + self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: + """ + Use this method to set async default chat permissions for all members. + The bot must be an administrator in the group or a supergroup for this to work + and must have the can_restrict_members admin rights. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param permissions: New async default chat permissions + :return: True on success + """ + return await asyncio_helper.set_chat_permissions(self.token, chat_id, permissions) + + async def create_chat_invite_link( + self, chat_id: Union[int, str], + name: Optional[str]=None, + expire_date: Optional[Union[int, datetime]]=None, + member_limit: Optional[int]=None, + creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: + """ + Use this method to create an additional invite link for a chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param name: Invite link name; 0-32 characters + :param expire_date: Point in time (Unix timestamp) when the link will expire + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified + :return: + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request) + ) + + async def edit_chat_invite_link( + self, chat_id: Union[int, str], + invite_link: Optional[str] = None, + name: Optional[str]=None, + expire_date: Optional[Union[int, datetime]]=None, + member_limit: Optional[int]=None, + creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: + """ + Use this method to edit a non-primary invite link created by the bot. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param name: Invite link name; 0-32 characters + :param invite_link: The invite link to edit + :param expire_date: Point in time (Unix timestamp) when the link will expire + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified + :return: + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) + ) + + async def revoke_chat_invite_link( + self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: + """ + Use this method to revoke an invite link created by the bot. + Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param invite_link: The invite link to revoke + :return: + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.revoke_chat_invite_link(self.token, chat_id, invite_link) + ) + + async def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: + """ + Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: exported invite link as String on success. + """ + return await asyncio_helper.export_chat_invite_link(self.token, chat_id) + + + async def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: + """ + Use this method to approve a chat join request. + The bot must be an administrator in the chat for this to work and must have + the can_invite_users administrator right. Returns True on success. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :return: True on success. + """ + return await asyncio_helper.approve_chat_join_request(self.token, chat_id, user_id) + + async def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: + """ + Use this method to decline a chat join request. + The bot must be an administrator in the chat for this to work and must have + the can_invite_users administrator right. Returns True on success. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :return: True on success. + """ + return await asyncio_helper.decline_chat_join_request(self.token, chat_id, user_id) + + async def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: + """ + Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param photo: InputFile: New chat photo, uploaded using multipart/form-data + :return: + """ + return await asyncio_helper.set_chat_photo(self.token, chat_id, photo) + + async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to delete a chat photo. Photos can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + """ + return await asyncio_helper.delete_chat_photo(self.token, chat_id) + + async def get_my_commands(self, scope: Optional[types.BotCommandScope], + language_code: Optional[str]) -> List[types.BotCommand]: + """ + Use this method to get the current list of the bot's commands. + Returns List of BotCommand on success. + :param scope: The scope of users for which the commands are relevant. + async defaults to BotCommandScopeasync default. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands + """ + result = await asyncio_helper.get_my_commands(self.token, scope, language_code) + return [types.BotCommand.de_json(cmd) for cmd in result] + + async def set_my_commands(self, commands: List[types.BotCommand], + scope: Optional[types.BotCommandScope]=None, + language_code: Optional[str]=None) -> bool: + """ + Use this method to change the list of the bot's commands. + :param commands: List of BotCommand. At most 100 commands can be specified. + :param scope: The scope of users for which the commands are relevant. + async defaults to BotCommandScopeasync default. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands + :return: + """ + return await asyncio_helper.set_my_commands(self.token, commands, scope, language_code) + + async def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, + language_code: Optional[int]=None) -> bool: + """ + Use this method to delete the list of the bot's commands for the given scope and user language. + After deletion, higher level commands will be shown to affected users. + Returns True on success. + :param scope: The scope of users for which the commands are relevant. + async defaults to BotCommandScopeasync default. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands + """ + return await asyncio_helper.delete_my_commands(self.token, scope, language_code) + + async def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: + """ + Use this method to change the title of a chat. Titles can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param title: New chat title, 1-255 characters + :return: + """ + return await asyncio_helper.set_chat_title(self.token, chat_id, title) + + async def set_chat_description(self, chat_id: Union[int, str], description: Optional[str]=None) -> bool: + """ + Use this method to change the description of a supergroup or a channel. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param description: Str: New chat description, 0-255 characters + :return: True on success. + """ + return await asyncio_helper.set_chat_description(self.token, chat_id, description) + + async def pin_chat_message( + self, chat_id: Union[int, str], message_id: int, + disable_notification: Optional[bool]=False) -> bool: + """ + Use this method to pin a message in a supergroup. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param message_id: Int: Identifier of a message to pin + :param disable_notification: Bool: Pass True, if it is not necessary to send a notification + to all group members about the new pinned message + :return: + """ + return await asyncio_helper.pin_chat_message(self.token, chat_id, message_id, disable_notification) + + async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: + """ + Use this method to unpin specific pinned message in a supergroup chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param message_id: Int: Identifier of a message to unpin + :return: + """ + return await asyncio_helper.unpin_chat_message(self.token, chat_id, message_id) + + async def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to unpin a all pinned messages in a supergroup chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: + """ + return await asyncio_helper.unpin_all_chat_messages(self.token, chat_id) + + async def edit_message_text( + self, text: str, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + parse_mode: Optional[str]=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. + :param text: + :param chat_id: + :param message_id: + :param inline_message_id: + :param parse_mode: + :param entities: + :param disable_web_page_preview: + :param reply_markup: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, + 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) + + async def edit_message_media( + self, media: Any, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + """ + Use this method to edit animation, audio, document, photo, or video messages. + If a message is a part of a message album, then it can be edited only to a photo or a video. + Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. + Use previously uploaded file via its file_id or specify a URL. + :param media: + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ + result = await asyncio_helper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) + if type(result) == bool: # if edit inline message return is bool not Message. + return result + return types.Message.de_json(result) + + async def edit_message_reply_markup( + self, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + """ + Use this method to edit only the reply markup of messages. + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ + result = await asyncio_helper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) + if type(result) == bool: + return result + return types.Message.de_json(result) + + async def send_game( + self, chat_id: Union[int, str], game_short_name: str, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Used to send the game + :param chat_id: + :param game_short_name: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :return: + """ + result = await asyncio_helper.send_game( + self.token, chat_id, game_short_name, disable_notification, + reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply) + return types.Message.de_json(result) + + async def set_game_score( + self, user_id: Union[int, str], score: int, + force: Optional[bool]=None, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: + """ + Sets the value of points in the game to a specific user + :param user_id: + :param score: + :param force: + :param chat_id: + :param message_id: + :param inline_message_id: + :param disable_edit_message: + :return: + """ + result = await asyncio_helper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, + message_id, inline_message_id) + if type(result) == bool: + return result + return types.Message.de_json(result) + + async def get_game_high_scores( + self, user_id: int, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: + """ + Gets top points and game play + :param user_id: + :param chat_id: + :param message_id: + :param inline_message_id: + :return: + """ + result = await asyncio_helper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) + return [types.GameHighScore.de_json(r) for r in result] + + async def send_invoice( + self, chat_id: Union[int, str], title: str, description: str, + invoice_payload: str, provider_token: str, currency: str, + prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, + photo_url: Optional[str]=None, photo_size: Optional[int]=None, + photo_width: Optional[int]=None, photo_height: Optional[int]=None, + need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, + need_email: Optional[bool]=None, need_shipping_address: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, + is_flexible: Optional[bool]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + provider_data: Optional[str]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[List[int]]=None) -> types.Message: + """ + Sends invoice + :param chat_id: Unique identifier for the target private chat + :param title: Product name + :param description: Product description + :param invoice_payload: Bot-async defined invoice payload, 1-128 bytes. This will not be displayed to the user, + use for your internal processes. + :param provider_token: Payments provider token, obtained via @Botfather + :param currency: Three-letter ISO 4217 currency code, + see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components + (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice + when used as a start parameter + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods + or a marketing image for a service. People like it better when they see what they are paying for. + :param photo_size: Photo size + :param photo_width: Photo width + :param photo_height: Photo height + :param need_name: Pass True, if you require the user's full name to complete the order + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :param need_email: Pass True, if you require the user's email to complete the order + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :param is_flexible: Pass True, if the final price depends on the shipping method + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_markup: 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 + :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 allow_sending_without_reply: + :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. + :return: + """ + result = await asyncio_helper.send_invoice( + self.token, chat_id, title, description, invoice_payload, provider_token, + currency, prices, start_parameter, photo_url, photo_size, photo_width, + photo_height, need_name, need_phone_number, need_email, need_shipping_address, + send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, + reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, + max_tip_amount, suggested_tip_amounts) + return types.Message.de_json(result) + + # noinspection PyShadowingBuiltins + async def send_poll( + self, chat_id: Union[int, str], question: str, options: List[str], + is_anonymous: Optional[bool]=None, type: Optional[str]=None, + allows_multiple_answers: Optional[bool]=None, + correct_option_id: Optional[int]=None, + explanation: Optional[str]=None, + explanation_parse_mode: Optional[str]=None, + open_period: Optional[int]=None, + close_date: Optional[Union[int, datetime]]=None, + is_closed: Optional[bool]=None, + disable_notification: Optional[bool]=False, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + allow_sending_without_reply: Optional[bool]=None, + timeout: Optional[int]=None, + explanation_entities: Optional[List[types.MessageEntity]]=None) -> types.Message: + """ + Send polls + :param chat_id: + :param question: + :param options: array of str with answers + :param is_anonymous: + :param type: + :param allows_multiple_answers: + :param correct_option_id: + :param explanation: + :param explanation_parse_mode: + :param open_period: + :param close_date: + :param is_closed: + :param disable_notification: + :param reply_to_message_id: + :param allow_sending_without_reply: + :param reply_markup: + :param timeout: + :param explanation_entities: + :return: + """ - @util.async_dec() - def set_game_score(self, *args, **kwargs): - return TeleBot.set_game_score(self, *args, **kwargs) + if isinstance(question, types.Poll): + raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") - @util.async_dec() - def get_game_high_scores(self, *args, **kwargs): - return TeleBot.get_game_high_scores(self, *args, **kwargs) + return types.Message.de_json( + await asyncio_helper.send_poll( + self.token, chat_id, + question, options, + is_anonymous, type, allows_multiple_answers, correct_option_id, + explanation, explanation_parse_mode, open_period, close_date, is_closed, + disable_notification, reply_to_message_id, allow_sending_without_reply, + reply_markup, timeout, explanation_entities)) - @util.async_dec() - def send_invoice(self, *args, **kwargs): - return TeleBot.send_invoice(self, *args, **kwargs) + async def stop_poll( + self, chat_id: Union[int, str], message_id: int, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: + """ + Stops poll + :param chat_id: + :param message_id: + :param reply_markup: + :return: + """ + return types.Poll.de_json(await asyncio_helper.stop_poll(self.token, chat_id, message_id, reply_markup)) - @util.async_dec() - def answer_shipping_query(self, *args, **kwargs): - return TeleBot.answer_shipping_query(self, *args, **kwargs) + async def answer_shipping_query( + self, shipping_query_id: str, ok: bool, + shipping_options: Optional[List[types.ShippingOption]]=None, + error_message: Optional[str]=None) -> bool: + """ + Asks for an answer to a shipping question + :param shipping_query_id: + :param ok: + :param shipping_options: + :param error_message: + :return: + """ + return await asyncio_helper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) - @util.async_dec() - def answer_pre_checkout_query(self, *args, **kwargs): - return TeleBot.answer_pre_checkout_query(self, *args, **kwargs) + async def answer_pre_checkout_query( + self, pre_checkout_query_id: int, ok: bool, + error_message: Optional[str]=None) -> bool: + """ + Response to a request for pre-inspection + :param pre_checkout_query_id: + :param ok: + :param error_message: + :return: + """ + return await asyncio_helper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) - @util.async_dec() - def edit_message_caption(self, *args, **kwargs): - return TeleBot.edit_message_caption(self, *args, **kwargs) + async def edit_message_caption( + self, caption: str, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + """ + Use this method to edit captions of messages + :param caption: + :param chat_id: + :param message_id: + :param inline_message_id: + :param parse_mode: + :param caption_entities: + :param reply_markup: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - @util.async_dec() - def answer_inline_query(self, *args, **kwargs): - return TeleBot.answer_inline_query(self, *args, **kwargs) + result = await asyncio_helper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, + parse_mode, caption_entities, reply_markup) + if type(result) == bool: + return result + return types.Message.de_json(result) - @util.async_dec() - def answer_callback_query(self, *args, **kwargs): - return TeleBot.answer_callback_query(self, *args, **kwargs) + async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: + """ + Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` + :param message: + :param text: + :param kwargs: + :return: + """ + return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) - @util.async_dec() - def get_sticker_set(self, *args, **kwargs): - return TeleBot.get_sticker_set(self, *args, **kwargs) + async def answer_inline_query( + self, inline_query_id: str, + results: List[Any], + cache_time: Optional[int]=None, + is_personal: Optional[bool]=None, + next_offset: Optional[str]=None, + switch_pm_text: Optional[str]=None, + switch_pm_parameter: Optional[str]=None) -> bool: + """ + Use this method to send answers to an inline query. On success, True is returned. + No more than 50 results per query are allowed. + :param inline_query_id: Unique identifier for the answered query + :param results: Array of results for the inline query + :param cache_time: The maximum amount of time in seconds that the result of the inline query + may be cached on the server. + :param is_personal: Pass True, if results may be cached on the server side only for + the user that sent the query. + :param next_offset: Pass the offset that a client should send in the next query with the same text + to receive more results. + :param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user + to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter + :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button + :return: True means success. + """ + return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, + switch_pm_text, switch_pm_parameter) - @util.async_dec() - def upload_sticker_file(self, *args, **kwargs): - return TeleBot.upload_sticker_file(self, *args, **kwargs) + async def answer_callback_query( + self, callback_query_id: int, + text: Optional[str]=None, show_alert: Optional[bool]=None, + url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: + """ + Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to + the user as a notification at the top of the chat screen or as an alert. + :param callback_query_id: + :param text: + :param show_alert: + :param url: + :param cache_time: + :return: + """ + return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - @util.async_dec() - def create_new_sticker_set(self, *args, **kwargs): - return TeleBot.create_new_sticker_set(self, *args, **kwargs) + async def set_sticker_set_thumb( + self, name: str, user_id: int, thumb: Union[Any, str]=None): + """ + Use this method to set the thumbnail of a sticker set. + Animated thumbnails can be set for animated sticker sets only. Returns True on success. + """ + return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) - @util.async_dec() - def add_sticker_to_set(self, *args, **kwargs): - return TeleBot.add_sticker_to_set(self, *args, **kwargs) + async def get_sticker_set(self, name: str) -> types.StickerSet: + """ + Use this method to get a sticker set. On success, a StickerSet object is returned. + :param name: + :return: + """ + result = await asyncio_helper.get_sticker_set(self.token, name) + return types.StickerSet.de_json(result) - @util.async_dec() - def set_sticker_position_in_set(self, *args, **kwargs): - return TeleBot.set_sticker_position_in_set(self, *args, **kwargs) + async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: + """ + Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet + methods (can be used multiple times). Returns the uploaded File on success. + :param user_id: + :param png_sticker: + :return: + """ + result = await asyncio_helper.upload_sticker_file(self.token, user_id, png_sticker) + return types.File.de_json(result) - @util.async_dec() - def delete_sticker_from_set(self, *args, **kwargs): - return TeleBot.delete_sticker_from_set(self, *args, **kwargs) - - @util.async_dec() - def set_sticker_set_thumb(self, *args, **kwargs): - return TeleBot.set_sticker_set_thumb(self, *args, **kwargs) + async def create_new_sticker_set( + self, user_id: int, name: str, title: str, + emojis: str, + png_sticker: Union[Any, str], + tgs_sticker: Union[Any, str], + contains_masks: Optional[bool]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to create new sticker set owned by a user. + The bot will be able to edit the created sticker set. + Returns True on success. + :param user_id: + :param name: + :param title: + :param emojis: + :param png_sticker: + :param tgs_sticker: + :param contains_masks: + :param mask_position: + :return: + """ + return await asyncio_helper.create_new_sticker_set( + self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, + contains_masks, mask_position) + + + async def add_sticker_to_set( + self, user_id: int, name: str, emojis: str, + png_sticker: Optional[Union[Any, str]]=None, + tgs_sticker: Optional[Union[Any, str]]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to add a new sticker to a set created by the bot. + It's required to pass `png_sticker` or `tgs_sticker`. + Returns True on success. + :param user_id: + :param name: + :param emojis: + :param png_sticker: Required if `tgs_sticker` is None + :param tgs_sticker: Required if `png_sticker` is None + :param mask_position: + :return: + """ + return await asyncio_helper.add_sticker_to_set( + self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position) + + + async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: + """ + Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. + :param sticker: + :param position: + :return: + """ + return await asyncio_helper.set_sticker_position_in_set(self.token, sticker, position) + + async def delete_sticker_from_set(self, sticker: str) -> bool: + """ + Use this method to delete a sticker from a set created by the bot. Returns True on success. + :param sticker: + :return: + """ + return await asyncio_helper.delete_sticker_from_set(self.token, sticker) + + async def register_for_reply( + self, message: types.Message, callback: Callable, *args, **kwargs) -> None: + """ + Registers a callback function to be notified when a reply to `message` arrives. + + Warning: In case `callback` as lambda function, saving reply handlers will not work. + + :param message: The message for which we are awaiting a reply. + :param callback: The callback function to be called when a reply arrives. Must accept one `message` + parameter, which will contain the replied message. + """ + message_id = message.message_id + self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs) + + async def register_for_reply_by_message_id( + self, message_id: int, callback: Callable, *args, **kwargs) -> None: + """ + Registers a callback function to be notified when a reply to `message` arrives. + + Warning: In case `callback` as lambda function, saving reply handlers will not work. + + :param message_id: The id of the message for which we are awaiting a reply. + :param callback: The callback function to be called when a reply arrives. Must accept one `message` + parameter, which will contain the replied message. + """ + self.reply_backend.register_handler(message_id, Handler(callback, *args, **kwargs)) + + async def _notify_reply_handlers(self, new_messages) -> None: + """ + Notify handlers of the answers + :param new_messages: + :return: + """ + for message in new_messages: + if hasattr(message, "reply_to_message") and message.reply_to_message is not None: + handlers = self.reply_backend.get_handlers(message.reply_to_message.message_id) + if handlers: + for handler in handlers: + self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + + async def register_next_step_handler( + self, message: types.Message, callback: Callable, *args, **kwargs) -> None: + """ + Registers a callback function to be notified when new message arrives after `message`. + + Warning: In case `callback` as lambda function, saving next step handlers will not work. + + :param message: The message for which we want to handle new message in the same chat. + :param callback: The callback function which next new message arrives. + :param args: Args to pass in callback func + :param kwargs: Args to pass in callback func + """ + chat_id = message.chat.id + self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) + + async def set_state(self, chat_id, state): + """ + Sets a new state of a user. + :param chat_id: + :param state: new state. can be string or integer. + """ + await self.current_states.add_state(chat_id, state) + + async def delete_state(self, chat_id): + """ + Delete the current state of a user. + :param chat_id: + :return: + """ + await self.current_states.delete_state(chat_id) + + def retrieve_data(self, chat_id): + return self.current_states.retrieve_data(chat_id) + + async def get_state(self, chat_id): + """ + Get current state of a user. + :param chat_id: + :return: state of a user + """ + return await self.current_states.current_state(chat_id) - @util.async_dec() - def send_poll(self, *args, **kwargs): - return TeleBot.send_poll(self, *args, **kwargs) + async def add_data(self, chat_id, **kwargs): + """ + Add data to states. + :param chat_id: + """ + for key, value in kwargs.items(): + await self.current_states._add_data(chat_id, key, value) - @util.async_dec() - def stop_poll(self, *args, **kwargs): - return TeleBot.stop_poll(self, *args, **kwargs) + \ No newline at end of file diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py new file mode 100644 index 000000000..c8242fef2 --- /dev/null +++ b/telebot/asyncio_filters.py @@ -0,0 +1,176 @@ +from abc import ABC + +class SimpleCustomFilter(ABC): + """ + Simple Custom Filter base class. + Create child class with check() method. + Accepts only message, returns bool value, that is compared with given in handler. + """ + + async def check(self, message): + """ + Perform a check. + """ + pass + + +class AdvancedCustomFilter(ABC): + """ + Simple Custom Filter base class. + Create child class with check() method. + Accepts two parameters, returns bool: True - filter passed, False - filter failed. + message: Message class + text: Filter value given in handler + """ + + async def check(self, message, text): + """ + Perform a check. + """ + pass + + +class TextMatchFilter(AdvancedCustomFilter): + """ + Filter to check Text message. + key: text + + Example: + @bot.message_handler(text=['account']) + """ + + key = 'text' + + async def check(self, message, text): + if type(text) is list:return message.text in text + else: return text == message.text + +class TextContainsFilter(AdvancedCustomFilter): + """ + Filter to check Text message. + key: text + + Example: + # Will respond if any message.text contains word 'account' + @bot.message_handler(text_contains=['account']) + """ + + key = 'text_contains' + + async def check(self, message, text): + return text in message.text + +class TextStartsFilter(AdvancedCustomFilter): + """ + Filter to check whether message starts with some text. + + Example: + # Will work if message.text starts with 'Sir'. + @bot.message_handler(text_startswith='Sir') + """ + + key = 'text_startswith' + async def check(self, message, text): + return message.text.startswith(text) + +class ChatFilter(AdvancedCustomFilter): + """ + Check whether chat_id corresponds to given chat_id. + + Example: + @bot.message_handler(chat_id=[99999]) + """ + + key = 'chat_id' + async def check(self, message, text): + return message.chat.id in text + +class ForwardFilter(SimpleCustomFilter): + """ + Check whether message was forwarded from channel or group. + + Example: + + @bot.message_handler(is_forwarded=True) + """ + + key = 'is_forwarded' + + async def check(self, message): + return message.forward_from_chat is not None + +class IsReplyFilter(SimpleCustomFilter): + """ + Check whether message is a reply. + + Example: + + @bot.message_handler(is_reply=True) + """ + + key = 'is_reply' + + async def check(self, message): + return message.reply_to_message is not None + + + +class LanguageFilter(AdvancedCustomFilter): + """ + Check users language_code. + + Example: + + @bot.message_handler(language_code=['ru']) + """ + + key = 'language_code' + + async def check(self, message, text): + if type(text) is list:return message.from_user.language_code in text + else: return message.from_user.language_code == text + +class IsAdminFilter(SimpleCustomFilter): + """ + Check whether the user is administrator / owner of the chat. + + Example: + @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) + """ + + key = 'is_chat_admin' + + def __init__(self, bot): + self._bot = bot + + async def check(self, message): + return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] + +class StateFilter(AdvancedCustomFilter): + """ + Filter to check state. + + Example: + @bot.message_handler(state=1) + """ + def __init__(self, bot): + self.bot = bot + key = 'state' + + async def check(self, message, text): + if await self.bot.current_states.current_state(message.from_user.id) is False: return False + elif text == '*': return True + elif type(text) is list: return await self.bot.current_states.current_state(message.from_user.id) in text + return await self.bot.current_states.current_state(message.from_user.id) == text + +class IsDigitFilter(SimpleCustomFilter): + """ + Filter to check whether the string is made up of only digits. + + Example: + @bot.message_handler(is_digit=True) + """ + key = 'is_digit' + + async def check(self, message): + return message.text.isdigit() diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py new file mode 100644 index 000000000..001f869b2 --- /dev/null +++ b/telebot/asyncio_handler_backends.py @@ -0,0 +1,343 @@ +import os +import pickle +import threading + +from telebot import apihelper + + +class HandlerBackend(object): + """ + Class for saving (next step|reply) handlers + """ + def __init__(self, handlers=None): + if handlers is None: + handlers = {} + self.handlers = handlers + + async def register_handler(self, handler_group_id, handler): + raise NotImplementedError() + + async def clear_handlers(self, handler_group_id): + raise NotImplementedError() + + async def get_handlers(self, handler_group_id): + raise NotImplementedError() + + +class MemoryHandlerBackend(HandlerBackend): + async def register_handler(self, handler_group_id, handler): + if handler_group_id in self.handlers: + self.handlers[handler_group_id].append(handler) + else: + self.handlers[handler_group_id] = [handler] + + async def clear_handlers(self, handler_group_id): + self.handlers.pop(handler_group_id, None) + + async def get_handlers(self, handler_group_id): + return self.handlers.pop(handler_group_id, None) + + async def load_handlers(self, filename, del_file_after_loading): + raise NotImplementedError() + + +class FileHandlerBackend(HandlerBackend): + def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120): + super(FileHandlerBackend, self).__init__(handlers) + self.filename = filename + self.delay = delay + self.timer = threading.Timer(delay, self.save_handlers) + + async def register_handler(self, handler_group_id, handler): + if handler_group_id in self.handlers: + self.handlers[handler_group_id].append(handler) + else: + self.handlers[handler_group_id] = [handler] + await self.start_save_timer() + + async def clear_handlers(self, handler_group_id): + self.handlers.pop(handler_group_id, None) + await self.start_save_timer() + + async def get_handlers(self, handler_group_id): + handlers = self.handlers.pop(handler_group_id, None) + await self.start_save_timer() + return handlers + + async def start_save_timer(self): + if not self.timer.is_alive(): + if self.delay <= 0: + self.save_handlers() + else: + self.timer = threading.Timer(self.delay, self.save_handlers) + self.timer.start() + + async def save_handlers(self): + await self.dump_handlers(self.handlers, self.filename) + + async def load_handlers(self, filename=None, del_file_after_loading=True): + if not filename: + filename = self.filename + tmp = await self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) + if tmp is not None: + self.handlers.update(tmp) + + @staticmethod + async def dump_handlers(handlers, filename, file_mode="wb"): + dirs = filename.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + + with open(filename + ".tmp", file_mode) as file: + if (apihelper.CUSTOM_SERIALIZER is None): + pickle.dump(handlers, file) + else: + apihelper.CUSTOM_SERIALIZER.dump(handlers, file) + + if os.path.isfile(filename): + os.remove(filename) + + os.rename(filename + ".tmp", filename) + + @staticmethod + async def return_load_handlers(filename, del_file_after_loading=True): + if os.path.isfile(filename) and os.path.getsize(filename) > 0: + with open(filename, "rb") as file: + if (apihelper.CUSTOM_SERIALIZER is None): + handlers = pickle.load(file) + else: + handlers = apihelper.CUSTOM_SERIALIZER.load(file) + + if del_file_after_loading: + os.remove(filename) + + return handlers + + +class RedisHandlerBackend(HandlerBackend): + def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot', password=None): + super(RedisHandlerBackend, self).__init__(handlers) + from redis import Redis + self.prefix = prefix + self.redis = Redis(host, port, db, password) + + async def _key(self, handle_group_id): + return ':'.join((self.prefix, str(handle_group_id))) + + async def register_handler(self, handler_group_id, handler): + handlers = [] + value = self.redis.get(self._key(handler_group_id)) + if value: + handlers = pickle.loads(value) + handlers.append(handler) + self.redis.set(self._key(handler_group_id), pickle.dumps(handlers)) + + async def clear_handlers(self, handler_group_id): + self.redis.delete(self._key(handler_group_id)) + + async def get_handlers(self, handler_group_id): + handlers = None + value = self.redis.get(self._key(handler_group_id)) + if value: + handlers = pickle.loads(value) + self.clear_handlers(handler_group_id) + return handlers + + +class StateMemory: + def __init__(self): + self._states = {} + + async def add_state(self, chat_id, state): + """ + Add a state. + :param chat_id: + :param state: new state + """ + if chat_id in self._states: + + self._states[chat_id]['state'] = state + else: + self._states[chat_id] = {'state': state,'data': {}} + + async def current_state(self, chat_id): + """Current state""" + if chat_id in self._states: return self._states[chat_id]['state'] + else: return False + + async def delete_state(self, chat_id): + """Delete a state""" + self._states.pop(chat_id) + + def _get_data(self, chat_id): + return self._states[chat_id]['data'] + + async def set(self, chat_id, new_state): + """ + Set a new state for a user. + :param chat_id: + :param new_state: new_state of a user + """ + await self.add_state(chat_id,new_state) + + async def _add_data(self, chat_id, key, value): + result = self._states[chat_id]['data'][key] = value + return result + + async def finish(self, chat_id): + """ + Finish(delete) state of a user. + :param chat_id: + """ + await self.delete_state(chat_id) + + def retrieve_data(self, chat_id): + """ + Save input text. + + Usage: + with bot.retrieve_data(message.chat.id) as data: + data['name'] = message.text + + Also, at the end of your 'Form' you can get the name: + data['name'] + """ + return StateContext(self, chat_id) + + +class StateFile: + """ + Class to save states in a file. + """ + def __init__(self, filename): + self.file_path = filename + + async def add_state(self, chat_id, state): + """ + Add a state. + :param chat_id: + :param state: new state + """ + states_data = self._read_data() + if chat_id in states_data: + states_data[chat_id]['state'] = state + return await self._save_data(states_data) + else: + new_data = states_data[chat_id] = {'state': state,'data': {}} + return await self._save_data(states_data) + + + async def current_state(self, chat_id): + """Current state.""" + states_data = self._read_data() + if chat_id in states_data: return states_data[chat_id]['state'] + else: return False + + async def delete_state(self, chat_id): + """Delete a state""" + states_data = await self._read_data() + states_data.pop(chat_id) + await self._save_data(states_data) + + async def _read_data(self): + """ + Read the data from file. + """ + file = open(self.file_path, 'rb') + states_data = pickle.load(file) + file.close() + return states_data + + async def _create_dir(self): + """ + Create directory .save-handlers. + """ + dirs = self.file_path.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + if not os.path.isfile(self.file_path): + with open(self.file_path,'wb') as file: + pickle.dump({}, file) + + async def _save_data(self, new_data): + """ + Save data after editing. + :param new_data: + """ + with open(self.file_path, 'wb+') as state_file: + pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL) + return True + + def _get_data(self, chat_id): + return self._read_data()[chat_id]['data'] + + async def set(self, chat_id, new_state): + """ + Set a new state for a user. + :param chat_id: + :param new_state: new_state of a user + + """ + await self.add_state(chat_id,new_state) + + async def _add_data(self, chat_id, key, value): + states_data = self._read_data() + result = states_data[chat_id]['data'][key] = value + await self._save_data(result) + + return result + + async def finish(self, chat_id): + """ + Finish(delete) state of a user. + :param chat_id: + """ + await self.delete_state(chat_id) + + async def retrieve_data(self, chat_id): + """ + Save input text. + + Usage: + with bot.retrieve_data(message.chat.id) as data: + data['name'] = message.text + + Also, at the end of your 'Form' you can get the name: + data['name'] + """ + return StateFileContext(self, chat_id) + + +class StateContext: + """ + Class for data. + """ + def __init__(self , obj: StateMemory, chat_id) -> None: + self.obj = obj + self.chat_id = chat_id + self.data = obj._get_data(chat_id) + + async def __aenter__(self): + return self.data + + async def __aexit__(self, exc_type, exc_val, exc_tb): + return + +class StateFileContext: + """ + Class for data. + """ + def __init__(self , obj: StateFile, chat_id) -> None: + self.obj = obj + self.chat_id = chat_id + self.data = None + + async def __aenter__(self): + self.data = self.obj._get_data(self.chat_id) + return self.data + + async def __aexit__(self, exc_type, exc_val, exc_tb): + old_data = await self.obj._read_data() + for i in self.data: + old_data[self.chat_id]['data'][i] = self.data.get(i) + await self.obj._save_data(old_data) + + return diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py new file mode 100644 index 000000000..7bb649e8b --- /dev/null +++ b/telebot/asyncio_helper.py @@ -0,0 +1,1607 @@ +import asyncio +from time import time +import aiohttp +from telebot import types +import json +import logging + +try: + import ujson as json +except ImportError: + import json + +import requests +from requests.exceptions import HTTPError, ConnectionError, Timeout + +try: + # noinspection PyUnresolvedReferences + from requests.packages.urllib3 import fields + format_header_param = fields.format_header_param +except ImportError: + format_header_param = None + +API_URL = 'https://api.telegram.org/bot{0}/{1}' + +from datetime import datetime +from telebot import util + +class SessionBase: + def __init__(self) -> None: + self.session = None + async def _get_new_session(self): + self.session = aiohttp.ClientSession() + return self.session + +session_manager = SessionBase() + +proxy = None +session = None + +FILE_URL = None + +CONNECT_TIMEOUT = 15 +READ_TIMEOUT = 30 + +LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates) + + +RETRY_ON_ERROR = False +RETRY_TIMEOUT = 2 +MAX_RETRIES = 15 + +CUSTOM_SERIALIZER = None +CUSTOM_REQUEST_SENDER = None + +ENABLE_MIDDLEWARE = False + +async def _process_request(token, url, method='get', params=None, files=None): + async with await session_manager._get_new_session() as session: + async with session.get(API_URL.format(token, url), params=params, data=files) as response: + json_result = await _check_result(url, response) + if json_result: + return json_result['result'] + + +async def _convert_markup(markup): + if isinstance(markup, types.JsonSerializable): + return markup.to_json() + return markup + + + +async def get_me(token): + method_url = r'getMe' + return await _process_request(token, method_url) + + +async def log_out(token): + method_url = r'logOut' + return await _process_request(token, method_url) + + +async def close(token): + method_url = r'close' + return await _process_request(token, method_url) + + +async def get_file(token, file_id): + method_url = r'getFile' + return await _process_request(token, method_url, params={'file_id': file_id}) + + +async def get_file_url(token, file_id): + if FILE_URL is None: + return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id)['file_path']) + else: + # noinspection PyUnresolvedReferences + return FILE_URL.format(token, get_file(token, file_id)['file_path']) + + +async def download_file(token, file_path): + if FILE_URL is None: + url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path) + else: + # noinspection PyUnresolvedReferences + url = FILE_URL.format(token, file_path) + # TODO: rewrite this method + async with await session_manager._get_new_session() as session: + async with session.get(url, proxy=proxy) as response: + result = await response.read() + if response.status != 200: + raise ApiHTTPException('Download file', result) + + return result + + +async def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, + drop_pending_updates = None, timeout=None): + method_url = r'setWebhook' + payload = { + 'url': url if url else "", + } + files = None + if certificate: + files = {'certificate': certificate} + if max_connections: + payload['max_connections'] = max_connections + if allowed_updates is not None: # Empty lists should pass + payload['allowed_updates'] = json.dumps(allowed_updates) + if ip_address is not None: # Empty string should pass + payload['ip_address'] = ip_address + if drop_pending_updates is not None: # Any bool value should pass + payload['drop_pending_updates'] = drop_pending_updates + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload, files=files) + + +async def delete_webhook(token, drop_pending_updates=None, timeout=None): + method_url = r'deleteWebhook' + payload = {} + if drop_pending_updates is not None: # Any bool value should pass + payload['drop_pending_updates'] = drop_pending_updates + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + +async def get_webhook_info(token, timeout=None): + method_url = r'getWebhookInfo' + payload = {} + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + + +async def get_updates(token, offset=None, limit=None, + timeout=None, allowed_updates=None, long_polling_timeout=None): + method_name = 'getUpdates' + params = {} + if offset: + params['offset'] = offset + elif limit: + params['limit'] = limit + elif timeout: + params['timeout'] = timeout + elif allowed_updates: + params['allowed_updates'] = allowed_updates + params['long_polling_timeout'] = long_polling_timeout if long_polling_timeout else LONG_POLLING_TIMEOUT + return await _process_request(token, method_name, params=params) + +async def _check_result(method_name, result): + """ + Checks whether `result` is a valid API response. + A result is considered invalid if: + - The server returned an HTTP response code other than 200 + - The content of the result is invalid JSON. + - The method call was unsuccessful (The JSON 'ok' field equals False) + + :raises ApiException: if one of the above listed cases is applicable + :param method_name: The name of the method called + :param result: The returned result of the method request + :return: The result parsed to a JSON dictionary. + """ + try: + result_json = await result.json(encoding="utf-8") + except: + if result.status_code != 200: + raise ApiHTTPException(method_name, result) + else: + raise ApiInvalidJSONException(method_name, result) + else: + if not result_json['ok']: + raise ApiTelegramException(method_name, result, result_json) + + return result_json + + +async def send_message( + token, chat_id, text, + disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None, + entities=None, allow_sending_without_reply=None): + """ + Use this method to send text messages. On success, the sent Message is returned. + :param token: + :param chat_id: + :param text: + :param disable_web_page_preview: + :param reply_to_message_id: + :param reply_markup: + :param parse_mode: + :param disable_notification: + :param timeout: + :param entities: + :param allow_sending_without_reply: + :return: + """ + method_name = 'sendMessage' + params = {'chat_id': str(chat_id), 'text': text} + if disable_web_page_preview is not None: + params['disable_web_page_preview'] = disable_web_page_preview + if reply_to_message_id: + params['reply_to_message_id'] = reply_to_message_id + if reply_markup: + params['reply_markup'] = await _convert_markup(reply_markup) + if parse_mode: + params['parse_mode'] = parse_mode + if disable_notification is not None: + params['disable_notification'] = disable_notification + if timeout: + params['timeout'] = timeout + if entities: + params['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) + if allow_sending_without_reply is not None: + params['allow_sending_without_reply'] = allow_sending_without_reply + + return await _process_request(token, method_name, params=params) + +# here shit begins + +async def get_user_profile_photos(token, user_id, offset=None, limit=None): + method_url = r'getUserProfilePhotos' + payload = {'user_id': user_id} + if offset: + payload['offset'] = offset + if limit: + payload['limit'] = limit + return await _process_request(token, method_url, params=payload) + + +async def get_chat(token, chat_id): + method_url = r'getChat' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + + +async def leave_chat(token, chat_id): + method_url = r'leaveChat' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + + +async def get_chat_administrators(token, chat_id): + method_url = r'getChatAdministrators' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + + +async def get_chat_member_count(token, chat_id): + method_url = r'getChatMemberCount' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + + +async def set_sticker_set_thumb(token, name, user_id, thumb): + method_url = r'setStickerSetThumb' + payload = {'name': name, 'user_id': user_id} + files = {} + if thumb: + if not isinstance(thumb, str): + files['thumb'] = thumb + else: + payload['thumb'] = thumb + return await _process_request(token, method_url, params=payload, files=files or None) + + +async def set_chat_sticker_set(token, chat_id, sticker_set_name): + method_url = r'setChatStickerSet' + payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name} + return await _process_request(token, method_url, params=payload) + + +async def delete_chat_sticker_set(token, chat_id): + method_url = r'deleteChatStickerSet' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + + +async def get_chat_member(token, chat_id, user_id): + method_url = r'getChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id} + return await _process_request(token, method_url, params=payload) + + +async def forward_message( + token, chat_id, from_chat_id, message_id, + disable_notification=None, timeout=None): + method_url = r'forwardMessage' + payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + +async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, + disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, + reply_markup=None, timeout=None): + method_url = r'copyMessage' + payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} + if caption is not None: + payload['caption'] = caption + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities is not None: + payload['caption_entities'] = await _convert_entites(caption_entities) + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup is not None: + payload['reply_markup'] = await _convert_markup(reply_markup) + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + +async def send_dice( + token, chat_id, + emoji=None, disable_notification=None, reply_to_message_id=None, + reply_markup=None, timeout=None, allow_sending_without_reply=None): + method_url = r'sendDice' + payload = {'chat_id': chat_id} + if emoji: + payload['emoji'] = emoji + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if timeout: + payload['timeout'] = timeout + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload) + + +async def send_photo( + token, chat_id, photo, + caption=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None, + caption_entities=None, allow_sending_without_reply=None): + method_url = r'sendPhoto' + payload = {'chat_id': chat_id} + files = None + if util.is_string(photo): + payload['photo'] = photo + elif util.is_pil_image(photo): + files = {'photo': util.pil_image_to_file(photo)} + else: + files = {'photo': photo} + if caption: + payload['caption'] = caption + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def send_media_group( + token, chat_id, media, + disable_notification=None, reply_to_message_id=None, + timeout=None, allow_sending_without_reply=None): + method_url = r'sendMediaGroup' + media_json, files = await convert_input_media_array(media) + payload = {'chat_id': chat_id, 'media': media_json} + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if timeout: + payload['timeout'] = timeout + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request( + token, method_url, params=payload, + method='post' if files else 'get', + files=files if files else None) + + +async def send_location( + token, chat_id, latitude, longitude, + live_period=None, reply_to_message_id=None, + reply_markup=None, disable_notification=None, + timeout=None, horizontal_accuracy=None, heading=None, + proximity_alert_radius=None, allow_sending_without_reply=None): + method_url = r'sendLocation' + payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} + if live_period: + payload['live_period'] = live_period + if horizontal_accuracy: + payload['horizontal_accuracy'] = horizontal_accuracy + if heading: + payload['heading'] = heading + if proximity_alert_radius: + payload['proximity_alert_radius'] = proximity_alert_radius + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + +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): + method_url = r'editMessageLiveLocation' + payload = {'latitude': latitude, 'longitude': longitude} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if horizontal_accuracy: + payload['horizontal_accuracy'] = horizontal_accuracy + if heading: + payload['heading'] = heading + if proximity_alert_radius: + payload['proximity_alert_radius'] = proximity_alert_radius + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + +async def stop_message_live_location( + token, chat_id=None, message_id=None, + inline_message_id=None, reply_markup=None, timeout=None): + method_url = r'stopMessageLiveLocation' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + +async def send_venue( + token, chat_id, latitude, longitude, title, address, + foursquare_id=None, foursquare_type=None, disable_notification=None, + reply_to_message_id=None, reply_markup=None, timeout=None, + allow_sending_without_reply=None, google_place_id=None, + google_place_type=None): + method_url = r'sendVenue' + payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} + if foursquare_id: + payload['foursquare_id'] = foursquare_id + if foursquare_type: + payload['foursquare_type'] = foursquare_type + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if timeout: + payload['timeout'] = timeout + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if google_place_id: + payload['google_place_id'] = google_place_id + if google_place_type: + payload['google_place_type'] = google_place_type + return await _process_request(token, method_url, params=payload) + + +async def send_contact( + token, chat_id, phone_number, first_name, last_name=None, vcard=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, + allow_sending_without_reply=None): + method_url = r'sendContact' + payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} + if last_name: + payload['last_name'] = last_name + if vcard: + payload['vcard'] = vcard + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if timeout: + payload['timeout'] = timeout + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload) + + +async def send_chat_action(token, chat_id, action, timeout=None): + method_url = r'sendChatAction' + payload = {'chat_id': chat_id, 'action': action} + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload) + + +async def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, + thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None): + method_url = r'sendVideo' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(data): + files = {'video': data} + else: + payload['video'] = data + if duration: + payload['duration'] = duration + if caption: + payload['caption'] = caption + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode + if supports_streaming is not None: + payload['supports_streaming'] = supports_streaming + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + if thumb: + if not util.is_string(thumb): + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} + else: + payload['thumb'] = thumb + if width: + payload['width'] = width + if height: + payload['height'] = height + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def send_animation( + token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, + allow_sending_without_reply=None): + method_url = r'sendAnimation' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(data): + files = {'animation': data} + else: + payload['animation'] = data + if duration: + payload['duration'] = duration + if caption: + payload['caption'] = caption + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + if thumb: + if not util.is_string(thumb): + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} + else: + payload['thumb'] = thumb + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, + parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, + allow_sending_without_reply=None): + method_url = r'sendVoice' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(voice): + files = {'voice': voice} + else: + payload['voice'] = voice + if caption: + payload['caption'] = caption + if duration: + payload['duration'] = duration + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, + disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None): + method_url = r'sendVideoNote' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(data): + files = {'video_note': data} + else: + payload['video_note'] = data + if duration: + payload['duration'] = duration + if length and (str(length).isdigit() and int(length) <= 639): + payload['length'] = length + else: + payload['length'] = 639 # seems like it is MAX length size + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + if thumb: + if not util.is_string(thumb): + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} + else: + payload['thumb'] = thumb + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, + caption_entities=None, allow_sending_without_reply=None): + method_url = r'sendAudio' + payload = {'chat_id': chat_id} + files = None + if not util.is_string(audio): + files = {'audio': audio} + else: + payload['audio'] = audio + if caption: + payload['caption'] = caption + if duration: + payload['duration'] = duration + if performer: + payload['performer'] = performer + if title: + payload['title'] = title + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if parse_mode: + payload['parse_mode'] = parse_mode + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + if thumb: + if not util.is_string(thumb): + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} + else: + payload['thumb'] = thumb + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, + disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, + allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None): + method_url = get_method_by_type(data_type) + payload = {'chat_id': chat_id} + files = None + if not util.is_string(data): + file_data = data + if visible_file_name: + file_data = (visible_file_name, data) + files = {data_type: file_data} + else: + payload[data_type] = data + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if parse_mode and data_type == 'document': + payload['parse_mode'] = parse_mode + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if timeout: + payload['timeout'] = timeout + if caption: + payload['caption'] = caption + if thumb: + if not util.is_string(thumb): + if files: + files['thumb'] = thumb + else: + files = {'thumb': thumb} + else: + payload['thumb'] = thumb + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if method_url == 'sendDocument' and disable_content_type_detection is not None: + payload['disable_content_type_detection'] = disable_content_type_detection + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def get_method_by_type(data_type): + if data_type == 'document': + return r'sendDocument' + if data_type == 'sticker': + return r'sendSticker' + + +async def ban_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None): + method_url = 'banChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id} + if isinstance(until_date, datetime): + payload['until_date'] = until_date.timestamp() + else: + payload['until_date'] = until_date + if revoke_messages is not None: + payload['revoke_messages'] = revoke_messages + return await _process_request(token, method_url, params=payload, method='post') + + +async def unban_chat_member(token, chat_id, user_id, only_if_banned): + method_url = 'unbanChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id} + if only_if_banned is not None: # None / True / False + payload['only_if_banned'] = only_if_banned + return await _process_request(token, method_url, params=payload, method='post') + + +async def restrict_chat_member( + token, chat_id, user_id, until_date=None, + can_send_messages=None, can_send_media_messages=None, + can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None): + method_url = 'restrictChatMember' + permissions = {} + if can_send_messages is not None: + permissions['can_send_messages'] = can_send_messages + if can_send_media_messages is not None: + permissions['can_send_media_messages'] = can_send_media_messages + if can_send_polls is not None: + permissions['can_send_polls'] = can_send_polls + if can_send_other_messages is not None: + permissions['can_send_other_messages'] = can_send_other_messages + if can_add_web_page_previews is not None: + permissions['can_add_web_page_previews'] = can_add_web_page_previews + if can_change_info is not None: + permissions['can_change_info'] = can_change_info + if can_invite_users is not None: + permissions['can_invite_users'] = can_invite_users + if can_pin_messages is not None: + permissions['can_pin_messages'] = can_pin_messages + permissions_json = json.dumps(permissions) + payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} + if until_date is not None: + if isinstance(until_date, datetime): + payload['until_date'] = until_date.timestamp() + else: + payload['until_date'] = until_date + return await _process_request(token, method_url, params=payload, method='post') + + +async def promote_chat_member( + token, chat_id, user_id, can_change_info=None, can_post_messages=None, + can_edit_messages=None, can_delete_messages=None, can_invite_users=None, + can_restrict_members=None, can_pin_messages=None, can_promote_members=None, + is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None): + method_url = 'promoteChatMember' + payload = {'chat_id': chat_id, 'user_id': user_id} + if can_change_info is not None: + payload['can_change_info'] = can_change_info + if can_post_messages is not None: + payload['can_post_messages'] = can_post_messages + if can_edit_messages is not None: + payload['can_edit_messages'] = can_edit_messages + if can_delete_messages is not None: + payload['can_delete_messages'] = can_delete_messages + if can_invite_users is not None: + payload['can_invite_users'] = can_invite_users + if can_restrict_members is not None: + payload['can_restrict_members'] = can_restrict_members + if can_pin_messages is not None: + payload['can_pin_messages'] = can_pin_messages + if can_promote_members is not None: + payload['can_promote_members'] = can_promote_members + if is_anonymous is not None: + payload['is_anonymous'] = is_anonymous + if can_manage_chat is not None: + payload['can_manage_chat'] = can_manage_chat + if can_manage_voice_chats is not None: + payload['can_manage_voice_chats'] = can_manage_voice_chats + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_chat_administrator_custom_title(token, chat_id, user_id, custom_title): + method_url = 'setChatAdministratorCustomTitle' + payload = { + 'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title + } + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_chat_permissions(token, chat_id, permissions): + method_url = 'setChatPermissions' + payload = { + 'chat_id': chat_id, + 'permissions': permissions.to_json() + } + return await _process_request(token, method_url, params=payload, method='post') + + +async def create_chat_invite_link(token, chat_id, name, expire_date, member_limit, creates_join_request): + method_url = 'createChatInviteLink' + payload = { + 'chat_id': chat_id + } + + if expire_date is not None: + if isinstance(expire_date, datetime): + payload['expire_date'] = expire_date.timestamp() + else: + payload['expire_date'] = expire_date + if member_limit: + payload['member_limit'] = member_limit + if creates_join_request is not None: + payload['creates_join_request'] = creates_join_request + if name: + payload['name'] = name + + return await _process_request(token, method_url, params=payload, method='post') + + +async def edit_chat_invite_link(token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request): + method_url = 'editChatInviteLink' + payload = { + 'chat_id': chat_id, + 'invite_link': invite_link + } + + if expire_date is not None: + if isinstance(expire_date, datetime): + payload['expire_date'] = expire_date.timestamp() + else: + payload['expire_date'] = expire_date + + if member_limit is not None: + payload['member_limit'] = member_limit + if name: + payload['name'] = name + if creates_join_request is not None: + payload['creates_join_request'] = creates_join_request + + return await _process_request(token, method_url, params=payload, method='post') + +async def revoke_chat_invite_link(token, chat_id, invite_link): + method_url = 'revokeChatInviteLink' + payload = { + 'chat_id': chat_id, + 'invite_link': invite_link + } + return await _process_request(token, method_url, params=payload, method='post') + + +async def export_chat_invite_link(token, chat_id): + method_url = 'exportChatInviteLink' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload, method='post') + +async def approve_chat_join_request(token, chat_id, user_id): + method_url = 'approveChatJoinRequest' + payload = { + 'chat_id': chat_id, + 'user_id': user_id + } + return await _process_request(token, method_url, params=payload, method='post') + + +async def decline_chat_join_request(token, chat_id, user_id): + method_url = 'declineChatJoinRequest' + payload = { + 'chat_id': chat_id, + 'user_id': user_id + } + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_chat_photo(token, chat_id, photo): + method_url = 'setChatPhoto' + payload = {'chat_id': chat_id} + files = None + if util.is_string(photo): + payload['photo'] = photo + elif util.is_pil_image(photo): + files = {'photo': util.pil_image_to_file(photo)} + else: + files = {'photo': photo} + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def delete_chat_photo(token, chat_id): + method_url = 'deleteChatPhoto' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_chat_title(token, chat_id, title): + method_url = 'setChatTitle' + payload = {'chat_id': chat_id, 'title': title} + return await _process_request(token, method_url, params=payload, method='post') + + +async def get_my_commands(token, scope=None, language_code=None): + method_url = r'getMyCommands' + payload = {} + if scope: + payload['scope'] = scope.to_json() + if language_code: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload) + + +async def set_my_commands(token, commands, scope=None, language_code=None): + method_url = r'setMyCommands' + payload = {'commands': await _convert_list_json_serializable(commands)} + if scope: + payload['scope'] = scope.to_json() + if language_code: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload, method='post') + + +async def delete_my_commands(token, scope=None, language_code=None): + method_url = r'deleteMyCommands' + payload = {} + if scope: + payload['scope'] = scope.to_json() + if language_code: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_chat_description(token, chat_id, description): + method_url = 'setChatDescription' + payload = {'chat_id': chat_id} + if description is not None: # Allow empty strings + payload['description'] = description + return await _process_request(token, method_url, params=payload, method='post') + + +async def pin_chat_message(token, chat_id, message_id, disable_notification=None): + method_url = 'pinChatMessage' + payload = {'chat_id': chat_id, 'message_id': message_id} + if disable_notification is not None: + payload['disable_notification'] = disable_notification + return await _process_request(token, method_url, params=payload, method='post') + + +async def unpin_chat_message(token, chat_id, message_id): + method_url = 'unpinChatMessage' + payload = {'chat_id': chat_id} + if message_id: + payload['message_id'] = message_id + return await _process_request(token, method_url, params=payload, method='post') + + +async def unpin_all_chat_messages(token, chat_id): + method_url = 'unpinAllChatMessages' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload, method='post') + + +# Updating messages + +async def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, + entities = None, disable_web_page_preview=None, reply_markup=None): + method_url = r'editMessageText' + payload = {'text': text} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + 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: + payload['reply_markup'] = await _convert_markup(reply_markup) + return await _process_request(token, method_url, params=payload, method='post') + + +async def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, + parse_mode=None, caption_entities=None,reply_markup=None): + method_url = r'editMessageCaption' + payload = {'caption': caption} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + return await _process_request(token, method_url, params=payload, method='post') + + +async def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + method_url = r'editMessageMedia' + media_json, file = convert_input_media(media) + payload = {'media': media_json} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + return await _process_request(token, method_url, params=payload, files=file, method='post' if file else 'get') + + +async def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): + method_url = r'editMessageReplyMarkup' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + return await _process_request(token, method_url, params=payload, method='post') + + +async def delete_message(token, chat_id, message_id, timeout=None): + method_url = r'deleteMessage' + payload = {'chat_id': chat_id, 'message_id': message_id} + if timeout: + payload['timeout'] = timeout + return await _process_request(token, method_url, params=payload, method='post') + + +# Game + +async def send_game( + token, chat_id, game_short_name, + disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, + allow_sending_without_reply=None): + method_url = r'sendGame' + payload = {'chat_id': chat_id, 'game_short_name': game_short_name} + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if timeout: + payload['timeout'] = timeout + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + return await _process_request(token, method_url, params=payload) + + +# https://core.telegram.org/bots/api#setgamescore +async def set_game_score(token, user_id, score, force=None, disable_edit_message=None, chat_id=None, message_id=None, + inline_message_id=None): + """ + Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if the new score is not greater than the user's current score in the chat. + :param token: Bot's token (you don't need to fill this) + :param user_id: User identifier + :param score: New score, must be non-negative + :param force: (Optional) Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters + :param disable_edit_message: (Optional) Pass True, if the game message should not be automatically edited to include the current scoreboard + :param chat_id: (Optional, required if inline_message_id is not specified) Unique identifier for the target chat (or username of the target channel in the format @channelusername) + :param message_id: (Optional, required if inline_message_id is not specified) Unique identifier of the sent message + :param inline_message_id: (Optional, required if chat_id and message_id are not specified) Identifier of the inline message + :return: + """ + method_url = r'setGameScore' + payload = {'user_id': user_id, 'score': score} + if force is not None: + payload['force'] = force + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + if disable_edit_message is not None: + payload['disable_edit_message'] = disable_edit_message + return await _process_request(token, method_url, params=payload) + + +# https://core.telegram.org/bots/api#getgamehighscores +async def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_message_id=None): + """ + Use this method to get data for high score tables. Will return the score of the specified user and several of his neighbors in a game. On success, returns an Array of GameHighScore objects. + This method will currently return scores for the target user, plus two of his closest neighbors on each side. Will also return the top three users if the user and his neighbors are not among them. Please note that this behavior is subject to change. + :param token: Bot's token (you don't need to fill this) + :param user_id: Target user id + :param chat_id: (Optional, required if inline_message_id is not specified) Unique identifier for the target chat (or username of the target channel in the format @channelusername) + :param message_id: (Optional, required if inline_message_id is not specified) Unique identifier of the sent message + :param inline_message_id: (Optional, required if chat_id and message_id are not specified) Identifier of the inline message + :return: + """ + method_url = r'getGameHighScores' + payload = {'user_id': user_id} + if chat_id: + payload['chat_id'] = chat_id + if message_id: + payload['message_id'] = message_id + if inline_message_id: + payload['inline_message_id'] = inline_message_id + return await _process_request(token, method_url, params=payload) + + +# Payments (https://core.telegram.org/bots/api#payments) + +async def send_invoice( + token, chat_id, title, description, invoice_payload, provider_token, currency, prices, + start_parameter = None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, + send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, + disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, + timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None): + """ + Use this method to send invoices. On success, the sent Message is returned. + :param token: Bot's token (you don't need to fill this) + :param chat_id: Unique identifier for the target private chat + :param title: Product name + :param description: Product description + :param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :param provider_token: Payments provider token, obtained via @Botfather + :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for. + :param photo_size: Photo size + :param photo_width: Photo width + :param photo_height: Photo height + :param need_name: Pass True, if you require the user's full name to complete the order + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :param need_email: Pass True, if you require the user's email to complete the order + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :param is_flexible: Pass True, if the final price depends on the shipping method + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_markup: 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 + :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 allow_sending_without_reply: + :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. + :return: + """ + method_url = r'sendInvoice' + payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, + 'provider_token': provider_token, 'currency': currency, + 'prices': await _convert_list_json_serializable(prices)} + if start_parameter: + payload['start_parameter'] = start_parameter + if photo_url: + payload['photo_url'] = photo_url + if photo_size: + payload['photo_size'] = photo_size + if photo_width: + payload['photo_width'] = photo_width + if photo_height: + payload['photo_height'] = photo_height + if need_name is not None: + payload['need_name'] = need_name + if need_phone_number is not None: + payload['need_phone_number'] = need_phone_number + if need_email is not None: + payload['need_email'] = need_email + if need_shipping_address is not None: + payload['need_shipping_address'] = need_shipping_address + if send_phone_number_to_provider is not None: + payload['send_phone_number_to_provider'] = send_phone_number_to_provider + if send_email_to_provider is not None: + payload['send_email_to_provider'] = send_email_to_provider + if is_flexible is not None: + payload['is_flexible'] = is_flexible + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if reply_to_message_id: + payload['reply_to_message_id'] = reply_to_message_id + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + if provider_data: + payload['provider_data'] = provider_data + if timeout: + payload['timeout'] = timeout + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if max_tip_amount is not None: + payload['max_tip_amount'] = max_tip_amount + if suggested_tip_amounts is not None: + payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) + return await _process_request(token, method_url, params=payload) + + +async def answer_shipping_query(token, shipping_query_id, ok, shipping_options=None, error_message=None): + """ + If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned. + :param token: Bot's token (you don't need to fill this) + :param shipping_query_id: Unique identifier for the query to be answered + :param ok: Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible) + :param shipping_options: Required if ok is True. A JSON-serialized array of available shipping options. + :param error_message: Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. + :return: + """ + method_url = 'answerShippingQuery' + payload = {'shipping_query_id': shipping_query_id, 'ok': ok} + if shipping_options: + payload['shipping_options'] = await _convert_list_json_serializable(shipping_options) + if error_message: + payload['error_message'] = error_message + return await _process_request(token, method_url, params=payload) + + +async def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=None): + """ + Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the field pre_checkout_query. Use this method to respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. + :param token: Bot's token (you don't need to fill this) + :param pre_checkout_query_id: Unique identifier for the query to be answered + :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. + :param error_message: Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user. + :return: + """ + method_url = 'answerPreCheckoutQuery' + payload = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok} + if error_message: + payload['error_message'] = error_message + return await _process_request(token, method_url, params=payload) + + +# InlineQuery + +async def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None, cache_time=None): + """ + Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned. + Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via BotFather and accept the terms. Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter. + :param token: Bot's token (you don't need to fill this) + :param callback_query_id: Unique identifier for the query to be answered + :param text: (Optional) Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters + :param show_alert: (Optional) If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false. + :param url: (Optional) URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your game – note that this will only work if the query comes from a callback_game button. + Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter. + :param cache_time: (Optional) The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0. + :return: + """ + method_url = 'answerCallbackQuery' + payload = {'callback_query_id': callback_query_id} + if text: + payload['text'] = text + if show_alert is not None: + payload['show_alert'] = show_alert + if url: + payload['url'] = url + if cache_time is not None: + payload['cache_time'] = cache_time + return await _process_request(token, method_url, params=payload, method='post') + + +async def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, + switch_pm_text=None, switch_pm_parameter=None): + method_url = 'answerInlineQuery' + payload = {'inline_query_id': inline_query_id, 'results': await _convert_list_json_serializable(results)} + if cache_time is not None: + payload['cache_time'] = cache_time + if is_personal is not None: + payload['is_personal'] = is_personal + if next_offset is not None: + payload['next_offset'] = next_offset + if switch_pm_text: + payload['switch_pm_text'] = switch_pm_text + if switch_pm_parameter: + payload['switch_pm_parameter'] = switch_pm_parameter + return await _process_request(token, method_url, params=payload, method='post') + + +async def get_sticker_set(token, name): + method_url = 'getStickerSet' + return await _process_request(token, method_url, params={'name': name}) + + +async def upload_sticker_file(token, user_id, png_sticker): + method_url = 'uploadStickerFile' + payload = {'user_id': user_id} + files = {'png_sticker': png_sticker} + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def create_new_sticker_set( + token, user_id, name, title, emojis, png_sticker, tgs_sticker, + contains_masks=None, mask_position=None): + method_url = 'createNewStickerSet' + payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} + stype = 'png_sticker' if png_sticker else 'tgs_sticker' + sticker = png_sticker or tgs_sticker + files = None + if not util.is_string(sticker): + files = {stype: sticker} + else: + payload[stype] = sticker + if contains_masks is not None: + payload['contains_masks'] = contains_masks + if mask_position: + payload['mask_position'] = mask_position.to_json() + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position): + method_url = 'addStickerToSet' + payload = {'user_id': user_id, 'name': name, 'emojis': emojis} + stype = 'png_sticker' if png_sticker else 'tgs_sticker' + sticker = png_sticker or tgs_sticker + files = None + if not util.is_string(sticker): + files = {stype: sticker} + else: + payload[stype] = sticker + if mask_position: + payload['mask_position'] = mask_position.to_json() + return await _process_request(token, method_url, params=payload, files=files, method='post') + + +async def set_sticker_position_in_set(token, sticker, position): + method_url = 'setStickerPositionInSet' + payload = {'sticker': sticker, 'position': position} + return await _process_request(token, method_url, params=payload, method='post') + + +async def delete_sticker_from_set(token, sticker): + method_url = 'deleteStickerFromSet' + payload = {'sticker': sticker} + return await _process_request(token, method_url, params=payload, method='post') + + +# noinspection PyShadowingBuiltins +async def send_poll( + 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_to_message_id=None, allow_sending_without_reply=None, + reply_markup=None, timeout=None, explanation_entities=None): + method_url = r'sendPoll' + payload = { + 'chat_id': str(chat_id), + 'question': question, + 'options': json.dumps(await _convert_poll_options(options))} + + if is_anonymous is not None: + payload['is_anonymous'] = is_anonymous + if type is not None: + 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: + payload['explanation'] = explanation + if explanation_parse_mode is not None: + payload['explanation_parse_mode'] = explanation_parse_mode + if open_period is not None: + payload['open_period'] = open_period + if close_date is not None: + if isinstance(close_date, datetime): + payload['close_date'] = close_date.timestamp() + else: + 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_to_message_id is not None: + payload['reply_to_message_id'] = reply_to_message_id + if allow_sending_without_reply is not None: + payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_markup is not None: + payload['reply_markup'] = await _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)) + return await _process_request(token, method_url, params=payload) + +async def _convert_list_json_serializable(results): + ret = '' + for r in results: + if isinstance(r, types.JsonSerializable): + ret = ret + r.to_json() + ',' + if len(ret) > 0: + ret = ret[:-1] + return '[' + ret + ']' + + + +async def _convert_entites(entites): + if entites is None: + return None + elif len(entites) == 0: + return [] + elif isinstance(entites[0], types.JsonSerializable): + return [entity.to_json() for entity in entites] + else: + return entites + + +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() + return None, None + + +async def convert_input_media_array(array): + media = [] + files = {} + for input_media in array: + if isinstance(input_media, types.InputMedia): + media_dict = input_media.to_dict() + if media_dict['media'].startswith('attach://'): + key = media_dict['media'].replace('attach://', '') + files[key] = input_media.media + media.append(media_dict) + return json.dumps(media), files + + +async def _no_encode(func): + def wrapper(key, val): + if key == 'filename': + return u'{0}={1}'.format(key, val) + else: + return func(key, val) + + return wrapper + +async def stop_poll(token, chat_id, message_id, reply_markup=None): + method_url = r'stopPoll' + payload = {'chat_id': str(chat_id), 'message_id': message_id} + if reply_markup: + payload['reply_markup'] = await _convert_markup(reply_markup) + return await _process_request(token, method_url, params=payload) + +# exceptions +class ApiException(Exception): + """ + This class represents a base Exception thrown when a call to the Telegram API fails. + In addition to an informative message, it has a `function_name` and a `result` attribute, which respectively + contain the name of the failed function and the returned result that made the function to be considered as + failed. + """ + + def __init__(self, msg, function_name, result): + super(ApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg)) + self.function_name = function_name + self.result = result + +class ApiHTTPException(ApiException): + """ + This class represents an Exception thrown when a call to the + Telegram API server returns HTTP code that is not 200. + """ + def __init__(self, function_name, result): + super(ApiHTTPException, self).__init__( + "The server returned HTTP {0} {1}. Response body:\n[{2}]" \ + .format(result.status_code, result.reason, result), + function_name, + result) + +class ApiInvalidJSONException(ApiException): + """ + This class represents an Exception thrown when a call to the + Telegram API server returns invalid json. + """ + def __init__(self, function_name, result): + super(ApiInvalidJSONException, self).__init__( + "The server returned an invalid JSON response. Response body:\n[{0}]" \ + .format(result), + function_name, + result) + +class ApiTelegramException(ApiException): + """ + This class represents an Exception thrown when a Telegram API returns error code. + """ + def __init__(self, function_name, result, result_json): + super(ApiTelegramException, self).__init__( + "Error code: {0}. Description: {1}" \ + .format(result_json['error_code'], result_json['description']), + function_name, + result) + self.result_json = result_json + self.error_code = result_json['error_code'] \ No newline at end of file From 53f9232f369e9f6ffa8866b3886cd9d5a6fb4bca Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 20 Nov 2021 15:54:43 +0500 Subject: [PATCH 0756/1808] Update requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 3a014e7d8..8e06e278b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pytest==3.0.2 requests==2.20.0 wheel==0.24.0 +aiohttp>=3.8.0,<3.9.0 \ No newline at end of file From 1e4477c148e1a86f9ddee51bf6be822f87f8df50 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 20 Nov 2021 16:01:38 +0500 Subject: [PATCH 0757/1808] Logging fix --- telebot/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 65906f2ba..68c2e48ed 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3488,8 +3488,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: continue except Exception as e: - print('Cause exception while getting updates.') - logger.critical(str(e)) + logger.error('Cause exception while getting updates.') + logger.error(str(e)) await asyncio.sleep(3) continue @@ -3512,7 +3512,7 @@ async def _process_updates(self, handlers, messages): await self._loop_create_task(message_handler['function'](message)) break except Exception as e: - print(str(e)) + logger.error(str(e)) # update handling async def process_new_updates(self, updates): From 714ae7d67f26dcc223d3fc7d9ddce0f5c5c743d1 Mon Sep 17 00:00:00 2001 From: abdullaev Date: Tue, 23 Nov 2021 18:01:51 +0500 Subject: [PATCH 0758/1808] CallbackData class added --- examples/CallbackData_example.py | 86 ++++++++++++++++++++++++++ telebot/callback_data.py | 100 +++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 examples/CallbackData_example.py create mode 100644 telebot/callback_data.py diff --git a/examples/CallbackData_example.py b/examples/CallbackData_example.py new file mode 100644 index 000000000..a0d70eae3 --- /dev/null +++ b/examples/CallbackData_example.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you how to use CallbackData +""" + +from telebot.callback_data import CallbackData, CallbackDataFilter +from telebot import types, TeleBot +from telebot.custom_filters import AdvancedCustomFilter + +API_TOKEN = '1802978815:AAG6ju32eC-O3s8WRg57BRCT64VkJhQiHNk' +PRODUCTS = [ + {'id': '0', 'name': 'xiaomi mi 10', 'price': 400}, + {'id': '1', 'name': 'samsung s20', 'price': 800}, + {'id': '2', 'name': 'iphone 13', 'price': 1300} +] + +bot = TeleBot(API_TOKEN) +products_factory = CallbackData('product_id', prefix='products') + + +def products_keyboard(): + return types.InlineKeyboardMarkup( + keyboard=[ + [ + types.InlineKeyboardButton( + text=product['name'], + callback_data=products_factory.new(product_id=product["id"]) + ) + ] + for product in PRODUCTS + ] + ) + + +def back_keyboard(): + return types.InlineKeyboardMarkup( + keyboard=[ + [ + types.InlineKeyboardButton( + text='⬅', + callback_data='back' + ) + ] + ] + ) + + +class ProductsCallbackFilter(AdvancedCustomFilter): + key = 'config' + + def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +@bot.message_handler(commands=['products']) +def products_command_handler(message: types.Message): + bot.send_message(message.chat.id, 'Products:', reply_markup=products_keyboard()) + + +# Only product with field - product_id = 2 +@bot.callback_query_handler(func=None, config=products_factory.filter(product_id='2')) +def product_one_callback(call: types.CallbackQuery): + bot.answer_callback_query(callback_query_id=call.id, text='Not available :(', show_alert=True) + + +# Any other products +@bot.callback_query_handler(func=None, config=products_factory.filter()) +def products_callback(call: types.CallbackQuery): + callback_data: dict = products_factory.parse(callback_data=call.data) + product_id = int(callback_data['product_id']) + product = PRODUCTS[product_id] + + text = f"Product name: {product['name']}\n" \ + f"Product price: {product['price']}" + bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, + text=text, reply_markup=back_keyboard()) + + +@bot.callback_query_handler(func=lambda c: c.data == 'back') +def back_callback(call: types.CallbackQuery): + bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, + text='Products:', reply_markup=products_keyboard()) + + +bot.add_custom_filter(ProductsCallbackFilter()) +bot.infinity_polling() diff --git a/telebot/callback_data.py b/telebot/callback_data.py new file mode 100644 index 000000000..2897c4070 --- /dev/null +++ b/telebot/callback_data.py @@ -0,0 +1,100 @@ +import typing + + +class CallbackDataFilter: + + def __init__(self, factory, config: typing.Dict[str, str]): + self.config = config + self.factory = factory + + def check(self, query): + try: + data = self.factory.parse(query.data) + except ValueError: + return False + + for key, value in self.config.items(): + if isinstance(value, (list, tuple, set, frozenset)): + if data.get(key) not in value: + return False + elif data.get(key) != value: + return False + return True + + +class CallbackData: + """ + Callback data factory + """ + + def __init__(self, *parts, prefix: str, sep=':'): + if not isinstance(prefix, str): + raise TypeError(f'Prefix must be instance of str not {type(prefix).__name__}') + if not prefix: + raise ValueError("Prefix can't be empty") + if sep in prefix: + raise ValueError(f"Separator {sep!r} can't be used in prefix") + + self.prefix = prefix + self.sep = sep + + self._part_names = parts + + def new(self, *args, **kwargs) -> str: + """ + Generate callback data + """ + args = list(args) + + data = [self.prefix] + + for part in self._part_names: + value = kwargs.pop(part, None) + if value is None: + if args: + value = args.pop(0) + else: + raise ValueError(f'Value for {part!r} was not passed!') + + if value is not None and not isinstance(value, str): + value = str(value) + + if self.sep in value: + raise ValueError(f"Symbol {self.sep!r} is defined as the separator and can't be used in parts' values") + + data.append(value) + + if args or kwargs: + raise TypeError('Too many arguments were passed!') + + callback_data = self.sep.join(data) + + if len(callback_data.encode()) > 64: + raise ValueError('Resulted callback data is too long!') + + return callback_data + + def parse(self, callback_data: str) -> typing.Dict[str, str]: + """ + Parse data from the callback data + """ + + prefix, *parts = callback_data.split(self.sep) + if prefix != self.prefix: + raise ValueError("Passed callback data can't be parsed with that prefix.") + elif len(parts) != len(self._part_names): + raise ValueError('Invalid parts count!') + + result = {'@': prefix} + result.update(zip(self._part_names, parts)) + return result + + def filter(self, **config) -> CallbackDataFilter: + """ + Generate filter + """ + + for key in config.keys(): + if key not in self._part_names: + raise ValueError(f'Invalid field name {key!r}') + return CallbackDataFilter(self, config) From 9c8ea29fc677f878b670da865b15bdc6e1e093aa Mon Sep 17 00:00:00 2001 From: abdullaev Date: Tue, 23 Nov 2021 18:15:52 +0500 Subject: [PATCH 0759/1808] token removed :) --- examples/CallbackData_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CallbackData_example.py b/examples/CallbackData_example.py index a0d70eae3..5f4c0eb74 100644 --- a/examples/CallbackData_example.py +++ b/examples/CallbackData_example.py @@ -7,7 +7,7 @@ from telebot import types, TeleBot from telebot.custom_filters import AdvancedCustomFilter -API_TOKEN = '1802978815:AAG6ju32eC-O3s8WRg57BRCT64VkJhQiHNk' +API_TOKEN = '' PRODUCTS = [ {'id': '0', 'name': 'xiaomi mi 10', 'price': 400}, {'id': '1', 'name': 'samsung s20', 'price': 800}, From 8b6eba82039d5aa603bbce8911ca96a74217c3b8 Mon Sep 17 00:00:00 2001 From: abdullaev Date: Wed, 24 Nov 2021 20:26:58 +0500 Subject: [PATCH 0760/1808] Docstrings added --- telebot/callback_data.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/telebot/callback_data.py b/telebot/callback_data.py index 2897c4070..ecbe81e8c 100644 --- a/telebot/callback_data.py +++ b/telebot/callback_data.py @@ -8,6 +8,12 @@ def __init__(self, factory, config: typing.Dict[str, str]): self.factory = factory def check(self, query): + """ + Checks if query.data appropriates to specified config + :param query: telebot.types.CallbackQuery + :return: bool + """ + try: data = self.factory.parse(query.data) except ValueError: @@ -25,6 +31,7 @@ def check(self, query): class CallbackData: """ Callback data factory + This class will help you to work with CallbackQuery """ def __init__(self, *parts, prefix: str, sep=':'): @@ -43,6 +50,9 @@ def __init__(self, *parts, prefix: str, sep=':'): def new(self, *args, **kwargs) -> str: """ Generate callback data + :param args: positional parameters of CallbackData instance parts + :param kwargs: named parameters + :return: str """ args = list(args) @@ -77,6 +87,8 @@ def new(self, *args, **kwargs) -> str: def parse(self, callback_data: str) -> typing.Dict[str, str]: """ Parse data from the callback data + :param callback_data: string, use to telebot.types.CallbackQuery to parse it from string to a dict + :return: dict parsed from callback data """ prefix, *parts = callback_data.split(self.sep) @@ -92,6 +104,9 @@ def parse(self, callback_data: str) -> typing.Dict[str, str]: def filter(self, **config) -> CallbackDataFilter: """ Generate filter + + :param config: specified named parameters will be checked with CallbackQury.data + :return: CallbackDataFilter class """ for key in config.keys(): From 6770011dd77ab5e64255bd842034e1de180e02bb Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 27 Nov 2021 19:04:03 +0500 Subject: [PATCH 0761/1808] Middleware support --- telebot/__init__.py | 256 ++++++++++++---------------- telebot/asyncio_handler_backends.py | 156 ++--------------- telebot/asyncio_helper.py | 31 +--- 3 files changed, 137 insertions(+), 306 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 68c2e48ed..89689eb81 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -13,6 +13,9 @@ import telebot.util import telebot.types + +from inspect import signature + logger = logging.getLogger('TeleBot') formatter = logging.Formatter( @@ -69,6 +72,30 @@ def handle(self, exception): return False +class SkipHandler: + """ + Class for skipping handlers. + Just return instance of this class + in middleware to skip handler. + Update will go to post_process, + but will skip execution of handler. + """ + + def __init__(self) -> None: + pass + +class CancelUpdate: + """ + Class for canceling updates. + Just return instance of this class + in middleware to skip update. + Update will skip handler and execution + of post_process in middlewares. + """ + + def __init__(self) -> None: + pass + class TeleBot: """ This is TeleBot Class Methods: @@ -3351,33 +3378,16 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, self.current_states = asyncio_handler_backends.StateMemory() - if asyncio_helper.ENABLE_MIDDLEWARE: - self.typed_middleware_handlers = { - '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': [] - } - self.default_middleware_handlers = [] + self.middlewares = [] async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, - timeout: Optional[int]=None, allowed_updates: Optional[List]=None) -> types.Update: - json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates) + timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> types.Update: + json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, - long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None, + request_timeout: int=20, allowed_updates: Optional[List[str]]=None, none_stop: Optional[bool]=None): """ This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. @@ -3389,7 +3399,7 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= :param non_stop: Do not stop polling when an ApiException occurs. :param timeout: Request connection timeout :param skip_pending: skip old updates - :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param request_timeout: Timeout in seconds for a request. :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -3406,9 +3416,9 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= if skip_pending: await self.skip_updates() - await self._process_polling(non_stop, interval, timeout, long_polling_timeout, allowed_updates) + await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) - async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, logger_level=logging.ERROR, + async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -3432,7 +3442,7 @@ async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long self._polling = True while self._polling: try: - await self._process_polling(non_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, + await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: @@ -3447,13 +3457,13 @@ async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long logger.error("Break infinity polling") async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, - long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None): + request_timeout: int=20, allowed_updates: Optional[List[str]]=None): """ Function to process polling. :param non_stop: Do not stop polling when an ApiException occurs. :param interval: Delay between two update retrivals :param timeout: Request connection timeout - :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param request_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. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -3471,49 +3481,74 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: while self._polling: try: - updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout) - - if updates: - logger.debug(f"Received {len(updates)} updates.") - - await self.process_new_updates(updates) - if interval: await asyncio.sleep(interval) - except KeyboardInterrupt: - logger.info("KeyboardInterrupt received.") - break + updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout, request_timeout=request_timeout) except asyncio.CancelledError: - break + return + except asyncio_helper.ApiTelegramException as e: - logger.info(str(e)) + logger.error(str(e)) continue except Exception as e: logger.error('Cause exception while getting updates.') - logger.error(str(e)) - await asyncio.sleep(3) - continue + if non_stop: + logger.error(str(e)) + await asyncio.sleep(3) + continue + else: + raise e + if updates: + self.offset = updates[-1].update_id + 1 + self._loop_create_task(self.process_new_updates(updates)) # Seperate task for processing updates + if interval: await asyncio.sleep(interval) finally: self._polling = False logger.warning('Polling is stopped.') - async def _loop_create_task(self, coro): + def _loop_create_task(self, coro): return asyncio.create_task(coro) - async def _process_updates(self, handlers, messages): + async def _process_updates(self, handlers, messages, update_type): + """ + Process updates. + :param handlers: + :param messages: + :return: + """ for message in messages: - for message_handler in handlers: - process_update = await self._test_message_handler(message_handler, message) - if not process_update: - continue - elif process_update: - try: - await self._loop_create_task(message_handler['function'](message)) + middleware = await self.process_middlewares(message, update_type) + self._loop_create_task(self._run_middlewares_and_handlers(handlers, message, middleware)) + + + async def _run_middlewares_and_handlers(self, handlers, message, middleware): + handler_error = None + data = {} + for message_handler in handlers: + process_update = await self._test_message_handler(message_handler, message) + if not process_update: + continue + elif process_update: + if middleware: + middleware_result = await middleware.pre_process(message, data) + if isinstance(middleware_result, SkipHandler): + await middleware.post_process(message, data, handler_error) break - except Exception as e: - logger.error(str(e)) + if isinstance(middleware_result, CancelUpdate): + return + try: + if "data" in signature(message_handler['function']).parameters: + await message_handler['function'](message, data) + else: + await message_handler['function'](message) + break + except Exception as e: + handler_error = e + logger.info(str(e)) + if middleware: + await middleware.post_process(message, data, handler_error) # update handling async def process_new_updates(self, updates): upd_count = len(updates) @@ -3535,21 +3570,8 @@ async def process_new_updates(self, updates): new_chat_members = None chat_join_request = None for update in updates: - if asyncio_helper.ENABLE_MIDDLEWARE: - try: - self.process_middlewares(update) - except Exception as e: - logger.error(str(e)) - if not self.suppress_middleware_excepions: - raise - else: - if update.update_id > self.offset: self.offset = update.update_id - continue logger.debug('Processing updates: {0}'.format(update)) - if update.update_id: - self.offset = update.update_id + 1 if update.message: - logger.info('Processing message') if new_messages is None: new_messages = [] new_messages.append(update.message) if update.edited_message: @@ -3620,69 +3642,55 @@ async def process_new_updates(self, updates): await self.process_new_chat_member(new_chat_members) if chat_join_request: await self.process_chat_join_request(chat_join_request) - async def process_new_messages(self, new_messages): await self.__notify_update(new_messages) - await self._process_updates(self.message_handlers, new_messages) + await self._process_updates(self.message_handlers, new_messages, 'message') async def process_new_edited_messages(self, edited_message): - await self._process_updates(self.edited_message_handlers, edited_message) + await self._process_updates(self.edited_message_handlers, edited_message, 'edited_message') async def process_new_channel_posts(self, channel_post): - await self._process_updates(self.channel_post_handlers, channel_post) + await self._process_updates(self.channel_post_handlers, channel_post , 'channel_post') async def process_new_edited_channel_posts(self, edited_channel_post): - await self._process_updates(self.edited_channel_post_handlers, edited_channel_post) + await self._process_updates(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') async def process_new_inline_query(self, new_inline_querys): - await self._process_updates(self.inline_handlers, new_inline_querys) + await self._process_updates(self.inline_handlers, new_inline_querys, 'inline_query') async def process_new_chosen_inline_query(self, new_chosen_inline_querys): - await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_querys) + await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') async def process_new_callback_query(self, new_callback_querys): - await self._process_updates(self.callback_query_handlers, new_callback_querys) + await self._process_updates(self.callback_query_handlers, new_callback_querys, 'callback_query') async def process_new_shipping_query(self, new_shipping_querys): - await self._process_updates(self.shipping_query_handlers, new_shipping_querys) + await self._process_updates(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') async def process_new_pre_checkout_query(self, pre_checkout_querys): - await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_querys) + await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') async def process_new_poll(self, polls): - await self._process_updates(self.poll_handlers, polls) + await self._process_updates(self.poll_handlers, polls, 'poll') async def process_new_poll_answer(self, poll_answers): - await self._process_updates(self.poll_answer_handlers, poll_answers) + await self._process_updates(self.poll_answer_handlers, poll_answers, 'poll_answer') async def process_new_my_chat_member(self, my_chat_members): - await self._process_updates(self.my_chat_member_handlers, my_chat_members) + await self._process_updates(self.my_chat_member_handlers, my_chat_members, 'my_chat_member') async def process_new_chat_member(self, chat_members): - await self._process_updates(self.chat_member_handlers, chat_members) + await self._process_updates(self.chat_member_handlers, chat_members, 'chat_member') async def process_chat_join_request(self, chat_join_request): - await self._process_updates(self.chat_join_request_handlers, chat_join_request) - - async def process_middlewares(self, update): - for update_type, middlewares in self.typed_middleware_handlers.items(): - if getattr(update, update_type) is not None: - for typed_middleware_handler in middlewares: - try: - typed_middleware_handler(self, getattr(update, update_type)) - except Exception as e: - e.args = e.args + (f'Typed middleware handler "{typed_middleware_handler.__qualname__}"',) - raise - - if len(self.default_middleware_handlers) > 0: - for default_middleware_handler in self.default_middleware_handlers: - try: - default_middleware_handler(self, update) - except Exception as e: - e.args = e.args + (f'Default middleware handler "{default_middleware_handler.__qualname__}"',) - raise + await self._process_updates(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') + async def process_middlewares(self, update, update_type): + for middleware in self.middlewares: + if update_type in middleware.update_types: + return middleware + return None async def __notify_update(self, new_messages): if len(self.update_listener) == 0: @@ -3759,56 +3767,16 @@ async def _check_filter(self, message_filter, filter_value, message): elif isinstance(filter_check, asyncio_filters.AdvancedCustomFilter): return await filter_check.check(message, filter_value) else: - logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter!") + logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") return False - def middleware_handler(self, update_types=None): - """ - Middleware handler decorator. - - This decorator can be used to decorate functions that must be handled as middlewares before entering any other - message handlers - But, be careful and check type of the update inside the handler if more than one update_type is given - - Example: - - bot = TeleBot('TOKEN') - - # Print post message text before entering to any post_channel handlers - @bot.middleware_handler(update_types=['channel_post', 'edited_channel_post']) - def print_channel_post_text(bot_instance, channel_post): - print(channel_post.text) - - # Print update id before entering to any handlers - @bot.middleware_handler() - def print_channel_post_text(bot_instance, update): - print(update.update_id) - - :param update_types: Optional list of update types that can be passed into the middleware handler. - + def setup_middleware(self, middleware): """ - - def decorator(handler): - self.add_middleware_handler(handler, update_types) - return handler - - return decorator - - def add_middleware_handler(self, handler, update_types=None): - """ - Add middleware handler - :param handler: - :param update_types: + Setup middleware + :param middleware: :return: """ - if not asyncio_helper.ENABLE_MIDDLEWARE: - raise RuntimeError("Middleware is not enabled. Use asyncio_helper.ENABLE_MIDDLEWARE.") - - if update_types: - for update_type in update_types: - self.typed_middleware_handlers[update_type].append(handler) - else: - self.default_middleware_handlers.append(handler) + self.middlewares.append(middleware) def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 001f869b2..b46c9887c 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -1,146 +1,6 @@ import os import pickle -import threading -from telebot import apihelper - - -class HandlerBackend(object): - """ - Class for saving (next step|reply) handlers - """ - def __init__(self, handlers=None): - if handlers is None: - handlers = {} - self.handlers = handlers - - async def register_handler(self, handler_group_id, handler): - raise NotImplementedError() - - async def clear_handlers(self, handler_group_id): - raise NotImplementedError() - - async def get_handlers(self, handler_group_id): - raise NotImplementedError() - - -class MemoryHandlerBackend(HandlerBackend): - async def register_handler(self, handler_group_id, handler): - if handler_group_id in self.handlers: - self.handlers[handler_group_id].append(handler) - else: - self.handlers[handler_group_id] = [handler] - - async def clear_handlers(self, handler_group_id): - self.handlers.pop(handler_group_id, None) - - async def get_handlers(self, handler_group_id): - return self.handlers.pop(handler_group_id, None) - - async def load_handlers(self, filename, del_file_after_loading): - raise NotImplementedError() - - -class FileHandlerBackend(HandlerBackend): - def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120): - super(FileHandlerBackend, self).__init__(handlers) - self.filename = filename - self.delay = delay - self.timer = threading.Timer(delay, self.save_handlers) - - async def register_handler(self, handler_group_id, handler): - if handler_group_id in self.handlers: - self.handlers[handler_group_id].append(handler) - else: - self.handlers[handler_group_id] = [handler] - await self.start_save_timer() - - async def clear_handlers(self, handler_group_id): - self.handlers.pop(handler_group_id, None) - await self.start_save_timer() - - async def get_handlers(self, handler_group_id): - handlers = self.handlers.pop(handler_group_id, None) - await self.start_save_timer() - return handlers - - async def start_save_timer(self): - if not self.timer.is_alive(): - if self.delay <= 0: - self.save_handlers() - else: - self.timer = threading.Timer(self.delay, self.save_handlers) - self.timer.start() - - async def save_handlers(self): - await self.dump_handlers(self.handlers, self.filename) - - async def load_handlers(self, filename=None, del_file_after_loading=True): - if not filename: - filename = self.filename - tmp = await self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading) - if tmp is not None: - self.handlers.update(tmp) - - @staticmethod - async def dump_handlers(handlers, filename, file_mode="wb"): - dirs = filename.rsplit('/', maxsplit=1)[0] - os.makedirs(dirs, exist_ok=True) - - with open(filename + ".tmp", file_mode) as file: - if (apihelper.CUSTOM_SERIALIZER is None): - pickle.dump(handlers, file) - else: - apihelper.CUSTOM_SERIALIZER.dump(handlers, file) - - if os.path.isfile(filename): - os.remove(filename) - - os.rename(filename + ".tmp", filename) - - @staticmethod - async def return_load_handlers(filename, del_file_after_loading=True): - if os.path.isfile(filename) and os.path.getsize(filename) > 0: - with open(filename, "rb") as file: - if (apihelper.CUSTOM_SERIALIZER is None): - handlers = pickle.load(file) - else: - handlers = apihelper.CUSTOM_SERIALIZER.load(file) - - if del_file_after_loading: - os.remove(filename) - - return handlers - - -class RedisHandlerBackend(HandlerBackend): - def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot', password=None): - super(RedisHandlerBackend, self).__init__(handlers) - from redis import Redis - self.prefix = prefix - self.redis = Redis(host, port, db, password) - - async def _key(self, handle_group_id): - return ':'.join((self.prefix, str(handle_group_id))) - - async def register_handler(self, handler_group_id, handler): - handlers = [] - value = self.redis.get(self._key(handler_group_id)) - if value: - handlers = pickle.loads(value) - handlers.append(handler) - self.redis.set(self._key(handler_group_id), pickle.dumps(handlers)) - - async def clear_handlers(self, handler_group_id): - self.redis.delete(self._key(handler_group_id)) - - async def get_handlers(self, handler_group_id): - handlers = None - value = self.redis.get(self._key(handler_group_id)) - if value: - handlers = pickle.loads(value) - self.clear_handlers(handler_group_id) - return handlers class StateMemory: @@ -341,3 +201,19 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): await self.obj._save_data(old_data) return + + +class BaseMiddleware: + """ + Base class for middleware. + + Your middlewares should be inherited from this class. + """ + def __init__(self): + pass + + async def pre_process(self, message, data): + raise NotImplementedError + async def post_process(self, message, data, exception): + raise NotImplementedError + diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 7bb649e8b..3d1189d66 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1,28 +1,19 @@ -import asyncio +import asyncio # for future uses from time import time import aiohttp from telebot import types import json -import logging try: import ujson as json except ImportError: import json -import requests -from requests.exceptions import HTTPError, ConnectionError, Timeout - -try: - # noinspection PyUnresolvedReferences - from requests.packages.urllib3 import fields - format_header_param = fields.format_header_param -except ImportError: - format_header_param = None - API_URL = 'https://api.telegram.org/bot{0}/{1}' from datetime import datetime + +import telebot from telebot import util class SessionBase: @@ -44,19 +35,16 @@ async def _get_new_session(self): LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates) +logger = telebot.logger RETRY_ON_ERROR = False RETRY_TIMEOUT = 2 MAX_RETRIES = 15 -CUSTOM_SERIALIZER = None -CUSTOM_REQUEST_SENDER = None - -ENABLE_MIDDLEWARE = False - -async def _process_request(token, url, method='get', params=None, files=None): +async def _process_request(token, url, method='get', params=None, files=None, request_timeout=None): async with await session_manager._get_new_session() as session: - async with session.get(API_URL.format(token, url), params=params, data=files) as response: + async with session.get(API_URL.format(token, url), params=params, data=files, timeout=request_timeout) as response: + logger.debug("Request: method={0} url={1} params={2} files={3} request_timeout={4}".format(method, url, params, files, request_timeout).replace(token, token.split(':')[0] + ":{TOKEN}")) json_result = await _check_result(url, response) if json_result: return json_result['result'] @@ -155,7 +143,7 @@ async def get_webhook_info(token, timeout=None): async def get_updates(token, offset=None, limit=None, - timeout=None, allowed_updates=None, long_polling_timeout=None): + timeout=None, allowed_updates=None, request_timeout=None): method_name = 'getUpdates' params = {} if offset: @@ -166,8 +154,7 @@ async def get_updates(token, offset=None, limit=None, params['timeout'] = timeout elif allowed_updates: params['allowed_updates'] = allowed_updates - params['long_polling_timeout'] = long_polling_timeout if long_polling_timeout else LONG_POLLING_TIMEOUT - return await _process_request(token, method_name, params=params) + return await _process_request(token, method_name, params=params, request_timeout=request_timeout) async def _check_result(method_name, result): """ From f666c15a1fc5ed3bc98015e60256e486c478d2ec Mon Sep 17 00:00:00 2001 From: Carlos Morales Aguilera Date: Sat, 27 Nov 2021 16:32:58 +0100 Subject: [PATCH 0762/1808] Add GrandQuiz Bot by Carlosma7 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 37d56c792..49166c7f2 100644 --- a/README.md +++ b/README.md @@ -796,7 +796,6 @@ Get help. Discuss. Chat. * [Vlun Finder Bot](https://github.com/resinprotein2333/Vlun-Finder-bot) by [Resinprotein2333](https://github.com/resinprotein2333). This bot can help you to find The information of CVE vulnerabilities. * [ETHGasFeeTrackerBot](https://t.me/ETHGasFeeTrackerBot) ([Source](https://github.com/DevAdvik/ETHGasFeeTrackerBot]) by *DevAdvik* - Get Live Ethereum Gas Fees in GWEI * [Google Sheet Bot](https://github.com/JoachimStanislaus/Tele_Sheet_bot) by [JoachimStanislaus](https://github.com/JoachimStanislaus). This bot can help you to track your expenses by uploading your bot entries to your google sheet. - - +* [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From a9b422783f05b71d09319ee94d019c1b8252a9bc Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 27 Nov 2021 23:41:39 +0500 Subject: [PATCH 0763/1808] Middlewares, new file, and examples --- .../asynchronous_telebot/chat_join_request.py | 11 + .../chat_member_example.py | 33 + .../custom_filters/admin_filter_example.py | 13 + .../custom_filters/general_custom_filters.py | 44 + .../custom_filters/id_filter_example.py | 19 + .../custom_filters/is_filter_example.py | 22 + .../custom_filters/text_filter_example.py | 21 + .../asynchronous_telebot/custom_states.py | 75 + examples/asynchronous_telebot/echo_bot.py | 27 + .../asynchronous_telebot/register_handler.py | 19 + .../skip_updates_example.py | 13 + telebot/__init__.py | 2823 +--------------- telebot/async_telebot.py | 2869 +++++++++++++++++ telebot/asyncio_filters.py | 10 +- telebot/asyncio_handler_backends.py | 10 +- 15 files changed, 3178 insertions(+), 2831 deletions(-) create mode 100644 examples/asynchronous_telebot/chat_join_request.py create mode 100644 examples/asynchronous_telebot/chat_member_example.py create mode 100644 examples/asynchronous_telebot/custom_filters/admin_filter_example.py create mode 100644 examples/asynchronous_telebot/custom_filters/general_custom_filters.py create mode 100644 examples/asynchronous_telebot/custom_filters/id_filter_example.py create mode 100644 examples/asynchronous_telebot/custom_filters/is_filter_example.py create mode 100644 examples/asynchronous_telebot/custom_filters/text_filter_example.py create mode 100644 examples/asynchronous_telebot/custom_states.py create mode 100644 examples/asynchronous_telebot/echo_bot.py create mode 100644 examples/asynchronous_telebot/register_handler.py create mode 100644 examples/asynchronous_telebot/skip_updates_example.py create mode 100644 telebot/async_telebot.py diff --git a/examples/asynchronous_telebot/chat_join_request.py b/examples/asynchronous_telebot/chat_join_request.py new file mode 100644 index 000000000..6b2bfb7b9 --- /dev/null +++ b/examples/asynchronous_telebot/chat_join_request.py @@ -0,0 +1,11 @@ +from telebot.async_telebot import AsyncTeleBot +import asyncio +import telebot +bot = AsyncTeleBot('TOKEN') + +@bot.chat_join_request_handler() +async def make_some(message: telebot.types.ChatJoinRequest): + await bot.send_message(message.chat.id, 'I accepted a new user!') + await bot.approve_chat_join_request(message.chat.id, message.from_user.id) + +asyncio.run(bot.polling(skip_pending=True)) \ No newline at end of file diff --git a/examples/asynchronous_telebot/chat_member_example.py b/examples/asynchronous_telebot/chat_member_example.py new file mode 100644 index 000000000..7806cfd73 --- /dev/null +++ b/examples/asynchronous_telebot/chat_member_example.py @@ -0,0 +1,33 @@ +from telebot import types,util +from telebot.async_telebot import AsyncTeleBot +import asyncio +bot = AsyncTeleBot('TOKEN') + +#chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member. +@bot.chat_member_handler() +async def chat_m(message: types.ChatMemberUpdated): + old = message.old_chat_member + new = message.new_chat_member + if new.status == "member": + await bot.send_message(message.chat.id,"Hello {name}!".format(name=new.user.first_name)) # Welcome message + +#if bot is added to group, this handler will work +@bot.my_chat_member_handler() +async def my_chat_m(message: types.ChatMemberUpdated): + old = message.old_chat_member + new = message.new_chat_member + if new.status == "member": + await bot.send_message(message.chat.id,"Somebody added me to group") # Welcome message, if bot was added to group + await bot.leave_chat(message.chat.id) + +#content_Type_service is: +#'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', +#'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', +#'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', +#'voice_chat_participants_invited', 'message_auto_delete_timer_changed' +# this handler deletes service messages + +@bot.message_handler(content_types=util.content_type_service) +async def delall(message: types.Message): + await bot.delete_message(message.chat.id,message.message_id) +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/admin_filter_example.py b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py new file mode 100644 index 000000000..3aee738ab --- /dev/null +++ b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py @@ -0,0 +1,13 @@ +import asyncio +from telebot.async_telebot import AsyncTeleBot +from telebot import asyncio_filters +bot = AsyncTeleBot('TOKEN') + +# Handler +@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) +async def answer_for_admin(message): + await bot.send_message(message.chat.id,"hello my admin") + +# Register filter +bot.add_custom_filter(asyncio_filters.IsAdminFilter(bot)) +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/general_custom_filters.py b/examples/asynchronous_telebot/custom_filters/general_custom_filters.py new file mode 100644 index 000000000..dfeeb880c --- /dev/null +++ b/examples/asynchronous_telebot/custom_filters/general_custom_filters.py @@ -0,0 +1,44 @@ +from telebot.async_telebot import AsyncTeleBot +import telebot +bot = AsyncTeleBot('TOKEN') + + +# AdvancedCustomFilter is for list, string filter values +class MainFilter(telebot.asyncio_filters.AdvancedCustomFilter): + key='text' + @staticmethod + async def check(message, text): + return message.text in text + +# SimpleCustomFilter is for boolean values, such as is_admin=True +class IsAdmin(telebot.asyncio_filters.SimpleCustomFilter): + key='is_admin' + @staticmethod + async def check(message: telebot.types.Message): + result = await bot.get_chat_member(message.chat.id,message.from_user.id) + return result.status in ['administrator','creator'] + + +@bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin +async def admin_rep(message): + await bot.send_message(message.chat.id, "Hi admin") + +@bot.message_handler(is_admin=False, commands=['admin']) # If user is not admin +async def not_admin(message): + await bot.send_message(message.chat.id, "You are not admin") + +@bot.message_handler(text=['hi']) # Response to hi message +async def welcome_hi(message): + await bot.send_message(message.chat.id, 'You said hi') + +@bot.message_handler(text=['bye']) # Response to bye message +async def bye_user(message): + await bot.send_message(message.chat.id, 'You said bye') + + +# Do not forget to register filters +bot.add_custom_filter(MainFilter()) +bot.add_custom_filter(IsAdmin()) + +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/id_filter_example.py b/examples/asynchronous_telebot/custom_filters/id_filter_example.py new file mode 100644 index 000000000..5878bc71d --- /dev/null +++ b/examples/asynchronous_telebot/custom_filters/id_filter_example.py @@ -0,0 +1,19 @@ +from telebot.async_telebot import AsyncTeleBot +import telebot +import asyncio +bot = AsyncTeleBot('TOKEN') + + +# Chat id can be private or supergroups. +@bot.message_handler(chat_id=[12345678], commands=['admin']) # chat_id checks id corresponds to your list or not. +async def admin_rep(message): + await bot.send_message(message.chat.id, "You are allowed to use this command.") + +@bot.message_handler(commands=['admin']) +async def not_admin(message): + await bot.send_message(message.chat.id, "You are not allowed to use this command") + +# Do not forget to register +bot.add_custom_filter(telebot.asyncio_filters.ChatFilter()) + +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/is_filter_example.py b/examples/asynchronous_telebot/custom_filters/is_filter_example.py new file mode 100644 index 000000000..20857bea4 --- /dev/null +++ b/examples/asynchronous_telebot/custom_filters/is_filter_example.py @@ -0,0 +1,22 @@ +from telebot.async_telebot import AsyncTeleBot +import telebot +import asyncio +bot = AsyncTeleBot('TOKEN') + + + +# Check if message is a reply +@bot.message_handler(is_reply=True) +async def start_filter(message): + await bot.send_message(message.chat.id, "Looks like you replied to my message.") + +# Check if message was forwarded +@bot.message_handler(is_forwarded=True) +async def text_filter(message): + await bot.send_message(message.chat.id, "I do not accept forwarded messages!") + +# Do not forget to register filters +bot.add_custom_filter(telebot.asyncio_filters.IsReplyFilter()) +bot.add_custom_filter(telebot.asyncio_filters.ForwardFilter()) + +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/text_filter_example.py b/examples/asynchronous_telebot/custom_filters/text_filter_example.py new file mode 100644 index 000000000..57513ea55 --- /dev/null +++ b/examples/asynchronous_telebot/custom_filters/text_filter_example.py @@ -0,0 +1,21 @@ +from telebot.async_telebot import AsyncTeleBot +import telebot +import asyncio +bot = AsyncTeleBot('TOKEN') + + +# Check if message starts with @admin tag +@bot.message_handler(text_startswith="@admin") +async def start_filter(message): + await bot.send_message(message.chat.id, "Looks like you are calling admin, wait...") + +# Check if text is hi or hello +@bot.message_handler(text=['hi','hello']) +async def text_filter(message): + await bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name)) + +# Do not forget to register filters +bot.add_custom_filter(telebot.asyncio_filters.TextMatchFilter()) +bot.add_custom_filter(telebot.asyncio_filters.TextStartsFilter()) + +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py new file mode 100644 index 000000000..2f0257a1c --- /dev/null +++ b/examples/asynchronous_telebot/custom_states.py @@ -0,0 +1,75 @@ +import telebot +from telebot import asyncio_filters +from telebot.async_telebot import AsyncTeleBot +import asyncio +bot = AsyncTeleBot('TOKEN') + + + +class MyStates: + name = 1 + surname = 2 + age = 3 + + + +@bot.message_handler(commands=['start']) +async def start_ex(message): + """ + Start command. Here we are starting state + """ + await bot.set_state(message.from_user.id, MyStates.name) + await bot.send_message(message.chat.id, 'Hi, write me a name') + + + +@bot.message_handler(state="*", commands='cancel') +async def any_state(message): + """ + Cancel state + """ + await bot.send_message(message.chat.id, "Your state was cancelled.") + await bot.delete_state(message.from_user.id) + +@bot.message_handler(state=MyStates.name) +async def name_get(message): + """ + State 1. Will process when user's state is 1. + """ + await bot.send_message(message.chat.id, f'Now write me a surname') + await bot.set_state(message.from_user.id, MyStates.surname) + async with bot.retrieve_data(message.from_user.id) as data: + data['name'] = message.text + + +@bot.message_handler(state=MyStates.surname) +async def ask_age(message): + """ + State 2. Will process when user's state is 2. + """ + await bot.send_message(message.chat.id, "What is your age?") + await bot.set_state(message.from_user.id, MyStates.age) + async with bot.retrieve_data(message.from_user.id) as data: + data['surname'] = message.text + +# result +@bot.message_handler(state=MyStates.age, is_digit=True) +async def ready_for_answer(message): + async with bot.retrieve_data(message.from_user.id) as data: + await bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") + await bot.delete_state(message.from_user.id) + +#incorrect number +@bot.message_handler(state=MyStates.age, is_digit=False) +async def age_incorrect(message): + await bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') + +# register filters + +bot.add_custom_filter(asyncio_filters.StateFilter(bot)) +bot.add_custom_filter(asyncio_filters.IsDigitFilter()) + +# set saving states into file. +bot.enable_saving_states() # you can delete this if you do not need to save states + +asyncio.run(bot.polling()) \ No newline at end of file diff --git a/examples/asynchronous_telebot/echo_bot.py b/examples/asynchronous_telebot/echo_bot.py new file mode 100644 index 000000000..24cbe3fe2 --- /dev/null +++ b/examples/asynchronous_telebot/echo_bot.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +# This is a simple echo bot using the decorator mechanism. +# It echoes any incoming text messages. + +from telebot.async_telebot import AsyncTeleBot +import asyncio +bot = AsyncTeleBot('TOKEN') + + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +async def send_welcome(message): + await bot.reply_to(message, """\ +Hi there, I am EchoBot. +I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\ +""") + + +# Handle all other messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +async def echo_message(message): + await bot.reply_to(message, message.text) + + +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/register_handler.py b/examples/asynchronous_telebot/register_handler.py new file mode 100644 index 000000000..04dabd41c --- /dev/null +++ b/examples/asynchronous_telebot/register_handler.py @@ -0,0 +1,19 @@ +from telebot.async_telebot import AsyncTeleBot +import asyncio +bot = AsyncTeleBot('TOKEN') + +async def start_executor(message): + await bot.send_message(message.chat.id, 'Hello!') + +bot.register_message_handler(start_executor, commands=['start']) # Start command executor + +# See also +# bot.register_callback_query_handler(*args, **kwargs) +# bot.register_channel_post_handler(*args, **kwargs) +# bot.register_chat_member_handler(*args, **kwargs) +# bot.register_inline_handler(*args, **kwargs) +# bot.register_my_chat_member_handler(*args, **kwargs) +# bot.register_edited_message_handler(*args, **kwargs) +# And other functions.. + +asyncio.run(bot.polling(skip_pending=True)) diff --git a/examples/asynchronous_telebot/skip_updates_example.py b/examples/asynchronous_telebot/skip_updates_example.py new file mode 100644 index 000000000..dc2c1578f --- /dev/null +++ b/examples/asynchronous_telebot/skip_updates_example.py @@ -0,0 +1,13 @@ +from telebot.async_telebot import AsyncTeleBot +import asyncio +bot = AsyncTeleBot('TOKEN') + +@bot.message_handler(commands=['start', 'help']) +async def send_welcome(message): + await bot.reply_to(message, "Howdy, how are you doing?") + +@bot.message_handler(func=lambda message: True) +async def echo_all(message): + await bot.reply_to(message, message.text) + +asyncio.run(bot.polling(skip_pending=True))# Skip pending skips old updates diff --git a/telebot/__init__.py b/telebot/__init__.py index 89689eb81..7983a2648 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -14,7 +14,6 @@ import telebot.types -from inspect import signature logger = logging.getLogger('TeleBot') @@ -28,12 +27,9 @@ logger.setLevel(logging.ERROR) -from telebot import apihelper, util, types, asyncio_helper +from telebot import apihelper, util, types from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMemory, StateFile from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter -import asyncio -from telebot import asyncio_handler_backends -from telebot import asyncio_filters @@ -3340,2823 +3336,6 @@ def _notify_command_handlers(self, handlers, new_messages): break -class AsyncTeleBot: - - - def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, - exception_handler=None,suppress_middleware_excepions=False) -> None: # TODO: ADD TYPEHINTS - self.token = token - logger.info('creating some objects..') - self.loop = asyncio.get_event_loop() - self.offset = offset - self.token = token - self.parse_mode = parse_mode - self.update_listener = [] - self.suppress_middleware_excepions = suppress_middleware_excepions - - self.exc_info = None - - self.exception_handler = exception_handler - - self.message_handlers = [] - self.edited_message_handlers = [] - self.channel_post_handlers = [] - self.edited_channel_post_handlers = [] - self.inline_handlers = [] - self.chosen_inline_handlers = [] - self.callback_query_handlers = [] - self.shipping_query_handlers = [] - self.pre_checkout_query_handlers = [] - self.poll_handlers = [] - self.poll_answer_handlers = [] - self.my_chat_member_handlers = [] - self.chat_member_handlers = [] - self.chat_join_request_handlers = [] - self.custom_filters = {} - self.state_handlers = [] - - self.current_states = asyncio_handler_backends.StateMemory() - - - self.middlewares = [] - - - async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, - timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> types.Update: - json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) - return [types.Update.de_json(ju) for ju in json_updates] - - async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, - request_timeout: int=20, allowed_updates: Optional[List[str]]=None, - none_stop: Optional[bool]=None): - """ - This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. - - Warning: Do not call this function more than once! - - Always get updates. - :param interval: Delay between two update retrivals - :param non_stop: Do not stop polling when an ApiException occurs. - :param timeout: Request connection timeout - :param skip_pending: skip old updates - :param request_timeout: Timeout in seconds for a request. - :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). - If not specified, the previous setting will be used. - - 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 skip_pending: - await self.skip_updates() - await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) - - async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, - allowed_updates: Optional[List[str]]=None, *args, **kwargs): - """ - Wrap polling with infinite loop and exception handling to avoid bot stops polling. - - :param timeout: Request connection timeout - :param long_polling_timeout: Timeout in seconds for long polling (see API docs) - :param skip_pending: skip old updates - :param logger_level: Custom logging level for infinity_polling logging. - Use logger levels from logging as a value. None/NOTSET = no error logging - :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). - If not specified, the previous setting will be used. - - 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. - """ - if skip_pending: - await self.skip_updates() - self._polling = True - while self._polling: - try: - await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, - allowed_updates=allowed_updates, *args, **kwargs) - except Exception as e: - if logger_level and logger_level >= logging.ERROR: - logger.error("Infinity polling exception: %s", str(e)) - if logger_level and logger_level >= logging.DEBUG: - logger.error("Exception traceback:\n%s", traceback.format_exc()) - time.sleep(3) - continue - if logger_level and logger_level >= logging.INFO: - logger.error("Infinity polling: polling exited") - if logger_level and logger_level >= logging.INFO: - logger.error("Break infinity polling") - - async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, - request_timeout: int=20, allowed_updates: Optional[List[str]]=None): - """ - Function to process polling. - :param non_stop: Do not stop polling when an ApiException occurs. - :param interval: Delay between two update retrivals - :param timeout: Request connection timeout - :param request_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. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). - If not specified, the previous setting will be used. - - 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. - :return: - - """ - self._polling = True - - try: - while self._polling: - try: - - updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout, request_timeout=request_timeout) - except asyncio.CancelledError: - return - - except asyncio_helper.ApiTelegramException as e: - logger.error(str(e)) - - continue - except Exception as e: - logger.error('Cause exception while getting updates.') - if non_stop: - logger.error(str(e)) - await asyncio.sleep(3) - continue - else: - raise e - if updates: - self.offset = updates[-1].update_id + 1 - self._loop_create_task(self.process_new_updates(updates)) # Seperate task for processing updates - if interval: await asyncio.sleep(interval) - - finally: - self._polling = False - logger.warning('Polling is stopped.') - - - def _loop_create_task(self, coro): - return asyncio.create_task(coro) - - async def _process_updates(self, handlers, messages, update_type): - """ - Process updates. - :param handlers: - :param messages: - :return: - """ - for message in messages: - middleware = await self.process_middlewares(message, update_type) - self._loop_create_task(self._run_middlewares_and_handlers(handlers, message, middleware)) - - - async def _run_middlewares_and_handlers(self, handlers, message, middleware): - handler_error = None - data = {} - for message_handler in handlers: - process_update = await self._test_message_handler(message_handler, message) - if not process_update: - continue - elif process_update: - if middleware: - middleware_result = await middleware.pre_process(message, data) - if isinstance(middleware_result, SkipHandler): - await middleware.post_process(message, data, handler_error) - break - if isinstance(middleware_result, CancelUpdate): - return - try: - if "data" in signature(message_handler['function']).parameters: - await message_handler['function'](message, data) - else: - await message_handler['function'](message) - break - except Exception as e: - handler_error = e - logger.info(str(e)) - - if middleware: - await middleware.post_process(message, data, handler_error) - # update handling - async def process_new_updates(self, updates): - upd_count = len(updates) - logger.info('Received {0} new updates'.format(upd_count)) - if upd_count == 0: return - - new_messages = None - new_edited_messages = None - new_channel_posts = None - new_edited_channel_posts = None - new_inline_queries = None - new_chosen_inline_results = None - new_callback_queries = None - new_shipping_queries = None - new_pre_checkout_queries = None - new_polls = None - new_poll_answers = None - new_my_chat_members = None - new_chat_members = None - chat_join_request = None - for update in updates: - logger.debug('Processing updates: {0}'.format(update)) - if update.message: - if new_messages is None: new_messages = [] - new_messages.append(update.message) - if update.edited_message: - if new_edited_messages is None: new_edited_messages = [] - new_edited_messages.append(update.edited_message) - if update.channel_post: - if new_channel_posts is None: new_channel_posts = [] - new_channel_posts.append(update.channel_post) - if update.edited_channel_post: - if new_edited_channel_posts is None: new_edited_channel_posts = [] - new_edited_channel_posts.append(update.edited_channel_post) - if update.inline_query: - if new_inline_queries is None: new_inline_queries = [] - new_inline_queries.append(update.inline_query) - if update.chosen_inline_result: - if new_chosen_inline_results is None: new_chosen_inline_results = [] - new_chosen_inline_results.append(update.chosen_inline_result) - if update.callback_query: - if new_callback_queries is None: new_callback_queries = [] - new_callback_queries.append(update.callback_query) - if update.shipping_query: - if new_shipping_queries is None: new_shipping_queries = [] - new_shipping_queries.append(update.shipping_query) - if update.pre_checkout_query: - if new_pre_checkout_queries is None: new_pre_checkout_queries = [] - new_pre_checkout_queries.append(update.pre_checkout_query) - if update.poll: - if new_polls is None: new_polls = [] - new_polls.append(update.poll) - if update.poll_answer: - if new_poll_answers is None: new_poll_answers = [] - new_poll_answers.append(update.poll_answer) - if update.my_chat_member: - if new_my_chat_members is None: new_my_chat_members = [] - new_my_chat_members.append(update.my_chat_member) - if update.chat_member: - if new_chat_members is None: new_chat_members = [] - new_chat_members.append(update.chat_member) - if update.chat_join_request: - if chat_join_request is None: chat_join_request = [] - chat_join_request.append(update.chat_join_request) - - if new_messages: - await self.process_new_messages(new_messages) - if new_edited_messages: - await self.process_new_edited_messages(new_edited_messages) - if new_channel_posts: - await self.process_new_channel_posts(new_channel_posts) - if new_edited_channel_posts: - await self.process_new_edited_channel_posts(new_edited_channel_posts) - if new_inline_queries: - await self.process_new_inline_query(new_inline_queries) - if new_chosen_inline_results: - await self.process_new_chosen_inline_query(new_chosen_inline_results) - if new_callback_queries: - await self.process_new_callback_query(new_callback_queries) - if new_shipping_queries: - await self.process_new_shipping_query(new_shipping_queries) - if new_pre_checkout_queries: - await self.process_new_pre_checkout_query(new_pre_checkout_queries) - if new_polls: - await self.process_new_poll(new_polls) - if new_poll_answers: - await self.process_new_poll_answer(new_poll_answers) - if new_my_chat_members: - await self.process_new_my_chat_member(new_my_chat_members) - if new_chat_members: - await self.process_new_chat_member(new_chat_members) - if chat_join_request: - await self.process_chat_join_request(chat_join_request) - - async def process_new_messages(self, new_messages): - await self.__notify_update(new_messages) - await self._process_updates(self.message_handlers, new_messages, 'message') - - async def process_new_edited_messages(self, edited_message): - await self._process_updates(self.edited_message_handlers, edited_message, 'edited_message') - - async def process_new_channel_posts(self, channel_post): - await self._process_updates(self.channel_post_handlers, channel_post , 'channel_post') - - async def process_new_edited_channel_posts(self, edited_channel_post): - await self._process_updates(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') - - async def process_new_inline_query(self, new_inline_querys): - await self._process_updates(self.inline_handlers, new_inline_querys, 'inline_query') - - async def process_new_chosen_inline_query(self, new_chosen_inline_querys): - await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') - - async def process_new_callback_query(self, new_callback_querys): - await self._process_updates(self.callback_query_handlers, new_callback_querys, 'callback_query') - - async def process_new_shipping_query(self, new_shipping_querys): - await self._process_updates(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') - - async def process_new_pre_checkout_query(self, pre_checkout_querys): - await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') - - async def process_new_poll(self, polls): - await self._process_updates(self.poll_handlers, polls, 'poll') - - async def process_new_poll_answer(self, poll_answers): - await self._process_updates(self.poll_answer_handlers, poll_answers, 'poll_answer') - - async def process_new_my_chat_member(self, my_chat_members): - await self._process_updates(self.my_chat_member_handlers, my_chat_members, 'my_chat_member') - - async def process_new_chat_member(self, chat_members): - await self._process_updates(self.chat_member_handlers, chat_members, 'chat_member') - - async def process_chat_join_request(self, chat_join_request): - await self._process_updates(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') - - async def process_middlewares(self, update, update_type): - for middleware in self.middlewares: - if update_type in middleware.update_types: - return middleware - return None - - async def __notify_update(self, new_messages): - if len(self.update_listener) == 0: - return - for listener in self.update_listener: - self._loop_create_task(listener, new_messages) - - async def _test_message_handler(self, message_handler, message): - """ - Test message handler - :param message_handler: - :param message: - :return: - """ - for message_filter, filter_value in message_handler['filters'].items(): - if filter_value is None: - continue - - if not await self._test_filter(message_filter, filter_value, message): - return False - - return True - - def add_custom_filter(self, custom_filter): - """ - Create custom filter. - custom_filter: Class with check(message) method. - """ - self.custom_filters[custom_filter.key] = custom_filter - - async def _test_filter(self, message_filter, filter_value, message): - """ - Test filters - :param message_filter: Filter type passed in handler - :param filter_value: Filter value passed in handler - :param message: Message to test - :return: True if filter conforms - """ - # test_cases = { - # 'content_types': lambda msg: msg.content_type in filter_value, - # 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), - # 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, - # 'func': lambda msg: filter_value(msg) - # } - # return test_cases.get(message_filter, lambda msg: False)(message) - if message_filter == 'content_types': - return message.content_type in filter_value - elif message_filter == 'regexp': - return message.content_type == 'text' and re.search(filter_value, message.text, re.IGNORECASE) - elif message_filter == 'commands': - return message.content_type == 'text' and util.extract_command(message.text) in filter_value - elif message_filter == 'chat_types': - return message.chat.type in filter_value - elif message_filter == 'func': - return filter_value(message) - elif self.custom_filters and message_filter in self.custom_filters: - return await self._check_filter(message_filter,filter_value,message) - else: - return False - - async def _check_filter(self, message_filter, filter_value, message): - """ - Check up the filter - :param message_filter: - :param filter_value: - :param message: - :return: - """ - filter_check = self.custom_filters.get(message_filter) - if not filter_check: - return False - elif isinstance(filter_check, asyncio_filters.SimpleCustomFilter): - return filter_value == await filter_check.check(message) - elif isinstance(filter_check, asyncio_filters.AdvancedCustomFilter): - return await filter_check.check(message, filter_value) - else: - logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") - return False - - def setup_middleware(self, middleware): - """ - Setup middleware - :param middleware: - :return: - """ - self.middlewares.append(middleware) - - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): - """ - Message handler decorator. - This decorator can be used to decorate functions that must handle certain types of messages. - All message handlers are tested in the order they were added. - - Example: - - bot = TeleBot('TOKEN') - - # Handles all messages which text matches regexp. - @bot.message_handler(regexp='someregexp') - async def command_help(message): - bot.send_message(message.chat.id, 'Did someone call for help?') - - # Handles messages in private chat - @bot.message_handler(chat_types=['private']) # You can add more chat types - async def command_help(message): - bot.send_message(message.chat.id, 'Private chat detected, sir!') - - # Handle all sent documents of type 'text/plain'. - @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', - content_types=['document']) - async def command_handle_document(message): - bot.send_message(message.chat.id, 'Document received, sir!') - - # Handle all other messages. - @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', - 'text', 'location', 'contact', 'sticker']) - async def async default_command(message): - bot.send_message(message.chat.id, "This is the async default command handler.") - - :param commands: Optional list of strings (commands to handle). - :param regexp: Optional regular expression. - :param func: Optional lambda function. The lambda receives the message to test as the first parameter. - It must return True if the command should handle the message. - :param content_types: Supported message content types. Must be a list. async defaults to ['text']. - :param chat_types: list of chat types - """ - - if content_types is None: - content_types = ["text"] - - if isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("message_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, - chat_types=chat_types, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_message_handler(handler_dict) - return handler - - return decorator - - def add_message_handler(self, handler_dict): - """ - Adds a message handler - :param handler_dict: - :return: - """ - self.message_handlers.append(handler_dict) - - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): - """ - Registers message handler. - :param callback: function to be called - :param content_types: list of content_types - :param commands: list of commands - :param regexp: - :param func: - :param chat_types: True for private chat - :return: decorated function - """ - if isinstance(commands, str): - logger.warning("register_message_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - handler_dict = self._build_handler_dict(callback, - chat_types=chat_types, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_message_handler(handler_dict) - - def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): - """ - Edit message handler decorator - :param commands: - :param regexp: - :param func: - :param content_types: - :param chat_types: list of chat types - :param kwargs: - :return: - """ - - if content_types is None: - content_types = ["text"] - - if isinstance(commands, str): - logger.warning("edited_message_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, - chat_types=chat_types, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_edited_message_handler(handler_dict) - return handler - - return decorator - - def add_edited_message_handler(self, handler_dict): - """ - Adds the edit message handler - :param handler_dict: - :return: - """ - self.edited_message_handlers.append(handler_dict) - - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): - """ - Registers edited message handler. - :param callback: function to be called - :param content_types: list of content_types - :param commands: list of commands - :param regexp: - :param func: - :param chat_types: True for private chat - :return: decorated function - """ - if isinstance(commands, str): - logger.warning("register_edited_message_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("register_edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - handler_dict = self._build_handler_dict(callback, - chat_types=chat_types, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_edited_message_handler(handler_dict) - - - def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): - """ - Channel post handler decorator - :param commands: - :param regexp: - :param func: - :param content_types: - :param kwargs: - :return: - """ - if content_types is None: - content_types = ["text"] - - if isinstance(commands, str): - logger.warning("channel_post_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_channel_post_handler(handler_dict) - return handler - - return decorator - - def add_channel_post_handler(self, handler_dict): - """ - Adds channel post handler - :param handler_dict: - :return: - """ - self.channel_post_handlers.append(handler_dict) - - def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): - """ - Registers channel post message handler. - :param callback: function to be called - :param content_types: list of content_types - :param commands: list of commands - :param regexp: - :param func: - :return: decorated function - """ - if isinstance(commands, str): - logger.warning("register_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("register_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - handler_dict = self._build_handler_dict(callback, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_channel_post_handler(handler_dict) - - def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): - """ - Edit channel post handler decorator - :param commands: - :param regexp: - :param func: - :param content_types: - :param kwargs: - :return: - """ - if content_types is None: - content_types = ["text"] - - if isinstance(commands, str): - logger.warning("edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_edited_channel_post_handler(handler_dict) - return handler - - return decorator - - def add_edited_channel_post_handler(self, handler_dict): - """ - Adds the edit channel post handler - :param handler_dict: - :return: - """ - self.edited_channel_post_handlers.append(handler_dict) - - def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): - """ - Registers edited channel post message handler. - :param callback: function to be called - :param content_types: list of content_types - :param commands: list of commands - :param regexp: - :param func: - :return: decorated function - """ - if isinstance(commands, str): - logger.warning("register_edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") - commands = [commands] - - if isinstance(content_types, str): - logger.warning("register_edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") - content_types = [content_types] - - handler_dict = self._build_handler_dict(callback, - content_types=content_types, - commands=commands, - regexp=regexp, - func=func, - **kwargs) - self.add_edited_channel_post_handler(handler_dict) - - def inline_handler(self, func, **kwargs): - """ - Inline call handler decorator - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_inline_handler(handler_dict) - return handler - - return decorator - - def add_inline_handler(self, handler_dict): - """ - Adds inline call handler - :param handler_dict: - :return: - """ - self.inline_handlers.append(handler_dict) - - def register_inline_handler(self, callback, func, **kwargs): - """ - Registers inline handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_inline_handler(handler_dict) - - def chosen_inline_handler(self, func, **kwargs): - """ - Description: TBD - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_chosen_inline_handler(handler_dict) - return handler - - return decorator - - def add_chosen_inline_handler(self, handler_dict): - """ - Description: TBD - :param handler_dict: - :return: - """ - self.chosen_inline_handlers.append(handler_dict) - - def register_chosen_inline_handler(self, callback, func, **kwargs): - """ - Registers chosen inline handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_chosen_inline_handler(handler_dict) - - def callback_query_handler(self, func, **kwargs): - """ - Callback request handler decorator - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_callback_query_handler(handler_dict) - return handler - - return decorator - - def add_callback_query_handler(self, handler_dict): - """ - Adds a callback request handler - :param handler_dict: - :return: - """ - self.callback_query_handlers.append(handler_dict) - - def register_callback_query_handler(self, callback, func, **kwargs): - """ - Registers callback query handler.. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_callback_query_handler(handler_dict) - - def shipping_query_handler(self, func, **kwargs): - """ - Shipping request handler - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_shipping_query_handler(handler_dict) - return handler - - return decorator - - def add_shipping_query_handler(self, handler_dict): - """ - Adds a shipping request handler - :param handler_dict: - :return: - """ - self.shipping_query_handlers.append(handler_dict) - - def register_shipping_query_handler(self, callback, func, **kwargs): - """ - Registers shipping query handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_shipping_query_handler(handler_dict) - - def pre_checkout_query_handler(self, func, **kwargs): - """ - Pre-checkout request handler - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_pre_checkout_query_handler(handler_dict) - return handler - - return decorator - - def add_pre_checkout_query_handler(self, handler_dict): - """ - Adds a pre-checkout request handler - :param handler_dict: - :return: - """ - self.pre_checkout_query_handlers.append(handler_dict) - - def register_pre_checkout_query_handler(self, callback, func, **kwargs): - """ - Registers pre-checkout request handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_pre_checkout_query_handler(handler_dict) - - def poll_handler(self, func, **kwargs): - """ - Poll request handler - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_poll_handler(handler_dict) - return handler - - return decorator - - def add_poll_handler(self, handler_dict): - """ - Adds a poll request handler - :param handler_dict: - :return: - """ - self.poll_handlers.append(handler_dict) - - def register_poll_handler(self, callback, func, **kwargs): - """ - Registers poll handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_poll_handler(handler_dict) - - def poll_answer_handler(self, func=None, **kwargs): - """ - Poll_answer request handler - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_poll_answer_handler(handler_dict) - return handler - - return decorator - - def add_poll_answer_handler(self, handler_dict): - """ - Adds a poll_answer request handler - :param handler_dict: - :return: - """ - self.poll_answer_handlers.append(handler_dict) - - def register_poll_answer_handler(self, callback, func, **kwargs): - """ - Registers poll answer handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_poll_answer_handler(handler_dict) - - def my_chat_member_handler(self, func=None, **kwargs): - """ - my_chat_member handler - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_my_chat_member_handler(handler_dict) - return handler - - return decorator - - def add_my_chat_member_handler(self, handler_dict): - """ - Adds a my_chat_member handler - :param handler_dict: - :return: - """ - self.my_chat_member_handlers.append(handler_dict) - - def register_my_chat_member_handler(self, callback, func=None, **kwargs): - """ - Registers my chat member handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_my_chat_member_handler(handler_dict) - - def chat_member_handler(self, func=None, **kwargs): - """ - chat_member handler - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_chat_member_handler(handler_dict) - return handler - - return decorator - - def add_chat_member_handler(self, handler_dict): - """ - Adds a chat_member handler - :param handler_dict: - :return: - """ - self.chat_member_handlers.append(handler_dict) - - def register_chat_member_handler(self, callback, func=None, **kwargs): - """ - Registers chat member handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_chat_member_handler(handler_dict) - - def chat_join_request_handler(self, func=None, **kwargs): - """ - chat_join_request handler - :param func: - :param kwargs: - :return: - """ - - def decorator(handler): - handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_chat_join_request_handler(handler_dict) - return handler - - return decorator - - def add_chat_join_request_handler(self, handler_dict): - """ - Adds a chat_join_request handler - :param handler_dict: - :return: - """ - self.chat_join_request_handlers.append(handler_dict) - - def register_chat_join_request_handler(self, callback, func=None, **kwargs): - """ - Registers chat join request handler. - :param callback: function to be called - :param func: - :return: decorated function - """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) - self.add_chat_join_request_handler(handler_dict) - - @staticmethod - def _build_handler_dict(handler, **filters): - """ - Builds a dictionary for a handler - :param handler: - :param filters: - :return: - """ - return { - 'function': handler, - 'filters': {ftype: fvalue for ftype, fvalue in filters.items() if fvalue is not None} - # Remove None values, they are skipped in _test_filter anyway - #'filters': filters - } - - async def skip_updates(self): - await self.get_updates(-1) - return True - - # all methods begin here - - async def get_me(self) -> types.User: - """ - Returns basic information about the bot in form of a User object. - """ - result = await asyncio_helper.get_me(self.token) - return types.User.de_json(result) - - async def get_file(self, file_id: str) -> types.File: - """ - Use this method to get basic info about a file and prepare it for downloading. - For the moment, bots can download files of up to 20MB in size. - On success, a File object is returned. - It is guaranteed that the link will be valid for at least 1 hour. - When the link expires, a new one can be requested by calling get_file again. - """ - return types.File.de_json(await asyncio_helper.get_file(self.token, file_id)) - - async def get_file_url(self, file_id: str) -> str: - return await asyncio_helper.get_file_url(self.token, file_id) - - async def download_file(self, file_path: str) -> bytes: - return await asyncio_helper.download_file(self.token, file_path) - - async def log_out(self) -> bool: - """ - Use this method to log out from the cloud Bot API server before launching the bot locally. - You MUST log out the bot before running it locally, otherwise there is no guarantee - that the bot will receive updates. - After a successful call, you can immediately log in on a local server, - but will not be able to log in back to the cloud Bot API server for 10 minutes. - Returns True on success. - """ - return await asyncio_helper.log_out(self.token) - - async def close(self) -> bool: - """ - Use this method to close the bot instance before moving it from one local server to another. - You need to delete the webhook before calling this method to ensure that the bot isn't launched again - after server restart. - The method will return error 429 in the first 10 minutes after the bot is launched. - Returns True on success. - """ - return await asyncio_helper.close(self.token) - - async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, - drop_pending_updates = None, timeout=None): - """ - Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an - update for the bot, we will send an HTTPS POST request to the specified url, - containing a JSON-serialized Update. - In case of an unsuccessful request, we will give up after a reasonable amount of attempts. - Returns True on success. - - :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration - :param certificate: Upload your public key certificate so that the root certificate in use can be checked. - See our self-signed guide for details. - :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook - for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, - and higher values to increase your bot's throughput. - :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates - of these types. See Update for a complete list of available update types. - Specify an empty list to receive all updates regardless of type (default). - If not specified, the previous setting will be used. - :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address - resolved through DNS - :param drop_pending_updates: Pass True to drop all pending updates - :param timeout: Integer. Request connection timeout - :return: - """ - return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, - drop_pending_updates, timeout) - - async def delete_webhook(self, drop_pending_updates=None, timeout=None): - """ - Use this method to remove webhook integration if you decide to switch back to getUpdates. - - :param drop_pending_updates: Pass True to drop all pending updates - :param timeout: Integer. Request connection timeout - :return: bool - """ - return await asyncio_helper.delete_webhook(self.token, drop_pending_updates, timeout) - - async def get_webhook_info(self, timeout=None): - """ - Use this method to get current webhook status. Requires no parameters. - If the bot is using getUpdates, will return an object with the url field empty. - - :param timeout: Integer. Request connection timeout - :return: On success, returns a WebhookInfo object. - """ - result = await asyncio_helper.get_webhook_info(self.token, timeout) - return types.WebhookInfo.de_json(result) - - async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, - limit: Optional[int]=None) -> types.UserProfilePhotos: - """ - Retrieves the user profile photos of the person with 'user_id' - See https://core.telegram.org/bots/api#getuserprofilephotos - :param user_id: - :param offset: - :param limit: - :return: API reply. - """ - 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: - """ - 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. - :param chat_id: - :return: - """ - result = await asyncio_helper.get_chat(self.token, chat_id) - return types.Chat.de_json(result) - - async def leave_chat(self, chat_id: Union[int, str]) -> bool: - """ - Use this method for your bot to leave a group, supergroup or channel. Returns True on success. - :param chat_id: - :return: - """ - result = await asyncio_helper.leave_chat(self.token, chat_id) - return result - - async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMember]: - """ - Use this method to get a list of administrators in a chat. - On success, returns an Array of ChatMember objects that contains - information about all chat administrators except other bots. - :param chat_id: Unique identifier for the target chat or username - of the target supergroup or channel (in the format @channelusername) - :return: - """ - result = await asyncio_helper.get_chat_administrators(self.token, chat_id) - return [types.ChatMember.de_json(r) for r in result] - - async def get_chat_members_count(self, chat_id: Union[int, str]) -> int: - """ - This function is deprecated. Use `get_chat_member_count` instead - """ - logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead.') - result = await asyncio_helper.get_chat_member_count(self.token, chat_id) - return result - - async def get_chat_member_count(self, chat_id: Union[int, str]) -> int: - """ - Use this method to get the number of members in a chat. Returns Int on success. - :param chat_id: - :return: - """ - result = await asyncio_helper.get_chat_member_count(self.token, chat_id) - return result - - async def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: - """ - Use this method to set a new group sticker set for a supergroup. The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. - Use the field can_set_sticker_set optionally returned in getChat requests to check - if the bot can use this method. Returns True on success. - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) - :param sticker_set_name: Name of the sticker set to be set as the group sticker set - :return: - """ - result = await asyncio_helper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) - return result - - async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: - """ - Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat - for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set - optionally returned in getChat requests to check if the bot can use this method. Returns True on success. - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) - :return: - """ - result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) - return result - - async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: - """ - Use this method to get information about a member of a chat. Returns a ChatMember object on success. - :param chat_id: - :param user_id: - :return: - """ - result = await asyncio_helper.get_chat_member(self.token, chat_id, user_id) - return types.ChatMember.de_json(result) - - async def send_message( - self, chat_id: Union[int, str], text: str, - disable_web_page_preview: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send text messages. - - Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 4000 characters, - use the `split_string` or `smart_split` function in util.py. - - :param chat_id: - :param text: - :param disable_web_page_preview: - :param reply_to_message_id: - :param reply_markup: - :param parse_mode: - :param disable_notification: Boolean, Optional. Sends the message silently. - :param timeout: - :param entities: - :param allow_sending_without_reply: - :return: API reply. - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - return types.Message.de_json( - await asyncio_helper.send_message( - self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, - entities, allow_sending_without_reply)) - - async def forward_message( - self, chat_id: Union[int, str], from_chat_id: Union[int, str], - message_id: int, disable_notification: Optional[bool]=None, - timeout: Optional[int]=None) -> types.Message: - """ - Use this method to forward messages of any kind. - :param disable_notification: - :param chat_id: which chat to forward - :param from_chat_id: which chat message from - :param message_id: message id - :param timeout: - :return: API reply. - """ - return types.Message.de_json( - await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) - - async def copy_message( - self, chat_id: Union[int, str], - from_chat_id: Union[int, str], - message_id: int, - caption: Optional[str]=None, - parse_mode: Optional[str]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> int: - """ - Use this method to copy messages of any kind. - :param chat_id: which chat to forward - :param from_chat_id: which chat message from - :param message_id: message id - :param caption: - :param parse_mode: - :param caption_entities: - :param disable_notification: - :param reply_to_message_id: - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :return: API reply. - """ - return types.MessageID.de_json( - await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, - disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, - timeout)) - - async def delete_message(self, chat_id: Union[int, str], message_id: int, - timeout: Optional[int]=None) -> bool: - """ - Use this method to delete message. Returns True on success. - :param chat_id: in which chat to delete - :param message_id: which message to delete - :param timeout: - :return: API reply. - """ - return await asyncio_helper.delete_message(self.token, chat_id, message_id, timeout) - - async def send_dice( - self, chat_id: Union[int, str], - emoji: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send dices. - :param chat_id: - :param emoji: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :return: Message - """ - return types.Message.de_json( - await asyncio_helper.send_dice( - self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout, allow_sending_without_reply) - ) - - async def send_photo( - self, chat_id: Union[int, str], photo: Union[Any, str], - caption: Optional[str]=None, reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send photos. - :param chat_id: - :param photo: - :param caption: - :param parse_mode: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :return: API reply. - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - return types.Message.de_json( - await asyncio_helper.send_photo( - self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply)) - - async def send_audio( - self, chat_id: Union[int, str], audio: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, - performer: Optional[str]=None, title: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send audio files, if you want Telegram clients to display them in the music player. - Your audio must be in the .mp3 format. - :param chat_id:Unique identifier for the message recipient - :param audio:Audio file to send. - :param caption: - :param duration:Duration of the audio in seconds - :param performer:Performer - :param title:Track name - :param reply_to_message_id:If the message is a reply, ID of the original message - :param reply_markup: - :param parse_mode - :param disable_notification: - :param timeout: - :param thumb: - :param caption_entities: - :param allow_sending_without_reply: - :return: Message - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - return types.Message.de_json( - await asyncio_helper.send_audio( - self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply)) - - async def send_voice( - self, chat_id: Union[int, str], voice: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send audio files, if you want Telegram clients to display the file - as a playable voice message. - :param chat_id:Unique identifier for the message recipient. - :param voice: - :param caption: - :param duration:Duration of sent audio in seconds - :param reply_to_message_id: - :param reply_markup: - :param parse_mode - :param disable_notification: - :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :return: Message - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - return types.Message.de_json( - await asyncio_helper.send_voice( - self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, - parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply)) - - async def send_document( - self, chat_id: Union[int, str], data: Union[Any, str], - reply_to_message_id: Optional[int]=None, - caption: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - 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, - disable_content_type_detection: Optional[bool]=None) -> types.Message: - """ - Use this method to send general files. - :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: 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; 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 async 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( - await asyncio_helper.send_data( - 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)) - - async def send_sticker( - self, chat_id: Union[int, str], data: Union[Any, str], - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send .webp stickers. - :param chat_id: - :param data: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: to disable the notification - :param timeout: timeout - :param allow_sending_without_reply: - :return: API reply. - """ - return types.Message.de_json( - await asyncio_helper.send_data( - 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)) - - async def send_video( - self, chat_id: Union[int, str], data: Union[Any, str], - duration: Optional[int]=None, - caption: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - supports_streaming: Optional[bool]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, - width: Optional[int]=None, - height: Optional[int]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send video files, Telegram clients support mp4 videos. - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend - a video that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param caption: String : Video caption (may also be used when resending videos by file_id). - :param parse_mode: - :param supports_streaming: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent - :param width: - :param height: - :param caption_entities: - :param allow_sending_without_reply: - :return: - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - return types.Message.de_json( - await asyncio_helper.send_video( - self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, - parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, - caption_entities, allow_sending_without_reply)) - - async def send_animation( - self, chat_id: Union[int, str], animation: Union[Any, str], - duration: Optional[int]=None, - caption: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an - animation that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param caption: String : Animation caption (may also be used when resending animation by file_id). - :param parse_mode: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent - :param caption_entities: - :param allow_sending_without_reply: - :return: - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - return types.Message.de_json( - await asyncio_helper.send_animation( - self.token, chat_id, animation, duration, caption, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply)) - - async def send_video_note( - self, chat_id: Union[int, str], data: Union[Any, str], - duration: Optional[int]=None, - length: Optional[int]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send - video messages. - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend - a video that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent - :param allow_sending_without_reply: - :return: - """ - return types.Message.de_json( - await asyncio_helper.send_video_note( - self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb, allow_sending_without_reply)) - - async def send_media_group( - self, chat_id: Union[int, str], - media: List[Union[ - types.InputMediaAudio, types.InputMediaDocument, - types.InputMediaPhoto, types.InputMediaVideo]], - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: - """ - send a group of photos or videos as an album. On success, an array of the sent Messages is returned. - :param chat_id: - :param media: - :param disable_notification: - :param reply_to_message_id: - :param timeout: - :param allow_sending_without_reply: - :return: - """ - result = await asyncio_helper.send_media_group( - self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, - allow_sending_without_reply) - return [types.Message.de_json(msg) for msg in result] - - async def send_location( - self, chat_id: Union[int, str], - latitude: float, longitude: float, - live_period: Optional[int]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, - proximity_alert_radius: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - - - """ - Use this method to send point on the map. - :param chat_id: - :param latitude: - :param longitude: - :param live_period: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param horizontal_accuracy: - :param heading: - :param proximity_alert_radius: - :param allow_sending_without_reply: - :return: API reply. - """ - return types.Message.de_json( - await asyncio_helper.send_location( - self.token, chat_id, latitude, longitude, live_period, - reply_to_message_id, reply_markup, disable_notification, timeout, - horizontal_accuracy, heading, proximity_alert_radius, - allow_sending_without_reply)) - - async def edit_message_live_location( - self, latitude: float, longitude: float, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, - proximity_alert_radius: Optional[int]=None) -> types.Message: - """ - Use this method to edit live location - :param latitude: - :param longitude: - :param chat_id: - :param message_id: - :param reply_markup: - :param timeout: - :param inline_message_id: - :param horizontal_accuracy: - :param heading: - :param proximity_alert_radius: - :return: - """ - return types.Message.de_json( - 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)) - - async def stop_message_live_location( - self, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> types.Message: - """ - Use this method to stop updating a live location message sent by the bot - or via the bot (for inline bots) before live_period expires - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :param timeout: - :return: - """ - return types.Message.de_json( - await asyncio_helper.stop_message_live_location( - self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) - - async def send_venue( - self, chat_id: Union[int, str], - latitude: float, longitude: float, - title: str, address: str, - foursquare_id: Optional[str]=None, - foursquare_type: Optional[str]=None, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, - google_place_id: Optional[str]=None, - google_place_type: Optional[str]=None) -> types.Message: - """ - Use this method to send information about a venue. - :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel - :param latitude: Float : Latitude of the venue - :param longitude: Float : Longitude of the venue - :param title: String : Name of the venue - :param address: String : Address of the venue - :param foursquare_id: String : Foursquare identifier of the venue - :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/async default”, - “arts_entertainment/aquarium” or “food/icecream”.) - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param google_place_id: - :param google_place_type: - :return: - """ - return types.Message.de_json( - await asyncio_helper.send_venue( - self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, - disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, google_place_id, google_place_type) - ) - - async def send_contact( - self, chat_id: Union[int, str], phone_number: str, - first_name: str, last_name: Optional[str]=None, - vcard: Optional[str]=None, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - return types.Message.de_json( - await asyncio_helper.send_contact( - self.token, chat_id, phone_number, first_name, last_name, vcard, - disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply) - ) - - async def send_chat_action( - self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: - """ - Use this method when you need to tell the user that something is happening on the bot's side. - The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear - its typing status). - :param chat_id: - :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', - 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', - 'upload_video_note'. - :param timeout: - :return: API reply. :type: boolean - """ - return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout) - - async def kick_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date:Optional[Union[int, datetime]]=None, - revoke_messages: Optional[bool]=None) -> bool: - """ - This function is deprecated. Use `ban_chat_member` instead - """ - logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') - return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) - - async def ban_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date:Optional[Union[int, datetime]]=None, - revoke_messages: Optional[bool]=None) -> bool: - """ - Use this method to ban a user in a group, a supergroup or a channel. - In the case of supergroups and channels, the user will not be able to return to the chat on their - own using invite links, etc., unless unbanned first. - Returns True on success. - :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup - :param user_id: Int : Unique identifier of the target user - :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or - less than 30 seconds from the current time they are considered to be banned forever - :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. - If False, the user will be able to see messages in the group that were sent before the user was removed. - Always True for supergroups and channels. - :return: boolean - """ - return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) - - async def unban_chat_member( - self, chat_id: Union[int, str], user_id: int, - only_if_banned: Optional[bool]=False) -> bool: - """ - Use this method to unban a previously kicked user in a supergroup or channel. - The user will not return to the group or channel automatically, but will be able to join via link, etc. - The bot must be an administrator for this to work. By async default, this method guarantees that after the call - the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat - they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. - - :param chat_id: Unique identifier for the target group or username of the target supergroup or channel - (in the format @username) - :param user_id: Unique identifier of the target user - :param only_if_banned: Do nothing if the user is not banned - :return: True on success - """ - return await asyncio_helper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) - - async def restrict_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date: Optional[Union[int, datetime]]=None, - can_send_messages: Optional[bool]=None, - can_send_media_messages: Optional[bool]=None, - can_send_polls: Optional[bool]=None, - can_send_other_messages: Optional[bool]=None, - can_add_web_page_previews: Optional[bool]=None, - can_change_info: Optional[bool]=None, - can_invite_users: Optional[bool]=None, - can_pin_messages: Optional[bool]=None) -> bool: - """ - Use this method to restrict a user in a supergroup. - The bot must be an administrator in the supergroup for this to work and must have - the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. - - :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup - or channel (in the format @channelusername) - :param user_id: Int : Unique identifier of the target user - :param until_date: Date when restrictions will be lifted for the user, unix time. - If user is restricted for more than 366 days or less than 30 seconds from the current time, - they are considered to be restricted forever - :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues - :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes - and voice notes, implies can_send_messages - :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages - :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and - use inline bots, implies can_send_media_messages - :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, - implies can_send_media_messages - :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. - Ignored in public supergroups - :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, - implies can_invite_users - :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups - :return: True on success - """ - return await asyncio_helper.restrict_chat_member( - self.token, chat_id, user_id, until_date, - can_send_messages, can_send_media_messages, - can_send_polls, can_send_other_messages, - can_add_web_page_previews, can_change_info, - can_invite_users, can_pin_messages) - - async def promote_chat_member( - self, chat_id: Union[int, str], user_id: int, - can_change_info: Optional[bool]=None, - can_post_messages: Optional[bool]=None, - can_edit_messages: Optional[bool]=None, - can_delete_messages: Optional[bool]=None, - can_invite_users: Optional[bool]=None, - can_restrict_members: Optional[bool]=None, - can_pin_messages: Optional[bool]=None, - can_promote_members: Optional[bool]=None, - is_anonymous: Optional[bool]=None, - can_manage_chat: Optional[bool]=None, - can_manage_voice_chats: Optional[bool]=None) -> bool: - """ - Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. - Pass False for all boolean parameters to demote a user. - - :param chat_id: Unique identifier for the target chat or username of the target channel ( - in the format @channelusername) - :param user_id: Int : Unique identifier of the target user - :param can_change_info: Bool: Pass True, if the administrator can change chat title, photo and other settings - :param can_post_messages: Bool : Pass True, if the administrator can create channel posts, channels only - :param can_edit_messages: Bool : Pass True, if the administrator can edit messages of other users, channels only - :param can_delete_messages: Bool : Pass True, if the administrator can delete messages of other users - :param can_invite_users: Bool : Pass True, if the administrator can invite new users to the chat - :param can_restrict_members: Bool: Pass True, if the administrator can restrict, ban or unban chat members - :param can_pin_messages: Bool: Pass True, if the administrator can pin messages, supergroups only - :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset - of his own privileges or demote administrators that he has promoted, directly or indirectly - (promoted by administrators that were appointed by him) - :param is_anonymous: Bool: Pass True, if the administrator's presence in the chat is hidden - :param can_manage_chat: Bool: Pass True, if the administrator can access the chat event log, chat statistics, - message statistics in channels, see channel members, - see anonymous administrators in supergroups and ignore slow mode. - Implied by any other administrator privilege - :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats - For now, bots can use this privilege only for passing to other administrators. - :return: True on success. - """ - return await asyncio_helper.promote_chat_member( - self.token, chat_id, user_id, can_change_info, can_post_messages, - can_edit_messages, can_delete_messages, can_invite_users, - can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_voice_chats) - - async def set_chat_administrator_custom_title( - self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: - """ - Use this method to set a custom title for an administrator - in a supergroup promoted by the bot. - - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) - :param user_id: Unique identifier of the target user - :param custom_title: New custom title for the administrator; - 0-16 characters, emoji are not allowed - :return: True on success. - """ - return await asyncio_helper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - - async def set_chat_permissions( - self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: - """ - Use this method to set async default chat permissions for all members. - The bot must be an administrator in the group or a supergroup for this to work - and must have the can_restrict_members admin rights. - - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) - :param permissions: New async default chat permissions - :return: True on success - """ - return await asyncio_helper.set_chat_permissions(self.token, chat_id, permissions) - - async def create_chat_invite_link( - self, chat_id: Union[int, str], - name: Optional[str]=None, - expire_date: Optional[Union[int, datetime]]=None, - member_limit: Optional[int]=None, - creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: - """ - Use this method to create an additional invite link for a chat. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - - :param chat_id: Id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param name: Invite link name; 0-32 characters - :param expire_date: Point in time (Unix timestamp) when the link will expire - :param member_limit: Maximum number of users that can be members of the chat simultaneously - :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified - :return: - """ - return types.ChatInviteLink.de_json( - await asyncio_helper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request) - ) - - async def edit_chat_invite_link( - self, chat_id: Union[int, str], - invite_link: Optional[str] = None, - name: Optional[str]=None, - expire_date: Optional[Union[int, datetime]]=None, - member_limit: Optional[int]=None, - creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: - """ - Use this method to edit a non-primary invite link created by the bot. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - - :param chat_id: Id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param name: Invite link name; 0-32 characters - :param invite_link: The invite link to edit - :param expire_date: Point in time (Unix timestamp) when the link will expire - :param member_limit: Maximum number of users that can be members of the chat simultaneously - :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified - :return: - """ - return types.ChatInviteLink.de_json( - await asyncio_helper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) - ) - - async def revoke_chat_invite_link( - self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: - """ - Use this method to revoke an invite link created by the bot. - Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. - - :param chat_id: Id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param invite_link: The invite link to revoke - :return: - """ - return types.ChatInviteLink.de_json( - await asyncio_helper.revoke_chat_invite_link(self.token, chat_id, invite_link) - ) - - async def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: - """ - Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. - - :param chat_id: Id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :return: exported invite link as String on success. - """ - return await asyncio_helper.export_chat_invite_link(self.token, chat_id) - - - async def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: - """ - Use this method to approve a chat join request. - The bot must be an administrator in the chat for this to work and must have - the can_invite_users administrator right. Returns True on success. - - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) - :param user_id: Unique identifier of the target user - :return: True on success. - """ - return await asyncio_helper.approve_chat_join_request(self.token, chat_id, user_id) - - async def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: - """ - Use this method to decline a chat join request. - The bot must be an administrator in the chat for this to work and must have - the can_invite_users administrator right. Returns True on success. - - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) - :param user_id: Unique identifier of the target user - :return: True on success. - """ - return await asyncio_helper.decline_chat_join_request(self.token, chat_id, user_id) - - async def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: - """ - Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. - Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param photo: InputFile: New chat photo, uploaded using multipart/form-data - :return: - """ - return await asyncio_helper.set_chat_photo(self.token, chat_id, photo) - - async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: - """ - Use this method to delete a chat photo. Photos can't be changed for private chats. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. - Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - """ - return await asyncio_helper.delete_chat_photo(self.token, chat_id) - - async def get_my_commands(self, scope: Optional[types.BotCommandScope], - language_code: Optional[str]) -> List[types.BotCommand]: - """ - Use this method to get the current list of the bot's commands. - Returns List of BotCommand on success. - :param scope: The scope of users for which the commands are relevant. - async defaults to BotCommandScopeasync default. - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, - for whose language there are no dedicated commands - """ - result = await asyncio_helper.get_my_commands(self.token, scope, language_code) - return [types.BotCommand.de_json(cmd) for cmd in result] - - async def set_my_commands(self, commands: List[types.BotCommand], - scope: Optional[types.BotCommandScope]=None, - language_code: Optional[str]=None) -> bool: - """ - Use this method to change the list of the bot's commands. - :param commands: List of BotCommand. At most 100 commands can be specified. - :param scope: The scope of users for which the commands are relevant. - async defaults to BotCommandScopeasync default. - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, - for whose language there are no dedicated commands - :return: - """ - return await asyncio_helper.set_my_commands(self.token, commands, scope, language_code) - - async def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, - language_code: Optional[int]=None) -> bool: - """ - Use this method to delete the list of the bot's commands for the given scope and user language. - After deletion, higher level commands will be shown to affected users. - Returns True on success. - :param scope: The scope of users for which the commands are relevant. - async defaults to BotCommandScopeasync default. - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, - for whose language there are no dedicated commands - """ - return await asyncio_helper.delete_my_commands(self.token, scope, language_code) - - async def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: - """ - Use this method to change the title of a chat. Titles can't be changed for private chats. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. - Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param title: New chat title, 1-255 characters - :return: - """ - return await asyncio_helper.set_chat_title(self.token, chat_id, title) - - async def set_chat_description(self, chat_id: Union[int, str], description: Optional[str]=None) -> bool: - """ - Use this method to change the description of a supergroup or a channel. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param description: Str: New chat description, 0-255 characters - :return: True on success. - """ - return await asyncio_helper.set_chat_description(self.token, chat_id, description) - - async def pin_chat_message( - self, chat_id: Union[int, str], message_id: int, - disable_notification: Optional[bool]=False) -> bool: - """ - Use this method to pin a message in a supergroup. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param message_id: Int: Identifier of a message to pin - :param disable_notification: Bool: Pass True, if it is not necessary to send a notification - to all group members about the new pinned message - :return: - """ - return await asyncio_helper.pin_chat_message(self.token, chat_id, message_id, disable_notification) - - async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: - """ - Use this method to unpin specific pinned message in a supergroup chat. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param message_id: Int: Identifier of a message to unpin - :return: - """ - return await asyncio_helper.unpin_chat_message(self.token, chat_id, message_id) - - async def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: - """ - Use this method to unpin a all pinned messages in a supergroup chat. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :return: - """ - return await asyncio_helper.unpin_all_chat_messages(self.token, chat_id) - - async def edit_message_text( - self, text: str, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, - parse_mode: Optional[str]=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. - :param text: - :param chat_id: - :param message_id: - :param inline_message_id: - :param parse_mode: - :param entities: - :param disable_web_page_preview: - :param reply_markup: - :return: - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - 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) - - async def edit_message_media( - self, media: Any, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: - """ - Use this method to edit animation, audio, document, photo, or video messages. - If a message is a part of a message album, then it can be edited only to a photo or a video. - Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. - Use previously uploaded file via its file_id or specify a URL. - :param media: - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :return: - """ - result = await asyncio_helper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) - if type(result) == bool: # if edit inline message return is bool not Message. - return result - return types.Message.de_json(result) - - async def edit_message_reply_markup( - self, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: - """ - Use this method to edit only the reply markup of messages. - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :return: - """ - result = await asyncio_helper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) - if type(result) == bool: - return result - return types.Message.de_json(result) - - async def send_game( - self, chat_id: Union[int, str], game_short_name: str, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: - """ - Used to send the game - :param chat_id: - :param game_short_name: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :return: - """ - result = await asyncio_helper.send_game( - self.token, chat_id, game_short_name, disable_notification, - reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply) - return types.Message.de_json(result) - - async def set_game_score( - self, user_id: Union[int, str], score: int, - force: Optional[bool]=None, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, - disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: - """ - Sets the value of points in the game to a specific user - :param user_id: - :param score: - :param force: - :param chat_id: - :param message_id: - :param inline_message_id: - :param disable_edit_message: - :return: - """ - result = await asyncio_helper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, - message_id, inline_message_id) - if type(result) == bool: - return result - return types.Message.de_json(result) - - async def get_game_high_scores( - self, user_id: int, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: - """ - Gets top points and game play - :param user_id: - :param chat_id: - :param message_id: - :param inline_message_id: - :return: - """ - result = await asyncio_helper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) - return [types.GameHighScore.de_json(r) for r in result] - - async def send_invoice( - self, chat_id: Union[int, str], title: str, description: str, - invoice_payload: str, provider_token: str, currency: str, - prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, - photo_url: Optional[str]=None, photo_size: Optional[int]=None, - photo_width: Optional[int]=None, photo_height: Optional[int]=None, - need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, - need_email: Optional[bool]=None, need_shipping_address: Optional[bool]=None, - send_phone_number_to_provider: Optional[bool]=None, - send_email_to_provider: Optional[bool]=None, - is_flexible: Optional[bool]=None, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - provider_data: Optional[str]=None, - timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, - max_tip_amount: Optional[int] = None, - suggested_tip_amounts: Optional[List[int]]=None) -> types.Message: - """ - Sends invoice - :param chat_id: Unique identifier for the target private chat - :param title: Product name - :param description: Product description - :param invoice_payload: Bot-async defined invoice payload, 1-128 bytes. This will not be displayed to the user, - use for your internal processes. - :param provider_token: Payments provider token, obtained via @Botfather - :param currency: Three-letter ISO 4217 currency code, - see https://core.telegram.org/bots/payments#supported-currencies - :param prices: Price breakdown, a list of components - (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) - :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice - when used as a start parameter - :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods - or a marketing image for a service. People like it better when they see what they are paying for. - :param photo_size: Photo size - :param photo_width: Photo width - :param photo_height: Photo height - :param need_name: Pass True, if you require the user's full name to complete the order - :param need_phone_number: Pass True, if you require the user's phone number to complete the order - :param need_email: Pass True, if you require the user's email to complete the order - :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order - :param is_flexible: Pass True, if the final price depends on the shipping method - :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider - :param send_email_to_provider: Pass True, if user's email address should be sent to provider - :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: 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 - :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 allow_sending_without_reply: - :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. - :return: - """ - result = await asyncio_helper.send_invoice( - self.token, chat_id, title, description, invoice_payload, provider_token, - currency, prices, start_parameter, photo_url, photo_size, photo_width, - photo_height, need_name, need_phone_number, need_email, need_shipping_address, - send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, - reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, - max_tip_amount, suggested_tip_amounts) - return types.Message.de_json(result) - - # noinspection PyShadowingBuiltins - async def send_poll( - self, chat_id: Union[int, str], question: str, options: List[str], - is_anonymous: Optional[bool]=None, type: Optional[str]=None, - allows_multiple_answers: Optional[bool]=None, - correct_option_id: Optional[int]=None, - explanation: Optional[str]=None, - explanation_parse_mode: Optional[str]=None, - open_period: Optional[int]=None, - close_date: Optional[Union[int, datetime]]=None, - is_closed: Optional[bool]=None, - disable_notification: Optional[bool]=False, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - allow_sending_without_reply: Optional[bool]=None, - timeout: Optional[int]=None, - explanation_entities: Optional[List[types.MessageEntity]]=None) -> types.Message: - """ - Send polls - :param chat_id: - :param question: - :param options: array of str with answers - :param is_anonymous: - :param type: - :param allows_multiple_answers: - :param correct_option_id: - :param explanation: - :param explanation_parse_mode: - :param open_period: - :param close_date: - :param is_closed: - :param disable_notification: - :param reply_to_message_id: - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :param explanation_entities: - :return: - """ - - if isinstance(question, types.Poll): - raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") - - return types.Message.de_json( - await asyncio_helper.send_poll( - self.token, chat_id, - question, options, - is_anonymous, type, allows_multiple_answers, correct_option_id, - explanation, explanation_parse_mode, open_period, close_date, is_closed, - disable_notification, reply_to_message_id, allow_sending_without_reply, - reply_markup, timeout, explanation_entities)) - - async def stop_poll( - self, chat_id: Union[int, str], message_id: int, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: - """ - Stops poll - :param chat_id: - :param message_id: - :param reply_markup: - :return: - """ - return types.Poll.de_json(await asyncio_helper.stop_poll(self.token, chat_id, message_id, reply_markup)) - - async def answer_shipping_query( - self, shipping_query_id: str, ok: bool, - shipping_options: Optional[List[types.ShippingOption]]=None, - error_message: Optional[str]=None) -> bool: - """ - Asks for an answer to a shipping question - :param shipping_query_id: - :param ok: - :param shipping_options: - :param error_message: - :return: - """ - return await asyncio_helper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) - - async def answer_pre_checkout_query( - self, pre_checkout_query_id: int, ok: bool, - error_message: Optional[str]=None) -> bool: - """ - Response to a request for pre-inspection - :param pre_checkout_query_id: - :param ok: - :param error_message: - :return: - """ - return await asyncio_helper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) - - async def edit_message_caption( - self, caption: str, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, - parse_mode: Optional[str]=None, - caption_entities: Optional[List[types.MessageEntity]]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: - """ - Use this method to edit captions of messages - :param caption: - :param chat_id: - :param message_id: - :param inline_message_id: - :param parse_mode: - :param caption_entities: - :param reply_markup: - :return: - """ - parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - - result = await asyncio_helper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, caption_entities, reply_markup) - if type(result) == bool: - return result - return types.Message.de_json(result) - - async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: - """ - Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` - :param message: - :param text: - :param kwargs: - :return: - """ - return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) - - async def answer_inline_query( - self, inline_query_id: str, - results: List[Any], - cache_time: Optional[int]=None, - is_personal: Optional[bool]=None, - next_offset: Optional[str]=None, - switch_pm_text: Optional[str]=None, - switch_pm_parameter: Optional[str]=None) -> bool: - """ - Use this method to send answers to an inline query. On success, True is returned. - No more than 50 results per query are allowed. - :param inline_query_id: Unique identifier for the answered query - :param results: Array of results for the inline query - :param cache_time: The maximum amount of time in seconds that the result of the inline query - may be cached on the server. - :param is_personal: Pass True, if results may be cached on the server side only for - the user that sent the query. - :param next_offset: Pass the offset that a client should send in the next query with the same text - to receive more results. - :param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user - to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter - :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button - :return: True means success. - """ - return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, - switch_pm_text, switch_pm_parameter) - - async def answer_callback_query( - self, callback_query_id: int, - text: Optional[str]=None, show_alert: Optional[bool]=None, - url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: - """ - Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to - the user as a notification at the top of the chat screen or as an alert. - :param callback_query_id: - :param text: - :param show_alert: - :param url: - :param cache_time: - :return: - """ - return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - - async def set_sticker_set_thumb( - self, name: str, user_id: int, thumb: Union[Any, str]=None): - """ - Use this method to set the thumbnail of a sticker set. - Animated thumbnails can be set for animated sticker sets only. Returns True on success. - """ - return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) - - async def get_sticker_set(self, name: str) -> types.StickerSet: - """ - Use this method to get a sticker set. On success, a StickerSet object is returned. - :param name: - :return: - """ - result = await asyncio_helper.get_sticker_set(self.token, name) - return types.StickerSet.de_json(result) - - async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: - """ - Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet - methods (can be used multiple times). Returns the uploaded File on success. - :param user_id: - :param png_sticker: - :return: - """ - result = await asyncio_helper.upload_sticker_file(self.token, user_id, png_sticker) - return types.File.de_json(result) - - async def create_new_sticker_set( - self, user_id: int, name: str, title: str, - emojis: str, - png_sticker: Union[Any, str], - tgs_sticker: Union[Any, str], - contains_masks: Optional[bool]=None, - mask_position: Optional[types.MaskPosition]=None) -> bool: - """ - Use this method to create new sticker set owned by a user. - The bot will be able to edit the created sticker set. - Returns True on success. - :param user_id: - :param name: - :param title: - :param emojis: - :param png_sticker: - :param tgs_sticker: - :param contains_masks: - :param mask_position: - :return: - """ - return await asyncio_helper.create_new_sticker_set( - self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks, mask_position) - - - async def add_sticker_to_set( - self, user_id: int, name: str, emojis: str, - png_sticker: Optional[Union[Any, str]]=None, - tgs_sticker: Optional[Union[Any, str]]=None, - mask_position: Optional[types.MaskPosition]=None) -> bool: - """ - Use this method to add a new sticker to a set created by the bot. - It's required to pass `png_sticker` or `tgs_sticker`. - Returns True on success. - :param user_id: - :param name: - :param emojis: - :param png_sticker: Required if `tgs_sticker` is None - :param tgs_sticker: Required if `png_sticker` is None - :param mask_position: - :return: - """ - return await asyncio_helper.add_sticker_to_set( - self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position) - - - async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: - """ - Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. - :param sticker: - :param position: - :return: - """ - return await asyncio_helper.set_sticker_position_in_set(self.token, sticker, position) - - async def delete_sticker_from_set(self, sticker: str) -> bool: - """ - Use this method to delete a sticker from a set created by the bot. Returns True on success. - :param sticker: - :return: - """ - return await asyncio_helper.delete_sticker_from_set(self.token, sticker) - - async def register_for_reply( - self, message: types.Message, callback: Callable, *args, **kwargs) -> None: - """ - Registers a callback function to be notified when a reply to `message` arrives. - - Warning: In case `callback` as lambda function, saving reply handlers will not work. - - :param message: The message for which we are awaiting a reply. - :param callback: The callback function to be called when a reply arrives. Must accept one `message` - parameter, which will contain the replied message. - """ - message_id = message.message_id - self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs) - - async def register_for_reply_by_message_id( - self, message_id: int, callback: Callable, *args, **kwargs) -> None: - """ - Registers a callback function to be notified when a reply to `message` arrives. - - Warning: In case `callback` as lambda function, saving reply handlers will not work. - - :param message_id: The id of the message for which we are awaiting a reply. - :param callback: The callback function to be called when a reply arrives. Must accept one `message` - parameter, which will contain the replied message. - """ - self.reply_backend.register_handler(message_id, Handler(callback, *args, **kwargs)) - - async def _notify_reply_handlers(self, new_messages) -> None: - """ - Notify handlers of the answers - :param new_messages: - :return: - """ - for message in new_messages: - if hasattr(message, "reply_to_message") and message.reply_to_message is not None: - handlers = self.reply_backend.get_handlers(message.reply_to_message.message_id) - if handlers: - for handler in handlers: - self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - - async def register_next_step_handler( - self, message: types.Message, callback: Callable, *args, **kwargs) -> None: - """ - Registers a callback function to be notified when new message arrives after `message`. - - Warning: In case `callback` as lambda function, saving next step handlers will not work. - - :param message: The message for which we want to handle new message in the same chat. - :param callback: The callback function which next new message arrives. - :param args: Args to pass in callback func - :param kwargs: Args to pass in callback func - """ - chat_id = message.chat.id - self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) - - async def set_state(self, chat_id, state): - """ - Sets a new state of a user. - :param chat_id: - :param state: new state. can be string or integer. - """ - await self.current_states.add_state(chat_id, state) - - async def delete_state(self, chat_id): - """ - Delete the current state of a user. - :param chat_id: - :return: - """ - await self.current_states.delete_state(chat_id) - - def retrieve_data(self, chat_id): - return self.current_states.retrieve_data(chat_id) - - async def get_state(self, chat_id): - """ - Get current state of a user. - :param chat_id: - :return: state of a user - """ - return await self.current_states.current_state(chat_id) - async def add_data(self, chat_id, **kwargs): - """ - Add data to states. - :param chat_id: - """ - for key, value in kwargs.items(): - await self.current_states._add_data(chat_id, key, value) \ No newline at end of file diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py new file mode 100644 index 000000000..eb2ac4490 --- /dev/null +++ b/telebot/async_telebot.py @@ -0,0 +1,2869 @@ +# -*- coding: utf-8 -*- +from datetime import datetime + +import logging +import re +import sys +import time +import traceback +from typing import Any, Callable, List, Optional, Union + +# this imports are used to avoid circular import error +import telebot.util +import telebot.types + + +from inspect import signature + +from telebot import logger + +formatter = logging.Formatter( + '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' +) + +console_output_handler = logging.StreamHandler(sys.stderr) +console_output_handler.setFormatter(formatter) +logger.addHandler(console_output_handler) + +logger.setLevel(logging.ERROR) + +from telebot import util, types, asyncio_helper +import asyncio +from telebot import asyncio_handler_backends +from telebot import asyncio_filters + + + +REPLY_MARKUP_TYPES = Union[ + types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, types.ForceReply] + + + +""" +Module : telebot +""" + + +class Handler: + """ + Class for (next step|reply) handlers + """ + + def __init__(self, callback, *args, **kwargs): + self.callback = callback + self.args = args + self.kwargs = kwargs + + def __getitem__(self, item): + return getattr(self, item) + + +class ExceptionHandler: + """ + Class for handling exceptions while Polling + """ + + # noinspection PyMethodMayBeStatic,PyUnusedLocal + def handle(self, exception): + return False + + +class SkipHandler: + """ + Class for skipping handlers. + Just return instance of this class + in middleware to skip handler. + Update will go to post_process, + but will skip execution of handler. + """ + + def __init__(self) -> None: + pass + +class CancelUpdate: + """ + Class for canceling updates. + Just return instance of this class + in middleware to skip update. + Update will skip handler and execution + of post_process in middlewares. + """ + + def __init__(self) -> None: + pass + + +class AsyncTeleBot: + + + def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, + exception_handler=None,suppress_middleware_excepions=False) -> None: # TODO: ADD TYPEHINTS + self.token = token + + self.offset = offset + self.token = token + self.parse_mode = parse_mode + self.update_listener = [] + self.suppress_middleware_excepions = suppress_middleware_excepions + + self.exc_info = None + + self.exception_handler = exception_handler + + self.message_handlers = [] + self.edited_message_handlers = [] + self.channel_post_handlers = [] + self.edited_channel_post_handlers = [] + self.inline_handlers = [] + self.chosen_inline_handlers = [] + self.callback_query_handlers = [] + self.shipping_query_handlers = [] + self.pre_checkout_query_handlers = [] + self.poll_handlers = [] + self.poll_answer_handlers = [] + self.my_chat_member_handlers = [] + self.chat_member_handlers = [] + self.chat_join_request_handlers = [] + self.custom_filters = {} + self.state_handlers = [] + + self.current_states = asyncio_handler_backends.StateMemory() + + + self.middlewares = [] + + + async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, + timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> types.Update: + json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) + return [types.Update.de_json(ju) for ju in json_updates] + + async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, + request_timeout: int=20, allowed_updates: Optional[List[str]]=None, + none_stop: Optional[bool]=None): + """ + This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. + + Warning: Do not call this function more than once! + + Always get updates. + :param interval: Delay between two update retrivals + :param non_stop: Do not stop polling when an ApiException occurs. + :param timeout: Request connection timeout + :param skip_pending: skip old updates + :param request_timeout: Timeout in seconds for a request. + :param allowed_updates: A list of the update types you want your bot to receive. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + 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 skip_pending: + await self.skip_updates() + await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) + + async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, + allowed_updates: Optional[List[str]]=None, *args, **kwargs): + """ + Wrap polling with infinite loop and exception handling to avoid bot stops polling. + + :param timeout: Request connection timeout + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param skip_pending: skip old updates + :param logger_level: Custom logging level for infinity_polling logging. + Use logger levels from logging as a value. None/NOTSET = no error logging + :param allowed_updates: A list of the update types you want your bot to receive. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + 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. + """ + if skip_pending: + await self.skip_updates() + self._polling = True + while self._polling: + try: + await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, + allowed_updates=allowed_updates, *args, **kwargs) + except Exception as e: + if logger_level and logger_level >= logging.ERROR: + logger.error("Infinity polling exception: %s", str(e)) + if logger_level and logger_level >= logging.DEBUG: + logger.error("Exception traceback:\n%s", traceback.format_exc()) + time.sleep(3) + continue + if logger_level and logger_level >= logging.INFO: + logger.error("Infinity polling: polling exited") + if logger_level and logger_level >= logging.INFO: + logger.error("Break infinity polling") + + async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, + request_timeout: int=20, allowed_updates: Optional[List[str]]=None): + """ + Function to process polling. + :param non_stop: Do not stop polling when an ApiException occurs. + :param interval: Delay between two update retrivals + :param timeout: Request connection timeout + :param request_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. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + 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. + :return: + + """ + self._polling = True + + try: + while self._polling: + try: + + updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout, request_timeout=request_timeout) + except asyncio.CancelledError: + return + + except asyncio_helper.ApiTelegramException as e: + logger.error(str(e)) + + continue + except Exception as e: + logger.error('Cause exception while getting updates.') + if non_stop: + logger.error(str(e)) + await asyncio.sleep(3) + continue + else: + raise e + if updates: + self.offset = updates[-1].update_id + 1 + self._loop_create_task(self.process_new_updates(updates)) # Seperate task for processing updates + if interval: await asyncio.sleep(interval) + + finally: + self._polling = False + logger.warning('Polling is stopped.') + + + def _loop_create_task(self, coro): + return asyncio.create_task(coro) + + async def _process_updates(self, handlers, messages, update_type): + """ + Process updates. + :param handlers: + :param messages: + :return: + """ + for message in messages: + middleware = await self.process_middlewares(message, update_type) + self._loop_create_task(self._run_middlewares_and_handlers(handlers, message, middleware)) + + + async def _run_middlewares_and_handlers(self, handlers, message, middleware): + handler_error = None + data = {} + for message_handler in handlers: + process_update = await self._test_message_handler(message_handler, message) + if not process_update: + continue + elif process_update: + if middleware: + middleware_result = await middleware.pre_process(message, data) + if isinstance(middleware_result, SkipHandler): + await middleware.post_process(message, data, handler_error) + break + if isinstance(middleware_result, CancelUpdate): + return + try: + if "data" in signature(message_handler['function']).parameters: + await message_handler['function'](message, data) + else: + await message_handler['function'](message) + break + except Exception as e: + handler_error = e + logger.info(str(e)) + + if middleware: + await middleware.post_process(message, data, handler_error) + # update handling + async def process_new_updates(self, updates): + upd_count = len(updates) + logger.info('Received {0} new updates'.format(upd_count)) + if upd_count == 0: return + + new_messages = None + new_edited_messages = None + new_channel_posts = None + new_edited_channel_posts = None + new_inline_queries = None + new_chosen_inline_results = None + new_callback_queries = None + new_shipping_queries = None + new_pre_checkout_queries = None + new_polls = None + new_poll_answers = None + new_my_chat_members = None + new_chat_members = None + chat_join_request = None + for update in updates: + logger.debug('Processing updates: {0}'.format(update)) + if update.message: + if new_messages is None: new_messages = [] + new_messages.append(update.message) + if update.edited_message: + if new_edited_messages is None: new_edited_messages = [] + new_edited_messages.append(update.edited_message) + if update.channel_post: + if new_channel_posts is None: new_channel_posts = [] + new_channel_posts.append(update.channel_post) + if update.edited_channel_post: + if new_edited_channel_posts is None: new_edited_channel_posts = [] + new_edited_channel_posts.append(update.edited_channel_post) + if update.inline_query: + if new_inline_queries is None: new_inline_queries = [] + new_inline_queries.append(update.inline_query) + if update.chosen_inline_result: + if new_chosen_inline_results is None: new_chosen_inline_results = [] + new_chosen_inline_results.append(update.chosen_inline_result) + if update.callback_query: + if new_callback_queries is None: new_callback_queries = [] + new_callback_queries.append(update.callback_query) + if update.shipping_query: + if new_shipping_queries is None: new_shipping_queries = [] + new_shipping_queries.append(update.shipping_query) + if update.pre_checkout_query: + if new_pre_checkout_queries is None: new_pre_checkout_queries = [] + new_pre_checkout_queries.append(update.pre_checkout_query) + if update.poll: + if new_polls is None: new_polls = [] + new_polls.append(update.poll) + if update.poll_answer: + if new_poll_answers is None: new_poll_answers = [] + new_poll_answers.append(update.poll_answer) + if update.my_chat_member: + if new_my_chat_members is None: new_my_chat_members = [] + new_my_chat_members.append(update.my_chat_member) + if update.chat_member: + if new_chat_members is None: new_chat_members = [] + new_chat_members.append(update.chat_member) + if update.chat_join_request: + if chat_join_request is None: chat_join_request = [] + chat_join_request.append(update.chat_join_request) + + if new_messages: + await self.process_new_messages(new_messages) + if new_edited_messages: + await self.process_new_edited_messages(new_edited_messages) + if new_channel_posts: + await self.process_new_channel_posts(new_channel_posts) + if new_edited_channel_posts: + await self.process_new_edited_channel_posts(new_edited_channel_posts) + if new_inline_queries: + await self.process_new_inline_query(new_inline_queries) + if new_chosen_inline_results: + await self.process_new_chosen_inline_query(new_chosen_inline_results) + if new_callback_queries: + await self.process_new_callback_query(new_callback_queries) + if new_shipping_queries: + await self.process_new_shipping_query(new_shipping_queries) + if new_pre_checkout_queries: + await self.process_new_pre_checkout_query(new_pre_checkout_queries) + if new_polls: + await self.process_new_poll(new_polls) + if new_poll_answers: + await self.process_new_poll_answer(new_poll_answers) + if new_my_chat_members: + await self.process_new_my_chat_member(new_my_chat_members) + if new_chat_members: + await self.process_new_chat_member(new_chat_members) + if chat_join_request: + await self.process_chat_join_request(chat_join_request) + + async def process_new_messages(self, new_messages): + await self.__notify_update(new_messages) + await self._process_updates(self.message_handlers, new_messages, 'message') + + async def process_new_edited_messages(self, edited_message): + await self._process_updates(self.edited_message_handlers, edited_message, 'edited_message') + + async def process_new_channel_posts(self, channel_post): + await self._process_updates(self.channel_post_handlers, channel_post , 'channel_post') + + async def process_new_edited_channel_posts(self, edited_channel_post): + await self._process_updates(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') + + async def process_new_inline_query(self, new_inline_querys): + await self._process_updates(self.inline_handlers, new_inline_querys, 'inline_query') + + async def process_new_chosen_inline_query(self, new_chosen_inline_querys): + await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') + + async def process_new_callback_query(self, new_callback_querys): + await self._process_updates(self.callback_query_handlers, new_callback_querys, 'callback_query') + + async def process_new_shipping_query(self, new_shipping_querys): + await self._process_updates(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') + + async def process_new_pre_checkout_query(self, pre_checkout_querys): + await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') + + async def process_new_poll(self, polls): + await self._process_updates(self.poll_handlers, polls, 'poll') + + async def process_new_poll_answer(self, poll_answers): + await self._process_updates(self.poll_answer_handlers, poll_answers, 'poll_answer') + + async def process_new_my_chat_member(self, my_chat_members): + await self._process_updates(self.my_chat_member_handlers, my_chat_members, 'my_chat_member') + + async def process_new_chat_member(self, chat_members): + await self._process_updates(self.chat_member_handlers, chat_members, 'chat_member') + + async def process_chat_join_request(self, chat_join_request): + await self._process_updates(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') + + async def process_middlewares(self, update, update_type): + for middleware in self.middlewares: + if update_type in middleware.update_types: + return middleware + return None + + async def __notify_update(self, new_messages): + if len(self.update_listener) == 0: + return + for listener in self.update_listener: + self._loop_create_task(listener, new_messages) + + async def _test_message_handler(self, message_handler, message): + """ + Test message handler + :param message_handler: + :param message: + :return: + """ + for message_filter, filter_value in message_handler['filters'].items(): + if filter_value is None: + continue + + if not await self._test_filter(message_filter, filter_value, message): + return False + + return True + + def add_custom_filter(self, custom_filter): + """ + Create custom filter. + custom_filter: Class with check(message) method. + """ + self.custom_filters[custom_filter.key] = custom_filter + + async def _test_filter(self, message_filter, filter_value, message): + """ + Test filters + :param message_filter: Filter type passed in handler + :param filter_value: Filter value passed in handler + :param message: Message to test + :return: True if filter conforms + """ + # test_cases = { + # 'content_types': lambda msg: msg.content_type in filter_value, + # 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), + # 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, + # 'func': lambda msg: filter_value(msg) + # } + # return test_cases.get(message_filter, lambda msg: False)(message) + if message_filter == 'content_types': + return message.content_type in filter_value + elif message_filter == 'regexp': + return message.content_type == 'text' and re.search(filter_value, message.text, re.IGNORECASE) + elif message_filter == 'commands': + return message.content_type == 'text' and util.extract_command(message.text) in filter_value + elif message_filter == 'chat_types': + return message.chat.type in filter_value + elif message_filter == 'func': + return filter_value(message) + elif self.custom_filters and message_filter in self.custom_filters: + return await self._check_filter(message_filter,filter_value,message) + else: + return False + + async def _check_filter(self, message_filter, filter_value, message): + """ + Check up the filter + :param message_filter: + :param filter_value: + :param message: + :return: + """ + filter_check = self.custom_filters.get(message_filter) + if not filter_check: + return False + elif isinstance(filter_check, asyncio_filters.SimpleCustomFilter): + return filter_value == await filter_check.check(message) + elif isinstance(filter_check, asyncio_filters.AdvancedCustomFilter): + return await filter_check.check(message, filter_value) + else: + logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") + return False + + def setup_middleware(self, middleware): + """ + Setup middleware + :param middleware: + :return: + """ + self.middlewares.append(middleware) + + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): + """ + Message handler decorator. + This decorator can be used to decorate functions that must handle certain types of messages. + All message handlers are tested in the order they were added. + + Example: + + bot = TeleBot('TOKEN') + + # Handles all messages which text matches regexp. + @bot.message_handler(regexp='someregexp') + async def command_help(message): + bot.send_message(message.chat.id, 'Did someone call for help?') + + # Handles messages in private chat + @bot.message_handler(chat_types=['private']) # You can add more chat types + async def command_help(message): + bot.send_message(message.chat.id, 'Private chat detected, sir!') + + # Handle all sent documents of type 'text/plain'. + @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', + content_types=['document']) + async def command_handle_document(message): + bot.send_message(message.chat.id, 'Document received, sir!') + + # Handle all other messages. + @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', + 'text', 'location', 'contact', 'sticker']) + async def async default_command(message): + bot.send_message(message.chat.id, "This is the async default command handler.") + + :param commands: Optional list of strings (commands to handle). + :param regexp: Optional regular expression. + :param func: Optional lambda function. The lambda receives the message to test as the first parameter. + It must return True if the command should handle the message. + :param content_types: Supported message content types. Must be a list. async defaults to ['text']. + :param chat_types: list of chat types + """ + + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_message_handler(handler_dict) + return handler + + return decorator + + def add_message_handler(self, handler_dict): + """ + Adds a message handler + :param handler_dict: + :return: + """ + self.message_handlers.append(handler_dict) + + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + """ + Registers message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :param chat_types: True for private chat + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_message_handler(handler_dict) + + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): + """ + Edit message handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param chat_types: list of chat types + :param kwargs: + :return: + """ + + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_message_handler(handler_dict) + return handler + + return decorator + + def add_edited_message_handler(self, handler_dict): + """ + Adds the edit message handler + :param handler_dict: + :return: + """ + self.edited_message_handlers.append(handler_dict) + + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + """ + Registers edited message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :param chat_types: True for private chat + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + chat_types=chat_types, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_message_handler(handler_dict) + + + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Channel post handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param kwargs: + :return: + """ + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_channel_post_handler(handler_dict) + return handler + + return decorator + + def add_channel_post_handler(self, handler_dict): + """ + Adds channel post handler + :param handler_dict: + :return: + """ + self.channel_post_handlers.append(handler_dict) + + def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers channel post message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_channel_post_handler(handler_dict) + + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Edit channel post handler decorator + :param commands: + :param regexp: + :param func: + :param content_types: + :param kwargs: + :return: + """ + if content_types is None: + content_types = ["text"] + + if isinstance(commands, str): + logger.warning("edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_channel_post_handler(handler_dict) + return handler + + return decorator + + def add_edited_channel_post_handler(self, handler_dict): + """ + Adds the edit channel post handler + :param handler_dict: + :return: + """ + self.edited_channel_post_handlers.append(handler_dict) + + def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + """ + Registers edited channel post message handler. + :param callback: function to be called + :param content_types: list of content_types + :param commands: list of commands + :param regexp: + :param func: + :return: decorated function + """ + if isinstance(commands, str): + logger.warning("register_edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + commands = [commands] + + if isinstance(content_types, str): + logger.warning("register_edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_channel_post_handler(handler_dict) + + def inline_handler(self, func, **kwargs): + """ + Inline call handler decorator + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_inline_handler(handler_dict) + return handler + + return decorator + + def add_inline_handler(self, handler_dict): + """ + Adds inline call handler + :param handler_dict: + :return: + """ + self.inline_handlers.append(handler_dict) + + def register_inline_handler(self, callback, func, **kwargs): + """ + Registers inline handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_inline_handler(handler_dict) + + def chosen_inline_handler(self, func, **kwargs): + """ + Description: TBD + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chosen_inline_handler(handler_dict) + return handler + + return decorator + + def add_chosen_inline_handler(self, handler_dict): + """ + Description: TBD + :param handler_dict: + :return: + """ + self.chosen_inline_handlers.append(handler_dict) + + def register_chosen_inline_handler(self, callback, func, **kwargs): + """ + Registers chosen inline handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_chosen_inline_handler(handler_dict) + + def callback_query_handler(self, func, **kwargs): + """ + Callback request handler decorator + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_callback_query_handler(handler_dict) + return handler + + return decorator + + def add_callback_query_handler(self, handler_dict): + """ + Adds a callback request handler + :param handler_dict: + :return: + """ + self.callback_query_handlers.append(handler_dict) + + def register_callback_query_handler(self, callback, func, **kwargs): + """ + Registers callback query handler.. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_callback_query_handler(handler_dict) + + def shipping_query_handler(self, func, **kwargs): + """ + Shipping request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_shipping_query_handler(handler_dict) + return handler + + return decorator + + def add_shipping_query_handler(self, handler_dict): + """ + Adds a shipping request handler + :param handler_dict: + :return: + """ + self.shipping_query_handlers.append(handler_dict) + + def register_shipping_query_handler(self, callback, func, **kwargs): + """ + Registers shipping query handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_shipping_query_handler(handler_dict) + + def pre_checkout_query_handler(self, func, **kwargs): + """ + Pre-checkout request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_pre_checkout_query_handler(handler_dict) + return handler + + return decorator + + def add_pre_checkout_query_handler(self, handler_dict): + """ + Adds a pre-checkout request handler + :param handler_dict: + :return: + """ + self.pre_checkout_query_handlers.append(handler_dict) + + def register_pre_checkout_query_handler(self, callback, func, **kwargs): + """ + Registers pre-checkout request handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_pre_checkout_query_handler(handler_dict) + + def poll_handler(self, func, **kwargs): + """ + Poll request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_poll_handler(handler_dict) + return handler + + return decorator + + def add_poll_handler(self, handler_dict): + """ + Adds a poll request handler + :param handler_dict: + :return: + """ + self.poll_handlers.append(handler_dict) + + def register_poll_handler(self, callback, func, **kwargs): + """ + Registers poll handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_poll_handler(handler_dict) + + def poll_answer_handler(self, func=None, **kwargs): + """ + Poll_answer request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_poll_answer_handler(handler_dict) + return handler + + return decorator + + def add_poll_answer_handler(self, handler_dict): + """ + Adds a poll_answer request handler + :param handler_dict: + :return: + """ + self.poll_answer_handlers.append(handler_dict) + + def register_poll_answer_handler(self, callback, func, **kwargs): + """ + Registers poll answer handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_poll_answer_handler(handler_dict) + + def my_chat_member_handler(self, func=None, **kwargs): + """ + my_chat_member handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_my_chat_member_handler(handler_dict) + return handler + + return decorator + + def add_my_chat_member_handler(self, handler_dict): + """ + Adds a my_chat_member handler + :param handler_dict: + :return: + """ + self.my_chat_member_handlers.append(handler_dict) + + def register_my_chat_member_handler(self, callback, func=None, **kwargs): + """ + Registers my chat member handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_my_chat_member_handler(handler_dict) + + def chat_member_handler(self, func=None, **kwargs): + """ + chat_member handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_member_handler(handler_dict) + return handler + + return decorator + + def add_chat_member_handler(self, handler_dict): + """ + Adds a chat_member handler + :param handler_dict: + :return: + """ + self.chat_member_handlers.append(handler_dict) + + def register_chat_member_handler(self, callback, func=None, **kwargs): + """ + Registers chat member handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_chat_member_handler(handler_dict) + + def chat_join_request_handler(self, func=None, **kwargs): + """ + chat_join_request handler + :param func: + :param kwargs: + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_join_request_handler(handler_dict) + return handler + + return decorator + + def add_chat_join_request_handler(self, handler_dict): + """ + Adds a chat_join_request handler + :param handler_dict: + :return: + """ + self.chat_join_request_handlers.append(handler_dict) + + def register_chat_join_request_handler(self, callback, func=None, **kwargs): + """ + Registers chat join request handler. + :param callback: function to be called + :param func: + :return: decorated function + """ + handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + self.add_chat_join_request_handler(handler_dict) + + @staticmethod + def _build_handler_dict(handler, **filters): + """ + Builds a dictionary for a handler + :param handler: + :param filters: + :return: + """ + return { + 'function': handler, + 'filters': {ftype: fvalue for ftype, fvalue in filters.items() if fvalue is not None} + # Remove None values, they are skipped in _test_filter anyway + #'filters': filters + } + + async def skip_updates(self): + await self.get_updates(-1) + return True + + # all methods begin here + + async def get_me(self) -> types.User: + """ + Returns basic information about the bot in form of a User object. + """ + result = await asyncio_helper.get_me(self.token) + return types.User.de_json(result) + + async def get_file(self, file_id: str) -> types.File: + """ + Use this method to get basic info about a file and prepare it for downloading. + For the moment, bots can download files of up to 20MB in size. + On success, a File object is returned. + It is guaranteed that the link will be valid for at least 1 hour. + When the link expires, a new one can be requested by calling get_file again. + """ + return types.File.de_json(await asyncio_helper.get_file(self.token, file_id)) + + async def get_file_url(self, file_id: str) -> str: + return await asyncio_helper.get_file_url(self.token, file_id) + + async def download_file(self, file_path: str) -> bytes: + return await asyncio_helper.download_file(self.token, file_path) + + async def log_out(self) -> bool: + """ + Use this method to log out from the cloud Bot API server before launching the bot locally. + You MUST log out the bot before running it locally, otherwise there is no guarantee + that the bot will receive updates. + After a successful call, you can immediately log in on a local server, + but will not be able to log in back to the cloud Bot API server for 10 minutes. + Returns True on success. + """ + return await asyncio_helper.log_out(self.token) + + async def close(self) -> bool: + """ + Use this method to close the bot instance before moving it from one local server to another. + You need to delete the webhook before calling this method to ensure that the bot isn't launched again + after server restart. + The method will return error 429 in the first 10 minutes after the bot is launched. + Returns True on success. + """ + return await asyncio_helper.close(self.token) + + def enable_saving_states(self, filename="./.state-save/states.pkl"): + """ + Enable saving states (by default saving disabled) + + :param filename: Filename of saving file + """ + + self.current_states = asyncio_handler_backends.StateFile(filename=filename) + self.current_states._create_dir() + + async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, + drop_pending_updates = None, timeout=None): + """ + Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an + update for the bot, we will send an HTTPS POST request to the specified url, + containing a JSON-serialized Update. + In case of an unsuccessful request, we will give up after a reasonable amount of attempts. + Returns True on success. + + :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration + :param certificate: Upload your public key certificate so that the root certificate in use can be checked. + See our self-signed guide for details. + :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook + for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, + and higher values to increase your bot's throughput. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates + of these types. See Update for a complete list of available update types. + Specify an empty list to receive all updates regardless of type (default). + If not specified, the previous setting will be used. + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address + resolved through DNS + :param drop_pending_updates: Pass True to drop all pending updates + :param timeout: Integer. Request connection timeout + :return: + """ + return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, + drop_pending_updates, timeout) + + async def delete_webhook(self, drop_pending_updates=None, timeout=None): + """ + Use this method to remove webhook integration if you decide to switch back to getUpdates. + + :param drop_pending_updates: Pass True to drop all pending updates + :param timeout: Integer. Request connection timeout + :return: bool + """ + return await asyncio_helper.delete_webhook(self.token, drop_pending_updates, timeout) + + async def get_webhook_info(self, timeout=None): + """ + Use this method to get current webhook status. Requires no parameters. + If the bot is using getUpdates, will return an object with the url field empty. + + :param timeout: Integer. Request connection timeout + :return: On success, returns a WebhookInfo object. + """ + result = await asyncio_helper.get_webhook_info(self.token, timeout) + return types.WebhookInfo.de_json(result) + + async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, + limit: Optional[int]=None) -> types.UserProfilePhotos: + """ + Retrieves the user profile photos of the person with 'user_id' + See https://core.telegram.org/bots/api#getuserprofilephotos + :param user_id: + :param offset: + :param limit: + :return: API reply. + """ + 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: + """ + 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. + :param chat_id: + :return: + """ + result = await asyncio_helper.get_chat(self.token, chat_id) + return types.Chat.de_json(result) + + async def leave_chat(self, chat_id: Union[int, str]) -> bool: + """ + Use this method for your bot to leave a group, supergroup or channel. Returns True on success. + :param chat_id: + :return: + """ + result = await asyncio_helper.leave_chat(self.token, chat_id) + return result + + async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMember]: + """ + Use this method to get a list of administrators in a chat. + On success, returns an Array of ChatMember objects that contains + information about all chat administrators except other bots. + :param chat_id: Unique identifier for the target chat or username + of the target supergroup or channel (in the format @channelusername) + :return: + """ + result = await asyncio_helper.get_chat_administrators(self.token, chat_id) + return [types.ChatMember.de_json(r) for r in result] + + async def get_chat_members_count(self, chat_id: Union[int, str]) -> int: + """ + This function is deprecated. Use `get_chat_member_count` instead + """ + logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead.') + result = await asyncio_helper.get_chat_member_count(self.token, chat_id) + return result + + async def get_chat_member_count(self, chat_id: Union[int, str]) -> int: + """ + Use this method to get the number of members in a chat. Returns Int on success. + :param chat_id: + :return: + """ + result = await asyncio_helper.get_chat_member_count(self.token, chat_id) + return result + + async def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: + """ + Use this method to set a new group sticker set for a supergroup. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + Use the field can_set_sticker_set optionally returned in getChat requests to check + if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param sticker_set_name: Name of the sticker set to be set as the group sticker set + :return: + """ + result = await asyncio_helper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) + return result + + async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat + for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set + optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :return: + """ + result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) + return result + + async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: + """ + Use this method to get information about a member of a chat. Returns a ChatMember object on success. + :param chat_id: + :param user_id: + :return: + """ + result = await asyncio_helper.get_chat_member(self.token, chat_id, user_id) + return types.ChatMember.de_json(result) + + async def send_message( + self, chat_id: Union[int, str], text: str, + disable_web_page_preview: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send text messages. + + Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. + If you must send more than 4000 characters, + use the `split_string` or `smart_split` function in util.py. + + :param chat_id: + :param text: + :param disable_web_page_preview: + :param reply_to_message_id: + :param reply_markup: + :param parse_mode: + :param disable_notification: Boolean, Optional. Sends the message silently. + :param timeout: + :param entities: + :param allow_sending_without_reply: + :return: API reply. + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_message( + self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, + entities, allow_sending_without_reply)) + + async def forward_message( + self, chat_id: Union[int, str], from_chat_id: Union[int, str], + message_id: int, disable_notification: Optional[bool]=None, + timeout: Optional[int]=None) -> types.Message: + """ + Use this method to forward messages of any kind. + :param disable_notification: + :param chat_id: which chat to forward + :param from_chat_id: which chat message from + :param message_id: message id + :param timeout: + :return: API reply. + """ + return types.Message.de_json( + await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) + + async def copy_message( + self, chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None) -> int: + """ + Use this method to copy messages of any kind. + :param chat_id: which chat to forward + :param from_chat_id: which chat message from + :param message_id: message id + :param caption: + :param parse_mode: + :param caption_entities: + :param disable_notification: + :param reply_to_message_id: + :param allow_sending_without_reply: + :param reply_markup: + :param timeout: + :return: API reply. + """ + return types.MessageID.de_json( + await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, + disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, + timeout)) + + async def delete_message(self, chat_id: Union[int, str], message_id: int, + timeout: Optional[int]=None) -> bool: + """ + Use this method to delete message. Returns True on success. + :param chat_id: in which chat to delete + :param message_id: which message to delete + :param timeout: + :return: API reply. + """ + return await asyncio_helper.delete_message(self.token, chat_id, message_id, timeout) + + async def send_dice( + self, chat_id: Union[int, str], + emoji: Optional[str]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send dices. + :param chat_id: + :param emoji: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :return: Message + """ + return types.Message.de_json( + await asyncio_helper.send_dice( + self.token, chat_id, emoji, disable_notification, reply_to_message_id, + reply_markup, timeout, allow_sending_without_reply) + ) + + async def send_photo( + self, chat_id: Union[int, str], photo: Union[Any, str], + caption: Optional[str]=None, reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send photos. + :param chat_id: + :param photo: + :param caption: + :param parse_mode: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param caption_entities: + :param allow_sending_without_reply: + :return: API reply. + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_photo( + self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, + parse_mode, disable_notification, timeout, caption_entities, + allow_sending_without_reply)) + + async def send_audio( + self, chat_id: Union[int, str], audio: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, + performer: Optional[str]=None, title: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send audio files, if you want Telegram clients to display them in the music player. + Your audio must be in the .mp3 format. + :param chat_id:Unique identifier for the message recipient + :param audio:Audio file to send. + :param caption: + :param duration:Duration of the audio in seconds + :param performer:Performer + :param title:Track name + :param reply_to_message_id:If the message is a reply, ID of the original message + :param reply_markup: + :param parse_mode + :param disable_notification: + :param timeout: + :param thumb: + :param caption_entities: + :param allow_sending_without_reply: + :return: Message + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_audio( + self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, thumb, + caption_entities, allow_sending_without_reply)) + + async def send_voice( + self, chat_id: Union[int, str], voice: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send audio files, if you want Telegram clients to display the file + as a playable voice message. + :param chat_id:Unique identifier for the message recipient. + :param voice: + :param caption: + :param duration:Duration of sent audio in seconds + :param reply_to_message_id: + :param reply_markup: + :param parse_mode + :param disable_notification: + :param timeout: + :param caption_entities: + :param allow_sending_without_reply: + :return: Message + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_voice( + self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, + parse_mode, disable_notification, timeout, caption_entities, + allow_sending_without_reply)) + + async def send_document( + self, chat_id: Union[int, str], data: Union[Any, str], + reply_to_message_id: Optional[int]=None, + caption: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + 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, + disable_content_type_detection: Optional[bool]=None) -> types.Message: + """ + Use this method to send general files. + :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: 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; 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 async 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( + await asyncio_helper.send_data( + 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)) + + async def send_sticker( + self, chat_id: Union[int, str], data: Union[Any, str], + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send .webp stickers. + :param chat_id: + :param data: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: to disable the notification + :param timeout: timeout + :param allow_sending_without_reply: + :return: API reply. + """ + return types.Message.de_json( + await asyncio_helper.send_data( + 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)) + + async def send_video( + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, + caption: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + supports_streaming: Optional[bool]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + width: Optional[int]=None, + height: Optional[int]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send video files, Telegram clients support mp4 videos. + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend + a video that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param caption: String : Video caption (may also be used when resending videos by file_id). + :param parse_mode: + :param supports_streaming: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent + :param width: + :param height: + :param caption_entities: + :param allow_sending_without_reply: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_video( + self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, + parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, + caption_entities, allow_sending_without_reply)) + + async def send_animation( + self, chat_id: Union[int, str], animation: Union[Any, str], + duration: Optional[int]=None, + caption: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an + animation that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param caption: String : Animation caption (may also be used when resending animation by file_id). + :param parse_mode: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent + :param caption_entities: + :param allow_sending_without_reply: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + return types.Message.de_json( + await asyncio_helper.send_animation( + self.token, chat_id, animation, duration, caption, reply_to_message_id, + reply_markup, parse_mode, disable_notification, timeout, thumb, + caption_entities, allow_sending_without_reply)) + + async def send_video_note( + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, + length: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send + video messages. + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id + :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend + a video that is already on the Telegram server + :param duration: Integer : Duration of sent video in seconds + :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param thumb: InputFile or String : Thumbnail of the file sent + :param allow_sending_without_reply: + :return: + """ + return types.Message.de_json( + await asyncio_helper.send_video_note( + self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, + disable_notification, timeout, thumb, allow_sending_without_reply)) + + async def send_media_group( + self, chat_id: Union[int, str], + media: List[Union[ + types.InputMediaAudio, types.InputMediaDocument, + types.InputMediaPhoto, types.InputMediaVideo]], + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: + """ + send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + :param chat_id: + :param media: + :param disable_notification: + :param reply_to_message_id: + :param timeout: + :param allow_sending_without_reply: + :return: + """ + result = await asyncio_helper.send_media_group( + self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, + allow_sending_without_reply) + return [types.Message.de_json(msg) for msg in result] + + async def send_location( + self, chat_id: Union[int, str], + latitude: float, longitude: float, + live_period: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, + proximity_alert_radius: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + + + """ + Use this method to send point on the map. + :param chat_id: + :param latitude: + :param longitude: + :param live_period: + :param reply_to_message_id: + :param reply_markup: + :param disable_notification: + :param timeout: + :param horizontal_accuracy: + :param heading: + :param proximity_alert_radius: + :param allow_sending_without_reply: + :return: API reply. + """ + return types.Message.de_json( + await asyncio_helper.send_location( + self.token, chat_id, latitude, longitude, live_period, + reply_to_message_id, reply_markup, disable_notification, timeout, + horizontal_accuracy, heading, proximity_alert_radius, + allow_sending_without_reply)) + + async def edit_message_live_location( + self, latitude: float, longitude: float, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, + proximity_alert_radius: Optional[int]=None) -> types.Message: + """ + Use this method to edit live location + :param latitude: + :param longitude: + :param chat_id: + :param message_id: + :param reply_markup: + :param timeout: + :param inline_message_id: + :param horizontal_accuracy: + :param heading: + :param proximity_alert_radius: + :return: + """ + return types.Message.de_json( + 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)) + + async def stop_message_live_location( + self, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None) -> types.Message: + """ + Use this method to stop updating a live location message sent by the bot + or via the bot (for inline bots) before live_period expires + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :param timeout: + :return: + """ + return types.Message.de_json( + await asyncio_helper.stop_message_live_location( + self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) + + async def send_venue( + self, chat_id: Union[int, str], + latitude: float, longitude: float, + title: str, address: str, + foursquare_id: Optional[str]=None, + foursquare_type: Optional[str]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + google_place_id: Optional[str]=None, + google_place_type: Optional[str]=None) -> types.Message: + """ + Use this method to send information about a venue. + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel + :param latitude: Float : Latitude of the venue + :param longitude: Float : Longitude of the venue + :param title: String : Name of the venue + :param address: String : Address of the venue + :param foursquare_id: String : Foursquare identifier of the venue + :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/async default”, + “arts_entertainment/aquarium” or “food/icecream”.) + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :param google_place_id: + :param google_place_type: + :return: + """ + return types.Message.de_json( + await asyncio_helper.send_venue( + self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, + disable_notification, reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply, google_place_id, google_place_type) + ) + + async def send_contact( + self, chat_id: Union[int, str], phone_number: str, + first_name: str, last_name: Optional[str]=None, + vcard: Optional[str]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + return types.Message.de_json( + await asyncio_helper.send_contact( + self.token, chat_id, phone_number, first_name, last_name, vcard, + disable_notification, reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply) + ) + + async def send_chat_action( + self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: + """ + Use this method when you need to tell the user that something is happening on the bot's side. + The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear + its typing status). + :param chat_id: + :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', + 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', + 'upload_video_note'. + :param timeout: + :return: API reply. :type: boolean + """ + return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout) + + async def kick_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, + revoke_messages: Optional[bool]=None) -> bool: + """ + This function is deprecated. Use `ban_chat_member` instead + """ + logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') + return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + + async def ban_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, + revoke_messages: Optional[bool]=None) -> bool: + """ + Use this method to ban a user in a group, a supergroup or a channel. + In the case of supergroups and channels, the user will not be able to return to the chat on their + own using invite links, etc., unless unbanned first. + Returns True on success. + :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup + :param user_id: Int : Unique identifier of the target user + :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or + less than 30 seconds from the current time they are considered to be banned forever + :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. + If False, the user will be able to see messages in the group that were sent before the user was removed. + Always True for supergroups and channels. + :return: boolean + """ + return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + + async def unban_chat_member( + self, chat_id: Union[int, str], user_id: int, + only_if_banned: Optional[bool]=False) -> bool: + """ + Use this method to unban a previously kicked user in a supergroup or channel. + The user will not return to the group or channel automatically, but will be able to join via link, etc. + The bot must be an administrator for this to work. By async default, this method guarantees that after the call + the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat + they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. + + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel + (in the format @username) + :param user_id: Unique identifier of the target user + :param only_if_banned: Do nothing if the user is not banned + :return: True on success + """ + return await asyncio_helper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) + + async def restrict_chat_member( + self, chat_id: Union[int, str], user_id: int, + until_date: Optional[Union[int, datetime]]=None, + can_send_messages: Optional[bool]=None, + can_send_media_messages: Optional[bool]=None, + can_send_polls: Optional[bool]=None, + can_send_other_messages: Optional[bool]=None, + can_add_web_page_previews: Optional[bool]=None, + can_change_info: Optional[bool]=None, + can_invite_users: Optional[bool]=None, + can_pin_messages: Optional[bool]=None) -> bool: + """ + Use this method to restrict a user in a supergroup. + The bot must be an administrator in the supergroup for this to work and must have + the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. + + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup + or channel (in the format @channelusername) + :param user_id: Int : Unique identifier of the target user + :param until_date: Date when restrictions will be lifted for the user, unix time. + If user is restricted for more than 366 days or less than 30 seconds from the current time, + they are considered to be restricted forever + :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues + :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes + and voice notes, implies can_send_messages + :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages + :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and + use inline bots, implies can_send_media_messages + :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, + implies can_send_media_messages + :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. + Ignored in public supergroups + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, + implies can_invite_users + :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups + :return: True on success + """ + return await asyncio_helper.restrict_chat_member( + self.token, chat_id, user_id, until_date, + can_send_messages, can_send_media_messages, + can_send_polls, can_send_other_messages, + can_add_web_page_previews, can_change_info, + can_invite_users, can_pin_messages) + + async def promote_chat_member( + self, chat_id: Union[int, str], user_id: int, + can_change_info: Optional[bool]=None, + can_post_messages: Optional[bool]=None, + can_edit_messages: Optional[bool]=None, + can_delete_messages: Optional[bool]=None, + can_invite_users: Optional[bool]=None, + can_restrict_members: Optional[bool]=None, + can_pin_messages: Optional[bool]=None, + can_promote_members: Optional[bool]=None, + is_anonymous: Optional[bool]=None, + can_manage_chat: Optional[bool]=None, + can_manage_voice_chats: Optional[bool]=None) -> bool: + """ + Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + Pass False for all boolean parameters to demote a user. + + :param chat_id: Unique identifier for the target chat or username of the target channel ( + in the format @channelusername) + :param user_id: Int : Unique identifier of the target user + :param can_change_info: Bool: Pass True, if the administrator can change chat title, photo and other settings + :param can_post_messages: Bool : Pass True, if the administrator can create channel posts, channels only + :param can_edit_messages: Bool : Pass True, if the administrator can edit messages of other users, channels only + :param can_delete_messages: Bool : Pass True, if the administrator can delete messages of other users + :param can_invite_users: Bool : Pass True, if the administrator can invite new users to the chat + :param can_restrict_members: Bool: Pass True, if the administrator can restrict, ban or unban chat members + :param can_pin_messages: Bool: Pass True, if the administrator can pin messages, supergroups only + :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset + of his own privileges or demote administrators that he has promoted, directly or indirectly + (promoted by administrators that were appointed by him) + :param is_anonymous: Bool: Pass True, if the administrator's presence in the chat is hidden + :param can_manage_chat: Bool: Pass True, if the administrator can access the chat event log, chat statistics, + message statistics in channels, see channel members, + see anonymous administrators in supergroups and ignore slow mode. + Implied by any other administrator privilege + :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats + For now, bots can use this privilege only for passing to other administrators. + :return: True on success. + """ + return await asyncio_helper.promote_chat_member( + self.token, chat_id, user_id, can_change_info, can_post_messages, + can_edit_messages, can_delete_messages, can_invite_users, + can_restrict_members, can_pin_messages, can_promote_members, + is_anonymous, can_manage_chat, can_manage_voice_chats) + + async def set_chat_administrator_custom_title( + self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: + """ + Use this method to set a custom title for an administrator + in a supergroup promoted by the bot. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :param custom_title: New custom title for the administrator; + 0-16 characters, emoji are not allowed + :return: True on success. + """ + return await asyncio_helper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) + + async def set_chat_permissions( + self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: + """ + Use this method to set async default chat permissions for all members. + The bot must be an administrator in the group or a supergroup for this to work + and must have the can_restrict_members admin rights. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param permissions: New async default chat permissions + :return: True on success + """ + return await asyncio_helper.set_chat_permissions(self.token, chat_id, permissions) + + async def create_chat_invite_link( + self, chat_id: Union[int, str], + name: Optional[str]=None, + expire_date: Optional[Union[int, datetime]]=None, + member_limit: Optional[int]=None, + creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: + """ + Use this method to create an additional invite link for a chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param name: Invite link name; 0-32 characters + :param expire_date: Point in time (Unix timestamp) when the link will expire + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified + :return: + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request) + ) + + async def edit_chat_invite_link( + self, chat_id: Union[int, str], + invite_link: Optional[str] = None, + name: Optional[str]=None, + expire_date: Optional[Union[int, datetime]]=None, + member_limit: Optional[int]=None, + creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: + """ + Use this method to edit a non-primary invite link created by the bot. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param name: Invite link name; 0-32 characters + :param invite_link: The invite link to edit + :param expire_date: Point in time (Unix timestamp) when the link will expire + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified + :return: + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) + ) + + async def revoke_chat_invite_link( + self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: + """ + Use this method to revoke an invite link created by the bot. + Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param invite_link: The invite link to revoke + :return: + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.revoke_chat_invite_link(self.token, chat_id, invite_link) + ) + + async def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: + """ + Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator + in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: exported invite link as String on success. + """ + return await asyncio_helper.export_chat_invite_link(self.token, chat_id) + + + async def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: + """ + Use this method to approve a chat join request. + The bot must be an administrator in the chat for this to work and must have + the can_invite_users administrator right. Returns True on success. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :return: True on success. + """ + return await asyncio_helper.approve_chat_join_request(self.token, chat_id, user_id) + + async def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: + """ + Use this method to decline a chat join request. + The bot must be an administrator in the chat for this to work and must have + the can_invite_users administrator right. Returns True on success. + + :param chat_id: Unique identifier for the target chat or username of the target supergroup + (in the format @supergroupusername) + :param user_id: Unique identifier of the target user + :return: True on success. + """ + return await asyncio_helper.decline_chat_join_request(self.token, chat_id, user_id) + + async def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: + """ + Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param photo: InputFile: New chat photo, uploaded using multipart/form-data + :return: + """ + return await asyncio_helper.set_chat_photo(self.token, chat_id, photo) + + async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to delete a chat photo. Photos can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + """ + return await asyncio_helper.delete_chat_photo(self.token, chat_id) + + async def get_my_commands(self, scope: Optional[types.BotCommandScope], + language_code: Optional[str]) -> List[types.BotCommand]: + """ + Use this method to get the current list of the bot's commands. + Returns List of BotCommand on success. + :param scope: The scope of users for which the commands are relevant. + async defaults to BotCommandScopeasync default. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands + """ + result = await asyncio_helper.get_my_commands(self.token, scope, language_code) + return [types.BotCommand.de_json(cmd) for cmd in result] + + async def set_my_commands(self, commands: List[types.BotCommand], + scope: Optional[types.BotCommandScope]=None, + language_code: Optional[str]=None) -> bool: + """ + Use this method to change the list of the bot's commands. + :param commands: List of BotCommand. At most 100 commands can be specified. + :param scope: The scope of users for which the commands are relevant. + async defaults to BotCommandScopeasync default. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands + :return: + """ + return await asyncio_helper.set_my_commands(self.token, commands, scope, language_code) + + async def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, + language_code: Optional[int]=None) -> bool: + """ + Use this method to delete the list of the bot's commands for the given scope and user language. + After deletion, higher level commands will be shown to affected users. + Returns True on success. + :param scope: The scope of users for which the commands are relevant. + async defaults to BotCommandScopeasync default. + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, + for whose language there are no dedicated commands + """ + return await asyncio_helper.delete_my_commands(self.token, scope, language_code) + + async def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: + """ + Use this method to change the title of a chat. Titles can't be changed for private chats. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param title: New chat title, 1-255 characters + :return: + """ + return await asyncio_helper.set_chat_title(self.token, chat_id, title) + + async def set_chat_description(self, chat_id: Union[int, str], description: Optional[str]=None) -> bool: + """ + Use this method to change the description of a supergroup or a channel. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param description: Str: New chat description, 0-255 characters + :return: True on success. + """ + return await asyncio_helper.set_chat_description(self.token, chat_id, description) + + async def pin_chat_message( + self, chat_id: Union[int, str], message_id: int, + disable_notification: Optional[bool]=False) -> bool: + """ + Use this method to pin a message in a supergroup. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param message_id: Int: Identifier of a message to pin + :param disable_notification: Bool: Pass True, if it is not necessary to send a notification + to all group members about the new pinned message + :return: + """ + return await asyncio_helper.pin_chat_message(self.token, chat_id, message_id, disable_notification) + + async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: + """ + Use this method to unpin specific pinned message in a supergroup chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :param message_id: Int: Identifier of a message to unpin + :return: + """ + return await asyncio_helper.unpin_chat_message(self.token, chat_id, message_id) + + async def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to unpin a all pinned messages in a supergroup chat. + The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :return: + """ + return await asyncio_helper.unpin_all_chat_messages(self.token, chat_id) + + async def edit_message_text( + self, text: str, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + parse_mode: Optional[str]=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. + :param text: + :param chat_id: + :param message_id: + :param inline_message_id: + :param parse_mode: + :param entities: + :param disable_web_page_preview: + :param reply_markup: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, + 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) + + async def edit_message_media( + self, media: Any, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + """ + Use this method to edit animation, audio, document, photo, or video messages. + If a message is a part of a message album, then it can be edited only to a photo or a video. + Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. + Use previously uploaded file via its file_id or specify a URL. + :param media: + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ + result = await asyncio_helper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) + if type(result) == bool: # if edit inline message return is bool not Message. + return result + return types.Message.de_json(result) + + async def edit_message_reply_markup( + self, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + """ + Use this method to edit only the reply markup of messages. + :param chat_id: + :param message_id: + :param inline_message_id: + :param reply_markup: + :return: + """ + result = await asyncio_helper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) + if type(result) == bool: + return result + return types.Message.de_json(result) + + async def send_game( + self, chat_id: Union[int, str], game_short_name: str, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None) -> types.Message: + """ + Used to send the game + :param chat_id: + :param game_short_name: + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :return: + """ + result = await asyncio_helper.send_game( + self.token, chat_id, game_short_name, disable_notification, + reply_to_message_id, reply_markup, timeout, + allow_sending_without_reply) + return types.Message.de_json(result) + + async def set_game_score( + self, user_id: Union[int, str], score: int, + force: Optional[bool]=None, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: + """ + Sets the value of points in the game to a specific user + :param user_id: + :param score: + :param force: + :param chat_id: + :param message_id: + :param inline_message_id: + :param disable_edit_message: + :return: + """ + result = await asyncio_helper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, + message_id, inline_message_id) + if type(result) == bool: + return result + return types.Message.de_json(result) + + async def get_game_high_scores( + self, user_id: int, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: + """ + Gets top points and game play + :param user_id: + :param chat_id: + :param message_id: + :param inline_message_id: + :return: + """ + result = await asyncio_helper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) + return [types.GameHighScore.de_json(r) for r in result] + + async def send_invoice( + self, chat_id: Union[int, str], title: str, description: str, + invoice_payload: str, provider_token: str, currency: str, + prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, + photo_url: Optional[str]=None, photo_size: Optional[int]=None, + photo_width: Optional[int]=None, photo_height: Optional[int]=None, + need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, + need_email: Optional[bool]=None, need_shipping_address: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, + is_flexible: Optional[bool]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + provider_data: Optional[str]=None, + timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[List[int]]=None) -> types.Message: + """ + Sends invoice + :param chat_id: Unique identifier for the target private chat + :param title: Product name + :param description: Product description + :param invoice_payload: Bot-async defined invoice payload, 1-128 bytes. This will not be displayed to the user, + use for your internal processes. + :param provider_token: Payments provider token, obtained via @Botfather + :param currency: Three-letter ISO 4217 currency code, + see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components + (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice + when used as a start parameter + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods + or a marketing image for a service. People like it better when they see what they are paying for. + :param photo_size: Photo size + :param photo_width: Photo width + :param photo_height: Photo height + :param need_name: Pass True, if you require the user's full name to complete the order + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :param need_email: Pass True, if you require the user's email to complete the order + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :param is_flexible: Pass True, if the final price depends on the shipping method + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_markup: 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 + :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 allow_sending_without_reply: + :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. + :return: + """ + result = await asyncio_helper.send_invoice( + self.token, chat_id, title, description, invoice_payload, provider_token, + currency, prices, start_parameter, photo_url, photo_size, photo_width, + photo_height, need_name, need_phone_number, need_email, need_shipping_address, + send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, + reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, + max_tip_amount, suggested_tip_amounts) + return types.Message.de_json(result) + + # noinspection PyShadowingBuiltins + async def send_poll( + self, chat_id: Union[int, str], question: str, options: List[str], + is_anonymous: Optional[bool]=None, type: Optional[str]=None, + allows_multiple_answers: Optional[bool]=None, + correct_option_id: Optional[int]=None, + explanation: Optional[str]=None, + explanation_parse_mode: Optional[str]=None, + open_period: Optional[int]=None, + close_date: Optional[Union[int, datetime]]=None, + is_closed: Optional[bool]=None, + disable_notification: Optional[bool]=False, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + allow_sending_without_reply: Optional[bool]=None, + timeout: Optional[int]=None, + explanation_entities: Optional[List[types.MessageEntity]]=None) -> types.Message: + """ + Send polls + :param chat_id: + :param question: + :param options: array of str with answers + :param is_anonymous: + :param type: + :param allows_multiple_answers: + :param correct_option_id: + :param explanation: + :param explanation_parse_mode: + :param open_period: + :param close_date: + :param is_closed: + :param disable_notification: + :param reply_to_message_id: + :param allow_sending_without_reply: + :param reply_markup: + :param timeout: + :param explanation_entities: + :return: + """ + + if isinstance(question, types.Poll): + raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") + + return types.Message.de_json( + await asyncio_helper.send_poll( + self.token, chat_id, + question, options, + is_anonymous, type, allows_multiple_answers, correct_option_id, + explanation, explanation_parse_mode, open_period, close_date, is_closed, + disable_notification, reply_to_message_id, allow_sending_without_reply, + reply_markup, timeout, explanation_entities)) + + async def stop_poll( + self, chat_id: Union[int, str], message_id: int, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: + """ + Stops poll + :param chat_id: + :param message_id: + :param reply_markup: + :return: + """ + return types.Poll.de_json(await asyncio_helper.stop_poll(self.token, chat_id, message_id, reply_markup)) + + async def answer_shipping_query( + self, shipping_query_id: str, ok: bool, + shipping_options: Optional[List[types.ShippingOption]]=None, + error_message: Optional[str]=None) -> bool: + """ + Asks for an answer to a shipping question + :param shipping_query_id: + :param ok: + :param shipping_options: + :param error_message: + :return: + """ + return await asyncio_helper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) + + async def answer_pre_checkout_query( + self, pre_checkout_query_id: int, ok: bool, + error_message: Optional[str]=None) -> bool: + """ + Response to a request for pre-inspection + :param pre_checkout_query_id: + :param ok: + :param error_message: + :return: + """ + return await asyncio_helper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) + + async def edit_message_caption( + self, caption: str, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + """ + Use this method to edit captions of messages + :param caption: + :param chat_id: + :param message_id: + :param inline_message_id: + :param parse_mode: + :param caption_entities: + :param reply_markup: + :return: + """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + + result = await asyncio_helper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, + parse_mode, caption_entities, reply_markup) + if type(result) == bool: + return result + return types.Message.de_json(result) + + async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: + """ + Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` + :param message: + :param text: + :param kwargs: + :return: + """ + return await self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) + + async def answer_inline_query( + self, inline_query_id: str, + results: List[Any], + cache_time: Optional[int]=None, + is_personal: Optional[bool]=None, + next_offset: Optional[str]=None, + switch_pm_text: Optional[str]=None, + switch_pm_parameter: Optional[str]=None) -> bool: + """ + Use this method to send answers to an inline query. On success, True is returned. + No more than 50 results per query are allowed. + :param inline_query_id: Unique identifier for the answered query + :param results: Array of results for the inline query + :param cache_time: The maximum amount of time in seconds that the result of the inline query + may be cached on the server. + :param is_personal: Pass True, if results may be cached on the server side only for + the user that sent the query. + :param next_offset: Pass the offset that a client should send in the next query with the same text + to receive more results. + :param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user + to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter + :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button + :return: True means success. + """ + return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, + switch_pm_text, switch_pm_parameter) + + async def answer_callback_query( + self, callback_query_id: int, + text: Optional[str]=None, show_alert: Optional[bool]=None, + url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: + """ + Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to + the user as a notification at the top of the chat screen or as an alert. + :param callback_query_id: + :param text: + :param show_alert: + :param url: + :param cache_time: + :return: + """ + return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) + + async def set_sticker_set_thumb( + self, name: str, user_id: int, thumb: Union[Any, str]=None): + """ + Use this method to set the thumbnail of a sticker set. + Animated thumbnails can be set for animated sticker sets only. Returns True on success. + """ + return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) + + async def get_sticker_set(self, name: str) -> types.StickerSet: + """ + Use this method to get a sticker set. On success, a StickerSet object is returned. + :param name: + :return: + """ + result = await asyncio_helper.get_sticker_set(self.token, name) + return types.StickerSet.de_json(result) + + async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: + """ + Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet + methods (can be used multiple times). Returns the uploaded File on success. + :param user_id: + :param png_sticker: + :return: + """ + result = await asyncio_helper.upload_sticker_file(self.token, user_id, png_sticker) + return types.File.de_json(result) + + async def create_new_sticker_set( + self, user_id: int, name: str, title: str, + emojis: str, + png_sticker: Union[Any, str], + tgs_sticker: Union[Any, str], + contains_masks: Optional[bool]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to create new sticker set owned by a user. + The bot will be able to edit the created sticker set. + Returns True on success. + :param user_id: + :param name: + :param title: + :param emojis: + :param png_sticker: + :param tgs_sticker: + :param contains_masks: + :param mask_position: + :return: + """ + return await asyncio_helper.create_new_sticker_set( + self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, + contains_masks, mask_position) + + + async def add_sticker_to_set( + self, user_id: int, name: str, emojis: str, + png_sticker: Optional[Union[Any, str]]=None, + tgs_sticker: Optional[Union[Any, str]]=None, + mask_position: Optional[types.MaskPosition]=None) -> bool: + """ + Use this method to add a new sticker to a set created by the bot. + It's required to pass `png_sticker` or `tgs_sticker`. + Returns True on success. + :param user_id: + :param name: + :param emojis: + :param png_sticker: Required if `tgs_sticker` is None + :param tgs_sticker: Required if `png_sticker` is None + :param mask_position: + :return: + """ + return await asyncio_helper.add_sticker_to_set( + self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position) + + + async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: + """ + Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. + :param sticker: + :param position: + :return: + """ + return await asyncio_helper.set_sticker_position_in_set(self.token, sticker, position) + + async def delete_sticker_from_set(self, sticker: str) -> bool: + """ + Use this method to delete a sticker from a set created by the bot. Returns True on success. + :param sticker: + :return: + """ + return await asyncio_helper.delete_sticker_from_set(self.token, sticker) + + + async def set_state(self, chat_id, state): + """ + Sets a new state of a user. + :param chat_id: + :param state: new state. can be string or integer. + """ + await self.current_states.add_state(chat_id, state) + + async def delete_state(self, chat_id): + """ + Delete the current state of a user. + :param chat_id: + :return: + """ + await self.current_states.delete_state(chat_id) + + def retrieve_data(self, chat_id): + return self.current_states.retrieve_data(chat_id) + + async def get_state(self, chat_id): + """ + Get current state of a user. + :param chat_id: + :return: state of a user + """ + return await self.current_states.current_state(chat_id) + + async def add_data(self, chat_id, **kwargs): + """ + Add data to states. + :param chat_id: + """ + for key, value in kwargs.items(): + await self.current_states._add_data(chat_id, key, value) \ No newline at end of file diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index c8242fef2..cce7017c6 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -144,7 +144,8 @@ def __init__(self, bot): self._bot = bot async def check(self, message): - return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] + result = await self._bot.get_chat_member(message.chat.id, message.from_user.id) + return result.status in ['creator', 'administrator'] class StateFilter(AdvancedCustomFilter): """ @@ -158,10 +159,11 @@ def __init__(self, bot): key = 'state' async def check(self, message, text): - if await self.bot.current_states.current_state(message.from_user.id) is False: return False + result = await self.bot.current_states.current_state(message.from_user.id) + if result is False: return False elif text == '*': return True - elif type(text) is list: return await self.bot.current_states.current_state(message.from_user.id) in text - return await self.bot.current_states.current_state(message.from_user.id) == text + elif type(text) is list: return result in text + return result == text class IsDigitFilter(SimpleCustomFilter): """ diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index b46c9887c..d3c452f86 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -94,11 +94,11 @@ async def current_state(self, chat_id): async def delete_state(self, chat_id): """Delete a state""" - states_data = await self._read_data() + states_data = self._read_data() states_data.pop(chat_id) await self._save_data(states_data) - async def _read_data(self): + def _read_data(self): """ Read the data from file. """ @@ -107,7 +107,7 @@ async def _read_data(self): file.close() return states_data - async def _create_dir(self): + def _create_dir(self): """ Create directory .save-handlers. """ @@ -152,7 +152,7 @@ async def finish(self, chat_id): """ await self.delete_state(chat_id) - async def retrieve_data(self, chat_id): + def retrieve_data(self, chat_id): """ Save input text. @@ -195,7 +195,7 @@ async def __aenter__(self): return self.data async def __aexit__(self, exc_type, exc_val, exc_tb): - old_data = await self.obj._read_data() + old_data = self.obj._read_data() for i in self.data: old_data[self.chat_id]['data'][i] = self.data.get(i) await self.obj._save_data(old_data) From bfc0b8ecd540c8e9af118011be4087defa016808 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 28 Nov 2021 00:21:09 +0500 Subject: [PATCH 0764/1808] Update async_telebot.py --- telebot/async_telebot.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index eb2ac4490..7ee990ffe 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -69,30 +69,6 @@ def handle(self, exception): return False -class SkipHandler: - """ - Class for skipping handlers. - Just return instance of this class - in middleware to skip handler. - Update will go to post_process, - but will skip execution of handler. - """ - - def __init__(self) -> None: - pass - -class CancelUpdate: - """ - Class for canceling updates. - Just return instance of this class - in middleware to skip update. - Update will skip handler and execution - of post_process in middlewares. - """ - - def __init__(self) -> None: - pass - class AsyncTeleBot: From d58336adcb8bc5508a30285117d123276ffd2f47 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 28 Nov 2021 00:25:56 +0500 Subject: [PATCH 0765/1808] Fix --- telebot/__init__.py | 24 ------------------------ telebot/async_telebot.py | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7983a2648..6f29f86de 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -68,30 +68,6 @@ def handle(self, exception): return False -class SkipHandler: - """ - Class for skipping handlers. - Just return instance of this class - in middleware to skip handler. - Update will go to post_process, - but will skip execution of handler. - """ - - def __init__(self) -> None: - pass - -class CancelUpdate: - """ - Class for canceling updates. - Just return instance of this class - in middleware to skip update. - Update will skip handler and execution - of post_process in middlewares. - """ - - def __init__(self) -> None: - pass - class TeleBot: """ This is TeleBot Class Methods: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7ee990ffe..48b648733 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -69,6 +69,29 @@ def handle(self, exception): return False +class SkipHandler: + """ + Class for skipping handlers. + Just return instance of this class + in middleware to skip handler. + Update will go to post_process, + but will skip execution of handler. + """ + + def __init__(self) -> None: + pass + +class CancelUpdate: + """ + Class for canceling updates. + Just return instance of this class + in middleware to skip update. + Update will skip handler and execution + of post_process in middlewares. + """ + + def __init__(self) -> None: + pass class AsyncTeleBot: From 7d9856dae3739565dc2ac9fbfd4d3a9fd9271f70 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 27 Nov 2021 22:29:57 +0300 Subject: [PATCH 0766/1808] Python 3.10 added --- .travis.yml | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2f6ddfbd8..36dbf89e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "3.7" - "3.8" - "3.9" + - "3.10" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/README.md b/README.md index 849a24935..2e9607ec9 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ ## Getting started -This API is tested with Python 3.6-3.9 and Pypy 3. +This API is tested with Python 3.6-3.10 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager)*: From 411c7e915aa820c0fbae0ec28416466dfdfcab96 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 28 Nov 2021 01:04:49 +0500 Subject: [PATCH 0767/1808] No asyncio.run() --- examples/asynchronous_telebot/chat_join_request.py | 4 ++-- .../asynchronous_telebot/chat_member_example.py | 4 ++-- .../custom_filters/admin_filter_example.py | 3 +-- .../custom_filters/general_custom_filters.py | 3 +-- .../custom_filters/id_filter_example.py | 4 +--- .../custom_filters/is_filter_example.py | 4 ++-- .../custom_filters/text_filter_example.py | 3 +-- examples/asynchronous_telebot/custom_states.py | 3 +-- examples/asynchronous_telebot/echo_bot.py | 3 +-- examples/asynchronous_telebot/register_handler.py | 3 +-- .../asynchronous_telebot/skip_updates_example.py | 4 ++-- telebot/async_telebot.py | 14 +++++++------- 12 files changed, 22 insertions(+), 30 deletions(-) diff --git a/examples/asynchronous_telebot/chat_join_request.py b/examples/asynchronous_telebot/chat_join_request.py index 6b2bfb7b9..5262ebdd1 100644 --- a/examples/asynchronous_telebot/chat_join_request.py +++ b/examples/asynchronous_telebot/chat_join_request.py @@ -1,5 +1,5 @@ from telebot.async_telebot import AsyncTeleBot -import asyncio + import telebot bot = AsyncTeleBot('TOKEN') @@ -8,4 +8,4 @@ async def make_some(message: telebot.types.ChatJoinRequest): await bot.send_message(message.chat.id, 'I accepted a new user!') await bot.approve_chat_join_request(message.chat.id, message.from_user.id) -asyncio.run(bot.polling(skip_pending=True)) \ No newline at end of file +bot.polling(skip_pending=True) \ No newline at end of file diff --git a/examples/asynchronous_telebot/chat_member_example.py b/examples/asynchronous_telebot/chat_member_example.py index 7806cfd73..4d90036af 100644 --- a/examples/asynchronous_telebot/chat_member_example.py +++ b/examples/asynchronous_telebot/chat_member_example.py @@ -1,6 +1,6 @@ from telebot import types,util from telebot.async_telebot import AsyncTeleBot -import asyncio + bot = AsyncTeleBot('TOKEN') #chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member. @@ -30,4 +30,4 @@ async def my_chat_m(message: types.ChatMemberUpdated): @bot.message_handler(content_types=util.content_type_service) async def delall(message: types.Message): await bot.delete_message(message.chat.id,message.message_id) -asyncio.run(bot.polling()) +bot.polling() diff --git a/examples/asynchronous_telebot/custom_filters/admin_filter_example.py b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py index 3aee738ab..5a508c45d 100644 --- a/examples/asynchronous_telebot/custom_filters/admin_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py @@ -1,4 +1,3 @@ -import asyncio from telebot.async_telebot import AsyncTeleBot from telebot import asyncio_filters bot = AsyncTeleBot('TOKEN') @@ -10,4 +9,4 @@ async def answer_for_admin(message): # Register filter bot.add_custom_filter(asyncio_filters.IsAdminFilter(bot)) -asyncio.run(bot.polling()) +bot.polling() diff --git a/examples/asynchronous_telebot/custom_filters/general_custom_filters.py b/examples/asynchronous_telebot/custom_filters/general_custom_filters.py index dfeeb880c..1b36beb31 100644 --- a/examples/asynchronous_telebot/custom_filters/general_custom_filters.py +++ b/examples/asynchronous_telebot/custom_filters/general_custom_filters.py @@ -40,5 +40,4 @@ async def bye_user(message): bot.add_custom_filter(MainFilter()) bot.add_custom_filter(IsAdmin()) -import asyncio -asyncio.run(bot.polling()) +bot.polling() diff --git a/examples/asynchronous_telebot/custom_filters/id_filter_example.py b/examples/asynchronous_telebot/custom_filters/id_filter_example.py index 5878bc71d..5a0796302 100644 --- a/examples/asynchronous_telebot/custom_filters/id_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/id_filter_example.py @@ -1,6 +1,5 @@ from telebot.async_telebot import AsyncTeleBot import telebot -import asyncio bot = AsyncTeleBot('TOKEN') @@ -15,5 +14,4 @@ async def not_admin(message): # Do not forget to register bot.add_custom_filter(telebot.asyncio_filters.ChatFilter()) - -asyncio.run(bot.polling()) +bot.polling() diff --git a/examples/asynchronous_telebot/custom_filters/is_filter_example.py b/examples/asynchronous_telebot/custom_filters/is_filter_example.py index 20857bea4..961fd0fb4 100644 --- a/examples/asynchronous_telebot/custom_filters/is_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/is_filter_example.py @@ -1,6 +1,6 @@ from telebot.async_telebot import AsyncTeleBot import telebot -import asyncio + bot = AsyncTeleBot('TOKEN') @@ -19,4 +19,4 @@ async def text_filter(message): bot.add_custom_filter(telebot.asyncio_filters.IsReplyFilter()) bot.add_custom_filter(telebot.asyncio_filters.ForwardFilter()) -asyncio.run(bot.polling()) +bot.polling() diff --git a/examples/asynchronous_telebot/custom_filters/text_filter_example.py b/examples/asynchronous_telebot/custom_filters/text_filter_example.py index 57513ea55..84aaee931 100644 --- a/examples/asynchronous_telebot/custom_filters/text_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/text_filter_example.py @@ -1,6 +1,5 @@ from telebot.async_telebot import AsyncTeleBot import telebot -import asyncio bot = AsyncTeleBot('TOKEN') @@ -18,4 +17,4 @@ async def text_filter(message): bot.add_custom_filter(telebot.asyncio_filters.TextMatchFilter()) bot.add_custom_filter(telebot.asyncio_filters.TextStartsFilter()) -asyncio.run(bot.polling()) +bot.polling() diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index 2f0257a1c..36132d8da 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -1,7 +1,6 @@ import telebot from telebot import asyncio_filters from telebot.async_telebot import AsyncTeleBot -import asyncio bot = AsyncTeleBot('TOKEN') @@ -72,4 +71,4 @@ async def age_incorrect(message): # set saving states into file. bot.enable_saving_states() # you can delete this if you do not need to save states -asyncio.run(bot.polling()) \ No newline at end of file +bot.polling() \ No newline at end of file diff --git a/examples/asynchronous_telebot/echo_bot.py b/examples/asynchronous_telebot/echo_bot.py index 24cbe3fe2..940aecc46 100644 --- a/examples/asynchronous_telebot/echo_bot.py +++ b/examples/asynchronous_telebot/echo_bot.py @@ -4,7 +4,6 @@ # It echoes any incoming text messages. from telebot.async_telebot import AsyncTeleBot -import asyncio bot = AsyncTeleBot('TOKEN') @@ -24,4 +23,4 @@ async def echo_message(message): await bot.reply_to(message, message.text) -asyncio.run(bot.polling()) +bot.polling() diff --git a/examples/asynchronous_telebot/register_handler.py b/examples/asynchronous_telebot/register_handler.py index 04dabd41c..76d194db2 100644 --- a/examples/asynchronous_telebot/register_handler.py +++ b/examples/asynchronous_telebot/register_handler.py @@ -1,5 +1,4 @@ from telebot.async_telebot import AsyncTeleBot -import asyncio bot = AsyncTeleBot('TOKEN') async def start_executor(message): @@ -16,4 +15,4 @@ async def start_executor(message): # bot.register_edited_message_handler(*args, **kwargs) # And other functions.. -asyncio.run(bot.polling(skip_pending=True)) +bot.polling(skip_pending=True) diff --git a/examples/asynchronous_telebot/skip_updates_example.py b/examples/asynchronous_telebot/skip_updates_example.py index dc2c1578f..c149cb22c 100644 --- a/examples/asynchronous_telebot/skip_updates_example.py +++ b/examples/asynchronous_telebot/skip_updates_example.py @@ -1,5 +1,5 @@ from telebot.async_telebot import AsyncTeleBot -import asyncio + bot = AsyncTeleBot('TOKEN') @bot.message_handler(commands=['start', 'help']) @@ -10,4 +10,4 @@ async def send_welcome(message): async def echo_all(message): await bot.reply_to(message, message.text) -asyncio.run(bot.polling(skip_pending=True))# Skip pending skips old updates +bot.polling(skip_pending=True)# Skip pending skips old updates diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 48b648733..9f24d90b7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -138,7 +138,7 @@ async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=Non json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] - async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, + def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, request_timeout: int=20, allowed_updates: Optional[List[str]]=None, none_stop: Optional[bool]=None): """ @@ -167,10 +167,10 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= non_stop = none_stop if skip_pending: - await self.skip_updates() - await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) + asyncio.run(self.skip_updates()) + asyncio.run(self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates)) - async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, + def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -190,12 +190,12 @@ async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, requ so unwanted updates may be received for a short period of time. """ if skip_pending: - await self.skip_updates() + asyncio.run(self.skip_updates()) self._polling = True while self._polling: try: - await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, - allowed_updates=allowed_updates, *args, **kwargs) + asyncio.run( self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, + allowed_updates=allowed_updates, *args, **kwargs) ) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) From a5305f551c50a0ff06e787fe856c41868eefb94b Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 3 Dec 2021 21:13:02 +0500 Subject: [PATCH 0768/1808] Update README.md --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1908e45de..492a1bfdb 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ * [Reply markup](#reply-markup) * [Advanced use of the API](#advanced-use-of-the-api) * [Using local Bot API Server](#using-local-bot-api-sever) - * [Asynchronous delivery of messages](#asynchronous-delivery-of-messages) + * [Asynchronous TeleBot](#asynchronous-telebot) * [Sending large text messages](#sending-large-text-messages) * [Controlling the amount of Threads used by TeleBot](#controlling-the-amount-of-threads-used-by-telebot) * [The listener mechanism](#the-listener-mechanism) @@ -555,26 +555,26 @@ apihelper.API_URL = "http://localhost:4200/bot{0}/{1}" *Note: 4200 is an example port* -### Asynchronous delivery of messages -There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up your bot __significantly__, but it has unwanted side effects if used without caution. +### Asynchronous TeleBot +New: There is an asynchronous implementation of telebot. It is more flexible. To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot. ```python tb = telebot.AsyncTeleBot("TOKEN") ``` -Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following: +Now, every function that calls the Telegram API is executed in a separate asynchronous task. +Using AsyncTeleBot allows you to do the following: ```python import telebot tb = telebot.AsyncTeleBot("TOKEN") -task = tb.get_me() # Execute an API call -# Do some other operations... -a = 0 -for a in range(100): - a += 10 -result = task.wait() # Get the result of the execution +@tb.message_handler(commands=['start']) +async def start_message(message): + await bot.send_message(message.chat.id, 'Hello'!) + ``` -*Note: if you execute send_xyz functions after eachother without calling wait(), the order in which messages are delivered might be wrong.* + +See more in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) ### Sending large text messages Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API: From 51eabde320b99520f7ed64876fd9b01a799fd0c2 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:11:51 +0500 Subject: [PATCH 0769/1808] Update --- .../download_file_example.py | 20 ++++++++ .../asynchronous_telebot/exception_handler.py | 27 +++++++++++ .../middleware/flooding_middleware.py | 39 +++++++++++++++ .../asynchronous_telebot/middleware/i18n.py | 48 +++++++++++++++++++ .../asynchronous_telebot/send_file_example.py | 27 +++++++++++ 5 files changed, 161 insertions(+) create mode 100644 examples/asynchronous_telebot/download_file_example.py create mode 100644 examples/asynchronous_telebot/exception_handler.py create mode 100644 examples/asynchronous_telebot/middleware/flooding_middleware.py create mode 100644 examples/asynchronous_telebot/middleware/i18n.py create mode 100644 examples/asynchronous_telebot/send_file_example.py diff --git a/examples/asynchronous_telebot/download_file_example.py b/examples/asynchronous_telebot/download_file_example.py new file mode 100644 index 000000000..5105d9dbc --- /dev/null +++ b/examples/asynchronous_telebot/download_file_example.py @@ -0,0 +1,20 @@ + +import telebot +from telebot.async_telebot import AsyncTeleBot + + + +bot = AsyncTeleBot('TOKEN') + + +@bot.message_handler(content_types=['photo']) +async def new_message(message: telebot.types.Message): + result_message = await bot.send_message(message.chat.id, 'Downloading your photo...', parse_mode='HTML', disable_web_page_preview=True) + file_path = await bot.get_file(message.photo[-1].file_id) + downloaded_file = await bot.download_file(file_path.file_path) + with open('file.jpg', 'wb') as new_file: + new_file.write(downloaded_file) + await bot.edit_message_text(chat_id=message.chat.id, message_id=result_message.id, text='Done!', parse_mode='HTML') + + +bot.polling(skip_pending=True) diff --git a/examples/asynchronous_telebot/exception_handler.py b/examples/asynchronous_telebot/exception_handler.py new file mode 100644 index 000000000..f1da60fe5 --- /dev/null +++ b/examples/asynchronous_telebot/exception_handler.py @@ -0,0 +1,27 @@ + +import telebot +from telebot.async_telebot import AsyncTeleBot + + +import logging + +logger = telebot.logger +telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. + +class ExceptionHandler(telebot.ExceptionHandler): + def handle(self, exception): + logger.error(exception) + +bot = AsyncTeleBot('TOKEN',exception_handler=ExceptionHandler()) + + + + +@bot.message_handler(commands=['photo']) +async def photo_send(message: telebot.types.Message): + await bot.send_message(message.chat.id, 'Hi, this is an example of exception handlers.') + raise Exception('test') # Exception goes to ExceptionHandler if it is set + + + +bot.polling(skip_pending=True) diff --git a/examples/asynchronous_telebot/middleware/flooding_middleware.py b/examples/asynchronous_telebot/middleware/flooding_middleware.py new file mode 100644 index 000000000..de70702ac --- /dev/null +++ b/examples/asynchronous_telebot/middleware/flooding_middleware.py @@ -0,0 +1,39 @@ +# Just a little example of middleware handlers + +import telebot +from telebot.asyncio_handler_backends import BaseMiddleware +from telebot.async_telebot import AsyncTeleBot +from telebot.async_telebot import CancelUpdate +bot = AsyncTeleBot('TOKEN') + + +class SimpleMiddleware(BaseMiddleware): + def __init__(self, limit) -> None: + self.last_time = {} + self.limit = limit + self.update_types = ['message'] + # Always specify update types, otherwise middlewares won't work + + + async def pre_process(self, message, data): + if not message.from_user.id in self.last_time: + # User is not in a dict, so lets add and cancel this function + self.last_time[message.from_user.id] = message.date + return + if message.date - self.last_time[message.from_user.id] < self.limit: + # User is flooding + await bot.send_message(message.chat.id, 'You are making request too often') + return CancelUpdate() + self.last_time[message.from_user.id] = message.date + + + async def post_process(self, message, data, exception): + pass + +bot.setup_middleware(SimpleMiddleware(2)) + +@bot.message_handler(commands=['start']) +async def start(message): + await bot.send_message(message.chat.id, 'Hello!') + +bot.polling() \ No newline at end of file diff --git a/examples/asynchronous_telebot/middleware/i18n.py b/examples/asynchronous_telebot/middleware/i18n.py new file mode 100644 index 000000000..3c3196e94 --- /dev/null +++ b/examples/asynchronous_telebot/middleware/i18n.py @@ -0,0 +1,48 @@ +#!/usr/bin/python + +# This example shows how to implement i18n (internationalization) l10n (localization) to create +# multi-language bots with middleware handler. +# +# Also, you could check language code in handler itself too. +# But this example just to show the work of middlewares. + +import telebot +from telebot.async_telebot import AsyncTeleBot +from telebot import asyncio_handler_backends +import logging + +logger = telebot.logger +telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. + +TRANSLATIONS = { + 'hello': { + 'en': 'hello', + 'ru': 'привет', + 'uz': 'salom' + } +} + + + +bot = AsyncTeleBot('TOKEN') + + +class LanguageMiddleware(asyncio_handler_backends.BaseMiddleware): + def __init__(self): + self.update_types = ['message'] # Update types that will be handled by this middleware. + async def pre_process(self, message, data): + data['response'] = TRANSLATIONS['hello'][message.from_user.language_code] + async def post_process(self, message, data, exception): + if exception: # You can get exception occured in handler. + logger.exception(str(exception)) + +bot.setup_middleware(LanguageMiddleware()) # do not forget to setup + +@bot.message_handler(commands=['start']) +async def start(message, data: dict): + # you can get the data in handler too. + # Not necessary to create data parameter in handler function. + await bot.send_message(message.chat.id, data['response']) + + +bot.polling() diff --git a/examples/asynchronous_telebot/send_file_example.py b/examples/asynchronous_telebot/send_file_example.py new file mode 100644 index 000000000..64e304744 --- /dev/null +++ b/examples/asynchronous_telebot/send_file_example.py @@ -0,0 +1,27 @@ + +import telebot +from telebot.async_telebot import AsyncTeleBot + + + +bot = AsyncTeleBot('1297441208:AAH-Z-YbiK_pQ1jTuHXYa-hA_PLZQVQ6qsw') + + +@bot.message_handler(commands=['photo']) +async def photo_send(message: telebot.types.Message): + with open('test.png', 'rb') as new_file: + await bot.send_photo(message.chat.id, new_file) + +@bot.message_handler(commands=['document']) +async def document_send(message: telebot.types.Message): + with open('test.docx', 'rb') as new_file: + await bot.send_document(message.chat.id, new_file) + +@bot.message_handler(commands=['photos']) +async def photos_send(message: telebot.types.Message): + with open('test.png', 'rb') as new_file, open('test2.png', 'rb') as new_file2: + await bot.send_media_group(message.chat.id, [telebot.types.InputMediaPhoto(new_file), telebot.types.InputMediaPhoto(new_file2)]) + + + +bot.polling(skip_pending=True) From 3035763277f9729ec28ef0c41f217a847029afad Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:22:00 +0500 Subject: [PATCH 0770/1808] Update send_file_example.py --- examples/asynchronous_telebot/send_file_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/send_file_example.py b/examples/asynchronous_telebot/send_file_example.py index 64e304744..e67f8d87f 100644 --- a/examples/asynchronous_telebot/send_file_example.py +++ b/examples/asynchronous_telebot/send_file_example.py @@ -4,7 +4,7 @@ -bot = AsyncTeleBot('1297441208:AAH-Z-YbiK_pQ1jTuHXYa-hA_PLZQVQ6qsw') +bot = AsyncTeleBot('TOKEN') @bot.message_handler(commands=['photo']) From 60294d0c4142273455cdb15c51bc4607a7f71634 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:22:44 +0500 Subject: [PATCH 0771/1808] Update README.md --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.md b/README.md index 492a1bfdb..ebd37a72a 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ * [Proxy](#proxy) * [Testing](#testing) * [API conformance](#api-conformance) + * [Asynchronous TeleBot](#asynctelebot) * [F.A.Q.](#faq) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) * [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors) @@ -712,6 +713,52 @@ Result will be: * ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016) +## AsyncTeleBot +### Asynchronous version of telebot +We have a fully asynchronous version of TeleBot. +This class is not controlled by threads. Asyncio tasks are created to execute all the stuff. + +### EchoBot +Echo Bot example on AsyncTeleBot: + +```python +# This is a simple echo bot using the decorator mechanism. +# It echoes any incoming text messages. + +from telebot.async_telebot import AsyncTeleBot +bot = AsyncTeleBot('TOKEN') + + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +async def send_welcome(message): + await bot.reply_to(message, """\ +Hi there, I am EchoBot. +I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\ +""") + + +# Handle all other messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +async def echo_message(message): + await bot.reply_to(message, message.text) + + +bot.polling() +``` +As you can see here, keywords are await and async. + +### Why should I use async? +Asynchronous tasks depend on processor performance. Many asynchronous tasks can run parallelly, while thread tasks will block each other. + +### Differences in AsyncTeleBot +AsyncTeleBot has different middlewares. See example on [middlewares](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) + +### Examples +See more examples in our [examples](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder + + ## F.A.Q. ### How can I distinguish a User and a GroupChat in message.chat? From bbe4a96984dc173a74dec0c8484576e27c937c11 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:23:23 +0500 Subject: [PATCH 0772/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebd37a72a..995abb6c8 100644 --- a/README.md +++ b/README.md @@ -753,7 +753,7 @@ As you can see here, keywords are await and async. Asynchronous tasks depend on processor performance. Many asynchronous tasks can run parallelly, while thread tasks will block each other. ### Differences in AsyncTeleBot -AsyncTeleBot has different middlewares. See example on [middlewares](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) +AsyncTeleBot has different middlewares. See example on [middlewares](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot/middleware) ### Examples See more examples in our [examples](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder From 482589af4935d8425f32379106ec38ddae9123db Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:25:14 +0500 Subject: [PATCH 0773/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 995abb6c8..7e1d4993e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ #

pyTelegramBotAPI

A simple, but extensible Python implementation for the Telegram Bot API. +

Supports both sync and async ways.. ##

Supported Bot API version: 5.4! From f4b9480588ddc44bcc4e862423eb1fe993f9fc69 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:25:47 +0500 Subject: [PATCH 0774/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e1d4993e..d1b13d0cd 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ #

pyTelegramBotAPI

A simple, but extensible Python implementation for the Telegram Bot API. -

Supports both sync and async ways.. +

Supports both sync and async ways. ##

Supported Bot API version: 5.4! From a2822c74ed04375518eb15ec0ae40952d2f7e533 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:34:15 +0500 Subject: [PATCH 0775/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1b13d0cd..ce33584f7 100644 --- a/README.md +++ b/README.md @@ -558,7 +558,7 @@ apihelper.API_URL = "http://localhost:4200/bot{0}/{1}" *Note: 4200 is an example port* ### Asynchronous TeleBot -New: There is an asynchronous implementation of telebot. It is more flexible. +New: There is an asynchronous implementation of telebot. To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot. ```python tb = telebot.AsyncTeleBot("TOKEN") From e2dbb884596572cff2f08ef336cae1bff3f664a3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 4 Dec 2021 19:41:25 +0300 Subject: [PATCH 0776/1808] Readme minor fixed --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d1b13d0cd..7570e81be 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ #

pyTelegramBotAPI

A simple, but extensible Python implementation for the Telegram Bot API. -

Supports both sync and async ways. +Supports both sync and async ways.

##

Supported Bot API version: 5.4! @@ -53,7 +53,7 @@ * [Proxy](#proxy) * [Testing](#testing) * [API conformance](#api-conformance) - * [Asynchronous TeleBot](#asynctelebot) + * [AsyncTeleBot](#asynctelebot) * [F.A.Q.](#faq) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) * [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors) @@ -184,8 +184,8 @@ TeleBot supports the following filters: |content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.| |regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))| |commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.| -|chat_types|list of chat types|`True` if `message.chat.type` in your filter -|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` +|chat_types|list of chat types|`True` if `message.chat.type` in your filter| +|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`| Here are some examples of using the filters and message handlers: @@ -376,8 +376,8 @@ bot.add_custom_filter(IsAdmin()) # Now, you can use it in handler. @bot.message_handler(is_admin=True) def admin_of_group(message): - bot.send_message(message.chat.id, 'You are admin of this group'!) - + bot.send_message(message.chat.id, 'You are admin of this group!') + ``` @@ -558,7 +558,7 @@ apihelper.API_URL = "http://localhost:4200/bot{0}/{1}" *Note: 4200 is an example port* ### Asynchronous TeleBot -New: There is an asynchronous implementation of telebot. It is more flexible. +New: There is an asynchronous implementation of telebot. To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot. ```python tb = telebot.AsyncTeleBot("TOKEN") @@ -572,8 +572,8 @@ tb = telebot.AsyncTeleBot("TOKEN") @tb.message_handler(commands=['start']) async def start_message(message): - await bot.send_message(message.chat.id, 'Hello'!) - + await bot.send_message(message.chat.id, 'Hello!') + ``` See more in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) @@ -769,7 +769,7 @@ Telegram Bot API support new type Chat for message.chat. - ```python if message.chat.type == "private": - # private chat message + # private chat message if message.chat.type == "group": # group chat message From 6cca77f755dcb591801bb503a6cd9590255dfd73 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 4 Dec 2021 19:43:01 +0300 Subject: [PATCH 0777/1808] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7570e81be..d393e3b98 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ #

pyTelegramBotAPI -

A simple, but extensible Python implementation for the Telegram Bot API. -Supports both sync and async ways.

+

A simple, but extensible Python implementation for the Telegram Bot API

. +

Supports both sync and async ways.

-##

Supported Bot API version: 5.4! +##

Supporting Bot API version: 5.4! ## Contents From f224069a34025717eb7501a421941c8804eafd47 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 4 Dec 2021 19:43:33 +0300 Subject: [PATCH 0778/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d393e3b98..5d6290811 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ #

pyTelegramBotAPI -

A simple, but extensible Python implementation for the Telegram Bot API

. +

A simple, but extensible Python implementation for the Telegram Bot API.

Supports both sync and async ways.

##

Supporting Bot API version: 5.4! From fb52137bff4e27bd398f05296a411020855c00be Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:54:26 +0500 Subject: [PATCH 0779/1808] 2 new examples --- .../CallbackData_example.py | 87 +++++++++++++++++++ .../asynchronous_telebot/update_listener.py | 14 +++ 2 files changed, 101 insertions(+) create mode 100644 examples/asynchronous_telebot/CallbackData_example.py create mode 100644 examples/asynchronous_telebot/update_listener.py diff --git a/examples/asynchronous_telebot/CallbackData_example.py b/examples/asynchronous_telebot/CallbackData_example.py new file mode 100644 index 000000000..0386becf1 --- /dev/null +++ b/examples/asynchronous_telebot/CallbackData_example.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you how to use CallbackData +""" + +from telebot.callback_data import CallbackData, CallbackDataFilter +from telebot import types +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_filters import AdvancedCustomFilter + +API_TOKEN = 'TOKEN' +PRODUCTS = [ + {'id': '0', 'name': 'xiaomi mi 10', 'price': 400}, + {'id': '1', 'name': 'samsung s20', 'price': 800}, + {'id': '2', 'name': 'iphone 13', 'price': 1300} +] + +bot = AsyncTeleBot(API_TOKEN) +products_factory = CallbackData('product_id', prefix='products') + + +def products_keyboard(): + return types.InlineKeyboardMarkup( + keyboard=[ + [ + types.InlineKeyboardButton( + text=product['name'], + callback_data=products_factory.new(product_id=product["id"]) + ) + ] + for product in PRODUCTS + ] + ) + + +def back_keyboard(): + return types.InlineKeyboardMarkup( + keyboard=[ + [ + types.InlineKeyboardButton( + text='⬅', + callback_data='back' + ) + ] + ] + ) + + +class ProductsCallbackFilter(AdvancedCustomFilter): + key = 'config' + + async def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +@bot.message_handler(commands=['products']) +async def products_command_handler(message: types.Message): + await bot.send_message(message.chat.id, 'Products:', reply_markup=products_keyboard()) + + +# Only product with field - product_id = 2 +@bot.callback_query_handler(func=None, config=products_factory.filter(product_id='2')) +async def product_one_callback(call: types.CallbackQuery): + await bot.answer_callback_query(callback_query_id=call.id, text='Not available :(', show_alert=True) + + +# Any other products +@bot.callback_query_handler(func=None, config=products_factory.filter()) +async def products_callback(call: types.CallbackQuery): + callback_data: dict = products_factory.parse(callback_data=call.data) + product_id = int(callback_data['product_id']) + product = PRODUCTS[product_id] + + text = f"Product name: {product['name']}\n" \ + f"Product price: {product['price']}" + await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, + text=text, reply_markup=back_keyboard()) + + +@bot.callback_query_handler(func=lambda c: c.data == 'back') +async def back_callback(call: types.CallbackQuery): + await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, + text='Products:', reply_markup=products_keyboard()) + + +bot.add_custom_filter(ProductsCallbackFilter()) +bot.polling() diff --git a/examples/asynchronous_telebot/update_listener.py b/examples/asynchronous_telebot/update_listener.py new file mode 100644 index 000000000..75488cf73 --- /dev/null +++ b/examples/asynchronous_telebot/update_listener.py @@ -0,0 +1,14 @@ +from telebot.async_telebot import AsyncTeleBot + +# Update listeners are functions that are called when any update is received. + +bot = AsyncTeleBot(token='TOKEN') + +async def update_listener(messages): + for message in messages: + if message.text == '/start': + await bot.send_message(message.chat.id, 'Hello!') + +bot.set_update_listener(update_listener) + +bot.polling() \ No newline at end of file From a5ee5f816c7ac32872fc98b5db1af06f55c901aa Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 21:57:16 +0500 Subject: [PATCH 0780/1808] Update README.md --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ce33584f7..5d6290811 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ #

pyTelegramBotAPI -

A simple, but extensible Python implementation for the Telegram Bot API. -

Supports both sync and async ways. +

A simple, but extensible Python implementation for the Telegram Bot API.

+

Supports both sync and async ways.

-##

Supported Bot API version: 5.4! +##

Supporting Bot API version: 5.4! ## Contents @@ -53,7 +53,7 @@ * [Proxy](#proxy) * [Testing](#testing) * [API conformance](#api-conformance) - * [Asynchronous TeleBot](#asynctelebot) + * [AsyncTeleBot](#asynctelebot) * [F.A.Q.](#faq) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) * [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors) @@ -184,8 +184,8 @@ TeleBot supports the following filters: |content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.| |regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))| |commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.| -|chat_types|list of chat types|`True` if `message.chat.type` in your filter -|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True` +|chat_types|list of chat types|`True` if `message.chat.type` in your filter| +|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`| Here are some examples of using the filters and message handlers: @@ -376,8 +376,8 @@ bot.add_custom_filter(IsAdmin()) # Now, you can use it in handler. @bot.message_handler(is_admin=True) def admin_of_group(message): - bot.send_message(message.chat.id, 'You are admin of this group'!) - + bot.send_message(message.chat.id, 'You are admin of this group!') + ``` @@ -572,8 +572,8 @@ tb = telebot.AsyncTeleBot("TOKEN") @tb.message_handler(commands=['start']) async def start_message(message): - await bot.send_message(message.chat.id, 'Hello'!) - + await bot.send_message(message.chat.id, 'Hello!') + ``` See more in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) @@ -769,7 +769,7 @@ Telegram Bot API support new type Chat for message.chat. - ```python if message.chat.type == "private": - # private chat message + # private chat message if message.chat.type == "group": # group chat message From 4f198bc6f59c5545a1515e470eb343b004c26322 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Dec 2021 22:03:14 +0500 Subject: [PATCH 0781/1808] Forgot to update file --- telebot/async_telebot.py | 33 ++++++++++++++++++--------- telebot/asyncio_helper.py | 47 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 9f24d90b7..e7221bcf6 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -97,16 +97,15 @@ class AsyncTeleBot: def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, - exception_handler=None,suppress_middleware_excepions=False) -> None: # TODO: ADD TYPEHINTS + exception_handler=None) -> None: # TODO: ADD TYPEHINTS self.token = token self.offset = offset self.token = token self.parse_mode = parse_mode self.update_listener = [] - self.suppress_middleware_excepions = suppress_middleware_excepions - self.exc_info = None + self.exception_handler = exception_handler @@ -234,13 +233,23 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: try: updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout, request_timeout=request_timeout) + if updates: + self.offset = updates[-1].update_id + 1 + self._loop_create_task(self.process_new_updates(updates)) # Seperate task for processing updates + if interval: await asyncio.sleep(interval) + + except KeyboardInterrupt: + return except asyncio.CancelledError: return except asyncio_helper.ApiTelegramException as e: logger.error(str(e)) - continue + if non_stop: + continue + else: + break except Exception as e: logger.error('Cause exception while getting updates.') if non_stop: @@ -249,10 +258,6 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: continue else: raise e - if updates: - self.offset = updates[-1].update_id + 1 - self._loop_create_task(self.process_new_updates(updates)) # Seperate task for processing updates - if interval: await asyncio.sleep(interval) finally: self._polling = False @@ -297,7 +302,12 @@ async def _run_middlewares_and_handlers(self, handlers, message, middleware): break except Exception as e: handler_error = e - logger.info(str(e)) + + if not middleware: + if self.exception_handler: + return self.exception_handler.handle(e) + logging.error(str(e)) + return if middleware: await middleware.post_process(message, data, handler_error) @@ -448,7 +458,7 @@ async def __notify_update(self, new_messages): if len(self.update_listener) == 0: return for listener in self.update_listener: - self._loop_create_task(listener, new_messages) + self._loop_create_task(listener(new_messages)) async def _test_message_handler(self, message_handler, message): """ @@ -466,6 +476,9 @@ async def _test_message_handler(self, message_handler, message): return True + def set_update_listener(self, func): + self.update_listener.append(func) + def add_custom_filter(self, custom_filter): """ Create custom filter. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3d1189d66..3a765de54 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -8,7 +8,7 @@ import ujson as json except ImportError: import json - +import os API_URL = 'https://api.telegram.org/bot{0}/{1}' from datetime import datetime @@ -42,14 +42,55 @@ async def _get_new_session(self): MAX_RETRIES = 15 async def _process_request(token, url, method='get', params=None, files=None, request_timeout=None): + params = compose_data(params, files) async with await session_manager._get_new_session() as session: - async with session.get(API_URL.format(token, url), params=params, data=files, timeout=request_timeout) as response: + async with session.request(method=method, url=API_URL.format(token, url), data=params, timeout=request_timeout) as response: logger.debug("Request: method={0} url={1} params={2} files={3} request_timeout={4}".format(method, url, params, files, request_timeout).replace(token, token.split(':')[0] + ":{TOKEN}")) json_result = await _check_result(url, response) if json_result: return json_result['result'] +def guess_filename(obj): + """ + Get file name from object + + :param obj: + :return: + """ + name = getattr(obj, 'name', None) + if name and isinstance(name, str) and name[0] != '<' and name[-1] != '>': + return os.path.basename(name) + + +def compose_data(params=None, files=None): + """ + Prepare request data + + :param params: + :param files: + :return: + """ + data = aiohttp.formdata.FormData(quote_fields=False) + + if params: + for key, value in params.items(): + data.add_field(key, str(value)) + + if files: + for key, f in files.items(): + if isinstance(f, tuple): + if len(f) == 2: + filename, fileobj = f + else: + raise ValueError('Tuple must have exactly 2 elements: filename, fileobj') + else: + filename, fileobj = guess_filename(f) or key, f + + data.add_field(key, fileobj, filename=filename) + + return data + async def _convert_markup(markup): if isinstance(markup, types.JsonSerializable): return markup.to_json() @@ -731,7 +772,7 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None): - method_url = get_method_by_type(data_type) + method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None if not util.is_string(data): From fbf34f59533fb2175c052718a6b4b0aa10e2b381 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 4 Dec 2021 20:25:39 +0300 Subject: [PATCH 0782/1808] Bump version to 4.2.1 - AsyncTeleBot alpha --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 24f8550f0..9011d0c13 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.2.0' +__version__ = '4.2.1' From 038be81db360357567c0718aad4294d5f9bf83c4 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 7 Dec 2021 22:17:51 +0500 Subject: [PATCH 0783/1808] 5.5 --- telebot/__init__.py | 35 +++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 14 ++++++++++++++ telebot/async_telebot.py | 34 ++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 13 +++++++++++++ telebot/types.py | 17 +++++++++++++---- 5 files changed, 109 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6f29f86de..f30a9db77 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1667,6 +1667,41 @@ def set_chat_administrator_custom_title( """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) + + def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str], + until_date:Optional[Union[int, datetime]]=None) -> bool: + """ + Use this method to ban a channel chat in a supergroup or a channel. + The owner of the chat will not be able to send messages and join live + streams on behalf of the chat, unless it is unbanned first. + The bot must be an administrator in the supergroup or channel + for this to work and must have the appropriate administrator rights. + Returns True on success. + + :params: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param sender_chat_id: Unique identifier of the target sender chat + :param until_date: Date when the sender chat will be unbanned, unix time. If the chat is banned for more than 366 days + or less than 30 seconds from the current time they are considered to be banned forever. + :return: True on success. + """ + return apihelper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id, until_date) + + def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: + """ + Use this method to unban a previously banned channel chat in a supergroup or channel. + The bot must be an administrator for this to work and must have the appropriate + administrator rights. + Returns True on success. + + :params: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param sender_chat_id: Unique identifier of the target sender chat + :return: True on success. + """ + return apihelper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) + + def set_chat_permissions( self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0ca982ef3..6a38b67e5 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -969,6 +969,20 @@ def set_chat_administrator_custom_title(token, chat_id, user_id, custom_title): return _make_request(token, method_url, params=payload, method='post') +def ban_chat_sender_chat(token, chat_id, sender_chat_id, until_date=None): + method_url = 'banChatSenderChat' + payload = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} + if until_date: + payload['until_date'] = until_date + return _make_request(token, method_url, params=payload, method='post') + + +def unban_chat_sender_chat(token, chat_id, sender_chat_id): + method_url = 'unbanChatSenderChat' + payload = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} + return _make_request(token, method_url, params=payload, method='post') + + def set_chat_permissions(token, chat_id, permissions): method_url = 'setChatPermissions' payload = { diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e7221bcf6..176d2b9fa 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2135,6 +2135,40 @@ async def set_chat_administrator_custom_title( """ return await asyncio_helper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) + + async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str], + until_date:Optional[Union[int, datetime]]=None) -> bool: + """ + Use this method to ban a channel chat in a supergroup or a channel. + The owner of the chat will not be able to send messages and join live + streams on behalf of the chat, unless it is unbanned first. + The bot must be an administrator in the supergroup or channel + for this to work and must have the appropriate administrator rights. + Returns True on success. + + :params: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param sender_chat_id: Unique identifier of the target sender chat + :param until_date: Date when the sender chat will be unbanned, unix time. If the chat is banned for more than 366 days + or less than 30 seconds from the current time they are considered to be banned forever. + :return: True on success. + """ + return await asyncio_helper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id, until_date) + + async def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: + """ + Use this method to unban a previously banned channel chat in a supergroup or channel. + The bot must be an administrator for this to work and must have the appropriate + administrator rights. + Returns True on success. + + :params: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param sender_chat_id: Unique identifier of the target sender chat + :return: True on success. + """ + return await asyncio_helper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) + async def set_chat_permissions( self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3a765de54..1fcf47fd9 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -912,6 +912,19 @@ async def set_chat_administrator_custom_title(token, chat_id, user_id, custom_ti return await _process_request(token, method_url, params=payload, method='post') +async def ban_chat_sender_chat(token, chat_id, sender_chat_id, until_date=None): + method_url = 'banChatSenderChat' + payload = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} + if until_date: + payload['until_date'] = until_date + return await _process_request(token, method_url, params=payload, method='post') + + +async def unban_chat_sender_chat(token, chat_id, sender_chat_id): + method_url = 'unbanChatSenderChat' + payload = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} + return await _process_request(token, method_url, params=payload, method='post') + async def set_chat_permissions(token, chat_id, permissions): method_url = 'setChatPermissions' payload = { diff --git a/telebot/types.py b/telebot/types.py index 263b327e6..eb3fabffd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -274,10 +274,11 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, - last_name=None, photo=None, bio=None, description=None, invite_link=None, - pinned_message=None, permissions=None, slow_mode_delay=None, - message_auto_delete_time=None, sticker_set_name=None, can_set_sticker_set=None, - linked_chat_id=None, location=None, **kwargs): + last_name=None, photo=None, bio=None, has_private_forwards=None, + description=None, invite_link=None, pinned_message=None, + permissions=None, slow_mode_delay=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, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -286,12 +287,14 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.last_name: str = last_name self.photo: ChatPhoto = photo self.bio: str = bio + self.has_private_forwards: bool = has_private_forwards self.description: str = description self.invite_link: str = invite_link self.pinned_message: Message = pinned_message self.permissions: ChatPermissions = permissions self.slow_mode_delay: int = slow_mode_delay self.message_auto_delete_time: int = message_auto_delete_time + self.has_protected_content: bool = has_protected_content self.sticker_set_name: str = sticker_set_name self.can_set_sticker_set: bool = can_set_sticker_set self.linked_chat_id: int = linked_chat_id @@ -334,12 +337,16 @@ def de_json(cls, json_string): opts['forward_sender_name'] = obj.get('forward_sender_name') if 'forward_date' in obj: opts['forward_date'] = obj.get('forward_date') + if 'is_automatic_forward' in obj: + opts['is_automatic_forward'] = obj.get('is_automatic_forward') if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) if 'via_bot' in obj: opts['via_bot'] = User.de_json(obj['via_bot']) if 'edit_date' in obj: opts['edit_date'] = obj.get('edit_date') + if 'has_protected_content' in obj: + opts['has_protected_content'] = obj.get('has_protected_content') if 'media_group_id' in obj: opts['media_group_id'] = obj.get('media_group_id') if 'author_signature' in obj: @@ -503,9 +510,11 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.forward_signature: Optional[str] = None self.forward_sender_name: Optional[str] = None self.forward_date: Optional[int] = None + self.is_automatic_forward: Optional[bool] = None self.reply_to_message: Optional[Message] = None self.via_bot: Optional[User] = None self.edit_date: Optional[int] = None + self.has_protected_content: Optional[bool] = None self.media_group_id: Optional[str] = None self.author_signature: Optional[str] = None self.text: Optional[str] = None From 5a03ab62d069546e3a17726063d39d7f4278afb8 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 7 Dec 2021 22:27:19 +0500 Subject: [PATCH 0784/1808] Update test_types.py --- tests/test_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_types.py b/tests/test_types.py index 7f9b32fed..4a580ebb3 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -50,7 +50,7 @@ def test_json_message_with_dice(): def test_json_message_group(): - json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG","is_bot":true},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}' + json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG","is_bot":true},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI","has_protected_content":true}' msg = types.Message.de_json(json_string) assert msg.text == 'HIHI' assert len(msg.chat.title) != 0 From 555257a3fe3309fadb1508f4b91e6d9105ec0392 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 8 Dec 2021 14:00:39 +0500 Subject: [PATCH 0785/1808] Documentation Bug fixed --- telebot/__init__.py | 5 ++--- telebot/apihelper.py | 4 +--- telebot/async_telebot.py | 5 ++--- telebot/asyncio_helper.py | 4 +--- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f30a9db77..d1ff2c2c2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1668,8 +1668,7 @@ def set_chat_administrator_custom_title( return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str], - until_date:Optional[Union[int, datetime]]=None) -> bool: + def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ Use this method to ban a channel chat in a supergroup or a channel. The owner of the chat will not be able to send messages and join live @@ -1685,7 +1684,7 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i or less than 30 seconds from the current time they are considered to be banned forever. :return: True on success. """ - return apihelper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id, until_date) + return apihelper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id) def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6a38b67e5..229fa7856 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -969,11 +969,9 @@ def set_chat_administrator_custom_title(token, chat_id, user_id, custom_title): return _make_request(token, method_url, params=payload, method='post') -def ban_chat_sender_chat(token, chat_id, sender_chat_id, until_date=None): +def ban_chat_sender_chat(token, chat_id, sender_chat_id): method_url = 'banChatSenderChat' payload = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} - if until_date: - payload['until_date'] = until_date return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 176d2b9fa..782ba6d22 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2136,8 +2136,7 @@ async def set_chat_administrator_custom_title( return await asyncio_helper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str], - until_date:Optional[Union[int, datetime]]=None) -> bool: + async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ Use this method to ban a channel chat in a supergroup or a channel. The owner of the chat will not be able to send messages and join live @@ -2153,7 +2152,7 @@ async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: U or less than 30 seconds from the current time they are considered to be banned forever. :return: True on success. """ - return await asyncio_helper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id, until_date) + return await asyncio_helper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id) async def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 1fcf47fd9..0360b0950 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -912,11 +912,9 @@ async def set_chat_administrator_custom_title(token, chat_id, user_id, custom_ti return await _process_request(token, method_url, params=payload, method='post') -async def ban_chat_sender_chat(token, chat_id, sender_chat_id, until_date=None): +async def ban_chat_sender_chat(token, chat_id, sender_chat_id): method_url = 'banChatSenderChat' payload = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} - if until_date: - payload['until_date'] = until_date return await _process_request(token, method_url, params=payload, method='post') From 08fc32b70a523e98aa93029cfe01700ec4906e92 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 8 Dec 2021 14:13:39 +0500 Subject: [PATCH 0786/1808] Comment fix --- telebot/__init__.py | 51 ++++++++++++--------------------- telebot/async_telebot.py | 61 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 34 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d1ff2c2c2..acea58b6b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -75,74 +75,59 @@ class TeleBot: logOut close sendMessage + Formatting options forwardMessage copyMessage - deleteMessage sendPhoto sendAudio sendDocument - sendSticker sendVideo - sendVenue sendAnimation + sendVoice sendVideoNote + sendMediaGroup sendLocation - sendChatAction - sendDice + editMessageLiveLocation + stopMessageLiveLocation + sendVenue sendContact - sendInvoice - sendMediaGroup + sendPoll + sendDice + sendChatAction getUserProfilePhotos - getUpdates getFile - sendPoll - stopPoll - sendGame - setGameScore - getGameHighScores - editMessageText - editMessageCaption - editMessageMedia - editMessageReplyMarkup - editMessageLiveLocation - stopMessageLiveLocation banChatMember unbanChatMember restrictChatMember promoteChatMember setChatAdministratorCustomTitle + banChatSenderChat + unbanChatSenderChat setChatPermissions + exportChatInviteLink createChatInviteLink editChatInviteLink revokeChatInviteLink - exportChatInviteLink - setChatStickerSet - deleteChatStickerSet - createNewStickerSet - addStickerToSet - deleteStickerFromSet - setStickerPositionInSet - uploadStickerFile - setStickerSetThumb - getStickerSet + approveChatJoinRequest + declineChatJoinRequest setChatPhoto deleteChatPhoto setChatTitle setChatDescription pinChatMessage unpinChatMessage + unpinAllChatMessages leaveChat getChat getChatAdministrators getChatMemberCount getChatMember + setChatStickerSet + deleteChatStickerSet answerCallbackQuery - getMyCommands setMyCommands deleteMyCommands - answerInlineQuery - answerShippingQuery - answerPreCheckoutQuery + getMyCommands """ def __init__( diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 782ba6d22..a467bfd44 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -94,7 +94,66 @@ def __init__(self) -> None: pass class AsyncTeleBot: - + """ This is AsyncTeleBot Class + Methods: + getMe + logOut + close + sendMessage + Formatting options + forwardMessage + copyMessage + sendPhoto + sendAudio + sendDocument + sendVideo + sendAnimation + sendVoice + sendVideoNote + sendMediaGroup + sendLocation + editMessageLiveLocation + stopMessageLiveLocation + sendVenue + sendContact + sendPoll + sendDice + sendChatAction + getUserProfilePhotos + getFile + banChatMember + unbanChatMember + restrictChatMember + promoteChatMember + setChatAdministratorCustomTitle + banChatSenderChat + unbanChatSenderChat + setChatPermissions + exportChatInviteLink + createChatInviteLink + editChatInviteLink + revokeChatInviteLink + approveChatJoinRequest + declineChatJoinRequest + setChatPhoto + deleteChatPhoto + setChatTitle + setChatDescription + pinChatMessage + unpinChatMessage + unpinAllChatMessages + leaveChat + getChat + getChatAdministrators + getChatMemberCount + getChatMember + setChatStickerSet + deleteChatStickerSet + answerCallbackQuery + setMyCommands + deleteMyCommands + getMyCommands + """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, exception_handler=None) -> None: # TODO: ADD TYPEHINTS From 311eec68880e7cbf1dc9288f027c6ae3a525da7f Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 8 Dec 2021 14:15:40 +0500 Subject: [PATCH 0787/1808] fix --- telebot/__init__.py | 51 ++++++++++++++++++++++++++-------------- telebot/async_telebot.py | 51 ++++++++++++++++++++++++++-------------- 2 files changed, 66 insertions(+), 36 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index acea58b6b..d1ff2c2c2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -75,59 +75,74 @@ class TeleBot: logOut close sendMessage - Formatting options forwardMessage copyMessage + deleteMessage sendPhoto sendAudio sendDocument + sendSticker sendVideo + sendVenue sendAnimation - sendVoice sendVideoNote - sendMediaGroup sendLocation - editMessageLiveLocation - stopMessageLiveLocation - sendVenue - sendContact - sendPoll - sendDice sendChatAction + sendDice + sendContact + sendInvoice + sendMediaGroup getUserProfilePhotos + getUpdates getFile + sendPoll + stopPoll + sendGame + setGameScore + getGameHighScores + editMessageText + editMessageCaption + editMessageMedia + editMessageReplyMarkup + editMessageLiveLocation + stopMessageLiveLocation banChatMember unbanChatMember restrictChatMember promoteChatMember setChatAdministratorCustomTitle - banChatSenderChat - unbanChatSenderChat setChatPermissions - exportChatInviteLink createChatInviteLink editChatInviteLink revokeChatInviteLink - approveChatJoinRequest - declineChatJoinRequest + exportChatInviteLink + setChatStickerSet + deleteChatStickerSet + createNewStickerSet + addStickerToSet + deleteStickerFromSet + setStickerPositionInSet + uploadStickerFile + setStickerSetThumb + getStickerSet setChatPhoto deleteChatPhoto setChatTitle setChatDescription pinChatMessage unpinChatMessage - unpinAllChatMessages leaveChat getChat getChatAdministrators getChatMemberCount getChatMember - setChatStickerSet - deleteChatStickerSet answerCallbackQuery + getMyCommands setMyCommands deleteMyCommands - getMyCommands + answerInlineQuery + answerShippingQuery + answerPreCheckoutQuery """ def __init__( diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a467bfd44..4813f7c04 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -100,59 +100,74 @@ class AsyncTeleBot: logOut close sendMessage - Formatting options forwardMessage copyMessage + deleteMessage sendPhoto sendAudio sendDocument + sendSticker sendVideo + sendVenue sendAnimation - sendVoice sendVideoNote - sendMediaGroup sendLocation - editMessageLiveLocation - stopMessageLiveLocation - sendVenue - sendContact - sendPoll - sendDice sendChatAction + sendDice + sendContact + sendInvoice + sendMediaGroup getUserProfilePhotos + getUpdates getFile + sendPoll + stopPoll + sendGame + setGameScore + getGameHighScores + editMessageText + editMessageCaption + editMessageMedia + editMessageReplyMarkup + editMessageLiveLocation + stopMessageLiveLocation banChatMember unbanChatMember restrictChatMember promoteChatMember setChatAdministratorCustomTitle - banChatSenderChat - unbanChatSenderChat setChatPermissions - exportChatInviteLink createChatInviteLink editChatInviteLink revokeChatInviteLink - approveChatJoinRequest - declineChatJoinRequest + exportChatInviteLink + setChatStickerSet + deleteChatStickerSet + createNewStickerSet + addStickerToSet + deleteStickerFromSet + setStickerPositionInSet + uploadStickerFile + setStickerSetThumb + getStickerSet setChatPhoto deleteChatPhoto setChatTitle setChatDescription pinChatMessage unpinChatMessage - unpinAllChatMessages leaveChat getChat getChatAdministrators getChatMemberCount getChatMember - setChatStickerSet - deleteChatStickerSet answerCallbackQuery + getMyCommands setMyCommands deleteMyCommands - getMyCommands + answerInlineQuery + answerShippingQuery + answerPreCheckoutQuery """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, From bb19687854c583c36a6fc0e067be65e93a47673f Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 8 Dec 2021 15:15:57 +0500 Subject: [PATCH 0788/1808] fix --- telebot/__init__.py | 2 -- telebot/async_telebot.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d1ff2c2c2..7104ebbb1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1680,8 +1680,6 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i :params: :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param sender_chat_id: Unique identifier of the target sender chat - :param until_date: Date when the sender chat will be unbanned, unix time. If the chat is banned for more than 366 days - or less than 30 seconds from the current time they are considered to be banned forever. :return: True on success. """ return apihelper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4813f7c04..3b8317d24 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2222,8 +2222,6 @@ async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: U :params: :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param sender_chat_id: Unique identifier of the target sender chat - :param until_date: Date when the sender chat will be unbanned, unix time. If the chat is banned for more than 366 days - or less than 30 seconds from the current time they are considered to be banned forever. :return: True on success. """ return await asyncio_helper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id) From 751deeafd716bf5dc4ad0f22fd5dc370cd4bfe3f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 8 Dec 2021 23:44:57 +0300 Subject: [PATCH 0789/1808] Bump version to 4.2.2 --- .travis.yml | 1 - README.md | 17 +++-------------- telebot/version.py | 2 +- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36dbf89e5..c4bd8afe9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "3.6" - "3.7" - "3.8" - "3.9" diff --git a/README.md b/README.md index 5d6290811..b751585b0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Supports both sync and async ways.

-##

Supporting Bot API version: 5.4! +##

Supporting Bot API version: 5.5! ## Contents @@ -685,8 +685,9 @@ Result will be: ## API conformance +* ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021) * ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021) -* ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMemberXXX classes are full copies of ChatMember +* ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMember* classes are full copies of ChatMember * ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021) * ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021) * ✔ [Bot API 5.0](https://core.telegram.org/bots/api-changelog#november-4-2020) @@ -700,18 +701,6 @@ Result will be: * ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019) * ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support * ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support -* ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018) -* ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) -* ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017) -* ✔ [Bot API 3.3](https://core.telegram.org/bots/api-changelog#august-23-2017) -* ✔ [Bot API 3.2](https://core.telegram.org/bots/api-changelog#july-21-2017) -* ✔ [Bot API 3.1](https://core.telegram.org/bots/api-changelog#june-30-2017) -* ✔ [Bot API 3.0](https://core.telegram.org/bots/api-changelog#may-18-2017) -* ✔ [Bot API 2.3.1](https://core.telegram.org/bots/api-changelog#december-4-2016) -* ✔ [Bot API 2.3](https://core.telegram.org/bots/api-changelog#november-21-2016) -* ✔ [Bot API 2.2](https://core.telegram.org/bots/api-changelog#october-3-2016) -* ✔ [Bot API 2.1](https://core.telegram.org/bots/api-changelog#may-22-2016) -* ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016) ## AsyncTeleBot diff --git a/telebot/version.py b/telebot/version.py index 9011d0c13..468312f60 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.2.1' +__version__ = '4.2.2' From e76649bb49ea493cecc01adcf4127b2b8959ae1e Mon Sep 17 00:00:00 2001 From: zeph1997 Date: Sat, 11 Dec 2021 02:11:15 +0800 Subject: [PATCH 0790/1808] Create reply_keyboard_markup_example.py Example to show how to use ReplyKeyboardMarkup as well as a template for a QWERTY keyboard --- examples/reply_keyboard_markup_example.py | 63 +++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 examples/reply_keyboard_markup_example.py diff --git a/examples/reply_keyboard_markup_example.py b/examples/reply_keyboard_markup_example.py new file mode 100644 index 000000000..425f3ec5f --- /dev/null +++ b/examples/reply_keyboard_markup_example.py @@ -0,0 +1,63 @@ +# This example shows you how to create a custom QWERTY keyboard using reply keyboard markup +import telebot +from telebot.types import ReplyKeyboardMarkup, KeyboardButton + +TOKEN = "" +bot = telebot.TeleBot(TOKEN) + +keys = ["1","2","3","4","5","6","7","8","9","0","q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m"] +symbols = ["1","2","3","4","5","6","7","8","9","0","!","@","#","$","%","^","&","*","(",")","\'","\"","/","\\",",",".",";",":"] + +def keyboard(key_type="Normal"): + markup = ReplyKeyboardMarkup(row_width=10) + if key_type == "Normal": + row = [KeyboardButton(x) for x in keys[:10]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + row = [KeyboardButton(x) for x in keys[10:20]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + row = [KeyboardButton(x) for x in keys[20:29]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8]) + row = [KeyboardButton(x) for x in keys[29:]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6]) + markup.add(KeyboardButton("Caps Lock"),KeyboardButton("Symbols"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done")) + elif key_type == "Symbols": + row = [KeyboardButton(x) for x in symbols[:10]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + row = [KeyboardButton(x) for x in symbols[10:20]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + row = [KeyboardButton(x) for x in symbols[20:]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7]) + markup.add(KeyboardButton("Caps Lock"),KeyboardButton("Normal"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done")) + else: + row = [KeyboardButton(x.upper()) for x in keys[:10]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + row = [KeyboardButton(x.upper()) for x in keys[10:20]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + row = [KeyboardButton(x.upper()) for x in keys[20:29]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8]) + row = [KeyboardButton(x.upper()) for x in keys[29:]] + markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6]) + markup.add(KeyboardButton("Normal"),KeyboardButton("Symbols"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done")) + return markup + +@bot.message_handler(commands=["start"]) +def start_message(message): + bot.send_message(message.chat.id,"You can use the keyboard",reply_markup=keyboard()) + +@bot.message_handler(func=lambda message:True) +def all_messages(message): + if message.text == "✅Done": + markup = telebot.types.ReplyKeyboardRemove() + bot.send_message(message.from_user.id,"Done with Keyboard",reply_markup=markup) + elif message.text == "Symbols": + bot.send_message(message.from_user.id,"Special characters",reply_markup=keyboard("Symbols")) + elif message.text == "Normal": + bot.send_message(message.from_user.id,"Normal Keyboard",reply_markup=keyboard("Normal")) + elif message.text == "Caps Lock": + bot.send_message(message.from_user.id,"Caps Lock",reply_markup=keyboard("Caps")) + elif message.text == "🔙Delete": + bot.delete_message(message.from_user.id,message.message_id) + else: + bot.send_message(message.chat.id,message.text) + +bot.polling() From 5a06d8021b9940bef478d618c092b830fe706fa9 Mon Sep 17 00:00:00 2001 From: zeph1997 Date: Sat, 11 Dec 2021 02:28:19 +0800 Subject: [PATCH 0791/1808] changed markup.add row from hardcoded index to *row --- examples/reply_keyboard_markup_example.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/reply_keyboard_markup_example.py b/examples/reply_keyboard_markup_example.py index 425f3ec5f..7808d7d83 100644 --- a/examples/reply_keyboard_markup_example.py +++ b/examples/reply_keyboard_markup_example.py @@ -12,31 +12,31 @@ def keyboard(key_type="Normal"): markup = ReplyKeyboardMarkup(row_width=10) if key_type == "Normal": row = [KeyboardButton(x) for x in keys[:10]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + markup.add(*row) row = [KeyboardButton(x) for x in keys[10:20]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + markup.add(*row) row = [KeyboardButton(x) for x in keys[20:29]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8]) + markup.add(*row) row = [KeyboardButton(x) for x in keys[29:]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6]) + markup.add(*row) markup.add(KeyboardButton("Caps Lock"),KeyboardButton("Symbols"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done")) elif key_type == "Symbols": row = [KeyboardButton(x) for x in symbols[:10]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + markup.add(*row) row = [KeyboardButton(x) for x in symbols[10:20]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + markup.add(*row) row = [KeyboardButton(x) for x in symbols[20:]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7]) + markup.add(*row) markup.add(KeyboardButton("Caps Lock"),KeyboardButton("Normal"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done")) else: row = [KeyboardButton(x.upper()) for x in keys[:10]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + markup.add(*row) row = [KeyboardButton(x.upper()) for x in keys[10:20]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9]) + markup.add(*row) row = [KeyboardButton(x.upper()) for x in keys[20:29]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8]) + markup.add(*row) row = [KeyboardButton(x.upper()) for x in keys[29:]] - markup.add(row[0],row[1],row[2],row[3],row[4],row[5],row[6]) + markup.add(*row) markup.add(KeyboardButton("Normal"),KeyboardButton("Symbols"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done")) return markup From 7588c9fb9fd96fddd8b762ecc4847b76f78229d6 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 Dec 2021 21:32:57 +0300 Subject: [PATCH 0792/1808] infinity_polling --- examples/reply_keyboard_markup_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/reply_keyboard_markup_example.py b/examples/reply_keyboard_markup_example.py index 7808d7d83..a4e33cb36 100644 --- a/examples/reply_keyboard_markup_example.py +++ b/examples/reply_keyboard_markup_example.py @@ -60,4 +60,4 @@ def all_messages(message): else: bot.send_message(message.chat.id,message.text) -bot.polling() +bot.infinity_polling() From e92946301f3da31b474604e5dc4b0f191218844c Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Dec 2021 15:07:30 +0500 Subject: [PATCH 0793/1808] Asyncio.run back --- .../asynchronous_telebot/CallbackData_example.py | 3 ++- examples/asynchronous_telebot/chat_join_request.py | 3 ++- .../asynchronous_telebot/chat_member_example.py | 3 ++- .../custom_filters/admin_filter_example.py | 6 ++++-- .../custom_filters/general_custom_filters.py | 3 ++- .../custom_filters/id_filter_example.py | 3 ++- .../custom_filters/is_filter_example.py | 3 ++- .../custom_filters/text_filter_example.py | 3 ++- examples/asynchronous_telebot/custom_states.py | 3 ++- .../asynchronous_telebot/download_file_example.py | 3 ++- examples/asynchronous_telebot/echo_bot.py | 3 ++- examples/asynchronous_telebot/exception_handler.py | 3 ++- .../middleware/flooding_middleware.py | 3 ++- examples/asynchronous_telebot/middleware/i18n.py | 3 ++- examples/asynchronous_telebot/register_handler.py | 4 +++- examples/asynchronous_telebot/send_file_example.py | 4 +++- .../asynchronous_telebot/skip_updates_example.py | 4 +++- examples/asynchronous_telebot/update_listener.py | 4 +++- telebot/async_telebot.py | 14 +++++++------- 19 files changed, 49 insertions(+), 26 deletions(-) diff --git a/examples/asynchronous_telebot/CallbackData_example.py b/examples/asynchronous_telebot/CallbackData_example.py index 0386becf1..fad37c173 100644 --- a/examples/asynchronous_telebot/CallbackData_example.py +++ b/examples/asynchronous_telebot/CallbackData_example.py @@ -84,4 +84,5 @@ async def back_callback(call: types.CallbackQuery): bot.add_custom_filter(ProductsCallbackFilter()) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/chat_join_request.py b/examples/asynchronous_telebot/chat_join_request.py index 5262ebdd1..85bf8c35c 100644 --- a/examples/asynchronous_telebot/chat_join_request.py +++ b/examples/asynchronous_telebot/chat_join_request.py @@ -8,4 +8,5 @@ async def make_some(message: telebot.types.ChatJoinRequest): await bot.send_message(message.chat.id, 'I accepted a new user!') await bot.approve_chat_join_request(message.chat.id, message.from_user.id) -bot.polling(skip_pending=True) \ No newline at end of file +import asyncio +asyncio.run(bot.polling()) \ No newline at end of file diff --git a/examples/asynchronous_telebot/chat_member_example.py b/examples/asynchronous_telebot/chat_member_example.py index 4d90036af..30f47e279 100644 --- a/examples/asynchronous_telebot/chat_member_example.py +++ b/examples/asynchronous_telebot/chat_member_example.py @@ -30,4 +30,5 @@ async def my_chat_m(message: types.ChatMemberUpdated): @bot.message_handler(content_types=util.content_type_service) async def delall(message: types.Message): await bot.delete_message(message.chat.id,message.message_id) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/admin_filter_example.py b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py index 5a508c45d..8d3b2b59d 100644 --- a/examples/asynchronous_telebot/custom_filters/admin_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py @@ -1,6 +1,6 @@ from telebot.async_telebot import AsyncTeleBot from telebot import asyncio_filters -bot = AsyncTeleBot('TOKEN') +bot = AsyncTeleBot('1297441208:AAGiez5xhzai5russPtPKmZjbdjybW4T0U8') # Handler @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) @@ -9,4 +9,6 @@ async def answer_for_admin(message): # Register filter bot.add_custom_filter(asyncio_filters.IsAdminFilter(bot)) -bot.polling() + +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/general_custom_filters.py b/examples/asynchronous_telebot/custom_filters/general_custom_filters.py index 1b36beb31..dfeeb880c 100644 --- a/examples/asynchronous_telebot/custom_filters/general_custom_filters.py +++ b/examples/asynchronous_telebot/custom_filters/general_custom_filters.py @@ -40,4 +40,5 @@ async def bye_user(message): bot.add_custom_filter(MainFilter()) bot.add_custom_filter(IsAdmin()) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/id_filter_example.py b/examples/asynchronous_telebot/custom_filters/id_filter_example.py index 5a0796302..85e909b19 100644 --- a/examples/asynchronous_telebot/custom_filters/id_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/id_filter_example.py @@ -14,4 +14,5 @@ async def not_admin(message): # Do not forget to register bot.add_custom_filter(telebot.asyncio_filters.ChatFilter()) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/is_filter_example.py b/examples/asynchronous_telebot/custom_filters/is_filter_example.py index 961fd0fb4..46a798832 100644 --- a/examples/asynchronous_telebot/custom_filters/is_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/is_filter_example.py @@ -19,4 +19,5 @@ async def text_filter(message): bot.add_custom_filter(telebot.asyncio_filters.IsReplyFilter()) bot.add_custom_filter(telebot.asyncio_filters.ForwardFilter()) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_filters/text_filter_example.py b/examples/asynchronous_telebot/custom_filters/text_filter_example.py index 84aaee931..e2b756405 100644 --- a/examples/asynchronous_telebot/custom_filters/text_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/text_filter_example.py @@ -17,4 +17,5 @@ async def text_filter(message): bot.add_custom_filter(telebot.asyncio_filters.TextMatchFilter()) bot.add_custom_filter(telebot.asyncio_filters.TextStartsFilter()) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index 36132d8da..56546bc9e 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -71,4 +71,5 @@ async def age_incorrect(message): # set saving states into file. bot.enable_saving_states() # you can delete this if you do not need to save states -bot.polling() \ No newline at end of file +import asyncio +asyncio.run(bot.polling()) \ No newline at end of file diff --git a/examples/asynchronous_telebot/download_file_example.py b/examples/asynchronous_telebot/download_file_example.py index 5105d9dbc..695258618 100644 --- a/examples/asynchronous_telebot/download_file_example.py +++ b/examples/asynchronous_telebot/download_file_example.py @@ -17,4 +17,5 @@ async def new_message(message: telebot.types.Message): await bot.edit_message_text(chat_id=message.chat.id, message_id=result_message.id, text='Done!', parse_mode='HTML') -bot.polling(skip_pending=True) +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/echo_bot.py b/examples/asynchronous_telebot/echo_bot.py index 940aecc46..cd2353755 100644 --- a/examples/asynchronous_telebot/echo_bot.py +++ b/examples/asynchronous_telebot/echo_bot.py @@ -23,4 +23,5 @@ async def echo_message(message): await bot.reply_to(message, message.text) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/exception_handler.py b/examples/asynchronous_telebot/exception_handler.py index f1da60fe5..b37f94508 100644 --- a/examples/asynchronous_telebot/exception_handler.py +++ b/examples/asynchronous_telebot/exception_handler.py @@ -24,4 +24,5 @@ async def photo_send(message: telebot.types.Message): -bot.polling(skip_pending=True) +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/middleware/flooding_middleware.py b/examples/asynchronous_telebot/middleware/flooding_middleware.py index de70702ac..4a2d37b08 100644 --- a/examples/asynchronous_telebot/middleware/flooding_middleware.py +++ b/examples/asynchronous_telebot/middleware/flooding_middleware.py @@ -36,4 +36,5 @@ async def post_process(self, message, data, exception): async def start(message): await bot.send_message(message.chat.id, 'Hello!') -bot.polling() \ No newline at end of file +import asyncio +asyncio.run(bot.polling()) \ No newline at end of file diff --git a/examples/asynchronous_telebot/middleware/i18n.py b/examples/asynchronous_telebot/middleware/i18n.py index 3c3196e94..81281bc44 100644 --- a/examples/asynchronous_telebot/middleware/i18n.py +++ b/examples/asynchronous_telebot/middleware/i18n.py @@ -45,4 +45,5 @@ async def start(message, data: dict): await bot.send_message(message.chat.id, data['response']) -bot.polling() +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/register_handler.py b/examples/asynchronous_telebot/register_handler.py index 76d194db2..f78c5d381 100644 --- a/examples/asynchronous_telebot/register_handler.py +++ b/examples/asynchronous_telebot/register_handler.py @@ -15,4 +15,6 @@ async def start_executor(message): # bot.register_edited_message_handler(*args, **kwargs) # And other functions.. -bot.polling(skip_pending=True) + +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/send_file_example.py b/examples/asynchronous_telebot/send_file_example.py index e67f8d87f..974ae8eeb 100644 --- a/examples/asynchronous_telebot/send_file_example.py +++ b/examples/asynchronous_telebot/send_file_example.py @@ -24,4 +24,6 @@ async def photos_send(message: telebot.types.Message): -bot.polling(skip_pending=True) + +import asyncio +asyncio.run(bot.polling()) diff --git a/examples/asynchronous_telebot/skip_updates_example.py b/examples/asynchronous_telebot/skip_updates_example.py index c149cb22c..89ff7970e 100644 --- a/examples/asynchronous_telebot/skip_updates_example.py +++ b/examples/asynchronous_telebot/skip_updates_example.py @@ -10,4 +10,6 @@ async def send_welcome(message): async def echo_all(message): await bot.reply_to(message, message.text) -bot.polling(skip_pending=True)# Skip pending skips old updates + +import asyncio +asyncio.run(bot.polling(skip_pending=True)) # to skip updates diff --git a/examples/asynchronous_telebot/update_listener.py b/examples/asynchronous_telebot/update_listener.py index 75488cf73..5e41cc414 100644 --- a/examples/asynchronous_telebot/update_listener.py +++ b/examples/asynchronous_telebot/update_listener.py @@ -11,4 +11,6 @@ async def update_listener(messages): bot.set_update_listener(update_listener) -bot.polling() \ No newline at end of file + +import asyncio +asyncio.run(bot.polling()) \ No newline at end of file diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3b8317d24..8234b5fad 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -211,7 +211,7 @@ async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=Non json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] - def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, + async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, request_timeout: int=20, allowed_updates: Optional[List[str]]=None, none_stop: Optional[bool]=None): """ @@ -240,10 +240,10 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim non_stop = none_stop if skip_pending: - asyncio.run(self.skip_updates()) - asyncio.run(self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates)) + await self.skip_updates() + await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) - def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, + async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -263,12 +263,12 @@ def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_ti so unwanted updates may be received for a short period of time. """ if skip_pending: - asyncio.run(self.skip_updates()) + await self.skip_updates() self._polling = True while self._polling: try: - asyncio.run( self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, - allowed_updates=allowed_updates, *args, **kwargs) ) + await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, + allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) From ed5b47cb962dd62f88618700a56cdf70aa7209c0 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Dec 2021 15:11:42 +0500 Subject: [PATCH 0794/1808] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b751585b0..23923a206 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) * [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors) * [The Telegram Chat Group](#the-telegram-chat-group) + * [Telegram Channel](#telegram-channel) * [More examples](#more-examples) * [Bots using this API](#bots-using-this-api) @@ -780,7 +781,11 @@ Bot instances that were idle for a long time might be rejected by the server whe Get help. Discuss. Chat. * Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A) + +## Telegram Channel +Join the [News channel](https://t.me/pyTelegramBotAPI). Here we will post releases and updates. + ## More examples * [Echo Bot](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py) From 096d58ae71bcde06a50fa857bee1a6ca460f7389 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Dec 2021 15:13:07 +0500 Subject: [PATCH 0795/1808] Update admin_filter_example.py --- .../asynchronous_telebot/custom_filters/admin_filter_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/custom_filters/admin_filter_example.py b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py index 8d3b2b59d..7236e4bf3 100644 --- a/examples/asynchronous_telebot/custom_filters/admin_filter_example.py +++ b/examples/asynchronous_telebot/custom_filters/admin_filter_example.py @@ -1,6 +1,6 @@ from telebot.async_telebot import AsyncTeleBot from telebot import asyncio_filters -bot = AsyncTeleBot('1297441208:AAGiez5xhzai5russPtPKmZjbdjybW4T0U8') +bot = AsyncTeleBot('TOKEN') # Handler @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) From ba9bf17f468188e295d9c762c4981b34d24b9f1f Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 12 Dec 2021 15:54:06 +0500 Subject: [PATCH 0796/1808] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 23923a206..3f9cd926d 100644 --- a/README.md +++ b/README.md @@ -717,6 +717,7 @@ Echo Bot example on AsyncTeleBot: # It echoes any incoming text messages. from telebot.async_telebot import AsyncTeleBot +import asyncio bot = AsyncTeleBot('TOKEN') @@ -736,7 +737,7 @@ async def echo_message(message): await bot.reply_to(message, message.text) -bot.polling() +asyncio.run(bot.polling()) ``` As you can see here, keywords are await and async. From 13fffe58a15e6e4250b99998e374a8464602b3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20Romero=20Rodr=C3=ADguez?= <51219689+studentenherz@users.noreply.github.com> Date: Sun, 12 Dec 2021 23:23:52 -0300 Subject: [PATCH 0797/1808] Added bot using pyTelegramBotAPI --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3f9cd926d..73e674b07 100644 --- a/README.md +++ b/README.md @@ -840,5 +840,6 @@ Join the [News channel](https://t.me/pyTelegramBotAPI). Here we will post releas * [ETHGasFeeTrackerBot](https://t.me/ETHGasFeeTrackerBot) ([Source](https://github.com/DevAdvik/ETHGasFeeTrackerBot]) by *DevAdvik* - Get Live Ethereum Gas Fees in GWEI * [Google Sheet Bot](https://github.com/JoachimStanislaus/Tele_Sheet_bot) by [JoachimStanislaus](https://github.com/JoachimStanislaus). This bot can help you to track your expenses by uploading your bot entries to your google sheet. * [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development. +* [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From c86fc4c3fa0251b61c2ff53d7900c1f5d8511523 Mon Sep 17 00:00:00 2001 From: Enrique-Moran Date: Thu, 23 Dec 2021 12:31:01 -0800 Subject: [PATCH 0798/1808] Added bot to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 73e674b07..82a4beb9b 100644 --- a/README.md +++ b/README.md @@ -841,5 +841,6 @@ Join the [News channel](https://t.me/pyTelegramBotAPI). Here we will post releas * [Google Sheet Bot](https://github.com/JoachimStanislaus/Tele_Sheet_bot) by [JoachimStanislaus](https://github.com/JoachimStanislaus). This bot can help you to track your expenses by uploading your bot entries to your google sheet. * [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development. * [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search. +* [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 24a9491ec040b656d9678bbeee9296c2118dc61a Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Dec 2021 16:04:29 +0400 Subject: [PATCH 0799/1808] _make_request function edited --- telebot/async_telebot.py | 8 ++++++++ telebot/asyncio_helper.py | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8234b5fad..beaca2d4d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -317,6 +317,14 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: except asyncio.CancelledError: return + except asyncio_helper.RequestTimeout as e: + logger.error(str(e)) + if non_stop: + await asyncio.sleep(2) + continue + else: + return + except asyncio_helper.ApiTelegramException as e: logger.error(str(e)) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0360b0950..eab1aea0b 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -34,21 +34,32 @@ async def _get_new_session(self): READ_TIMEOUT = 30 LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates) - +REQUEST_TIMEOUT = 10 +MAX_RETRIES = 3 logger = telebot.logger -RETRY_ON_ERROR = False -RETRY_TIMEOUT = 2 -MAX_RETRIES = 15 - async def _process_request(token, url, method='get', params=None, files=None, request_timeout=None): params = compose_data(params, files) + if request_timeout is None: + request_timeout = REQUEST_TIMEOUT + timeout = aiohttp.ClientTimeout(total=request_timeout) + got_result = False + current_try=0 async with await session_manager._get_new_session() as session: - async with session.request(method=method, url=API_URL.format(token, url), data=params, timeout=request_timeout) as response: - logger.debug("Request: method={0} url={1} params={2} files={3} request_timeout={4}".format(method, url, params, files, request_timeout).replace(token, token.split(':')[0] + ":{TOKEN}")) - json_result = await _check_result(url, response) - if json_result: - return json_result['result'] + while not got_result and current_try Date: Sat, 25 Dec 2021 16:23:26 +0300 Subject: [PATCH 0800/1808] send_document param type fix --- telebot/__init__.py | 13 +++++++++---- telebot/async_telebot.py | 19 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7104ebbb1..840b047e1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1158,7 +1158,7 @@ def send_voice( allow_sending_without_reply)) def send_document( - self, chat_id: Union[int, str], data: Union[Any, str], + self, chat_id: Union[int, str], document: Union[Any, str], reply_to_message_id: Optional[int]=None, caption: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, @@ -1169,11 +1169,12 @@ def send_document( caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, visible_file_name: Optional[str]=None, - disable_content_type_detection: Optional[bool]=None) -> types.Message: + disable_content_type_detection: Optional[bool]=None, + data: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send general files. :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 document: (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: @@ -1185,13 +1186,17 @@ def send_document( :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 + :param data: function typo miss compatibility: do not use it :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + if data and not(document): + # function typo miss compatibility + document = data return types.Message.de_json( apihelper.send_data( - self.token, chat_id, data, 'document', + self.token, chat_id, document, '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, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3b8317d24..f6722012e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6,7 +6,7 @@ import sys import time import traceback -from typing import Any, Callable, List, Optional, Union +from typing import Any, List, Optional, Union # this imports are used to avoid circular import error import telebot.util @@ -207,7 +207,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, - timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> types.Update: + timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> List[types.Update]: json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] @@ -249,7 +249,7 @@ def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_ti Wrap polling with infinite loop and exception handling to avoid bot stops polling. :param timeout: Request connection timeout - :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param request_timeout: Timeout in seconds for long polling (see API docs) :param skip_pending: skip old updates :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging @@ -1700,7 +1700,7 @@ async def send_voice( allow_sending_without_reply)) async def send_document( - self, chat_id: Union[int, str], data: Union[Any, str], + self, chat_id: Union[int, str], document: Union[Any, str], reply_to_message_id: Optional[int]=None, caption: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, @@ -1711,11 +1711,12 @@ async def send_document( caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, visible_file_name: Optional[str]=None, - disable_content_type_detection: Optional[bool]=None) -> types.Message: + disable_content_type_detection: Optional[bool]=None, + data: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send general files. :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 document: (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: @@ -1727,13 +1728,17 @@ async def send_document( :param allow_sending_without_reply: :param visible_file_name: allows to async 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 + :param data: function typo compatibility: do not use it :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + if data and not(document): + # function typo miss compatibility + document = data return types.Message.de_json( await asyncio_helper.send_data( - self.token, chat_id, data, 'document', + self.token, chat_id, document, '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, From 7f064249805de437fa7a718cfbbe7ec1da6b7dac Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 31 Dec 2021 13:20:54 +0400 Subject: [PATCH 0801/1808] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82a4beb9b..3c6b2d58a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Supports both sync and async ways.

-##

Supporting Bot API version: 5.5! +##

Supporting Bot API version: 5.6! ## Contents @@ -686,6 +686,7 @@ Result will be: ## API conformance +* ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021) * ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021) * ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021) * ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMember* classes are full copies of ChatMember From d334f5cb8d87a250886ebe9056f34d28554b0955 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 31 Dec 2021 15:05:40 +0400 Subject: [PATCH 0802/1808] Added protect_content parameter. Remade some methods. --- telebot/__init__.py | 191 ++- telebot/apihelper.py | 75 +- telebot/asyncio_types.py | 2885 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 3063 insertions(+), 88 deletions(-) create mode 100644 telebot/asyncio_types.py diff --git a/telebot/__init__.py b/telebot/__init__.py index 840b047e1..a73b21e2c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -936,32 +936,35 @@ def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatM def send_message( self, chat_id: Union[int, str], text: str, + parse_mode: Optional[str]=None, + entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + timeout: Optional[int]=None) -> types.Message: """ Use this method to send text messages. + Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. If you must send more than 4000 characters, use the `split_string` or `smart_split` function in util.py. - :param chat_id: - :param text: - :param disable_web_page_preview: - :param reply_to_message_id: - :param reply_markup: - :param parse_mode: - :param disable_notification: Boolean, Optional. Sends the message silently. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param text: Text of the message to be sent + :param parse_mode: Mode for parsing entities in the message text. + :param entities: A JSON-serialized list of special entities that appear in message text, which can be specified instead of parse_mode + :param disable_web_page_preview: Disables link previews for links in this message + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :param reply_to_message_id: If the message is a reply, ID of the original message + :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :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 timeout: - :param entities: - :param allow_sending_without_reply: - :return: API reply. + :return: """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -969,11 +972,12 @@ def send_message( apihelper.send_message( self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, - entities, allow_sending_without_reply)) + entities, allow_sending_without_reply, protect_content=protect_content)) def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], - message_id: int, disable_notification: Optional[bool]=None, + message_id: int, disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, timeout: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -981,11 +985,12 @@ def forward_message( :param chat_id: which chat to forward :param from_chat_id: which chat message from :param message_id: message id + :param protect_content: Protects the contents of the forwarded message from forwarding and saving :param timeout: :return: API reply. """ return types.Message.de_json( - apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) + apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) def copy_message( self, chat_id: Union[int, str], @@ -995,6 +1000,7 @@ def copy_message( parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, @@ -1008,6 +1014,7 @@ def copy_message( :param parse_mode: :param caption_entities: :param disable_notification: + :param protect_content: :param reply_to_message_id: :param allow_sending_without_reply: :param reply_markup: @@ -1017,7 +1024,7 @@ def copy_message( return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, - timeout)) + timeout, protect_content)) def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -1036,7 +1043,8 @@ def send_dice( reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send dices. :param chat_id: @@ -1046,22 +1054,25 @@ def send_dice( :param reply_markup: :param timeout: :param allow_sending_without_reply: + :param protect_content: :return: Message """ return types.Message.de_json( apihelper.send_dice( self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout, allow_sending_without_reply) + reply_markup, timeout, allow_sending_without_reply, protect_content) ) def send_photo( self, chat_id: Union[int, str], photo: Union[Any, str], - caption: Optional[str]=None, reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None,) -> types.Message: """ Use this method to send photos. :param chat_id: @@ -1082,8 +1093,9 @@ def send_photo( apihelper.send_photo( self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply)) + allow_sending_without_reply, protect_content)) + # TODO: Rewrite this method like in API. def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, @@ -1095,7 +1107,8 @@ def send_audio( timeout: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -1113,6 +1126,7 @@ def send_audio( :param thumb: :param caption_entities: :param allow_sending_without_reply: + :param protect_content: :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1121,8 +1135,9 @@ def send_audio( apihelper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply)) + caption_entities, allow_sending_without_reply, protect_content)) + # TODO: Rewrite this method like in API. def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, @@ -1132,7 +1147,8 @@ def send_voice( disable_notification: Optional[bool]=None, timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -1147,6 +1163,7 @@ def send_voice( :param timeout: :param caption_entities: :param allow_sending_without_reply: + :param protect_content: :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1155,8 +1172,9 @@ def send_voice( apihelper.send_voice( self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply)) + allow_sending_without_reply, protect_content)) + # TODO: Rewrite this method like in API. def send_document( self, chat_id: Union[int, str], document: Union[Any, str], reply_to_message_id: Optional[int]=None, @@ -1170,7 +1188,8 @@ def send_document( allow_sending_without_reply: Optional[bool]=None, visible_file_name: Optional[str]=None, disable_content_type_detection: Optional[bool]=None, - data: Optional[Union[Any, str]]=None) -> types.Message: + data: Optional[Union[Any, str]]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send general files. :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1187,6 +1206,7 @@ def send_document( :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 :param data: function typo miss compatibility: do not use it + :param protect_content: :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1200,15 +1220,17 @@ def send_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)) + disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content)) + # TODO: Rewrite this method like in API. def send_sticker( self, chat_id: Union[int, str], data: Union[Any, str], reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content:Optional[bool]=None) -> types.Message: """ Use this method to send .webp stickers. :param chat_id: @@ -1218,6 +1240,7 @@ def send_sticker( :param disable_notification: to disable the notification :param timeout: timeout :param allow_sending_without_reply: + :param protect_content: :return: API reply. """ return types.Message.de_json( @@ -1225,23 +1248,24 @@ def send_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)) + allow_sending_without_reply=allow_sending_without_reply, protect_content=protect_content)) def send_video( self, chat_id: Union[int, str], data: Union[Any, str], - duration: Optional[int]=None, + duration: Optional[int]=None, + width: Optional[int]=None, + height: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, caption: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, - supports_streaming: Optional[bool]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, - width: Optional[int]=None, - height: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + supports_streaming: Optional[bool]=None, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None,) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos. :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -1268,33 +1292,38 @@ def send_video( apihelper.send_video( self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, - caption_entities, allow_sending_without_reply)) + caption_entities, allow_sending_without_reply, protect_content)) def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], duration: Optional[int]=None, + width: Optional[int]=None, + height: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, caption: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds + :param width: Integer : Video width + :param height: Integer : Video height + :param thumb: InputFile or String : Thumbnail of the file sent :param caption: String : Animation caption (may also be used when resending animation by file_id). :param parse_mode: :param reply_to_message_id: :param reply_markup: :param disable_notification: :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent :param caption_entities: :param allow_sending_without_reply: :return: @@ -1305,8 +1334,9 @@ def send_animation( apihelper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply)) + caption_entities, allow_sending_without_reply, protect_content, width, height)) + # TODO: Rewrite this method like in API. def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], duration: Optional[int]=None, @@ -1316,7 +1346,8 @@ def send_video_note( disable_notification: Optional[bool]=None, timeout: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. @@ -1331,12 +1362,13 @@ def send_video_note( :param timeout: :param thumb: InputFile or String : Thumbnail of the file sent :param allow_sending_without_reply: + :param protect_content: :return: """ return types.Message.de_json( apihelper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb, allow_sending_without_reply)) + disable_notification, timeout, thumb, allow_sending_without_reply, protect_content)) def send_media_group( self, chat_id: Union[int, str], @@ -1344,6 +1376,7 @@ def send_media_group( types.InputMediaAudio, types.InputMediaDocument, types.InputMediaPhoto, types.InputMediaVideo]], disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: @@ -1352,6 +1385,7 @@ def send_media_group( :param chat_id: :param media: :param disable_notification: + :param protect_content: :param reply_to_message_id: :param timeout: :param allow_sending_without_reply: @@ -1359,9 +1393,11 @@ def send_media_group( """ result = apihelper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, - allow_sending_without_reply) + allow_sending_without_reply, protect_content) return [types.Message.de_json(msg) for msg in result] + + # TODO: Rewrite this method like in API. def send_location( self, chat_id: Union[int, str], latitude: float, longitude: float, @@ -1373,7 +1409,8 @@ def send_location( horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ @@ -1390,6 +1427,7 @@ def send_location( :param heading: :param proximity_alert_radius: :param allow_sending_without_reply: + :param protect_content: :return: API reply. """ return types.Message.de_json( @@ -1397,7 +1435,7 @@ def send_location( self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - allow_sending_without_reply)) + allow_sending_without_reply, protect_content)) def edit_message_live_location( self, latitude: float, longitude: float, @@ -1449,6 +1487,7 @@ def stop_message_live_location( apihelper.stop_message_live_location( self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) + # TODO: Rewrite this method like in API. def send_venue( self, chat_id: Union[int, str], latitude: float, longitude: float, @@ -1461,7 +1500,8 @@ def send_venue( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, google_place_id: Optional[str]=None, - google_place_type: Optional[str]=None) -> types.Message: + google_place_type: Optional[str]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel @@ -1479,15 +1519,16 @@ def send_venue( :param allow_sending_without_reply: :param google_place_id: :param google_place_type: + :param protect_content: :return: """ return types.Message.de_json( apihelper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, google_place_id, google_place_type) - ) + allow_sending_without_reply, google_place_id, google_place_type, protect_content)) + # TODO: Rewrite this method like in API. def send_contact( self, chat_id: Union[int, str], phone_number: str, first_name: str, last_name: Optional[str]=None, @@ -1496,13 +1537,13 @@ def send_contact( reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: return types.Message.de_json( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply) - ) + allow_sending_without_reply, protect_content)) def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: @@ -2026,7 +2067,8 @@ def send_game( reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Used to send the game :param chat_id: @@ -2036,12 +2078,13 @@ def send_game( :param reply_markup: :param timeout: :param allow_sending_without_reply: + :param protect_content: :return: """ result = apihelper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply) + allow_sending_without_reply, protect_content) return types.Message.de_json(result) def set_game_score( @@ -2083,6 +2126,7 @@ def get_game_high_scores( result = apihelper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) return [types.GameHighScore.de_json(r) for r in result] + # TODO: rewrite this method like in API def send_invoice( self, chat_id: Union[int, str], title: str, description: str, invoice_payload: str, provider_token: str, currency: str, @@ -2101,7 +2145,8 @@ def send_invoice( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, max_tip_amount: Optional[int] = None, - suggested_tip_amounts: Optional[List[int]]=None) -> types.Message: + suggested_tip_amounts: Optional[List[int]]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Sends invoice :param chat_id: Unique identifier for the target private chat @@ -2140,6 +2185,7 @@ def send_invoice( :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: :return: """ result = apihelper.send_invoice( @@ -2148,10 +2194,11 @@ def send_invoice( photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, - max_tip_amount, suggested_tip_amounts) + max_tip_amount, suggested_tip_amounts, protect_content) return types.Message.de_json(result) # noinspection PyShadowingBuiltins + # TODO: rewrite this method like in API def send_poll( self, chat_id: Union[int, str], question: str, options: List[str], is_anonymous: Optional[bool]=None, type: Optional[str]=None, @@ -2167,7 +2214,8 @@ def send_poll( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, allow_sending_without_reply: Optional[bool]=None, timeout: Optional[int]=None, - explanation_entities: Optional[List[types.MessageEntity]]=None) -> types.Message: + explanation_entities: Optional[List[types.MessageEntity]]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Send polls :param chat_id: @@ -2188,6 +2236,7 @@ def send_poll( :param reply_markup: :param timeout: :param explanation_entities: + :param protect_content: :return: """ @@ -2201,7 +2250,7 @@ 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_to_message_id, allow_sending_without_reply, - reply_markup, timeout, explanation_entities)) + reply_markup, timeout, explanation_entities, protect_content)) def stop_poll( self, chat_id: Union[int, str], message_id: int, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 229fa7856..3f9562d5a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -232,7 +232,7 @@ def send_message( token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - entities=None, allow_sending_without_reply=None): + entities=None, allow_sending_without_reply=None, protect_content=None): """ Use this method to send text messages. On success, the sent Message is returned. :param token: @@ -266,6 +266,8 @@ def send_message( payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload, method='post') @@ -390,19 +392,21 @@ def get_chat_member(token, chat_id, user_id): def forward_message( token, chat_id, from_chat_id, message_id, - disable_notification=None, timeout=None): + disable_notification=None, timeout=None, protect_content=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None): + reply_markup=None, timeout=None, protect_content=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -421,13 +425,15 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['allow_sending_without_reply'] = allow_sending_without_reply if timeout: payload['timeout'] = timeout + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None, allow_sending_without_reply=None): + reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -442,6 +448,8 @@ def send_dice( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) @@ -449,7 +457,7 @@ def send_photo( token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - caption_entities=None, allow_sending_without_reply=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -475,13 +483,15 @@ def send_photo( payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload, files=files, method='post') def send_media_group( token, chat_id, media, disable_notification=None, reply_to_message_id=None, - timeout=None, allow_sending_without_reply=None): + timeout=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -493,6 +503,8 @@ def send_media_group( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -504,7 +516,7 @@ def send_location( live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, allow_sending_without_reply=None): + proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -525,6 +537,8 @@ def send_location( payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) @@ -576,7 +590,7 @@ def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, allow_sending_without_reply=None, google_place_id=None, - google_place_type=None): + google_place_type=None, protect_content=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -597,13 +611,15 @@ def send_venue( payload['google_place_id'] = google_place_id if google_place_type: payload['google_place_type'] = google_place_type + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, protect_content=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -620,6 +636,9 @@ def send_contact( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content + return _make_request(token, method_url, params=payload) @@ -633,7 +652,7 @@ def send_chat_action(token, chat_id, action, timeout=None): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, - thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None): + thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -673,13 +692,15 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload, files=files, method='post') def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, protect_content=None, width=None, height=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -713,12 +734,18 @@ def send_animation( payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content + if width: + payload['width'] = width + if height: + payload['height'] = height return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, protect_content=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -744,11 +771,13 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None): + disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -780,12 +809,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['thumb'] = thumb if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, - caption_entities=None, allow_sending_without_reply=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -823,6 +854,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1236,7 +1269,7 @@ def delete_message(token, chat_id, message_id, timeout=None): def send_game( token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, protect_content=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1249,6 +1282,9 @@ def send_game( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content + return _make_request(token, method_url, params=payload) @@ -1313,7 +1349,8 @@ def send_invoice( need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None): + timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, + protect_content=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1391,6 +1428,8 @@ def send_invoice( payload['max_tip_amount'] = max_tip_amount if suggested_tip_amounts is not None: payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) + if protect_content is not None: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) @@ -1539,7 +1578,7 @@ 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_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, explanation_entities=None): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1581,6 +1620,8 @@ def send_poll( if explanation_entities: payload['explanation_entities'] = json.dumps( types.MessageEntity.to_list_of_dicts(explanation_entities)) + if protect_content: + payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) diff --git a/telebot/asyncio_types.py b/telebot/asyncio_types.py new file mode 100644 index 000000000..eb3fabffd --- /dev/null +++ b/telebot/asyncio_types.py @@ -0,0 +1,2885 @@ +# -*- coding: utf-8 -*- + +import logging +from typing import Dict, List, Optional, Union +from abc import ABC + +try: + import ujson as json +except ImportError: + import json + +from telebot import util + +DISABLE_KEYLEN_ERROR = False + +logger = logging.getLogger('TeleBot') + + +class JsonSerializable(object): + """ + Subclasses of this class are guaranteed to be able to be converted to JSON format. + All subclasses of this class must override to_json. + """ + + def to_json(self): + """ + Returns a JSON string representation of this class. + + This function must be overridden by subclasses. + :return: a JSON formatted string. + """ + raise NotImplementedError + + +class Dictionaryable(object): + """ + Subclasses of this class are guaranteed to be able to be converted to dictionary. + All subclasses of this class must override to_dict. + """ + + def to_dict(self): + """ + Returns a DICT with class field values + + This function must be overridden by subclasses. + :return: a DICT + """ + raise NotImplementedError + + +class JsonDeserializable(object): + """ + Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string. + All subclasses of this class must override de_json. + """ + + @classmethod + def de_json(cls, json_string): + """ + Returns an instance of this class from the given json dict or string. + + This function must be overridden by subclasses. + :return: an instance of this class created from the given json dict or string. + """ + raise NotImplementedError + + @staticmethod + def check_json(json_type, dict_copy = True): + """ + Checks whether json_type is a dict or a string. If it is already a dict, it is returned as-is. + If it is not, it is converted to a dict by means of json.loads(json_type) + :param json_type: input json or parsed dict + :param dict_copy: if dict is passed and it is changed outside - should be True! + :return: Dictionary parsed from json or original dict + """ + if util.is_dict(json_type): + return json_type.copy() if dict_copy else json_type + elif util.is_string(json_type): + return json.loads(json_type) + else: + raise ValueError("json_type should be a json dict or string.") + + def __str__(self): + d = { + x: y.__dict__ if hasattr(y, '__dict__') else y + for x, y in self.__dict__.items() + } + return str(d) + + +class Update(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string, dict_copy=False) + update_id = obj['update_id'] + message = Message.de_json(obj.get('message')) + edited_message = Message.de_json(obj.get('edited_message')) + channel_post = Message.de_json(obj.get('channel_post')) + edited_channel_post = Message.de_json(obj.get('edited_channel_post')) + inline_query = InlineQuery.de_json(obj.get('inline_query')) + chosen_inline_result = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) + callback_query = CallbackQuery.de_json(obj.get('callback_query')) + shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) + pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) + poll = Poll.de_json(obj.get('poll')) + poll_answer = PollAnswer.de_json(obj.get('poll_answer')) + my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member')) + chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) + chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) + 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) + + 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): + self.update_id = update_id + self.message = message + self.edited_message = edited_message + self.channel_post = channel_post + self.edited_channel_post = edited_channel_post + self.inline_query = inline_query + self.chosen_inline_result = chosen_inline_result + self.callback_query = callback_query + self.shipping_query = shipping_query + self.pre_checkout_query = pre_checkout_query + self.poll = poll + self.poll_answer = poll_answer + self.my_chat_member = my_chat_member + self.chat_member = chat_member + self.chat_join_request = chat_join_request + + +class ChatMemberUpdated(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['old_chat_member'] = ChatMember.de_json(obj['old_chat_member']) + obj['new_chat_member'] = ChatMember.de_json(obj['new_chat_member']) + 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, **kwargs): + self.chat: Chat = chat + self.from_user: User = from_user + self.date: int = date + self.old_chat_member: ChatMember = old_chat_member + self.new_chat_member: ChatMember = new_chat_member + self.invite_link: Optional[ChatInviteLink] = invite_link + + @property + def difference(self) -> Dict[str, List]: + """ + Get the difference between `old_chat_member` and `new_chat_member` + as a dict in the following format {'parameter': [old_value, new_value]} + E.g {'status': ['member', 'kicked'], 'until_date': [None, 1625055092]} + """ + old: Dict = self.old_chat_member.__dict__ + new: Dict = self.new_chat_member.__dict__ + dif = {} + for key in new: + if key == 'user': continue + if new[key] != old[key]: + dif[key] = [old[key], new[key]] + return dif + +class ChatJoinRequest(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['from_user'] = User.de_json(obj['from']) + obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link')) + return cls(**obj) + + def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs): + self.chat = chat + self.from_user = from_user + self.date = date + self.bio = bio + self.invite_link = invite_link + +class WebhookInfo(JsonDeserializable): + @classmethod + 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) + + def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, + last_error_date=None, last_error_message=None, max_connections=None, + allowed_updates=None, **kwargs): + self.url = url + self.has_custom_certificate = has_custom_certificate + self.pending_update_count = pending_update_count + self.ip_address = ip_address + self.last_error_date = last_error_date + self.last_error_message = last_error_message + self.max_connections = max_connections + self.allowed_updates = allowed_updates + + +class User(JsonDeserializable, Dictionaryable, JsonSerializable): + @classmethod + 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) + + 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, **kwargs): + self.id: int = id + self.is_bot: bool = is_bot + self.first_name: str = first_name + self.username: str = username + self.last_name: str = last_name + self.language_code: str = language_code + self.can_join_groups: bool = can_join_groups + self.can_read_all_group_messages: bool = can_read_all_group_messages + self.supports_inline_queries: bool = supports_inline_queries + + @property + def full_name(self): + full_name = self.first_name + if self.last_name: + full_name += ' {0}'.format(self.last_name) + return full_name + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'id': self.id, + 'is_bot': self.is_bot, + 'first_name': self.first_name, + 'last_name': self.last_name, + 'username': self.username, + 'language_code': self.language_code, + 'can_join_groups': self.can_join_groups, + 'can_read_all_group_messages': self.can_read_all_group_messages, + 'supports_inline_queries': self.supports_inline_queries} + + +class GroupChat(JsonDeserializable): + @classmethod + 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) + + def __init__(self, id, title, **kwargs): + self.id: int = id + self.title: str = title + + +class Chat(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'photo' in obj: + obj['photo'] = ChatPhoto.de_json(obj['photo']) + if 'pinned_message' in obj: + obj['pinned_message'] = Message.de_json(obj['pinned_message']) + if 'permissions' in obj: + obj['permissions'] = ChatPermissions.de_json(obj['permissions']) + if 'location' in obj: + obj['location'] = ChatLocation.de_json(obj['location']) + return cls(**obj) + + def __init__(self, id, type, title=None, username=None, first_name=None, + last_name=None, photo=None, bio=None, has_private_forwards=None, + description=None, invite_link=None, pinned_message=None, + permissions=None, slow_mode_delay=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, **kwargs): + self.id: int = id + self.type: str = type + self.title: str = title + self.username: str = username + self.first_name: str = first_name + self.last_name: str = last_name + self.photo: ChatPhoto = photo + self.bio: str = bio + self.has_private_forwards: bool = has_private_forwards + self.description: str = description + self.invite_link: str = invite_link + self.pinned_message: Message = pinned_message + self.permissions: ChatPermissions = permissions + self.slow_mode_delay: int = slow_mode_delay + self.message_auto_delete_time: int = message_auto_delete_time + self.has_protected_content: bool = has_protected_content + self.sticker_set_name: str = sticker_set_name + self.can_set_sticker_set: bool = can_set_sticker_set + self.linked_chat_id: int = linked_chat_id + self.location: ChatLocation = location + + +class MessageID(JsonDeserializable): + @classmethod + 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) + + def __init__(self, message_id, **kwargs): + self.message_id = message_id + + +class Message(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string, dict_copy=False) + message_id = obj['message_id'] + from_user = User.de_json(obj.get('from')) + date = obj['date'] + 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: + opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) + if 'forward_from_message_id' in obj: + opts['forward_from_message_id'] = obj.get('forward_from_message_id') + if 'forward_signature' in obj: + opts['forward_signature'] = obj.get('forward_signature') + if 'forward_sender_name' in obj: + opts['forward_sender_name'] = obj.get('forward_sender_name') + if 'forward_date' in obj: + opts['forward_date'] = obj.get('forward_date') + if 'is_automatic_forward' in obj: + opts['is_automatic_forward'] = obj.get('is_automatic_forward') + if 'reply_to_message' in obj: + opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) + if 'via_bot' in obj: + opts['via_bot'] = User.de_json(obj['via_bot']) + if 'edit_date' in obj: + opts['edit_date'] = obj.get('edit_date') + if 'has_protected_content' in obj: + opts['has_protected_content'] = obj.get('has_protected_content') + if 'media_group_id' in obj: + opts['media_group_id'] = obj.get('media_group_id') + if 'author_signature' in obj: + opts['author_signature'] = obj.get('author_signature') + if 'text' in obj: + opts['text'] = obj['text'] + content_type = 'text' + if 'entities' in obj: + opts['entities'] = Message.parse_entities(obj['entities']) + if 'caption_entities' in obj: + opts['caption_entities'] = Message.parse_entities(obj['caption_entities']) + if 'audio' in obj: + opts['audio'] = Audio.de_json(obj['audio']) + content_type = 'audio' + if 'document' in obj: + opts['document'] = Document.de_json(obj['document']) + content_type = 'document' + if 'animation' in obj: + # Document content type accompanies "animation", + # so "animation" should be checked below "document" to override it + opts['animation'] = Animation.de_json(obj['animation']) + content_type = 'animation' + if 'game' in obj: + opts['game'] = Game.de_json(obj['game']) + content_type = 'game' + if 'photo' in obj: + opts['photo'] = Message.parse_photo(obj['photo']) + content_type = 'photo' + if 'sticker' in obj: + opts['sticker'] = Sticker.de_json(obj['sticker']) + content_type = 'sticker' + if 'video' in obj: + opts['video'] = Video.de_json(obj['video']) + content_type = 'video' + if 'video_note' in obj: + opts['video_note'] = VideoNote.de_json(obj['video_note']) + content_type = 'video_note' + if 'voice' in obj: + opts['voice'] = Audio.de_json(obj['voice']) + content_type = 'voice' + if 'caption' in obj: + opts['caption'] = obj['caption'] + if 'contact' in obj: + opts['contact'] = Contact.de_json(json.dumps(obj['contact'])) + content_type = 'contact' + if 'location' in obj: + opts['location'] = Location.de_json(obj['location']) + content_type = 'location' + if 'venue' in obj: + opts['venue'] = Venue.de_json(obj['venue']) + content_type = 'venue' + if 'dice' in obj: + opts['dice'] = Dice.de_json(obj['dice']) + content_type = 'dice' + if 'new_chat_members' in obj: + new_chat_members = [] + for member in obj['new_chat_members']: + new_chat_members.append(User.de_json(member)) + opts['new_chat_members'] = new_chat_members + content_type = 'new_chat_members' + if 'left_chat_member' in obj: + opts['left_chat_member'] = User.de_json(obj['left_chat_member']) + content_type = 'left_chat_member' + if 'new_chat_title' in obj: + opts['new_chat_title'] = obj['new_chat_title'] + content_type = 'new_chat_title' + if 'new_chat_photo' in obj: + opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo']) + content_type = 'new_chat_photo' + if 'delete_chat_photo' in obj: + opts['delete_chat_photo'] = obj['delete_chat_photo'] + content_type = 'delete_chat_photo' + if 'group_chat_created' in obj: + opts['group_chat_created'] = obj['group_chat_created'] + content_type = 'group_chat_created' + if 'supergroup_chat_created' in obj: + opts['supergroup_chat_created'] = obj['supergroup_chat_created'] + content_type = 'supergroup_chat_created' + if 'channel_chat_created' in obj: + opts['channel_chat_created'] = obj['channel_chat_created'] + content_type = 'channel_chat_created' + if 'migrate_to_chat_id' in obj: + opts['migrate_to_chat_id'] = obj['migrate_to_chat_id'] + content_type = 'migrate_to_chat_id' + if 'migrate_from_chat_id' in obj: + opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] + content_type = 'migrate_from_chat_id' + if 'pinned_message' in obj: + opts['pinned_message'] = Message.de_json(obj['pinned_message']) + content_type = 'pinned_message' + if 'invoice' in obj: + opts['invoice'] = Invoice.de_json(obj['invoice']) + content_type = 'invoice' + if 'successful_payment' in obj: + opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment']) + content_type = 'successful_payment' + if 'connected_website' in obj: + opts['connected_website'] = obj['connected_website'] + content_type = 'connected_website' + if 'poll' in obj: + opts['poll'] = Poll.de_json(obj['poll']) + content_type = 'poll' + if 'passport_data' in obj: + opts['passport_data'] = obj['passport_data'] + content_type = 'passport_data' + if 'proximity_alert_triggered' in obj: + opts['proximity_alert_triggered'] = ProximityAlertTriggered.de_json(obj[ + 'proximity_alert_triggered']) + content_type = 'proximity_alert_triggered' + if 'voice_chat_scheduled' in obj: + opts['voice_chat_scheduled'] = VoiceChatScheduled.de_json(obj['voice_chat_scheduled']) + content_type = 'voice_chat_scheduled' + if 'voice_chat_started' in obj: + opts['voice_chat_started'] = VoiceChatStarted.de_json(obj['voice_chat_started']) + content_type = 'voice_chat_started' + if 'voice_chat_ended' in obj: + opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) + content_type = 'voice_chat_ended' + if 'voice_chat_participants_invited' in obj: + opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['voice_chat_participants_invited']) + content_type = 'voice_chat_participants_invited' + if 'message_auto_delete_timer_changed' in obj: + opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(obj['message_auto_delete_timer_changed']) + content_type = 'message_auto_delete_timer_changed' + if 'reply_markup' in obj: + opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) + return cls(message_id, from_user, date, chat, content_type, opts, json_string) + + @classmethod + def parse_chat(cls, chat): + if 'first_name' not in chat: + return GroupChat.de_json(chat) + else: + return User.de_json(chat) + + @classmethod + def parse_photo(cls, photo_size_array): + ret = [] + for ps in photo_size_array: + ret.append(PhotoSize.de_json(ps)) + return ret + + @classmethod + def parse_entities(cls, message_entity_array): + ret = [] + for me in message_entity_array: + ret.append(MessageEntity.de_json(me)) + return ret + + def __init__(self, message_id, from_user, date, chat, content_type, options, json_string): + self.content_type: str = content_type + self.id: int = message_id # Lets fix the telegram usability ####up with ID in Message :) + self.message_id: int = message_id + 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 + self.forward_signature: Optional[str] = None + self.forward_sender_name: Optional[str] = None + self.forward_date: Optional[int] = None + self.is_automatic_forward: Optional[bool] = None + self.reply_to_message: Optional[Message] = None + self.via_bot: Optional[User] = None + self.edit_date: Optional[int] = None + self.has_protected_content: Optional[bool] = None + self.media_group_id: Optional[str] = None + self.author_signature: Optional[str] = None + self.text: Optional[str] = None + self.entities: Optional[List[MessageEntity]] = None + self.caption_entities: Optional[List[MessageEntity]] = None + self.audio: Optional[Audio] = None + self.document: Optional[Document] = None + self.photo: Optional[List[PhotoSize]] = None + self.sticker: Optional[Sticker] = None + self.video: Optional[Video] = None + self.video_note: Optional[VideoNote] = None + self.voice: Optional[Voice] = None + self.caption: Optional[str] = None + self.contact: Optional[Contact] = None + self.location: Optional[Location] = None + self.venue: Optional[Venue] = None + self.animation: Optional[Animation] = None + self.dice: Optional[Dice] = None + self.new_chat_member: Optional[User] = None # Deprecated since Bot API 3.0. Not processed anymore + self.new_chat_members: Optional[List[User]] = None + self.left_chat_member: Optional[User] = None + self.new_chat_title: Optional[str] = None + self.new_chat_photo: Optional[List[PhotoSize]] = None + self.delete_chat_photo: Optional[bool] = None + self.group_chat_created: Optional[bool] = None + self.supergroup_chat_created: Optional[bool] = None + self.channel_chat_created: Optional[bool] = None + self.migrate_to_chat_id: Optional[int] = None + self.migrate_from_chat_id: Optional[int] = None + self.pinned_message: Optional[Message] = None + self.invoice: Optional[Invoice] = None + self.successful_payment: Optional[SuccessfulPayment] = None + self.connected_website: Optional[str] = None + self.reply_markup: Optional[InlineKeyboardMarkup] = None + for key in options: + setattr(self, key, options[key]) + self.json = json_string + + def __html_text(self, text, entities): + """ + Author: @sviat9440 + Updaters: @badiboy + Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" + + Example: + message.html_text + >> "Test parse formatting, url, text_mention and mention @username" + + Custom subs: + You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. + Example: + message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} + message.html_text + >> "Test parse formatting, url and text_mention and mention @username" + """ + + if not entities: + return text + + _subs = { + "bold": "{text}", + "italic": "{text}", + "pre": "

{text}
", + "code": "{text}", + # "url": "{text}", # @badiboy plain URLs have no text and do not need tags + "text_link": "{text}", + "strikethrough": "{text}", + "underline": "{text}" + } + + if hasattr(self, "custom_subs"): + for key, value in self.custom_subs.items(): + _subs[key] = value + utf16_text = text.encode("utf-16-le") + html_text = "" + + def func(upd_text, subst_type=None, url=None, user=None): + upd_text = upd_text.decode("utf-16-le") + if subst_type == "text_mention": + subst_type = "text_link" + url = "tg://user?id={0}".format(user.id) + elif subst_type == "mention": + url = "https://t.me/{0}".format(upd_text[1:]) + upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") + if not subst_type or not _subs.get(subst_type): + return upd_text + subs = _subs.get(subst_type) + return subs.format(text=upd_text, url=url) + + offset = 0 + for entity in entities: + if entity.offset > offset: + html_text += func(utf16_text[offset * 2 : entity.offset * 2]) + offset = entity.offset + html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + offset += entity.length + elif entity.offset == offset: + html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + offset += entity.length + else: + # TODO: process nested entities from Bot API 4.5 + # Now ignoring them + pass + if offset * 2 < len(utf16_text): + html_text += func(utf16_text[offset * 2:]) + return html_text + + @property + def html_text(self): + return self.__html_text(self.text, self.entities) + + @property + def html_caption(self): + return self.__html_text(self.caption, self.caption_entities) + + +class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): + @staticmethod + def to_list_of_dicts(entity_list) -> Union[List[Dict], None]: + res = [] + for e in entity_list: + res.append(MessageEntity.to_dict(e)) + return res or None + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'user' in obj: + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs): + self.type: str = type + self.offset: int = offset + self.length: int = length + self.url: str = url + self.user: User = user + self.language: str = language + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {"type": self.type, + "offset": self.offset, + "length": self.length, + "url": self.url, + "user": self.user, + "language": self.language} + + +class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): + @classmethod + 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) + + def __init__(self, value, emoji, **kwargs): + self.value: int = value + self.emoji: str = emoji + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'value': self.value, + 'emoji': self.emoji} + + +class PhotoSize(JsonDeserializable): + @classmethod + 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) + + def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.file_size: int = file_size + + +class Audio(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, + file_size=None, thumb=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.duration: int = duration + self.performer: str = performer + self.title: str = title + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + self.thumb: PhotoSize = thumb + + +class Voice(JsonDeserializable): + @classmethod + 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) + + def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.duration: int = duration + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class Document(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class Video(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class VideoNote(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.length: int = length + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_size: int = file_size + + +class Contact(JsonDeserializable): + @classmethod + 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) + + def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None, **kwargs): + self.phone_number: str = phone_number + self.first_name: str = first_name + self.last_name: str = last_name + self.user_id: int = user_id + self.vcard: str = vcard + + +class Location(JsonDeserializable, JsonSerializable, Dictionaryable): + @classmethod + 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) + + def __init__(self, longitude, latitude, horizontal_accuracy=None, + live_period=None, heading=None, proximity_alert_radius=None, **kwargs): + self.longitude: float = longitude + self.latitude: float = latitude + self.horizontal_accuracy: float = horizontal_accuracy + self.live_period: int = live_period + self.heading: int = heading + self.proximity_alert_radius: int = proximity_alert_radius + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return { + "longitude": self.longitude, + "latitude": self.latitude, + "horizontal_accuracy": self.horizontal_accuracy, + "live_period": self.live_period, + "heading": self.heading, + "proximity_alert_radius": self.proximity_alert_radius, + } + + +class Venue(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, + google_place_id=None, google_place_type=None, **kwargs): + self.location: Location = location + self.title: str = title + self.address: str = address + self.foursquare_id: str = foursquare_id + self.foursquare_type: str = foursquare_type + self.google_place_id: str = google_place_id + self.google_place_type: str = google_place_type + + +class UserProfilePhotos(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'photos' in obj: + photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] + obj['photos'] = photos + return cls(**obj) + + def __init__(self, total_count, photos=None, **kwargs): + self.total_count: int = total_count + self.photos: List[PhotoSize] = photos + + +class File(JsonDeserializable): + @classmethod + 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) + + def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.file_size: int = file_size + self.file_path: str = file_path + + +class ForceReply(JsonSerializable): + def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Optional[str]=None): + self.selective: bool = selective + self.input_field_placeholder: str = input_field_placeholder + + def to_json(self): + json_dict = {'force_reply': True} + if self.selective is not None: + json_dict['selective'] = self.selective + if self.input_field_placeholder: + json_dict['input_field_placeholder'] = self.input_field_placeholder + return json.dumps(json_dict) + + +class ReplyKeyboardRemove(JsonSerializable): + def __init__(self, selective=None): + self.selective: bool = selective + + def to_json(self): + json_dict = {'remove_keyboard': True} + if self.selective: + json_dict['selective'] = self.selective + return json.dumps(json_dict) + + +class ReplyKeyboardMarkup(JsonSerializable): + max_row_keys = 12 + + def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, + selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None): + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + if not DISABLE_KEYLEN_ERROR: + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + self.resize_keyboard: bool = resize_keyboard + self.one_time_keyboard: bool = one_time_keyboard + self.selective: bool = selective + self.row_width: int = row_width + self.input_field_placeholder: str = input_field_placeholder + self.keyboard: List[List[KeyboardButton]] = [] + + def add(self, *args, row_width=None): + """ + This function adds strings to the keyboard, while not exceeding row_width. + E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} + when row_width is set to 1. + When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} + See https://core.telegram.org/bots/api#replykeyboardmarkup + :param args: KeyboardButton to append to the keyboard + :param row_width: width of row + :return: self, to allow function chaining. + """ + if row_width is None: + row_width = self.row_width + + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + if not DISABLE_KEYLEN_ERROR: + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + for row in util.chunks(args, row_width): + button_array = [] + for button in row: + if util.is_string(button): + button_array.append({'text': button}) + elif util.is_bytes(button): + button_array.append({'text': button.decode('utf-8')}) + else: + button_array.append(button.to_dict()) + self.keyboard.append(button_array) + + return self + + def row(self, *args): + """ + Adds a list of KeyboardButton to the keyboard. This function does not consider row_width. + ReplyKeyboardMarkup#row("A")#row("B", "C")#to_json() outputs '{keyboard: [["A"], ["B", "C"]]}' + See https://core.telegram.org/bots/api#replykeyboardmarkup + :param args: strings + :return: self, to allow function chaining. + """ + + return self.add(*args, row_width=self.max_row_keys) + + def to_json(self): + """ + Converts this object to its json representation following the Telegram API guidelines described here: + https://core.telegram.org/bots/api#replykeyboardmarkup + :return: + """ + json_dict = {'keyboard': self.keyboard} + if self.one_time_keyboard is not None: + json_dict['one_time_keyboard'] = self.one_time_keyboard + if self.resize_keyboard is not None: + json_dict['resize_keyboard'] = self.resize_keyboard + if self.selective is not None: + json_dict['selective'] = self.selective + if self.input_field_placeholder: + json_dict['input_field_placeholder'] = self.input_field_placeholder + 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[KeyboardButtonPollType]=None): + self.text: str = text + self.request_contact: bool = request_contact + self.request_location: bool = request_location + self.request_poll: KeyboardButtonPollType = request_poll + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'text': self.text} + if self.request_contact is not None: + json_dict['request_contact'] = self.request_contact + if self.request_location is not None: + json_dict['request_location'] = self.request_location + if self.request_poll is not None: + json_dict['request_poll'] = self.request_poll.to_dict() + return json_dict + + +class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable): + max_row_keys = 8 + + @classmethod + def de_json(cls, json_string): + 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 = keyboard) + + def __init__(self, keyboard=None, row_width=3): + """ + This object represents an inline keyboard that appears + right next to the message it belongs to. + + :return: + """ + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + self.row_width: int = row_width + self.keyboard: List[List[InlineKeyboardButton]] = keyboard or [] + + def add(self, *args, row_width=None): + """ + This method adds buttons to the keyboard without exceeding row_width. + + E.g. InlineKeyboardMarkup.add("A", "B", "C") yields the json result: + {keyboard: [["A"], ["B"], ["C"]]} + when row_width is set to 1. + When row_width is set to 2, the result: + {keyboard: [["A", "B"], ["C"]]} + See https://core.telegram.org/bots/api#inlinekeyboardmarkup + + :param args: Array of InlineKeyboardButton to append to the keyboard + :param row_width: width of row + :return: self, to allow function chaining. + """ + if row_width is None: + row_width = self.row_width + + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + for row in util.chunks(args, row_width): + button_array = [button for button in row] + self.keyboard.append(button_array) + + return self + + def row(self, *args): + """ + Adds a list of InlineKeyboardButton to the keyboard. + This method does not consider row_width. + + InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: + '{keyboard: [["A"], ["B", "C"]]}' + See https://core.telegram.org/bots/api#inlinekeyboardmarkup + + :param args: Array of InlineKeyboardButton to append to the keyboard + :return: self, to allow function chaining. + """ + + return self.add(*args, row_width=self.max_row_keys) + + def to_json(self): + """ + Converts this object to its json representation + following the Telegram API guidelines described here: + https://core.telegram.org/bots/api#inlinekeyboardmarkup + :return: + """ + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = dict() + json_dict['inline_keyboard'] = [[button.to_dict() for button in row] for row in self.keyboard] + return json_dict + + +class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'login_url' in obj: + obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) + return cls(**obj) + + def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, + switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): + self.text: str = text + self.url: str = url + self.callback_data: str = callback_data + self.switch_inline_query: str = switch_inline_query + self.switch_inline_query_current_chat: str = switch_inline_query_current_chat + self.callback_game = callback_game # Not Implemented + self.pay: bool = pay + self.login_url: LoginUrl = login_url + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'text': self.text} + if self.url: + json_dict['url'] = self.url + if self.callback_data: + json_dict['callback_data'] = self.callback_data + if self.switch_inline_query is not None: + json_dict['switch_inline_query'] = self.switch_inline_query + if self.switch_inline_query_current_chat is not None: + json_dict['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat + if self.callback_game is not None: + json_dict['callback_game'] = self.callback_game + if self.pay is not None: + json_dict['pay'] = self.pay + if self.login_url is not None: + json_dict['login_url'] = self.login_url.to_dict() + return json_dict + + +class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): + @classmethod + 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) + + def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None, **kwargs): + self.url: str = url + self.forward_text: str = forward_text + self.bot_username: str = bot_username + self.request_write_access: bool = request_write_access + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'url': self.url} + if self.forward_text: + json_dict['forward_text'] = self.forward_text + if self.bot_username: + json_dict['bot_username'] = self.bot_username + if self.request_write_access is not None: + json_dict['request_write_access'] = self.request_write_access + return json_dict + + +class CallbackQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if not "data" in obj: + # "data" field is Optional in the API, but historically is mandatory in the class constructor + obj['data'] = None + obj['from_user'] = User.de_json(obj.pop('from')) + if 'message' in obj: + obj['message'] = Message.de_json(obj.get('message')) + return cls(**obj) + + def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None, **kwargs): + self.id: int = id + self.from_user: User = from_user + self.message: Message = message + self.inline_message_id: str = inline_message_id + self.chat_instance: str = chat_instance + self.data: str = data + self.game_short_name: str = game_short_name + + +class ChatPhoto(JsonDeserializable): + @classmethod + 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) + + def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id, **kwargs): + self.small_file_id: str = small_file_id + self.small_file_unique_id: str = small_file_unique_id + self.big_file_id: str = big_file_id + self.big_file_unique_id: str = big_file_unique_id + + +class ChatMember(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, + can_post_messages=None, can_edit_messages=None, can_delete_messages=None, + can_restrict_members=None, can_promote_members=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None, is_member=None, + can_send_messages=None, can_send_media_messages=None, can_send_polls=None, + can_send_other_messages=None, can_add_web_page_previews=None, + can_manage_chat=None, can_manage_voice_chats=None, + until_date=None, **kwargs): + self.user: User = user + self.status: str = status + self.custom_title: str = custom_title + self.is_anonymous: bool = is_anonymous + self.can_be_edited: bool = can_be_edited + self.can_post_messages: bool = can_post_messages + self.can_edit_messages: bool = can_edit_messages + self.can_delete_messages: bool = can_delete_messages + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + self.is_member: bool = is_member + self.can_send_messages: bool = can_send_messages + self.can_send_media_messages: bool = can_send_media_messages + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_manage_chat: bool = can_manage_chat + self.can_manage_voice_chats: bool = can_manage_voice_chats + self.until_date: int = until_date + + +class ChatMemberOwner(ChatMember): + pass + +class ChatMemberAdministrator(ChatMember): + pass + + +class ChatMemberMember(ChatMember): + pass + + +class ChatMemberRestricted(ChatMember): + pass + + +class ChatMemberLeft(ChatMember): + pass + + +class ChatMemberBanned(ChatMember): + pass + + +class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return json_string + obj = cls.check_json(json_string, dict_copy=False) + return cls(**obj) + + def __init__(self, can_send_messages=None, can_send_media_messages=None, + can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None, **kwargs): + self.can_send_messages: bool = can_send_messages + self.can_send_media_messages: bool = can_send_media_messages + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = dict() + if self.can_send_messages is not None: + json_dict['can_send_messages'] = self.can_send_messages + if self.can_send_media_messages is not None: + json_dict['can_send_media_messages'] = self.can_send_media_messages + if self.can_send_polls is not None: + json_dict['can_send_polls'] = self.can_send_polls + if self.can_send_other_messages is not None: + json_dict['can_send_other_messages'] = self.can_send_other_messages + if self.can_add_web_page_previews is not None: + json_dict['can_add_web_page_previews'] = self.can_add_web_page_previews + if self.can_change_info is not None: + json_dict['can_change_info'] = self.can_change_info + if self.can_invite_users is not None: + json_dict['can_invite_users'] = self.can_invite_users + if self.can_pin_messages is not None: + json_dict['can_pin_messages'] = self.can_pin_messages + return json_dict + + +class BotCommand(JsonSerializable, JsonDeserializable): + @classmethod + 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) + + def __init__(self, command, description): + """ + This object represents a bot command. + :param command: Text of the command, 1-32 characters. + Can contain only lowercase English letters, digits and underscores. + :param description: Description of the command, 3-256 characters. + :return: + """ + self.command: str = command + self.description: str = description + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'command': self.command, 'description': self.description} + + +# BotCommandScopes + +class BotCommandScope(ABC, JsonSerializable): + def __init__(self, type='default', chat_id=None, user_id=None): + """ + Abstract class. + Use BotCommandScopeX classes to set a specific scope type: + BotCommandScopeDefault + BotCommandScopeAllPrivateChats + BotCommandScopeAllGroupChats + BotCommandScopeAllChatAdministrators + BotCommandScopeChat + BotCommandScopeChatAdministrators + BotCommandScopeChatMember + """ + self.type: str = type + self.chat_id: Optional[Union[int, str]] = chat_id + self.user_id: Optional[Union[int, str]] = user_id + + def to_json(self): + json_dict = {'type': self.type} + if self.chat_id: + json_dict['chat_id'] = self.chat_id + if self.user_id: + json_dict['user_id'] = self.user_id + return json.dumps(json_dict) + + +class BotCommandScopeDefault(BotCommandScope): + def __init__(self): + """ + Represents the default scope of bot commands. + Default commands are used if no commands with a narrower scope are specified for the user. + """ + super(BotCommandScopeDefault, self).__init__(type='default') + + +class BotCommandScopeAllPrivateChats(BotCommandScope): + def __init__(self): + """ + Represents the scope of bot commands, covering all private chats. + """ + super(BotCommandScopeAllPrivateChats, self).__init__(type='all_private_chats') + + +class BotCommandScopeAllGroupChats(BotCommandScope): + def __init__(self): + """ + Represents the scope of bot commands, covering all group and supergroup chats. + """ + super(BotCommandScopeAllGroupChats, self).__init__(type='all_group_chats') + + +class BotCommandScopeAllChatAdministrators(BotCommandScope): + def __init__(self): + """ + Represents the scope of bot commands, covering all group and supergroup chat administrators. + """ + super(BotCommandScopeAllChatAdministrators, self).__init__(type='all_chat_administrators') + + +class BotCommandScopeChat(BotCommandScope): + def __init__(self, chat_id=None): + super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) + + +class BotCommandScopeChatAdministrators(BotCommandScope): + def __init__(self, chat_id=None): + """ + Represents the scope of bot commands, covering a specific chat. + @param chat_id: Unique identifier for the target chat + """ + super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) + + +class BotCommandScopeChatMember(BotCommandScope): + def __init__(self, chat_id=None, user_id=None): + """ + Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat + @param chat_id: Unique identifier for the target chat + @param user_id: Unique identifier of the target user + """ + super(BotCommandScopeChatMember, self).__init__(type='chat_member', chat_id=chat_id, user_id=user_id) + + +# InlineQuery + +class InlineQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, id, from_user, query, offset, chat_type=None, location=None, **kwargs): + """ + This object represents an incoming inline query. + When the user sends an empty query, your bot could + return some default or trending results. + :param id: string Unique identifier for this query + :param from_user: User Sender + :param query: String Text of the query + :param chat_type: String Type of the chat, from which the inline query was sent. + Can be either “sender” for a private chat with the inline query sender, + “private”, “group”, “supergroup”, or “channel”. + :param offset: String Offset of the results to be returned, can be controlled by the bot + :param location: Sender location, only for bots that request user location + :return: InlineQuery Object + """ + self.id: int = id + self.from_user: User = from_user + self.query: str = query + self.offset: str = offset + self.chat_type: str = chat_type + self.location: Location = location + + +class InputTextMessageContent(Dictionaryable): + def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None): + self.message_text: str = message_text + self.parse_mode: str = parse_mode + self.entities: List[MessageEntity] = entities + self.disable_web_page_preview: bool = disable_web_page_preview + + def to_dict(self): + json_dict = {'message_text': self.message_text} + if self.parse_mode: + json_dict['parse_mode'] = self.parse_mode + if self.entities: + json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) + if self.disable_web_page_preview is not None: + json_dict['disable_web_page_preview'] = self.disable_web_page_preview + return json_dict + + +class InputLocationMessageContent(Dictionaryable): + def __init__(self, latitude, longitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None): + self.latitude: float = latitude + self.longitude: float = longitude + self.horizontal_accuracy: float = horizontal_accuracy + self.live_period: int = live_period + self.heading: int = heading + self.proximity_alert_radius: int = proximity_alert_radius + + def to_dict(self): + json_dict = {'latitude': self.latitude, '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 + return json_dict + + +class InputVenueMessageContent(Dictionaryable): + def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, + google_place_id=None, google_place_type=None): + self.latitude: float = latitude + self.longitude: float = longitude + self.title: str = title + self.address: str = address + self.foursquare_id: str = foursquare_id + self.foursquare_type: str = foursquare_type + self.google_place_id: str = google_place_id + self.google_place_type: str = google_place_type + + def to_dict(self): + json_dict = { + 'latitude': self.latitude, + 'longitude': self.longitude, + 'title': self.title, + '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.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_dict + + +class InputContactMessageContent(Dictionaryable): + def __init__(self, phone_number, first_name, last_name=None, vcard=None): + self.phone_number: str = phone_number + self.first_name: str = first_name + self.last_name: str = last_name + self.vcard: str = vcard + + def to_dict(self): + json_dict = {'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 + return json_dict + + +class InputInvoiceMessageContent(Dictionaryable): + def __init__(self, title, description, payload, provider_token, currency, prices, + max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, + photo_url=None, photo_size=None, photo_width=None, photo_height=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, + send_phone_number_to_provider=None, send_email_to_provider=None, + is_flexible=None): + self.title: str = title + self.description: str = description + self.payload: str = payload + self.provider_token: str = provider_token + self.currency: str = currency + self.prices: List[LabeledPrice] = prices + self.max_tip_amount: Optional[int] = max_tip_amount + self.suggested_tip_amounts: Optional[List[int]] = suggested_tip_amounts + self.provider_data: Optional[str] = provider_data + self.photo_url: Optional[str] = photo_url + self.photo_size: Optional[int] = photo_size + self.photo_width: Optional[int] = photo_width + self.photo_height: Optional[int] = photo_height + self.need_name: Optional[bool] = need_name + self.need_phone_number: Optional[bool] = need_phone_number + self.need_email: Optional[bool] = need_email + self.need_shipping_address: Optional[bool] = need_shipping_address + self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider + self.send_email_to_provider: Optional[bool] = send_email_to_provider + self.is_flexible: Optional[bool] = is_flexible + + def to_dict(self): + json_dict = { + 'title': self.title, + 'description': self.description, + 'payload': self.payload, + 'provider_token': self.provider_token, + 'currency': self.currency, + 'prices': [LabeledPrice.to_dict(lp) for lp in self.prices] + } + if self.max_tip_amount: + json_dict['max_tip_amount'] = self.max_tip_amount + if self.suggested_tip_amounts: + json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts + if self.provider_data: + json_dict['provider_data'] = self.provider_data + if self.photo_url: + json_dict['photo_url'] = self.photo_url + if self.photo_size: + json_dict['photo_size'] = self.photo_size + if self.photo_width: + json_dict['photo_width'] = self.photo_width + if self.photo_height: + json_dict['photo_height'] = self.photo_height + if self.need_name is not None: + json_dict['need_name'] = self.need_name + if self.need_phone_number is not None: + json_dict['need_phone_number'] = self.need_phone_number + if self.need_email is not None: + json_dict['need_email'] = self.need_email + if self.need_shipping_address is not None: + json_dict['need_shipping_address'] = self.need_shipping_address + if self.send_phone_number_to_provider is not None: + json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider + if self.send_email_to_provider is not None: + json_dict['send_email_to_provider'] = self.send_email_to_provider + if self.is_flexible is not None: + json_dict['is_flexible'] = self.is_flexible + return json_dict + + +class ChosenInlineResult(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, result_id, from_user, query, location=None, inline_message_id=None, **kwargs): + """ + This object represents a result of an inline query + that was chosen by the user and sent to their chat partner. + :param result_id: string The unique identifier for the result that was chosen. + :param from_user: User The user that chose the result. + :param query: String The query that was used to obtain the result. + :return: ChosenInlineResult Object. + """ + self.result_id: str = result_id + self.from_user: User = from_user + self.location: Location = location + self.inline_message_id: str = inline_message_id + self.query: str = query + + +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. + :param title: Title of the result. + :param input_message_content: InputMessageContent : Content of the message to be sent + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param url: URL of the result. + :param hide_url: Pass True, if you don't want the URL to be shown in the message. + :param description: Short description of the result. + :param thumb_url: Url of the thumbnail for the result. + :param thumb_width: Thumbnail width. + :param thumb_height: Thumbnail height + :return: + """ + 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 + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height + + def to_dict(self): + json_dict = super().to_dict() + if self.url: + json_dict['url'] = self.url + if self.hide_url: + json_dict['hide_url'] = self.hide_url + if self.description: + json_dict['description'] = self.description + 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 InlineQueryResultPhoto(InlineQueryResultBase): + def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=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 + :param photo_url: A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB + :param thumb_url: URL of the thumbnail for the photo + :param photo_width: Width of the photo. + :param photo_height: Height of the photo. + :param title: Title for the result. + :param description: Short description of the result. + :param caption: Caption of the photo to be sent, 0-200 characters. + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or + inline URLs in the media caption. + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo + :return: + """ + 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.description = description + + 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.description: + json_dict['description'] = self.description + return json_dict + + +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, + thumb_mime_type=None): + """ + Represents a link to an animated GIF file. + :param id: Unique identifier for this result, 1-64 bytes. + :param gif_url: A valid URL for the GIF file. File size must not exceed 1MB + :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result. + :param gif_width: Width of the GIF. + :param gif_height: Height of the GIF. + :param title: Title for the result. + :param caption: Caption of the GIF file to be sent, 0-200 characters + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo + :return: + """ + 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.gif_duration = gif_duration + self.thumb_mime_type = thumb_mime_type + + 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.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 + if self.thumb_mime_type: + json_dict['thumb_mime_type'] = self.thumb_mime_type + return json_dict + + +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, + thumb_mime_type=None): + """ + Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). + :param id: Unique identifier for this result, 1-64 bytes + :param mpeg4_url: A valid URL for the MP4 file. File size must not exceed 1MB + :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result + :param mpeg4_width: Video width + :param mpeg4_height: Video height + :param title: Title for the result + :param caption: Caption of the MPEG-4 file to be sent, 0-200 characters + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text + or inline URLs in the media caption. + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo + :return: + """ + 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.mpeg4_duration = mpeg4_duration + self.thumb_mime_type = thumb_mime_type + + 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 + json_dict['thumb_url'] = self.thumb_url + if self.mpeg4_duration: + json_dict['mpeg4_duration '] = self.mpeg4_duration + if self.thumb_mime_type: + json_dict['thumb_mime_type'] = self.thumb_mime_type + return json_dict + + +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. + :param id: Unique identifier for this result, 1-64 bytes + :param video_url: A valid URL for the embedded video player or video file + :param mime_type: Mime type of the content of video url, “text/html” or “video/mp4” + :param thumb_url: URL of the thumbnail (jpeg only) for the video + :param title: Title for the result + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or + inline URLs in the media caption. + :param video_width: Video width + :param video_height: Video height + :param video_duration: Video duration in seconds + :param description: Short description of the result + :return: + """ + 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.description = description + + 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 + return json_dict + + +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.performer = performer + self.audio_duration = audio_duration + + 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 + return json_dict + + +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.voice_duration = voice_duration + + 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 + return json_dict + + +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.description = description + 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['document_url'] = self.document_url + json_dict['mime_type'] = self.mime_type + if self.description: + json_dict['description'] = self.description + 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 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, 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.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_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 + return json_dict + + +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): + 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.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 + + 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.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 + 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 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 + self.title = None + self.description = None + self.caption = None + self.reply_markup = None + self.input_message_content = None + self.parse_mode = None + self.caption_entities = None + self.payload_dic = {} + + def to_json(self): + json_dict = self.payload_dic + json_dict['type'] = self.type + json_dict['id'] = self.id + 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.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.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(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): + 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(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(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): + 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(InlineQueryResultCachedBase): + def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content=None): + InlineQueryResultCachedBase.__init__(self) + self.type = 'sticker' + self.id = id + self.sticker_file_id = sticker_file_id + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.payload_dic['sticker_file_id'] = sticker_file_id + + +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(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): + 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(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(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 + self.payload_dic['audio_file_id'] = audio_file_id + + +# Games + +class Game(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['photo'] = Game.parse_photo(obj['photo']) + if 'text_entities' in obj: + obj['text_entities'] = Game.parse_entities(obj['text_entities']) + if 'animation' in obj: + obj['animation'] = Animation.de_json(obj['animation']) + return cls(**obj) + + @classmethod + def parse_photo(cls, photo_size_array): + ret = [] + for ps in photo_size_array: + ret.append(PhotoSize.de_json(ps)) + return ret + + @classmethod + def parse_entities(cls, message_entity_array): + ret = [] + for me in message_entity_array: + ret.append(MessageEntity.de_json(me)) + return ret + + def __init__(self, title, description, photo, text=None, text_entities=None, animation=None, **kwargs): + self.title: str = title + self.description: str = description + self.photo: List[PhotoSize] = photo + self.text: str = text + self.text_entities: List[MessageEntity] = text_entities + self.animation: Animation = animation + + +class Animation(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj["thumb"] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, + thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class GameHighScore(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, position, user, score, **kwargs): + self.position: int = position + self.user: User = user + self.score: int = score + + +# Payments + +class LabeledPrice(JsonSerializable): + def __init__(self, label, amount): + self.label: str = label + self.amount: int = amount + + def to_dict(self): + return { + 'label': self.label, 'amount': self.amount + } + + def to_json(self): + return json.dumps(self.to_dict()) + + +class Invoice(JsonDeserializable): + @classmethod + 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) + + def __init__(self, title, description, start_parameter, currency, total_amount, **kwargs): + self.title: str = title + self.description: str = description + self.start_parameter: str = start_parameter + self.currency: str = currency + self.total_amount: int = total_amount + + +class ShippingAddress(JsonDeserializable): + @classmethod + 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) + + def __init__(self, country_code, state, city, street_line1, street_line2, post_code, **kwargs): + self.country_code: str = country_code + self.state: str = state + self.city: str = city + self.street_line1: str = street_line1 + self.street_line2: str = street_line2 + self.post_code: str = post_code + + +class OrderInfo(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['shipping_address'] = ShippingAddress.de_json(obj.get('shipping_address')) + return cls(**obj) + + def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs): + self.name: str = name + self.phone_number: str = phone_number + self.email: str = email + self.shipping_address: ShippingAddress = shipping_address + + +class ShippingOption(JsonSerializable): + def __init__(self, id, title): + self.id: str = id + self.title: str = title + self.prices: List[LabeledPrice] = [] + + def add_price(self, *args): + """ + Add LabeledPrice to ShippingOption + :param args: LabeledPrices + """ + for price in args: + self.prices.append(price) + return self + + def to_json(self): + price_list = [] + for p in self.prices: + price_list.append(p.to_dict()) + json_dict = json.dumps({'id': self.id, 'title': self.title, 'prices': price_list}) + return json_dict + + +class SuccessfulPayment(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) + return cls(**obj) + + def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, + telegram_payment_charge_id=None, provider_payment_charge_id=None, **kwargs): + self.currency: str = currency + self.total_amount: int = total_amount + self.invoice_payload: str = invoice_payload + self.shipping_option_id: str = shipping_option_id + self.order_info: OrderInfo = order_info + self.telegram_payment_charge_id: str = telegram_payment_charge_id + self.provider_payment_charge_id: str = provider_payment_charge_id + + +class ShippingQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) + return cls(**obj) + + def __init__(self, id, from_user, invoice_payload, shipping_address, **kwargs): + self.id: str = id + self.from_user: User = from_user + self.invoice_payload: str = invoice_payload + self.shipping_address: ShippingAddress = shipping_address + + +class PreCheckoutQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) + return cls(**obj) + + def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs): + self.id: str = id + self.from_user: User = from_user + self.currency: str = currency + self.total_amount: int = total_amount + self.invoice_payload: str = invoice_payload + self.shipping_option_id: str = shipping_option_id + self.order_info: OrderInfo = order_info + + +# Stickers + +class StickerSet(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + stickers = [] + for s in obj['stickers']: + stickers.append(Sticker.de_json(s)) + obj['stickers'] = stickers + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, name, title, is_animated, contains_masks, stickers, thumb=None, **kwargs): + self.name: str = name + self.title: str = title + self.is_animated: bool = is_animated + self.contains_masks: bool = contains_masks + self.stickers: List[Sticker] = stickers + self.thumb: PhotoSize = thumb + + +class Sticker(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + if 'mask_position' in obj: + obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width, height, is_animated, + thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.is_animated: bool = is_animated + self.thumb: PhotoSize = thumb + self.emoji: str = emoji + self.set_name: str = set_name + self.mask_position: MaskPosition = mask_position + self.file_size: int = file_size + + + +class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): + @classmethod + 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) + + def __init__(self, point, x_shift, y_shift, scale, **kwargs): + self.point: str = point + self.x_shift: float = x_shift + self.y_shift: float = y_shift + self.scale: float = scale + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale} + + +# InputMedia + +class InputMedia(Dictionaryable, JsonSerializable): + def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None): + self.type: str = type + self.media: str = media + self.caption: Optional[str] = caption + self.parse_mode: Optional[str] = parse_mode + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + + if util.is_string(self.media): + self._media_name = '' + self._media_dic = self.media + else: + self._media_name = util.generate_random_token() + self._media_dic = 'attach://{0}'.format(self._media_name) + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'type': self.type, 'media': self._media_dic} + if self.caption: + json_dict['caption'] = self.caption + 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_dict + + def convert_input_media(self): + if util.is_string(self.media): + return self.to_json(), None + + return self.to_json(), {self._media_name: self.media} + + +class InputMediaPhoto(InputMedia): + def __init__(self, media, caption=None, parse_mode=None): + if util.is_pil_image(media): + media = util.pil_image_to_file(media) + + super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) + + def to_dict(self): + return super(InputMediaPhoto, self).to_dict() + + +class InputMediaVideo(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, + supports_streaming=None): + super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.width = width + self.height = height + self.duration = duration + self.supports_streaming = supports_streaming + + def to_dict(self): + ret = super(InputMediaVideo, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.width: + ret['width'] = self.width + if self.height: + ret['height'] = self.height + if self.duration: + ret['duration'] = self.duration + if self.supports_streaming: + ret['supports_streaming'] = self.supports_streaming + return ret + + +class InputMediaAnimation(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): + super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.width = width + self.height = height + self.duration = duration + + def to_dict(self): + ret = super(InputMediaAnimation, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.width: + ret['width'] = self.width + if self.height: + ret['height'] = self.height + if self.duration: + ret['duration'] = self.duration + return ret + + +class InputMediaAudio(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): + super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.duration = duration + self.performer = performer + self.title = title + + def to_dict(self): + ret = super(InputMediaAudio, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.duration: + ret['duration'] = self.duration + if self.performer: + ret['performer'] = self.performer + if self.title: + ret['title'] = self.title + return ret + + +class InputMediaDocument(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None): + super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.disable_content_type_detection = disable_content_type_detection + + def to_dict(self): + ret = super(InputMediaDocument, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.disable_content_type_detection is not None: + ret['disable_content_type_detection'] = self.disable_content_type_detection + return ret + + +class PollOption(JsonDeserializable): + @classmethod + 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) + + def __init__(self, text, voter_count = 0, **kwargs): + self.text: str = text + self.voter_count: int = voter_count + # Converted in _convert_poll_options + # def to_json(self): + # # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll + # return json.dumps(self.text) + + +class Poll(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['poll_id'] = obj.pop('id') + options = [] + for opt in obj['options']: + options.append(PollOption.de_json(opt)) + obj['options'] = options or None + if 'explanation_entities' in obj: + obj['explanation_entities'] = Message.parse_entities(obj['explanation_entities']) + return cls(**obj) + + def __init__( + self, + question, options, + poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, + allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, + open_period=None, close_date=None, **kwargs): + self.id: str = poll_id + self.question: str = question + self.options: List[PollOption] = options + self.total_voter_count: int = total_voter_count + self.is_closed: bool = is_closed + self.is_anonymous: bool = is_anonymous + self.type: str = poll_type + self.allows_multiple_answers: bool = allows_multiple_answers + self.correct_option_id: int = correct_option_id + self.explanation: str = explanation + self.explanation_entities: List[MessageEntity] = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] + self.open_period: int = open_period + self.close_date: int = close_date + + def add(self, option): + if type(option) is PollOption: + self.options.append(option) + else: + self.options.append(PollOption(option)) + + +class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, poll_id, user, option_ids, **kwargs): + self.poll_id: str = poll_id + self.user: User = user + self.option_ids: List[int] = option_ids + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'poll_id': self.poll_id, + 'user': self.user.to_dict(), + 'option_ids': self.option_ids} + + +class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return json_string + obj = cls.check_json(json_string) + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, location, address, **kwargs): + self.location: Location = location + self.address: str = address + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return { + "location": self.location.to_dict(), + "address": self.address + } + + +class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['creator'] = User.de_json(obj['creator']) + return cls(**obj) + + def __init__(self, invite_link, creator, creates_join_request , is_primary, is_revoked, + name=None, expire_date=None, member_limit=None, pending_join_request_count=None, **kwargs): + self.invite_link: str = invite_link + self.creator: User = creator + self.creates_join_request: bool = creates_join_request + self.is_primary: bool = is_primary + self.is_revoked: bool = is_revoked + self.name: str = name + self.expire_date: int = expire_date + self.member_limit: int = member_limit + self.pending_join_request_count: int = pending_join_request_count + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = { + "invite_link": self.invite_link, + "creator": self.creator.to_dict(), + "is_primary": self.is_primary, + "is_revoked": self.is_revoked, + "creates_join_request": self.creates_join_request + } + if self.expire_date: + json_dict["expire_date"] = self.expire_date + if self.member_limit: + json_dict["member_limit"] = self.member_limit + if self.pending_join_request_count: + json_dict["pending_join_request_count"] = self.pending_join_request_count + if self.name: + json_dict["name"] = self.name + return json_dict + + +class ProximityAlertTriggered(JsonDeserializable): + @classmethod + 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) + + def __init__(self, traveler, watcher, distance, **kwargs): + self.traveler: User = traveler + self.watcher: User = watcher + self.distance: int = distance + + +class VoiceChatStarted(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + return cls() + + def __init__(self): + """ + This object represents a service message about a voice chat started in the chat. + Currently holds no information. + """ + pass + + +class VoiceChatScheduled(JsonDeserializable): + @classmethod + 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) + + def __init__(self, start_date, **kwargs): + self.start_date: int = start_date + + +class VoiceChatEnded(JsonDeserializable): + @classmethod + 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) + + def __init__(self, duration, **kwargs): + self.duration: int = duration + + +class VoiceChatParticipantsInvited(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'users' in obj: + obj['users'] = [User.de_json(u) for u in obj['users']] + return cls(**obj) + + def __init__(self, users=None, **kwargs): + self.users: List[User] = users + + +class MessageAutoDeleteTimerChanged(JsonDeserializable): + @classmethod + 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) + + def __init__(self, message_auto_delete_time, **kwargs): + self.message_auto_delete_time = message_auto_delete_time From b3b318fd2872ca2fc844d43096a0e9ef059b7871 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 31 Dec 2021 15:14:42 +0400 Subject: [PATCH 0803/1808] Delete asyncio_types.py --- telebot/asyncio_types.py | 2885 -------------------------------------- 1 file changed, 2885 deletions(-) delete mode 100644 telebot/asyncio_types.py diff --git a/telebot/asyncio_types.py b/telebot/asyncio_types.py deleted file mode 100644 index eb3fabffd..000000000 --- a/telebot/asyncio_types.py +++ /dev/null @@ -1,2885 +0,0 @@ -# -*- coding: utf-8 -*- - -import logging -from typing import Dict, List, Optional, Union -from abc import ABC - -try: - import ujson as json -except ImportError: - import json - -from telebot import util - -DISABLE_KEYLEN_ERROR = False - -logger = logging.getLogger('TeleBot') - - -class JsonSerializable(object): - """ - Subclasses of this class are guaranteed to be able to be converted to JSON format. - All subclasses of this class must override to_json. - """ - - def to_json(self): - """ - Returns a JSON string representation of this class. - - This function must be overridden by subclasses. - :return: a JSON formatted string. - """ - raise NotImplementedError - - -class Dictionaryable(object): - """ - Subclasses of this class are guaranteed to be able to be converted to dictionary. - All subclasses of this class must override to_dict. - """ - - def to_dict(self): - """ - Returns a DICT with class field values - - This function must be overridden by subclasses. - :return: a DICT - """ - raise NotImplementedError - - -class JsonDeserializable(object): - """ - Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string. - All subclasses of this class must override de_json. - """ - - @classmethod - def de_json(cls, json_string): - """ - Returns an instance of this class from the given json dict or string. - - This function must be overridden by subclasses. - :return: an instance of this class created from the given json dict or string. - """ - raise NotImplementedError - - @staticmethod - def check_json(json_type, dict_copy = True): - """ - Checks whether json_type is a dict or a string. If it is already a dict, it is returned as-is. - If it is not, it is converted to a dict by means of json.loads(json_type) - :param json_type: input json or parsed dict - :param dict_copy: if dict is passed and it is changed outside - should be True! - :return: Dictionary parsed from json or original dict - """ - if util.is_dict(json_type): - return json_type.copy() if dict_copy else json_type - elif util.is_string(json_type): - return json.loads(json_type) - else: - raise ValueError("json_type should be a json dict or string.") - - def __str__(self): - d = { - x: y.__dict__ if hasattr(y, '__dict__') else y - for x, y in self.__dict__.items() - } - return str(d) - - -class Update(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string, dict_copy=False) - update_id = obj['update_id'] - message = Message.de_json(obj.get('message')) - edited_message = Message.de_json(obj.get('edited_message')) - channel_post = Message.de_json(obj.get('channel_post')) - edited_channel_post = Message.de_json(obj.get('edited_channel_post')) - inline_query = InlineQuery.de_json(obj.get('inline_query')) - chosen_inline_result = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) - callback_query = CallbackQuery.de_json(obj.get('callback_query')) - shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) - pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) - poll = Poll.de_json(obj.get('poll')) - poll_answer = PollAnswer.de_json(obj.get('poll_answer')) - my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member')) - chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) - chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) - 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) - - 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): - self.update_id = update_id - self.message = message - self.edited_message = edited_message - self.channel_post = channel_post - self.edited_channel_post = edited_channel_post - self.inline_query = inline_query - self.chosen_inline_result = chosen_inline_result - self.callback_query = callback_query - self.shipping_query = shipping_query - self.pre_checkout_query = pre_checkout_query - self.poll = poll - self.poll_answer = poll_answer - self.my_chat_member = my_chat_member - self.chat_member = chat_member - self.chat_join_request = chat_join_request - - -class ChatMemberUpdated(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['chat'] = Chat.de_json(obj['chat']) - obj['from_user'] = User.de_json(obj.pop('from')) - obj['old_chat_member'] = ChatMember.de_json(obj['old_chat_member']) - obj['new_chat_member'] = ChatMember.de_json(obj['new_chat_member']) - 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, **kwargs): - self.chat: Chat = chat - self.from_user: User = from_user - self.date: int = date - self.old_chat_member: ChatMember = old_chat_member - self.new_chat_member: ChatMember = new_chat_member - self.invite_link: Optional[ChatInviteLink] = invite_link - - @property - def difference(self) -> Dict[str, List]: - """ - Get the difference between `old_chat_member` and `new_chat_member` - as a dict in the following format {'parameter': [old_value, new_value]} - E.g {'status': ['member', 'kicked'], 'until_date': [None, 1625055092]} - """ - old: Dict = self.old_chat_member.__dict__ - new: Dict = self.new_chat_member.__dict__ - dif = {} - for key in new: - if key == 'user': continue - if new[key] != old[key]: - dif[key] = [old[key], new[key]] - return dif - -class ChatJoinRequest(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['chat'] = Chat.de_json(obj['chat']) - obj['from_user'] = User.de_json(obj['from']) - obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link')) - return cls(**obj) - - def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs): - self.chat = chat - self.from_user = from_user - self.date = date - self.bio = bio - self.invite_link = invite_link - -class WebhookInfo(JsonDeserializable): - @classmethod - 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) - - def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, - last_error_date=None, last_error_message=None, max_connections=None, - allowed_updates=None, **kwargs): - self.url = url - self.has_custom_certificate = has_custom_certificate - self.pending_update_count = pending_update_count - self.ip_address = ip_address - self.last_error_date = last_error_date - self.last_error_message = last_error_message - self.max_connections = max_connections - self.allowed_updates = allowed_updates - - -class User(JsonDeserializable, Dictionaryable, JsonSerializable): - @classmethod - 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) - - 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, **kwargs): - self.id: int = id - self.is_bot: bool = is_bot - self.first_name: str = first_name - self.username: str = username - self.last_name: str = last_name - self.language_code: str = language_code - self.can_join_groups: bool = can_join_groups - self.can_read_all_group_messages: bool = can_read_all_group_messages - self.supports_inline_queries: bool = supports_inline_queries - - @property - def full_name(self): - full_name = self.first_name - if self.last_name: - full_name += ' {0}'.format(self.last_name) - return full_name - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'id': self.id, - 'is_bot': self.is_bot, - 'first_name': self.first_name, - 'last_name': self.last_name, - 'username': self.username, - 'language_code': self.language_code, - 'can_join_groups': self.can_join_groups, - 'can_read_all_group_messages': self.can_read_all_group_messages, - 'supports_inline_queries': self.supports_inline_queries} - - -class GroupChat(JsonDeserializable): - @classmethod - 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) - - def __init__(self, id, title, **kwargs): - self.id: int = id - self.title: str = title - - -class Chat(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'photo' in obj: - obj['photo'] = ChatPhoto.de_json(obj['photo']) - if 'pinned_message' in obj: - obj['pinned_message'] = Message.de_json(obj['pinned_message']) - if 'permissions' in obj: - obj['permissions'] = ChatPermissions.de_json(obj['permissions']) - if 'location' in obj: - obj['location'] = ChatLocation.de_json(obj['location']) - return cls(**obj) - - def __init__(self, id, type, title=None, username=None, first_name=None, - last_name=None, photo=None, bio=None, has_private_forwards=None, - description=None, invite_link=None, pinned_message=None, - permissions=None, slow_mode_delay=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, **kwargs): - self.id: int = id - self.type: str = type - self.title: str = title - self.username: str = username - self.first_name: str = first_name - self.last_name: str = last_name - self.photo: ChatPhoto = photo - self.bio: str = bio - self.has_private_forwards: bool = has_private_forwards - self.description: str = description - self.invite_link: str = invite_link - self.pinned_message: Message = pinned_message - self.permissions: ChatPermissions = permissions - self.slow_mode_delay: int = slow_mode_delay - self.message_auto_delete_time: int = message_auto_delete_time - self.has_protected_content: bool = has_protected_content - self.sticker_set_name: str = sticker_set_name - self.can_set_sticker_set: bool = can_set_sticker_set - self.linked_chat_id: int = linked_chat_id - self.location: ChatLocation = location - - -class MessageID(JsonDeserializable): - @classmethod - 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) - - def __init__(self, message_id, **kwargs): - self.message_id = message_id - - -class Message(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string, dict_copy=False) - message_id = obj['message_id'] - from_user = User.de_json(obj.get('from')) - date = obj['date'] - 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: - opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) - if 'forward_from_message_id' in obj: - opts['forward_from_message_id'] = obj.get('forward_from_message_id') - if 'forward_signature' in obj: - opts['forward_signature'] = obj.get('forward_signature') - if 'forward_sender_name' in obj: - opts['forward_sender_name'] = obj.get('forward_sender_name') - if 'forward_date' in obj: - opts['forward_date'] = obj.get('forward_date') - if 'is_automatic_forward' in obj: - opts['is_automatic_forward'] = obj.get('is_automatic_forward') - if 'reply_to_message' in obj: - opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) - if 'via_bot' in obj: - opts['via_bot'] = User.de_json(obj['via_bot']) - if 'edit_date' in obj: - opts['edit_date'] = obj.get('edit_date') - if 'has_protected_content' in obj: - opts['has_protected_content'] = obj.get('has_protected_content') - if 'media_group_id' in obj: - opts['media_group_id'] = obj.get('media_group_id') - if 'author_signature' in obj: - opts['author_signature'] = obj.get('author_signature') - if 'text' in obj: - opts['text'] = obj['text'] - content_type = 'text' - if 'entities' in obj: - opts['entities'] = Message.parse_entities(obj['entities']) - if 'caption_entities' in obj: - opts['caption_entities'] = Message.parse_entities(obj['caption_entities']) - if 'audio' in obj: - opts['audio'] = Audio.de_json(obj['audio']) - content_type = 'audio' - if 'document' in obj: - opts['document'] = Document.de_json(obj['document']) - content_type = 'document' - if 'animation' in obj: - # Document content type accompanies "animation", - # so "animation" should be checked below "document" to override it - opts['animation'] = Animation.de_json(obj['animation']) - content_type = 'animation' - if 'game' in obj: - opts['game'] = Game.de_json(obj['game']) - content_type = 'game' - if 'photo' in obj: - opts['photo'] = Message.parse_photo(obj['photo']) - content_type = 'photo' - if 'sticker' in obj: - opts['sticker'] = Sticker.de_json(obj['sticker']) - content_type = 'sticker' - if 'video' in obj: - opts['video'] = Video.de_json(obj['video']) - content_type = 'video' - if 'video_note' in obj: - opts['video_note'] = VideoNote.de_json(obj['video_note']) - content_type = 'video_note' - if 'voice' in obj: - opts['voice'] = Audio.de_json(obj['voice']) - content_type = 'voice' - if 'caption' in obj: - opts['caption'] = obj['caption'] - if 'contact' in obj: - opts['contact'] = Contact.de_json(json.dumps(obj['contact'])) - content_type = 'contact' - if 'location' in obj: - opts['location'] = Location.de_json(obj['location']) - content_type = 'location' - if 'venue' in obj: - opts['venue'] = Venue.de_json(obj['venue']) - content_type = 'venue' - if 'dice' in obj: - opts['dice'] = Dice.de_json(obj['dice']) - content_type = 'dice' - if 'new_chat_members' in obj: - new_chat_members = [] - for member in obj['new_chat_members']: - new_chat_members.append(User.de_json(member)) - opts['new_chat_members'] = new_chat_members - content_type = 'new_chat_members' - if 'left_chat_member' in obj: - opts['left_chat_member'] = User.de_json(obj['left_chat_member']) - content_type = 'left_chat_member' - if 'new_chat_title' in obj: - opts['new_chat_title'] = obj['new_chat_title'] - content_type = 'new_chat_title' - if 'new_chat_photo' in obj: - opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo']) - content_type = 'new_chat_photo' - if 'delete_chat_photo' in obj: - opts['delete_chat_photo'] = obj['delete_chat_photo'] - content_type = 'delete_chat_photo' - if 'group_chat_created' in obj: - opts['group_chat_created'] = obj['group_chat_created'] - content_type = 'group_chat_created' - if 'supergroup_chat_created' in obj: - opts['supergroup_chat_created'] = obj['supergroup_chat_created'] - content_type = 'supergroup_chat_created' - if 'channel_chat_created' in obj: - opts['channel_chat_created'] = obj['channel_chat_created'] - content_type = 'channel_chat_created' - if 'migrate_to_chat_id' in obj: - opts['migrate_to_chat_id'] = obj['migrate_to_chat_id'] - content_type = 'migrate_to_chat_id' - if 'migrate_from_chat_id' in obj: - opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] - content_type = 'migrate_from_chat_id' - if 'pinned_message' in obj: - opts['pinned_message'] = Message.de_json(obj['pinned_message']) - content_type = 'pinned_message' - if 'invoice' in obj: - opts['invoice'] = Invoice.de_json(obj['invoice']) - content_type = 'invoice' - if 'successful_payment' in obj: - opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment']) - content_type = 'successful_payment' - if 'connected_website' in obj: - opts['connected_website'] = obj['connected_website'] - content_type = 'connected_website' - if 'poll' in obj: - opts['poll'] = Poll.de_json(obj['poll']) - content_type = 'poll' - if 'passport_data' in obj: - opts['passport_data'] = obj['passport_data'] - content_type = 'passport_data' - if 'proximity_alert_triggered' in obj: - opts['proximity_alert_triggered'] = ProximityAlertTriggered.de_json(obj[ - 'proximity_alert_triggered']) - content_type = 'proximity_alert_triggered' - if 'voice_chat_scheduled' in obj: - opts['voice_chat_scheduled'] = VoiceChatScheduled.de_json(obj['voice_chat_scheduled']) - content_type = 'voice_chat_scheduled' - if 'voice_chat_started' in obj: - opts['voice_chat_started'] = VoiceChatStarted.de_json(obj['voice_chat_started']) - content_type = 'voice_chat_started' - if 'voice_chat_ended' in obj: - opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) - content_type = 'voice_chat_ended' - if 'voice_chat_participants_invited' in obj: - opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['voice_chat_participants_invited']) - content_type = 'voice_chat_participants_invited' - if 'message_auto_delete_timer_changed' in obj: - opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(obj['message_auto_delete_timer_changed']) - content_type = 'message_auto_delete_timer_changed' - if 'reply_markup' in obj: - opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) - return cls(message_id, from_user, date, chat, content_type, opts, json_string) - - @classmethod - def parse_chat(cls, chat): - if 'first_name' not in chat: - return GroupChat.de_json(chat) - else: - return User.de_json(chat) - - @classmethod - def parse_photo(cls, photo_size_array): - ret = [] - for ps in photo_size_array: - ret.append(PhotoSize.de_json(ps)) - return ret - - @classmethod - def parse_entities(cls, message_entity_array): - ret = [] - for me in message_entity_array: - ret.append(MessageEntity.de_json(me)) - return ret - - def __init__(self, message_id, from_user, date, chat, content_type, options, json_string): - self.content_type: str = content_type - self.id: int = message_id # Lets fix the telegram usability ####up with ID in Message :) - self.message_id: int = message_id - 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 - self.forward_signature: Optional[str] = None - self.forward_sender_name: Optional[str] = None - self.forward_date: Optional[int] = None - self.is_automatic_forward: Optional[bool] = None - self.reply_to_message: Optional[Message] = None - self.via_bot: Optional[User] = None - self.edit_date: Optional[int] = None - self.has_protected_content: Optional[bool] = None - self.media_group_id: Optional[str] = None - self.author_signature: Optional[str] = None - self.text: Optional[str] = None - self.entities: Optional[List[MessageEntity]] = None - self.caption_entities: Optional[List[MessageEntity]] = None - self.audio: Optional[Audio] = None - self.document: Optional[Document] = None - self.photo: Optional[List[PhotoSize]] = None - self.sticker: Optional[Sticker] = None - self.video: Optional[Video] = None - self.video_note: Optional[VideoNote] = None - self.voice: Optional[Voice] = None - self.caption: Optional[str] = None - self.contact: Optional[Contact] = None - self.location: Optional[Location] = None - self.venue: Optional[Venue] = None - self.animation: Optional[Animation] = None - self.dice: Optional[Dice] = None - self.new_chat_member: Optional[User] = None # Deprecated since Bot API 3.0. Not processed anymore - self.new_chat_members: Optional[List[User]] = None - self.left_chat_member: Optional[User] = None - self.new_chat_title: Optional[str] = None - self.new_chat_photo: Optional[List[PhotoSize]] = None - self.delete_chat_photo: Optional[bool] = None - self.group_chat_created: Optional[bool] = None - self.supergroup_chat_created: Optional[bool] = None - self.channel_chat_created: Optional[bool] = None - self.migrate_to_chat_id: Optional[int] = None - self.migrate_from_chat_id: Optional[int] = None - self.pinned_message: Optional[Message] = None - self.invoice: Optional[Invoice] = None - self.successful_payment: Optional[SuccessfulPayment] = None - self.connected_website: Optional[str] = None - self.reply_markup: Optional[InlineKeyboardMarkup] = None - for key in options: - setattr(self, key, options[key]) - self.json = json_string - - def __html_text(self, text, entities): - """ - Author: @sviat9440 - Updaters: @badiboy - Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" - - Example: - message.html_text - >> "Test parse formatting, url, text_mention and mention @username" - - Custom subs: - You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. - Example: - message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} - message.html_text - >> "Test parse formatting, url and text_mention and mention @username" - """ - - if not entities: - return text - - _subs = { - "bold": "{text}", - "italic": "{text}", - "pre": "
{text}
", - "code": "{text}", - # "url": "{text}", # @badiboy plain URLs have no text and do not need tags - "text_link": "{text}", - "strikethrough": "{text}", - "underline": "{text}" - } - - if hasattr(self, "custom_subs"): - for key, value in self.custom_subs.items(): - _subs[key] = value - utf16_text = text.encode("utf-16-le") - html_text = "" - - def func(upd_text, subst_type=None, url=None, user=None): - upd_text = upd_text.decode("utf-16-le") - if subst_type == "text_mention": - subst_type = "text_link" - url = "tg://user?id={0}".format(user.id) - elif subst_type == "mention": - url = "https://t.me/{0}".format(upd_text[1:]) - upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") - if not subst_type or not _subs.get(subst_type): - return upd_text - subs = _subs.get(subst_type) - return subs.format(text=upd_text, url=url) - - offset = 0 - for entity in entities: - if entity.offset > offset: - html_text += func(utf16_text[offset * 2 : entity.offset * 2]) - offset = entity.offset - html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) - offset += entity.length - elif entity.offset == offset: - html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) - offset += entity.length - else: - # TODO: process nested entities from Bot API 4.5 - # Now ignoring them - pass - if offset * 2 < len(utf16_text): - html_text += func(utf16_text[offset * 2:]) - return html_text - - @property - def html_text(self): - return self.__html_text(self.text, self.entities) - - @property - def html_caption(self): - return self.__html_text(self.caption, self.caption_entities) - - -class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): - @staticmethod - def to_list_of_dicts(entity_list) -> Union[List[Dict], None]: - res = [] - for e in entity_list: - res.append(MessageEntity.to_dict(e)) - return res or None - - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'user' in obj: - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs): - self.type: str = type - self.offset: int = offset - self.length: int = length - self.url: str = url - self.user: User = user - self.language: str = language - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {"type": self.type, - "offset": self.offset, - "length": self.length, - "url": self.url, - "user": self.user, - "language": self.language} - - -class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): - @classmethod - 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) - - def __init__(self, value, emoji, **kwargs): - self.value: int = value - self.emoji: str = emoji - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'value': self.value, - 'emoji': self.emoji} - - -class PhotoSize(JsonDeserializable): - @classmethod - 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) - - def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.file_size: int = file_size - - -class Audio(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, - file_size=None, thumb=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.duration: int = duration - self.performer: str = performer - self.title: str = title - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - self.thumb: PhotoSize = thumb - - -class Voice(JsonDeserializable): - @classmethod - 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) - - def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.duration: int = duration - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class Document(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.thumb: PhotoSize = thumb - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class Video(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - return cls(**obj) - - def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.duration: int = duration - self.thumb: PhotoSize = thumb - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class VideoNote(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - return cls(**obj) - - def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.length: int = length - self.duration: int = duration - self.thumb: PhotoSize = thumb - self.file_size: int = file_size - - -class Contact(JsonDeserializable): - @classmethod - 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) - - def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None, **kwargs): - self.phone_number: str = phone_number - self.first_name: str = first_name - self.last_name: str = last_name - self.user_id: int = user_id - self.vcard: str = vcard - - -class Location(JsonDeserializable, JsonSerializable, Dictionaryable): - @classmethod - 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) - - def __init__(self, longitude, latitude, horizontal_accuracy=None, - live_period=None, heading=None, proximity_alert_radius=None, **kwargs): - self.longitude: float = longitude - self.latitude: float = latitude - self.horizontal_accuracy: float = horizontal_accuracy - self.live_period: int = live_period - self.heading: int = heading - self.proximity_alert_radius: int = proximity_alert_radius - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return { - "longitude": self.longitude, - "latitude": self.latitude, - "horizontal_accuracy": self.horizontal_accuracy, - "live_period": self.live_period, - "heading": self.heading, - "proximity_alert_radius": self.proximity_alert_radius, - } - - -class Venue(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, - google_place_id=None, google_place_type=None, **kwargs): - self.location: Location = location - self.title: str = title - self.address: str = address - self.foursquare_id: str = foursquare_id - self.foursquare_type: str = foursquare_type - self.google_place_id: str = google_place_id - self.google_place_type: str = google_place_type - - -class UserProfilePhotos(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'photos' in obj: - photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] - obj['photos'] = photos - return cls(**obj) - - def __init__(self, total_count, photos=None, **kwargs): - self.total_count: int = total_count - self.photos: List[PhotoSize] = photos - - -class File(JsonDeserializable): - @classmethod - 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) - - def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.file_size: int = file_size - self.file_path: str = file_path - - -class ForceReply(JsonSerializable): - def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Optional[str]=None): - self.selective: bool = selective - self.input_field_placeholder: str = input_field_placeholder - - def to_json(self): - json_dict = {'force_reply': True} - if self.selective is not None: - json_dict['selective'] = self.selective - if self.input_field_placeholder: - json_dict['input_field_placeholder'] = self.input_field_placeholder - return json.dumps(json_dict) - - -class ReplyKeyboardRemove(JsonSerializable): - def __init__(self, selective=None): - self.selective: bool = selective - - def to_json(self): - json_dict = {'remove_keyboard': True} - if self.selective: - json_dict['selective'] = self.selective - return json.dumps(json_dict) - - -class ReplyKeyboardMarkup(JsonSerializable): - max_row_keys = 12 - - def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, - selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None): - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - if not DISABLE_KEYLEN_ERROR: - logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - self.resize_keyboard: bool = resize_keyboard - self.one_time_keyboard: bool = one_time_keyboard - self.selective: bool = selective - self.row_width: int = row_width - self.input_field_placeholder: str = input_field_placeholder - self.keyboard: List[List[KeyboardButton]] = [] - - def add(self, *args, row_width=None): - """ - This function adds strings to the keyboard, while not exceeding row_width. - E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} - when row_width is set to 1. - When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} - See https://core.telegram.org/bots/api#replykeyboardmarkup - :param args: KeyboardButton to append to the keyboard - :param row_width: width of row - :return: self, to allow function chaining. - """ - if row_width is None: - row_width = self.row_width - - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - if not DISABLE_KEYLEN_ERROR: - logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - for row in util.chunks(args, row_width): - button_array = [] - for button in row: - if util.is_string(button): - button_array.append({'text': button}) - elif util.is_bytes(button): - button_array.append({'text': button.decode('utf-8')}) - else: - button_array.append(button.to_dict()) - self.keyboard.append(button_array) - - return self - - def row(self, *args): - """ - Adds a list of KeyboardButton to the keyboard. This function does not consider row_width. - ReplyKeyboardMarkup#row("A")#row("B", "C")#to_json() outputs '{keyboard: [["A"], ["B", "C"]]}' - See https://core.telegram.org/bots/api#replykeyboardmarkup - :param args: strings - :return: self, to allow function chaining. - """ - - return self.add(*args, row_width=self.max_row_keys) - - def to_json(self): - """ - Converts this object to its json representation following the Telegram API guidelines described here: - https://core.telegram.org/bots/api#replykeyboardmarkup - :return: - """ - json_dict = {'keyboard': self.keyboard} - if self.one_time_keyboard is not None: - json_dict['one_time_keyboard'] = self.one_time_keyboard - if self.resize_keyboard is not None: - json_dict['resize_keyboard'] = self.resize_keyboard - if self.selective is not None: - json_dict['selective'] = self.selective - if self.input_field_placeholder: - json_dict['input_field_placeholder'] = self.input_field_placeholder - 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[KeyboardButtonPollType]=None): - self.text: str = text - self.request_contact: bool = request_contact - self.request_location: bool = request_location - self.request_poll: KeyboardButtonPollType = request_poll - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'text': self.text} - if self.request_contact is not None: - json_dict['request_contact'] = self.request_contact - if self.request_location is not None: - json_dict['request_location'] = self.request_location - if self.request_poll is not None: - json_dict['request_poll'] = self.request_poll.to_dict() - return json_dict - - -class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable): - max_row_keys = 8 - - @classmethod - def de_json(cls, json_string): - 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 = keyboard) - - def __init__(self, keyboard=None, row_width=3): - """ - This object represents an inline keyboard that appears - right next to the message it belongs to. - - :return: - """ - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - self.row_width: int = row_width - self.keyboard: List[List[InlineKeyboardButton]] = keyboard or [] - - def add(self, *args, row_width=None): - """ - This method adds buttons to the keyboard without exceeding row_width. - - E.g. InlineKeyboardMarkup.add("A", "B", "C") yields the json result: - {keyboard: [["A"], ["B"], ["C"]]} - when row_width is set to 1. - When row_width is set to 2, the result: - {keyboard: [["A", "B"], ["C"]]} - See https://core.telegram.org/bots/api#inlinekeyboardmarkup - - :param args: Array of InlineKeyboardButton to append to the keyboard - :param row_width: width of row - :return: self, to allow function chaining. - """ - if row_width is None: - row_width = self.row_width - - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - for row in util.chunks(args, row_width): - button_array = [button for button in row] - self.keyboard.append(button_array) - - return self - - def row(self, *args): - """ - Adds a list of InlineKeyboardButton to the keyboard. - This method does not consider row_width. - - InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: - '{keyboard: [["A"], ["B", "C"]]}' - See https://core.telegram.org/bots/api#inlinekeyboardmarkup - - :param args: Array of InlineKeyboardButton to append to the keyboard - :return: self, to allow function chaining. - """ - - return self.add(*args, row_width=self.max_row_keys) - - def to_json(self): - """ - Converts this object to its json representation - following the Telegram API guidelines described here: - https://core.telegram.org/bots/api#inlinekeyboardmarkup - :return: - """ - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = dict() - json_dict['inline_keyboard'] = [[button.to_dict() for button in row] for row in self.keyboard] - return json_dict - - -class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'login_url' in obj: - obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) - return cls(**obj) - - def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, - switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): - self.text: str = text - self.url: str = url - self.callback_data: str = callback_data - self.switch_inline_query: str = switch_inline_query - self.switch_inline_query_current_chat: str = switch_inline_query_current_chat - self.callback_game = callback_game # Not Implemented - self.pay: bool = pay - self.login_url: LoginUrl = login_url - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'text': self.text} - if self.url: - json_dict['url'] = self.url - if self.callback_data: - json_dict['callback_data'] = self.callback_data - if self.switch_inline_query is not None: - json_dict['switch_inline_query'] = self.switch_inline_query - if self.switch_inline_query_current_chat is not None: - json_dict['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat - if self.callback_game is not None: - json_dict['callback_game'] = self.callback_game - if self.pay is not None: - json_dict['pay'] = self.pay - if self.login_url is not None: - json_dict['login_url'] = self.login_url.to_dict() - return json_dict - - -class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): - @classmethod - 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) - - def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None, **kwargs): - self.url: str = url - self.forward_text: str = forward_text - self.bot_username: str = bot_username - self.request_write_access: bool = request_write_access - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'url': self.url} - if self.forward_text: - json_dict['forward_text'] = self.forward_text - if self.bot_username: - json_dict['bot_username'] = self.bot_username - if self.request_write_access is not None: - json_dict['request_write_access'] = self.request_write_access - return json_dict - - -class CallbackQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if not "data" in obj: - # "data" field is Optional in the API, but historically is mandatory in the class constructor - obj['data'] = None - obj['from_user'] = User.de_json(obj.pop('from')) - if 'message' in obj: - obj['message'] = Message.de_json(obj.get('message')) - return cls(**obj) - - def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None, **kwargs): - self.id: int = id - self.from_user: User = from_user - self.message: Message = message - self.inline_message_id: str = inline_message_id - self.chat_instance: str = chat_instance - self.data: str = data - self.game_short_name: str = game_short_name - - -class ChatPhoto(JsonDeserializable): - @classmethod - 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) - - def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id, **kwargs): - self.small_file_id: str = small_file_id - self.small_file_unique_id: str = small_file_unique_id - self.big_file_id: str = big_file_id - self.big_file_unique_id: str = big_file_unique_id - - -class ChatMember(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, - can_post_messages=None, can_edit_messages=None, can_delete_messages=None, - can_restrict_members=None, can_promote_members=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, is_member=None, - can_send_messages=None, can_send_media_messages=None, can_send_polls=None, - can_send_other_messages=None, can_add_web_page_previews=None, - can_manage_chat=None, can_manage_voice_chats=None, - until_date=None, **kwargs): - self.user: User = user - self.status: str = status - self.custom_title: str = custom_title - self.is_anonymous: bool = is_anonymous - self.can_be_edited: bool = can_be_edited - self.can_post_messages: bool = can_post_messages - self.can_edit_messages: bool = can_edit_messages - self.can_delete_messages: bool = can_delete_messages - self.can_restrict_members: bool = can_restrict_members - self.can_promote_members: bool = can_promote_members - self.can_change_info: bool = can_change_info - self.can_invite_users: bool = can_invite_users - self.can_pin_messages: bool = can_pin_messages - self.is_member: bool = is_member - self.can_send_messages: bool = can_send_messages - self.can_send_media_messages: bool = can_send_media_messages - self.can_send_polls: bool = can_send_polls - self.can_send_other_messages: bool = can_send_other_messages - self.can_add_web_page_previews: bool = can_add_web_page_previews - self.can_manage_chat: bool = can_manage_chat - self.can_manage_voice_chats: bool = can_manage_voice_chats - self.until_date: int = until_date - - -class ChatMemberOwner(ChatMember): - pass - -class ChatMemberAdministrator(ChatMember): - pass - - -class ChatMemberMember(ChatMember): - pass - - -class ChatMemberRestricted(ChatMember): - pass - - -class ChatMemberLeft(ChatMember): - pass - - -class ChatMemberBanned(ChatMember): - pass - - -class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return json_string - obj = cls.check_json(json_string, dict_copy=False) - return cls(**obj) - - def __init__(self, can_send_messages=None, can_send_media_messages=None, - can_send_polls=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, **kwargs): - self.can_send_messages: bool = can_send_messages - self.can_send_media_messages: bool = can_send_media_messages - self.can_send_polls: bool = can_send_polls - self.can_send_other_messages: bool = can_send_other_messages - self.can_add_web_page_previews: bool = can_add_web_page_previews - self.can_change_info: bool = can_change_info - self.can_invite_users: bool = can_invite_users - self.can_pin_messages: bool = can_pin_messages - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = dict() - if self.can_send_messages is not None: - json_dict['can_send_messages'] = self.can_send_messages - if self.can_send_media_messages is not None: - json_dict['can_send_media_messages'] = self.can_send_media_messages - if self.can_send_polls is not None: - json_dict['can_send_polls'] = self.can_send_polls - if self.can_send_other_messages is not None: - json_dict['can_send_other_messages'] = self.can_send_other_messages - if self.can_add_web_page_previews is not None: - json_dict['can_add_web_page_previews'] = self.can_add_web_page_previews - if self.can_change_info is not None: - json_dict['can_change_info'] = self.can_change_info - if self.can_invite_users is not None: - json_dict['can_invite_users'] = self.can_invite_users - if self.can_pin_messages is not None: - json_dict['can_pin_messages'] = self.can_pin_messages - return json_dict - - -class BotCommand(JsonSerializable, JsonDeserializable): - @classmethod - 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) - - def __init__(self, command, description): - """ - This object represents a bot command. - :param command: Text of the command, 1-32 characters. - Can contain only lowercase English letters, digits and underscores. - :param description: Description of the command, 3-256 characters. - :return: - """ - self.command: str = command - self.description: str = description - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'command': self.command, 'description': self.description} - - -# BotCommandScopes - -class BotCommandScope(ABC, JsonSerializable): - def __init__(self, type='default', chat_id=None, user_id=None): - """ - Abstract class. - Use BotCommandScopeX classes to set a specific scope type: - BotCommandScopeDefault - BotCommandScopeAllPrivateChats - BotCommandScopeAllGroupChats - BotCommandScopeAllChatAdministrators - BotCommandScopeChat - BotCommandScopeChatAdministrators - BotCommandScopeChatMember - """ - self.type: str = type - self.chat_id: Optional[Union[int, str]] = chat_id - self.user_id: Optional[Union[int, str]] = user_id - - def to_json(self): - json_dict = {'type': self.type} - if self.chat_id: - json_dict['chat_id'] = self.chat_id - if self.user_id: - json_dict['user_id'] = self.user_id - return json.dumps(json_dict) - - -class BotCommandScopeDefault(BotCommandScope): - def __init__(self): - """ - Represents the default scope of bot commands. - Default commands are used if no commands with a narrower scope are specified for the user. - """ - super(BotCommandScopeDefault, self).__init__(type='default') - - -class BotCommandScopeAllPrivateChats(BotCommandScope): - def __init__(self): - """ - Represents the scope of bot commands, covering all private chats. - """ - super(BotCommandScopeAllPrivateChats, self).__init__(type='all_private_chats') - - -class BotCommandScopeAllGroupChats(BotCommandScope): - def __init__(self): - """ - Represents the scope of bot commands, covering all group and supergroup chats. - """ - super(BotCommandScopeAllGroupChats, self).__init__(type='all_group_chats') - - -class BotCommandScopeAllChatAdministrators(BotCommandScope): - def __init__(self): - """ - Represents the scope of bot commands, covering all group and supergroup chat administrators. - """ - super(BotCommandScopeAllChatAdministrators, self).__init__(type='all_chat_administrators') - - -class BotCommandScopeChat(BotCommandScope): - def __init__(self, chat_id=None): - super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) - - -class BotCommandScopeChatAdministrators(BotCommandScope): - def __init__(self, chat_id=None): - """ - Represents the scope of bot commands, covering a specific chat. - @param chat_id: Unique identifier for the target chat - """ - super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) - - -class BotCommandScopeChatMember(BotCommandScope): - def __init__(self, chat_id=None, user_id=None): - """ - Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat - @param chat_id: Unique identifier for the target chat - @param user_id: Unique identifier of the target user - """ - super(BotCommandScopeChatMember, self).__init__(type='chat_member', chat_id=chat_id, user_id=user_id) - - -# InlineQuery - -class InlineQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - if 'location' in obj: - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, id, from_user, query, offset, chat_type=None, location=None, **kwargs): - """ - This object represents an incoming inline query. - When the user sends an empty query, your bot could - return some default or trending results. - :param id: string Unique identifier for this query - :param from_user: User Sender - :param query: String Text of the query - :param chat_type: String Type of the chat, from which the inline query was sent. - Can be either “sender” for a private chat with the inline query sender, - “private”, “group”, “supergroup”, or “channel”. - :param offset: String Offset of the results to be returned, can be controlled by the bot - :param location: Sender location, only for bots that request user location - :return: InlineQuery Object - """ - self.id: int = id - self.from_user: User = from_user - self.query: str = query - self.offset: str = offset - self.chat_type: str = chat_type - self.location: Location = location - - -class InputTextMessageContent(Dictionaryable): - def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None): - self.message_text: str = message_text - self.parse_mode: str = parse_mode - self.entities: List[MessageEntity] = entities - self.disable_web_page_preview: bool = disable_web_page_preview - - def to_dict(self): - json_dict = {'message_text': self.message_text} - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode - if self.entities: - json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) - if self.disable_web_page_preview is not None: - json_dict['disable_web_page_preview'] = self.disable_web_page_preview - return json_dict - - -class InputLocationMessageContent(Dictionaryable): - def __init__(self, latitude, longitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None): - self.latitude: float = latitude - self.longitude: float = longitude - self.horizontal_accuracy: float = horizontal_accuracy - self.live_period: int = live_period - self.heading: int = heading - self.proximity_alert_radius: int = proximity_alert_radius - - def to_dict(self): - json_dict = {'latitude': self.latitude, '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 - return json_dict - - -class InputVenueMessageContent(Dictionaryable): - def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, - google_place_id=None, google_place_type=None): - self.latitude: float = latitude - self.longitude: float = longitude - self.title: str = title - self.address: str = address - self.foursquare_id: str = foursquare_id - self.foursquare_type: str = foursquare_type - self.google_place_id: str = google_place_id - self.google_place_type: str = google_place_type - - def to_dict(self): - json_dict = { - 'latitude': self.latitude, - 'longitude': self.longitude, - 'title': self.title, - '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.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_dict - - -class InputContactMessageContent(Dictionaryable): - def __init__(self, phone_number, first_name, last_name=None, vcard=None): - self.phone_number: str = phone_number - self.first_name: str = first_name - self.last_name: str = last_name - self.vcard: str = vcard - - def to_dict(self): - json_dict = {'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 - return json_dict - - -class InputInvoiceMessageContent(Dictionaryable): - def __init__(self, title, description, payload, provider_token, currency, prices, - max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, - photo_url=None, photo_size=None, photo_width=None, photo_height=None, - need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, - send_phone_number_to_provider=None, send_email_to_provider=None, - is_flexible=None): - self.title: str = title - self.description: str = description - self.payload: str = payload - self.provider_token: str = provider_token - self.currency: str = currency - self.prices: List[LabeledPrice] = prices - self.max_tip_amount: Optional[int] = max_tip_amount - self.suggested_tip_amounts: Optional[List[int]] = suggested_tip_amounts - self.provider_data: Optional[str] = provider_data - self.photo_url: Optional[str] = photo_url - self.photo_size: Optional[int] = photo_size - self.photo_width: Optional[int] = photo_width - self.photo_height: Optional[int] = photo_height - self.need_name: Optional[bool] = need_name - self.need_phone_number: Optional[bool] = need_phone_number - self.need_email: Optional[bool] = need_email - self.need_shipping_address: Optional[bool] = need_shipping_address - self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider - self.send_email_to_provider: Optional[bool] = send_email_to_provider - self.is_flexible: Optional[bool] = is_flexible - - def to_dict(self): - json_dict = { - 'title': self.title, - 'description': self.description, - 'payload': self.payload, - 'provider_token': self.provider_token, - 'currency': self.currency, - 'prices': [LabeledPrice.to_dict(lp) for lp in self.prices] - } - if self.max_tip_amount: - json_dict['max_tip_amount'] = self.max_tip_amount - if self.suggested_tip_amounts: - json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts - if self.provider_data: - json_dict['provider_data'] = self.provider_data - if self.photo_url: - json_dict['photo_url'] = self.photo_url - if self.photo_size: - json_dict['photo_size'] = self.photo_size - if self.photo_width: - json_dict['photo_width'] = self.photo_width - if self.photo_height: - json_dict['photo_height'] = self.photo_height - if self.need_name is not None: - json_dict['need_name'] = self.need_name - if self.need_phone_number is not None: - json_dict['need_phone_number'] = self.need_phone_number - if self.need_email is not None: - json_dict['need_email'] = self.need_email - if self.need_shipping_address is not None: - json_dict['need_shipping_address'] = self.need_shipping_address - if self.send_phone_number_to_provider is not None: - json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider - if self.send_email_to_provider is not None: - json_dict['send_email_to_provider'] = self.send_email_to_provider - if self.is_flexible is not None: - json_dict['is_flexible'] = self.is_flexible - return json_dict - - -class ChosenInlineResult(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - if 'location' in obj: - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, result_id, from_user, query, location=None, inline_message_id=None, **kwargs): - """ - This object represents a result of an inline query - that was chosen by the user and sent to their chat partner. - :param result_id: string The unique identifier for the result that was chosen. - :param from_user: User The user that chose the result. - :param query: String The query that was used to obtain the result. - :return: ChosenInlineResult Object. - """ - self.result_id: str = result_id - self.from_user: User = from_user - self.location: Location = location - self.inline_message_id: str = inline_message_id - self.query: str = query - - -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. - :param title: Title of the result. - :param input_message_content: InputMessageContent : Content of the message to be sent - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param url: URL of the result. - :param hide_url: Pass True, if you don't want the URL to be shown in the message. - :param description: Short description of the result. - :param thumb_url: Url of the thumbnail for the result. - :param thumb_width: Thumbnail width. - :param thumb_height: Thumbnail height - :return: - """ - 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 - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - def to_dict(self): - json_dict = super().to_dict() - if self.url: - json_dict['url'] = self.url - if self.hide_url: - json_dict['hide_url'] = self.hide_url - if self.description: - json_dict['description'] = self.description - 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 InlineQueryResultPhoto(InlineQueryResultBase): - def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=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 - :param photo_url: A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB - :param thumb_url: URL of the thumbnail for the photo - :param photo_width: Width of the photo. - :param photo_height: Height of the photo. - :param title: Title for the result. - :param description: Short description of the result. - :param caption: Caption of the photo to be sent, 0-200 characters. - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or - inline URLs in the media caption. - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ - 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.description = description - - 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.description: - json_dict['description'] = self.description - return json_dict - - -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, - thumb_mime_type=None): - """ - Represents a link to an animated GIF file. - :param id: Unique identifier for this result, 1-64 bytes. - :param gif_url: A valid URL for the GIF file. File size must not exceed 1MB - :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result. - :param gif_width: Width of the GIF. - :param gif_height: Height of the GIF. - :param title: Title for the result. - :param caption: Caption of the GIF file to be sent, 0-200 characters - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ - 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.gif_duration = gif_duration - self.thumb_mime_type = thumb_mime_type - - 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.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 - if self.thumb_mime_type: - json_dict['thumb_mime_type'] = self.thumb_mime_type - return json_dict - - -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, - thumb_mime_type=None): - """ - Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). - :param id: Unique identifier for this result, 1-64 bytes - :param mpeg4_url: A valid URL for the MP4 file. File size must not exceed 1MB - :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result - :param mpeg4_width: Video width - :param mpeg4_height: Video height - :param title: Title for the result - :param caption: Caption of the MPEG-4 file to be sent, 0-200 characters - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text - or inline URLs in the media caption. - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ - 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.mpeg4_duration = mpeg4_duration - self.thumb_mime_type = thumb_mime_type - - 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 - json_dict['thumb_url'] = self.thumb_url - if self.mpeg4_duration: - json_dict['mpeg4_duration '] = self.mpeg4_duration - if self.thumb_mime_type: - json_dict['thumb_mime_type'] = self.thumb_mime_type - return json_dict - - -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. - :param id: Unique identifier for this result, 1-64 bytes - :param video_url: A valid URL for the embedded video player or video file - :param mime_type: Mime type of the content of video url, “text/html” or “video/mp4” - :param thumb_url: URL of the thumbnail (jpeg only) for the video - :param title: Title for the result - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or - inline URLs in the media caption. - :param video_width: Video width - :param video_height: Video height - :param video_duration: Video duration in seconds - :param description: Short description of the result - :return: - """ - 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.description = description - - 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 - return json_dict - - -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.performer = performer - self.audio_duration = audio_duration - - 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 - return json_dict - - -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.voice_duration = voice_duration - - 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 - return json_dict - - -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.description = description - 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['document_url'] = self.document_url - json_dict['mime_type'] = self.mime_type - if self.description: - json_dict['description'] = self.description - 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 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, 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.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_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 - return json_dict - - -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): - 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.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 - - 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.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 - 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 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 - self.title = None - self.description = None - self.caption = None - self.reply_markup = None - self.input_message_content = None - self.parse_mode = None - self.caption_entities = None - self.payload_dic = {} - - def to_json(self): - json_dict = self.payload_dic - json_dict['type'] = self.type - json_dict['id'] = self.id - 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.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.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(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): - 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(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(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): - 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(InlineQueryResultCachedBase): - def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content=None): - InlineQueryResultCachedBase.__init__(self) - self.type = 'sticker' - self.id = id - self.sticker_file_id = sticker_file_id - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.payload_dic['sticker_file_id'] = sticker_file_id - - -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(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): - 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(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(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 - self.payload_dic['audio_file_id'] = audio_file_id - - -# Games - -class Game(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['photo'] = Game.parse_photo(obj['photo']) - if 'text_entities' in obj: - obj['text_entities'] = Game.parse_entities(obj['text_entities']) - if 'animation' in obj: - obj['animation'] = Animation.de_json(obj['animation']) - return cls(**obj) - - @classmethod - def parse_photo(cls, photo_size_array): - ret = [] - for ps in photo_size_array: - ret.append(PhotoSize.de_json(ps)) - return ret - - @classmethod - def parse_entities(cls, message_entity_array): - ret = [] - for me in message_entity_array: - ret.append(MessageEntity.de_json(me)) - return ret - - def __init__(self, title, description, photo, text=None, text_entities=None, animation=None, **kwargs): - self.title: str = title - self.description: str = description - self.photo: List[PhotoSize] = photo - self.text: str = text - self.text_entities: List[MessageEntity] = text_entities - self.animation: Animation = animation - - -class Animation(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj["thumb"] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, - thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.duration: int = duration - self.thumb: PhotoSize = thumb - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class GameHighScore(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, position, user, score, **kwargs): - self.position: int = position - self.user: User = user - self.score: int = score - - -# Payments - -class LabeledPrice(JsonSerializable): - def __init__(self, label, amount): - self.label: str = label - self.amount: int = amount - - def to_dict(self): - return { - 'label': self.label, 'amount': self.amount - } - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Invoice(JsonDeserializable): - @classmethod - 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) - - def __init__(self, title, description, start_parameter, currency, total_amount, **kwargs): - self.title: str = title - self.description: str = description - self.start_parameter: str = start_parameter - self.currency: str = currency - self.total_amount: int = total_amount - - -class ShippingAddress(JsonDeserializable): - @classmethod - 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) - - def __init__(self, country_code, state, city, street_line1, street_line2, post_code, **kwargs): - self.country_code: str = country_code - self.state: str = state - self.city: str = city - self.street_line1: str = street_line1 - self.street_line2: str = street_line2 - self.post_code: str = post_code - - -class OrderInfo(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['shipping_address'] = ShippingAddress.de_json(obj.get('shipping_address')) - return cls(**obj) - - def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs): - self.name: str = name - self.phone_number: str = phone_number - self.email: str = email - self.shipping_address: ShippingAddress = shipping_address - - -class ShippingOption(JsonSerializable): - def __init__(self, id, title): - self.id: str = id - self.title: str = title - self.prices: List[LabeledPrice] = [] - - def add_price(self, *args): - """ - Add LabeledPrice to ShippingOption - :param args: LabeledPrices - """ - for price in args: - self.prices.append(price) - return self - - def to_json(self): - price_list = [] - for p in self.prices: - price_list.append(p.to_dict()) - json_dict = json.dumps({'id': self.id, 'title': self.title, 'prices': price_list}) - return json_dict - - -class SuccessfulPayment(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) - return cls(**obj) - - def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, - telegram_payment_charge_id=None, provider_payment_charge_id=None, **kwargs): - self.currency: str = currency - self.total_amount: int = total_amount - self.invoice_payload: str = invoice_payload - self.shipping_option_id: str = shipping_option_id - self.order_info: OrderInfo = order_info - self.telegram_payment_charge_id: str = telegram_payment_charge_id - self.provider_payment_charge_id: str = provider_payment_charge_id - - -class ShippingQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) - return cls(**obj) - - def __init__(self, id, from_user, invoice_payload, shipping_address, **kwargs): - self.id: str = id - self.from_user: User = from_user - self.invoice_payload: str = invoice_payload - self.shipping_address: ShippingAddress = shipping_address - - -class PreCheckoutQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) - return cls(**obj) - - def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs): - self.id: str = id - self.from_user: User = from_user - self.currency: str = currency - self.total_amount: int = total_amount - self.invoice_payload: str = invoice_payload - self.shipping_option_id: str = shipping_option_id - self.order_info: OrderInfo = order_info - - -# Stickers - -class StickerSet(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - stickers = [] - for s in obj['stickers']: - stickers.append(Sticker.de_json(s)) - obj['stickers'] = stickers - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, name, title, is_animated, contains_masks, stickers, thumb=None, **kwargs): - self.name: str = name - self.title: str = title - self.is_animated: bool = is_animated - self.contains_masks: bool = contains_masks - self.stickers: List[Sticker] = stickers - self.thumb: PhotoSize = thumb - - -class Sticker(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - if 'mask_position' in obj: - obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) - return cls(**obj) - - def __init__(self, file_id, file_unique_id, width, height, is_animated, - thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.is_animated: bool = is_animated - self.thumb: PhotoSize = thumb - self.emoji: str = emoji - self.set_name: str = set_name - self.mask_position: MaskPosition = mask_position - self.file_size: int = file_size - - - -class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): - @classmethod - 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) - - def __init__(self, point, x_shift, y_shift, scale, **kwargs): - self.point: str = point - self.x_shift: float = x_shift - self.y_shift: float = y_shift - self.scale: float = scale - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale} - - -# InputMedia - -class InputMedia(Dictionaryable, JsonSerializable): - def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None): - self.type: str = type - self.media: str = media - self.caption: Optional[str] = caption - self.parse_mode: Optional[str] = parse_mode - self.caption_entities: Optional[List[MessageEntity]] = caption_entities - - if util.is_string(self.media): - self._media_name = '' - self._media_dic = self.media - else: - self._media_name = util.generate_random_token() - self._media_dic = 'attach://{0}'.format(self._media_name) - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'type': self.type, 'media': self._media_dic} - if self.caption: - json_dict['caption'] = self.caption - 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_dict - - def convert_input_media(self): - if util.is_string(self.media): - return self.to_json(), None - - return self.to_json(), {self._media_name: self.media} - - -class InputMediaPhoto(InputMedia): - def __init__(self, media, caption=None, parse_mode=None): - if util.is_pil_image(media): - media = util.pil_image_to_file(media) - - super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) - - def to_dict(self): - return super(InputMediaPhoto, self).to_dict() - - -class InputMediaVideo(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, - supports_streaming=None): - super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.width = width - self.height = height - self.duration = duration - self.supports_streaming = supports_streaming - - def to_dict(self): - ret = super(InputMediaVideo, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.width: - ret['width'] = self.width - if self.height: - ret['height'] = self.height - if self.duration: - ret['duration'] = self.duration - if self.supports_streaming: - ret['supports_streaming'] = self.supports_streaming - return ret - - -class InputMediaAnimation(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): - super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.width = width - self.height = height - self.duration = duration - - def to_dict(self): - ret = super(InputMediaAnimation, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.width: - ret['width'] = self.width - if self.height: - ret['height'] = self.height - if self.duration: - ret['duration'] = self.duration - return ret - - -class InputMediaAudio(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): - super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.duration = duration - self.performer = performer - self.title = title - - def to_dict(self): - ret = super(InputMediaAudio, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.duration: - ret['duration'] = self.duration - if self.performer: - ret['performer'] = self.performer - if self.title: - ret['title'] = self.title - return ret - - -class InputMediaDocument(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None): - super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.disable_content_type_detection = disable_content_type_detection - - def to_dict(self): - ret = super(InputMediaDocument, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.disable_content_type_detection is not None: - ret['disable_content_type_detection'] = self.disable_content_type_detection - return ret - - -class PollOption(JsonDeserializable): - @classmethod - 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) - - def __init__(self, text, voter_count = 0, **kwargs): - self.text: str = text - self.voter_count: int = voter_count - # Converted in _convert_poll_options - # def to_json(self): - # # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll - # return json.dumps(self.text) - - -class Poll(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['poll_id'] = obj.pop('id') - options = [] - for opt in obj['options']: - options.append(PollOption.de_json(opt)) - obj['options'] = options or None - if 'explanation_entities' in obj: - obj['explanation_entities'] = Message.parse_entities(obj['explanation_entities']) - return cls(**obj) - - def __init__( - self, - question, options, - poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, - allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, - open_period=None, close_date=None, **kwargs): - self.id: str = poll_id - self.question: str = question - self.options: List[PollOption] = options - self.total_voter_count: int = total_voter_count - self.is_closed: bool = is_closed - self.is_anonymous: bool = is_anonymous - self.type: str = poll_type - self.allows_multiple_answers: bool = allows_multiple_answers - self.correct_option_id: int = correct_option_id - self.explanation: str = explanation - self.explanation_entities: List[MessageEntity] = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] - self.open_period: int = open_period - self.close_date: int = close_date - - def add(self, option): - if type(option) is PollOption: - self.options.append(option) - else: - self.options.append(PollOption(option)) - - -class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, poll_id, user, option_ids, **kwargs): - self.poll_id: str = poll_id - self.user: User = user - self.option_ids: List[int] = option_ids - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'poll_id': self.poll_id, - 'user': self.user.to_dict(), - 'option_ids': self.option_ids} - - -class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return json_string - obj = cls.check_json(json_string) - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, location, address, **kwargs): - self.location: Location = location - self.address: str = address - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return { - "location": self.location.to_dict(), - "address": self.address - } - - -class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['creator'] = User.de_json(obj['creator']) - return cls(**obj) - - def __init__(self, invite_link, creator, creates_join_request , is_primary, is_revoked, - name=None, expire_date=None, member_limit=None, pending_join_request_count=None, **kwargs): - self.invite_link: str = invite_link - self.creator: User = creator - self.creates_join_request: bool = creates_join_request - self.is_primary: bool = is_primary - self.is_revoked: bool = is_revoked - self.name: str = name - self.expire_date: int = expire_date - self.member_limit: int = member_limit - self.pending_join_request_count: int = pending_join_request_count - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = { - "invite_link": self.invite_link, - "creator": self.creator.to_dict(), - "is_primary": self.is_primary, - "is_revoked": self.is_revoked, - "creates_join_request": self.creates_join_request - } - if self.expire_date: - json_dict["expire_date"] = self.expire_date - if self.member_limit: - json_dict["member_limit"] = self.member_limit - if self.pending_join_request_count: - json_dict["pending_join_request_count"] = self.pending_join_request_count - if self.name: - json_dict["name"] = self.name - return json_dict - - -class ProximityAlertTriggered(JsonDeserializable): - @classmethod - 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) - - def __init__(self, traveler, watcher, distance, **kwargs): - self.traveler: User = traveler - self.watcher: User = watcher - self.distance: int = distance - - -class VoiceChatStarted(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - return cls() - - def __init__(self): - """ - This object represents a service message about a voice chat started in the chat. - Currently holds no information. - """ - pass - - -class VoiceChatScheduled(JsonDeserializable): - @classmethod - 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) - - def __init__(self, start_date, **kwargs): - self.start_date: int = start_date - - -class VoiceChatEnded(JsonDeserializable): - @classmethod - 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) - - def __init__(self, duration, **kwargs): - self.duration: int = duration - - -class VoiceChatParticipantsInvited(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'users' in obj: - obj['users'] = [User.de_json(u) for u in obj['users']] - return cls(**obj) - - def __init__(self, users=None, **kwargs): - self.users: List[User] = users - - -class MessageAutoDeleteTimerChanged(JsonDeserializable): - @classmethod - 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) - - def __init__(self, message_auto_delete_time, **kwargs): - self.message_auto_delete_time = message_auto_delete_time From e7d0ec1f6c057f95d195a5d35d9b386e4640ab76 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 31 Dec 2021 19:25:29 +0400 Subject: [PATCH 0804/1808] Fix asyncio_helper.py --- telebot/asyncio_helper.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index eab1aea0b..02ffbcdb9 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -39,7 +39,7 @@ async def _get_new_session(self): logger = telebot.logger async def _process_request(token, url, method='get', params=None, files=None, request_timeout=None): - params = compose_data(params, files) + params = prepare_data(params, files) if request_timeout is None: request_timeout = REQUEST_TIMEOUT timeout = aiohttp.ClientTimeout(total=request_timeout) @@ -54,6 +54,8 @@ async def _process_request(token, url, method='get', params=None, files=None, re json_result = await _check_result(url, response) if json_result: return json_result['result'] + except (ApiTelegramException,ApiInvalidJSONException, ApiHTTPException) as e: + raise e except: pass if not got_result: @@ -62,9 +64,9 @@ async def _process_request(token, url, method='get', params=None, files=None, re -def guess_filename(obj): +def prepare_file(obj): """ - Get file name from object + returns os.path.basename for a given file :param obj: :return: @@ -74,9 +76,9 @@ def guess_filename(obj): return os.path.basename(name) -def compose_data(params=None, files=None): +def prepare_data(params=None, files=None): """ - Prepare request data + prepare data for request. :param params: :param files: @@ -96,7 +98,7 @@ def compose_data(params=None, files=None): else: raise ValueError('Tuple must have exactly 2 elements: filename, fileobj') else: - filename, fileobj = guess_filename(f) or key, f + filename, fileobj = prepare_file(f) or key, f data.add_field(key, fileobj, filename=filename) From b71507387f8b58d949f6b25b48b3142ac769a897 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 2 Jan 2022 14:12:15 +0400 Subject: [PATCH 0805/1808] Added spoiler --- telebot/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index eb3fabffd..a200b7313 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -582,7 +582,8 @@ def __html_text(self, text, entities): # "url": "{text}", # @badiboy plain URLs have no text and do not need tags "text_link": "{text}", "strikethrough": "{text}", - "underline": "{text}" + "underline": "{text}", + "spoiler": "{text}", } if hasattr(self, "custom_subs"): From ed6fb57cb513740010fa0731259e13027e2d41dc Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 2 Jan 2022 14:53:47 +0400 Subject: [PATCH 0806/1808] Added protect_content parameter to all methods --- telebot/asyncio_helper.py | 77 ++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 02ffbcdb9..fc0277551 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -241,7 +241,7 @@ async def send_message( token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - entities=None, allow_sending_without_reply=None): + entities=None, allow_sending_without_reply=None, protect_content=None): """ Use this method to send text messages. On success, the sent Message is returned. :param token: @@ -255,6 +255,7 @@ async def send_message( :param timeout: :param entities: :param allow_sending_without_reply: + :param protect_content: :return: """ method_name = 'sendMessage' @@ -275,6 +276,8 @@ async def send_message( params['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) if allow_sending_without_reply is not None: params['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + params['protect_content'] = protect_content return await _process_request(token, method_name, params=params) @@ -346,19 +349,21 @@ async def get_chat_member(token, chat_id, user_id): async def forward_message( token, chat_id, from_chat_id, message_id, - disable_notification=None, timeout=None): + disable_notification=None, timeout=None, protect_content=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None): + reply_markup=None, timeout=None, protect_content=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -377,13 +382,15 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['allow_sending_without_reply'] = allow_sending_without_reply if timeout: payload['timeout'] = timeout + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) async def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None, allow_sending_without_reply=None): + reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -398,6 +405,8 @@ async def send_dice( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) @@ -405,7 +414,7 @@ async def send_photo( token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - caption_entities=None, allow_sending_without_reply=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -431,13 +440,15 @@ async def send_photo( payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_media_group( token, chat_id, media, disable_notification=None, reply_to_message_id=None, - timeout=None, allow_sending_without_reply=None): + timeout=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendMediaGroup' media_json, files = await convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -449,6 +460,8 @@ async def send_media_group( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request( token, method_url, params=payload, method='post' if files else 'get', @@ -460,7 +473,7 @@ async def send_location( live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, allow_sending_without_reply=None): + proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -481,6 +494,8 @@ async def send_location( payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) @@ -532,7 +547,7 @@ async def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, allow_sending_without_reply=None, google_place_id=None, - google_place_type=None): + google_place_type=None, protect_content=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -553,13 +568,15 @@ async def send_venue( payload['google_place_id'] = google_place_id if google_place_type: payload['google_place_type'] = google_place_type + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, protect_content=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -576,6 +593,8 @@ async def send_contact( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) @@ -589,7 +608,8 @@ async def send_chat_action(token, chat_id, action, timeout=None): async def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, - thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None): + thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, + protect_content=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -629,13 +649,15 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, width=None, height=None, protect_content=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -669,12 +691,18 @@ async def send_animation( payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if width: + payload['width'] = width + if height: + payload['height'] = height + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, protect_content=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -700,11 +728,13 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_t payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None): + disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -736,12 +766,14 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl payload['thumb'] = thumb if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, - caption_entities=None, allow_sending_without_reply=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -779,12 +811,14 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None): + allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -1189,7 +1223,7 @@ async def delete_message(token, chat_id, message_id, timeout=None): async def send_game( token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None): + allow_sending_without_reply=None, protect_content=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1202,6 +1236,8 @@ async def send_game( payload['timeout'] = timeout if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) @@ -1266,7 +1302,7 @@ async def send_invoice( need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None): + timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, protect_content=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1298,6 +1334,7 @@ async def send_invoice( :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 protect_content: :return: """ method_url = r'sendInvoice' @@ -1344,6 +1381,8 @@ async def send_invoice( payload['max_tip_amount'] = max_tip_amount if suggested_tip_amounts is not None: payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) + if protect_content is not None: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) @@ -1492,7 +1531,7 @@ 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_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, explanation_entities=None): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1534,6 +1573,8 @@ async def send_poll( if explanation_entities: payload['explanation_entities'] = json.dumps( types.MessageEntity.to_list_of_dicts(explanation_entities)) + if protect_content: + payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) async def _convert_list_json_serializable(results): From 034241ba311c092475209e82c298f0fa8ab6fc73 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 2 Jan 2022 14:58:15 +0400 Subject: [PATCH 0807/1808] Fix commit --- telebot/__init__.py | 66 +++++++------ telebot/async_telebot.py | 209 +++++++++++++++++++++++++-------------- 2 files changed, 171 insertions(+), 104 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a73b21e2c..e3d8dfb51 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -955,11 +955,11 @@ def send_message( :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param text: Text of the message to be sent - :param parse_mode: Mode for parsing entities in the message text. - :param entities: A JSON-serialized list of special entities that appear in message text, which can be specified instead of parse_mode + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. + :param entities: List of special entities that appear in message text, which can be specified instead of parse_mode :param disable_web_page_preview: Disables link previews for links in this message :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :param protect_content: If True, the message content will be hidden for all users except for the target user :param reply_to_message_id: If the message is a reply, ID of the original message :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found :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. @@ -1074,18 +1074,19 @@ def send_photo( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None,) -> types.Message: """ - Use this method to send photos. + Use this method to send photos. On success, the sent Message is returned. :param chat_id: :param photo: :param caption: :param parse_mode: + :param caption_entities: :param disable_notification: + :param protect_content: :param reply_to_message_id: + :param allow_sending_without_reply: :param reply_markup: :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :return: API reply. + :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1224,13 +1225,14 @@ def send_document( # TODO: Rewrite this method like in API. def send_sticker( - self, chat_id: Union[int, str], data: Union[Any, str], + self, chat_id: Union[int, str], sticker: Union[Any, str], reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content:Optional[bool]=None) -> types.Message: + protect_content:Optional[bool]=None, + data: Union[Any, str]=None) -> types.Message: """ Use this method to send .webp stickers. :param chat_id: @@ -1241,8 +1243,12 @@ def send_sticker( :param timeout: timeout :param allow_sending_without_reply: :param protect_content: + :param data: function typo miss compatibility: do not use it :return: API reply. """ + if data and not(sticker): + # function typo miss compatibility + sticker = data return types.Message.de_json( apihelper.send_data( self.token, chat_id, data, 'sticker', @@ -1251,7 +1257,7 @@ def send_sticker( allow_sending_without_reply=allow_sending_without_reply, protect_content=protect_content)) def send_video( - self, chat_id: Union[int, str], data: Union[Any, str], + self, chat_id: Union[int, str], video: Union[Any, str], duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, @@ -1265,32 +1271,36 @@ def send_video( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None,) -> types.Message: + timeout: Optional[int]=None, + data: Optional[Union[Any, str]]=None) -> types.Message: """ - Use this method to send video files, Telegram clients support mp4 videos. - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend - a video that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param caption: String : Video caption (may also be used when resending videos by file_id). - :param parse_mode: - :param supports_streaming: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent - :param width: - :param height: + Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param video: Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video file using multipart/form-data. + :param duration: Duration of sent video in seconds + :param width: Video width + :param height: Video height + :param thumb: 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: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing + :param parse_mode: Mode for parsing entities in the video caption :param caption_entities: + :param supports_streaming: Pass True, if the uploaded video is suitable for streaming + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param protect_content: + :param reply_to_message_id: If the message is a reply, ID of the original message :param allow_sending_without_reply: - :return: + :param reply_markup: + :param timeout: + :param data: function typo miss compatibility: do not use it """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + if data and not(video): + # function typo miss compatibility + video = data return types.Message.de_json( apihelper.send_video( - self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, + self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, caption_entities, allow_sending_without_reply, protect_content)) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6f1fca539..51faabdce 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1484,16 +1484,19 @@ async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types result = await asyncio_helper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) + + async def send_message( self, chat_id: Union[int, str], text: str, + parse_mode: Optional[str]=None, + entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + timeout: Optional[int]=None) -> types.Message: """ Use this method to send text messages. @@ -1511,6 +1514,7 @@ async def send_message( :param timeout: :param entities: :param allow_sending_without_reply: + :param protect_content: :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1519,11 +1523,12 @@ async def send_message( await asyncio_helper.send_message( self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, - entities, allow_sending_without_reply)) + entities, allow_sending_without_reply, protect_content)) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], - message_id: int, disable_notification: Optional[bool]=None, + message_id: int, disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, timeout: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -1531,11 +1536,12 @@ async def forward_message( :param chat_id: which chat to forward :param from_chat_id: which chat message from :param message_id: message id + :param protect_content: :param timeout: :return: API reply. """ return types.Message.de_json( - await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout)) + await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) async def copy_message( self, chat_id: Union[int, str], @@ -1545,6 +1551,7 @@ async def copy_message( parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, @@ -1562,12 +1569,13 @@ async def copy_message( :param allow_sending_without_reply: :param reply_markup: :param timeout: + :param protect_content: :return: API reply. """ return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, - timeout)) + timeout, protect_content)) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -1586,7 +1594,8 @@ async def send_dice( reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send dices. :param chat_id: @@ -1596,22 +1605,25 @@ async def send_dice( :param reply_markup: :param timeout: :param allow_sending_without_reply: + :param protect_content: :return: Message """ return types.Message.de_json( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout, allow_sending_without_reply) + reply_markup, timeout, allow_sending_without_reply, protect_content) ) async def send_photo( self, chat_id: Union[int, str], photo: Union[Any, str], - caption: Optional[str]=None, reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None,) -> types.Message: """ Use this method to send photos. :param chat_id: @@ -1624,6 +1636,7 @@ async def send_photo( :param timeout: :param caption_entities: :param allow_sending_without_reply: + :param protect_content: :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1632,7 +1645,7 @@ async def send_photo( await asyncio_helper.send_photo( self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply)) + allow_sending_without_reply, protect_content)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -1645,7 +1658,8 @@ async def send_audio( timeout: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -1663,6 +1677,7 @@ async def send_audio( :param thumb: :param caption_entities: :param allow_sending_without_reply: + :param protect_content: :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1671,7 +1686,7 @@ async def send_audio( await asyncio_helper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply)) + caption_entities, allow_sending_without_reply, protect_content)) async def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -1682,7 +1697,8 @@ async def send_voice( disable_notification: Optional[bool]=None, timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -1697,6 +1713,7 @@ async def send_voice( :param timeout: :param caption_entities: :param allow_sending_without_reply: + :param protect_content: :return: Message """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1705,7 +1722,7 @@ async def send_voice( await asyncio_helper.send_voice( self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply)) + allow_sending_without_reply, protect_content)) async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], @@ -1720,7 +1737,8 @@ async def send_document( allow_sending_without_reply: Optional[bool]=None, visible_file_name: Optional[str]=None, disable_content_type_detection: Optional[bool]=None, - data: Optional[Union[Any, str]]=None) -> types.Message: + data: Optional[Union[Any, str]]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send general files. :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1737,6 +1755,7 @@ async def send_document( :param visible_file_name: allows to async 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 :param data: function typo compatibility: do not use it + :param protect_content: :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1750,7 +1769,7 @@ async def send_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)) + disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content)) async def send_sticker( self, chat_id: Union[int, str], data: Union[Any, str], @@ -1758,7 +1777,8 @@ async def send_sticker( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send .webp stickers. :param chat_id: @@ -1768,6 +1788,7 @@ async def send_sticker( :param disable_notification: to disable the notification :param timeout: timeout :param allow_sending_without_reply: + :param protect_content: :return: API reply. """ return types.Message.de_json( @@ -1775,42 +1796,44 @@ async def send_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)) + allow_sending_without_reply=allow_sending_without_reply, protect_content=protect_content)) async def send_video( - self, chat_id: Union[int, str], data: Union[Any, str], - duration: Optional[int]=None, + self, chat_id: Union[int, str], video: Union[Any, str], + duration: Optional[int]=None, + width: Optional[int]=None, + height: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, caption: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, - supports_streaming: Optional[bool]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, - width: Optional[int]=None, - height: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + supports_streaming: Optional[bool]=None, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, + data: Optional[Union[Any, str]]=None) -> types.Message: """ - Use this method to send video files, Telegram clients support mp4 videos. - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video to send. You can either pass a file_id as String to resend - a video that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param caption: String : Video caption (may also be used when resending videos by file_id). - :param parse_mode: - :param supports_streaming: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent - :param width: - :param height: + Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param video: Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video file using multipart/form-data. + :param duration: Duration of sent video in seconds + :param width: Video width + :param height: Video height + :param thumb: 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: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing + :param parse_mode: Mode for parsing entities in the video caption :param caption_entities: + :param supports_streaming: Pass True, if the uploaded video is suitable for streaming + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param protect_content: + :param reply_to_message_id: If the message is a reply, ID of the original message :param allow_sending_without_reply: - :return: + :param reply_markup: + :param timeout: + :param data: function typo miss compatibility: do not use it """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1818,33 +1841,38 @@ async def send_video( await asyncio_helper.send_video( self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, - caption_entities, allow_sending_without_reply)) + caption_entities, allow_sending_without_reply, protect_content)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], duration: Optional[int]=None, + width: Optional[int]=None, + height: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None, caption: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + timeout: Optional[int]=None, ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server :param duration: Integer : Duration of sent video in seconds + :param width: Integer : Video width + :param height: Integer : Video height + :param thumb: InputFile or String : Thumbnail of the file sent :param caption: String : Animation caption (may also be used when resending animation by file_id). :param parse_mode: :param reply_to_message_id: :param reply_markup: :param disable_notification: :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent :param caption_entities: :param allow_sending_without_reply: :return: @@ -1855,7 +1883,7 @@ async def send_animation( await asyncio_helper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply)) + caption_entities, allow_sending_without_reply, width, height, protect_content)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -1866,7 +1894,8 @@ async def send_video_note( disable_notification: Optional[bool]=None, timeout: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. @@ -1881,12 +1910,13 @@ async def send_video_note( :param timeout: :param thumb: InputFile or String : Thumbnail of the file sent :param allow_sending_without_reply: + :param protect_content: :return: """ return types.Message.de_json( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb, allow_sending_without_reply)) + disable_notification, timeout, thumb, allow_sending_without_reply, protect_content)) async def send_media_group( self, chat_id: Union[int, str], @@ -1894,6 +1924,7 @@ async def send_media_group( types.InputMediaAudio, types.InputMediaDocument, types.InputMediaPhoto, types.InputMediaVideo]], disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: @@ -1905,11 +1936,12 @@ async def send_media_group( :param reply_to_message_id: :param timeout: :param allow_sending_without_reply: + :param protect_content: :return: """ result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, - allow_sending_without_reply) + allow_sending_without_reply, protect_content) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -1923,7 +1955,8 @@ async def send_location( horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ @@ -1940,6 +1973,7 @@ async def send_location( :param heading: :param proximity_alert_radius: :param allow_sending_without_reply: + :param protect_content: :return: API reply. """ return types.Message.de_json( @@ -1947,7 +1981,7 @@ async def send_location( self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - allow_sending_without_reply)) + allow_sending_without_reply, protect_content)) async def edit_message_live_location( self, latitude: float, longitude: float, @@ -2011,7 +2045,8 @@ async def send_venue( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, google_place_id: Optional[str]=None, - google_place_type: Optional[str]=None) -> types.Message: + google_place_type: Optional[str]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel @@ -2029,13 +2064,14 @@ async def send_venue( :param allow_sending_without_reply: :param google_place_id: :param google_place_type: + :param protect_content: :return: """ return types.Message.de_json( await asyncio_helper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, google_place_id, google_place_type) + allow_sending_without_reply, google_place_id, google_place_type, protect_content) ) async def send_contact( @@ -2046,12 +2082,27 @@ async def send_contact( reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: + """ + Use this method to send phone contacts. + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel + :param phone_number: String : Contact's phone number + :param first_name: String : Contact's first name + :param last_name: String : Contact's last name + :param vcard: String : Additional data about the contact in the form of a vCard, 0-2048 bytes + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :param protect_content: + """ return types.Message.de_json( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply) + allow_sending_without_reply, protect_content) ) async def send_chat_action( @@ -2576,7 +2627,8 @@ async def send_game( reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> types.Message: + allow_sending_without_reply: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Used to send the game :param chat_id: @@ -2586,12 +2638,13 @@ async def send_game( :param reply_markup: :param timeout: :param allow_sending_without_reply: + :param protect_content: :return: """ result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply) + allow_sending_without_reply, protect_content) return types.Message.de_json(result) async def set_game_score( @@ -2651,7 +2704,8 @@ async def send_invoice( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, max_tip_amount: Optional[int] = None, - suggested_tip_amounts: Optional[List[int]]=None) -> types.Message: + suggested_tip_amounts: Optional[List[int]]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Sends invoice :param chat_id: Unique identifier for the target private chat @@ -2690,6 +2744,7 @@ async def send_invoice( :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: :return: """ result = await asyncio_helper.send_invoice( @@ -2698,7 +2753,7 @@ async def send_invoice( photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, - max_tip_amount, suggested_tip_amounts) + max_tip_amount, suggested_tip_amounts, protect_content) return types.Message.de_json(result) # noinspection PyShadowingBuiltins @@ -2717,7 +2772,8 @@ async def send_poll( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, allow_sending_without_reply: Optional[bool]=None, timeout: Optional[int]=None, - explanation_entities: Optional[List[types.MessageEntity]]=None) -> types.Message: + explanation_entities: Optional[List[types.MessageEntity]]=None, + protect_content: Optional[bool]=None) -> types.Message: """ Send polls :param chat_id: @@ -2738,6 +2794,7 @@ async def send_poll( :param reply_markup: :param timeout: :param explanation_entities: + :param protect_content: :return: """ @@ -2751,7 +2808,7 @@ 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_to_message_id, allow_sending_without_reply, - reply_markup, timeout, explanation_entities)) + reply_markup, timeout, explanation_entities, protect_content)) async def stop_poll( self, chat_id: Union[int, str], message_id: int, From 3f243c64ca7f9c488926333a3b35d3f57e94a8f0 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 2 Jan 2022 22:09:09 +0400 Subject: [PATCH 0808/1808] Fix data-typo --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e3d8dfb51..ca93487a2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1251,7 +1251,7 @@ def send_sticker( sticker = data return types.Message.de_json( apihelper.send_data( - self.token, chat_id, data, 'sticker', + self.token, chat_id, sticker, '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, protect_content=protect_content)) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 51faabdce..011ff46b4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1772,17 +1772,18 @@ async def send_document( disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content)) async def send_sticker( - self, chat_id: Union[int, str], data: Union[Any, str], + self, chat_id: Union[int, str], sticker: Union[Any, str], reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + data: Union[Any, str]=None) -> types.Message: """ Use this method to send .webp stickers. :param chat_id: - :param data: + :param sticker: :param reply_to_message_id: :param reply_markup: :param disable_notification: to disable the notification @@ -1791,9 +1792,12 @@ async def send_sticker( :param protect_content: :return: API reply. """ + if data and not(sticker): + # function typo miss compatibility + sticker = data return types.Message.de_json( await asyncio_helper.send_data( - self.token, chat_id, data, 'sticker', + self.token, chat_id, sticker, '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, protect_content=protect_content)) From df8f34e7260cf4da6b49f9822c33f7a1283fcad3 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 3 Jan 2022 16:16:19 +0400 Subject: [PATCH 0809/1808] Create webhook_fastapi_echo_bot.py --- .../webhook_fastapi_echo_bot.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 examples/webhook_examples/webhook_fastapi_echo_bot.py diff --git a/examples/webhook_examples/webhook_fastapi_echo_bot.py b/examples/webhook_examples/webhook_fastapi_echo_bot.py new file mode 100644 index 000000000..433b9a1f6 --- /dev/null +++ b/examples/webhook_examples/webhook_fastapi_echo_bot.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This is a simple echo bot using decorators and webhook with fastapi +# It echoes any incoming text messages and does not use the polling method. + +import logging +import fastapi +import telebot + +API_TOKEN = 'TOKEN' + +WEBHOOK_HOST = '' +WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open') +WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr + +WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate +WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key + +# Quick'n'dirty SSL certificate generation: +# +# openssl genrsa -out webhook_pkey.pem 2048 +# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem +# +# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply +# with the same value in you put in WEBHOOK_HOST + +WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT) +WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN) + +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) + +bot = telebot.TeleBot(API_TOKEN) + +app = fastapi.FastAPI() + + +# Process webhook calls +@app.post(f'/{API_TOKEN}/') +def process_webhook(update: dict): + if update: + update = telebot.types.Update.de_json(update) + bot.process_new_updates([update]) + else: + return + + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +def send_welcome(message): + bot.reply_to(message, + ("Hi there, I am EchoBot.\n" + "I am here to echo your kind words back to you.")) + + +# Handle all other messages +@bot.message_handler(func=lambda message: True, content_types=['text']) +def echo_message(message): + bot.reply_to(message, message.text) + + +# Remove webhook, it fails sometimes the set if there is a previous webhook +bot.remove_webhook() + +# Set webhook +bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, + certificate=open(WEBHOOK_SSL_CERT, 'r')) + + +import uvicorn +uvicorn.run( + app, + host=WEBHOOK_LISTEN, + port=WEBHOOK_PORT, + ssl_certfile=WEBHOOK_SSL_CERT, + ssl_keyfile=WEBHOOK_SSL_PRIV +) From a96bc802bc28298a95cb12033d3e9edf5e863aaa Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 3 Jan 2022 16:28:24 +0400 Subject: [PATCH 0810/1808] Update README.md --- examples/webhook_examples/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/webhook_examples/README.md b/examples/webhook_examples/README.md index 221d9c5d4..74fe912a4 100644 --- a/examples/webhook_examples/README.md +++ b/examples/webhook_examples/README.md @@ -50,5 +50,12 @@ There are 5 examples in this directory using different libraries: * **Cons:** * Twisted is low-level, which may be good or bad depending on use case * Considerable learning curve - reading docs is a must. +* **FastAPI(0.70.1) + * **Pros:** + * Can be written for both sync and async + * Good documentation + * **Cons:** + * Requires python 3.6+ + -*Latest update of this document: 2020-12-17* +*Latest update of this document: 01-03-2022 From a51ff0f10081d74d3365a90654e211adc0c4a483 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 3 Jan 2022 16:30:49 +0400 Subject: [PATCH 0811/1808] Fix readme typos --- examples/webhook_examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/webhook_examples/README.md b/examples/webhook_examples/README.md index 74fe912a4..532d13e65 100644 --- a/examples/webhook_examples/README.md +++ b/examples/webhook_examples/README.md @@ -50,7 +50,7 @@ There are 5 examples in this directory using different libraries: * **Cons:** * Twisted is low-level, which may be good or bad depending on use case * Considerable learning curve - reading docs is a must. -* **FastAPI(0.70.1) +* **FastAPI(0.70.1):** *webhook_fastapi_echo_bot.py* * **Pros:** * Can be written for both sync and async * Good documentation From 6550a5d745a287c1dcee527b5c58c5dc0509b3b9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 8 Jan 2022 20:02:54 +0300 Subject: [PATCH 0812/1808] Bump version to 4.3.0 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 468312f60..42af8f629 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.2.2' +__version__ = '4.3.0' From 8d8aa5a380402026b6eeecbb5cb1e7e275789206 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 10 Jan 2022 14:38:28 +0300 Subject: [PATCH 0813/1808] Bugfix in send_data Bugfix in send_data Protected methods renamed Some hints added --- telebot/__init__.py | 15 ++++++++----- telebot/apihelper.py | 8 ++++++- telebot/handler_backends.py | 45 +++++++++++++++++-------------------- telebot/util.py | 5 +++-- telebot/version.py | 2 +- 5 files changed, 42 insertions(+), 33 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ca93487a2..5f299a659 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -252,7 +252,7 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): """ self.current_states = StateFile(filename=filename) - self.current_states._create_dir() + self.current_states.create_dir() def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): """ @@ -1221,11 +1221,13 @@ def send_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, protect_content = protect_content)) + disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, + protect_content = protect_content)) # TODO: Rewrite this method like in API. def send_sticker( - self, chat_id: Union[int, str], sticker: Union[Any, str], + self, chat_id: Union[int, str], + sticker: Union[Any, str], reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, @@ -1236,6 +1238,7 @@ def send_sticker( """ Use this method to send .webp stickers. :param chat_id: + :param sticker: :param data: :param reply_to_message_id: :param reply_markup: @@ -1254,7 +1257,8 @@ def send_sticker( self.token, chat_id, sticker, '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, protect_content=protect_content)) + allow_sending_without_reply=allow_sending_without_reply, + protect_content=protect_content)) def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -1330,6 +1334,7 @@ def send_animation( :param thumb: InputFile or String : Thumbnail of the file sent :param caption: String : Animation caption (may also be used when resending animation by file_id). :param parse_mode: + :param protect_content: :param reply_to_message_id: :param reply_markup: :param disable_notification: @@ -2559,7 +2564,7 @@ def add_data(self, chat_id: int, **kwargs): :param chat_id: """ for key, value in kwargs.items(): - self.current_states._add_data(chat_id, key, value) + self.current_states.add_data(chat_id, key, value) def register_next_step_handler_by_chat_id( self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3f9562d5a..870c60915 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -144,6 +144,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout), proxies=proxy) elif CUSTOM_REQUEST_SENDER: + # noinspection PyCallingNonCallable result = CUSTOM_REQUEST_SENDER( method, request_url, params=params, files=files, timeout=(connect_timeout, read_timeout), proxies=proxy) @@ -246,6 +247,7 @@ def send_message( :param timeout: :param entities: :param allow_sending_without_reply: + :param protect_content: :return: """ method_url = r'sendMessage' @@ -861,7 +863,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None): + allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, + protect_content = None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -896,6 +899,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: payload['disable_content_type_detection'] = disable_content_type_detection return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1382,6 +1387,7 @@ def send_invoice( :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 protect_content: :return: """ method_url = r'sendInvoice' diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 45b903beb..6f12c3527 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -168,7 +168,7 @@ def delete_state(self, chat_id): """Delete a state""" self._states.pop(chat_id) - def _get_data(self, chat_id): + def get_data(self, chat_id): return self._states[chat_id]['data'] def set(self, chat_id, new_state): @@ -179,7 +179,7 @@ def set(self, chat_id, new_state): """ self.add_state(chat_id,new_state) - def _add_data(self, chat_id, key, value): + def add_data(self, chat_id, key, value): result = self._states[chat_id]['data'][key] = value return result @@ -217,28 +217,27 @@ def add_state(self, chat_id, state): :param chat_id: :param state: new state """ - states_data = self._read_data() + states_data = self.read_data() if chat_id in states_data: states_data[chat_id]['state'] = state - return self._save_data(states_data) + return self.save_data(states_data) else: - new_data = states_data[chat_id] = {'state': state,'data': {}} - return self._save_data(states_data) - + states_data[chat_id] = {'state': state,'data': {}} + return self.save_data(states_data) def current_state(self, chat_id): """Current state.""" - states_data = self._read_data() + states_data = self.read_data() if chat_id in states_data: return states_data[chat_id]['state'] else: return False def delete_state(self, chat_id): """Delete a state""" - states_data = self._read_data() + states_data = self.read_data() states_data.pop(chat_id) - self._save_data(states_data) + self.save_data(states_data) - def _read_data(self): + def read_data(self): """ Read the data from file. """ @@ -247,7 +246,7 @@ def _read_data(self): file.close() return states_data - def _create_dir(self): + def create_dir(self): """ Create directory .save-handlers. """ @@ -257,7 +256,7 @@ def _create_dir(self): with open(self.file_path,'wb') as file: pickle.dump({}, file) - def _save_data(self, new_data): + def save_data(self, new_data): """ Save data after editing. :param new_data: @@ -266,23 +265,21 @@ def _save_data(self, new_data): pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL) return True - def _get_data(self, chat_id): - return self._read_data()[chat_id]['data'] + def get_data(self, chat_id): + return self.read_data()[chat_id]['data'] def set(self, chat_id, new_state): """ Set a new state for a user. :param chat_id: :param new_state: new_state of a user - """ self.add_state(chat_id,new_state) def _add_data(self, chat_id, key, value): - states_data = self._read_data() + states_data = self.read_data() result = states_data[chat_id]['data'][key] = value - self._save_data(result) - + self.save_data(result) return result def finish(self, chat_id): @@ -313,7 +310,7 @@ class StateContext: def __init__(self , obj: StateMemory, chat_id) -> None: self.obj = obj self.chat_id = chat_id - self.data = obj._get_data(chat_id) + self.data = obj.get_data(chat_id) def __enter__(self): return self.data @@ -321,6 +318,7 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): return + class StateFileContext: """ Class for data. @@ -328,15 +326,14 @@ class StateFileContext: def __init__(self , obj: StateFile, chat_id) -> None: self.obj = obj self.chat_id = chat_id - self.data = self.obj._get_data(self.chat_id) + self.data = self.obj.get_data(self.chat_id) def __enter__(self): return self.data def __exit__(self, exc_type, exc_val, exc_tb): - old_data = self.obj._read_data() + old_data = self.obj.read_data() for i in self.data: old_data[self.chat_id]['data'][i] = self.data.get(i) - self.obj._save_data(old_data) - + self.obj.save_data(old_data) return diff --git a/telebot/util.py b/telebot/util.py index 1ab620128..414fa56f9 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -449,7 +449,7 @@ def wrapper(*args, **kwargs): logger.info(f"`{function.__name__}` is deprecated." + (f" Use `{alternative.__name__}` instead" if alternative else "")) else: - logger.warn(f"`{function.__name__}` is deprecated." + logger.warning(f"`{function.__name__}` is deprecated." + (f" Use `{alternative.__name__}` instead" if alternative else "")) return function(*args, **kwargs) return wrapper @@ -484,6 +484,7 @@ def antiflood(function, *args, **kwargs): """ from telebot.apihelper import ApiTelegramException from time import sleep + msg = None try: msg = function(*args, **kwargs) except ApiTelegramException as ex: @@ -491,4 +492,4 @@ def antiflood(function, *args, **kwargs): sleep(ex.result_json['parameters']['retry_after']) msg = function(*args, **kwargs) finally: - return msg \ No newline at end of file + return msg diff --git a/telebot/version.py b/telebot/version.py index 42af8f629..531afbd9f 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.3.0' +__version__ = '4.3.1' From 2e6b6bda53a80892d153cb73381d7fcad55dec53 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 10 Jan 2022 16:40:33 +0300 Subject: [PATCH 0814/1808] Additional bugfix Additional bugfix Plus protected methods removal --- telebot/async_telebot.py | 13 ++++++--- telebot/asyncio_handler_backends.py | 42 ++++++++++++++--------------- telebot/asyncio_helper.py | 4 +-- telebot/handler_backends.py | 2 +- telebot/util.py | 10 +++---- tests/test_telebot.py | 21 ++++++++++----- 6 files changed, 52 insertions(+), 40 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 011ff46b4..0e1deaebf 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1335,7 +1335,7 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): """ self.current_states = asyncio_handler_backends.StateFile(filename=filename) - self.current_states._create_dir() + self.current_states.create_dir() async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, drop_pending_updates = None, timeout=None): @@ -1790,6 +1790,7 @@ async def send_sticker( :param timeout: timeout :param allow_sending_without_reply: :param protect_content: + :param data: deprecated, for backward compatibility :return: API reply. """ if data and not(sticker): @@ -1837,13 +1838,16 @@ async def send_video( :param allow_sending_without_reply: :param reply_markup: :param timeout: - :param data: function typo miss compatibility: do not use it + :param data: deprecated, for backward compatibility """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + if data and not(video): + # function typo miss compatibility + video = data return types.Message.de_json( await asyncio_helper.send_video( - self.token, chat_id, data, duration, caption, reply_to_message_id, reply_markup, + self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, caption_entities, allow_sending_without_reply, protect_content)) @@ -1873,6 +1877,7 @@ async def send_animation( :param thumb: InputFile or String : Thumbnail of the file sent :param caption: String : Animation caption (may also be used when resending animation by file_id). :param parse_mode: + :param protect_content: :param reply_to_message_id: :param reply_markup: :param disable_notification: @@ -3057,4 +3062,4 @@ async def add_data(self, chat_id, **kwargs): :param chat_id: """ for key, value in kwargs.items(): - await self.current_states._add_data(chat_id, key, value) \ No newline at end of file + await self.current_states.add_data(chat_id, key, value) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index d3c452f86..0a78a9000 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -28,7 +28,7 @@ async def delete_state(self, chat_id): """Delete a state""" self._states.pop(chat_id) - def _get_data(self, chat_id): + def get_data(self, chat_id): return self._states[chat_id]['data'] async def set(self, chat_id, new_state): @@ -39,7 +39,7 @@ async def set(self, chat_id, new_state): """ await self.add_state(chat_id,new_state) - async def _add_data(self, chat_id, key, value): + async def add_data(self, chat_id, key, value): result = self._states[chat_id]['data'][key] = value return result @@ -77,28 +77,28 @@ async def add_state(self, chat_id, state): :param chat_id: :param state: new state """ - states_data = self._read_data() + states_data = self.read_data() if chat_id in states_data: states_data[chat_id]['state'] = state - return await self._save_data(states_data) + return await self.save_data(states_data) else: - new_data = states_data[chat_id] = {'state': state,'data': {}} - return await self._save_data(states_data) + states_data[chat_id] = {'state': state,'data': {}} + return await self.save_data(states_data) async def current_state(self, chat_id): """Current state.""" - states_data = self._read_data() + states_data = self.read_data() if chat_id in states_data: return states_data[chat_id]['state'] else: return False async def delete_state(self, chat_id): """Delete a state""" - states_data = self._read_data() + states_data = self.read_data() states_data.pop(chat_id) - await self._save_data(states_data) + await self.save_data(states_data) - def _read_data(self): + def read_data(self): """ Read the data from file. """ @@ -107,7 +107,7 @@ def _read_data(self): file.close() return states_data - def _create_dir(self): + def create_dir(self): """ Create directory .save-handlers. """ @@ -117,7 +117,7 @@ def _create_dir(self): with open(self.file_path,'wb') as file: pickle.dump({}, file) - async def _save_data(self, new_data): + async def save_data(self, new_data): """ Save data after editing. :param new_data: @@ -126,8 +126,8 @@ async def _save_data(self, new_data): pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL) return True - def _get_data(self, chat_id): - return self._read_data()[chat_id]['data'] + def get_data(self, chat_id): + return self.read_data()[chat_id]['data'] async def set(self, chat_id, new_state): """ @@ -138,10 +138,10 @@ async def set(self, chat_id, new_state): """ await self.add_state(chat_id,new_state) - async def _add_data(self, chat_id, key, value): - states_data = self._read_data() + async def add_data(self, chat_id, key, value): + states_data = self.read_data() result = states_data[chat_id]['data'][key] = value - await self._save_data(result) + await self.save_data(result) return result @@ -173,7 +173,7 @@ class StateContext: def __init__(self , obj: StateMemory, chat_id) -> None: self.obj = obj self.chat_id = chat_id - self.data = obj._get_data(chat_id) + self.data = obj.get_data(chat_id) async def __aenter__(self): return self.data @@ -191,14 +191,14 @@ def __init__(self , obj: StateFile, chat_id) -> None: self.data = None async def __aenter__(self): - self.data = self.obj._get_data(self.chat_id) + self.data = self.obj.get_data(self.chat_id) return self.data async def __aexit__(self, exc_type, exc_val, exc_tb): - old_data = self.obj._read_data() + old_data = self.obj.read_data() for i in self.data: old_data[self.chat_id]['data'][i] = self.data.get(i) - await self.obj._save_data(old_data) + await self.obj.save_data(old_data) return diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index fc0277551..e36a97422 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1,8 +1,6 @@ import asyncio # for future uses -from time import time import aiohttp from telebot import types -import json try: import ujson as json @@ -853,6 +851,8 @@ async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, r payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply + if protect_content is not None: + payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: payload['disable_content_type_detection'] = disable_content_type_detection return await _process_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 6f12c3527..1e6787045 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -276,7 +276,7 @@ def set(self, chat_id, new_state): """ self.add_state(chat_id,new_state) - def _add_data(self, chat_id, key, value): + def add_data(self, chat_id, key, value): states_data = self.read_data() result = states_data[chat_id]['data'][key] = value self.save_data(result) diff --git a/telebot/util.py b/telebot/util.py index 414fa56f9..8ec6be68b 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -4,6 +4,7 @@ import string import threading import traceback +import warnings from typing import Any, Callable, List, Dict, Optional, Union # noinspection PyPep8Naming @@ -436,7 +437,7 @@ def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) -def deprecated(warn: bool=False, alternative: Optional[Callable]=None): +def deprecated(warn: bool=True, alternative: Optional[Callable]=None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. @@ -445,12 +446,11 @@ def deprecated(warn: bool=False, alternative: Optional[Callable]=None): """ def decorator(function): def wrapper(*args, **kwargs): + info = f"`{function.__name__}` is deprecated." + (f" Use `{alternative.__name__}` instead" if alternative else "") if not warn: - logger.info(f"`{function.__name__}` is deprecated." - + (f" Use `{alternative.__name__}` instead" if alternative else "")) + logger.info(info) else: - logger.warning(f"`{function.__name__}` is deprecated." - + (f" Use `{alternative.__name__}` instead" if alternative else "")) + logger.warning(info) return function(*args, **kwargs) return wrapper return decorator diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 2976a9ab9..1a4dc038a 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import sys +import warnings sys.path.append('../') @@ -19,14 +20,19 @@ CHAT_ID = os.environ['CHAT_ID'] GROUP_ID = os.environ['GROUP_ID'] -def _new_test(): - pass -@util.deprecated(alternative=_new_test) -def _test(): - pass - +def deprecated1_new_function(): + print("deprecated1_new_function") +def deprecated1_old_function(): + print("deprecated1_old_function") + warnings.warn("The 'deprecated1_old_function' is deprecated. Use `deprecated1_new_function` instead", DeprecationWarning, 2) + deprecated1_new_function() +def deprecated2_new_function(): + print("deprecated2_new_function") +@util.deprecated(alternative=deprecated2_new_function) +def deprecated2_old_function(): + print("deprecated2_old_function") @pytest.mark.skipif(should_skip, reason="No environment variables configured") class TestTeleBot: @@ -633,7 +639,8 @@ def command_handler(message): assert update.message.text == 'got' * 2 def test_deprecated_dec(self): - _test() + deprecated1_old_function() + deprecated2_old_function() def test_chat_permissions(self): return # CHAT_ID is private chat, no permissions can be set From 914004495619556db93a1b4eccce47edf229a4f2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 10 Jan 2022 16:49:49 +0300 Subject: [PATCH 0815/1808] Tests ant type hint fix --- telebot/__init__.py | 2 +- tests/test_telebot.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5f299a659..5fed5c192 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1929,7 +1929,7 @@ def set_my_commands(self, commands: List[types.BotCommand], return apihelper.set_my_commands(self.token, commands, scope, language_code) def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, - language_code: Optional[int]=None) -> bool: + language_code: Optional[str]=None) -> bool: """ Use this method to delete the list of the bot's commands for the given scope and user language. After deletion, higher level commands will be shown to affected users. diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 1a4dc038a..54d380786 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -222,7 +222,7 @@ def test_send_photo_dis_noti(self): def test_send_audio(self): file_data = open('./test_data/record.mp3', 'rb') tb = telebot.TeleBot(TOKEN) - ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram') + ret_msg = tb.send_audio(CHAT_ID, file_data, duration = 1, performer='eternnoir', title='pyTelegram') assert ret_msg.content_type == 'audio' assert ret_msg.audio.performer == 'eternnoir' assert ret_msg.audio.title == 'pyTelegram' @@ -230,7 +230,7 @@ def test_send_audio(self): def test_send_audio_dis_noti(self): file_data = open('./test_data/record.mp3', 'rb') tb = telebot.TeleBot(TOKEN) - ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram', + ret_msg = tb.send_audio(CHAT_ID, file_data, duration = 1, performer='eternnoir', title='pyTelegram', disable_notification=True) assert ret_msg.content_type == 'audio' assert ret_msg.audio.performer == 'eternnoir' @@ -439,8 +439,10 @@ def test_export_chat_invite_link(self): def test_create_revoke_detailed_chat_invite_link(self): tb = telebot.TeleBot(TOKEN) - cil = tb.create_chat_invite_link(GROUP_ID, - (datetime.now() + timedelta(minutes=1)).timestamp(), member_limit=5) + cil = tb.create_chat_invite_link( + GROUP_ID, + expire_date = datetime.now() + timedelta(minutes=1), + member_limit=5) assert isinstance(cil.invite_link, str) assert cil.creator.id == tb.get_me().id assert isinstance(cil.expire_date, (float, int)) @@ -464,9 +466,10 @@ def test_edit_markup(self): def test_antiflood(self): text = "Flooding" tb = telebot.TeleBot(TOKEN) - for _ in range(0,100): + i = -1 + for i in range(0,100): util.antiflood(tb.send_message, CHAT_ID, text) - assert _ + assert i @staticmethod def create_text_message(text): @@ -585,14 +588,14 @@ def test_chat_commands(self): ret_msg = tb.set_my_commands([telebot.types.BotCommand(command, description)], scope, lang) assert ret_msg is True - ret_msg = tb.get_my_commands(scope, lang) + ret_msg = tb.get_my_commands(scope = scope, language_code = lang) assert ret_msg[0].command == command assert ret_msg[0].description == description - ret_msg = tb.delete_my_commands(scope, lang) + ret_msg = tb.delete_my_commands(scope = scope, language_code = lang) assert ret_msg is True - ret_msg = tb.get_my_commands(scope, lang) + ret_msg = tb.get_my_commands(scope = scope, language_code = lang) assert ret_msg == [] From 7fe8d27686a25c0823b09400f0a706751166bd74 Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo <66796758+barbax7@users.noreply.github.com> Date: Mon, 10 Jan 2022 16:19:21 +0100 Subject: [PATCH 0816/1808] Correct test for antiflood function --- tests/test_telebot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 54d380786..06ee681e9 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -464,12 +464,12 @@ def test_edit_markup(self): assert new_msg.message_id def test_antiflood(self): - text = "Flooding" + text = "Testing antiflood function" tb = telebot.TeleBot(TOKEN) i = -1 - for i in range(0,100): + for i in range(0,200): util.antiflood(tb.send_message, CHAT_ID, text) - assert i + assert i == 199 @staticmethod def create_text_message(text): From 685c0710561f936524f5747bfa5470108b13be6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20Romero=20Rodr=C3=ADguez?= Date: Tue, 11 Jan 2022 19:24:16 -0300 Subject: [PATCH 0817/1808] Removed redundant logger configuration in async_telebot that made logs repeated twice --- telebot/async_telebot.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0e1deaebf..6bfa799fa 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -17,16 +17,6 @@ from telebot import logger -formatter = logging.Formatter( - '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' -) - -console_output_handler = logging.StreamHandler(sys.stderr) -console_output_handler.setFormatter(formatter) -logger.addHandler(console_output_handler) - -logger.setLevel(logging.ERROR) - from telebot import util, types, asyncio_helper import asyncio from telebot import asyncio_handler_backends From 40465643b9227e4ed10bf825672462517029007d Mon Sep 17 00:00:00 2001 From: Artem Lavrenov Date: Fri, 21 Jan 2022 12:32:23 +0300 Subject: [PATCH 0818/1808] Add timer_bot sync and async example --- .../asynchronous_telebot/timer_bot_async.py | 52 +++++++++++++++++++ examples/timer_bot.py | 43 +++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 examples/asynchronous_telebot/timer_bot_async.py create mode 100644 examples/timer_bot.py diff --git a/examples/asynchronous_telebot/timer_bot_async.py b/examples/asynchronous_telebot/timer_bot_async.py new file mode 100644 index 000000000..1d1da6584 --- /dev/null +++ b/examples/asynchronous_telebot/timer_bot_async.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 + +# This is a simple bot with schedule timer +# https://github.com/ibrb/python-aioschedule +# https://schedule.readthedocs.io + +import asyncio +import os, aioschedule +from telebot.async_telebot import AsyncTeleBot + +API_TOKEN = '' +bot = AsyncTeleBot(API_TOKEN) + + +async def beep(chat_id) -> None: + """Send the beep message.""" + await bot.send_message(chat_id, text='Beep!') + aioschedule.clear(chat_id) # return schedule.CancelJob not working in aioschedule use tag for delete + + +@bot.message_handler(commands=['help', 'start']) +async def send_welcome(message): + await bot.reply_to(message, "Hi! Use /set to set a timer") + + +@bot.message_handler(commands=['set']) +async def set_timer(message): + args = message.text.split() + if len(args) > 1 and args[1].isdigit(): + sec = int(args[1]) + aioschedule.every(sec).seconds.do(beep, message.chat.id).tag(message.chat.id) + else: + await bot.reply_to(message, 'Usage: /set ') + + +@bot.message_handler(commands=['unset']) +def unset_timer(message): + aioschedule.clean(message.chat.id) + + +async def scheduler(): + while True: + await aioschedule.run_pending() + await asyncio.sleep(1) + + +async def main(): + await asyncio.gather(bot.infinity_polling(), scheduler()) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/timer_bot.py b/examples/timer_bot.py new file mode 100644 index 000000000..85f669751 --- /dev/null +++ b/examples/timer_bot.py @@ -0,0 +1,43 @@ +#!/usr/bin/python + +# This is a simple bot with schedule timer +# https://schedule.readthedocs.io + +import os, time, threading, schedule +from telebot import TeleBot + +API_TOKEN = '' +bot = TeleBot(API_TOKEN) + + +@bot.message_handler(commands=['help', 'start']) +def send_welcome(message): + bot.reply_to(message, "Hi! Use /set to set a timer") + + +def beep(chat_id) -> None: + """Send the beep message.""" + bot.send_message(chat_id, text='Beep!') + return schedule.CancelJob # Run a job once + + +@bot.message_handler(commands=['set']) +def set_timer(message): + args = message.text.split() + if len(args) > 1 and args[1].isdigit(): + sec = int(args[1]) + schedule.every(sec).seconds.do(beep, message.chat.id).tag(message.chat.id) + else: + bot.reply_to(message, 'Usage: /set ') + + +@bot.message_handler(commands=['unset']) +def unset_timer(message): + schedule.clean(message.chat.id) + + +if __name__ == '__main__': + threading.Thread(target=bot.infinity_polling, name='bot_infinity_polling', daemon=True).start() + while True: + schedule.run_pending() + time.sleep(1) From a07bf86c30c9c177fee09694ce9962dcb34d005e Mon Sep 17 00:00:00 2001 From: Artem Lavrenov Date: Fri, 21 Jan 2022 21:50:33 +0300 Subject: [PATCH 0819/1808] add default None for get_my_commands parameters scope and language_code sync\async, add examples for bot.set_my_commands --- .../set_command_example.py | 36 +++++++++++++++++++ examples/set_command_example.py | 29 +++++++++++++++ telebot/__init__.py | 6 ++-- telebot/async_telebot.py | 4 +-- 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 examples/asynchronous_telebot/set_command_example.py create mode 100644 examples/set_command_example.py diff --git a/examples/asynchronous_telebot/set_command_example.py b/examples/asynchronous_telebot/set_command_example.py new file mode 100644 index 000000000..fae588d06 --- /dev/null +++ b/examples/asynchronous_telebot/set_command_example.py @@ -0,0 +1,36 @@ +#!/usr/bin/python + +# This is a set_my_commands example. +# Press on [/] button in telegram client. +# Important, to update the command menu, be sure to exit the chat with the bot and log in again +# Important, command for chat_id and for group have a higher priority than for all + +import asyncio +import os +import telebot +from telebot.async_telebot import AsyncTeleBot + + +API_TOKEN = '' +bot = AsyncTeleBot(API_TOKEN) + + +async def main(): + # use in for delete with the necessary scope and language_code if necessary + await bot.delete_my_commands(scope=None, language_code=None) + + await bot.set_my_commands( + commands=[ + telebot.types.BotCommand("command1", "command1 description"), + telebot.types.BotCommand("command2", "command2 description") + ], + # scope=telebot.types.BotCommandScopeChat(12345678) # use for personal command menu for users + # scope=telebot.types.BotCommandScopeAllPrivateChats() # use for all private chats + ) + + cmd = await bot.get_my_commands(scope=None, language_code=None) + print([c.to_json() for c in cmd]) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/set_command_example.py b/examples/set_command_example.py new file mode 100644 index 000000000..ba38c1230 --- /dev/null +++ b/examples/set_command_example.py @@ -0,0 +1,29 @@ +#!/usr/bin/python + +# This is a set_my_commands example. +# Press on [/] button in telegram client. +# Important, to update the command menu, be sure to exit the chat with the bot and enter to chat again +# Important, command for chat_id and for group have a higher priority than for all + +import os +import telebot + + +API_TOKEN = '' +bot = telebot.TeleBot(API_TOKEN) + +# use in for delete with the necessary scope and language_code if necessary +bot.delete_my_commands(scope=None, language_code=None) + +bot.set_my_commands( + commands=[ + telebot.types.BotCommand("command1", "command1 description"), + telebot.types.BotCommand("command2", "command2 description") + ], + # scope=telebot.types.BotCommandScopeChat(12345678) # use for personal command for users + # scope=telebot.types.BotCommandScopeAllPrivateChats() # use for all private chats +) + +# check command +cmd = bot.get_my_commands(scope=None, language_code=None) +print([c.to_json() for c in cmd]) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5fed5c192..549ac06e4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1899,8 +1899,8 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ return apihelper.delete_chat_photo(self.token, chat_id) - def get_my_commands(self, scope: Optional[types.BotCommandScope], - language_code: Optional[str]) -> List[types.BotCommand]: + def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, + language_code: Optional[str]=None) -> List[types.BotCommand]: """ Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. @@ -3415,4 +3415,4 @@ def _notify_command_handlers(self, handlers, new_messages): - \ No newline at end of file + diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6bfa799fa..689ba9ff4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2443,8 +2443,8 @@ async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ return await asyncio_helper.delete_chat_photo(self.token, chat_id) - async def get_my_commands(self, scope: Optional[types.BotCommandScope], - language_code: Optional[str]) -> List[types.BotCommand]: + async def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, + language_code: Optional[str]=None) -> List[types.BotCommand]: """ Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. From 7eb759d1fd8c61f50d2890a9a8bf9fcd8eda6ae5 Mon Sep 17 00:00:00 2001 From: Artem Lavrenov Date: Fri, 21 Jan 2022 22:25:06 +0300 Subject: [PATCH 0820/1808] remove unused import --- examples/asynchronous_telebot/set_command_example.py | 1 - examples/asynchronous_telebot/timer_bot_async.py | 2 +- examples/set_command_example.py | 1 - examples/timer_bot.py | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/asynchronous_telebot/set_command_example.py b/examples/asynchronous_telebot/set_command_example.py index fae588d06..2de53f427 100644 --- a/examples/asynchronous_telebot/set_command_example.py +++ b/examples/asynchronous_telebot/set_command_example.py @@ -6,7 +6,6 @@ # Important, command for chat_id and for group have a higher priority than for all import asyncio -import os import telebot from telebot.async_telebot import AsyncTeleBot diff --git a/examples/asynchronous_telebot/timer_bot_async.py b/examples/asynchronous_telebot/timer_bot_async.py index 1d1da6584..c263dcbac 100644 --- a/examples/asynchronous_telebot/timer_bot_async.py +++ b/examples/asynchronous_telebot/timer_bot_async.py @@ -5,7 +5,7 @@ # https://schedule.readthedocs.io import asyncio -import os, aioschedule +import aioschedule from telebot.async_telebot import AsyncTeleBot API_TOKEN = '' diff --git a/examples/set_command_example.py b/examples/set_command_example.py index ba38c1230..1573e91d3 100644 --- a/examples/set_command_example.py +++ b/examples/set_command_example.py @@ -5,7 +5,6 @@ # Important, to update the command menu, be sure to exit the chat with the bot and enter to chat again # Important, command for chat_id and for group have a higher priority than for all -import os import telebot diff --git a/examples/timer_bot.py b/examples/timer_bot.py index 85f669751..d82e2d027 100644 --- a/examples/timer_bot.py +++ b/examples/timer_bot.py @@ -3,7 +3,7 @@ # This is a simple bot with schedule timer # https://schedule.readthedocs.io -import os, time, threading, schedule +import time, threading, schedule from telebot import TeleBot API_TOKEN = '' From a3cda2e0ff9b0bd9ac0d210009a5b7c74fb36935 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 17:15:04 +0400 Subject: [PATCH 0821/1808] Updated sync and async. Fixes and new features. --- telebot/__init__.py | 151 +++++++++++----- telebot/async_telebot.py | 168 ++++++++++++------ telebot/asyncio_filters.py | 20 ++- telebot/asyncio_handler_backends.py | 200 --------------------- telebot/asyncio_helper.py | 60 ++++--- telebot/asyncio_storage/__init__.py | 13 ++ telebot/asyncio_storage/base_storage.py | 69 ++++++++ telebot/asyncio_storage/memory_storage.py | 64 +++++++ telebot/asyncio_storage/pickle_storage.py | 107 ++++++++++++ telebot/asyncio_storage/redis_storage.py | 178 +++++++++++++++++++ telebot/asyncio_types.py | 1 + telebot/custom_filters.py | 20 ++- telebot/handler_backends.py | 204 +--------------------- telebot/storage/__init__.py | 13 ++ telebot/storage/base_storage.py | 65 +++++++ telebot/storage/memory_storage.py | 64 +++++++ telebot/storage/pickle_storage.py | 112 ++++++++++++ telebot/storage/redis_storage.py | 176 +++++++++++++++++++ 18 files changed, 1160 insertions(+), 525 deletions(-) create mode 100644 telebot/asyncio_storage/__init__.py create mode 100644 telebot/asyncio_storage/base_storage.py create mode 100644 telebot/asyncio_storage/memory_storage.py create mode 100644 telebot/asyncio_storage/pickle_storage.py create mode 100644 telebot/asyncio_storage/redis_storage.py create mode 100644 telebot/asyncio_types.py create mode 100644 telebot/storage/__init__.py create mode 100644 telebot/storage/base_storage.py create mode 100644 telebot/storage/memory_storage.py create mode 100644 telebot/storage/pickle_storage.py create mode 100644 telebot/storage/redis_storage.py diff --git a/telebot/__init__.py b/telebot/__init__.py index 5fed5c192..fca501ccb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -13,7 +13,8 @@ import telebot.util import telebot.types - +# storage +from telebot.storage import StatePickleStorage, StateMemoryStorage logger = logging.getLogger('TeleBot') @@ -28,7 +29,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMemory, StateFile +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter @@ -148,7 +149,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 + suppress_middleware_excepions=False, state_storage=StateMemoryStorage() ): """ :param token: bot API token @@ -193,7 +194,7 @@ def __init__( self.custom_filters = {} self.state_handlers = [] - self.current_states = StateMemory() + self.current_states = state_storage if apihelper.ENABLE_MIDDLEWARE: @@ -251,7 +252,7 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): :param filename: Filename of saving file """ - self.current_states = StateFile(filename=filename) + self.current_states = StatePickleStorage(filename=filename) self.current_states.create_dir() def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): @@ -777,6 +778,13 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ logger.info('Stopped polling.') def _exec_task(self, task, *args, **kwargs): + if kwargs.get('task_type') == 'handler': + pass_bot = kwargs.get('pass_bot') + kwargs.pop('pass_bot') + kwargs.pop('task_type') + if pass_bot: + kwargs['bot'] = self + if self.threaded: self.worker_pool.put(task, *args, **kwargs) else: @@ -2531,40 +2539,59 @@ def register_next_step_handler( chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) - def set_state(self, chat_id: int, state: Union[int, str]): + def set_state(self, user_id: int, state: Union[int, str], chat_id: int=None) -> None: """ Sets a new state of a user. :param chat_id: :param state: new state. can be string or integer. """ - self.current_states.add_state(chat_id, state) + if chat_id is None: + chat_id = user_id + self.current_states.set_state(chat_id, user_id, state) - def delete_state(self, chat_id: int): + def reset_data(self, user_id: int, chat_id: int=None): + """ + Reset data for a user in chat. + :param user_id: + :param chat_id: + """ + if chat_id is None: + chat_id = user_id + self.current_states.reset_data(chat_id, user_id) + def delete_state(self, user_id: int, chat_id: int=None) -> None: """ Delete the current state of a user. :param chat_id: :return: """ - self.current_states.delete_state(chat_id) + if chat_id is None: + chat_id = user_id + self.current_states.delete_state(chat_id, user_id) - def retrieve_data(self, chat_id: int): - return self.current_states.retrieve_data(chat_id) + def retrieve_data(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str]]: + if chat_id is None: + chat_id = user_id + return self.current_states.get_interactive_data(chat_id, user_id) - def get_state(self, chat_id: int): + def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str]]: """ Get current state of a user. :param chat_id: :return: state of a user """ - return self.current_states.current_state(chat_id) + if chat_id is None: + chat_id = user_id + return self.current_states.get_state(chat_id, user_id) - def add_data(self, chat_id: int, **kwargs): + def add_data(self, user_id: int, chat_id:int=None, **kwargs): """ Add data to states. :param chat_id: """ + if chat_id is None: + chat_id = user_id for key, value in kwargs.items(): - self.current_states.add_data(chat_id, key, value) + self.current_states.set_data(chat_id, user_id, key, value) def register_next_step_handler_by_chat_id( self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: @@ -2632,7 +2659,7 @@ def _notify_next_handlers(self, new_messages): @staticmethod - def _build_handler_dict(handler, **filters): + def _build_handler_dict(handler, pass_bot=False, **filters): """ Builds a dictionary for a handler :param handler: @@ -2641,6 +2668,7 @@ def _build_handler_dict(handler, **filters): """ return { 'function': handler, + 'pass_bot': pass_bot, 'filters': {ftype: fvalue for ftype, fvalue in filters.items() if fvalue is not None} # Remove None values, they are skipped in _test_filter anyway #'filters': filters @@ -2686,7 +2714,7 @@ def add_middleware_handler(self, handler, update_types=None): :return: """ if not apihelper.ENABLE_MIDDLEWARE: - raise RuntimeError("Middleware is not enabled. Use apihelper.ENABLE_MIDDLEWARE.") + raise RuntimeError("Middleware is not enabled. Use apihelper.ENABLE_MIDDLEWARE before initialising TeleBot.") if update_types: for update_type in update_types: @@ -2694,6 +2722,27 @@ def add_middleware_handler(self, handler, update_types=None): else: self.default_middleware_handlers.append(handler) + # function register_middleware_handler + def register_middleware_handler(self, callback, update_types=None): + """ + Middleware handler decorator. + + This function will create a decorator that can be used to decorate functions that must be handled as middlewares before entering any other + message handlers + But, be careful and check type of the update inside the handler if more than one update_type is given + + Example: + + bot = TeleBot('TOKEN') + + bot.register_middleware_handler(print_channel_post_text, update_types=['channel_post', 'edited_channel_post']) + + :param update_types: Optional list of update types that can be passed into the middleware handler. + + """ + + self.add_middleware_handler(callback, update_types) + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Message handler decorator. @@ -2766,7 +2815,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers message handler. :param callback: function to be called @@ -2775,6 +2824,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param regexp: :param func: :param chat_types: True for private chat + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ if isinstance(commands, str): @@ -2791,6 +2841,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_message_handler(handler_dict) @@ -2838,7 +2889,7 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers edited message handler. :param callback: function to be called @@ -2847,6 +2898,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param regexp: :param func: :param chat_types: True for private chat + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ if isinstance(commands, str): @@ -2863,6 +2915,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_edited_message_handler(handler_dict) @@ -2908,7 +2961,7 @@ def add_channel_post_handler(self, handler_dict): """ self.channel_post_handlers.append(handler_dict) - def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers channel post message handler. :param callback: function to be called @@ -2916,6 +2969,7 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N :param commands: list of commands :param regexp: :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ if isinstance(commands, str): @@ -2931,6 +2985,7 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_channel_post_handler(handler_dict) @@ -2975,7 +3030,7 @@ def add_edited_channel_post_handler(self, handler_dict): """ self.edited_channel_post_handlers.append(handler_dict) - def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers edited channel post message handler. :param callback: function to be called @@ -2983,6 +3038,7 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com :param commands: list of commands :param regexp: :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ if isinstance(commands, str): @@ -2998,6 +3054,7 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_edited_channel_post_handler(handler_dict) @@ -3024,14 +3081,15 @@ def add_inline_handler(self, handler_dict): """ self.inline_handlers.append(handler_dict) - def register_inline_handler(self, callback, func, **kwargs): + def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers inline handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_inline_handler(handler_dict) def chosen_inline_handler(self, func, **kwargs): @@ -3057,14 +3115,15 @@ def add_chosen_inline_handler(self, handler_dict): """ self.chosen_inline_handlers.append(handler_dict) - def register_chosen_inline_handler(self, callback, func, **kwargs): + def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers chosen inline handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chosen_inline_handler(handler_dict) def callback_query_handler(self, func, **kwargs): @@ -3090,14 +3149,15 @@ def add_callback_query_handler(self, handler_dict): """ self.callback_query_handlers.append(handler_dict) - def register_callback_query_handler(self, callback, func, **kwargs): + def register_callback_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers callback query handler.. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_callback_query_handler(handler_dict) def shipping_query_handler(self, func, **kwargs): @@ -3123,14 +3183,15 @@ def add_shipping_query_handler(self, handler_dict): """ self.shipping_query_handlers.append(handler_dict) - def register_shipping_query_handler(self, callback, func, **kwargs): + def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers shipping query handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_shipping_query_handler(handler_dict) def pre_checkout_query_handler(self, func, **kwargs): @@ -3156,14 +3217,15 @@ def add_pre_checkout_query_handler(self, handler_dict): """ self.pre_checkout_query_handlers.append(handler_dict) - def register_pre_checkout_query_handler(self, callback, func, **kwargs): + def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers pre-checkout request handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_pre_checkout_query_handler(handler_dict) def poll_handler(self, func, **kwargs): @@ -3189,14 +3251,15 @@ def add_poll_handler(self, handler_dict): """ self.poll_handlers.append(handler_dict) - def register_poll_handler(self, callback, func, **kwargs): + def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_handler(handler_dict) def poll_answer_handler(self, func=None, **kwargs): @@ -3222,14 +3285,15 @@ def add_poll_answer_handler(self, handler_dict): """ self.poll_answer_handlers.append(handler_dict) - def register_poll_answer_handler(self, callback, func, **kwargs): + def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll answer handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_answer_handler(handler_dict) def my_chat_member_handler(self, func=None, **kwargs): @@ -3255,14 +3319,15 @@ def add_my_chat_member_handler(self, handler_dict): """ self.my_chat_member_handlers.append(handler_dict) - def register_my_chat_member_handler(self, callback, func=None, **kwargs): + def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers my chat member handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_my_chat_member_handler(handler_dict) def chat_member_handler(self, func=None, **kwargs): @@ -3288,14 +3353,15 @@ def add_chat_member_handler(self, handler_dict): """ self.chat_member_handlers.append(handler_dict) - def register_chat_member_handler(self, callback, func=None, **kwargs): + def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat member handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_member_handler(handler_dict) def chat_join_request_handler(self, func=None, **kwargs): @@ -3321,14 +3387,15 @@ def add_chat_join_request_handler(self, handler_dict): """ self.chat_join_request_handlers.append(handler_dict) - def register_chat_join_request_handler(self, callback, func=None, **kwargs): + def register_chat_join_request_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat join request handler. :param callback: function to be called :param func: + :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_join_request_handler(handler_dict) def _test_message_handler(self, message_handler, message): @@ -3409,7 +3476,7 @@ def _notify_command_handlers(self, handlers, new_messages): for message in new_messages: for message_handler in handlers: if self._test_message_handler(message_handler, message): - self._exec_task(message_handler['function'], message) + self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') break diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6bfa799fa..f741f63d1 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -13,6 +13,9 @@ import telebot.types +# storages +from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage + from inspect import signature from telebot import logger @@ -161,7 +164,7 @@ class AsyncTeleBot: """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, - exception_handler=None) -> None: # TODO: ADD TYPEHINTS + exception_handler=None, states_storage=StateMemoryStorage()) -> None: # TODO: ADD TYPEHINTS self.token = token self.offset = offset @@ -190,12 +193,13 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, self.custom_filters = {} self.state_handlers = [] - self.current_states = asyncio_handler_backends.StateMemory() + self.current_states = states_storage self.middlewares = [] - + async def close_session(self): + await asyncio_helper.session_manager.session.close() async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> List[types.Update]: json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) @@ -299,7 +303,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout, request_timeout=request_timeout) if updates: self.offset = updates[-1].update_id + 1 - self._loop_create_task(self.process_new_updates(updates)) # Seperate task for processing updates + asyncio.create_task(self.process_new_updates(updates)) # Seperate task for processing updates if interval: await asyncio.sleep(interval) except KeyboardInterrupt: @@ -322,6 +326,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: continue else: break + except KeyboardInterrupt: + return except Exception as e: logger.error('Cause exception while getting updates.') if non_stop: @@ -333,6 +339,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: finally: self._polling = False + await self.close_session() logger.warning('Polling is stopped.') @@ -346,31 +353,47 @@ async def _process_updates(self, handlers, messages, update_type): :param messages: :return: """ + tasks = [] for message in messages: middleware = await self.process_middlewares(message, update_type) - self._loop_create_task(self._run_middlewares_and_handlers(handlers, message, middleware)) + tasks.append(self._run_middlewares_and_handlers(handlers, message, middleware)) + asyncio.gather(*tasks) async def _run_middlewares_and_handlers(self, handlers, message, middleware): handler_error = None data = {} - for message_handler in handlers: - process_update = await self._test_message_handler(message_handler, message) + process_handler = True + middleware_result = await middleware.pre_process(message, data) + if isinstance(middleware_result, SkipHandler): + await middleware.post_process(message, data, handler_error) + process_handler = False + if isinstance(middleware_result, CancelUpdate): + return + for handler in handlers: + if not process_handler: + break + + process_update = await self._test_message_handler(handler, message) if not process_update: continue elif process_update: - if middleware: - middleware_result = await middleware.pre_process(message, data) - if isinstance(middleware_result, SkipHandler): - await middleware.post_process(message, data, handler_error) - break - if isinstance(middleware_result, CancelUpdate): - return try: - if "data" in signature(message_handler['function']).parameters: - await message_handler['function'](message, data) - else: - await message_handler['function'](message) + params = [] + + for i in signature(handler['function']).parameters: + params.append(i) + if len(params) == 1: + await handler['function'](message) + break + if params[1] == 'data' and handler.get('pass_bot') is True: + await handler['function'](message, data, self) + break + elif params[1] == 'data' and handler.get('pass_bot') is False: + await handler['function'](message, data) + break + elif params[1] != 'data' and handler.get('pass_bot') is True: + await handler['function'](message, self) break except Exception as e: handler_error = e @@ -687,7 +710,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers message handler. :param callback: function to be called @@ -696,8 +719,11 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param regexp: :param func: :param chat_types: True for private chat + :param pass_bot: True if you want to get TeleBot instance in your handler :return: decorated function """ + if content_types is None: + content_types = ["text"] if isinstance(commands, str): logger.warning("register_message_handler: 'commands' filter should be List of strings (commands), not string.") commands = [commands] @@ -712,6 +738,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_message_handler(handler_dict) @@ -759,7 +786,7 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, **kwargs): + def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers edited message handler. :param callback: function to be called @@ -784,6 +811,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_edited_message_handler(handler_dict) @@ -829,7 +857,7 @@ def add_channel_post_handler(self, handler_dict): """ self.channel_post_handlers.append(handler_dict) - def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers channel post message handler. :param callback: function to be called @@ -852,6 +880,7 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_channel_post_handler(handler_dict) @@ -896,7 +925,7 @@ def add_edited_channel_post_handler(self, handler_dict): """ self.edited_channel_post_handlers.append(handler_dict) - def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, **kwargs): + def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers edited channel post message handler. :param callback: function to be called @@ -919,6 +948,7 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_edited_channel_post_handler(handler_dict) @@ -945,14 +975,14 @@ def add_inline_handler(self, handler_dict): """ self.inline_handlers.append(handler_dict) - def register_inline_handler(self, callback, func, **kwargs): + def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers inline handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_inline_handler(handler_dict) def chosen_inline_handler(self, func, **kwargs): @@ -978,14 +1008,14 @@ def add_chosen_inline_handler(self, handler_dict): """ self.chosen_inline_handlers.append(handler_dict) - def register_chosen_inline_handler(self, callback, func, **kwargs): + def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers chosen inline handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chosen_inline_handler(handler_dict) def callback_query_handler(self, func, **kwargs): @@ -1011,14 +1041,14 @@ def add_callback_query_handler(self, handler_dict): """ self.callback_query_handlers.append(handler_dict) - def register_callback_query_handler(self, callback, func, **kwargs): + def register_callback_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers callback query handler.. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_callback_query_handler(handler_dict) def shipping_query_handler(self, func, **kwargs): @@ -1044,14 +1074,14 @@ def add_shipping_query_handler(self, handler_dict): """ self.shipping_query_handlers.append(handler_dict) - def register_shipping_query_handler(self, callback, func, **kwargs): + def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers shipping query handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_shipping_query_handler(handler_dict) def pre_checkout_query_handler(self, func, **kwargs): @@ -1077,14 +1107,14 @@ def add_pre_checkout_query_handler(self, handler_dict): """ self.pre_checkout_query_handlers.append(handler_dict) - def register_pre_checkout_query_handler(self, callback, func, **kwargs): + def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers pre-checkout request handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_pre_checkout_query_handler(handler_dict) def poll_handler(self, func, **kwargs): @@ -1110,14 +1140,14 @@ def add_poll_handler(self, handler_dict): """ self.poll_handlers.append(handler_dict) - def register_poll_handler(self, callback, func, **kwargs): + def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_handler(handler_dict) def poll_answer_handler(self, func=None, **kwargs): @@ -1143,14 +1173,14 @@ def add_poll_answer_handler(self, handler_dict): """ self.poll_answer_handlers.append(handler_dict) - def register_poll_answer_handler(self, callback, func, **kwargs): + def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll answer handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_answer_handler(handler_dict) def my_chat_member_handler(self, func=None, **kwargs): @@ -1176,14 +1206,14 @@ def add_my_chat_member_handler(self, handler_dict): """ self.my_chat_member_handlers.append(handler_dict) - def register_my_chat_member_handler(self, callback, func=None, **kwargs): + def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers my chat member handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_my_chat_member_handler(handler_dict) def chat_member_handler(self, func=None, **kwargs): @@ -1209,14 +1239,14 @@ def add_chat_member_handler(self, handler_dict): """ self.chat_member_handlers.append(handler_dict) - def register_chat_member_handler(self, callback, func=None, **kwargs): + def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat member handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_member_handler(handler_dict) def chat_join_request_handler(self, func=None, **kwargs): @@ -1242,18 +1272,18 @@ def add_chat_join_request_handler(self, handler_dict): """ self.chat_join_request_handlers.append(handler_dict) - def register_chat_join_request_handler(self, callback, func=None, **kwargs): + def register_chat_join_request_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat join request handler. :param callback: function to be called :param func: :return: decorated function """ - handler_dict = self._build_handler_dict(callback, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_join_request_handler(handler_dict) @staticmethod - def _build_handler_dict(handler, **filters): + def _build_handler_dict(handler, pass_bot=False, **filters): """ Builds a dictionary for a handler :param handler: @@ -1262,6 +1292,7 @@ def _build_handler_dict(handler, **filters): """ return { 'function': handler, + 'pass_bot': pass_bot, 'filters': {ftype: fvalue for ftype, fvalue in filters.items() if fvalue is not None} # Remove None values, they are skipped in _test_filter anyway #'filters': filters @@ -1324,8 +1355,7 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): :param filename: Filename of saving file """ - self.current_states = asyncio_handler_backends.StateFile(filename=filename) - self.current_states.create_dir() + self.current_states = StatePickleStorage(file_path=filename) async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, drop_pending_updates = None, timeout=None): @@ -1356,6 +1386,8 @@ async def set_webhook(self, url=None, certificate=None, max_connections=None, al return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout) + + async def delete_webhook(self, drop_pending_updates=None, timeout=None): """ Use this method to remove webhook integration if you decide to switch back to getUpdates. @@ -1366,6 +1398,12 @@ async def delete_webhook(self, drop_pending_updates=None, timeout=None): """ return await asyncio_helper.delete_webhook(self.token, drop_pending_updates, timeout) + async def remove_webhook(self): + """ + Alternative for delete_webhook but uses set_webhook + """ + self.set_webhook() + async def get_webhook_info(self, timeout=None): """ Use this method to get current webhook status. Requires no parameters. @@ -3019,37 +3057,57 @@ async def delete_sticker_from_set(self, sticker: str) -> bool: return await asyncio_helper.delete_sticker_from_set(self.token, sticker) - async def set_state(self, chat_id, state): + async def set_state(self, user_id: int, state: str, chat_id: int=None): """ Sets a new state of a user. :param chat_id: :param state: new state. can be string or integer. """ - await self.current_states.add_state(chat_id, state) + if not chat_id: + chat_id = user_id + await self.current_states.set_state(chat_id, user_id, state) + + async def reset_data(self, user_id: int, chat_id: int=None): + """ + Reset data for a user in chat. + :param user_id: + :param chat_id: + """ + if chat_id is None: + chat_id = user_id + await self.current_states.reset_data(chat_id, user_id) - async def delete_state(self, chat_id): + async def delete_state(self, user_id: int, chat_id:int=None): """ Delete the current state of a user. :param chat_id: :return: """ - await self.current_states.delete_state(chat_id) + if not chat_id: + chat_id = user_id + await self.current_states.delete_state(chat_id, user_id) - def retrieve_data(self, chat_id): - return self.current_states.retrieve_data(chat_id) + def retrieve_data(self, user_id: int, chat_id: int=None): + if not chat_id: + chat_id = user_id + return self.current_states.get_interactive_data(chat_id, user_id) - async def get_state(self, chat_id): + async def get_state(self, user_id, chat_id: int=None): """ Get current state of a user. :param chat_id: :return: state of a user """ - return await self.current_states.current_state(chat_id) + if not chat_id: + chat_id = user_id + return await self.current_states.get_state(chat_id, user_id) - async def add_data(self, chat_id, **kwargs): + async def add_data(self, user_id: int, chat_id: int=None, **kwargs): """ Add data to states. :param chat_id: """ + if not chat_id: + chat_id = user_id for key, value in kwargs.items(): - await self.current_states.add_data(chat_id, key, value) + await self.current_states.set_data(chat_id, user_id, key, value) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index cce7017c6..5b193fdf0 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -159,11 +159,21 @@ def __init__(self, bot): key = 'state' async def check(self, message, text): - result = await self.bot.current_states.current_state(message.from_user.id) - if result is False: return False - elif text == '*': return True - elif type(text) is list: return result in text - return result == text + if text == '*': return True + if message.chat.type == 'group': + group_state = await self.bot.current_states.get_state(message.chat.id, message.from_user.id) + if group_state == text: + return True + elif group_state in text and type(text) is list: + return True + + + else: + user_state = await self.bot.current_states.get_state(message.chat.id,message.from_user.id) + if user_state == text: + return True + elif type(text) is list and user_state in text: + return True class IsDigitFilter(SimpleCustomFilter): """ diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 0a78a9000..08db40f85 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -3,206 +3,6 @@ -class StateMemory: - def __init__(self): - self._states = {} - - async def add_state(self, chat_id, state): - """ - Add a state. - :param chat_id: - :param state: new state - """ - if chat_id in self._states: - - self._states[chat_id]['state'] = state - else: - self._states[chat_id] = {'state': state,'data': {}} - - async def current_state(self, chat_id): - """Current state""" - if chat_id in self._states: return self._states[chat_id]['state'] - else: return False - - async def delete_state(self, chat_id): - """Delete a state""" - self._states.pop(chat_id) - - def get_data(self, chat_id): - return self._states[chat_id]['data'] - - async def set(self, chat_id, new_state): - """ - Set a new state for a user. - :param chat_id: - :param new_state: new_state of a user - """ - await self.add_state(chat_id,new_state) - - async def add_data(self, chat_id, key, value): - result = self._states[chat_id]['data'][key] = value - return result - - async def finish(self, chat_id): - """ - Finish(delete) state of a user. - :param chat_id: - """ - await self.delete_state(chat_id) - - def retrieve_data(self, chat_id): - """ - Save input text. - - Usage: - with bot.retrieve_data(message.chat.id) as data: - data['name'] = message.text - - Also, at the end of your 'Form' you can get the name: - data['name'] - """ - return StateContext(self, chat_id) - - -class StateFile: - """ - Class to save states in a file. - """ - def __init__(self, filename): - self.file_path = filename - - async def add_state(self, chat_id, state): - """ - Add a state. - :param chat_id: - :param state: new state - """ - states_data = self.read_data() - if chat_id in states_data: - states_data[chat_id]['state'] = state - return await self.save_data(states_data) - else: - states_data[chat_id] = {'state': state,'data': {}} - return await self.save_data(states_data) - - - async def current_state(self, chat_id): - """Current state.""" - states_data = self.read_data() - if chat_id in states_data: return states_data[chat_id]['state'] - else: return False - - async def delete_state(self, chat_id): - """Delete a state""" - states_data = self.read_data() - states_data.pop(chat_id) - await self.save_data(states_data) - - def read_data(self): - """ - Read the data from file. - """ - file = open(self.file_path, 'rb') - states_data = pickle.load(file) - file.close() - return states_data - - def create_dir(self): - """ - Create directory .save-handlers. - """ - dirs = self.file_path.rsplit('/', maxsplit=1)[0] - os.makedirs(dirs, exist_ok=True) - if not os.path.isfile(self.file_path): - with open(self.file_path,'wb') as file: - pickle.dump({}, file) - - async def save_data(self, new_data): - """ - Save data after editing. - :param new_data: - """ - with open(self.file_path, 'wb+') as state_file: - pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL) - return True - - def get_data(self, chat_id): - return self.read_data()[chat_id]['data'] - - async def set(self, chat_id, new_state): - """ - Set a new state for a user. - :param chat_id: - :param new_state: new_state of a user - - """ - await self.add_state(chat_id,new_state) - - async def add_data(self, chat_id, key, value): - states_data = self.read_data() - result = states_data[chat_id]['data'][key] = value - await self.save_data(result) - - return result - - async def finish(self, chat_id): - """ - Finish(delete) state of a user. - :param chat_id: - """ - await self.delete_state(chat_id) - - def retrieve_data(self, chat_id): - """ - Save input text. - - Usage: - with bot.retrieve_data(message.chat.id) as data: - data['name'] = message.text - - Also, at the end of your 'Form' you can get the name: - data['name'] - """ - return StateFileContext(self, chat_id) - - -class StateContext: - """ - Class for data. - """ - def __init__(self , obj: StateMemory, chat_id) -> None: - self.obj = obj - self.chat_id = chat_id - self.data = obj.get_data(chat_id) - - async def __aenter__(self): - return self.data - - async def __aexit__(self, exc_type, exc_val, exc_tb): - return - -class StateFileContext: - """ - Class for data. - """ - def __init__(self , obj: StateFile, chat_id) -> None: - self.obj = obj - self.chat_id = chat_id - self.data = None - - async def __aenter__(self): - self.data = self.obj.get_data(self.chat_id) - return self.data - - async def __aexit__(self, exc_type, exc_val, exc_tb): - old_data = self.obj.read_data() - for i in self.data: - old_data[self.chat_id]['data'][i] = self.data.get(i) - await self.obj.save_data(old_data) - - return - - class BaseMiddleware: """ Base class for middleware. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index e36a97422..fe4c13e22 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -12,16 +12,8 @@ from datetime import datetime import telebot -from telebot import util +from telebot import util, logger -class SessionBase: - def __init__(self) -> None: - self.session = None - async def _get_new_session(self): - self.session = aiohttp.ClientSession() - return self.session - -session_manager = SessionBase() proxy = None session = None @@ -36,6 +28,29 @@ async def _get_new_session(self): MAX_RETRIES = 3 logger = telebot.logger + +REQUEST_LIMIT = 50 + +class SessionManager: + def __init__(self) -> None: + self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=REQUEST_LIMIT)) + + async def create_session(self): + self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=REQUEST_LIMIT)) + return self.session + + async def get_session(self): + if self.session.closed: + self.session = await self.create_session() + + if not self.session._loop.is_running(): + await self.session.close() + self.session = await self.create_session() + return self.session + + +session_manager = SessionManager() + async def _process_request(token, url, method='get', params=None, files=None, request_timeout=None): params = prepare_data(params, files) if request_timeout is None: @@ -43,19 +58,21 @@ async def _process_request(token, url, method='get', params=None, files=None, re timeout = aiohttp.ClientTimeout(total=request_timeout) got_result = False current_try=0 - async with await session_manager._get_new_session() as session: - while not got_result and current_try None: + pass + + async def set_data(self, chat_id, user_id, key, value): + """ + Set data for a user in a particular chat. + """ + raise NotImplementedError + + async def get_data(self, chat_id, user_id): + """ + Get data for a user in a particular chat. + """ + raise NotImplementedError + + async def set_state(self, chat_id, user_id, state): + """ + Set state for a particular user. + + ! Note that you should create a + record if it does not exist, and + if a record with state already exists, + you need to update a record. + """ + raise NotImplementedError + + async def delete_state(self, chat_id, user_id): + """ + Delete state for a particular user. + """ + raise NotImplementedError + + async def reset_data(self, chat_id, user_id): + """ + Reset data for a particular user in a chat. + """ + raise NotImplementedError + + async def get_state(self, chat_id, user_id): + raise NotImplementedError + + async def save(chat_id, user_id, data): + raise NotImplementedError + + + +class StateContext: + """ + Class for data. + """ + + def __init__(self, obj, chat_id, user_id): + self.obj = obj + self.data = None + self.chat_id = chat_id + self.user_id = user_id + + + + async def __aenter__(self): + self.data = copy.deepcopy(await self.obj.get_data(self.chat_id, self.user_id)) + return self.data + + async def __aexit__(self, exc_type, exc_val, exc_tb): + return await self.obj.save(self.chat_id, self.user_id, self.data) \ No newline at end of file diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py new file mode 100644 index 000000000..ab9c48642 --- /dev/null +++ b/telebot/asyncio_storage/memory_storage.py @@ -0,0 +1,64 @@ +from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext + +class StateMemoryStorage(StateStorageBase): + def __init__(self) -> None: + self.data = {} + # + # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} + + + async def set_state(self, chat_id, user_id, state): + if chat_id in self.data: + if user_id in self.data[chat_id]: + self.data[chat_id][user_id]['state'] = state + return True + else: + self.data[chat_id][user_id] = {'state': state, 'data': {}} + return True + self.data[chat_id] = {user_id: {'state': state, 'data': {}}} + return True + + async def delete_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + del self.data[chat_id][user_id] + if chat_id == user_id: + del self.data[chat_id] + + return True + + return False + + + async def get_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['state'] + + return None + async def get_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['data'] + + return None + + async def reset_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'] = {} + return True + return False + + async def set_data(self, chat_id, user_id, key, value): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'][key] = value + return True + raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + + def get_interactive_data(self, chat_id, user_id): + return StateContext(self, chat_id, user_id) + + async def save(self, chat_id, user_id, data): + self.data[chat_id][user_id]['data'] = data \ No newline at end of file diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py new file mode 100644 index 000000000..81ef46cd4 --- /dev/null +++ b/telebot/asyncio_storage/pickle_storage.py @@ -0,0 +1,107 @@ +from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext +import os + + +import pickle + + +class StatePickleStorage(StateStorageBase): + def __init__(self, file_path="./.state-save/states.pkl") -> None: + self.file_path = file_path + self.create_dir() + self.data = self.read() + + async def convert_old_to_new(self): + # old looks like: + # {1: {'state': 'start', 'data': {'name': 'John'}} + # we should update old version pickle to new. + # new looks like: + # {1: {2: {'state': 'start', 'data': {'name': 'John'}}}} + new_data = {} + for key, value in self.data.items(): + # this returns us id and dict with data and state + new_data[key] = {key: value} # convert this to new + # pass it to global data + self.data = new_data + self.update_data() # update data in file + + def create_dir(self): + """ + Create directory .save-handlers. + """ + dirs = self.file_path.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + if not os.path.isfile(self.file_path): + with open(self.file_path,'wb') as file: + pickle.dump({}, file) + + def read(self): + file = open(self.file_path, 'rb') + data = pickle.load(file) + file.close() + return data + + def update_data(self): + file = open(self.file_path, 'wb+') + pickle.dump(self.data, file, protocol=pickle.HIGHEST_PROTOCOL) + file.close() + + async def set_state(self, chat_id, user_id, state): + if chat_id in self.data: + if user_id in self.data[chat_id]: + self.data[chat_id][user_id]['state'] = state + return True + else: + self.data[chat_id][user_id] = {'state': state, 'data': {}} + return True + self.data[chat_id] = {user_id: {'state': state, 'data': {}}} + self.update_data() + return True + + async def delete_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + del self.data[chat_id][user_id] + if chat_id == user_id: + del self.data[chat_id] + self.update_data() + return True + + return False + + + async def get_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['state'] + + return None + async def get_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['data'] + + return None + + async def reset_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'] = {} + self.update_data() + return True + return False + + async def set_data(self, chat_id, user_id, key, value): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'][key] = value + self.update_data() + return True + raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + + def get_interactive_data(self, chat_id, user_id): + return StateContext(self, chat_id, user_id) + + async def save(self, chat_id, user_id, data): + self.data[chat_id][user_id]['data'] = data + self.update_data() \ No newline at end of file diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py new file mode 100644 index 000000000..f7ca0fa23 --- /dev/null +++ b/telebot/asyncio_storage/redis_storage.py @@ -0,0 +1,178 @@ +from pickle import FALSE +from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext +import json + +redis_installed = True +try: + import aioredis +except: + redis_installed = False + + +class StateRedisStorage(StateStorageBase): + """ + This class is for Redis storage. + This will work only for states. + To use it, just pass this class to: + TeleBot(storage=StateRedisStorage()) + """ + def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_'): + if not redis_installed: + raise ImportError('AioRedis is not installed. Install it via "pip install aioredis"') + + + aioredis_version = tuple(map(int, aioredis.__version__.split(".")[0])) + if aioredis_version < (2,): + raise ImportError('Invalid aioredis version. Aioredis version should be >= 2.0.0') + self.redis = aioredis.Redis(host=host, port=port, db=db, password=password) + + self.prefix = prefix + #self.con = Redis(connection_pool=self.redis) -> use this when necessary + # + # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} + + async def get_record(self, key): + """ + Function to get record from database. + It has nothing to do with states. + Made for backend compatibility + """ + result = await self.redis.get(self.prefix+str(key)) + if result: return json.loads(result) + return + + async def set_record(self, key, value): + """ + Function to set record to database. + It has nothing to do with states. + Made for backend compatibility + """ + + await self.redis.set(self.prefix+str(key), json.dumps(value)) + return True + + async def delete_record(self, key): + """ + Function to delete record from database. + It has nothing to do with states. + Made for backend compatibility + """ + await self.redis.delete(self.prefix+str(key)) + return True + + async def set_state(self, chat_id, user_id, state): + """ + Set state for a particular user in a chat. + """ + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['state'] = state + else: + response[user_id] = {'state': state, 'data': {}} + else: + response = {user_id: {'state': state, 'data': {}}} + await self.set_record(chat_id, response) + + return True + + async def delete_state(self, chat_id, user_id): + """ + Delete state for a particular user in a chat. + """ + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + del response[user_id] + if user_id == str(chat_id): + await self.delete_record(chat_id) + return True + else: await self.set_record(chat_id, response) + return True + return False + + + async def get_value(self, chat_id, user_id, key): + """ + Get value for a data of a user in a chat. + """ + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + if key in response[user_id]['data']: + return response[user_id]['data'][key] + return None + + + async def get_state(self, chat_id, user_id): + """ + Get state of a user in a chat. + """ + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + return response[user_id]['state'] + + return None + + + async def get_data(self, chat_id, user_id): + """ + Get data of particular user in a particular chat. + """ + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + return response[user_id]['data'] + return None + + + async def reset_data(self, chat_id, user_id): + """ + Reset data of a user in a chat. + """ + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['data'] = {} + await self.set_record(chat_id, response) + return True + + + + + async def set_data(self, chat_id, user_id, key, value): + """ + Set data without interactive data. + """ + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['data'][key] = value + await self.set_record(chat_id, response) + return True + return False + + def get_interactive_data(self, chat_id, user_id): + """ + Get Data in interactive way. + You can use with() with this function. + """ + return StateContext(self, chat_id, user_id) + + async def save(self, chat_id, user_id, data): + response = await self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['data'] = dict(data, **response[user_id]['data']) + await self.set_record(chat_id, response) + return True + \ No newline at end of file diff --git a/telebot/asyncio_types.py b/telebot/asyncio_types.py new file mode 100644 index 000000000..9fe798cd7 --- /dev/null +++ b/telebot/asyncio_types.py @@ -0,0 +1 @@ +# planned \ No newline at end of file diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 0b9552359..147596ca7 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -158,11 +158,21 @@ def __init__(self, bot): key = 'state' def check(self, message, text): - if self.bot.current_states.current_state(message.from_user.id) is False: return False - elif text == '*': return True - elif type(text) is list: return self.bot.current_states.current_state(message.from_user.id) in text - return self.bot.current_states.current_state(message.from_user.id) == text - + if text == '*': return True + if message.chat.type == 'group': + group_state = self.bot.current_states.get_state(message.chat.id, message.from_user.id) + if group_state == text: + return True + elif group_state in text and type(text) is list: + return True + + + else: + user_state = self.bot.current_states.get_state(message.chat.id,message.from_user.id) + if user_state == text: + return True + elif type(text) is list and user_state in text: + return True class IsDigitFilter(SimpleCustomFilter): """ Filter to check whether the string is made up of only digits. diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 1e6787045..df4d37fa7 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -3,6 +3,11 @@ import threading from telebot import apihelper +try: + from redis import Redis + redis_installed = True +except: + redis_installed = False class HandlerBackend(object): @@ -116,7 +121,8 @@ def return_load_handlers(filename, del_file_after_loading=True): class RedisHandlerBackend(HandlerBackend): def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot', password=None): super(RedisHandlerBackend, self).__init__(handlers) - from redis import Redis + if not redis_installed: + raise Exception("Redis is not installed. Install it via 'pip install redis'") self.prefix = prefix self.redis = Redis(host, port, db, password) @@ -142,198 +148,4 @@ def get_handlers(self, handler_group_id): self.clear_handlers(handler_group_id) return handlers - -class StateMemory: - def __init__(self): - self._states = {} - - def add_state(self, chat_id, state): - """ - Add a state. - :param chat_id: - :param state: new state - """ - if chat_id in self._states: - - self._states[chat_id]['state'] = state - else: - self._states[chat_id] = {'state': state,'data': {}} - - def current_state(self, chat_id): - """Current state""" - if chat_id in self._states: return self._states[chat_id]['state'] - else: return False - - def delete_state(self, chat_id): - """Delete a state""" - self._states.pop(chat_id) - - def get_data(self, chat_id): - return self._states[chat_id]['data'] - - def set(self, chat_id, new_state): - """ - Set a new state for a user. - :param chat_id: - :param new_state: new_state of a user - """ - self.add_state(chat_id,new_state) - - def add_data(self, chat_id, key, value): - result = self._states[chat_id]['data'][key] = value - return result - - def finish(self, chat_id): - """ - Finish(delete) state of a user. - :param chat_id: - """ - self.delete_state(chat_id) - - def retrieve_data(self, chat_id): - """ - Save input text. - - Usage: - with bot.retrieve_data(message.chat.id) as data: - data['name'] = message.text - - Also, at the end of your 'Form' you can get the name: - data['name'] - """ - return StateContext(self, chat_id) - - -class StateFile: - """ - Class to save states in a file. - """ - def __init__(self, filename): - self.file_path = filename - - def add_state(self, chat_id, state): - """ - Add a state. - :param chat_id: - :param state: new state - """ - states_data = self.read_data() - if chat_id in states_data: - states_data[chat_id]['state'] = state - return self.save_data(states_data) - else: - states_data[chat_id] = {'state': state,'data': {}} - return self.save_data(states_data) - - def current_state(self, chat_id): - """Current state.""" - states_data = self.read_data() - if chat_id in states_data: return states_data[chat_id]['state'] - else: return False - - def delete_state(self, chat_id): - """Delete a state""" - states_data = self.read_data() - states_data.pop(chat_id) - self.save_data(states_data) - - def read_data(self): - """ - Read the data from file. - """ - file = open(self.file_path, 'rb') - states_data = pickle.load(file) - file.close() - return states_data - - def create_dir(self): - """ - Create directory .save-handlers. - """ - dirs = self.file_path.rsplit('/', maxsplit=1)[0] - os.makedirs(dirs, exist_ok=True) - if not os.path.isfile(self.file_path): - with open(self.file_path,'wb') as file: - pickle.dump({}, file) - - def save_data(self, new_data): - """ - Save data after editing. - :param new_data: - """ - with open(self.file_path, 'wb+') as state_file: - pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL) - return True - - def get_data(self, chat_id): - return self.read_data()[chat_id]['data'] - - def set(self, chat_id, new_state): - """ - Set a new state for a user. - :param chat_id: - :param new_state: new_state of a user - """ - self.add_state(chat_id,new_state) - - def add_data(self, chat_id, key, value): - states_data = self.read_data() - result = states_data[chat_id]['data'][key] = value - self.save_data(result) - return result - - def finish(self, chat_id): - """ - Finish(delete) state of a user. - :param chat_id: - """ - self.delete_state(chat_id) - - def retrieve_data(self, chat_id): - """ - Save input text. - - Usage: - with bot.retrieve_data(message.chat.id) as data: - data['name'] = message.text - - Also, at the end of your 'Form' you can get the name: - data['name'] - """ - return StateFileContext(self, chat_id) - - -class StateContext: - """ - Class for data. - """ - def __init__(self , obj: StateMemory, chat_id) -> None: - self.obj = obj - self.chat_id = chat_id - self.data = obj.get_data(chat_id) - - def __enter__(self): - return self.data - - def __exit__(self, exc_type, exc_val, exc_tb): - return - - -class StateFileContext: - """ - Class for data. - """ - def __init__(self , obj: StateFile, chat_id) -> None: - self.obj = obj - self.chat_id = chat_id - self.data = self.obj.get_data(self.chat_id) - - def __enter__(self): - return self.data - - def __exit__(self, exc_type, exc_val, exc_tb): - old_data = self.obj.read_data() - for i in self.data: - old_data[self.chat_id]['data'][i] = self.data.get(i) - self.obj.save_data(old_data) - return + \ No newline at end of file diff --git a/telebot/storage/__init__.py b/telebot/storage/__init__.py new file mode 100644 index 000000000..59e2b058c --- /dev/null +++ b/telebot/storage/__init__.py @@ -0,0 +1,13 @@ +from telebot.storage.memory_storage import StateMemoryStorage +from telebot.storage.redis_storage import StateRedisStorage +from telebot.storage.pickle_storage import StatePickleStorage +from telebot.storage.base_storage import StateContext,StateStorageBase + + + + + +__all__ = [ + 'StateStorageBase', 'StateContext', + 'StateMemoryStorage', 'StateRedisStorage', 'StatePickleStorage' +] \ No newline at end of file diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py new file mode 100644 index 000000000..2ff2b8c51 --- /dev/null +++ b/telebot/storage/base_storage.py @@ -0,0 +1,65 @@ +import copy + +class StateStorageBase: + def __init__(self) -> None: + pass + + def set_data(self, chat_id, user_id, key, value): + """ + Set data for a user in a particular chat. + """ + raise NotImplementedError + + def get_data(self, chat_id, user_id): + """ + Get data for a user in a particular chat. + """ + raise NotImplementedError + + def set_state(self, chat_id, user_id, state): + """ + Set state for a particular user. + + ! Note that you should create a + record if it does not exist, and + if a record with state already exists, + you need to update a record. + """ + raise NotImplementedError + + def delete_state(self, chat_id, user_id): + """ + Delete state for a particular user. + """ + raise NotImplementedError + + def reset_data(self, chat_id, user_id): + """ + Reset data for a particular user in a chat. + """ + raise NotImplementedError + + def get_state(self, chat_id, user_id): + raise NotImplementedError + + def save(chat_id, user_id, data): + raise NotImplementedError + + + +class StateContext: + """ + Class for data. + """ + def __init__(self , obj, chat_id, user_id) -> None: + self.obj = obj + self.data = copy.deepcopy(obj.get_data(chat_id, user_id)) + self.chat_id = chat_id + self.user_id = user_id + + + def __enter__(self): + return self.data + + def __exit__(self, exc_type, exc_val, exc_tb): + return self.obj.save(self.chat_id, self.user_id, self.data) \ No newline at end of file diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py new file mode 100644 index 000000000..3540ac5d5 --- /dev/null +++ b/telebot/storage/memory_storage.py @@ -0,0 +1,64 @@ +from telebot.storage.base_storage import StateStorageBase, StateContext + +class StateMemoryStorage(StateStorageBase): + def __init__(self) -> None: + self.data = {} + # + # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} + + + def set_state(self, chat_id, user_id, state): + if chat_id in self.data: + if user_id in self.data[chat_id]: + self.data[chat_id][user_id]['state'] = state + return True + else: + self.data[chat_id][user_id] = {'state': state, 'data': {}} + return True + self.data[chat_id] = {user_id: {'state': state, 'data': {}}} + return True + + def delete_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + del self.data[chat_id][user_id] + if chat_id == user_id: + del self.data[chat_id] + + return True + + return False + + + def get_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['state'] + + return None + def get_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['data'] + + return None + + def reset_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'] = {} + return True + return False + + def set_data(self, chat_id, user_id, key, value): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'][key] = value + return True + raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + + def get_interactive_data(self, chat_id, user_id): + return StateContext(self, chat_id, user_id) + + def save(self, chat_id, user_id, data): + self.data[chat_id][user_id]['data'] = data \ No newline at end of file diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py new file mode 100644 index 000000000..988d454f7 --- /dev/null +++ b/telebot/storage/pickle_storage.py @@ -0,0 +1,112 @@ +from telebot.storage.base_storage import StateStorageBase, StateContext +import os + + +import pickle + + +class StatePickleStorage(StateStorageBase): + def __init__(self, file_path="./.state-save/states.pkl") -> None: + self.file_path = file_path + self.create_dir() + self.data = self.read() + + def convert_old_to_new(self): + """ + Use this function to convert old storage to new storage. + This function is for people who was using pickle storage + that was in version <=4.3.1. + """ + # old looks like: + # {1: {'state': 'start', 'data': {'name': 'John'}} + # we should update old version pickle to new. + # new looks like: + # {1: {2: {'state': 'start', 'data': {'name': 'John'}}}} + new_data = {} + for key, value in self.data.items(): + # this returns us id and dict with data and state + new_data[key] = {key: value} # convert this to new + # pass it to global data + self.data = new_data + self.update_data() # update data in file + + def create_dir(self): + """ + Create directory .save-handlers. + """ + dirs = self.file_path.rsplit('/', maxsplit=1)[0] + os.makedirs(dirs, exist_ok=True) + if not os.path.isfile(self.file_path): + with open(self.file_path,'wb') as file: + pickle.dump({}, file) + + def read(self): + file = open(self.file_path, 'rb') + data = pickle.load(file) + file.close() + return data + + def update_data(self): + file = open(self.file_path, 'wb+') + pickle.dump(self.data, file, protocol=pickle.HIGHEST_PROTOCOL) + file.close() + + def set_state(self, chat_id, user_id, state): + if chat_id in self.data: + if user_id in self.data[chat_id]: + self.data[chat_id][user_id]['state'] = state + return True + else: + self.data[chat_id][user_id] = {'state': state, 'data': {}} + return True + self.data[chat_id] = {user_id: {'state': state, 'data': {}}} + self.update_data() + return True + + def delete_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + del self.data[chat_id][user_id] + if chat_id == user_id: + del self.data[chat_id] + self.update_data() + return True + + return False + + + def get_state(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['state'] + + return None + def get_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + return self.data[chat_id][user_id]['data'] + + return None + + def reset_data(self, chat_id, user_id): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'] = {} + self.update_data() + return True + return False + + def set_data(self, chat_id, user_id, key, value): + if self.data.get(chat_id): + if self.data[chat_id].get(user_id): + self.data[chat_id][user_id]['data'][key] = value + self.update_data() + return True + raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + + def get_interactive_data(self, chat_id, user_id): + return StateContext(self, chat_id, user_id) + + def save(self, chat_id, user_id, data): + self.data[chat_id][user_id]['data'] = data + self.update_data() \ No newline at end of file diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py new file mode 100644 index 000000000..a8ba2c502 --- /dev/null +++ b/telebot/storage/redis_storage.py @@ -0,0 +1,176 @@ +from telebot.storage.base_storage import StateStorageBase, StateContext +import json + +redis_installed = True +try: + from redis import Redis, ConnectionPool + +except: + redis_installed = False + +class StateRedisStorage(StateStorageBase): + """ + This class is for Redis storage. + This will work only for states. + To use it, just pass this class to: + TeleBot(storage=StateRedisStorage()) + """ + def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_'): + self.redis = ConnectionPool(host=host, port=port, db=db, password=password) + #self.con = Redis(connection_pool=self.redis) -> use this when necessary + # + # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} + self.prefix = prefix + if not redis_installed: + raise Exception("Redis is not installed. Install it via 'pip install redis'") + + def get_record(self, key): + """ + Function to get record from database. + It has nothing to do with states. + Made for backend compatibility + """ + connection = Redis(connection_pool=self.redis) + result = connection.get(self.prefix+str(key)) + connection.close() + if result: return json.loads(result) + return + + def set_record(self, key, value): + """ + Function to set record to database. + It has nothing to do with states. + Made for backend compatibility + """ + connection = Redis(connection_pool=self.redis) + connection.set(self.prefix+str(key), json.dumps(value)) + connection.close() + return True + + def delete_record(self, key): + """ + Function to delete record from database. + It has nothing to do with states. + Made for backend compatibility + """ + connection = Redis(connection_pool=self.redis) + connection.delete(self.prefix+str(key)) + connection.close() + return True + + def set_state(self, chat_id, user_id, state): + """ + Set state for a particular user in a chat. + """ + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['state'] = state + else: + response[user_id] = {'state': state, 'data': {}} + else: + response = {user_id: {'state': state, 'data': {}}} + self.set_record(chat_id, response) + + return True + + def delete_state(self, chat_id, user_id): + """ + Delete state for a particular user in a chat. + """ + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + del response[user_id] + if user_id == str(chat_id): + self.delete_record(chat_id) + return True + else: self.set_record(chat_id, response) + return True + return False + + + def get_value(self, chat_id, user_id, key): + """ + Get value for a data of a user in a chat. + """ + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + if key in response[user_id]['data']: + return response[user_id]['data'][key] + return None + + + def get_state(self, chat_id, user_id): + """ + Get state of a user in a chat. + """ + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + return response[user_id]['state'] + + return None + + + def get_data(self, chat_id, user_id): + """ + Get data of particular user in a particular chat. + """ + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + return response[user_id]['data'] + return None + + + def reset_data(self, chat_id, user_id): + """ + Reset data of a user in a chat. + """ + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['data'] = {} + self.set_record(chat_id, response) + return True + + + + + def set_data(self, chat_id, user_id, key, value): + """ + Set data without interactive data. + """ + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['data'][key] = value + self.set_record(chat_id, response) + return True + return False + + def get_interactive_data(self, chat_id, user_id): + """ + Get Data in interactive way. + You can use with() with this function. + """ + return StateContext(self, chat_id, user_id) + + def save(self, chat_id, user_id, data): + response = self.get_record(chat_id) + user_id = str(user_id) + if response: + if user_id in response: + response[user_id]['data'] = dict(data, **response[user_id]['data']) + self.set_record(chat_id, response) + return True + \ No newline at end of file From 321d241483d9e0821faa91528bc69bb5ab7154ad Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 17:23:40 +0400 Subject: [PATCH 0822/1808] Delete types.py --- telebot/types.py | 2886 ---------------------------------------------- 1 file changed, 2886 deletions(-) delete mode 100644 telebot/types.py diff --git a/telebot/types.py b/telebot/types.py deleted file mode 100644 index a200b7313..000000000 --- a/telebot/types.py +++ /dev/null @@ -1,2886 +0,0 @@ -# -*- coding: utf-8 -*- - -import logging -from typing import Dict, List, Optional, Union -from abc import ABC - -try: - import ujson as json -except ImportError: - import json - -from telebot import util - -DISABLE_KEYLEN_ERROR = False - -logger = logging.getLogger('TeleBot') - - -class JsonSerializable(object): - """ - Subclasses of this class are guaranteed to be able to be converted to JSON format. - All subclasses of this class must override to_json. - """ - - def to_json(self): - """ - Returns a JSON string representation of this class. - - This function must be overridden by subclasses. - :return: a JSON formatted string. - """ - raise NotImplementedError - - -class Dictionaryable(object): - """ - Subclasses of this class are guaranteed to be able to be converted to dictionary. - All subclasses of this class must override to_dict. - """ - - def to_dict(self): - """ - Returns a DICT with class field values - - This function must be overridden by subclasses. - :return: a DICT - """ - raise NotImplementedError - - -class JsonDeserializable(object): - """ - Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string. - All subclasses of this class must override de_json. - """ - - @classmethod - def de_json(cls, json_string): - """ - Returns an instance of this class from the given json dict or string. - - This function must be overridden by subclasses. - :return: an instance of this class created from the given json dict or string. - """ - raise NotImplementedError - - @staticmethod - def check_json(json_type, dict_copy = True): - """ - Checks whether json_type is a dict or a string. If it is already a dict, it is returned as-is. - If it is not, it is converted to a dict by means of json.loads(json_type) - :param json_type: input json or parsed dict - :param dict_copy: if dict is passed and it is changed outside - should be True! - :return: Dictionary parsed from json or original dict - """ - if util.is_dict(json_type): - return json_type.copy() if dict_copy else json_type - elif util.is_string(json_type): - return json.loads(json_type) - else: - raise ValueError("json_type should be a json dict or string.") - - def __str__(self): - d = { - x: y.__dict__ if hasattr(y, '__dict__') else y - for x, y in self.__dict__.items() - } - return str(d) - - -class Update(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string, dict_copy=False) - update_id = obj['update_id'] - message = Message.de_json(obj.get('message')) - edited_message = Message.de_json(obj.get('edited_message')) - channel_post = Message.de_json(obj.get('channel_post')) - edited_channel_post = Message.de_json(obj.get('edited_channel_post')) - inline_query = InlineQuery.de_json(obj.get('inline_query')) - chosen_inline_result = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) - callback_query = CallbackQuery.de_json(obj.get('callback_query')) - shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) - pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) - poll = Poll.de_json(obj.get('poll')) - poll_answer = PollAnswer.de_json(obj.get('poll_answer')) - my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member')) - chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) - chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) - 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) - - 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): - self.update_id = update_id - self.message = message - self.edited_message = edited_message - self.channel_post = channel_post - self.edited_channel_post = edited_channel_post - self.inline_query = inline_query - self.chosen_inline_result = chosen_inline_result - self.callback_query = callback_query - self.shipping_query = shipping_query - self.pre_checkout_query = pre_checkout_query - self.poll = poll - self.poll_answer = poll_answer - self.my_chat_member = my_chat_member - self.chat_member = chat_member - self.chat_join_request = chat_join_request - - -class ChatMemberUpdated(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['chat'] = Chat.de_json(obj['chat']) - obj['from_user'] = User.de_json(obj.pop('from')) - obj['old_chat_member'] = ChatMember.de_json(obj['old_chat_member']) - obj['new_chat_member'] = ChatMember.de_json(obj['new_chat_member']) - 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, **kwargs): - self.chat: Chat = chat - self.from_user: User = from_user - self.date: int = date - self.old_chat_member: ChatMember = old_chat_member - self.new_chat_member: ChatMember = new_chat_member - self.invite_link: Optional[ChatInviteLink] = invite_link - - @property - def difference(self) -> Dict[str, List]: - """ - Get the difference between `old_chat_member` and `new_chat_member` - as a dict in the following format {'parameter': [old_value, new_value]} - E.g {'status': ['member', 'kicked'], 'until_date': [None, 1625055092]} - """ - old: Dict = self.old_chat_member.__dict__ - new: Dict = self.new_chat_member.__dict__ - dif = {} - for key in new: - if key == 'user': continue - if new[key] != old[key]: - dif[key] = [old[key], new[key]] - return dif - -class ChatJoinRequest(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['chat'] = Chat.de_json(obj['chat']) - obj['from_user'] = User.de_json(obj['from']) - obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link')) - return cls(**obj) - - def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs): - self.chat = chat - self.from_user = from_user - self.date = date - self.bio = bio - self.invite_link = invite_link - -class WebhookInfo(JsonDeserializable): - @classmethod - 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) - - def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, - last_error_date=None, last_error_message=None, max_connections=None, - allowed_updates=None, **kwargs): - self.url = url - self.has_custom_certificate = has_custom_certificate - self.pending_update_count = pending_update_count - self.ip_address = ip_address - self.last_error_date = last_error_date - self.last_error_message = last_error_message - self.max_connections = max_connections - self.allowed_updates = allowed_updates - - -class User(JsonDeserializable, Dictionaryable, JsonSerializable): - @classmethod - 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) - - 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, **kwargs): - self.id: int = id - self.is_bot: bool = is_bot - self.first_name: str = first_name - self.username: str = username - self.last_name: str = last_name - self.language_code: str = language_code - self.can_join_groups: bool = can_join_groups - self.can_read_all_group_messages: bool = can_read_all_group_messages - self.supports_inline_queries: bool = supports_inline_queries - - @property - def full_name(self): - full_name = self.first_name - if self.last_name: - full_name += ' {0}'.format(self.last_name) - return full_name - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'id': self.id, - 'is_bot': self.is_bot, - 'first_name': self.first_name, - 'last_name': self.last_name, - 'username': self.username, - 'language_code': self.language_code, - 'can_join_groups': self.can_join_groups, - 'can_read_all_group_messages': self.can_read_all_group_messages, - 'supports_inline_queries': self.supports_inline_queries} - - -class GroupChat(JsonDeserializable): - @classmethod - 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) - - def __init__(self, id, title, **kwargs): - self.id: int = id - self.title: str = title - - -class Chat(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'photo' in obj: - obj['photo'] = ChatPhoto.de_json(obj['photo']) - if 'pinned_message' in obj: - obj['pinned_message'] = Message.de_json(obj['pinned_message']) - if 'permissions' in obj: - obj['permissions'] = ChatPermissions.de_json(obj['permissions']) - if 'location' in obj: - obj['location'] = ChatLocation.de_json(obj['location']) - return cls(**obj) - - def __init__(self, id, type, title=None, username=None, first_name=None, - last_name=None, photo=None, bio=None, has_private_forwards=None, - description=None, invite_link=None, pinned_message=None, - permissions=None, slow_mode_delay=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, **kwargs): - self.id: int = id - self.type: str = type - self.title: str = title - self.username: str = username - self.first_name: str = first_name - self.last_name: str = last_name - self.photo: ChatPhoto = photo - self.bio: str = bio - self.has_private_forwards: bool = has_private_forwards - self.description: str = description - self.invite_link: str = invite_link - self.pinned_message: Message = pinned_message - self.permissions: ChatPermissions = permissions - self.slow_mode_delay: int = slow_mode_delay - self.message_auto_delete_time: int = message_auto_delete_time - self.has_protected_content: bool = has_protected_content - self.sticker_set_name: str = sticker_set_name - self.can_set_sticker_set: bool = can_set_sticker_set - self.linked_chat_id: int = linked_chat_id - self.location: ChatLocation = location - - -class MessageID(JsonDeserializable): - @classmethod - 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) - - def __init__(self, message_id, **kwargs): - self.message_id = message_id - - -class Message(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string, dict_copy=False) - message_id = obj['message_id'] - from_user = User.de_json(obj.get('from')) - date = obj['date'] - 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: - opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) - if 'forward_from_message_id' in obj: - opts['forward_from_message_id'] = obj.get('forward_from_message_id') - if 'forward_signature' in obj: - opts['forward_signature'] = obj.get('forward_signature') - if 'forward_sender_name' in obj: - opts['forward_sender_name'] = obj.get('forward_sender_name') - if 'forward_date' in obj: - opts['forward_date'] = obj.get('forward_date') - if 'is_automatic_forward' in obj: - opts['is_automatic_forward'] = obj.get('is_automatic_forward') - if 'reply_to_message' in obj: - opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) - if 'via_bot' in obj: - opts['via_bot'] = User.de_json(obj['via_bot']) - if 'edit_date' in obj: - opts['edit_date'] = obj.get('edit_date') - if 'has_protected_content' in obj: - opts['has_protected_content'] = obj.get('has_protected_content') - if 'media_group_id' in obj: - opts['media_group_id'] = obj.get('media_group_id') - if 'author_signature' in obj: - opts['author_signature'] = obj.get('author_signature') - if 'text' in obj: - opts['text'] = obj['text'] - content_type = 'text' - if 'entities' in obj: - opts['entities'] = Message.parse_entities(obj['entities']) - if 'caption_entities' in obj: - opts['caption_entities'] = Message.parse_entities(obj['caption_entities']) - if 'audio' in obj: - opts['audio'] = Audio.de_json(obj['audio']) - content_type = 'audio' - if 'document' in obj: - opts['document'] = Document.de_json(obj['document']) - content_type = 'document' - if 'animation' in obj: - # Document content type accompanies "animation", - # so "animation" should be checked below "document" to override it - opts['animation'] = Animation.de_json(obj['animation']) - content_type = 'animation' - if 'game' in obj: - opts['game'] = Game.de_json(obj['game']) - content_type = 'game' - if 'photo' in obj: - opts['photo'] = Message.parse_photo(obj['photo']) - content_type = 'photo' - if 'sticker' in obj: - opts['sticker'] = Sticker.de_json(obj['sticker']) - content_type = 'sticker' - if 'video' in obj: - opts['video'] = Video.de_json(obj['video']) - content_type = 'video' - if 'video_note' in obj: - opts['video_note'] = VideoNote.de_json(obj['video_note']) - content_type = 'video_note' - if 'voice' in obj: - opts['voice'] = Audio.de_json(obj['voice']) - content_type = 'voice' - if 'caption' in obj: - opts['caption'] = obj['caption'] - if 'contact' in obj: - opts['contact'] = Contact.de_json(json.dumps(obj['contact'])) - content_type = 'contact' - if 'location' in obj: - opts['location'] = Location.de_json(obj['location']) - content_type = 'location' - if 'venue' in obj: - opts['venue'] = Venue.de_json(obj['venue']) - content_type = 'venue' - if 'dice' in obj: - opts['dice'] = Dice.de_json(obj['dice']) - content_type = 'dice' - if 'new_chat_members' in obj: - new_chat_members = [] - for member in obj['new_chat_members']: - new_chat_members.append(User.de_json(member)) - opts['new_chat_members'] = new_chat_members - content_type = 'new_chat_members' - if 'left_chat_member' in obj: - opts['left_chat_member'] = User.de_json(obj['left_chat_member']) - content_type = 'left_chat_member' - if 'new_chat_title' in obj: - opts['new_chat_title'] = obj['new_chat_title'] - content_type = 'new_chat_title' - if 'new_chat_photo' in obj: - opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo']) - content_type = 'new_chat_photo' - if 'delete_chat_photo' in obj: - opts['delete_chat_photo'] = obj['delete_chat_photo'] - content_type = 'delete_chat_photo' - if 'group_chat_created' in obj: - opts['group_chat_created'] = obj['group_chat_created'] - content_type = 'group_chat_created' - if 'supergroup_chat_created' in obj: - opts['supergroup_chat_created'] = obj['supergroup_chat_created'] - content_type = 'supergroup_chat_created' - if 'channel_chat_created' in obj: - opts['channel_chat_created'] = obj['channel_chat_created'] - content_type = 'channel_chat_created' - if 'migrate_to_chat_id' in obj: - opts['migrate_to_chat_id'] = obj['migrate_to_chat_id'] - content_type = 'migrate_to_chat_id' - if 'migrate_from_chat_id' in obj: - opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] - content_type = 'migrate_from_chat_id' - if 'pinned_message' in obj: - opts['pinned_message'] = Message.de_json(obj['pinned_message']) - content_type = 'pinned_message' - if 'invoice' in obj: - opts['invoice'] = Invoice.de_json(obj['invoice']) - content_type = 'invoice' - if 'successful_payment' in obj: - opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment']) - content_type = 'successful_payment' - if 'connected_website' in obj: - opts['connected_website'] = obj['connected_website'] - content_type = 'connected_website' - if 'poll' in obj: - opts['poll'] = Poll.de_json(obj['poll']) - content_type = 'poll' - if 'passport_data' in obj: - opts['passport_data'] = obj['passport_data'] - content_type = 'passport_data' - if 'proximity_alert_triggered' in obj: - opts['proximity_alert_triggered'] = ProximityAlertTriggered.de_json(obj[ - 'proximity_alert_triggered']) - content_type = 'proximity_alert_triggered' - if 'voice_chat_scheduled' in obj: - opts['voice_chat_scheduled'] = VoiceChatScheduled.de_json(obj['voice_chat_scheduled']) - content_type = 'voice_chat_scheduled' - if 'voice_chat_started' in obj: - opts['voice_chat_started'] = VoiceChatStarted.de_json(obj['voice_chat_started']) - content_type = 'voice_chat_started' - if 'voice_chat_ended' in obj: - opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) - content_type = 'voice_chat_ended' - if 'voice_chat_participants_invited' in obj: - opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['voice_chat_participants_invited']) - content_type = 'voice_chat_participants_invited' - if 'message_auto_delete_timer_changed' in obj: - opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(obj['message_auto_delete_timer_changed']) - content_type = 'message_auto_delete_timer_changed' - if 'reply_markup' in obj: - opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) - return cls(message_id, from_user, date, chat, content_type, opts, json_string) - - @classmethod - def parse_chat(cls, chat): - if 'first_name' not in chat: - return GroupChat.de_json(chat) - else: - return User.de_json(chat) - - @classmethod - def parse_photo(cls, photo_size_array): - ret = [] - for ps in photo_size_array: - ret.append(PhotoSize.de_json(ps)) - return ret - - @classmethod - def parse_entities(cls, message_entity_array): - ret = [] - for me in message_entity_array: - ret.append(MessageEntity.de_json(me)) - return ret - - def __init__(self, message_id, from_user, date, chat, content_type, options, json_string): - self.content_type: str = content_type - self.id: int = message_id # Lets fix the telegram usability ####up with ID in Message :) - self.message_id: int = message_id - 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 - self.forward_signature: Optional[str] = None - self.forward_sender_name: Optional[str] = None - self.forward_date: Optional[int] = None - self.is_automatic_forward: Optional[bool] = None - self.reply_to_message: Optional[Message] = None - self.via_bot: Optional[User] = None - self.edit_date: Optional[int] = None - self.has_protected_content: Optional[bool] = None - self.media_group_id: Optional[str] = None - self.author_signature: Optional[str] = None - self.text: Optional[str] = None - self.entities: Optional[List[MessageEntity]] = None - self.caption_entities: Optional[List[MessageEntity]] = None - self.audio: Optional[Audio] = None - self.document: Optional[Document] = None - self.photo: Optional[List[PhotoSize]] = None - self.sticker: Optional[Sticker] = None - self.video: Optional[Video] = None - self.video_note: Optional[VideoNote] = None - self.voice: Optional[Voice] = None - self.caption: Optional[str] = None - self.contact: Optional[Contact] = None - self.location: Optional[Location] = None - self.venue: Optional[Venue] = None - self.animation: Optional[Animation] = None - self.dice: Optional[Dice] = None - self.new_chat_member: Optional[User] = None # Deprecated since Bot API 3.0. Not processed anymore - self.new_chat_members: Optional[List[User]] = None - self.left_chat_member: Optional[User] = None - self.new_chat_title: Optional[str] = None - self.new_chat_photo: Optional[List[PhotoSize]] = None - self.delete_chat_photo: Optional[bool] = None - self.group_chat_created: Optional[bool] = None - self.supergroup_chat_created: Optional[bool] = None - self.channel_chat_created: Optional[bool] = None - self.migrate_to_chat_id: Optional[int] = None - self.migrate_from_chat_id: Optional[int] = None - self.pinned_message: Optional[Message] = None - self.invoice: Optional[Invoice] = None - self.successful_payment: Optional[SuccessfulPayment] = None - self.connected_website: Optional[str] = None - self.reply_markup: Optional[InlineKeyboardMarkup] = None - for key in options: - setattr(self, key, options[key]) - self.json = json_string - - def __html_text(self, text, entities): - """ - Author: @sviat9440 - Updaters: @badiboy - Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" - - Example: - message.html_text - >> "Test parse formatting, url, text_mention and mention @username" - - Custom subs: - You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. - Example: - message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} - message.html_text - >> "Test parse formatting, url and text_mention and mention @username" - """ - - if not entities: - return text - - _subs = { - "bold": "{text}", - "italic": "{text}", - "pre": "
{text}
", - "code": "{text}", - # "url": "{text}", # @badiboy plain URLs have no text and do not need tags - "text_link": "{text}", - "strikethrough": "{text}", - "underline": "{text}", - "spoiler": "{text}", - } - - if hasattr(self, "custom_subs"): - for key, value in self.custom_subs.items(): - _subs[key] = value - utf16_text = text.encode("utf-16-le") - html_text = "" - - def func(upd_text, subst_type=None, url=None, user=None): - upd_text = upd_text.decode("utf-16-le") - if subst_type == "text_mention": - subst_type = "text_link" - url = "tg://user?id={0}".format(user.id) - elif subst_type == "mention": - url = "https://t.me/{0}".format(upd_text[1:]) - upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") - if not subst_type or not _subs.get(subst_type): - return upd_text - subs = _subs.get(subst_type) - return subs.format(text=upd_text, url=url) - - offset = 0 - for entity in entities: - if entity.offset > offset: - html_text += func(utf16_text[offset * 2 : entity.offset * 2]) - offset = entity.offset - html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) - offset += entity.length - elif entity.offset == offset: - html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) - offset += entity.length - else: - # TODO: process nested entities from Bot API 4.5 - # Now ignoring them - pass - if offset * 2 < len(utf16_text): - html_text += func(utf16_text[offset * 2:]) - return html_text - - @property - def html_text(self): - return self.__html_text(self.text, self.entities) - - @property - def html_caption(self): - return self.__html_text(self.caption, self.caption_entities) - - -class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): - @staticmethod - def to_list_of_dicts(entity_list) -> Union[List[Dict], None]: - res = [] - for e in entity_list: - res.append(MessageEntity.to_dict(e)) - return res or None - - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'user' in obj: - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs): - self.type: str = type - self.offset: int = offset - self.length: int = length - self.url: str = url - self.user: User = user - self.language: str = language - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {"type": self.type, - "offset": self.offset, - "length": self.length, - "url": self.url, - "user": self.user, - "language": self.language} - - -class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): - @classmethod - 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) - - def __init__(self, value, emoji, **kwargs): - self.value: int = value - self.emoji: str = emoji - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'value': self.value, - 'emoji': self.emoji} - - -class PhotoSize(JsonDeserializable): - @classmethod - 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) - - def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.file_size: int = file_size - - -class Audio(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, - file_size=None, thumb=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.duration: int = duration - self.performer: str = performer - self.title: str = title - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - self.thumb: PhotoSize = thumb - - -class Voice(JsonDeserializable): - @classmethod - 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) - - def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.duration: int = duration - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class Document(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.thumb: PhotoSize = thumb - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class Video(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - return cls(**obj) - - def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.duration: int = duration - self.thumb: PhotoSize = thumb - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class VideoNote(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - return cls(**obj) - - def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.length: int = length - self.duration: int = duration - self.thumb: PhotoSize = thumb - self.file_size: int = file_size - - -class Contact(JsonDeserializable): - @classmethod - 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) - - def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None, **kwargs): - self.phone_number: str = phone_number - self.first_name: str = first_name - self.last_name: str = last_name - self.user_id: int = user_id - self.vcard: str = vcard - - -class Location(JsonDeserializable, JsonSerializable, Dictionaryable): - @classmethod - 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) - - def __init__(self, longitude, latitude, horizontal_accuracy=None, - live_period=None, heading=None, proximity_alert_radius=None, **kwargs): - self.longitude: float = longitude - self.latitude: float = latitude - self.horizontal_accuracy: float = horizontal_accuracy - self.live_period: int = live_period - self.heading: int = heading - self.proximity_alert_radius: int = proximity_alert_radius - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return { - "longitude": self.longitude, - "latitude": self.latitude, - "horizontal_accuracy": self.horizontal_accuracy, - "live_period": self.live_period, - "heading": self.heading, - "proximity_alert_radius": self.proximity_alert_radius, - } - - -class Venue(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, - google_place_id=None, google_place_type=None, **kwargs): - self.location: Location = location - self.title: str = title - self.address: str = address - self.foursquare_id: str = foursquare_id - self.foursquare_type: str = foursquare_type - self.google_place_id: str = google_place_id - self.google_place_type: str = google_place_type - - -class UserProfilePhotos(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'photos' in obj: - photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] - obj['photos'] = photos - return cls(**obj) - - def __init__(self, total_count, photos=None, **kwargs): - self.total_count: int = total_count - self.photos: List[PhotoSize] = photos - - -class File(JsonDeserializable): - @classmethod - 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) - - def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.file_size: int = file_size - self.file_path: str = file_path - - -class ForceReply(JsonSerializable): - def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Optional[str]=None): - self.selective: bool = selective - self.input_field_placeholder: str = input_field_placeholder - - def to_json(self): - json_dict = {'force_reply': True} - if self.selective is not None: - json_dict['selective'] = self.selective - if self.input_field_placeholder: - json_dict['input_field_placeholder'] = self.input_field_placeholder - return json.dumps(json_dict) - - -class ReplyKeyboardRemove(JsonSerializable): - def __init__(self, selective=None): - self.selective: bool = selective - - def to_json(self): - json_dict = {'remove_keyboard': True} - if self.selective: - json_dict['selective'] = self.selective - return json.dumps(json_dict) - - -class ReplyKeyboardMarkup(JsonSerializable): - max_row_keys = 12 - - def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, - selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None): - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - if not DISABLE_KEYLEN_ERROR: - logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - self.resize_keyboard: bool = resize_keyboard - self.one_time_keyboard: bool = one_time_keyboard - self.selective: bool = selective - self.row_width: int = row_width - self.input_field_placeholder: str = input_field_placeholder - self.keyboard: List[List[KeyboardButton]] = [] - - def add(self, *args, row_width=None): - """ - This function adds strings to the keyboard, while not exceeding row_width. - E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} - when row_width is set to 1. - When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} - See https://core.telegram.org/bots/api#replykeyboardmarkup - :param args: KeyboardButton to append to the keyboard - :param row_width: width of row - :return: self, to allow function chaining. - """ - if row_width is None: - row_width = self.row_width - - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - if not DISABLE_KEYLEN_ERROR: - logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - for row in util.chunks(args, row_width): - button_array = [] - for button in row: - if util.is_string(button): - button_array.append({'text': button}) - elif util.is_bytes(button): - button_array.append({'text': button.decode('utf-8')}) - else: - button_array.append(button.to_dict()) - self.keyboard.append(button_array) - - return self - - def row(self, *args): - """ - Adds a list of KeyboardButton to the keyboard. This function does not consider row_width. - ReplyKeyboardMarkup#row("A")#row("B", "C")#to_json() outputs '{keyboard: [["A"], ["B", "C"]]}' - See https://core.telegram.org/bots/api#replykeyboardmarkup - :param args: strings - :return: self, to allow function chaining. - """ - - return self.add(*args, row_width=self.max_row_keys) - - def to_json(self): - """ - Converts this object to its json representation following the Telegram API guidelines described here: - https://core.telegram.org/bots/api#replykeyboardmarkup - :return: - """ - json_dict = {'keyboard': self.keyboard} - if self.one_time_keyboard is not None: - json_dict['one_time_keyboard'] = self.one_time_keyboard - if self.resize_keyboard is not None: - json_dict['resize_keyboard'] = self.resize_keyboard - if self.selective is not None: - json_dict['selective'] = self.selective - if self.input_field_placeholder: - json_dict['input_field_placeholder'] = self.input_field_placeholder - 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[KeyboardButtonPollType]=None): - self.text: str = text - self.request_contact: bool = request_contact - self.request_location: bool = request_location - self.request_poll: KeyboardButtonPollType = request_poll - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'text': self.text} - if self.request_contact is not None: - json_dict['request_contact'] = self.request_contact - if self.request_location is not None: - json_dict['request_location'] = self.request_location - if self.request_poll is not None: - json_dict['request_poll'] = self.request_poll.to_dict() - return json_dict - - -class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable): - max_row_keys = 8 - - @classmethod - def de_json(cls, json_string): - 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 = keyboard) - - def __init__(self, keyboard=None, row_width=3): - """ - This object represents an inline keyboard that appears - right next to the message it belongs to. - - :return: - """ - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - self.row_width: int = row_width - self.keyboard: List[List[InlineKeyboardButton]] = keyboard or [] - - def add(self, *args, row_width=None): - """ - This method adds buttons to the keyboard without exceeding row_width. - - E.g. InlineKeyboardMarkup.add("A", "B", "C") yields the json result: - {keyboard: [["A"], ["B"], ["C"]]} - when row_width is set to 1. - When row_width is set to 2, the result: - {keyboard: [["A", "B"], ["C"]]} - See https://core.telegram.org/bots/api#inlinekeyboardmarkup - - :param args: Array of InlineKeyboardButton to append to the keyboard - :param row_width: width of row - :return: self, to allow function chaining. - """ - if row_width is None: - row_width = self.row_width - - if row_width > self.max_row_keys: - # Todo: Will be replaced with Exception in future releases - logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) - row_width = self.max_row_keys - - for row in util.chunks(args, row_width): - button_array = [button for button in row] - self.keyboard.append(button_array) - - return self - - def row(self, *args): - """ - Adds a list of InlineKeyboardButton to the keyboard. - This method does not consider row_width. - - InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: - '{keyboard: [["A"], ["B", "C"]]}' - See https://core.telegram.org/bots/api#inlinekeyboardmarkup - - :param args: Array of InlineKeyboardButton to append to the keyboard - :return: self, to allow function chaining. - """ - - return self.add(*args, row_width=self.max_row_keys) - - def to_json(self): - """ - Converts this object to its json representation - following the Telegram API guidelines described here: - https://core.telegram.org/bots/api#inlinekeyboardmarkup - :return: - """ - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = dict() - json_dict['inline_keyboard'] = [[button.to_dict() for button in row] for row in self.keyboard] - return json_dict - - -class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'login_url' in obj: - obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) - return cls(**obj) - - def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, - switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): - self.text: str = text - self.url: str = url - self.callback_data: str = callback_data - self.switch_inline_query: str = switch_inline_query - self.switch_inline_query_current_chat: str = switch_inline_query_current_chat - self.callback_game = callback_game # Not Implemented - self.pay: bool = pay - self.login_url: LoginUrl = login_url - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'text': self.text} - if self.url: - json_dict['url'] = self.url - if self.callback_data: - json_dict['callback_data'] = self.callback_data - if self.switch_inline_query is not None: - json_dict['switch_inline_query'] = self.switch_inline_query - if self.switch_inline_query_current_chat is not None: - json_dict['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat - if self.callback_game is not None: - json_dict['callback_game'] = self.callback_game - if self.pay is not None: - json_dict['pay'] = self.pay - if self.login_url is not None: - json_dict['login_url'] = self.login_url.to_dict() - return json_dict - - -class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): - @classmethod - 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) - - def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None, **kwargs): - self.url: str = url - self.forward_text: str = forward_text - self.bot_username: str = bot_username - self.request_write_access: bool = request_write_access - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'url': self.url} - if self.forward_text: - json_dict['forward_text'] = self.forward_text - if self.bot_username: - json_dict['bot_username'] = self.bot_username - if self.request_write_access is not None: - json_dict['request_write_access'] = self.request_write_access - return json_dict - - -class CallbackQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if not "data" in obj: - # "data" field is Optional in the API, but historically is mandatory in the class constructor - obj['data'] = None - obj['from_user'] = User.de_json(obj.pop('from')) - if 'message' in obj: - obj['message'] = Message.de_json(obj.get('message')) - return cls(**obj) - - def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None, **kwargs): - self.id: int = id - self.from_user: User = from_user - self.message: Message = message - self.inline_message_id: str = inline_message_id - self.chat_instance: str = chat_instance - self.data: str = data - self.game_short_name: str = game_short_name - - -class ChatPhoto(JsonDeserializable): - @classmethod - 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) - - def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id, **kwargs): - self.small_file_id: str = small_file_id - self.small_file_unique_id: str = small_file_unique_id - self.big_file_id: str = big_file_id - self.big_file_unique_id: str = big_file_unique_id - - -class ChatMember(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, - can_post_messages=None, can_edit_messages=None, can_delete_messages=None, - can_restrict_members=None, can_promote_members=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, is_member=None, - can_send_messages=None, can_send_media_messages=None, can_send_polls=None, - can_send_other_messages=None, can_add_web_page_previews=None, - can_manage_chat=None, can_manage_voice_chats=None, - until_date=None, **kwargs): - self.user: User = user - self.status: str = status - self.custom_title: str = custom_title - self.is_anonymous: bool = is_anonymous - self.can_be_edited: bool = can_be_edited - self.can_post_messages: bool = can_post_messages - self.can_edit_messages: bool = can_edit_messages - self.can_delete_messages: bool = can_delete_messages - self.can_restrict_members: bool = can_restrict_members - self.can_promote_members: bool = can_promote_members - self.can_change_info: bool = can_change_info - self.can_invite_users: bool = can_invite_users - self.can_pin_messages: bool = can_pin_messages - self.is_member: bool = is_member - self.can_send_messages: bool = can_send_messages - self.can_send_media_messages: bool = can_send_media_messages - self.can_send_polls: bool = can_send_polls - self.can_send_other_messages: bool = can_send_other_messages - self.can_add_web_page_previews: bool = can_add_web_page_previews - self.can_manage_chat: bool = can_manage_chat - self.can_manage_voice_chats: bool = can_manage_voice_chats - self.until_date: int = until_date - - -class ChatMemberOwner(ChatMember): - pass - -class ChatMemberAdministrator(ChatMember): - pass - - -class ChatMemberMember(ChatMember): - pass - - -class ChatMemberRestricted(ChatMember): - pass - - -class ChatMemberLeft(ChatMember): - pass - - -class ChatMemberBanned(ChatMember): - pass - - -class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return json_string - obj = cls.check_json(json_string, dict_copy=False) - return cls(**obj) - - def __init__(self, can_send_messages=None, can_send_media_messages=None, - can_send_polls=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, **kwargs): - self.can_send_messages: bool = can_send_messages - self.can_send_media_messages: bool = can_send_media_messages - self.can_send_polls: bool = can_send_polls - self.can_send_other_messages: bool = can_send_other_messages - self.can_add_web_page_previews: bool = can_add_web_page_previews - self.can_change_info: bool = can_change_info - self.can_invite_users: bool = can_invite_users - self.can_pin_messages: bool = can_pin_messages - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = dict() - if self.can_send_messages is not None: - json_dict['can_send_messages'] = self.can_send_messages - if self.can_send_media_messages is not None: - json_dict['can_send_media_messages'] = self.can_send_media_messages - if self.can_send_polls is not None: - json_dict['can_send_polls'] = self.can_send_polls - if self.can_send_other_messages is not None: - json_dict['can_send_other_messages'] = self.can_send_other_messages - if self.can_add_web_page_previews is not None: - json_dict['can_add_web_page_previews'] = self.can_add_web_page_previews - if self.can_change_info is not None: - json_dict['can_change_info'] = self.can_change_info - if self.can_invite_users is not None: - json_dict['can_invite_users'] = self.can_invite_users - if self.can_pin_messages is not None: - json_dict['can_pin_messages'] = self.can_pin_messages - return json_dict - - -class BotCommand(JsonSerializable, JsonDeserializable): - @classmethod - 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) - - def __init__(self, command, description): - """ - This object represents a bot command. - :param command: Text of the command, 1-32 characters. - Can contain only lowercase English letters, digits and underscores. - :param description: Description of the command, 3-256 characters. - :return: - """ - self.command: str = command - self.description: str = description - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'command': self.command, 'description': self.description} - - -# BotCommandScopes - -class BotCommandScope(ABC, JsonSerializable): - def __init__(self, type='default', chat_id=None, user_id=None): - """ - Abstract class. - Use BotCommandScopeX classes to set a specific scope type: - BotCommandScopeDefault - BotCommandScopeAllPrivateChats - BotCommandScopeAllGroupChats - BotCommandScopeAllChatAdministrators - BotCommandScopeChat - BotCommandScopeChatAdministrators - BotCommandScopeChatMember - """ - self.type: str = type - self.chat_id: Optional[Union[int, str]] = chat_id - self.user_id: Optional[Union[int, str]] = user_id - - def to_json(self): - json_dict = {'type': self.type} - if self.chat_id: - json_dict['chat_id'] = self.chat_id - if self.user_id: - json_dict['user_id'] = self.user_id - return json.dumps(json_dict) - - -class BotCommandScopeDefault(BotCommandScope): - def __init__(self): - """ - Represents the default scope of bot commands. - Default commands are used if no commands with a narrower scope are specified for the user. - """ - super(BotCommandScopeDefault, self).__init__(type='default') - - -class BotCommandScopeAllPrivateChats(BotCommandScope): - def __init__(self): - """ - Represents the scope of bot commands, covering all private chats. - """ - super(BotCommandScopeAllPrivateChats, self).__init__(type='all_private_chats') - - -class BotCommandScopeAllGroupChats(BotCommandScope): - def __init__(self): - """ - Represents the scope of bot commands, covering all group and supergroup chats. - """ - super(BotCommandScopeAllGroupChats, self).__init__(type='all_group_chats') - - -class BotCommandScopeAllChatAdministrators(BotCommandScope): - def __init__(self): - """ - Represents the scope of bot commands, covering all group and supergroup chat administrators. - """ - super(BotCommandScopeAllChatAdministrators, self).__init__(type='all_chat_administrators') - - -class BotCommandScopeChat(BotCommandScope): - def __init__(self, chat_id=None): - super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) - - -class BotCommandScopeChatAdministrators(BotCommandScope): - def __init__(self, chat_id=None): - """ - Represents the scope of bot commands, covering a specific chat. - @param chat_id: Unique identifier for the target chat - """ - super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) - - -class BotCommandScopeChatMember(BotCommandScope): - def __init__(self, chat_id=None, user_id=None): - """ - Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat - @param chat_id: Unique identifier for the target chat - @param user_id: Unique identifier of the target user - """ - super(BotCommandScopeChatMember, self).__init__(type='chat_member', chat_id=chat_id, user_id=user_id) - - -# InlineQuery - -class InlineQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - if 'location' in obj: - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, id, from_user, query, offset, chat_type=None, location=None, **kwargs): - """ - This object represents an incoming inline query. - When the user sends an empty query, your bot could - return some default or trending results. - :param id: string Unique identifier for this query - :param from_user: User Sender - :param query: String Text of the query - :param chat_type: String Type of the chat, from which the inline query was sent. - Can be either “sender” for a private chat with the inline query sender, - “private”, “group”, “supergroup”, or “channel”. - :param offset: String Offset of the results to be returned, can be controlled by the bot - :param location: Sender location, only for bots that request user location - :return: InlineQuery Object - """ - self.id: int = id - self.from_user: User = from_user - self.query: str = query - self.offset: str = offset - self.chat_type: str = chat_type - self.location: Location = location - - -class InputTextMessageContent(Dictionaryable): - def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None): - self.message_text: str = message_text - self.parse_mode: str = parse_mode - self.entities: List[MessageEntity] = entities - self.disable_web_page_preview: bool = disable_web_page_preview - - def to_dict(self): - json_dict = {'message_text': self.message_text} - if self.parse_mode: - json_dict['parse_mode'] = self.parse_mode - if self.entities: - json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) - if self.disable_web_page_preview is not None: - json_dict['disable_web_page_preview'] = self.disable_web_page_preview - return json_dict - - -class InputLocationMessageContent(Dictionaryable): - def __init__(self, latitude, longitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None): - self.latitude: float = latitude - self.longitude: float = longitude - self.horizontal_accuracy: float = horizontal_accuracy - self.live_period: int = live_period - self.heading: int = heading - self.proximity_alert_radius: int = proximity_alert_radius - - def to_dict(self): - json_dict = {'latitude': self.latitude, '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 - return json_dict - - -class InputVenueMessageContent(Dictionaryable): - def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, - google_place_id=None, google_place_type=None): - self.latitude: float = latitude - self.longitude: float = longitude - self.title: str = title - self.address: str = address - self.foursquare_id: str = foursquare_id - self.foursquare_type: str = foursquare_type - self.google_place_id: str = google_place_id - self.google_place_type: str = google_place_type - - def to_dict(self): - json_dict = { - 'latitude': self.latitude, - 'longitude': self.longitude, - 'title': self.title, - '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.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_dict - - -class InputContactMessageContent(Dictionaryable): - def __init__(self, phone_number, first_name, last_name=None, vcard=None): - self.phone_number: str = phone_number - self.first_name: str = first_name - self.last_name: str = last_name - self.vcard: str = vcard - - def to_dict(self): - json_dict = {'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 - return json_dict - - -class InputInvoiceMessageContent(Dictionaryable): - def __init__(self, title, description, payload, provider_token, currency, prices, - max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, - photo_url=None, photo_size=None, photo_width=None, photo_height=None, - need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, - send_phone_number_to_provider=None, send_email_to_provider=None, - is_flexible=None): - self.title: str = title - self.description: str = description - self.payload: str = payload - self.provider_token: str = provider_token - self.currency: str = currency - self.prices: List[LabeledPrice] = prices - self.max_tip_amount: Optional[int] = max_tip_amount - self.suggested_tip_amounts: Optional[List[int]] = suggested_tip_amounts - self.provider_data: Optional[str] = provider_data - self.photo_url: Optional[str] = photo_url - self.photo_size: Optional[int] = photo_size - self.photo_width: Optional[int] = photo_width - self.photo_height: Optional[int] = photo_height - self.need_name: Optional[bool] = need_name - self.need_phone_number: Optional[bool] = need_phone_number - self.need_email: Optional[bool] = need_email - self.need_shipping_address: Optional[bool] = need_shipping_address - self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider - self.send_email_to_provider: Optional[bool] = send_email_to_provider - self.is_flexible: Optional[bool] = is_flexible - - def to_dict(self): - json_dict = { - 'title': self.title, - 'description': self.description, - 'payload': self.payload, - 'provider_token': self.provider_token, - 'currency': self.currency, - 'prices': [LabeledPrice.to_dict(lp) for lp in self.prices] - } - if self.max_tip_amount: - json_dict['max_tip_amount'] = self.max_tip_amount - if self.suggested_tip_amounts: - json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts - if self.provider_data: - json_dict['provider_data'] = self.provider_data - if self.photo_url: - json_dict['photo_url'] = self.photo_url - if self.photo_size: - json_dict['photo_size'] = self.photo_size - if self.photo_width: - json_dict['photo_width'] = self.photo_width - if self.photo_height: - json_dict['photo_height'] = self.photo_height - if self.need_name is not None: - json_dict['need_name'] = self.need_name - if self.need_phone_number is not None: - json_dict['need_phone_number'] = self.need_phone_number - if self.need_email is not None: - json_dict['need_email'] = self.need_email - if self.need_shipping_address is not None: - json_dict['need_shipping_address'] = self.need_shipping_address - if self.send_phone_number_to_provider is not None: - json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider - if self.send_email_to_provider is not None: - json_dict['send_email_to_provider'] = self.send_email_to_provider - if self.is_flexible is not None: - json_dict['is_flexible'] = self.is_flexible - return json_dict - - -class ChosenInlineResult(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - if 'location' in obj: - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, result_id, from_user, query, location=None, inline_message_id=None, **kwargs): - """ - This object represents a result of an inline query - that was chosen by the user and sent to their chat partner. - :param result_id: string The unique identifier for the result that was chosen. - :param from_user: User The user that chose the result. - :param query: String The query that was used to obtain the result. - :return: ChosenInlineResult Object. - """ - self.result_id: str = result_id - self.from_user: User = from_user - self.location: Location = location - self.inline_message_id: str = inline_message_id - self.query: str = query - - -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. - :param title: Title of the result. - :param input_message_content: InputMessageContent : Content of the message to be sent - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param url: URL of the result. - :param hide_url: Pass True, if you don't want the URL to be shown in the message. - :param description: Short description of the result. - :param thumb_url: Url of the thumbnail for the result. - :param thumb_width: Thumbnail width. - :param thumb_height: Thumbnail height - :return: - """ - 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 - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - def to_dict(self): - json_dict = super().to_dict() - if self.url: - json_dict['url'] = self.url - if self.hide_url: - json_dict['hide_url'] = self.hide_url - if self.description: - json_dict['description'] = self.description - 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 InlineQueryResultPhoto(InlineQueryResultBase): - def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=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 - :param photo_url: A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB - :param thumb_url: URL of the thumbnail for the photo - :param photo_width: Width of the photo. - :param photo_height: Height of the photo. - :param title: Title for the result. - :param description: Short description of the result. - :param caption: Caption of the photo to be sent, 0-200 characters. - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or - inline URLs in the media caption. - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ - 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.description = description - - 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.description: - json_dict['description'] = self.description - return json_dict - - -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, - thumb_mime_type=None): - """ - Represents a link to an animated GIF file. - :param id: Unique identifier for this result, 1-64 bytes. - :param gif_url: A valid URL for the GIF file. File size must not exceed 1MB - :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result. - :param gif_width: Width of the GIF. - :param gif_height: Height of the GIF. - :param title: Title for the result. - :param caption: Caption of the GIF file to be sent, 0-200 characters - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ - 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.gif_duration = gif_duration - self.thumb_mime_type = thumb_mime_type - - 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.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 - if self.thumb_mime_type: - json_dict['thumb_mime_type'] = self.thumb_mime_type - return json_dict - - -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, - thumb_mime_type=None): - """ - Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). - :param id: Unique identifier for this result, 1-64 bytes - :param mpeg4_url: A valid URL for the MP4 file. File size must not exceed 1MB - :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result - :param mpeg4_width: Video width - :param mpeg4_height: Video height - :param title: Title for the result - :param caption: Caption of the MPEG-4 file to be sent, 0-200 characters - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text - or inline URLs in the media caption. - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ - 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.mpeg4_duration = mpeg4_duration - self.thumb_mime_type = thumb_mime_type - - 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 - json_dict['thumb_url'] = self.thumb_url - if self.mpeg4_duration: - json_dict['mpeg4_duration '] = self.mpeg4_duration - if self.thumb_mime_type: - json_dict['thumb_mime_type'] = self.thumb_mime_type - return json_dict - - -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. - :param id: Unique identifier for this result, 1-64 bytes - :param video_url: A valid URL for the embedded video player or video file - :param mime_type: Mime type of the content of video url, “text/html” or “video/mp4” - :param thumb_url: URL of the thumbnail (jpeg only) for the video - :param title: Title for the result - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or - inline URLs in the media caption. - :param video_width: Video width - :param video_height: Video height - :param video_duration: Video duration in seconds - :param description: Short description of the result - :return: - """ - 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.description = description - - 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 - return json_dict - - -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.performer = performer - self.audio_duration = audio_duration - - 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 - return json_dict - - -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.voice_duration = voice_duration - - 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 - return json_dict - - -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.description = description - 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['document_url'] = self.document_url - json_dict['mime_type'] = self.mime_type - if self.description: - json_dict['description'] = self.description - 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 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, 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.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_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 - return json_dict - - -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): - 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.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 - - 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.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 - 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 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 - self.title = None - self.description = None - self.caption = None - self.reply_markup = None - self.input_message_content = None - self.parse_mode = None - self.caption_entities = None - self.payload_dic = {} - - def to_json(self): - json_dict = self.payload_dic - json_dict['type'] = self.type - json_dict['id'] = self.id - 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.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.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(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): - 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(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(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): - 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(InlineQueryResultCachedBase): - def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content=None): - InlineQueryResultCachedBase.__init__(self) - self.type = 'sticker' - self.id = id - self.sticker_file_id = sticker_file_id - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.payload_dic['sticker_file_id'] = sticker_file_id - - -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(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): - 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(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(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 - self.payload_dic['audio_file_id'] = audio_file_id - - -# Games - -class Game(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['photo'] = Game.parse_photo(obj['photo']) - if 'text_entities' in obj: - obj['text_entities'] = Game.parse_entities(obj['text_entities']) - if 'animation' in obj: - obj['animation'] = Animation.de_json(obj['animation']) - return cls(**obj) - - @classmethod - def parse_photo(cls, photo_size_array): - ret = [] - for ps in photo_size_array: - ret.append(PhotoSize.de_json(ps)) - return ret - - @classmethod - def parse_entities(cls, message_entity_array): - ret = [] - for me in message_entity_array: - ret.append(MessageEntity.de_json(me)) - return ret - - def __init__(self, title, description, photo, text=None, text_entities=None, animation=None, **kwargs): - self.title: str = title - self.description: str = description - self.photo: List[PhotoSize] = photo - self.text: str = text - self.text_entities: List[MessageEntity] = text_entities - self.animation: Animation = animation - - -class Animation(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj["thumb"] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, - thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.duration: int = duration - self.thumb: PhotoSize = thumb - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - - -class GameHighScore(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, position, user, score, **kwargs): - self.position: int = position - self.user: User = user - self.score: int = score - - -# Payments - -class LabeledPrice(JsonSerializable): - def __init__(self, label, amount): - self.label: str = label - self.amount: int = amount - - def to_dict(self): - return { - 'label': self.label, 'amount': self.amount - } - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Invoice(JsonDeserializable): - @classmethod - 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) - - def __init__(self, title, description, start_parameter, currency, total_amount, **kwargs): - self.title: str = title - self.description: str = description - self.start_parameter: str = start_parameter - self.currency: str = currency - self.total_amount: int = total_amount - - -class ShippingAddress(JsonDeserializable): - @classmethod - 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) - - def __init__(self, country_code, state, city, street_line1, street_line2, post_code, **kwargs): - self.country_code: str = country_code - self.state: str = state - self.city: str = city - self.street_line1: str = street_line1 - self.street_line2: str = street_line2 - self.post_code: str = post_code - - -class OrderInfo(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['shipping_address'] = ShippingAddress.de_json(obj.get('shipping_address')) - return cls(**obj) - - def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs): - self.name: str = name - self.phone_number: str = phone_number - self.email: str = email - self.shipping_address: ShippingAddress = shipping_address - - -class ShippingOption(JsonSerializable): - def __init__(self, id, title): - self.id: str = id - self.title: str = title - self.prices: List[LabeledPrice] = [] - - def add_price(self, *args): - """ - Add LabeledPrice to ShippingOption - :param args: LabeledPrices - """ - for price in args: - self.prices.append(price) - return self - - def to_json(self): - price_list = [] - for p in self.prices: - price_list.append(p.to_dict()) - json_dict = json.dumps({'id': self.id, 'title': self.title, 'prices': price_list}) - return json_dict - - -class SuccessfulPayment(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) - return cls(**obj) - - def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, - telegram_payment_charge_id=None, provider_payment_charge_id=None, **kwargs): - self.currency: str = currency - self.total_amount: int = total_amount - self.invoice_payload: str = invoice_payload - self.shipping_option_id: str = shipping_option_id - self.order_info: OrderInfo = order_info - self.telegram_payment_charge_id: str = telegram_payment_charge_id - self.provider_payment_charge_id: str = provider_payment_charge_id - - -class ShippingQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) - return cls(**obj) - - def __init__(self, id, from_user, invoice_payload, shipping_address, **kwargs): - self.id: str = id - self.from_user: User = from_user - self.invoice_payload: str = invoice_payload - self.shipping_address: ShippingAddress = shipping_address - - -class PreCheckoutQuery(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['from_user'] = User.de_json(obj.pop('from')) - obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) - return cls(**obj) - - def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs): - self.id: str = id - self.from_user: User = from_user - self.currency: str = currency - self.total_amount: int = total_amount - self.invoice_payload: str = invoice_payload - self.shipping_option_id: str = shipping_option_id - self.order_info: OrderInfo = order_info - - -# Stickers - -class StickerSet(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - stickers = [] - for s in obj['stickers']: - stickers.append(Sticker.de_json(s)) - obj['stickers'] = stickers - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - return cls(**obj) - - def __init__(self, name, title, is_animated, contains_masks, stickers, thumb=None, **kwargs): - self.name: str = name - self.title: str = title - self.is_animated: bool = is_animated - self.contains_masks: bool = contains_masks - self.stickers: List[Sticker] = stickers - self.thumb: PhotoSize = thumb - - -class Sticker(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - else: - obj['thumb'] = None - if 'mask_position' in obj: - obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) - return cls(**obj) - - def __init__(self, file_id, file_unique_id, width, height, is_animated, - thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): - self.file_id: str = file_id - self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.is_animated: bool = is_animated - self.thumb: PhotoSize = thumb - self.emoji: str = emoji - self.set_name: str = set_name - self.mask_position: MaskPosition = mask_position - self.file_size: int = file_size - - - -class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): - @classmethod - 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) - - def __init__(self, point, x_shift, y_shift, scale, **kwargs): - self.point: str = point - self.x_shift: float = x_shift - self.y_shift: float = y_shift - self.scale: float = scale - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale} - - -# InputMedia - -class InputMedia(Dictionaryable, JsonSerializable): - def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None): - self.type: str = type - self.media: str = media - self.caption: Optional[str] = caption - self.parse_mode: Optional[str] = parse_mode - self.caption_entities: Optional[List[MessageEntity]] = caption_entities - - if util.is_string(self.media): - self._media_name = '' - self._media_dic = self.media - else: - self._media_name = util.generate_random_token() - self._media_dic = 'attach://{0}'.format(self._media_name) - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = {'type': self.type, 'media': self._media_dic} - if self.caption: - json_dict['caption'] = self.caption - 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_dict - - def convert_input_media(self): - if util.is_string(self.media): - return self.to_json(), None - - return self.to_json(), {self._media_name: self.media} - - -class InputMediaPhoto(InputMedia): - def __init__(self, media, caption=None, parse_mode=None): - if util.is_pil_image(media): - media = util.pil_image_to_file(media) - - super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) - - def to_dict(self): - return super(InputMediaPhoto, self).to_dict() - - -class InputMediaVideo(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, - supports_streaming=None): - super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.width = width - self.height = height - self.duration = duration - self.supports_streaming = supports_streaming - - def to_dict(self): - ret = super(InputMediaVideo, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.width: - ret['width'] = self.width - if self.height: - ret['height'] = self.height - if self.duration: - ret['duration'] = self.duration - if self.supports_streaming: - ret['supports_streaming'] = self.supports_streaming - return ret - - -class InputMediaAnimation(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): - super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.width = width - self.height = height - self.duration = duration - - def to_dict(self): - ret = super(InputMediaAnimation, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.width: - ret['width'] = self.width - if self.height: - ret['height'] = self.height - if self.duration: - ret['duration'] = self.duration - return ret - - -class InputMediaAudio(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): - super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.duration = duration - self.performer = performer - self.title = title - - def to_dict(self): - ret = super(InputMediaAudio, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.duration: - ret['duration'] = self.duration - if self.performer: - ret['performer'] = self.performer - if self.title: - ret['title'] = self.title - return ret - - -class InputMediaDocument(InputMedia): - def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None): - super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) - self.thumb = thumb - self.disable_content_type_detection = disable_content_type_detection - - def to_dict(self): - ret = super(InputMediaDocument, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb - if self.disable_content_type_detection is not None: - ret['disable_content_type_detection'] = self.disable_content_type_detection - return ret - - -class PollOption(JsonDeserializable): - @classmethod - 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) - - def __init__(self, text, voter_count = 0, **kwargs): - self.text: str = text - self.voter_count: int = voter_count - # Converted in _convert_poll_options - # def to_json(self): - # # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll - # return json.dumps(self.text) - - -class Poll(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['poll_id'] = obj.pop('id') - options = [] - for opt in obj['options']: - options.append(PollOption.de_json(opt)) - obj['options'] = options or None - if 'explanation_entities' in obj: - obj['explanation_entities'] = Message.parse_entities(obj['explanation_entities']) - return cls(**obj) - - def __init__( - self, - question, options, - poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, - allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, - open_period=None, close_date=None, **kwargs): - self.id: str = poll_id - self.question: str = question - self.options: List[PollOption] = options - self.total_voter_count: int = total_voter_count - self.is_closed: bool = is_closed - self.is_anonymous: bool = is_anonymous - self.type: str = poll_type - self.allows_multiple_answers: bool = allows_multiple_answers - self.correct_option_id: int = correct_option_id - self.explanation: str = explanation - self.explanation_entities: List[MessageEntity] = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] - self.open_period: int = open_period - self.close_date: int = close_date - - def add(self, option): - if type(option) is PollOption: - self.options.append(option) - else: - self.options.append(PollOption(option)) - - -class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if (json_string is None): return None - obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) - return cls(**obj) - - def __init__(self, poll_id, user, option_ids, **kwargs): - self.poll_id: str = poll_id - self.user: User = user - self.option_ids: List[int] = option_ids - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return {'poll_id': self.poll_id, - 'user': self.user.to_dict(), - 'option_ids': self.option_ids} - - -class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return json_string - obj = cls.check_json(json_string) - obj['location'] = Location.de_json(obj['location']) - return cls(**obj) - - def __init__(self, location, address, **kwargs): - self.location: Location = location - self.address: str = address - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - return { - "location": self.location.to_dict(), - "address": self.address - } - - -class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - obj['creator'] = User.de_json(obj['creator']) - return cls(**obj) - - def __init__(self, invite_link, creator, creates_join_request , is_primary, is_revoked, - name=None, expire_date=None, member_limit=None, pending_join_request_count=None, **kwargs): - self.invite_link: str = invite_link - self.creator: User = creator - self.creates_join_request: bool = creates_join_request - self.is_primary: bool = is_primary - self.is_revoked: bool = is_revoked - self.name: str = name - self.expire_date: int = expire_date - self.member_limit: int = member_limit - self.pending_join_request_count: int = pending_join_request_count - - def to_json(self): - return json.dumps(self.to_dict()) - - def to_dict(self): - json_dict = { - "invite_link": self.invite_link, - "creator": self.creator.to_dict(), - "is_primary": self.is_primary, - "is_revoked": self.is_revoked, - "creates_join_request": self.creates_join_request - } - if self.expire_date: - json_dict["expire_date"] = self.expire_date - if self.member_limit: - json_dict["member_limit"] = self.member_limit - if self.pending_join_request_count: - json_dict["pending_join_request_count"] = self.pending_join_request_count - if self.name: - json_dict["name"] = self.name - return json_dict - - -class ProximityAlertTriggered(JsonDeserializable): - @classmethod - 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) - - def __init__(self, traveler, watcher, distance, **kwargs): - self.traveler: User = traveler - self.watcher: User = watcher - self.distance: int = distance - - -class VoiceChatStarted(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - return cls() - - def __init__(self): - """ - This object represents a service message about a voice chat started in the chat. - Currently holds no information. - """ - pass - - -class VoiceChatScheduled(JsonDeserializable): - @classmethod - 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) - - def __init__(self, start_date, **kwargs): - self.start_date: int = start_date - - -class VoiceChatEnded(JsonDeserializable): - @classmethod - 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) - - def __init__(self, duration, **kwargs): - self.duration: int = duration - - -class VoiceChatParticipantsInvited(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'users' in obj: - obj['users'] = [User.de_json(u) for u in obj['users']] - return cls(**obj) - - def __init__(self, users=None, **kwargs): - self.users: List[User] = users - - -class MessageAutoDeleteTimerChanged(JsonDeserializable): - @classmethod - 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) - - def __init__(self, message_auto_delete_time, **kwargs): - self.message_auto_delete_time = message_auto_delete_time From 195974ddc15bb0f1a71bbf794c12198895ed2add Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 18:33:59 +0400 Subject: [PATCH 0823/1808] Fix --- telebot/asyncio_types.py | 1 - telebot/types.py | 2886 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 2886 insertions(+), 1 deletion(-) delete mode 100644 telebot/asyncio_types.py create mode 100644 telebot/types.py diff --git a/telebot/asyncio_types.py b/telebot/asyncio_types.py deleted file mode 100644 index 9fe798cd7..000000000 --- a/telebot/asyncio_types.py +++ /dev/null @@ -1 +0,0 @@ -# planned \ No newline at end of file diff --git a/telebot/types.py b/telebot/types.py new file mode 100644 index 000000000..a200b7313 --- /dev/null +++ b/telebot/types.py @@ -0,0 +1,2886 @@ +# -*- coding: utf-8 -*- + +import logging +from typing import Dict, List, Optional, Union +from abc import ABC + +try: + import ujson as json +except ImportError: + import json + +from telebot import util + +DISABLE_KEYLEN_ERROR = False + +logger = logging.getLogger('TeleBot') + + +class JsonSerializable(object): + """ + Subclasses of this class are guaranteed to be able to be converted to JSON format. + All subclasses of this class must override to_json. + """ + + def to_json(self): + """ + Returns a JSON string representation of this class. + + This function must be overridden by subclasses. + :return: a JSON formatted string. + """ + raise NotImplementedError + + +class Dictionaryable(object): + """ + Subclasses of this class are guaranteed to be able to be converted to dictionary. + All subclasses of this class must override to_dict. + """ + + def to_dict(self): + """ + Returns a DICT with class field values + + This function must be overridden by subclasses. + :return: a DICT + """ + raise NotImplementedError + + +class JsonDeserializable(object): + """ + Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string. + All subclasses of this class must override de_json. + """ + + @classmethod + def de_json(cls, json_string): + """ + Returns an instance of this class from the given json dict or string. + + This function must be overridden by subclasses. + :return: an instance of this class created from the given json dict or string. + """ + raise NotImplementedError + + @staticmethod + def check_json(json_type, dict_copy = True): + """ + Checks whether json_type is a dict or a string. If it is already a dict, it is returned as-is. + If it is not, it is converted to a dict by means of json.loads(json_type) + :param json_type: input json or parsed dict + :param dict_copy: if dict is passed and it is changed outside - should be True! + :return: Dictionary parsed from json or original dict + """ + if util.is_dict(json_type): + return json_type.copy() if dict_copy else json_type + elif util.is_string(json_type): + return json.loads(json_type) + else: + raise ValueError("json_type should be a json dict or string.") + + def __str__(self): + d = { + x: y.__dict__ if hasattr(y, '__dict__') else y + for x, y in self.__dict__.items() + } + return str(d) + + +class Update(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string, dict_copy=False) + update_id = obj['update_id'] + message = Message.de_json(obj.get('message')) + edited_message = Message.de_json(obj.get('edited_message')) + channel_post = Message.de_json(obj.get('channel_post')) + edited_channel_post = Message.de_json(obj.get('edited_channel_post')) + inline_query = InlineQuery.de_json(obj.get('inline_query')) + chosen_inline_result = ChosenInlineResult.de_json(obj.get('chosen_inline_result')) + callback_query = CallbackQuery.de_json(obj.get('callback_query')) + shipping_query = ShippingQuery.de_json(obj.get('shipping_query')) + pre_checkout_query = PreCheckoutQuery.de_json(obj.get('pre_checkout_query')) + poll = Poll.de_json(obj.get('poll')) + poll_answer = PollAnswer.de_json(obj.get('poll_answer')) + my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member')) + chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) + chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) + 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) + + 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): + self.update_id = update_id + self.message = message + self.edited_message = edited_message + self.channel_post = channel_post + self.edited_channel_post = edited_channel_post + self.inline_query = inline_query + self.chosen_inline_result = chosen_inline_result + self.callback_query = callback_query + self.shipping_query = shipping_query + self.pre_checkout_query = pre_checkout_query + self.poll = poll + self.poll_answer = poll_answer + self.my_chat_member = my_chat_member + self.chat_member = chat_member + self.chat_join_request = chat_join_request + + +class ChatMemberUpdated(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['old_chat_member'] = ChatMember.de_json(obj['old_chat_member']) + obj['new_chat_member'] = ChatMember.de_json(obj['new_chat_member']) + 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, **kwargs): + self.chat: Chat = chat + self.from_user: User = from_user + self.date: int = date + self.old_chat_member: ChatMember = old_chat_member + self.new_chat_member: ChatMember = new_chat_member + self.invite_link: Optional[ChatInviteLink] = invite_link + + @property + def difference(self) -> Dict[str, List]: + """ + Get the difference between `old_chat_member` and `new_chat_member` + as a dict in the following format {'parameter': [old_value, new_value]} + E.g {'status': ['member', 'kicked'], 'until_date': [None, 1625055092]} + """ + old: Dict = self.old_chat_member.__dict__ + new: Dict = self.new_chat_member.__dict__ + dif = {} + for key in new: + if key == 'user': continue + if new[key] != old[key]: + dif[key] = [old[key], new[key]] + return dif + +class ChatJoinRequest(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['from_user'] = User.de_json(obj['from']) + obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link')) + return cls(**obj) + + def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs): + self.chat = chat + self.from_user = from_user + self.date = date + self.bio = bio + self.invite_link = invite_link + +class WebhookInfo(JsonDeserializable): + @classmethod + 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) + + def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, + last_error_date=None, last_error_message=None, max_connections=None, + allowed_updates=None, **kwargs): + self.url = url + self.has_custom_certificate = has_custom_certificate + self.pending_update_count = pending_update_count + self.ip_address = ip_address + self.last_error_date = last_error_date + self.last_error_message = last_error_message + self.max_connections = max_connections + self.allowed_updates = allowed_updates + + +class User(JsonDeserializable, Dictionaryable, JsonSerializable): + @classmethod + 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) + + 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, **kwargs): + self.id: int = id + self.is_bot: bool = is_bot + self.first_name: str = first_name + self.username: str = username + self.last_name: str = last_name + self.language_code: str = language_code + self.can_join_groups: bool = can_join_groups + self.can_read_all_group_messages: bool = can_read_all_group_messages + self.supports_inline_queries: bool = supports_inline_queries + + @property + def full_name(self): + full_name = self.first_name + if self.last_name: + full_name += ' {0}'.format(self.last_name) + return full_name + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'id': self.id, + 'is_bot': self.is_bot, + 'first_name': self.first_name, + 'last_name': self.last_name, + 'username': self.username, + 'language_code': self.language_code, + 'can_join_groups': self.can_join_groups, + 'can_read_all_group_messages': self.can_read_all_group_messages, + 'supports_inline_queries': self.supports_inline_queries} + + +class GroupChat(JsonDeserializable): + @classmethod + 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) + + def __init__(self, id, title, **kwargs): + self.id: int = id + self.title: str = title + + +class Chat(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'photo' in obj: + obj['photo'] = ChatPhoto.de_json(obj['photo']) + if 'pinned_message' in obj: + obj['pinned_message'] = Message.de_json(obj['pinned_message']) + if 'permissions' in obj: + obj['permissions'] = ChatPermissions.de_json(obj['permissions']) + if 'location' in obj: + obj['location'] = ChatLocation.de_json(obj['location']) + return cls(**obj) + + def __init__(self, id, type, title=None, username=None, first_name=None, + last_name=None, photo=None, bio=None, has_private_forwards=None, + description=None, invite_link=None, pinned_message=None, + permissions=None, slow_mode_delay=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, **kwargs): + self.id: int = id + self.type: str = type + self.title: str = title + self.username: str = username + self.first_name: str = first_name + self.last_name: str = last_name + self.photo: ChatPhoto = photo + self.bio: str = bio + self.has_private_forwards: bool = has_private_forwards + self.description: str = description + self.invite_link: str = invite_link + self.pinned_message: Message = pinned_message + self.permissions: ChatPermissions = permissions + self.slow_mode_delay: int = slow_mode_delay + self.message_auto_delete_time: int = message_auto_delete_time + self.has_protected_content: bool = has_protected_content + self.sticker_set_name: str = sticker_set_name + self.can_set_sticker_set: bool = can_set_sticker_set + self.linked_chat_id: int = linked_chat_id + self.location: ChatLocation = location + + +class MessageID(JsonDeserializable): + @classmethod + 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) + + def __init__(self, message_id, **kwargs): + self.message_id = message_id + + +class Message(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string, dict_copy=False) + message_id = obj['message_id'] + from_user = User.de_json(obj.get('from')) + date = obj['date'] + 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: + opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) + if 'forward_from_message_id' in obj: + opts['forward_from_message_id'] = obj.get('forward_from_message_id') + if 'forward_signature' in obj: + opts['forward_signature'] = obj.get('forward_signature') + if 'forward_sender_name' in obj: + opts['forward_sender_name'] = obj.get('forward_sender_name') + if 'forward_date' in obj: + opts['forward_date'] = obj.get('forward_date') + if 'is_automatic_forward' in obj: + opts['is_automatic_forward'] = obj.get('is_automatic_forward') + if 'reply_to_message' in obj: + opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) + if 'via_bot' in obj: + opts['via_bot'] = User.de_json(obj['via_bot']) + if 'edit_date' in obj: + opts['edit_date'] = obj.get('edit_date') + if 'has_protected_content' in obj: + opts['has_protected_content'] = obj.get('has_protected_content') + if 'media_group_id' in obj: + opts['media_group_id'] = obj.get('media_group_id') + if 'author_signature' in obj: + opts['author_signature'] = obj.get('author_signature') + if 'text' in obj: + opts['text'] = obj['text'] + content_type = 'text' + if 'entities' in obj: + opts['entities'] = Message.parse_entities(obj['entities']) + if 'caption_entities' in obj: + opts['caption_entities'] = Message.parse_entities(obj['caption_entities']) + if 'audio' in obj: + opts['audio'] = Audio.de_json(obj['audio']) + content_type = 'audio' + if 'document' in obj: + opts['document'] = Document.de_json(obj['document']) + content_type = 'document' + if 'animation' in obj: + # Document content type accompanies "animation", + # so "animation" should be checked below "document" to override it + opts['animation'] = Animation.de_json(obj['animation']) + content_type = 'animation' + if 'game' in obj: + opts['game'] = Game.de_json(obj['game']) + content_type = 'game' + if 'photo' in obj: + opts['photo'] = Message.parse_photo(obj['photo']) + content_type = 'photo' + if 'sticker' in obj: + opts['sticker'] = Sticker.de_json(obj['sticker']) + content_type = 'sticker' + if 'video' in obj: + opts['video'] = Video.de_json(obj['video']) + content_type = 'video' + if 'video_note' in obj: + opts['video_note'] = VideoNote.de_json(obj['video_note']) + content_type = 'video_note' + if 'voice' in obj: + opts['voice'] = Audio.de_json(obj['voice']) + content_type = 'voice' + if 'caption' in obj: + opts['caption'] = obj['caption'] + if 'contact' in obj: + opts['contact'] = Contact.de_json(json.dumps(obj['contact'])) + content_type = 'contact' + if 'location' in obj: + opts['location'] = Location.de_json(obj['location']) + content_type = 'location' + if 'venue' in obj: + opts['venue'] = Venue.de_json(obj['venue']) + content_type = 'venue' + if 'dice' in obj: + opts['dice'] = Dice.de_json(obj['dice']) + content_type = 'dice' + if 'new_chat_members' in obj: + new_chat_members = [] + for member in obj['new_chat_members']: + new_chat_members.append(User.de_json(member)) + opts['new_chat_members'] = new_chat_members + content_type = 'new_chat_members' + if 'left_chat_member' in obj: + opts['left_chat_member'] = User.de_json(obj['left_chat_member']) + content_type = 'left_chat_member' + if 'new_chat_title' in obj: + opts['new_chat_title'] = obj['new_chat_title'] + content_type = 'new_chat_title' + if 'new_chat_photo' in obj: + opts['new_chat_photo'] = Message.parse_photo(obj['new_chat_photo']) + content_type = 'new_chat_photo' + if 'delete_chat_photo' in obj: + opts['delete_chat_photo'] = obj['delete_chat_photo'] + content_type = 'delete_chat_photo' + if 'group_chat_created' in obj: + opts['group_chat_created'] = obj['group_chat_created'] + content_type = 'group_chat_created' + if 'supergroup_chat_created' in obj: + opts['supergroup_chat_created'] = obj['supergroup_chat_created'] + content_type = 'supergroup_chat_created' + if 'channel_chat_created' in obj: + opts['channel_chat_created'] = obj['channel_chat_created'] + content_type = 'channel_chat_created' + if 'migrate_to_chat_id' in obj: + opts['migrate_to_chat_id'] = obj['migrate_to_chat_id'] + content_type = 'migrate_to_chat_id' + if 'migrate_from_chat_id' in obj: + opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] + content_type = 'migrate_from_chat_id' + if 'pinned_message' in obj: + opts['pinned_message'] = Message.de_json(obj['pinned_message']) + content_type = 'pinned_message' + if 'invoice' in obj: + opts['invoice'] = Invoice.de_json(obj['invoice']) + content_type = 'invoice' + if 'successful_payment' in obj: + opts['successful_payment'] = SuccessfulPayment.de_json(obj['successful_payment']) + content_type = 'successful_payment' + if 'connected_website' in obj: + opts['connected_website'] = obj['connected_website'] + content_type = 'connected_website' + if 'poll' in obj: + opts['poll'] = Poll.de_json(obj['poll']) + content_type = 'poll' + if 'passport_data' in obj: + opts['passport_data'] = obj['passport_data'] + content_type = 'passport_data' + if 'proximity_alert_triggered' in obj: + opts['proximity_alert_triggered'] = ProximityAlertTriggered.de_json(obj[ + 'proximity_alert_triggered']) + content_type = 'proximity_alert_triggered' + if 'voice_chat_scheduled' in obj: + opts['voice_chat_scheduled'] = VoiceChatScheduled.de_json(obj['voice_chat_scheduled']) + content_type = 'voice_chat_scheduled' + if 'voice_chat_started' in obj: + opts['voice_chat_started'] = VoiceChatStarted.de_json(obj['voice_chat_started']) + content_type = 'voice_chat_started' + if 'voice_chat_ended' in obj: + opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) + content_type = 'voice_chat_ended' + if 'voice_chat_participants_invited' in obj: + opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['voice_chat_participants_invited']) + content_type = 'voice_chat_participants_invited' + if 'message_auto_delete_timer_changed' in obj: + opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(obj['message_auto_delete_timer_changed']) + content_type = 'message_auto_delete_timer_changed' + if 'reply_markup' in obj: + opts['reply_markup'] = InlineKeyboardMarkup.de_json(obj['reply_markup']) + return cls(message_id, from_user, date, chat, content_type, opts, json_string) + + @classmethod + def parse_chat(cls, chat): + if 'first_name' not in chat: + return GroupChat.de_json(chat) + else: + return User.de_json(chat) + + @classmethod + def parse_photo(cls, photo_size_array): + ret = [] + for ps in photo_size_array: + ret.append(PhotoSize.de_json(ps)) + return ret + + @classmethod + def parse_entities(cls, message_entity_array): + ret = [] + for me in message_entity_array: + ret.append(MessageEntity.de_json(me)) + return ret + + def __init__(self, message_id, from_user, date, chat, content_type, options, json_string): + self.content_type: str = content_type + self.id: int = message_id # Lets fix the telegram usability ####up with ID in Message :) + self.message_id: int = message_id + 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 + self.forward_signature: Optional[str] = None + self.forward_sender_name: Optional[str] = None + self.forward_date: Optional[int] = None + self.is_automatic_forward: Optional[bool] = None + self.reply_to_message: Optional[Message] = None + self.via_bot: Optional[User] = None + self.edit_date: Optional[int] = None + self.has_protected_content: Optional[bool] = None + self.media_group_id: Optional[str] = None + self.author_signature: Optional[str] = None + self.text: Optional[str] = None + self.entities: Optional[List[MessageEntity]] = None + self.caption_entities: Optional[List[MessageEntity]] = None + self.audio: Optional[Audio] = None + self.document: Optional[Document] = None + self.photo: Optional[List[PhotoSize]] = None + self.sticker: Optional[Sticker] = None + self.video: Optional[Video] = None + self.video_note: Optional[VideoNote] = None + self.voice: Optional[Voice] = None + self.caption: Optional[str] = None + self.contact: Optional[Contact] = None + self.location: Optional[Location] = None + self.venue: Optional[Venue] = None + self.animation: Optional[Animation] = None + self.dice: Optional[Dice] = None + self.new_chat_member: Optional[User] = None # Deprecated since Bot API 3.0. Not processed anymore + self.new_chat_members: Optional[List[User]] = None + self.left_chat_member: Optional[User] = None + self.new_chat_title: Optional[str] = None + self.new_chat_photo: Optional[List[PhotoSize]] = None + self.delete_chat_photo: Optional[bool] = None + self.group_chat_created: Optional[bool] = None + self.supergroup_chat_created: Optional[bool] = None + self.channel_chat_created: Optional[bool] = None + self.migrate_to_chat_id: Optional[int] = None + self.migrate_from_chat_id: Optional[int] = None + self.pinned_message: Optional[Message] = None + self.invoice: Optional[Invoice] = None + self.successful_payment: Optional[SuccessfulPayment] = None + self.connected_website: Optional[str] = None + self.reply_markup: Optional[InlineKeyboardMarkup] = None + for key in options: + setattr(self, key, options[key]) + self.json = json_string + + def __html_text(self, text, entities): + """ + Author: @sviat9440 + Updaters: @badiboy + Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" + + Example: + message.html_text + >> "Test parse formatting, url, text_mention and mention @username" + + Custom subs: + You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. + Example: + message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} + message.html_text + >> "Test parse formatting, url and text_mention and mention @username" + """ + + if not entities: + return text + + _subs = { + "bold": "{text}", + "italic": "{text}", + "pre": "
{text}
", + "code": "{text}", + # "url": "{text}", # @badiboy plain URLs have no text and do not need tags + "text_link": "{text}", + "strikethrough": "{text}", + "underline": "{text}", + "spoiler": "{text}", + } + + if hasattr(self, "custom_subs"): + for key, value in self.custom_subs.items(): + _subs[key] = value + utf16_text = text.encode("utf-16-le") + html_text = "" + + def func(upd_text, subst_type=None, url=None, user=None): + upd_text = upd_text.decode("utf-16-le") + if subst_type == "text_mention": + subst_type = "text_link" + url = "tg://user?id={0}".format(user.id) + elif subst_type == "mention": + url = "https://t.me/{0}".format(upd_text[1:]) + upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") + if not subst_type or not _subs.get(subst_type): + return upd_text + subs = _subs.get(subst_type) + return subs.format(text=upd_text, url=url) + + offset = 0 + for entity in entities: + if entity.offset > offset: + html_text += func(utf16_text[offset * 2 : entity.offset * 2]) + offset = entity.offset + html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + offset += entity.length + elif entity.offset == offset: + html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + offset += entity.length + else: + # TODO: process nested entities from Bot API 4.5 + # Now ignoring them + pass + if offset * 2 < len(utf16_text): + html_text += func(utf16_text[offset * 2:]) + return html_text + + @property + def html_text(self): + return self.__html_text(self.text, self.entities) + + @property + def html_caption(self): + return self.__html_text(self.caption, self.caption_entities) + + +class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): + @staticmethod + def to_list_of_dicts(entity_list) -> Union[List[Dict], None]: + res = [] + for e in entity_list: + res.append(MessageEntity.to_dict(e)) + return res or None + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'user' in obj: + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs): + self.type: str = type + self.offset: int = offset + self.length: int = length + self.url: str = url + self.user: User = user + self.language: str = language + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {"type": self.type, + "offset": self.offset, + "length": self.length, + "url": self.url, + "user": self.user, + "language": self.language} + + +class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): + @classmethod + 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) + + def __init__(self, value, emoji, **kwargs): + self.value: int = value + self.emoji: str = emoji + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'value': self.value, + 'emoji': self.emoji} + + +class PhotoSize(JsonDeserializable): + @classmethod + 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) + + def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.file_size: int = file_size + + +class Audio(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, + file_size=None, thumb=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.duration: int = duration + self.performer: str = performer + self.title: str = title + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + self.thumb: PhotoSize = thumb + + +class Voice(JsonDeserializable): + @classmethod + 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) + + def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.duration: int = duration + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class Document(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class Video(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class VideoNote(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.length: int = length + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_size: int = file_size + + +class Contact(JsonDeserializable): + @classmethod + 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) + + def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None, **kwargs): + self.phone_number: str = phone_number + self.first_name: str = first_name + self.last_name: str = last_name + self.user_id: int = user_id + self.vcard: str = vcard + + +class Location(JsonDeserializable, JsonSerializable, Dictionaryable): + @classmethod + 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) + + def __init__(self, longitude, latitude, horizontal_accuracy=None, + live_period=None, heading=None, proximity_alert_radius=None, **kwargs): + self.longitude: float = longitude + self.latitude: float = latitude + self.horizontal_accuracy: float = horizontal_accuracy + self.live_period: int = live_period + self.heading: int = heading + self.proximity_alert_radius: int = proximity_alert_radius + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return { + "longitude": self.longitude, + "latitude": self.latitude, + "horizontal_accuracy": self.horizontal_accuracy, + "live_period": self.live_period, + "heading": self.heading, + "proximity_alert_radius": self.proximity_alert_radius, + } + + +class Venue(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, + google_place_id=None, google_place_type=None, **kwargs): + self.location: Location = location + self.title: str = title + self.address: str = address + self.foursquare_id: str = foursquare_id + self.foursquare_type: str = foursquare_type + self.google_place_id: str = google_place_id + self.google_place_type: str = google_place_type + + +class UserProfilePhotos(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'photos' in obj: + photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']] + obj['photos'] = photos + return cls(**obj) + + def __init__(self, total_count, photos=None, **kwargs): + self.total_count: int = total_count + self.photos: List[PhotoSize] = photos + + +class File(JsonDeserializable): + @classmethod + 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) + + def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.file_size: int = file_size + self.file_path: str = file_path + + +class ForceReply(JsonSerializable): + def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Optional[str]=None): + self.selective: bool = selective + self.input_field_placeholder: str = input_field_placeholder + + def to_json(self): + json_dict = {'force_reply': True} + if self.selective is not None: + json_dict['selective'] = self.selective + if self.input_field_placeholder: + json_dict['input_field_placeholder'] = self.input_field_placeholder + return json.dumps(json_dict) + + +class ReplyKeyboardRemove(JsonSerializable): + def __init__(self, selective=None): + self.selective: bool = selective + + def to_json(self): + json_dict = {'remove_keyboard': True} + if self.selective: + json_dict['selective'] = self.selective + return json.dumps(json_dict) + + +class ReplyKeyboardMarkup(JsonSerializable): + max_row_keys = 12 + + def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, + selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None): + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + if not DISABLE_KEYLEN_ERROR: + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + self.resize_keyboard: bool = resize_keyboard + self.one_time_keyboard: bool = one_time_keyboard + self.selective: bool = selective + self.row_width: int = row_width + self.input_field_placeholder: str = input_field_placeholder + self.keyboard: List[List[KeyboardButton]] = [] + + def add(self, *args, row_width=None): + """ + This function adds strings to the keyboard, while not exceeding row_width. + E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} + when row_width is set to 1. + When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} + See https://core.telegram.org/bots/api#replykeyboardmarkup + :param args: KeyboardButton to append to the keyboard + :param row_width: width of row + :return: self, to allow function chaining. + """ + if row_width is None: + row_width = self.row_width + + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + if not DISABLE_KEYLEN_ERROR: + logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + for row in util.chunks(args, row_width): + button_array = [] + for button in row: + if util.is_string(button): + button_array.append({'text': button}) + elif util.is_bytes(button): + button_array.append({'text': button.decode('utf-8')}) + else: + button_array.append(button.to_dict()) + self.keyboard.append(button_array) + + return self + + def row(self, *args): + """ + Adds a list of KeyboardButton to the keyboard. This function does not consider row_width. + ReplyKeyboardMarkup#row("A")#row("B", "C")#to_json() outputs '{keyboard: [["A"], ["B", "C"]]}' + See https://core.telegram.org/bots/api#replykeyboardmarkup + :param args: strings + :return: self, to allow function chaining. + """ + + return self.add(*args, row_width=self.max_row_keys) + + def to_json(self): + """ + Converts this object to its json representation following the Telegram API guidelines described here: + https://core.telegram.org/bots/api#replykeyboardmarkup + :return: + """ + json_dict = {'keyboard': self.keyboard} + if self.one_time_keyboard is not None: + json_dict['one_time_keyboard'] = self.one_time_keyboard + if self.resize_keyboard is not None: + json_dict['resize_keyboard'] = self.resize_keyboard + if self.selective is not None: + json_dict['selective'] = self.selective + if self.input_field_placeholder: + json_dict['input_field_placeholder'] = self.input_field_placeholder + 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[KeyboardButtonPollType]=None): + self.text: str = text + self.request_contact: bool = request_contact + self.request_location: bool = request_location + self.request_poll: KeyboardButtonPollType = request_poll + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'text': self.text} + if self.request_contact is not None: + json_dict['request_contact'] = self.request_contact + if self.request_location is not None: + json_dict['request_location'] = self.request_location + if self.request_poll is not None: + json_dict['request_poll'] = self.request_poll.to_dict() + return json_dict + + +class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable): + max_row_keys = 8 + + @classmethod + def de_json(cls, json_string): + 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 = keyboard) + + def __init__(self, keyboard=None, row_width=3): + """ + This object represents an inline keyboard that appears + right next to the message it belongs to. + + :return: + """ + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + self.row_width: int = row_width + self.keyboard: List[List[InlineKeyboardButton]] = keyboard or [] + + def add(self, *args, row_width=None): + """ + This method adds buttons to the keyboard without exceeding row_width. + + E.g. InlineKeyboardMarkup.add("A", "B", "C") yields the json result: + {keyboard: [["A"], ["B"], ["C"]]} + when row_width is set to 1. + When row_width is set to 2, the result: + {keyboard: [["A", "B"], ["C"]]} + See https://core.telegram.org/bots/api#inlinekeyboardmarkup + + :param args: Array of InlineKeyboardButton to append to the keyboard + :param row_width: width of row + :return: self, to allow function chaining. + """ + if row_width is None: + row_width = self.row_width + + if row_width > self.max_row_keys: + # Todo: Will be replaced with Exception in future releases + logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) + row_width = self.max_row_keys + + for row in util.chunks(args, row_width): + button_array = [button for button in row] + self.keyboard.append(button_array) + + return self + + def row(self, *args): + """ + Adds a list of InlineKeyboardButton to the keyboard. + This method does not consider row_width. + + InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: + '{keyboard: [["A"], ["B", "C"]]}' + See https://core.telegram.org/bots/api#inlinekeyboardmarkup + + :param args: Array of InlineKeyboardButton to append to the keyboard + :return: self, to allow function chaining. + """ + + return self.add(*args, row_width=self.max_row_keys) + + def to_json(self): + """ + Converts this object to its json representation + following the Telegram API guidelines described here: + https://core.telegram.org/bots/api#inlinekeyboardmarkup + :return: + """ + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = dict() + json_dict['inline_keyboard'] = [[button.to_dict() for button in row] for row in self.keyboard] + return json_dict + + +class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'login_url' in obj: + obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) + return cls(**obj) + + def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, + switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): + self.text: str = text + self.url: str = url + self.callback_data: str = callback_data + self.switch_inline_query: str = switch_inline_query + self.switch_inline_query_current_chat: str = switch_inline_query_current_chat + self.callback_game = callback_game # Not Implemented + self.pay: bool = pay + self.login_url: LoginUrl = login_url + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'text': self.text} + if self.url: + json_dict['url'] = self.url + if self.callback_data: + json_dict['callback_data'] = self.callback_data + if self.switch_inline_query is not None: + json_dict['switch_inline_query'] = self.switch_inline_query + if self.switch_inline_query_current_chat is not None: + json_dict['switch_inline_query_current_chat'] = self.switch_inline_query_current_chat + if self.callback_game is not None: + json_dict['callback_game'] = self.callback_game + if self.pay is not None: + json_dict['pay'] = self.pay + if self.login_url is not None: + json_dict['login_url'] = self.login_url.to_dict() + return json_dict + + +class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): + @classmethod + 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) + + def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None, **kwargs): + self.url: str = url + self.forward_text: str = forward_text + self.bot_username: str = bot_username + self.request_write_access: bool = request_write_access + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'url': self.url} + if self.forward_text: + json_dict['forward_text'] = self.forward_text + if self.bot_username: + json_dict['bot_username'] = self.bot_username + if self.request_write_access is not None: + json_dict['request_write_access'] = self.request_write_access + return json_dict + + +class CallbackQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if not "data" in obj: + # "data" field is Optional in the API, but historically is mandatory in the class constructor + obj['data'] = None + obj['from_user'] = User.de_json(obj.pop('from')) + if 'message' in obj: + obj['message'] = Message.de_json(obj.get('message')) + return cls(**obj) + + def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None, **kwargs): + self.id: int = id + self.from_user: User = from_user + self.message: Message = message + self.inline_message_id: str = inline_message_id + self.chat_instance: str = chat_instance + self.data: str = data + self.game_short_name: str = game_short_name + + +class ChatPhoto(JsonDeserializable): + @classmethod + 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) + + def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_unique_id, **kwargs): + self.small_file_id: str = small_file_id + self.small_file_unique_id: str = small_file_unique_id + self.big_file_id: str = big_file_id + self.big_file_unique_id: str = big_file_unique_id + + +class ChatMember(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, + can_post_messages=None, can_edit_messages=None, can_delete_messages=None, + can_restrict_members=None, can_promote_members=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None, is_member=None, + can_send_messages=None, can_send_media_messages=None, can_send_polls=None, + can_send_other_messages=None, can_add_web_page_previews=None, + can_manage_chat=None, can_manage_voice_chats=None, + until_date=None, **kwargs): + self.user: User = user + self.status: str = status + self.custom_title: str = custom_title + self.is_anonymous: bool = is_anonymous + self.can_be_edited: bool = can_be_edited + self.can_post_messages: bool = can_post_messages + self.can_edit_messages: bool = can_edit_messages + self.can_delete_messages: bool = can_delete_messages + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + self.is_member: bool = is_member + self.can_send_messages: bool = can_send_messages + self.can_send_media_messages: bool = can_send_media_messages + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_manage_chat: bool = can_manage_chat + self.can_manage_voice_chats: bool = can_manage_voice_chats + self.until_date: int = until_date + + +class ChatMemberOwner(ChatMember): + pass + +class ChatMemberAdministrator(ChatMember): + pass + + +class ChatMemberMember(ChatMember): + pass + + +class ChatMemberRestricted(ChatMember): + pass + + +class ChatMemberLeft(ChatMember): + pass + + +class ChatMemberBanned(ChatMember): + pass + + +class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return json_string + obj = cls.check_json(json_string, dict_copy=False) + return cls(**obj) + + def __init__(self, can_send_messages=None, can_send_media_messages=None, + can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None, **kwargs): + self.can_send_messages: bool = can_send_messages + self.can_send_media_messages: bool = can_send_media_messages + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = dict() + if self.can_send_messages is not None: + json_dict['can_send_messages'] = self.can_send_messages + if self.can_send_media_messages is not None: + json_dict['can_send_media_messages'] = self.can_send_media_messages + if self.can_send_polls is not None: + json_dict['can_send_polls'] = self.can_send_polls + if self.can_send_other_messages is not None: + json_dict['can_send_other_messages'] = self.can_send_other_messages + if self.can_add_web_page_previews is not None: + json_dict['can_add_web_page_previews'] = self.can_add_web_page_previews + if self.can_change_info is not None: + json_dict['can_change_info'] = self.can_change_info + if self.can_invite_users is not None: + json_dict['can_invite_users'] = self.can_invite_users + if self.can_pin_messages is not None: + json_dict['can_pin_messages'] = self.can_pin_messages + return json_dict + + +class BotCommand(JsonSerializable, JsonDeserializable): + @classmethod + 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) + + def __init__(self, command, description): + """ + This object represents a bot command. + :param command: Text of the command, 1-32 characters. + Can contain only lowercase English letters, digits and underscores. + :param description: Description of the command, 3-256 characters. + :return: + """ + self.command: str = command + self.description: str = description + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'command': self.command, 'description': self.description} + + +# BotCommandScopes + +class BotCommandScope(ABC, JsonSerializable): + def __init__(self, type='default', chat_id=None, user_id=None): + """ + Abstract class. + Use BotCommandScopeX classes to set a specific scope type: + BotCommandScopeDefault + BotCommandScopeAllPrivateChats + BotCommandScopeAllGroupChats + BotCommandScopeAllChatAdministrators + BotCommandScopeChat + BotCommandScopeChatAdministrators + BotCommandScopeChatMember + """ + self.type: str = type + self.chat_id: Optional[Union[int, str]] = chat_id + self.user_id: Optional[Union[int, str]] = user_id + + def to_json(self): + json_dict = {'type': self.type} + if self.chat_id: + json_dict['chat_id'] = self.chat_id + if self.user_id: + json_dict['user_id'] = self.user_id + return json.dumps(json_dict) + + +class BotCommandScopeDefault(BotCommandScope): + def __init__(self): + """ + Represents the default scope of bot commands. + Default commands are used if no commands with a narrower scope are specified for the user. + """ + super(BotCommandScopeDefault, self).__init__(type='default') + + +class BotCommandScopeAllPrivateChats(BotCommandScope): + def __init__(self): + """ + Represents the scope of bot commands, covering all private chats. + """ + super(BotCommandScopeAllPrivateChats, self).__init__(type='all_private_chats') + + +class BotCommandScopeAllGroupChats(BotCommandScope): + def __init__(self): + """ + Represents the scope of bot commands, covering all group and supergroup chats. + """ + super(BotCommandScopeAllGroupChats, self).__init__(type='all_group_chats') + + +class BotCommandScopeAllChatAdministrators(BotCommandScope): + def __init__(self): + """ + Represents the scope of bot commands, covering all group and supergroup chat administrators. + """ + super(BotCommandScopeAllChatAdministrators, self).__init__(type='all_chat_administrators') + + +class BotCommandScopeChat(BotCommandScope): + def __init__(self, chat_id=None): + super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) + + +class BotCommandScopeChatAdministrators(BotCommandScope): + def __init__(self, chat_id=None): + """ + Represents the scope of bot commands, covering a specific chat. + @param chat_id: Unique identifier for the target chat + """ + super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) + + +class BotCommandScopeChatMember(BotCommandScope): + def __init__(self, chat_id=None, user_id=None): + """ + Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat + @param chat_id: Unique identifier for the target chat + @param user_id: Unique identifier of the target user + """ + super(BotCommandScopeChatMember, self).__init__(type='chat_member', chat_id=chat_id, user_id=user_id) + + +# InlineQuery + +class InlineQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, id, from_user, query, offset, chat_type=None, location=None, **kwargs): + """ + This object represents an incoming inline query. + When the user sends an empty query, your bot could + return some default or trending results. + :param id: string Unique identifier for this query + :param from_user: User Sender + :param query: String Text of the query + :param chat_type: String Type of the chat, from which the inline query was sent. + Can be either “sender” for a private chat with the inline query sender, + “private”, “group”, “supergroup”, or “channel”. + :param offset: String Offset of the results to be returned, can be controlled by the bot + :param location: Sender location, only for bots that request user location + :return: InlineQuery Object + """ + self.id: int = id + self.from_user: User = from_user + self.query: str = query + self.offset: str = offset + self.chat_type: str = chat_type + self.location: Location = location + + +class InputTextMessageContent(Dictionaryable): + def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None): + self.message_text: str = message_text + self.parse_mode: str = parse_mode + self.entities: List[MessageEntity] = entities + self.disable_web_page_preview: bool = disable_web_page_preview + + def to_dict(self): + json_dict = {'message_text': self.message_text} + if self.parse_mode: + json_dict['parse_mode'] = self.parse_mode + if self.entities: + json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) + if self.disable_web_page_preview is not None: + json_dict['disable_web_page_preview'] = self.disable_web_page_preview + return json_dict + + +class InputLocationMessageContent(Dictionaryable): + def __init__(self, latitude, longitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None): + self.latitude: float = latitude + self.longitude: float = longitude + self.horizontal_accuracy: float = horizontal_accuracy + self.live_period: int = live_period + self.heading: int = heading + self.proximity_alert_radius: int = proximity_alert_radius + + def to_dict(self): + json_dict = {'latitude': self.latitude, '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 + return json_dict + + +class InputVenueMessageContent(Dictionaryable): + def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, + google_place_id=None, google_place_type=None): + self.latitude: float = latitude + self.longitude: float = longitude + self.title: str = title + self.address: str = address + self.foursquare_id: str = foursquare_id + self.foursquare_type: str = foursquare_type + self.google_place_id: str = google_place_id + self.google_place_type: str = google_place_type + + def to_dict(self): + json_dict = { + 'latitude': self.latitude, + 'longitude': self.longitude, + 'title': self.title, + '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.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_dict + + +class InputContactMessageContent(Dictionaryable): + def __init__(self, phone_number, first_name, last_name=None, vcard=None): + self.phone_number: str = phone_number + self.first_name: str = first_name + self.last_name: str = last_name + self.vcard: str = vcard + + def to_dict(self): + json_dict = {'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 + return json_dict + + +class InputInvoiceMessageContent(Dictionaryable): + def __init__(self, title, description, payload, provider_token, currency, prices, + max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, + photo_url=None, photo_size=None, photo_width=None, photo_height=None, + need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, + send_phone_number_to_provider=None, send_email_to_provider=None, + is_flexible=None): + self.title: str = title + self.description: str = description + self.payload: str = payload + self.provider_token: str = provider_token + self.currency: str = currency + self.prices: List[LabeledPrice] = prices + self.max_tip_amount: Optional[int] = max_tip_amount + self.suggested_tip_amounts: Optional[List[int]] = suggested_tip_amounts + self.provider_data: Optional[str] = provider_data + self.photo_url: Optional[str] = photo_url + self.photo_size: Optional[int] = photo_size + self.photo_width: Optional[int] = photo_width + self.photo_height: Optional[int] = photo_height + self.need_name: Optional[bool] = need_name + self.need_phone_number: Optional[bool] = need_phone_number + self.need_email: Optional[bool] = need_email + self.need_shipping_address: Optional[bool] = need_shipping_address + self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider + self.send_email_to_provider: Optional[bool] = send_email_to_provider + self.is_flexible: Optional[bool] = is_flexible + + def to_dict(self): + json_dict = { + 'title': self.title, + 'description': self.description, + 'payload': self.payload, + 'provider_token': self.provider_token, + 'currency': self.currency, + 'prices': [LabeledPrice.to_dict(lp) for lp in self.prices] + } + if self.max_tip_amount: + json_dict['max_tip_amount'] = self.max_tip_amount + if self.suggested_tip_amounts: + json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts + if self.provider_data: + json_dict['provider_data'] = self.provider_data + if self.photo_url: + json_dict['photo_url'] = self.photo_url + if self.photo_size: + json_dict['photo_size'] = self.photo_size + if self.photo_width: + json_dict['photo_width'] = self.photo_width + if self.photo_height: + json_dict['photo_height'] = self.photo_height + if self.need_name is not None: + json_dict['need_name'] = self.need_name + if self.need_phone_number is not None: + json_dict['need_phone_number'] = self.need_phone_number + if self.need_email is not None: + json_dict['need_email'] = self.need_email + if self.need_shipping_address is not None: + json_dict['need_shipping_address'] = self.need_shipping_address + if self.send_phone_number_to_provider is not None: + json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider + if self.send_email_to_provider is not None: + json_dict['send_email_to_provider'] = self.send_email_to_provider + if self.is_flexible is not None: + json_dict['is_flexible'] = self.is_flexible + return json_dict + + +class ChosenInlineResult(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, result_id, from_user, query, location=None, inline_message_id=None, **kwargs): + """ + This object represents a result of an inline query + that was chosen by the user and sent to their chat partner. + :param result_id: string The unique identifier for the result that was chosen. + :param from_user: User The user that chose the result. + :param query: String The query that was used to obtain the result. + :return: ChosenInlineResult Object. + """ + self.result_id: str = result_id + self.from_user: User = from_user + self.location: Location = location + self.inline_message_id: str = inline_message_id + self.query: str = query + + +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. + :param title: Title of the result. + :param input_message_content: InputMessageContent : Content of the message to be sent + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param url: URL of the result. + :param hide_url: Pass True, if you don't want the URL to be shown in the message. + :param description: Short description of the result. + :param thumb_url: Url of the thumbnail for the result. + :param thumb_width: Thumbnail width. + :param thumb_height: Thumbnail height + :return: + """ + 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 + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height + + def to_dict(self): + json_dict = super().to_dict() + if self.url: + json_dict['url'] = self.url + if self.hide_url: + json_dict['hide_url'] = self.hide_url + if self.description: + json_dict['description'] = self.description + 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 InlineQueryResultPhoto(InlineQueryResultBase): + def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=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 + :param photo_url: A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB + :param thumb_url: URL of the thumbnail for the photo + :param photo_width: Width of the photo. + :param photo_height: Height of the photo. + :param title: Title for the result. + :param description: Short description of the result. + :param caption: Caption of the photo to be sent, 0-200 characters. + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or + inline URLs in the media caption. + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo + :return: + """ + 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.description = description + + 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.description: + json_dict['description'] = self.description + return json_dict + + +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, + thumb_mime_type=None): + """ + Represents a link to an animated GIF file. + :param id: Unique identifier for this result, 1-64 bytes. + :param gif_url: A valid URL for the GIF file. File size must not exceed 1MB + :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result. + :param gif_width: Width of the GIF. + :param gif_height: Height of the GIF. + :param title: Title for the result. + :param caption: Caption of the GIF file to be sent, 0-200 characters + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo + :return: + """ + 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.gif_duration = gif_duration + self.thumb_mime_type = thumb_mime_type + + 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.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 + if self.thumb_mime_type: + json_dict['thumb_mime_type'] = self.thumb_mime_type + return json_dict + + +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, + thumb_mime_type=None): + """ + Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). + :param id: Unique identifier for this result, 1-64 bytes + :param mpeg4_url: A valid URL for the MP4 file. File size must not exceed 1MB + :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result + :param mpeg4_width: Video width + :param mpeg4_height: Video height + :param title: Title for the result + :param caption: Caption of the MPEG-4 file to be sent, 0-200 characters + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text + or inline URLs in the media caption. + :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message + :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo + :return: + """ + 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.mpeg4_duration = mpeg4_duration + self.thumb_mime_type = thumb_mime_type + + 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 + json_dict['thumb_url'] = self.thumb_url + if self.mpeg4_duration: + json_dict['mpeg4_duration '] = self.mpeg4_duration + if self.thumb_mime_type: + json_dict['thumb_mime_type'] = self.thumb_mime_type + return json_dict + + +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. + :param id: Unique identifier for this result, 1-64 bytes + :param video_url: A valid URL for the embedded video player or video file + :param mime_type: Mime type of the content of video url, “text/html” or “video/mp4” + :param thumb_url: URL of the thumbnail (jpeg only) for the video + :param title: Title for the result + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or + inline URLs in the media caption. + :param video_width: Video width + :param video_height: Video height + :param video_duration: Video duration in seconds + :param description: Short description of the result + :return: + """ + 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.description = description + + 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 + return json_dict + + +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.performer = performer + self.audio_duration = audio_duration + + 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 + return json_dict + + +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.voice_duration = voice_duration + + 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 + return json_dict + + +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.description = description + 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['document_url'] = self.document_url + json_dict['mime_type'] = self.mime_type + if self.description: + json_dict['description'] = self.description + 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 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, 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.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_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 + return json_dict + + +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): + 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.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 + + 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.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 + 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 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 + self.title = None + self.description = None + self.caption = None + self.reply_markup = None + self.input_message_content = None + self.parse_mode = None + self.caption_entities = None + self.payload_dic = {} + + def to_json(self): + json_dict = self.payload_dic + json_dict['type'] = self.type + json_dict['id'] = self.id + 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.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.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(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): + 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(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(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): + 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(InlineQueryResultCachedBase): + def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content=None): + InlineQueryResultCachedBase.__init__(self) + self.type = 'sticker' + self.id = id + self.sticker_file_id = sticker_file_id + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.payload_dic['sticker_file_id'] = sticker_file_id + + +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(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): + 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(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(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 + self.payload_dic['audio_file_id'] = audio_file_id + + +# Games + +class Game(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['photo'] = Game.parse_photo(obj['photo']) + if 'text_entities' in obj: + obj['text_entities'] = Game.parse_entities(obj['text_entities']) + if 'animation' in obj: + obj['animation'] = Animation.de_json(obj['animation']) + return cls(**obj) + + @classmethod + def parse_photo(cls, photo_size_array): + ret = [] + for ps in photo_size_array: + ret.append(PhotoSize.de_json(ps)) + return ret + + @classmethod + def parse_entities(cls, message_entity_array): + ret = [] + for me in message_entity_array: + ret.append(MessageEntity.de_json(me)) + return ret + + def __init__(self, title, description, photo, text=None, text_entities=None, animation=None, **kwargs): + self.title: str = title + self.description: str = description + self.photo: List[PhotoSize] = photo + self.text: str = text + self.text_entities: List[MessageEntity] = text_entities + self.animation: Animation = animation + + +class Animation(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj["thumb"] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, + thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.duration: int = duration + self.thumb: PhotoSize = thumb + self.file_name: str = file_name + self.mime_type: str = mime_type + self.file_size: int = file_size + + +class GameHighScore(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, position, user, score, **kwargs): + self.position: int = position + self.user: User = user + self.score: int = score + + +# Payments + +class LabeledPrice(JsonSerializable): + def __init__(self, label, amount): + self.label: str = label + self.amount: int = amount + + def to_dict(self): + return { + 'label': self.label, 'amount': self.amount + } + + def to_json(self): + return json.dumps(self.to_dict()) + + +class Invoice(JsonDeserializable): + @classmethod + 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) + + def __init__(self, title, description, start_parameter, currency, total_amount, **kwargs): + self.title: str = title + self.description: str = description + self.start_parameter: str = start_parameter + self.currency: str = currency + self.total_amount: int = total_amount + + +class ShippingAddress(JsonDeserializable): + @classmethod + 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) + + def __init__(self, country_code, state, city, street_line1, street_line2, post_code, **kwargs): + self.country_code: str = country_code + self.state: str = state + self.city: str = city + self.street_line1: str = street_line1 + self.street_line2: str = street_line2 + self.post_code: str = post_code + + +class OrderInfo(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['shipping_address'] = ShippingAddress.de_json(obj.get('shipping_address')) + return cls(**obj) + + def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs): + self.name: str = name + self.phone_number: str = phone_number + self.email: str = email + self.shipping_address: ShippingAddress = shipping_address + + +class ShippingOption(JsonSerializable): + def __init__(self, id, title): + self.id: str = id + self.title: str = title + self.prices: List[LabeledPrice] = [] + + def add_price(self, *args): + """ + Add LabeledPrice to ShippingOption + :param args: LabeledPrices + """ + for price in args: + self.prices.append(price) + return self + + def to_json(self): + price_list = [] + for p in self.prices: + price_list.append(p.to_dict()) + json_dict = json.dumps({'id': self.id, 'title': self.title, 'prices': price_list}) + return json_dict + + +class SuccessfulPayment(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) + return cls(**obj) + + def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, + telegram_payment_charge_id=None, provider_payment_charge_id=None, **kwargs): + self.currency: str = currency + self.total_amount: int = total_amount + self.invoice_payload: str = invoice_payload + self.shipping_option_id: str = shipping_option_id + self.order_info: OrderInfo = order_info + self.telegram_payment_charge_id: str = telegram_payment_charge_id + self.provider_payment_charge_id: str = provider_payment_charge_id + + +class ShippingQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) + return cls(**obj) + + def __init__(self, id, from_user, invoice_payload, shipping_address, **kwargs): + self.id: str = id + self.from_user: User = from_user + self.invoice_payload: str = invoice_payload + self.shipping_address: ShippingAddress = shipping_address + + +class PreCheckoutQuery(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj.pop('from')) + obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) + return cls(**obj) + + def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs): + self.id: str = id + self.from_user: User = from_user + self.currency: str = currency + self.total_amount: int = total_amount + self.invoice_payload: str = invoice_payload + self.shipping_option_id: str = shipping_option_id + self.order_info: OrderInfo = order_info + + +# Stickers + +class StickerSet(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + stickers = [] + for s in obj['stickers']: + stickers.append(Sticker.de_json(s)) + obj['stickers'] = stickers + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + return cls(**obj) + + def __init__(self, name, title, is_animated, contains_masks, stickers, thumb=None, **kwargs): + self.name: str = name + self.title: str = title + self.is_animated: bool = is_animated + self.contains_masks: bool = contains_masks + self.stickers: List[Sticker] = stickers + self.thumb: PhotoSize = thumb + + +class Sticker(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + else: + obj['thumb'] = None + if 'mask_position' in obj: + obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, width, height, is_animated, + thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): + self.file_id: str = file_id + self.file_unique_id: str = file_unique_id + self.width: int = width + self.height: int = height + self.is_animated: bool = is_animated + self.thumb: PhotoSize = thumb + self.emoji: str = emoji + self.set_name: str = set_name + self.mask_position: MaskPosition = mask_position + self.file_size: int = file_size + + + +class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): + @classmethod + 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) + + def __init__(self, point, x_shift, y_shift, scale, **kwargs): + self.point: str = point + self.x_shift: float = x_shift + self.y_shift: float = y_shift + self.scale: float = scale + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'point': self.point, 'x_shift': self.x_shift, 'y_shift': self.y_shift, 'scale': self.scale} + + +# InputMedia + +class InputMedia(Dictionaryable, JsonSerializable): + def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None): + self.type: str = type + self.media: str = media + self.caption: Optional[str] = caption + self.parse_mode: Optional[str] = parse_mode + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + + if util.is_string(self.media): + self._media_name = '' + self._media_dic = self.media + else: + self._media_name = util.generate_random_token() + self._media_dic = 'attach://{0}'.format(self._media_name) + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = {'type': self.type, 'media': self._media_dic} + if self.caption: + json_dict['caption'] = self.caption + 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_dict + + def convert_input_media(self): + if util.is_string(self.media): + return self.to_json(), None + + return self.to_json(), {self._media_name: self.media} + + +class InputMediaPhoto(InputMedia): + def __init__(self, media, caption=None, parse_mode=None): + if util.is_pil_image(media): + media = util.pil_image_to_file(media) + + super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) + + def to_dict(self): + return super(InputMediaPhoto, self).to_dict() + + +class InputMediaVideo(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, + supports_streaming=None): + super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.width = width + self.height = height + self.duration = duration + self.supports_streaming = supports_streaming + + def to_dict(self): + ret = super(InputMediaVideo, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.width: + ret['width'] = self.width + if self.height: + ret['height'] = self.height + if self.duration: + ret['duration'] = self.duration + if self.supports_streaming: + ret['supports_streaming'] = self.supports_streaming + return ret + + +class InputMediaAnimation(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): + super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.width = width + self.height = height + self.duration = duration + + def to_dict(self): + ret = super(InputMediaAnimation, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.width: + ret['width'] = self.width + if self.height: + ret['height'] = self.height + if self.duration: + ret['duration'] = self.duration + return ret + + +class InputMediaAudio(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): + super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.duration = duration + self.performer = performer + self.title = title + + def to_dict(self): + ret = super(InputMediaAudio, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.duration: + ret['duration'] = self.duration + if self.performer: + ret['performer'] = self.performer + if self.title: + ret['title'] = self.title + return ret + + +class InputMediaDocument(InputMedia): + def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None): + super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) + self.thumb = thumb + self.disable_content_type_detection = disable_content_type_detection + + def to_dict(self): + ret = super(InputMediaDocument, self).to_dict() + if self.thumb: + ret['thumb'] = self.thumb + if self.disable_content_type_detection is not None: + ret['disable_content_type_detection'] = self.disable_content_type_detection + return ret + + +class PollOption(JsonDeserializable): + @classmethod + 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) + + def __init__(self, text, voter_count = 0, **kwargs): + self.text: str = text + self.voter_count: int = voter_count + # Converted in _convert_poll_options + # def to_json(self): + # # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll + # return json.dumps(self.text) + + +class Poll(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['poll_id'] = obj.pop('id') + options = [] + for opt in obj['options']: + options.append(PollOption.de_json(opt)) + obj['options'] = options or None + if 'explanation_entities' in obj: + obj['explanation_entities'] = Message.parse_entities(obj['explanation_entities']) + return cls(**obj) + + def __init__( + self, + question, options, + poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, + allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, + open_period=None, close_date=None, **kwargs): + self.id: str = poll_id + self.question: str = question + self.options: List[PollOption] = options + self.total_voter_count: int = total_voter_count + self.is_closed: bool = is_closed + self.is_anonymous: bool = is_anonymous + self.type: str = poll_type + self.allows_multiple_answers: bool = allows_multiple_answers + self.correct_option_id: int = correct_option_id + self.explanation: str = explanation + self.explanation_entities: List[MessageEntity] = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] + self.open_period: int = open_period + self.close_date: int = close_date + + def add(self, option): + if type(option) is PollOption: + self.options.append(option) + else: + self.options.append(PollOption(option)) + + +class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if (json_string is None): return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, poll_id, user, option_ids, **kwargs): + self.poll_id: str = poll_id + self.user: User = user + self.option_ids: List[int] = option_ids + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return {'poll_id': self.poll_id, + 'user': self.user.to_dict(), + 'option_ids': self.option_ids} + + +class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return json_string + obj = cls.check_json(json_string) + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, location, address, **kwargs): + self.location: Location = location + self.address: str = address + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + return { + "location": self.location.to_dict(), + "address": self.address + } + + +class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['creator'] = User.de_json(obj['creator']) + return cls(**obj) + + def __init__(self, invite_link, creator, creates_join_request , is_primary, is_revoked, + name=None, expire_date=None, member_limit=None, pending_join_request_count=None, **kwargs): + self.invite_link: str = invite_link + self.creator: User = creator + self.creates_join_request: bool = creates_join_request + self.is_primary: bool = is_primary + self.is_revoked: bool = is_revoked + self.name: str = name + self.expire_date: int = expire_date + self.member_limit: int = member_limit + self.pending_join_request_count: int = pending_join_request_count + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + json_dict = { + "invite_link": self.invite_link, + "creator": self.creator.to_dict(), + "is_primary": self.is_primary, + "is_revoked": self.is_revoked, + "creates_join_request": self.creates_join_request + } + if self.expire_date: + json_dict["expire_date"] = self.expire_date + if self.member_limit: + json_dict["member_limit"] = self.member_limit + if self.pending_join_request_count: + json_dict["pending_join_request_count"] = self.pending_join_request_count + if self.name: + json_dict["name"] = self.name + return json_dict + + +class ProximityAlertTriggered(JsonDeserializable): + @classmethod + 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) + + def __init__(self, traveler, watcher, distance, **kwargs): + self.traveler: User = traveler + self.watcher: User = watcher + self.distance: int = distance + + +class VoiceChatStarted(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + return cls() + + def __init__(self): + """ + This object represents a service message about a voice chat started in the chat. + Currently holds no information. + """ + pass + + +class VoiceChatScheduled(JsonDeserializable): + @classmethod + 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) + + def __init__(self, start_date, **kwargs): + self.start_date: int = start_date + + +class VoiceChatEnded(JsonDeserializable): + @classmethod + 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) + + def __init__(self, duration, **kwargs): + self.duration: int = duration + + +class VoiceChatParticipantsInvited(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'users' in obj: + obj['users'] = [User.de_json(u) for u in obj['users']] + return cls(**obj) + + def __init__(self, users=None, **kwargs): + self.users: List[User] = users + + +class MessageAutoDeleteTimerChanged(JsonDeserializable): + @classmethod + 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) + + def __init__(self, message_auto_delete_time, **kwargs): + self.message_auto_delete_time = message_auto_delete_time From 124b07ee44cada246f18a2423cb2451bb8297b35 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 19:08:34 +0400 Subject: [PATCH 0824/1808] Create __init__.py --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb From 8045ad56ea84dcc6267e0ad800bcb4283c1e3a0d Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 21:24:56 +0400 Subject: [PATCH 0825/1808] States Update --- .../asynchronous_telebot/custom_states.py | 32 ++++++---- examples/custom_states.py | 58 +++++++++++++------ telebot/async_telebot.py | 17 +++--- telebot/asyncio_handler_backends.py | 16 +++++ telebot/handler_backends.py | 16 +++++ 5 files changed, 101 insertions(+), 38 deletions(-) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index 56546bc9e..7658a8170 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -1,14 +1,22 @@ import telebot from telebot import asyncio_filters from telebot.async_telebot import AsyncTeleBot + +# list of storages, you can use any storage +from telebot.asyncio_storage import StateRedisStorage,StateMemoryStorage,StatePickleStorage + +# new feature for states. +from telebot.asyncio_handler_backends import State, StatesGroup + bot = AsyncTeleBot('TOKEN') +# Just create different statesgroup +class MyStates(StatesGroup): + name = State() # statesgroup should contain states + surname = State() + age = State() -class MyStates: - name = 1 - surname = 2 - age = 3 @@ -17,7 +25,7 @@ async def start_ex(message): """ Start command. Here we are starting state """ - await bot.set_state(message.from_user.id, MyStates.name) + await bot.set_state(message.from_user.id, MyStates.name, message.chat.id) await bot.send_message(message.chat.id, 'Hi, write me a name') @@ -28,7 +36,7 @@ async def any_state(message): Cancel state """ await bot.send_message(message.chat.id, "Your state was cancelled.") - await bot.delete_state(message.from_user.id) + await bot.delete_state(message.from_user.id, message.chat.id) @bot.message_handler(state=MyStates.name) async def name_get(message): @@ -36,8 +44,8 @@ async def name_get(message): State 1. Will process when user's state is 1. """ await bot.send_message(message.chat.id, f'Now write me a surname') - await bot.set_state(message.from_user.id, MyStates.surname) - async with bot.retrieve_data(message.from_user.id) as data: + await bot.set_state(message.from_user.id, MyStates.surname, message.chat.id) + async with bot.retrieve_data(message.from_user.id, message.chat.id) as data: data['name'] = message.text @@ -47,16 +55,16 @@ async def ask_age(message): State 2. Will process when user's state is 2. """ await bot.send_message(message.chat.id, "What is your age?") - await bot.set_state(message.from_user.id, MyStates.age) - async with bot.retrieve_data(message.from_user.id) as data: + await bot.set_state(message.from_user.id, MyStates.age, message.chat.id) + async with bot.retrieve_data(message.from_user.id, message.chat.id) as data: data['surname'] = message.text # result @bot.message_handler(state=MyStates.age, is_digit=True) async def ready_for_answer(message): - async with bot.retrieve_data(message.from_user.id) as data: + async with bot.retrieve_data(message.from_user.id, message.chat.id) as data: await bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") - await bot.delete_state(message.from_user.id) + await bot.delete_state(message.from_user.id, message.chat.id) #incorrect number @bot.message_handler(state=MyStates.age, is_digit=False) diff --git a/examples/custom_states.py b/examples/custom_states.py index 5acc8f2fd..7901896e7 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -1,14 +1,39 @@ -import telebot +import telebot # telebot from telebot import custom_filters +from telebot.handler_backends import State, StatesGroup #States -bot = telebot.TeleBot("") +# States storage +from telebot.storage import StateRedisStorage, StatePickleStorage, StateMemoryStorage -class MyStates: - name = 1 - surname = 2 - age = 3 +# Beginning from version 4.4.0+, we support storages. +# StateRedisStorage -> Redis-based storage. +# StatePickleStorage -> Pickle-based storage. +# For redis, you will need to install redis. +# Pass host, db, password, or anything else, +# if you need to change config for redis. +# Pickle requires path. Default path is in folder .state-saves. +# If you were using older version of pytba for pickle, +# you need to migrate from old pickle to new by using +# StatePickleStorage().convert_old_to_new() + + + +# Now, you can pass storage to bot. +state_storage = StateMemoryStorage() # you can init here another storage + +bot = telebot.TeleBot("TOKEN", +state_storage=state_storage) + + +# States group. +class MyStates(StatesGroup): + # Just name variables differently + name = State() # creating instances of State class is enough from now + surname = State() + age = State() + @@ -17,18 +42,18 @@ def start_ex(message): """ Start command. Here we are starting state """ - bot.set_state(message.from_user.id, MyStates.name) + bot.set_state(message.from_user.id, MyStates.name, message.chat.id) bot.send_message(message.chat.id, 'Hi, write me a name') - +# Any state @bot.message_handler(state="*", commands='cancel') def any_state(message): """ Cancel state """ bot.send_message(message.chat.id, "Your state was cancelled.") - bot.delete_state(message.from_user.id) + bot.delete_state(message.from_user.id, message.chat.id) @bot.message_handler(state=MyStates.name) def name_get(message): @@ -36,8 +61,8 @@ def name_get(message): State 1. Will process when user's state is 1. """ bot.send_message(message.chat.id, f'Now write me a surname') - bot.set_state(message.from_user.id, MyStates.surname) - with bot.retrieve_data(message.from_user.id) as data: + bot.set_state(message.from_user.id, MyStates.surname, message.chat.id) + with bot.retrieve_data(message.from_user.id, message.chat.id) as data: data['name'] = message.text @@ -47,16 +72,16 @@ def ask_age(message): State 2. Will process when user's state is 2. """ bot.send_message(message.chat.id, "What is your age?") - bot.set_state(message.from_user.id, MyStates.age) - with bot.retrieve_data(message.from_user.id) as data: + bot.set_state(message.from_user.id, MyStates.age, message.chat.id) + with bot.retrieve_data(message.from_user.id, message.chat.id) as data: data['surname'] = message.text # result @bot.message_handler(state=MyStates.age, is_digit=True) def ready_for_answer(message): - with bot.retrieve_data(message.from_user.id) as data: + with bot.retrieve_data(message.from_user.id, message.chat.id) as data: bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") - bot.delete_state(message.from_user.id) + bot.delete_state(message.from_user.id, message.chat.id) #incorrect number @bot.message_handler(state=MyStates.age, is_digit=False) @@ -68,7 +93,4 @@ def age_incorrect(message): bot.add_custom_filter(custom_filters.StateFilter(bot)) bot.add_custom_filter(custom_filters.IsDigitFilter()) -# set saving states into file. -bot.enable_saving_states() # you can delete this if you do not need to save states - bot.infinity_polling(skip_pending=True) \ No newline at end of file diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0e297ee51..a2edafbb8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -364,12 +364,13 @@ async def _run_middlewares_and_handlers(self, handlers, message, middleware): handler_error = None data = {} process_handler = True - middleware_result = await middleware.pre_process(message, data) - if isinstance(middleware_result, SkipHandler): - await middleware.post_process(message, data, handler_error) - process_handler = False - if isinstance(middleware_result, CancelUpdate): - return + if middleware: + middleware_result = await middleware.pre_process(message, data) + if isinstance(middleware_result, SkipHandler): + await middleware.post_process(message, data, handler_error) + process_handler = False + if isinstance(middleware_result, CancelUpdate): + return for handler in handlers: if not process_handler: break @@ -2481,8 +2482,8 @@ async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ return await asyncio_helper.delete_chat_photo(self.token, chat_id) - async def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, - language_code: Optional[str]=None) -> List[types.BotCommand]: + async def get_my_commands(self, scope: Optional[types.BotCommandScope], + language_code: Optional[str]) -> List[types.BotCommand]: """ Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 08db40f85..2b7020c4e 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -17,3 +17,19 @@ async def pre_process(self, message, data): async def post_process(self, message, data, exception): raise NotImplementedError + +class State: + def __init__(self) -> None: + self.name = None + def __str__(self) -> str: + return self.name + + +class StatesGroup: + def __init_subclass__(cls) -> None: + # print all variables of a subclass + for name, value in cls.__dict__.items(): + if not name.startswith('__') and not callable(value) and isinstance(value, State): + # change value of that variable + value.name = ':'.join((cls.__name__, name)) + diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index df4d37fa7..0b2bed715 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -148,4 +148,20 @@ def get_handlers(self, handler_group_id): self.clear_handlers(handler_group_id) return handlers + +class State: + def __init__(self) -> None: + self.name = None + def __str__(self) -> str: + return self.name + + +class StatesGroup: + def __init_subclass__(cls) -> None: + # print all variables of a subclass + for name, value in cls.__dict__.items(): + if not name.startswith('__') and not callable(value) and isinstance(value, State): + # change value of that variable + value.name = ':'.join((cls.__name__, name)) + \ No newline at end of file From 91d0877c61b207180490a54ddd63307ec2c5280b Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 21:28:10 +0400 Subject: [PATCH 0826/1808] Fix parameter name to fit --- examples/asynchronous_telebot/custom_states.py | 3 ++- telebot/async_telebot.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index 7658a8170..01cc66dbf 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -8,7 +8,8 @@ # new feature for states. from telebot.asyncio_handler_backends import State, StatesGroup -bot = AsyncTeleBot('TOKEN') +# default state storage is statememorystorage +bot = AsyncTeleBot('TOKEN', states_storage=StateMemoryStorage()) # Just create different statesgroup diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a2edafbb8..7bdb0dc17 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -164,7 +164,7 @@ class AsyncTeleBot: """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, - exception_handler=None, states_storage=StateMemoryStorage()) -> None: # TODO: ADD TYPEHINTS + exception_handler=None, state_storage=StateMemoryStorage()) -> None: # TODO: ADD TYPEHINTS self.token = token self.offset = offset @@ -193,7 +193,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, self.custom_filters = {} self.state_handlers = [] - self.current_states = states_storage + self.current_states = state_storage self.middlewares = [] From 588b5c4d896f1a71ac0fcf4f69443eb2fc53682e Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 21:28:56 +0400 Subject: [PATCH 0827/1808] Fix parameter for example --- examples/asynchronous_telebot/custom_states.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index 01cc66dbf..e999d432f 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -9,7 +9,7 @@ from telebot.asyncio_handler_backends import State, StatesGroup # default state storage is statememorystorage -bot = AsyncTeleBot('TOKEN', states_storage=StateMemoryStorage()) +bot = AsyncTeleBot('TOKEN', state_storage=StateMemoryStorage()) # Just create different statesgroup From c350ea0ced3368ba676c11564d6c3e5e926fc53f Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 24 Jan 2022 21:34:50 +0400 Subject: [PATCH 0828/1808] Comment fixes --- examples/asynchronous_telebot/custom_states.py | 16 ++++++++++++---- examples/custom_states.py | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index e999d432f..0dea62f0d 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -20,6 +20,10 @@ class MyStates(StatesGroup): +# set_state -> sets a new state +# delete_state -> delets state if exists +# get_state -> returns state if exists + @bot.message_handler(commands=['start']) async def start_ex(message): @@ -42,7 +46,7 @@ async def any_state(message): @bot.message_handler(state=MyStates.name) async def name_get(message): """ - State 1. Will process when user's state is 1. + State 1. Will process when user's state is MyStates.name. """ await bot.send_message(message.chat.id, f'Now write me a surname') await bot.set_state(message.from_user.id, MyStates.surname, message.chat.id) @@ -53,7 +57,7 @@ async def name_get(message): @bot.message_handler(state=MyStates.surname) async def ask_age(message): """ - State 2. Will process when user's state is 2. + State 2. Will process when user's state is MyStates.surname. """ await bot.send_message(message.chat.id, "What is your age?") await bot.set_state(message.from_user.id, MyStates.age, message.chat.id) @@ -63,6 +67,9 @@ async def ask_age(message): # result @bot.message_handler(state=MyStates.age, is_digit=True) async def ready_for_answer(message): + """ + State 3. Will process when user's state is MyStates.age. + """ async with bot.retrieve_data(message.from_user.id, message.chat.id) as data: await bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") await bot.delete_state(message.from_user.id, message.chat.id) @@ -70,6 +77,9 @@ async def ready_for_answer(message): #incorrect number @bot.message_handler(state=MyStates.age, is_digit=False) async def age_incorrect(message): + """ + Will process for wrong input when state is MyState.age + """ await bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') # register filters @@ -77,8 +87,6 @@ async def age_incorrect(message): bot.add_custom_filter(asyncio_filters.StateFilter(bot)) bot.add_custom_filter(asyncio_filters.IsDigitFilter()) -# set saving states into file. -bot.enable_saving_states() # you can delete this if you do not need to save states import asyncio asyncio.run(bot.polling()) \ No newline at end of file diff --git a/examples/custom_states.py b/examples/custom_states.py index 7901896e7..5997f840e 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -58,7 +58,7 @@ def any_state(message): @bot.message_handler(state=MyStates.name) def name_get(message): """ - State 1. Will process when user's state is 1. + State 1. Will process when user's state is MyStates.name. """ bot.send_message(message.chat.id, f'Now write me a surname') bot.set_state(message.from_user.id, MyStates.surname, message.chat.id) @@ -69,7 +69,7 @@ def name_get(message): @bot.message_handler(state=MyStates.surname) def ask_age(message): """ - State 2. Will process when user's state is 2. + State 2. Will process when user's state is MyStates.surname. """ bot.send_message(message.chat.id, "What is your age?") bot.set_state(message.from_user.id, MyStates.age, message.chat.id) @@ -79,6 +79,9 @@ def ask_age(message): # result @bot.message_handler(state=MyStates.age, is_digit=True) def ready_for_answer(message): + """ + State 3. Will process when user's state is MyStates.age. + """ with bot.retrieve_data(message.from_user.id, message.chat.id) as data: bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") bot.delete_state(message.from_user.id, message.chat.id) @@ -86,6 +89,9 @@ def ready_for_answer(message): #incorrect number @bot.message_handler(state=MyStates.age, is_digit=False) def age_incorrect(message): + """ + Wrong response for MyStates.age + """ bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') # register filters From 4166fb229e67caa8b16378789fb255690ecc5cbc Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 24 Jan 2022 22:38:35 +0300 Subject: [PATCH 0829/1808] Code base cleanup --- .github/workflows/setup_python.yml | 2 +- examples/anonymous_bot.py | 4 +- .../asynchronous_telebot/custom_states.py | 3 +- .../middleware/flooding_middleware.py | 1 - examples/custom_states.py | 2 +- examples/timer_bot.py | 1 - .../webhook_tornado_echo_bot.py | 11 +++- telebot/__init__.py | 63 ++++--------------- telebot/async_telebot.py | 36 ++++++----- telebot/asyncio_handler_backends.py | 6 -- telebot/asyncio_helper.py | 1 + telebot/asyncio_storage/base_storage.py | 3 +- telebot/asyncio_storage/redis_storage.py | 9 --- telebot/storage/base_storage.py | 2 +- telebot/storage/pickle_storage.py | 3 +- telebot/util.py | 2 +- 16 files changed, 52 insertions(+), 97 deletions(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index d2bcdf786..29367291a 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.6', 'pypy-3.7' ] #'pypy-3.8', 'pypy-3.9' NOT SUPPORTED NOW + python-version: [ '3.7','3.8','3.9', 'pypy-3.7', 'pypy-3.8' ] #'pypy-3.9' NOT SUPPORTED NOW name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 diff --git a/examples/anonymous_bot.py b/examples/anonymous_bot.py index db7eaf72b..70c44b90d 100644 --- a/examples/anonymous_bot.py +++ b/examples/anonymous_bot.py @@ -9,7 +9,7 @@ from telebot import types # Initialize bot with your token -bot = telebot.TeleBot(TOKEN) +bot = telebot.TeleBot('TOKEN') # The `users` variable is needed to contain chat ids that are either in the search or in the active dialog, like {chat_id, chat_id} users = {} @@ -47,7 +47,7 @@ def find(message: types.Message): if message.chat.id not in users: bot.send_message(message.chat.id, 'Finding...') - if freeid == None: + if freeid is None: freeid = message.chat.id else: # Question: diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index 0dea62f0d..aefad9809 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -1,9 +1,8 @@ -import telebot from telebot import asyncio_filters from telebot.async_telebot import AsyncTeleBot # list of storages, you can use any storage -from telebot.asyncio_storage import StateRedisStorage,StateMemoryStorage,StatePickleStorage +from telebot.asyncio_storage import StateMemoryStorage # new feature for states. from telebot.asyncio_handler_backends import State, StatesGroup diff --git a/examples/asynchronous_telebot/middleware/flooding_middleware.py b/examples/asynchronous_telebot/middleware/flooding_middleware.py index 4a2d37b08..b8f589ede 100644 --- a/examples/asynchronous_telebot/middleware/flooding_middleware.py +++ b/examples/asynchronous_telebot/middleware/flooding_middleware.py @@ -1,6 +1,5 @@ # Just a little example of middleware handlers -import telebot from telebot.asyncio_handler_backends import BaseMiddleware from telebot.async_telebot import AsyncTeleBot from telebot.async_telebot import CancelUpdate diff --git a/examples/custom_states.py b/examples/custom_states.py index 5997f840e..a18de1f8a 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -4,7 +4,7 @@ from telebot.handler_backends import State, StatesGroup #States # States storage -from telebot.storage import StateRedisStorage, StatePickleStorage, StateMemoryStorage +from telebot.storage import StateMemoryStorage # Beginning from version 4.4.0+, we support storages. diff --git a/examples/timer_bot.py b/examples/timer_bot.py index d82e2d027..9f49b6c01 100644 --- a/examples/timer_bot.py +++ b/examples/timer_bot.py @@ -18,7 +18,6 @@ def send_welcome(message): def beep(chat_id) -> None: """Send the beep message.""" bot.send_message(chat_id, text='Beep!') - return schedule.CancelJob # Run a job once @bot.message_handler(commands=['set']) diff --git a/examples/webhook_examples/webhook_tornado_echo_bot.py b/examples/webhook_examples/webhook_tornado_echo_bot.py index 171d2d74c..ccd48b197 100644 --- a/examples/webhook_examples/webhook_tornado_echo_bot.py +++ b/examples/webhook_examples/webhook_tornado_echo_bot.py @@ -5,6 +5,7 @@ # Documenation to Tornado: http://tornadoweb.org import signal +from typing import Optional, Awaitable import tornado.httpserver import tornado.ioloop @@ -33,12 +34,18 @@ class Root(tornado.web.RequestHandler): + def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: + pass + def get(self): self.write("Hi! This is webhook example!") self.finish() -class webhook_serv(tornado.web.RequestHandler): +class WebhookServ(tornado.web.RequestHandler): + def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: + pass + def get(self): self.write("What are you doing here?") self.finish() @@ -93,7 +100,7 @@ def send_welcome(message): signal.signal(signal.SIGINT, signal_handler) application = tornado.web.Application([ (r"/", Root), - (r"/" + WEBHOOK_SECRET, webhook_serv) + (r"/" + WEBHOOK_SECRET, WebhookServ) ]) http_server = tornado.httpserver.HTTPServer(application, ssl_options={ diff --git a/telebot/__init__.py b/telebot/__init__.py index 9434b59a0..7695a8a21 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -9,7 +9,7 @@ import traceback from typing import Any, Callable, List, Optional, Union -# this imports are used to avoid circular import error +# these imports are used to avoid circular import error import telebot.util import telebot.types @@ -33,13 +33,11 @@ from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter - REPLY_MARKUP_TYPES = Union[ types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply] - """ Module : telebot """ @@ -156,7 +154,6 @@ def __init__( :param parse_mode: default parse_mode :return: Telebot object. """ - self.token = token self.parse_mode = parse_mode self.update_listener = [] @@ -196,7 +193,6 @@ def __init__( self.current_states = state_storage - if apihelper.ENABLE_MIDDLEWARE: self.typed_middleware_handlers = { 'message': [], @@ -251,8 +247,7 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): :param filename: Filename of saving file """ - - self.current_states = StatePickleStorage(filename=filename) + self.current_states = StatePickleStorage(file_path=filename) self.current_states.create_dir() def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): @@ -519,7 +514,6 @@ def process_new_updates(self, updates): self.process_new_chat_member(new_chat_members) if chat_join_request: self.process_new_chat_join_request(chat_join_request) - def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) @@ -590,7 +584,6 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ @@ -778,7 +771,7 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ logger.info('Stopped polling.') def _exec_task(self, task, *args, **kwargs): - if kwargs.get('task_type') == 'handler': + if kwargs and kwargs.get('task_type') == 'handler': pass_bot = kwargs.get('pass_bot') kwargs.pop('pass_bot') kwargs.pop('task_type') @@ -956,7 +949,6 @@ def send_message( """ Use this method to send text messages. - Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. If you must send more than 4000 characters, use the `split_string` or `smart_split` function in util.py. @@ -1419,7 +1411,6 @@ def send_media_group( allow_sending_without_reply, protect_content) return [types.Message.de_json(msg) for msg in result] - # TODO: Rewrite this method like in API. def send_location( self, chat_id: Union[int, str], @@ -1434,8 +1425,6 @@ def send_location( proximity_alert_radius: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: - - """ Use this method to send point on the map. :param chat_id: @@ -1735,7 +1724,6 @@ def set_chat_administrator_custom_title( :return: True on success. """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ @@ -1767,7 +1755,6 @@ def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union """ return apihelper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) - def set_chat_permissions( self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: """ @@ -2262,7 +2249,6 @@ def send_poll( :param protect_content: :return: """ - if isinstance(question, types.Poll): raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") @@ -2445,7 +2431,6 @@ def create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, contains_masks, mask_position) - def add_sticker_to_set( self, user_id: int, name: str, emojis: str, png_sticker: Optional[Union[Any, str]]=None, @@ -2466,7 +2451,6 @@ def add_sticker_to_set( return apihelper.add_sticker_to_set( self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position) - def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. @@ -2542,8 +2526,9 @@ def register_next_step_handler( def set_state(self, user_id: int, state: Union[int, str], chat_id: int=None) -> None: """ Sets a new state of a user. - :param chat_id: + :param user_id: :param state: new state. can be string or integer. + :param chat_id: """ if chat_id is None: chat_id = user_id @@ -2557,10 +2542,12 @@ def reset_data(self, user_id: int, chat_id: int=None): """ if chat_id is None: chat_id = user_id + self.current_states.reset_data(chat_id, user_id) def delete_state(self, user_id: int, chat_id: int=None) -> None: """ Delete the current state of a user. + :param user_id: :param chat_id: :return: """ @@ -2576,6 +2563,7 @@ def retrieve_data(self, user_id: int, chat_id: int=None) -> Optional[Union[int, def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str]]: """ Get current state of a user. + :param user_id: :param chat_id: :return: state of a user """ @@ -2586,6 +2574,7 @@ def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str] def add_data(self, user_id: int, chat_id:int=None, **kwargs): """ Add data to states. + :param user_id: :param chat_id: """ if chat_id is None: @@ -2655,8 +2644,8 @@ def _notify_next_handlers(self, new_messages): need_pop = True self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) if need_pop: - new_messages.pop(i) # removing message that was detected with next_step_handler - + # removing message that was detected with next_step_handler + new_messages.pop(i) @staticmethod def _build_handler_dict(handler, pass_bot=False, **filters): @@ -2697,9 +2686,7 @@ def print_channel_post_text(bot_instance, update): print(update.update_id) :param update_types: Optional list of update types that can be passed into the middleware handler. - """ - def decorator(handler): self.add_middleware_handler(handler, update_types) return handler @@ -2737,10 +2724,9 @@ def register_middleware_handler(self, callback, update_types=None): bot.register_middleware_handler(print_channel_post_text, update_types=['channel_post', 'edited_channel_post']) + :param callback: :param update_types: Optional list of update types that can be passed into the middleware handler. - """ - self.add_middleware_handler(callback, update_types) def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): @@ -2782,7 +2768,6 @@ def default_command(message): :param content_types: Supported message content types. Must be a list. Defaults to ['text']. :param chat_types: list of chat types """ - if content_types is None: content_types = ["text"] @@ -2856,7 +2841,6 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ :param kwargs: :return: """ - if content_types is None: content_types = ["text"] @@ -3065,7 +3049,6 @@ def inline_handler(self, func, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_inline_handler(handler_dict) @@ -3099,7 +3082,6 @@ def chosen_inline_handler(self, func, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_chosen_inline_handler(handler_dict) @@ -3133,7 +3115,6 @@ def callback_query_handler(self, func, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_callback_query_handler(handler_dict) @@ -3167,7 +3148,6 @@ def shipping_query_handler(self, func, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_shipping_query_handler(handler_dict) @@ -3201,7 +3181,6 @@ def pre_checkout_query_handler(self, func, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_pre_checkout_query_handler(handler_dict) @@ -3235,7 +3214,6 @@ def poll_handler(self, func, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_poll_handler(handler_dict) @@ -3269,7 +3247,6 @@ def poll_answer_handler(self, func=None, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_poll_answer_handler(handler_dict) @@ -3303,7 +3280,6 @@ def my_chat_member_handler(self, func=None, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_my_chat_member_handler(handler_dict) @@ -3337,7 +3313,6 @@ def chat_member_handler(self, func=None, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_chat_member_handler(handler_dict) @@ -3371,7 +3346,6 @@ def chat_join_request_handler(self, func=None, **kwargs): :param kwargs: :return: """ - def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_chat_join_request_handler(handler_dict) @@ -3429,14 +3403,6 @@ def _test_filter(self, message_filter, filter_value, message): :param message: Message to test :return: True if filter conforms """ - # test_cases = { - # 'content_types': lambda msg: msg.content_type in filter_value, - # 'regexp': lambda msg: msg.content_type == 'text' and re.search(filter_value, msg.text, re.IGNORECASE), - # 'commands': lambda msg: msg.content_type == 'text' and util.extract_command(msg.text) in filter_value, - # 'func': lambda msg: filter_value(msg) - # } - # return test_cases.get(message_filter, lambda msg: False)(message) - if message_filter == 'content_types': return message.content_type in filter_value elif message_filter == 'regexp': @@ -3478,8 +3444,3 @@ def _notify_command_handlers(self, handlers, new_messages): if self._test_message_handler(message_handler, message): self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') break - - - - - diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7bdb0dc17..0d83c407c 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3,7 +3,6 @@ import logging import re -import sys import time import traceback from typing import Any, List, Optional, Union @@ -22,17 +21,14 @@ from telebot import util, types, asyncio_helper import asyncio -from telebot import asyncio_handler_backends from telebot import asyncio_filters - REPLY_MARKUP_TYPES = Union[ types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply] - """ Module : telebot """ @@ -42,7 +38,6 @@ class Handler: """ Class for (next step|reply) handlers """ - def __init__(self, callback, *args, **kwargs): self.callback = callback self.args = args @@ -310,7 +305,6 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: return except asyncio.CancelledError: return - except asyncio_helper.RequestTimeout as e: logger.error(str(e)) if non_stop: @@ -318,16 +312,12 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: continue else: return - except asyncio_helper.ApiTelegramException as e: logger.error(str(e)) - if non_stop: continue else: break - except KeyboardInterrupt: - return except Exception as e: logger.error('Cause exception while getting updates.') if non_stop: @@ -335,14 +325,12 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: await asyncio.sleep(3) continue else: - raise e - + raise e finally: self._polling = False await self.close_session() logger.warning('Polling is stopped.') - def _loop_create_task(self, coro): return asyncio.create_task(coro) @@ -357,8 +345,7 @@ async def _process_updates(self, handlers, messages, update_type): for message in messages: middleware = await self.process_middlewares(message, update_type) tasks.append(self._run_middlewares_and_handlers(handlers, message, middleware)) - asyncio.gather(*tasks) - + await asyncio.gather(*tasks) async def _run_middlewares_and_handlers(self, handlers, message, middleware): handler_error = None @@ -790,6 +777,7 @@ def add_edited_message_handler(self, handler_dict): def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers edited message handler. + :param pass_bot: :param callback: function to be called :param content_types: list of content_types :param commands: list of commands @@ -861,6 +849,7 @@ def add_channel_post_handler(self, handler_dict): def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers channel post message handler. + :param pass_bot: :param callback: function to be called :param content_types: list of content_types :param commands: list of commands @@ -929,6 +918,7 @@ def add_edited_channel_post_handler(self, handler_dict): def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers edited channel post message handler. + :param pass_bot: :param callback: function to be called :param content_types: list of content_types :param commands: list of commands @@ -979,6 +969,7 @@ def add_inline_handler(self, handler_dict): def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers inline handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1012,6 +1003,7 @@ def add_chosen_inline_handler(self, handler_dict): def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers chosen inline handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1045,6 +1037,7 @@ def add_callback_query_handler(self, handler_dict): def register_callback_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers callback query handler.. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1078,6 +1071,7 @@ def add_shipping_query_handler(self, handler_dict): def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers shipping query handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1111,6 +1105,7 @@ def add_pre_checkout_query_handler(self, handler_dict): def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers pre-checkout request handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1144,6 +1139,7 @@ def add_poll_handler(self, handler_dict): def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1177,6 +1173,7 @@ def add_poll_answer_handler(self, handler_dict): def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll answer handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1210,6 +1207,7 @@ def add_my_chat_member_handler(self, handler_dict): def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers my chat member handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1243,6 +1241,7 @@ def add_chat_member_handler(self, handler_dict): def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat member handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1276,6 +1275,7 @@ def add_chat_join_request_handler(self, handler_dict): def register_chat_join_request_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat join request handler. + :param pass_bot: :param callback: function to be called :param func: :return: decorated function @@ -1403,7 +1403,7 @@ async def remove_webhook(self): """ Alternative for delete_webhook but uses set_webhook """ - self.set_webhook() + await self.set_webhook() async def get_webhook_info(self, timeout=None): """ @@ -3061,6 +3061,7 @@ async def delete_sticker_from_set(self, sticker: str) -> bool: async def set_state(self, user_id: int, state: str, chat_id: int=None): """ Sets a new state of a user. + :param user_id: :param chat_id: :param state: new state. can be string or integer. """ @@ -3081,6 +3082,7 @@ async def reset_data(self, user_id: int, chat_id: int=None): async def delete_state(self, user_id: int, chat_id:int=None): """ Delete the current state of a user. + :param user_id: :param chat_id: :return: """ @@ -3096,6 +3098,7 @@ def retrieve_data(self, user_id: int, chat_id: int=None): async def get_state(self, user_id, chat_id: int=None): """ Get current state of a user. + :param user_id: :param chat_id: :return: state of a user """ @@ -3106,6 +3109,7 @@ async def get_state(self, user_id, chat_id: int=None): async def add_data(self, user_id: int, chat_id: int=None, **kwargs): """ Add data to states. + :param user_id: :param chat_id: """ if not chat_id: diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 2b7020c4e..1784481c4 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -1,8 +1,3 @@ -import os -import pickle - - - class BaseMiddleware: """ Base class for middleware. @@ -32,4 +27,3 @@ def __init_subclass__(cls) -> None: if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable value.name = ':'.join((cls.__name__, name)) - diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index fe4c13e22..f21c20430 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -43,6 +43,7 @@ async def get_session(self): if self.session.closed: self.session = await self.create_session() + # noinspection PyProtectedMember if not self.session._loop.is_running(): await self.session.close() self.session = await self.create_session() diff --git a/telebot/asyncio_storage/base_storage.py b/telebot/asyncio_storage/base_storage.py index 5139e6ce5..38615c4c2 100644 --- a/telebot/asyncio_storage/base_storage.py +++ b/telebot/asyncio_storage/base_storage.py @@ -43,11 +43,10 @@ async def reset_data(self, chat_id, user_id): async def get_state(self, chat_id, user_id): raise NotImplementedError - async def save(chat_id, user_id, data): + async def save(self, chat_id, user_id, data): raise NotImplementedError - class StateContext: """ Class for data. diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index f7ca0fa23..655fb9aec 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,4 +1,3 @@ -from pickle import FALSE from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext import json @@ -93,7 +92,6 @@ async def delete_state(self, chat_id, user_id): return True return False - async def get_value(self, chat_id, user_id, key): """ Get value for a data of a user in a chat. @@ -105,7 +103,6 @@ async def get_value(self, chat_id, user_id, key): if key in response[user_id]['data']: return response[user_id]['data'][key] return None - async def get_state(self, chat_id, user_id): """ @@ -119,7 +116,6 @@ async def get_state(self, chat_id, user_id): return None - async def get_data(self, chat_id, user_id): """ Get data of particular user in a particular chat. @@ -131,7 +127,6 @@ async def get_data(self, chat_id, user_id): return response[user_id]['data'] return None - async def reset_data(self, chat_id, user_id): """ Reset data of a user in a chat. @@ -144,9 +139,6 @@ async def reset_data(self, chat_id, user_id): await self.set_record(chat_id, response) return True - - - async def set_data(self, chat_id, user_id, key, value): """ Set data without interactive data. @@ -175,4 +167,3 @@ async def save(self, chat_id, user_id, data): response[user_id]['data'] = dict(data, **response[user_id]['data']) await self.set_record(chat_id, response) return True - \ No newline at end of file diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py index 2ff2b8c51..bafd9a1a8 100644 --- a/telebot/storage/base_storage.py +++ b/telebot/storage/base_storage.py @@ -42,7 +42,7 @@ def reset_data(self, chat_id, user_id): def get_state(self, chat_id, user_id): raise NotImplementedError - def save(chat_id, user_id, data): + def save(self, chat_id, user_id, data): raise NotImplementedError diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 988d454f7..90f2adb94 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -6,6 +6,7 @@ class StatePickleStorage(StateStorageBase): + # noinspection PyMissingConstructor def __init__(self, file_path="./.state-save/states.pkl") -> None: self.file_path = file_path self.create_dir() @@ -109,4 +110,4 @@ def get_interactive_data(self, chat_id, user_id): def save(self, chat_id, user_id, data): self.data[chat_id][user_id]['data'] = data - self.update_data() \ No newline at end of file + self.update_data() diff --git a/telebot/util.py b/telebot/util.py index 8ec6be68b..a10019da8 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -4,7 +4,6 @@ import string import threading import traceback -import warnings from typing import Any, Callable, List, Dict, Optional, Union # noinspection PyPep8Naming @@ -408,6 +407,7 @@ def changed(): def busy_wait(): while not or_event.is_set(): + # noinspection PyProtectedMember or_event._wait(3) for e in events: From e55938e23a80e027a31dc80e8d3a10627a8b081b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 25 Jan 2022 10:24:45 +0300 Subject: [PATCH 0830/1808] Keep python 3.6 check --- .github/workflows/setup_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 29367291a..111090843 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.7','3.8','3.9', 'pypy-3.7', 'pypy-3.8' ] #'pypy-3.9' NOT SUPPORTED NOW + python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.7', 'pypy-3.8' ] #'pypy-3.9' NOT SUPPORTED NOW name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 From 6aa97d055f15f0ab009639357440007c2523f6c3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 25 Jan 2022 10:25:53 +0300 Subject: [PATCH 0831/1808] Bump version to 4.4.0 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 531afbd9f..0a15dc1a4 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.3.1' +__version__ = '4.4.0' From 6fc7beba5733e2b4eaf57aa3182df8e2901ced8d Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 25 Jan 2022 15:18:12 +0400 Subject: [PATCH 0832/1808] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c6b2d58a..f0612306e 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ * [The Telegram Chat Group](#the-telegram-chat-group) * [Telegram Channel](#telegram-channel) * [More examples](#more-examples) + * [Code Template](#code-template) * [Bots using this API](#bots-using-this-api) ## Getting started @@ -749,7 +750,7 @@ Asynchronous tasks depend on processor performance. Many asynchronous tasks can AsyncTeleBot has different middlewares. See example on [middlewares](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot/middleware) ### Examples -See more examples in our [examples](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder +See more examples in our [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder ## F.A.Q. @@ -794,6 +795,10 @@ Join the [News channel](https://t.me/pyTelegramBotAPI). Here we will post releas * [Deep Linking](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py) * [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py) +## Code Template +* [AsyncTeleBot template](https://github.com/coder2020official/asynctelebot_template) +* [TeleBot template](https://github.com/coder2020official/telebot_template) + ## Bots using this API * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* From 23d20e0753e83d7b1a1a7769cb209a4de707732e Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 25 Jan 2022 15:21:09 +0400 Subject: [PATCH 0833/1808] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f0612306e..20ff4a216 100644 --- a/README.md +++ b/README.md @@ -796,9 +796,13 @@ Join the [News channel](https://t.me/pyTelegramBotAPI). Here we will post releas * [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py) ## Code Template +Template is a ready folder that contains architecture of basic project. +Here are some examples of template: + * [AsyncTeleBot template](https://github.com/coder2020official/asynctelebot_template) * [TeleBot template](https://github.com/coder2020official/telebot_template) + ## Bots using this API * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* From 69277400b76b2cf67c581657f5af1a7c1b4b8a4b Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo Date: Sun, 30 Jan 2022 17:53:55 +0100 Subject: [PATCH 0834/1808] The output of get_me() is already an User object --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7695a8a21..c1948a36e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -223,7 +223,7 @@ def user(self) -> types.User: Equivalent to bot.get_me() but the result is cached so only one API call is needed """ if not hasattr(self, "_user"): - self._user = types.User.de_json(self.get_me()) + self._user = self.get_me() return self._user def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): From 5ee2aa77c6aefd312fa68ee346746a1f23e2cf49 Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo Date: Mon, 31 Jan 2022 22:53:31 +0100 Subject: [PATCH 0835/1808] storage and asyncio_storage were not installed with previews setup function --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 7b6277638..1b87cb120 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from setuptools import setup +from setuptools import setup, find_packages from io import open import re @@ -19,7 +19,7 @@ def read(filename): author='eternnoir', author_email='eternnoir@gmail.com', url='https://github.com/eternnoir/pyTelegramBotAPI', - packages=['telebot'], + packages = find_packages(), license='GPL2', keywords='telegram bot api tools', install_requires=['requests'], @@ -33,5 +33,6 @@ def read(filename): 'Programming Language :: Python :: 3', 'Environment :: Console', 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', - ] + ], + ) From d7cb819502fd4cf7df2b113fdee22ce697b5b902 Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo Date: Mon, 31 Jan 2022 22:58:52 +0100 Subject: [PATCH 0836/1808] Excluding tests and examples from packages to install --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1b87cb120..0ee43843f 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ def read(filename): author='eternnoir', author_email='eternnoir@gmail.com', url='https://github.com/eternnoir/pyTelegramBotAPI', - packages = find_packages(), + packages = find_packages(exclude = ['tests', 'examples']), license='GPL2', keywords='telegram bot api tools', install_requires=['requests'], From 7ba021871a0b94dfb13b317304810c9b00fdb248 Mon Sep 17 00:00:00 2001 From: Andrea Barbagallo Date: Mon, 31 Jan 2022 23:09:18 +0100 Subject: [PATCH 0837/1808] Adding new way to install library --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 20ff4a216..cc6292b4f 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,10 @@ $ git clone https://github.com/eternnoir/pyTelegramBotAPI.git $ cd pyTelegramBotAPI $ python setup.py install ``` +or: +``` +$ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git +``` It is generally recommended to use the first option. From 4e7652be7ac89fab297b9389e0920e8a3ee96446 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 1 Feb 2022 14:47:42 +0400 Subject: [PATCH 0838/1808] Bot API 5.7 --- telebot/__init__.py | 12 ++++++++---- telebot/apihelper.py | 8 ++++++-- telebot/async_telebot.py | 12 ++++++++---- telebot/asyncio_helper.py | 9 +++++++-- telebot/types.py | 6 ++++-- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c1948a36e..2bc716e92 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2409,8 +2409,9 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> typ def create_new_sticker_set( self, user_id: int, name: str, title: str, emojis: str, - png_sticker: Union[Any, str], - tgs_sticker: Union[Any, str], + png_sticker: Union[Any, str]=None, + tgs_sticker: Union[Any, str]=None, + webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, mask_position: Optional[types.MaskPosition]=None) -> bool: """ @@ -2423,18 +2424,20 @@ def create_new_sticker_set( :param emojis: :param png_sticker: :param tgs_sticker: + :param webm_sticker: :param contains_masks: :param mask_position: :return: """ return apihelper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks, mask_position) + contains_masks, mask_position, webm_sticker) def add_sticker_to_set( self, user_id: int, name: str, emojis: str, png_sticker: Optional[Union[Any, str]]=None, tgs_sticker: Optional[Union[Any, str]]=None, + webm_sticker: Optional[Union[Any, str]]=None, mask_position: Optional[types.MaskPosition]=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. @@ -2445,11 +2448,12 @@ def add_sticker_to_set( :param emojis: :param png_sticker: Required if `tgs_sticker` is None :param tgs_sticker: Required if `png_sticker` is None + :webm_sticker: :param mask_position: :return: """ return apihelper.add_sticker_to_set( - self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position) + self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker) def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 870c60915..0e76a676c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1533,7 +1533,7 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks=None, mask_position=None): + contains_masks=None, mask_position=None, webm_sticker=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} stype = 'png_sticker' if png_sticker else 'tgs_sticker' @@ -1547,10 +1547,12 @@ def create_new_sticker_set( payload['contains_masks'] = contains_masks if mask_position: payload['mask_position'] = mask_position.to_json() + if webm_sticker: + payload['webm_sticker'] = webm_sticker return _make_request(token, method_url, params=payload, files=files, method='post') -def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position): +def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} stype = 'png_sticker' if png_sticker else 'tgs_sticker' @@ -1562,6 +1564,8 @@ def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, m payload[stype] = sticker if mask_position: payload['mask_position'] = mask_position.to_json() + if webm_sticker: + payload['webm_sticker'] = webm_sticker return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0d83c407c..be9e08d4c 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2996,8 +2996,9 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) async def create_new_sticker_set( self, user_id: int, name: str, title: str, emojis: str, - png_sticker: Union[Any, str], - tgs_sticker: Union[Any, str], + png_sticker: Union[Any, str]=None, + tgs_sticker: Union[Any, str]=None, + webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, mask_position: Optional[types.MaskPosition]=None) -> bool: """ @@ -3010,19 +3011,21 @@ async def create_new_sticker_set( :param emojis: :param png_sticker: :param tgs_sticker: + :webm_sticker: :param contains_masks: :param mask_position: :return: """ return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks, mask_position) + contains_masks, mask_position, webm_sticker) async def add_sticker_to_set( self, user_id: int, name: str, emojis: str, png_sticker: Optional[Union[Any, str]]=None, tgs_sticker: Optional[Union[Any, str]]=None, + webm_sticker: Optional[Union[Any, str]]=None, mask_position: Optional[types.MaskPosition]=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. @@ -3033,11 +3036,12 @@ async def add_sticker_to_set( :param emojis: :param png_sticker: Required if `tgs_sticker` is None :param tgs_sticker: Required if `png_sticker` is None + :webm_sticker: :param mask_position: :return: """ return await asyncio_helper.add_sticker_to_set( - self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position) + self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker) async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index f21c20430..a5fdb5adc 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1497,7 +1497,7 @@ async def upload_sticker_file(token, user_id, png_sticker): async def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks=None, mask_position=None): + contains_masks=None, mask_position=None, webm_sticker=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} stype = 'png_sticker' if png_sticker else 'tgs_sticker' @@ -1511,10 +1511,12 @@ async def create_new_sticker_set( payload['contains_masks'] = contains_masks if mask_position: payload['mask_position'] = mask_position.to_json() + if webm_sticker: + payload['webm_sticker'] = webm_sticker return await _process_request(token, method_url, params=payload, files=files, method='post') -async def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position): +async def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} stype = 'png_sticker' if png_sticker else 'tgs_sticker' @@ -1526,6 +1528,9 @@ async def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_stic payload[stype] = sticker if mask_position: payload['mask_position'] = mask_position.to_json() + + if webm_sticker: + payload['webm_sticker'] = webm_sticker return await _process_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/types.py b/telebot/types.py index a200b7313..1cce8172d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2484,10 +2484,11 @@ def de_json(cls, json_string): obj['thumb'] = None return cls(**obj) - def __init__(self, name, title, is_animated, contains_masks, stickers, thumb=None, **kwargs): + def __init__(self, name, title, is_animated, is_video, contains_masks, stickers, thumb=None, **kwargs): self.name: str = name self.title: str = title self.is_animated: bool = is_animated + self.is_video: bool = is_video self.contains_masks: bool = contains_masks self.stickers: List[Sticker] = stickers self.thumb: PhotoSize = thumb @@ -2507,12 +2508,13 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, file_id, file_unique_id, width, height, is_animated, - thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): + is_video=None, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.width: int = width self.height: int = height self.is_animated: bool = is_animated + self.is_video: bool = is_video self.thumb: PhotoSize = thumb self.emoji: str = emoji self.set_name: str = set_name From 1e0c2ea633252c2c8acd5cc6f8a067317b848421 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 1 Feb 2022 14:50:36 +0400 Subject: [PATCH 0839/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc6292b4f..c7a493c8a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Supports both sync and async ways.

-##

Supporting Bot API version: 5.6! +##

Supporting Bot API version: 5.7! ## Contents From 3b38d1b46efcb86832ed14706a5513172d0a80d7 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 1 Feb 2022 14:51:36 +0400 Subject: [PATCH 0840/1808] Bot API 5.7 in readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c7a493c8a..6f1a4b5da 100644 --- a/README.md +++ b/README.md @@ -690,7 +690,8 @@ Result will be: ## API conformance - + +* ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022) * ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021) * ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021) * ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021) From 71be20636a5309ce4801b20be47c256fa60a9042 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 1 Feb 2022 17:43:49 +0400 Subject: [PATCH 0841/1808] Bot API 5.7 --- telebot/apihelper.py | 22 ++++++++++++++++------ telebot/asyncio_helper.py | 21 +++++++++++++++++---- telebot/types.py | 2 +- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0e76a676c..67a8e7c00 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1536,8 +1536,14 @@ def create_new_sticker_set( contains_masks=None, mask_position=None, webm_sticker=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} - stype = 'png_sticker' if png_sticker else 'tgs_sticker' - sticker = png_sticker or tgs_sticker + stype = None + if png_sticker: + stype = 'png_sticker' + elif webm_sticker: + stype = 'webm_sticker' + else: + stype = 'tgs_sticker' + sticker = png_sticker or tgs_sticker or webm_sticker files = None if not util.is_string(sticker): files = {stype: sticker} @@ -1555,8 +1561,14 @@ def create_new_sticker_set( def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} - stype = 'png_sticker' if png_sticker else 'tgs_sticker' - sticker = png_sticker or tgs_sticker + stype = None + if png_sticker: + stype = 'png_sticker' + elif webm_sticker: + stype = 'webm_sticker' + else: + stype = 'tgs_sticker' + sticker = png_sticker or tgs_sticker or webm_sticker files = None if not util.is_string(sticker): files = {stype: sticker} @@ -1564,8 +1576,6 @@ def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, m payload[stype] = sticker if mask_position: payload['mask_position'] = mask_position.to_json() - if webm_sticker: - payload['webm_sticker'] = webm_sticker return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index a5fdb5adc..591410cfd 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1500,8 +1500,14 @@ async def create_new_sticker_set( contains_masks=None, mask_position=None, webm_sticker=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} - stype = 'png_sticker' if png_sticker else 'tgs_sticker' - sticker = png_sticker or tgs_sticker + stype = None + if png_sticker: + stype = 'png_sticker' + elif webm_sticker: + stype = 'webm_sticker' + else: + stype = 'tgs_sticker' + sticker = png_sticker or tgs_sticker or webm_sticker files = None if not util.is_string(sticker): files = {stype: sticker} @@ -1519,9 +1525,16 @@ async def create_new_sticker_set( async def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} - stype = 'png_sticker' if png_sticker else 'tgs_sticker' - sticker = png_sticker or tgs_sticker + stype = None + if png_sticker: + stype = 'png_sticker' + elif webm_sticker: + stype = 'webm_sticker' + else: + stype = 'tgs_sticker' files = None + sticker = png_sticker or tgs_sticker or webm_sticker + if not util.is_string(sticker): files = {stype: sticker} else: diff --git a/telebot/types.py b/telebot/types.py index 1cce8172d..15cd2d144 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2508,7 +2508,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, file_id, file_unique_id, width, height, is_animated, - is_video=None, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): + is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.width: int = width From 62fad9ca3a629e259747a6a667e4b0fcce6909c6 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 1 Feb 2022 18:16:53 +0400 Subject: [PATCH 0842/1808] Fix tests --- tests/test_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index 4a580ebb3..43200a8eb 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -80,7 +80,7 @@ def test_json_Message_Audio(): def test_json_Message_Sticker(): - json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 60, "height": 60}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 60, "height": 60}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb.height == 60 @@ -88,7 +88,7 @@ def test_json_Message_Sticker(): def test_json_Message_Sticker_without_thumb(): - json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb is None From ce56a035b5c8aabdbdf5116130f91abf7907e9e3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 1 Feb 2022 23:58:57 +0300 Subject: [PATCH 0843/1808] Extend custom exception_handler behaviour --- telebot/__init__.py | 12 ++++++++++-- telebot/util.py | 12 +++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2bc716e92..1eb496fc8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -214,7 +214,7 @@ def __init__( self.threaded = threaded if self.threaded: - self.worker_pool = util.ThreadPool(num_threads=num_threads) + self.worker_pool = util.ThreadPool(self, num_threads=num_threads) @property def user(self) -> types.User: @@ -781,7 +781,15 @@ def _exec_task(self, task, *args, **kwargs): if self.threaded: self.worker_pool.put(task, *args, **kwargs) else: - task(*args, **kwargs) + try: + task(*args, **kwargs) + except Exception as e: + if self.exception_handler is not None: + handled = self.exception_handler.handle(e) + else: + handled = False + if not handled: + raise e def stop_polling(self): self.__stop_polling.set() diff --git a/telebot/util.py b/telebot/util.py index a10019da8..6c09ca390 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -114,7 +114,8 @@ def stop(self): class ThreadPool: - def __init__(self, num_threads=2): + def __init__(self, telebot, num_threads=2): + self.telebot = telebot self.tasks = Queue.Queue() self.workers = [WorkerThread(self.on_exception, self.tasks) for _ in range(num_threads)] self.num_threads = num_threads @@ -126,8 +127,13 @@ def put(self, func, *args, **kwargs): self.tasks.put((func, args, kwargs)) def on_exception(self, worker_thread, exc_info): - self.exception_info = exc_info - self.exception_event.set() + if self.telebot.exception_handler is not None: + handled = self.telebot.exception_handler.handle(exc_info) + else: + handled = False + if not handled: + self.exception_info = exc_info + self.exception_event.set() worker_thread.continue_event.set() def raise_exceptions(self): From cd4a9add689b3a0c838af94db6c9d15af7a77db9 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 2 Feb 2022 14:44:02 +0400 Subject: [PATCH 0844/1808] Fix States --- telebot/asyncio_filters.py | 7 +++++++ telebot/asyncio_storage/memory_storage.py | 2 ++ telebot/asyncio_storage/pickle_storage.py | 2 ++ telebot/asyncio_storage/redis_storage.py | 2 ++ telebot/custom_filters.py | 7 ++++++- telebot/handler_backends.py | 1 + telebot/storage/memory_storage.py | 3 +++ telebot/storage/pickle_storage.py | 2 ++ telebot/storage/redis_storage.py | 4 ++++ 9 files changed, 29 insertions(+), 1 deletion(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 5b193fdf0..417b110c8 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -160,6 +160,13 @@ def __init__(self, bot): async def check(self, message, text): if text == '*': return True + + if isinstance(text, list): + new_text = [i.name for i in text] + text = new_text + elif isinstance(text, object): + text = text.name + if message.chat.type == 'group': group_state = await self.bot.current_states.get_state(message.chat.id, message.from_user.id) if group_state == text: diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py index ab9c48642..58a6e354e 100644 --- a/telebot/asyncio_storage/memory_storage.py +++ b/telebot/asyncio_storage/memory_storage.py @@ -8,6 +8,8 @@ def __init__(self) -> None: async def set_state(self, chat_id, user_id, state): + if isinstance(state, object): + state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: self.data[chat_id][user_id]['state'] = state diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index 81ef46cd4..dd6419e7e 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -47,6 +47,8 @@ def update_data(self): file.close() async def set_state(self, chat_id, user_id, state): + if isinstance(state, object): + state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: self.data[chat_id][user_id]['state'] = state diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 655fb9aec..f2a26065f 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -65,6 +65,8 @@ async def set_state(self, chat_id, user_id, state): """ response = await self.get_record(chat_id) user_id = str(user_id) + if isinstance(state, object): + state = state.name if response: if user_id in response: response[user_id]['state'] = state diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 147596ca7..d95ecd310 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -1,5 +1,4 @@ from abc import ABC - class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -159,6 +158,12 @@ def __init__(self, bot): def check(self, message, text): if text == '*': return True + + if isinstance(text, list): + new_text = [i.name for i in text] + text = new_text + elif isinstance(text, object): + text = text.name if message.chat.type == 'group': group_state = self.bot.current_states.get_state(message.chat.id, message.from_user.id) if group_state == text: diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 0b2bed715..d88457b3a 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -154,6 +154,7 @@ def __init__(self) -> None: self.name = None def __str__(self) -> str: return self.name + class StatesGroup: diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 3540ac5d5..45d4da3cc 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -8,6 +8,8 @@ def __init__(self) -> None: def set_state(self, chat_id, user_id, state): + if isinstance(state, object): + state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: self.data[chat_id][user_id]['state'] = state @@ -31,6 +33,7 @@ def delete_state(self, chat_id, user_id): def get_state(self, chat_id, user_id): + if self.data.get(chat_id): if self.data[chat_id].get(user_id): return self.data[chat_id][user_id]['state'] diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 90f2adb94..a2736903f 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -53,6 +53,8 @@ def update_data(self): file.close() def set_state(self, chat_id, user_id, state): + if isinstance(state, object): + state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: self.data[chat_id][user_id]['state'] = state diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index a8ba2c502..ff21b6e13 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -1,3 +1,4 @@ +from pyclbr import Class from telebot.storage.base_storage import StateStorageBase, StateContext import json @@ -64,6 +65,9 @@ def set_state(self, chat_id, user_id, state): """ response = self.get_record(chat_id) user_id = str(user_id) + if isinstance(state, object): + state = state.name + if response: if user_id in response: response[user_id]['state'] = state From 4028b44d0779e7ea3231539e6804783aa25d91db Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 6 Feb 2022 21:02:14 +0300 Subject: [PATCH 0845/1808] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f1a4b5da..9b004c42d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ This API is tested with Python 3.6-3.10 and Pypy 3. There are two ways to install the library: -* Installation using pip (a Python package manager)*: +* Installation using pip (a Python package manager): ``` $ pip install pyTelegramBotAPI @@ -87,7 +87,10 @@ $ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git It is generally recommended to use the first option. -**While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling `pip install pytelegrambotapi --upgrade`* +*While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling* +``` +pip install pytelegrambotapi --upgrade +``` ## Writing your first bot From 97dbedaa549d0275333d468eebdb979cdefac2c5 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 7 Feb 2022 00:57:33 +0300 Subject: [PATCH 0846/1808] Fix > --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 6c09ca390..24f6b5578 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -320,7 +320,7 @@ def escape(text: str) -> str: :param text: the text to escape :return: the escaped text """ - chars = {"&": "&", "<": "<", ">": ">"} + chars = {"&": "&", "<": "<", ">": ">"} for old, new in chars.items(): text = text.replace(old, new) return text From 0655a1f6b69e152c9b8d5d8417612e5161b70a07 Mon Sep 17 00:00:00 2001 From: Kamil Date: Wed, 9 Feb 2022 00:27:18 +0000 Subject: [PATCH 0847/1808] Update README.md added my pyfram-telegram-bot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9b004c42d..ea982e882 100644 --- a/README.md +++ b/README.md @@ -860,5 +860,6 @@ Here are some examples of template: * [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development. * [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search. * [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. +* [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From b9436821e0434b6e817aab0c0ef2094d7621ca8e Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Thu, 10 Feb 2022 13:33:44 +0500 Subject: [PATCH 0848/1808] callbackdata examples were separated into a directory --- .../simple_products_example.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{CallbackData_example.py => callback_data_examples/simple_products_example.py} (100%) diff --git a/examples/CallbackData_example.py b/examples/callback_data_examples/simple_products_example.py similarity index 100% rename from examples/CallbackData_example.py rename to examples/callback_data_examples/simple_products_example.py From 0c8e94d2c6fd1e759daddab11436185be9490826 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Thu, 10 Feb 2022 13:43:19 +0500 Subject: [PATCH 0849/1808] advanced usage of callbackdata was added --- .../advanced_calendar_example/filters.py | 25 +++++ .../advanced_calendar_example/keyboards.py | 92 +++++++++++++++++++ .../advanced_calendar_example/main.py | 56 +++++++++++ 3 files changed, 173 insertions(+) create mode 100644 examples/callback_data_examples/advanced_calendar_example/filters.py create mode 100644 examples/callback_data_examples/advanced_calendar_example/keyboards.py create mode 100644 examples/callback_data_examples/advanced_calendar_example/main.py diff --git a/examples/callback_data_examples/advanced_calendar_example/filters.py b/examples/callback_data_examples/advanced_calendar_example/filters.py new file mode 100644 index 000000000..e48f406a7 --- /dev/null +++ b/examples/callback_data_examples/advanced_calendar_example/filters.py @@ -0,0 +1,25 @@ +import telebot +from telebot import types, AdvancedCustomFilter +from telebot.callback_data import CallbackData, CallbackDataFilter + +calendar_factory = CallbackData("year", "month", prefix="calendar") +calendar_zoom = CallbackData("year", prefix="calendar_zoom") + + +class CalendarCallbackFilter(AdvancedCustomFilter): + key = 'calendar_config' + + def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +class CalendarZoomCallbackFilter(AdvancedCustomFilter): + key = 'calendar_zoom_config' + + def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +def bind_filters(bot: telebot.TeleBot): + bot.add_custom_filter(CalendarCallbackFilter()) + bot.add_custom_filter(CalendarZoomCallbackFilter()) diff --git a/examples/callback_data_examples/advanced_calendar_example/keyboards.py b/examples/callback_data_examples/advanced_calendar_example/keyboards.py new file mode 100644 index 000000000..46ffde157 --- /dev/null +++ b/examples/callback_data_examples/advanced_calendar_example/keyboards.py @@ -0,0 +1,92 @@ +import calendar +from datetime import date, timedelta + +from examples.callback_data_examples.advanced_calendar_example.filters import calendar_factory, calendar_zoom +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton + +EMTPY_FIELD = '1' +WEEK_DAYS = [calendar.day_abbr[i] for i in range(7)] +MONTHS = [(i, calendar.month_name[i]) for i in range(1, 13)] + + +def generate_calendar_days(year: int, month: int): + keyboard = InlineKeyboardMarkup(row_width=7) + today = date.today() + + keyboard.add( + InlineKeyboardButton( + text=date(year=year, month=month, day=1).strftime('%b %Y'), + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*[ + InlineKeyboardButton( + text=day, + callback_data=EMTPY_FIELD + ) + for day in WEEK_DAYS + ]) + + for week in calendar.Calendar().monthdayscalendar(year=year, month=month): + week_buttons = [] + for day in week: + day_name = ' ' + if day == today.day and today.year == year and today.month == month: + day_name = '🔘' + elif day != 0: + day_name = str(day) + week_buttons.append( + InlineKeyboardButton( + text=day_name, + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*week_buttons) + + previous_date = date(year=year, month=month, day=1) - timedelta(days=1) + next_date = date(year=year, month=month, day=1) + timedelta(days=31) + + keyboard.add( + InlineKeyboardButton( + text='Previous month', + callback_data=calendar_factory.new(year=previous_date.year, month=previous_date.month) + ), + InlineKeyboardButton( + text='Zoom out', + callback_data=calendar_zoom.new(year=year) + ), + InlineKeyboardButton( + text='Next month', + callback_data=calendar_factory.new(year=next_date.year, month=next_date.month) + ), + ) + + return keyboard + + +def generate_calendar_months(year: int): + keyboard = InlineKeyboardMarkup(row_width=3) + keyboard.add( + InlineKeyboardButton( + text=date(year=year, month=1, day=1).strftime('Year %Y'), + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*[ + InlineKeyboardButton( + text=month, + callback_data=calendar_factory.new(year=year, month=month_number) + ) + for month_number, month in MONTHS + ]) + keyboard.add( + InlineKeyboardButton( + text='Previous year', + callback_data=calendar_zoom.new(year=year - 1) + ), + InlineKeyboardButton( + text='Next year', + callback_data=calendar_zoom.new(year=year + 1) + ) + ) + return keyboard diff --git a/examples/callback_data_examples/advanced_calendar_example/main.py b/examples/callback_data_examples/advanced_calendar_example/main.py new file mode 100644 index 000000000..8ccf4698d --- /dev/null +++ b/examples/callback_data_examples/advanced_calendar_example/main.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you an advanced usage of CallbackData. +In this example calendar was implemented +""" + +from datetime import date + +from examples.callback_data_examples.advanced_calendar_example.keyboards import generate_calendar_days, \ + generate_calendar_months, EMTPY_FIELD +from filters import calendar_factory, calendar_zoom, bind_filters +from telebot import types, TeleBot + +API_TOKEN = '' +bot = TeleBot(API_TOKEN) + + +@bot.message_handler(commands='start') +def start_command_handler(message: types.Message): + bot.send_message(message.chat.id, + f"Hello {message.from_user.first_name}. This bot is an example of calendar keyboard." + "\nPress /calendar to see it.") + + +@bot.message_handler(commands='calendar') +def calendar_command_handler(message: types.Message): + now = date.today() + bot.send_message(message.chat.id, 'Calendar', reply_markup=generate_calendar_days(year=now.year, month=now.month)) + + +@bot.callback_query_handler(func=None, calendar_config=calendar_factory.filter()) +def calendar_action_handler(call: types.CallbackQuery): + callback_data: dict = calendar_factory.parse(callback_data=call.data) + year, month = int(callback_data['year']), int(callback_data['month']) + + bot.edit_message_reply_markup(call.message.chat.id, call.message.id, + reply_markup=generate_calendar_days(year=year, month=month)) + + +@bot.callback_query_handler(func=None, calendar_zoom_config=calendar_zoom.filter()) +def calendar_zoom_out_handler(call: types.CallbackQuery): + callback_data: dict = calendar_zoom.parse(callback_data=call.data) + year = int(callback_data.get('year')) + + bot.edit_message_reply_markup(call.message.chat.id, call.message.id, + reply_markup=generate_calendar_months(year=year)) + + +@bot.callback_query_handler(func=lambda call: call.data == EMTPY_FIELD) +def callback_empty_field_handler(call: types.CallbackQuery): + bot.answer_callback_query(call.id) + + +if __name__ == '__main__': + bind_filters(bot) + bot.infinity_polling() From 733bb2ebbb3ae14f219edc5def2b6dcc9de21ef2 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 13:35:52 +0500 Subject: [PATCH 0850/1808] new advanced TextFilter was added && An example demostrating TextFilter usage --- .../custom_filters/advanced_text_filter.py | 100 +++++++++++++++ telebot/custom_filters.py | 117 ++++++++++++++++-- 2 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 examples/custom_filters/advanced_text_filter.py diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py new file mode 100644 index 000000000..5f926b016 --- /dev/null +++ b/examples/custom_filters/advanced_text_filter.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you usage of TextFilter +In this example you will see how to use TextFilter +with (message_handler, callback_query_handler, poll_handler) +""" + +from telebot import TeleBot, types +from telebot.custom_filters import TextFilter, AdvancedCustomFilter + +bot = TeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") + + +class TextFilterKey(AdvancedCustomFilter): + key = 'text' + + def check(self, message, config: TextFilter): + return config.check(message) + + +@bot.message_handler(text=TextFilter(equals='hello')) +def hello_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True)) +def hello_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'])) +def contains_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True)) +def contains_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE +def starts_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE +def starts_with_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay +def ends_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY +def ends_with_handler_ignore_case(message: types.Message): + bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(equals='/callback')) +def send_callback(message: types.Message): + keyboard = types.InlineKeyboardMarkup( + keyboard=[ + [types.InlineKeyboardButton(text='callback data', callback_data='example')], + [types.InlineKeyboardButton(text='ignore case callback data', callback_data='ExAmPLe')] + ] + ) + bot.send_message(message.chat.id, message.text, reply_markup=keyboard) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example')) +def callback_query_handler(call: types.CallbackQuery): + bot.answer_callback_query(call.id, call.data, show_alert=True) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True)) +def callback_query_handler_ignore_case(call: types.CallbackQuery): + bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True) + + +@bot.message_handler(text=TextFilter(equals='/poll')) +def send_poll(message: types.Message): + bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night']) + bot.send_poll(message.chat.id, question='WHEN DO you pRefeR to worK?', options=['Morning', 'Night']) + + +@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?')) +def poll_question_handler(poll: types.Poll): + print(poll.question) + + +@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?', ignore_case=True)) +def poll_question_handler_ignore_case(poll: types.Poll): + print(poll.question + ' ignore case') + + +if __name__ == '__main__': + bot.add_custom_filter(TextFilterKey()) + bot.infinity_polling() diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index d95ecd310..efbd2185f 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -1,4 +1,9 @@ from abc import ABC +from typing import Optional, Union + +from telebot import types + + class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -29,6 +34,84 @@ def check(self, message, text): pass +class TextFilter: + """ + Advanced text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) + + example of usage is in examples/custom_filters/advanced_text_filter.py + """ + + def __init__(self, + equals: Optional[str] = None, + contains: Optional[Union[list, tuple]] = None, + starts_with: Optional[str] = None, + ends_with: Optional[str] = None, + ignore_case: bool = False): + + """ + :param equals: string, True if object's text is equal to passed string + :param contains: list[str] or tuple[str], True if object's text is in list or tuple + :param starts_with: string, True if object's text starts with passed string + :param ends_with: string, True if object's text starts with passed string + :param ignore_case: bool (default False), case insensitive + """ + + to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with))) + if to_check == 0: + raise ValueError('None of the check modes was specified') + elif to_check > 1: + raise ValueError('Only one check mode can be specified') + elif contains: + for i in contains: + if not isinstance(i, str): + raise ValueError(f"Invalid value '{i}' is in contains") + elif starts_with and not isinstance(starts_with, str): + raise ValueError("starts_with has to be a string") + elif ends_with and not isinstance(ends_with, str): + raise ValueError("ends_with has to be a string") + + self.equals = equals + self.contains = contains + self.starts_with = starts_with + self.ends_with = ends_with + self.ignore_case = ignore_case + + def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): + + if isinstance(obj, types.Poll): + text = obj.question + elif isinstance(obj, types.Message): + text = obj.text or obj.caption + elif isinstance(obj, types.CallbackQuery): + text = obj.data + elif isinstance(obj, types.InlineQuery): + text = obj.query + else: + return False + + if self.equals: + if self.ignore_case: + return self.equals.lower() == text.lower() + return self.equals == text + + if self.contains: + if self.ignore_case: + return text.lower() in (i.lower() for i in self.contains) + return text in self.contains + + if self.starts_with: + if self.ignore_case: + return text.lower().startswith(self.starts_with) + return text.startswith(self.starts_with) + + if self.ends_with: + if self.ignore_case: + return text.lower().endswith(self.ends_with) + return text.endswith(self.ends_with) + + return False + + class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. @@ -41,8 +124,11 @@ class TextMatchFilter(AdvancedCustomFilter): key = 'text' def check(self, message, text): - if type(text) is list:return message.text in text - else: return text == message.text + if type(text) is list: + return message.text in text + else: + return text == message.text + class TextContainsFilter(AdvancedCustomFilter): """ @@ -59,6 +145,7 @@ class TextContainsFilter(AdvancedCustomFilter): def check(self, message, text): return text in message.text + class TextStartsFilter(AdvancedCustomFilter): """ Filter to check whether message starts with some text. @@ -69,8 +156,10 @@ class TextStartsFilter(AdvancedCustomFilter): """ key = 'text_startswith' + def check(self, message, text): - return message.text.startswith(text) + return message.text.startswith(text) + class ChatFilter(AdvancedCustomFilter): """ @@ -81,9 +170,11 @@ class ChatFilter(AdvancedCustomFilter): """ key = 'chat_id' + def check(self, message, text): return message.chat.id in text + class ForwardFilter(SimpleCustomFilter): """ Check whether message was forwarded from channel or group. @@ -98,6 +189,7 @@ class ForwardFilter(SimpleCustomFilter): def check(self, message): return message.forward_from_chat is not None + class IsReplyFilter(SimpleCustomFilter): """ Check whether message is a reply. @@ -113,7 +205,6 @@ def check(self, message): return message.reply_to_message is not None - class LanguageFilter(AdvancedCustomFilter): """ Check users language_code. @@ -126,8 +217,11 @@ class LanguageFilter(AdvancedCustomFilter): key = 'language_code' def check(self, message, text): - if type(text) is list:return message.from_user.language_code in text - else: return message.from_user.language_code == text + if type(text) is list: + return message.from_user.language_code in text + else: + return message.from_user.language_code == text + class IsAdminFilter(SimpleCustomFilter): """ @@ -145,6 +239,7 @@ def __init__(self, bot): def check(self, message): return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] + class StateFilter(AdvancedCustomFilter): """ Filter to check state. @@ -152,8 +247,10 @@ class StateFilter(AdvancedCustomFilter): Example: @bot.message_handler(state=1) """ + def __init__(self, bot): self.bot = bot + key = 'state' def check(self, message, text): @@ -170,14 +267,16 @@ def check(self, message, text): return True elif group_state in text and type(text) is list: return True - - + + else: - user_state = self.bot.current_states.get_state(message.chat.id,message.from_user.id) + user_state = self.bot.current_states.get_state(message.chat.id, message.from_user.id) if user_state == text: return True elif type(text) is list and user_state in text: return True + + class IsDigitFilter(SimpleCustomFilter): """ Filter to check whether the string is made up of only digits. From 2e5590b566503a05b7fb014ef154c26b7efffb7f Mon Sep 17 00:00:00 2001 From: abdullaev388 <78722918+abdullaev388@users.noreply.github.com> Date: Sat, 12 Feb 2022 14:11:16 +0500 Subject: [PATCH 0851/1808] token removed :) --- examples/custom_filters/advanced_text_filter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 5f926b016..01387cccb 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -8,7 +8,7 @@ from telebot import TeleBot, types from telebot.custom_filters import TextFilter, AdvancedCustomFilter -bot = TeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") +bot = TeleBot("") class TextFilterKey(AdvancedCustomFilter): From b89ecb3e5af1d4bc339f3c6a950cf96270c43143 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 14:32:59 +0500 Subject: [PATCH 0852/1808] modified code --- telebot/custom_filters.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index efbd2185f..467f7dc61 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -89,24 +89,28 @@ def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery else: return False + if self.ignore_case: + text = text.lower() + + if self.equals: + self.equals = self.equals.lower() + elif self.contains: + self.contains = tuple(i.lower() for i in self.contains) + elif self.starts_with: + self.starts_with = self.starts_with.lower() + elif self.ends_with: + self.ends_with = self.ends_with.lower() + if self.equals: - if self.ignore_case: - return self.equals.lower() == text.lower() return self.equals == text if self.contains: - if self.ignore_case: - return text.lower() in (i.lower() for i in self.contains) return text in self.contains if self.starts_with: - if self.ignore_case: - return text.lower().startswith(self.starts_with) return text.startswith(self.starts_with) if self.ends_with: - if self.ignore_case: - return text.lower().endswith(self.ends_with) return text.endswith(self.ends_with) return False From 6fd2a38fe952285abf2a184f0bb4886e2a6cb2c0 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 15:12:30 +0500 Subject: [PATCH 0853/1808] An asyncio example demostrating TextFilter usage --- .../custom_filters/advanced_text_filter.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 examples/asynchronous_telebot/custom_filters/advanced_text_filter.py diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py new file mode 100644 index 000000000..eac295ea5 --- /dev/null +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you usage of TextFilter +In this example you will see how to use TextFilter +with (message_handler, callback_query_handler, poll_handler) +""" +import asyncio + +from telebot.async_telebot import AsyncTeleBot +from telebot import types +from telebot.custom_filters import TextFilter +from telebot.asyncio_filters import AdvancedCustomFilter + +bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") + + +class TextFilterKey(AdvancedCustomFilter): + key = 'text' + + async def check(self, message, config: TextFilter): + return config.check(message) + + +@bot.message_handler(text=TextFilter(equals='hello')) +async def hello_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True)) +async def hello_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'])) +async def contains_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True)) +async def contains_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE +async def starts_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE +async def starts_with_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay +async def ends_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY +async def ends_with_handler_ignore_case(message: types.Message): + await bot.send_message(message.chat.id, message.text + ' ignore case') + + +@bot.message_handler(text=TextFilter(equals='/callback')) +async def send_callback(message: types.Message): + keyboard = types.InlineKeyboardMarkup( + keyboard=[ + [types.InlineKeyboardButton(text='callback data', callback_data='example')], + [types.InlineKeyboardButton(text='ignore case callback data', callback_data='ExAmPLe')] + ] + ) + await bot.send_message(message.chat.id, message.text, reply_markup=keyboard) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example')) +async def callback_query_handler(call: types.CallbackQuery): + await bot.answer_callback_query(call.id, call.data, show_alert=True) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True)) +async def callback_query_handler_ignore_case(call: types.CallbackQuery): + await bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True) + + +@bot.message_handler(text=TextFilter(equals='/poll')) +async def send_poll(message: types.Message): + await bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night']) + await bot.send_poll(message.chat.id, question='WHEN DO you pRefeR to worK?', options=['Morning', 'Night']) + + +@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?')) +async def poll_question_handler(poll: types.Poll): + print(poll.question) + + +@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?', ignore_case=True)) +async def poll_question_handler_ignore_case(poll: types.Poll): + print(poll.question + ' ignore case') + + +if __name__ == '__main__': + bot.add_custom_filter(TextFilterKey()) + asyncio.run(bot.polling()) From a893fbc35895f8470fe992a1e791aeccfa623559 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 15:30:04 +0500 Subject: [PATCH 0854/1808] async advanced callback_data example was added --- .../advanced_calendar_example/filters.py | 26 ++++++ .../advanced_calendar_example/keyboards.py | 92 +++++++++++++++++++ .../advanced_calendar_example/main.py | 57 ++++++++++++ .../simple_products_example.py} | 0 4 files changed, 175 insertions(+) create mode 100644 examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py create mode 100644 examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py create mode 100644 examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py rename examples/asynchronous_telebot/{CallbackData_example.py => callback_data_examples/simple_products_example.py} (100%) diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py new file mode 100644 index 000000000..7c5c3043b --- /dev/null +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/filters.py @@ -0,0 +1,26 @@ +from telebot import types +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_filters import AdvancedCustomFilter +from telebot.callback_data import CallbackData, CallbackDataFilter + +calendar_factory = CallbackData("year", "month", prefix="calendar") +calendar_zoom = CallbackData("year", prefix="calendar_zoom") + + +class CalendarCallbackFilter(AdvancedCustomFilter): + key = 'calendar_config' + + async def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +class CalendarZoomCallbackFilter(AdvancedCustomFilter): + key = 'calendar_zoom_config' + + async def check(self, call: types.CallbackQuery, config: CallbackDataFilter): + return config.check(query=call) + + +def bind_filters(bot: AsyncTeleBot): + bot.add_custom_filter(CalendarCallbackFilter()) + bot.add_custom_filter(CalendarZoomCallbackFilter()) diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py new file mode 100644 index 000000000..1aee88c24 --- /dev/null +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/keyboards.py @@ -0,0 +1,92 @@ +import calendar +from datetime import date, timedelta + +from filters import calendar_factory, calendar_zoom +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton + +EMTPY_FIELD = '1' +WEEK_DAYS = [calendar.day_abbr[i] for i in range(7)] +MONTHS = [(i, calendar.month_name[i]) for i in range(1, 13)] + + +def generate_calendar_days(year: int, month: int): + keyboard = InlineKeyboardMarkup(row_width=7) + today = date.today() + + keyboard.add( + InlineKeyboardButton( + text=date(year=year, month=month, day=1).strftime('%b %Y'), + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*[ + InlineKeyboardButton( + text=day, + callback_data=EMTPY_FIELD + ) + for day in WEEK_DAYS + ]) + + for week in calendar.Calendar().monthdayscalendar(year=year, month=month): + week_buttons = [] + for day in week: + day_name = ' ' + if day == today.day and today.year == year and today.month == month: + day_name = '🔘' + elif day != 0: + day_name = str(day) + week_buttons.append( + InlineKeyboardButton( + text=day_name, + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*week_buttons) + + previous_date = date(year=year, month=month, day=1) - timedelta(days=1) + next_date = date(year=year, month=month, day=1) + timedelta(days=31) + + keyboard.add( + InlineKeyboardButton( + text='Previous month', + callback_data=calendar_factory.new(year=previous_date.year, month=previous_date.month) + ), + InlineKeyboardButton( + text='Zoom out', + callback_data=calendar_zoom.new(year=year) + ), + InlineKeyboardButton( + text='Next month', + callback_data=calendar_factory.new(year=next_date.year, month=next_date.month) + ), + ) + + return keyboard + + +def generate_calendar_months(year: int): + keyboard = InlineKeyboardMarkup(row_width=3) + keyboard.add( + InlineKeyboardButton( + text=date(year=year, month=1, day=1).strftime('Year %Y'), + callback_data=EMTPY_FIELD + ) + ) + keyboard.add(*[ + InlineKeyboardButton( + text=month, + callback_data=calendar_factory.new(year=year, month=month_number) + ) + for month_number, month in MONTHS + ]) + keyboard.add( + InlineKeyboardButton( + text='Previous year', + callback_data=calendar_zoom.new(year=year - 1) + ), + InlineKeyboardButton( + text='Next year', + callback_data=calendar_zoom.new(year=year + 1) + ) + ) + return keyboard diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py new file mode 100644 index 000000000..392f9acf8 --- /dev/null +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" +This Example will show you an advanced usage of CallbackData. +In this example calendar was implemented +""" +import asyncio +from datetime import date + +from filters import calendar_factory, calendar_zoom, bind_filters +from keyboards import generate_calendar_days, generate_calendar_months, EMTPY_FIELD +from telebot import types +from telebot.async_telebot import AsyncTeleBot + +API_TOKEN = '1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds' +bot = AsyncTeleBot(API_TOKEN) + + +@bot.message_handler(commands='start') +async def start_command_handler(message: types.Message): + await bot.send_message(message.chat.id, + f"Hello {message.from_user.first_name}. This bot is an example of calendar keyboard." + "\nPress /calendar to see it.") + + +@bot.message_handler(commands='calendar') +async def calendar_command_handler(message: types.Message): + now = date.today() + await bot.send_message(message.chat.id, 'Calendar', + reply_markup=generate_calendar_days(year=now.year, month=now.month)) + + +@bot.callback_query_handler(func=None, calendar_config=calendar_factory.filter()) +async def calendar_action_handler(call: types.CallbackQuery): + callback_data: dict = calendar_factory.parse(callback_data=call.data) + year, month = int(callback_data['year']), int(callback_data['month']) + + await bot.edit_message_reply_markup(call.message.chat.id, call.message.id, + reply_markup=generate_calendar_days(year=year, month=month)) + + +@bot.callback_query_handler(func=None, calendar_zoom_config=calendar_zoom.filter()) +async def calendar_zoom_out_handler(call: types.CallbackQuery): + callback_data: dict = calendar_zoom.parse(callback_data=call.data) + year = int(callback_data.get('year')) + + await bot.edit_message_reply_markup(call.message.chat.id, call.message.id, + reply_markup=generate_calendar_months(year=year)) + + +@bot.callback_query_handler(func=lambda call: call.data == EMTPY_FIELD) +async def callback_empty_field_handler(call: types.CallbackQuery): + await bot.answer_callback_query(call.id) + + +if __name__ == '__main__': + bind_filters(bot) + asyncio.run(bot.infinity_polling()) diff --git a/examples/asynchronous_telebot/CallbackData_example.py b/examples/asynchronous_telebot/callback_data_examples/simple_products_example.py similarity index 100% rename from examples/asynchronous_telebot/CallbackData_example.py rename to examples/asynchronous_telebot/callback_data_examples/simple_products_example.py From 3cd86d0e9390480816c00c544b2053a1c2ed8b3a Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 15:31:54 +0500 Subject: [PATCH 0855/1808] token. again. --- .../callback_data_examples/advanced_calendar_example/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py index 392f9acf8..004147434 100644 --- a/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py +++ b/examples/asynchronous_telebot/callback_data_examples/advanced_calendar_example/main.py @@ -11,7 +11,7 @@ from telebot import types from telebot.async_telebot import AsyncTeleBot -API_TOKEN = '1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds' +API_TOKEN = '' bot = AsyncTeleBot(API_TOKEN) From 5b1483f646b25a40d050ec508c0468686e99ef06 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 17:07:59 +0500 Subject: [PATCH 0856/1808] removed TextFilterKey in example, instead TextMatchFilter was modified --- .../custom_filters/advanced_text_filter.py | 13 ++---- .../custom_filters/advanced_text_filter.py | 11 +---- telebot/asyncio_filters.py | 40 ++++++++++++++----- telebot/custom_filters.py | 4 +- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index eac295ea5..184da3164 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -9,16 +9,9 @@ from telebot.async_telebot import AsyncTeleBot from telebot import types from telebot.custom_filters import TextFilter -from telebot.asyncio_filters import AdvancedCustomFilter +from telebot.asyncio_filters import TextMatchFilter -bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds") - - -class TextFilterKey(AdvancedCustomFilter): - key = 'text' - - async def check(self, message, config: TextFilter): - return config.check(message) +bot = AsyncTeleBot("") @bot.message_handler(text=TextFilter(equals='hello')) @@ -99,5 +92,5 @@ async def poll_question_handler_ignore_case(poll: types.Poll): if __name__ == '__main__': - bot.add_custom_filter(TextFilterKey()) + bot.add_custom_filter(TextMatchFilter()) asyncio.run(bot.polling()) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 01387cccb..9f2eb68ab 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -6,18 +6,11 @@ """ from telebot import TeleBot, types -from telebot.custom_filters import TextFilter, AdvancedCustomFilter +from telebot.custom_filters import TextFilter, TextMatchFilter bot = TeleBot("") -class TextFilterKey(AdvancedCustomFilter): - key = 'text' - - def check(self, message, config: TextFilter): - return config.check(message) - - @bot.message_handler(text=TextFilter(equals='hello')) def hello_handler(message: types.Message): bot.send_message(message.chat.id, message.text) @@ -96,5 +89,5 @@ def poll_question_handler_ignore_case(poll: types.Poll): if __name__ == '__main__': - bot.add_custom_filter(TextFilterKey()) + bot.add_custom_filter(TextMatchFilter()) bot.infinity_polling() diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 417b110c8..bacc26f85 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -1,5 +1,8 @@ from abc import ABC +from telebot.custom_filters import TextFilter + + class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -42,8 +45,13 @@ class TextMatchFilter(AdvancedCustomFilter): key = 'text' async def check(self, message, text): - if type(text) is list:return message.text in text - else: return text == message.text + if isinstance(text, TextFilter): + return text.check(message) + elif type(text) is list: + return message.text in text + else: + return text == message.text + class TextContainsFilter(AdvancedCustomFilter): """ @@ -60,6 +68,7 @@ class TextContainsFilter(AdvancedCustomFilter): async def check(self, message, text): return text in message.text + class TextStartsFilter(AdvancedCustomFilter): """ Filter to check whether message starts with some text. @@ -70,8 +79,10 @@ class TextStartsFilter(AdvancedCustomFilter): """ key = 'text_startswith' + async def check(self, message, text): - return message.text.startswith(text) + return message.text.startswith(text) + class ChatFilter(AdvancedCustomFilter): """ @@ -82,9 +93,11 @@ class ChatFilter(AdvancedCustomFilter): """ key = 'chat_id' + async def check(self, message, text): return message.chat.id in text + class ForwardFilter(SimpleCustomFilter): """ Check whether message was forwarded from channel or group. @@ -99,6 +112,7 @@ class ForwardFilter(SimpleCustomFilter): async def check(self, message): return message.forward_from_chat is not None + class IsReplyFilter(SimpleCustomFilter): """ Check whether message is a reply. @@ -114,7 +128,6 @@ async def check(self, message): return message.reply_to_message is not None - class LanguageFilter(AdvancedCustomFilter): """ Check users language_code. @@ -127,8 +140,11 @@ class LanguageFilter(AdvancedCustomFilter): key = 'language_code' async def check(self, message, text): - if type(text) is list:return message.from_user.language_code in text - else: return message.from_user.language_code == text + if type(text) is list: + return message.from_user.language_code in text + else: + return message.from_user.language_code == text + class IsAdminFilter(SimpleCustomFilter): """ @@ -147,6 +163,7 @@ async def check(self, message): result = await self._bot.get_chat_member(message.chat.id, message.from_user.id) return result.status in ['creator', 'administrator'] + class StateFilter(AdvancedCustomFilter): """ Filter to check state. @@ -154,8 +171,10 @@ class StateFilter(AdvancedCustomFilter): Example: @bot.message_handler(state=1) """ + def __init__(self, bot): self.bot = bot + key = 'state' async def check(self, message, text): @@ -166,22 +185,23 @@ async def check(self, message, text): text = new_text elif isinstance(text, object): text = text.name - + if message.chat.type == 'group': group_state = await self.bot.current_states.get_state(message.chat.id, message.from_user.id) if group_state == text: return True elif group_state in text and type(text) is list: return True - - + + else: - user_state = await self.bot.current_states.get_state(message.chat.id,message.from_user.id) + user_state = await self.bot.current_states.get_state(message.chat.id, message.from_user.id) if user_state == text: return True elif type(text) is list and user_state in text: return True + class IsDigitFilter(SimpleCustomFilter): """ Filter to check whether the string is made up of only digits. diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 467f7dc61..d7fe3e0ac 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -128,7 +128,9 @@ class TextMatchFilter(AdvancedCustomFilter): key = 'text' def check(self, message, text): - if type(text) is list: + if isinstance(text, TextFilter): + return text.check(message) + elif type(text) is list: return message.text in text else: return text == message.text From 5f7ccc8c9b5d14b99a7e2669830c3bf242380ae3 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 17:33:29 +0500 Subject: [PATCH 0857/1808] created async TextFilter --- .../custom_filters/advanced_text_filter.py | 5 +- telebot/asyncio_filters.py | 87 ++++++++++++++++++- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index 184da3164..4c363b5bf 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -6,10 +6,9 @@ """ import asyncio -from telebot.async_telebot import AsyncTeleBot from telebot import types -from telebot.custom_filters import TextFilter -from telebot.asyncio_filters import TextMatchFilter +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_filters import TextMatchFilter, TextFilter bot = AsyncTeleBot("") diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index bacc26f85..9944ff4b9 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -1,6 +1,7 @@ from abc import ABC +from typing import Optional, Union -from telebot.custom_filters import TextFilter +from telebot import types class SimpleCustomFilter(ABC): @@ -33,6 +34,88 @@ async def check(self, message, text): pass +class TextFilter: + """ + Advanced async text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) + + example of usage is in examples/custom_filters/advanced_text_filter.py + """ + + def __init__(self, + equals: Optional[str] = None, + contains: Optional[Union[list, tuple]] = None, + starts_with: Optional[str] = None, + ends_with: Optional[str] = None, + ignore_case: bool = False): + + """ + :param equals: string, True if object's text is equal to passed string + :param contains: list[str] or tuple[str], True if object's text is in list or tuple + :param starts_with: string, True if object's text starts with passed string + :param ends_with: string, True if object's text starts with passed string + :param ignore_case: bool (default False), case insensitive + """ + + to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with))) + if to_check == 0: + raise ValueError('None of the check modes was specified') + elif to_check > 1: + raise ValueError('Only one check mode can be specified') + elif contains: + for i in contains: + if not isinstance(i, str): + raise ValueError(f"Invalid value '{i}' is in contains") + elif starts_with and not isinstance(starts_with, str): + raise ValueError("starts_with has to be a string") + elif ends_with and not isinstance(ends_with, str): + raise ValueError("ends_with has to be a string") + + self.equals = equals + self.contains = contains + self.starts_with = starts_with + self.ends_with = ends_with + self.ignore_case = ignore_case + + async def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): + + if isinstance(obj, types.Poll): + text = obj.question + elif isinstance(obj, types.Message): + text = obj.text or obj.caption + elif isinstance(obj, types.CallbackQuery): + text = obj.data + elif isinstance(obj, types.InlineQuery): + text = obj.query + else: + return False + + if self.ignore_case: + text = text.lower() + + if self.equals: + self.equals = self.equals.lower() + elif self.contains: + self.contains = tuple(i.lower() for i in self.contains) + elif self.starts_with: + self.starts_with = self.starts_with.lower() + elif self.ends_with: + self.ends_with = self.ends_with.lower() + + if self.equals: + return self.equals == text + + if self.contains: + return text in self.contains + + if self.starts_with: + return text.startswith(self.starts_with) + + if self.ends_with: + return text.endswith(self.ends_with) + + return False + + class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. @@ -46,7 +129,7 @@ class TextMatchFilter(AdvancedCustomFilter): async def check(self, message, text): if isinstance(text, TextFilter): - return text.check(message) + return await text.check(message) elif type(text) is list: return message.text in text else: From 8bbd062d1345dcd83ed09ec1f173bb505813df84 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 20:31:02 +0500 Subject: [PATCH 0858/1808] text contains filter was fixed --- telebot/custom_filters.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index d7fe3e0ac..6eea0f020 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -50,7 +50,7 @@ def __init__(self, """ :param equals: string, True if object's text is equal to passed string - :param contains: list[str] or tuple[str], True if object's text is in list or tuple + :param contains: list[str] or tuple[str], True if any string element of iterable is in text :param starts_with: string, True if object's text starts with passed string :param ends_with: string, True if object's text starts with passed string :param ignore_case: bool (default False), case insensitive @@ -62,9 +62,12 @@ def __init__(self, elif to_check > 1: raise ValueError('Only one check mode can be specified') elif contains: - for i in contains: - if not isinstance(i, str): - raise ValueError(f"Invalid value '{i}' is in contains") + if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): + raise ValueError("Incorrect contains value") + elif isinstance(contains, str): + contains = [contains] + elif isinstance(contains, list) or isinstance(contains, tuple): + contains = [i for i in contains if isinstance(i, str)] elif starts_with and not isinstance(starts_with, str): raise ValueError("starts_with has to be a string") elif ends_with and not isinstance(ends_with, str): @@ -105,7 +108,7 @@ def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery return self.equals == text if self.contains: - return text in self.contains + return any([i in text for i in self.contains]) if self.starts_with: return text.startswith(self.starts_with) @@ -149,7 +152,14 @@ class TextContainsFilter(AdvancedCustomFilter): key = 'text_contains' def check(self, message, text): - return text in message.text + if not isinstance(text, str) and not isinstance(text, list) and not isinstance(text, tuple): + raise ValueError("Incorrect text_contains value") + elif isinstance(text, str): + text = [text] + elif isinstance(text, list) or isinstance(text, tuple): + text = [i for i in text if isinstance(i, str)] + + return any([i in message.text for i in text]) class TextStartsFilter(AdvancedCustomFilter): From 6e4f2e19d63fe763985c4183e368b5f9e92f654e Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 20:36:10 +0500 Subject: [PATCH 0859/1808] async text contains filter was fixed --- telebot/asyncio_filters.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 9944ff4b9..5fa003d17 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -38,7 +38,7 @@ class TextFilter: """ Advanced async text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) - example of usage is in examples/custom_filters/advanced_text_filter.py + example of usage is in examples/asynchronous_telebot/custom_filters/advanced_text_filter.py """ def __init__(self, @@ -50,7 +50,7 @@ def __init__(self, """ :param equals: string, True if object's text is equal to passed string - :param contains: list[str] or tuple[str], True if object's text is in list or tuple + :param contains: list[str] or tuple[str], True if any string element of iterable is in text :param starts_with: string, True if object's text starts with passed string :param ends_with: string, True if object's text starts with passed string :param ignore_case: bool (default False), case insensitive @@ -62,9 +62,12 @@ def __init__(self, elif to_check > 1: raise ValueError('Only one check mode can be specified') elif contains: - for i in contains: - if not isinstance(i, str): - raise ValueError(f"Invalid value '{i}' is in contains") + if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): + raise ValueError("Incorrect contains value") + elif isinstance(contains, str): + contains = [contains] + elif isinstance(contains, list) or isinstance(contains, tuple): + contains = [i for i in contains if isinstance(i, str)] elif starts_with and not isinstance(starts_with, str): raise ValueError("starts_with has to be a string") elif ends_with and not isinstance(ends_with, str): @@ -105,7 +108,7 @@ async def check(self, obj: Union[types.Message, types.CallbackQuery, types.Inlin return self.equals == text if self.contains: - return text in self.contains + return any([i in text for i in self.contains]) if self.starts_with: return text.startswith(self.starts_with) @@ -149,7 +152,14 @@ class TextContainsFilter(AdvancedCustomFilter): key = 'text_contains' async def check(self, message, text): - return text in message.text + if not isinstance(text, str) and not isinstance(text, list) and not isinstance(text, tuple): + raise ValueError("Incorrect text_contains value") + elif isinstance(text, str): + text = [text] + elif isinstance(text, list) or isinstance(text, tuple): + text = [i for i in text if isinstance(i, str)] + + return any([i in message.text for i in text]) class TextStartsFilter(AdvancedCustomFilter): From 6822f18cbbc9d6ac54049cf78828e769cc8a6507 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 21:41:10 +0500 Subject: [PATCH 0860/1808] multiple check patterns && multiple startwith, endswith fields --- telebot/asyncio_filters.py | 64 ++++++++++++++++++++++---------------- telebot/custom_filters.py | 61 ++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 53 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 5fa003d17..a8b718014 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -36,16 +36,16 @@ async def check(self, message, text): class TextFilter: """ - Advanced async text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) + Advanced text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) - example of usage is in examples/asynchronous_telebot/custom_filters/advanced_text_filter.py + example of usage is in examples/custom_filters/advanced_text_filter.py """ def __init__(self, equals: Optional[str] = None, contains: Optional[Union[list, tuple]] = None, - starts_with: Optional[str] = None, - ends_with: Optional[str] = None, + starts_with: Optional[Union[str, list, tuple]] = None, + ends_with: Optional[Union[str, list, tuple]] = None, ignore_case: bool = False): """ @@ -59,26 +59,24 @@ def __init__(self, to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with))) if to_check == 0: raise ValueError('None of the check modes was specified') - elif to_check > 1: - raise ValueError('Only one check mode can be specified') - elif contains: - if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): - raise ValueError("Incorrect contains value") - elif isinstance(contains, str): - contains = [contains] - elif isinstance(contains, list) or isinstance(contains, tuple): - contains = [i for i in contains if isinstance(i, str)] - elif starts_with and not isinstance(starts_with, str): - raise ValueError("starts_with has to be a string") - elif ends_with and not isinstance(ends_with, str): - raise ValueError("ends_with has to be a string") self.equals = equals - self.contains = contains - self.starts_with = starts_with - self.ends_with = ends_with + self.contains = self._check_iterable(contains) + self.starts_with = self._check_iterable(starts_with) + self.ends_with = self._check_iterable(ends_with) self.ignore_case = ignore_case + def _check_iterable(self, iterable): + if not iterable: + pass + elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): + raise ValueError + elif isinstance(iterable, str): + iterable = [iterable] + elif isinstance(iterable, list) or isinstance(iterable, tuple): + iterable = [i for i in iterable if isinstance(i, str)] + return iterable + async def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): if isinstance(obj, types.Poll): @@ -98,23 +96,35 @@ async def check(self, obj: Union[types.Message, types.CallbackQuery, types.Inlin if self.equals: self.equals = self.equals.lower() elif self.contains: - self.contains = tuple(i.lower() for i in self.contains) + self.contains = tuple(map(str.lower, self.contains)) elif self.starts_with: - self.starts_with = self.starts_with.lower() + self.starts_with = tuple(map(str.lower, self.starts_with)) elif self.ends_with: - self.ends_with = self.ends_with.lower() + self.ends_with = tuple(map(str.lower, self.ends_with)) if self.equals: - return self.equals == text + result = self.equals == text + if result: + return True + elif not result and not any((self.contains, self.starts_with, self.ends_with)): + return False if self.contains: - return any([i in text for i in self.contains]) + result = any([i in text for i in self.contains]) + if result: + return True + elif not result and not any((self.starts_with, self.ends_with)): + return False if self.starts_with: - return text.startswith(self.starts_with) + result = any([text.startswith(i) for i in self.starts_with]) + if result: + return True + elif not result and not self.ends_with: + return False if self.ends_with: - return text.endswith(self.ends_with) + return any([text.endswith(i) for i in self.ends_with]) return False diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 6eea0f020..145cc74a3 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -44,8 +44,8 @@ class TextFilter: def __init__(self, equals: Optional[str] = None, contains: Optional[Union[list, tuple]] = None, - starts_with: Optional[str] = None, - ends_with: Optional[str] = None, + starts_with: Optional[Union[str, list, tuple]] = None, + ends_with: Optional[Union[str, list, tuple]] = None, ignore_case: bool = False): """ @@ -59,26 +59,24 @@ def __init__(self, to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with))) if to_check == 0: raise ValueError('None of the check modes was specified') - elif to_check > 1: - raise ValueError('Only one check mode can be specified') - elif contains: - if not isinstance(contains, str) and not isinstance(contains, list) and not isinstance(contains, tuple): - raise ValueError("Incorrect contains value") - elif isinstance(contains, str): - contains = [contains] - elif isinstance(contains, list) or isinstance(contains, tuple): - contains = [i for i in contains if isinstance(i, str)] - elif starts_with and not isinstance(starts_with, str): - raise ValueError("starts_with has to be a string") - elif ends_with and not isinstance(ends_with, str): - raise ValueError("ends_with has to be a string") self.equals = equals - self.contains = contains - self.starts_with = starts_with - self.ends_with = ends_with + self.contains = self._check_iterable(contains) + self.starts_with = self._check_iterable(starts_with) + self.ends_with = self._check_iterable(ends_with) self.ignore_case = ignore_case + def _check_iterable(self, iterable): + if not iterable: + pass + elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): + raise ValueError + elif isinstance(iterable, str): + iterable = [iterable] + elif isinstance(iterable, list) or isinstance(iterable, tuple): + iterable = [i for i in iterable if isinstance(i, str)] + return iterable + def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): if isinstance(obj, types.Poll): @@ -98,27 +96,38 @@ def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery if self.equals: self.equals = self.equals.lower() elif self.contains: - self.contains = tuple(i.lower() for i in self.contains) + self.contains = tuple(map(str.lower, self.contains)) elif self.starts_with: - self.starts_with = self.starts_with.lower() + self.starts_with = tuple(map(str.lower, self.starts_with)) elif self.ends_with: - self.ends_with = self.ends_with.lower() + self.ends_with = tuple(map(str.lower, self.ends_with)) if self.equals: - return self.equals == text + result = self.equals == text + if result: + return True + elif not result and not any((self.contains, self.starts_with, self.ends_with)): + return False if self.contains: - return any([i in text for i in self.contains]) + result = any([i in text for i in self.contains]) + if result: + return True + elif not result and not any((self.starts_with, self.ends_with)): + return False if self.starts_with: - return text.startswith(self.starts_with) + result = any([text.startswith(i) for i in self.starts_with]) + if result: + return True + elif not result and not self.ends_with: + return False if self.ends_with: - return text.endswith(self.ends_with) + return any([text.endswith(i) for i in self.ends_with]) return False - class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. From 8c3d1e608ccf41bda7bf1f12501061d3b66f8510 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 12 Feb 2022 21:53:40 +0500 Subject: [PATCH 0861/1808] new TextFilter examples were added --- .../custom_filters/advanced_text_filter.py | 18 ++++++++++++++++++ .../custom_filters/advanced_text_filter.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index 4c363b5bf..df8b2dd67 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -90,6 +90,24 @@ async def poll_question_handler_ignore_case(poll: types.Poll): print(poll.question + ' ignore case') +# either hi or contains one of (привет, salom) +@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True)) +async def multiple_patterns_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +# starts with one of (mi, mea) for ex. minor, milk, meal, meat +@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True)) +async def multiple_starts_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + +# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson +@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True)) +async def multiple_ends_with_handler(message: types.Message): + await bot.send_message(message.chat.id, message.text) + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) asyncio.run(bot.polling()) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 9f2eb68ab..41ce3d51a 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -88,6 +88,24 @@ def poll_question_handler_ignore_case(poll: types.Poll): print(poll.question + ' ignore case') +# either hi or contains one of (привет, salom) +@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True)) +def multiple_patterns_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +# starts with one of (mi, mea) for ex. minor, milk, meal, meat +@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True)) +def multiple_starts_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + +# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson +@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True)) +def multiple_ends_with_handler(message: types.Message): + bot.send_message(message.chat.id, message.text) + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) bot.infinity_polling() From 68095ad69a193533de82cb824e4398dd4c29a9ed Mon Sep 17 00:00:00 2001 From: Troshchk Date: Tue, 15 Feb 2022 15:24:55 +0100 Subject: [PATCH 0862/1808] Adding checks for the commands and regexp input types --- telebot/__init__.py | 160 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 16 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1eb496fc8..f370205b6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2783,9 +2783,25 @@ def default_command(message): if content_types is None: content_types = ["text"] - if isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2824,9 +2840,25 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, str): - logger.warning("register_message_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2856,9 +2888,25 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ if content_types is None: content_types = ["text"] - if isinstance(commands, str): - logger.warning("edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2897,9 +2945,25 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, str): - logger.warning("register_edited_message_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("register_edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2929,9 +2993,25 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty if content_types is None: content_types = ["text"] - if isinstance(commands, str): - logger.warning("channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2968,9 +3048,25 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, str): - logger.warning("register_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("register_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2998,9 +3094,25 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con if content_types is None: content_types = ["text"] - if isinstance(commands, str): - logger.warning("edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -3037,9 +3149,25 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, str): - logger.warning("register_edited_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") + if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if any(not isinstance(item, (str, int, float)) for item in commands): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") + elif all(isinstance(item, str) for item in commands): + commands = list(commands) + else: + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") + commands = [str(item) if not isinstance(item,str) else item for item in commands] + elif isinstance(commands, str): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") commands = [commands] + elif isinstance(commands, (int, float)): + logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") + commands = [str(commands)] + elif not isinstance(commands, type(None)): + logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + + if not isinstance(regexp, (str, type(None))): + logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") if isinstance(content_types, str): logger.warning("register_edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") From 907543021071376c9b41ecca335090df39135051 Mon Sep 17 00:00:00 2001 From: Troshchk Date: Tue, 15 Feb 2022 15:46:02 +0100 Subject: [PATCH 0863/1808] Making first condition shorter, no change in functionality --- telebot/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f370205b6..d3b4e4282 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2783,7 +2783,7 @@ def default_command(message): if content_types is None: content_types = ["text"] - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): @@ -2840,7 +2840,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): @@ -2888,7 +2888,7 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ if content_types is None: content_types = ["text"] - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): @@ -2945,7 +2945,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): @@ -2993,7 +2993,7 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty if content_types is None: content_types = ["text"] - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): @@ -3048,7 +3048,7 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): @@ -3094,7 +3094,7 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con if content_types is None: content_types = ["text"] - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): @@ -3149,7 +3149,7 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, list) or isinstance(commands, set) or isinstance(commands, tuple): + if isinstance(commands, (list, set, tuple)): if any(not isinstance(item, (str, int, float)) for item in commands): logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") elif all(isinstance(item, str) for item in commands): From 966f2e7ef7f82580e445cdb7a96786b8386670f8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 15 Feb 2022 19:55:12 +0300 Subject: [PATCH 0864/1808] Fix timer_bot.py --- examples/timer_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/timer_bot.py b/examples/timer_bot.py index 9f49b6c01..3802dbfee 100644 --- a/examples/timer_bot.py +++ b/examples/timer_bot.py @@ -32,7 +32,7 @@ def set_timer(message): @bot.message_handler(commands=['unset']) def unset_timer(message): - schedule.clean(message.chat.id) + schedule.clear(message.chat.id) if __name__ == '__main__': From b41435f4079026afd546b36ae3bdbf8d5b144982 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Wed, 16 Feb 2022 12:29:27 +0500 Subject: [PATCH 0865/1808] more descriptive exceptions --- telebot/asyncio_filters.py | 10 +++++----- telebot/custom_filters.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index a8b718014..cb0120bbf 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -61,16 +61,16 @@ def __init__(self, raise ValueError('None of the check modes was specified') self.equals = equals - self.contains = self._check_iterable(contains) - self.starts_with = self._check_iterable(starts_with) - self.ends_with = self._check_iterable(ends_with) + self.contains = self._check_iterable(contains, filter_name='contains') + self.starts_with = self._check_iterable(starts_with, filter_name='starts_with') + self.ends_with = self._check_iterable(ends_with, filter_name='ends_with') self.ignore_case = ignore_case - def _check_iterable(self, iterable): + def _check_iterable(self, iterable, filter_name): if not iterable: pass elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): - raise ValueError + raise ValueError(f"Incorrect value of {filter_name!r}") elif isinstance(iterable, str): iterable = [iterable] elif isinstance(iterable, list) or isinstance(iterable, tuple): diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 145cc74a3..e6a1531aa 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -61,16 +61,16 @@ def __init__(self, raise ValueError('None of the check modes was specified') self.equals = equals - self.contains = self._check_iterable(contains) - self.starts_with = self._check_iterable(starts_with) - self.ends_with = self._check_iterable(ends_with) + self.contains = self._check_iterable(contains, filter_name='contains') + self.starts_with = self._check_iterable(starts_with, filter_name='starts_with') + self.ends_with = self._check_iterable(ends_with, filter_name='ends_with') self.ignore_case = ignore_case - def _check_iterable(self, iterable): + def _check_iterable(self, iterable, filter_name: str): if not iterable: pass elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): - raise ValueError + raise ValueError(f"Incorrect value of {filter_name!r}") elif isinstance(iterable, str): iterable = [iterable] elif isinstance(iterable, list) or isinstance(iterable, tuple): From 3a86916e7251bb69b3572adc47348f6cc5520731 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Wed, 16 Feb 2022 12:43:23 +0500 Subject: [PATCH 0866/1808] example of TextFilter starts_with and ends_with usage simultaneously --- .../custom_filters/advanced_text_filter.py | 16 +++++++++++++++- examples/custom_filters/advanced_text_filter.py | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py index df8b2dd67..1200a6cb2 100644 --- a/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py +++ b/examples/asynchronous_telebot/custom_filters/advanced_text_filter.py @@ -8,7 +8,7 @@ from telebot import types from telebot.async_telebot import AsyncTeleBot -from telebot.asyncio_filters import TextMatchFilter, TextFilter +from telebot.asyncio_filters import TextMatchFilter, TextFilter, IsReplyFilter bot = AsyncTeleBot("") @@ -108,6 +108,20 @@ async def multiple_ends_with_handler(message: types.Message): await bot.send_message(message.chat.id, message.text) +# !ban /ban .ban !бан /бан .бан +@bot.message_handler(is_reply=True, + text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True)) +async def ban_command_handler(message: types.Message): + if len(message.text) == 4 and message.chat.type != 'private': + try: + await bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id) + await bot.reply_to(message.reply_to_message, 'Banned.') + except Exception as err: + print(err.args) + return + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) + bot.add_custom_filter(IsReplyFilter()) asyncio.run(bot.polling()) diff --git a/examples/custom_filters/advanced_text_filter.py b/examples/custom_filters/advanced_text_filter.py index 41ce3d51a..2b01685c0 100644 --- a/examples/custom_filters/advanced_text_filter.py +++ b/examples/custom_filters/advanced_text_filter.py @@ -6,7 +6,7 @@ """ from telebot import TeleBot, types -from telebot.custom_filters import TextFilter, TextMatchFilter +from telebot.custom_filters import TextFilter, TextMatchFilter, IsReplyFilter bot = TeleBot("") @@ -106,6 +106,20 @@ def multiple_ends_with_handler(message: types.Message): bot.send_message(message.chat.id, message.text) +# !ban /ban .ban !бан /бан .бан +@bot.message_handler(is_reply=True, + text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True)) +def ban_command_handler(message: types.Message): + if len(message.text) == 4 and message.chat.type != 'private': + try: + bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id) + bot.reply_to(message.reply_to_message, 'Banned.') + except Exception as err: + print(err.args) + return + + if __name__ == '__main__': bot.add_custom_filter(TextMatchFilter()) + bot.add_custom_filter(IsReplyFilter()) bot.infinity_polling() From ec766a3e43a39a2a24c069ac15fda02d4d277fd2 Mon Sep 17 00:00:00 2001 From: Troshchk Date: Wed, 16 Feb 2022 14:05:54 +0100 Subject: [PATCH 0867/1808] Wrapping checking in private methods; warnings changed to errors --- telebot/__init__.py | 200 +++++++++++++------------------------------- 1 file changed, 56 insertions(+), 144 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d3b4e4282..c18f7dc5a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2741,6 +2741,14 @@ def register_middleware_handler(self, callback, update_types=None): """ self.add_middleware_handler(callback, update_types) + def __check_commands_input(self, commands, method_name): + if not isinstance(commands, list) or not all(isinstance(item, str) for item in commands): + logger.error(f"{method_name}: Commands filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use the supplied type.") + + def __check_regexp_input(self, regexp, method_name): + if not isinstance(regexp, str): + logger.error(f"{method_name}: Regexp filter should be string. Not able to use the supplied type.") + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Message handler decorator. @@ -2783,25 +2791,13 @@ def default_command(message): if content_types is None: content_types = ["text"] - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "message_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2840,25 +2836,13 @@ def register_message_handler(self, callback, content_types=None, commands=None, :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "register_message_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2888,25 +2872,13 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ if content_types is None: content_types = ["text"] - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "edited_message_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2945,25 +2917,13 @@ def register_edited_message_handler(self, callback, content_types=None, commands :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "register_edited_message_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2993,25 +2953,13 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty if content_types is None: content_types = ["text"] - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "channel_post_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -3048,25 +2996,13 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "register_channel_post_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -3094,25 +3030,13 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con if content_types is None: content_types = ["text"] - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "edited_channel_post_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -3149,25 +3073,13 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com :param pass_bot: Pass TeleBot to handler. :return: decorated function """ - if isinstance(commands, (list, set, tuple)): - if any(not isinstance(item, (str, int, float)) for item in commands): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use.") - elif all(isinstance(item, str) for item in commands): - commands = list(commands) - else: - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not list of different types. Converting non-strings to strings.") - commands = [str(item) if not isinstance(item,str) else item for item in commands] - elif isinstance(commands, str): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not string. Converting to list of string.") - commands = [commands] - elif isinstance(commands, (int, float)): - logger.warning("message_handler: 'commands' filter should be list of strings (commands), not numeric. Converting to the list of string.") - commands = [str(commands)] - elif not isinstance(commands, type(None)): - logger.warning("message_handler: commands' filter should be list of strings (commands), unknown type supplied to the 'commands' filter. Not able to use.") + method_name = "register_edited_channel_post_handler" + + if commands is not None: + self.__check_commands_input(commands, method_name) - if not isinstance(regexp, (str, type(None))): - logger.warning("message_handler: regex filter should be string. Not able to use the supplied type.") + if regexp is not None: + self.__check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") From 002c608d4515ccb38c88fcbeb5d0d28fb3fe8743 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 15:04:31 +0500 Subject: [PATCH 0868/1808] i18n class was added --- telebot/util.py | 113 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 17 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 24f6b5578..ebf746633 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import gettext +import os import random import re import string @@ -21,6 +23,7 @@ # noinspection PyPackageRequirements from PIL import Image from io import BytesIO + pil_imported = True except: pil_imported = False @@ -32,23 +35,26 @@ thread_local = threading.local() content_type_media = [ - 'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll', + 'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', + 'poll', 'venue', 'location' ] content_type_service = [ - 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', + 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', + 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', - 'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', + 'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', 'voice_chat_participants_invited', 'message_auto_delete_timer_changed' ] update_types = [ - "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", + "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" ] + class WorkerThread(threading.Thread): count = 0 @@ -177,7 +183,7 @@ def wait(self): class CustomRequestResponse(): - def __init__(self, json_text, status_code = 200, reason = ""): + def __init__(self, json_text, status_code=200, reason=""): self.status_code = status_code self.text = json_text self.reason = reason @@ -217,7 +223,7 @@ def pil_image_to_file(image, extension='JPEG', quality='web_low'): photoBuffer = BytesIO() image.convert('RGB').save(photoBuffer, extension, quality=quality) photoBuffer.seek(0) - + return photoBuffer else: raise RuntimeError('PIL module is not imported') @@ -280,7 +286,7 @@ def split_string(text: str, chars_per_string: int) -> List[str]: return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)] -def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]: +def smart_split(text: str, chars_per_string: int = MAX_MESSAGE_LENGTH) -> List[str]: """ Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. This is very useful for splitting one giant message into multiples. @@ -305,9 +311,12 @@ def _text_before_last(substr: str) -> str: part = text[:chars_per_string] - if "\n" in part: part = _text_before_last("\n") - elif ". " in part: part = _text_before_last(". ") - elif " " in part: part = _text_before_last(" ") + if "\n" in part: + part = _text_before_last("\n") + elif ". " in part: + part = _text_before_last(". ") + elif " " in part: + part = _text_before_last(" ") parts.append(part) text = text[len(part):] @@ -325,7 +334,7 @@ def escape(text: str) -> str: return text -def user_link(user: types.User, include_id: bool=False) -> str: +def user_link(user: types.User, include_id: bool = False) -> str: """ Returns an HTML user link. This is useful for reports. Attention: Don't forget to set parse_mode to 'HTML'! @@ -338,11 +347,11 @@ def user_link(user: types.User, include_id: bool=False) -> str: :return: HTML user link """ name = escape(user.first_name) - return (f"{name}" - + (f" (

{user.id}
)" if include_id else "")) + return (f"{name}" + + (f" (
{user.id}
)" if include_id else "")) -def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup: +def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int = 2) -> types.InlineKeyboardMarkup: """ Returns a reply markup from a dict in this format: {'text': kwargs} This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' @@ -443,22 +452,26 @@ def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) -def deprecated(warn: bool=True, alternative: Optional[Callable]=None): +def deprecated(warn: bool = True, alternative: Optional[Callable] = None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. :param warn: If True a warning is logged else an info :param alternative: The new function to use instead """ + def decorator(function): def wrapper(*args, **kwargs): - info = f"`{function.__name__}` is deprecated." + (f" Use `{alternative.__name__}` instead" if alternative else "") + info = f"`{function.__name__}` is deprecated." + ( + f" Use `{alternative.__name__}` instead" if alternative else "") if not warn: logger.info(info) else: logger.warning(info) return function(*args, **kwargs) + return wrapper + return decorator @@ -477,6 +490,7 @@ def webhook_google_functions(bot, request): else: return 'Bot ON' + def antiflood(function, *args, **kwargs): """ Use this function inside loops in order to avoid getting TooManyRequests error. @@ -499,3 +513,68 @@ def antiflood(function, *args, **kwargs): msg = function(*args, **kwargs) finally: return msg + + +def find_translations(path, domain): + """ + Looks for translations with passed 'domain' in passed 'path' + """ + if not os.path.exists(path): + raise RuntimeError(f"Translations directory by path: {path!r} was not found") + + result = {} + + for name in os.listdir(path): + translations_path = os.path.join(path, name, 'LC_MESSAGES') + + if not os.path.isdir(translations_path): + continue + + po_file = os.path.join(translations_path, domain + '.po') + mo_file = po_file[:-2] + 'mo' + + if os.path.isfile(po_file) and not os.path.isfile(mo_file): + raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") + + with open(mo_file, 'rb') as file: + result[name] = gettext.GNUTranslations(file) + + return result + + +class I18N: + """ + This class provides high-level tool for internationalization + It is based on gettext util. + """ + + def __init__(self, translations_path, domain_name: str): + self.path = translations_path + self.domain = domain_name + self.translations = find_translations(self.path, self.domain) + + @property + def available_translations(self): + return list(self.translations) + + def gettext(self, text: str, lang: str = None): + """ + Singular translations + """ + if not lang or lang not in self.translations: + return text + + translator = self.translations[lang] + return translator.gettext(text) + + def ngettext(self, singular: str, plural: str, lang: str = None, n=1): + """ + Plural translations + """ + if not lang or lang not in self.translations: + if n == 1: + return singular + return plural + + translator = self.translations[lang] + return translator.ngettext(singular, plural, n) From 0d85a345519406a5fdc311c41c191757cd51805d Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 15:07:46 +0500 Subject: [PATCH 0869/1808] an example for i18n class was added --- examples/i18n_class_example/keyboards.py | 23 +++ .../locales/en/LC_MESSAGES/messages.po | 51 ++++++ .../locales/ru/LC_MESSAGES/messages.po | 60 +++++++ .../locales/uz_Latn/LC_MESSAGES/messages.po | 58 +++++++ examples/i18n_class_example/main.py | 146 ++++++++++++++++++ 5 files changed, 338 insertions(+) create mode 100644 examples/i18n_class_example/keyboards.py create mode 100644 examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po create mode 100644 examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po create mode 100644 examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po create mode 100644 examples/i18n_class_example/main.py diff --git a/examples/i18n_class_example/keyboards.py b/examples/i18n_class_example/keyboards.py new file mode 100644 index 000000000..4a942686c --- /dev/null +++ b/examples/i18n_class_example/keyboards.py @@ -0,0 +1,23 @@ +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton + + +def languages_keyboard(): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text="English", callback_data='en'), + InlineKeyboardButton(text="Русский", callback_data='ru'), + InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn') + ] + ] + ) + + +def clicker_keyboard(_, lang): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text=_("click", lang=lang), callback_data='click'), + ] + ] + ) diff --git a/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po new file mode 100644 index 000000000..fc6565a4a --- /dev/null +++ b/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po @@ -0,0 +1,51 @@ +# English translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-18 17:54+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "" + +#: main.py:78 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example" +msgstr "" + +#: main.py:102 +msgid "Language has been changed" +msgstr "" + +#: main.py:114 +#, fuzzy +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "" +msgstr[1] "" + +#: main.py:120 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" + diff --git a/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po new file mode 100644 index 000000000..1733ef152 --- /dev/null +++ b/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po @@ -0,0 +1,60 @@ +# Russian translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-18 17:54+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: ru\n" +"Language-Team: ru \n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "Клик" + +#: main.py:78 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example" +msgstr "" +"Привет, {user_fist_name}!\n" +"Это пример мультиязычного бота.\n" +"Доступные команды:\n" +"\n" +"/lang - изменить язык\n" +"/plural - пример плюрализации" + +#: main.py:102 +msgid "Language has been changed" +msgstr "Язык был сменён" + +#: main.py:114 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "У вас {number} клик" +msgstr[1] "У вас {number} клика" +msgstr[2] "У вас {number} кликов" + +#: main.py:120 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Это кликер.\n" +"\n" + diff --git a/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po new file mode 100644 index 000000000..e694b7810 --- /dev/null +++ b/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po @@ -0,0 +1,58 @@ +# Uzbek (Latin) translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-18 17:54+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: uz_Latn\n" +"Language-Team: uz_Latn \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "clik" + +#: main.py:78 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example" +msgstr "" +"Salom, {user_fist_name}!\n" +"Bu multilanguage bot misoli.\n" +"Mavjud buyruqlar:\n" +"\n" +"/lang - tilni ozgartirish\n" +"/plural - pluralizatsiya misoli" + +#: main.py:102 +msgid "Language has been changed" +msgstr "Til ozgartirildi" + +#: main.py:114 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "Sizda {number}ta clik" +msgstr[1] "Sizda {number}ta clik" + +#: main.py:120 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Bu clicker.\n" +"\n" + diff --git a/examples/i18n_class_example/main.py b/examples/i18n_class_example/main.py new file mode 100644 index 000000000..aca71c27d --- /dev/null +++ b/examples/i18n_class_example/main.py @@ -0,0 +1,146 @@ +""" +In this example you will learn how to adapt your bot to different languages +Using built-in class I18N. + +You need to install babel package 'https://pypi.org/project/Babel/' +Babel provides a command-line interface for working with message catalogs +After installing babel package you have a script called 'pybabel' +Too see all the commands open terminal and type 'pybabel --help' +Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html' + +Create a directory 'locales' where our translations will be stored + +First we need to extract texts: + pybabel extract -o locales/{domain_name}.pot --input-dirs . +{domain_name}.pot - is the file where all translations are saved +The name of this file should be the same as domain which you pass to I18N class +In this example domain_name will be 'messages' + +For gettext (singular texts) we use '_' alias and it works perfect +You may also you some alias for ngettext (plural texts) but you can face with a problem that +your plural texts are not being extracted +That is because by default 'pybabel extract' recognizes the following keywords: + _, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_ +To add your own keyword you can use '-k' flag +In this example for 'ngettext' i will assign double underscore alias '__' + +Full command with pluralization support will look so: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . + +Then create directories with translations (get list of all locales: 'pybabel --list-locales'): + pybabel init -i locales/{domain_name}.pot -d locales -l en + pybabel init -i locales/{domain_name}.pot -d locales -l ru + pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn + +Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po +After you translated all the texts you need to compile .po files: + pybabel compile -d locales + +When you delete/update your texts you also need to update them in .po files: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . + pybabel update -i locales/{domain_name}.pot -d locales + - translate + pybabel compile -d locales +""" + +from functools import wraps +import keyboards +from telebot import TeleBot, types, custom_filters +from telebot.storage.memory_storage import StateMemoryStorage +from telebot.util import I18N + +storage = StateMemoryStorage() +bot = TeleBot("", state_storage=storage) + +i18n = I18N(translations_path='locales', domain_name='messages') +_ = i18n.gettext # for singular translations +__ = i18n.ngettext # for plural translations + +# These are example storages, do not use it in a production development +users_lang = {} +users_clicks = {} + + +def get_user_language(func): + """ + This decorator will pass to your handler current user's language + """ + @wraps(func) + def inner(*args, **kwargs): + obj = args[0] + kwargs.update(lang=users_lang.get(obj.from_user.id, 'en')) + return func(*args, **kwargs) + + return inner + + +@bot.message_handler(commands='start') +@get_user_language +def start_handler(message: types.Message, lang): + text = _("Hello, {user_fist_name}!\n" + "This is the example of multilanguage bot.\n" + "Available commands:\n\n" + "/lang - change your language\n" + "/plural - pluralization example", lang=lang) + + # remember don't use f string for interpolation, use .format method instead + text = text.format(user_fist_name=message.from_user.first_name) + bot.send_message(message.from_user.id, text) + + +@bot.message_handler(commands='lang') +def change_language_handler(message: types.Message): + bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang", + reply_markup=keyboards.languages_keyboard()) + + +@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(contains=['en', 'ru', 'uz_Latn'])) +def language_handler(call: types.CallbackQuery): + lang = call.data + users_lang[call.from_user.id] = lang + + bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id) + bot.delete_state(call.from_user.id) + + +@bot.message_handler(commands='plural') +@get_user_language +def pluralization_handler(message: types.Message, lang): + if not users_clicks.get(message.from_user.id): + users_clicks[message.from_user.id] = 0 + clicks = users_clicks[message.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks, + lang=lang + ) + text = _("This is clicker.\n\n", lang=lang) + text.format(number=clicks) + bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_, lang)) + + +@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(equals='click')) +@get_user_language +def click_handler(call: types.CallbackQuery, lang): + if not users_clicks.get(call.from_user.id): + users_clicks[call.from_user.id] = 1 + else: + users_clicks[call.from_user.id] += 1 + + clicks = users_clicks[call.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks, + lang=lang + ) + text = _("This is clicker.\n\n", lang=lang) + text.format(number=clicks) + bot.edit_message_text(text, call.from_user.id, call.message.message_id, + reply_markup=keyboards.clicker_keyboard(_, lang)) + + +if __name__ == '__main__': + bot.add_custom_filter(custom_filters.TextMatchFilter()) + bot.infinity_polling() From ae5d183db077bc87179647653def52df3d1a45fb Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 15:53:58 +0500 Subject: [PATCH 0870/1808] slight TextFilter class improvement --- telebot/asyncio_filters.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index cb0120bbf..232870a7d 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -92,39 +92,33 @@ async def check(self, obj: Union[types.Message, types.CallbackQuery, types.Inlin if self.ignore_case: text = text.lower() - - if self.equals: - self.equals = self.equals.lower() - elif self.contains: - self.contains = tuple(map(str.lower, self.contains)) - elif self.starts_with: - self.starts_with = tuple(map(str.lower, self.starts_with)) - elif self.ends_with: - self.ends_with = tuple(map(str.lower, self.ends_with)) + prepare_func = lambda string: str(string).lower() + else: + prepare_func = str if self.equals: - result = self.equals == text + result = prepare_func(self.equals) == text if result: return True elif not result and not any((self.contains, self.starts_with, self.ends_with)): return False if self.contains: - result = any([i in text for i in self.contains]) + result = any([prepare_func(i) in text for i in self.contains]) if result: return True elif not result and not any((self.starts_with, self.ends_with)): return False if self.starts_with: - result = any([text.startswith(i) for i in self.starts_with]) + result = any([text.startswith(prepare_func(i)) for i in self.starts_with]) if result: return True elif not result and not self.ends_with: return False if self.ends_with: - return any([text.endswith(i) for i in self.ends_with]) + return any([text.endswith(prepare_func(i)) for i in self.ends_with]) return False From 5337d4838d1b9d0774cdb8755f76549111944316 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 16:02:14 +0500 Subject: [PATCH 0871/1808] asyncio_middlewares.py was created && BaseMiddleware class was replaced to asyncio_middlewares.py --- .../middleware/flooding_middleware.py | 2 +- examples/asynchronous_telebot/middleware/i18n.py | 3 ++- telebot/asyncio_handler_backends.py | 15 --------------- telebot/asyncio_middlewares.py | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 17 deletions(-) create mode 100644 telebot/asyncio_middlewares.py diff --git a/examples/asynchronous_telebot/middleware/flooding_middleware.py b/examples/asynchronous_telebot/middleware/flooding_middleware.py index b8f589ede..761190d21 100644 --- a/examples/asynchronous_telebot/middleware/flooding_middleware.py +++ b/examples/asynchronous_telebot/middleware/flooding_middleware.py @@ -1,6 +1,6 @@ # Just a little example of middleware handlers -from telebot.asyncio_handler_backends import BaseMiddleware +from telebot.asyncio_middlewares import BaseMiddleware from telebot.async_telebot import AsyncTeleBot from telebot.async_telebot import CancelUpdate bot = AsyncTeleBot('TOKEN') diff --git a/examples/asynchronous_telebot/middleware/i18n.py b/examples/asynchronous_telebot/middleware/i18n.py index 81281bc44..cbbd82bb0 100644 --- a/examples/asynchronous_telebot/middleware/i18n.py +++ b/examples/asynchronous_telebot/middleware/i18n.py @@ -7,6 +7,7 @@ # But this example just to show the work of middlewares. import telebot +import telebot.asyncio_middlewares from telebot.async_telebot import AsyncTeleBot from telebot import asyncio_handler_backends import logging @@ -27,7 +28,7 @@ bot = AsyncTeleBot('TOKEN') -class LanguageMiddleware(asyncio_handler_backends.BaseMiddleware): +class LanguageMiddleware(telebot.asyncio_middlewares.BaseMiddleware): def __init__(self): self.update_types = ['message'] # Update types that will be handled by this middleware. async def pre_process(self, message, data): diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 1784481c4..51ed603a7 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -1,18 +1,3 @@ -class BaseMiddleware: - """ - Base class for middleware. - - Your middlewares should be inherited from this class. - """ - def __init__(self): - pass - - async def pre_process(self, message, data): - raise NotImplementedError - async def post_process(self, message, data, exception): - raise NotImplementedError - - class State: def __init__(self) -> None: self.name = None diff --git a/telebot/asyncio_middlewares.py b/telebot/asyncio_middlewares.py new file mode 100644 index 000000000..cf33280db --- /dev/null +++ b/telebot/asyncio_middlewares.py @@ -0,0 +1,15 @@ +class BaseMiddleware: + """ + Base class for middleware. + + Your middlewares should be inherited from this class. + """ + + def __init__(self): + pass + + async def pre_process(self, message, data): + raise NotImplementedError + + async def post_process(self, message, data, exception): + raise NotImplementedError From 1f6e60fd7433b5200ef947dce4dd48dd89d73a49 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 16:25:46 +0500 Subject: [PATCH 0872/1808] I18N middleware implementation was added --- telebot/asyncio_middlewares.py | 94 ++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/telebot/asyncio_middlewares.py b/telebot/asyncio_middlewares.py index cf33280db..d5d0e4adb 100644 --- a/telebot/asyncio_middlewares.py +++ b/telebot/asyncio_middlewares.py @@ -1,3 +1,15 @@ +import contextvars + +try: + from babel.support import LazyProxy + + babel_imported = True +except ImportError: + babel_imported = False + +from telebot import util + + class BaseMiddleware: """ Base class for middleware. @@ -13,3 +25,85 @@ async def pre_process(self, message, data): async def post_process(self, message, data, exception): raise NotImplementedError + + +class I18N(BaseMiddleware): + """ + This middleware provides high-level tool for internationalization + It is based on gettext util. + """ + + context_lang = contextvars.ContextVar('language', default=None) + + def __init__(self, translations_path, domain_name: str): + super().__init__() + self.update_types = self.process_update_types() + + self.path = translations_path + self.domain = domain_name + self.translations = util.find_translations(self.path, self.domain) + + @property + def available_translations(self): + return list(self.translations) + + def gettext(self, text: str, lang: str = None): + """ + Singular translations + """ + + if lang is None: + lang = self.context_lang.get() + + if lang not in self.translations: + return text + + translator = self.translations[lang] + return translator.gettext(text) + + def ngettext(self, singular: str, plural: str, lang: str = None, n=1): + """ + Plural translations + """ + if lang is None: + lang = self.context_lang.get() + + if lang not in self.translations: + if n == 1: + return singular + return plural + + translator = self.translations[lang] + return translator.ngettext(singular, plural, n) + + def lazy_gettext(self, text: str, lang: str = None): + if not babel_imported: + raise RuntimeError('babel module is not imported. Check that you installed it.') + return LazyProxy(self.gettext, text, lang, enable_cache=False) + + def lazy_ngettext(self, singular: str, plural: str, lang: str = None, n=1): + if not babel_imported: + raise RuntimeError('babel module is not imported. Check that you installed it.') + return LazyProxy(self.ngettext, singular, plural, lang, n, enable_cache=False) + + async def get_user_language(self, obj): + """ + You need to override this method and return user language + """ + raise NotImplementedError + + def process_update_types(self) -> list: + """ + You need to override this method and return any update types which you want to be processed + """ + raise NotImplementedError + + async def pre_process(self, message, data): + """ + context language variable will be set each time when update from 'process_update_types' comes + value is the result of 'get_user_language' method + """ + self.context_lang.set(await self.get_user_language(obj=message)) + + async def post_process(self, message, data, exception): + pass From 93b97fc3feab593c10a838053cf7eddb580d5cb2 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 18:47:36 +0500 Subject: [PATCH 0873/1808] I18N middleware example was added --- .../i18n_middleware_example/keyboards.py | 34 +++ .../locales/en/LC_MESSAGES/messages.po | 81 +++++++ .../locales/ru/LC_MESSAGES/messages.po | 82 +++++++ .../locales/uz_Latn/LC_MESSAGES/messages.po | 80 +++++++ .../i18n_middleware_example/main.py | 208 ++++++++++++++++++ 5 files changed, 485 insertions(+) create mode 100644 examples/asynchronous_telebot/middleware/i18n_middleware_example/keyboards.py create mode 100644 examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/en/LC_MESSAGES/messages.po create mode 100644 examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/ru/LC_MESSAGES/messages.po create mode 100644 examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/uz_Latn/LC_MESSAGES/messages.po create mode 100644 examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/keyboards.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/keyboards.py new file mode 100644 index 000000000..14d0473d0 --- /dev/null +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/keyboards.py @@ -0,0 +1,34 @@ +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton + + +def languages_keyboard(): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text="English", callback_data='en'), + InlineKeyboardButton(text="Русский", callback_data='ru'), + InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn') + ] + ] + ) + + +def clicker_keyboard(_): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text=_("click"), callback_data='click'), + ] + ] + ) + + +def menu_keyboard(_): + keyboard = ReplyKeyboardMarkup(resize_keyboard=True) + keyboard.add( + KeyboardButton(text=_("My user id")), + KeyboardButton(text=_("My user name")), + KeyboardButton(text=_("My first name")) + ) + + return keyboard diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/en/LC_MESSAGES/messages.po b/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/en/LC_MESSAGES/messages.po new file mode 100644 index 000000000..2cf8eef5d --- /dev/null +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/en/LC_MESSAGES/messages.po @@ -0,0 +1,81 @@ +# English translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-19 18:37+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "" + +#: keyboards.py:29 +msgid "My user id" +msgstr "" + +#: keyboards.py:30 +msgid "My user name" +msgstr "" + +#: keyboards.py:31 +msgid "My first name" +msgstr "" + +#: main.py:97 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example\n" +"/menu - text menu example" +msgstr "" + +#: main.py:121 +msgid "Language has been changed" +msgstr "" + +#: main.py:130 main.py:150 +#, fuzzy +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "" +msgstr[1] "" + +#: main.py:135 main.py:155 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" + +#: main.py:163 +msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot." +msgstr "" + +#: main.py:203 +msgid "Seems you confused language" +msgstr "" + +#~ msgid "" +#~ "Hello, {user_fist_name}!\n" +#~ "This is the example of multilanguage bot.\n" +#~ "Available commands:\n" +#~ "\n" +#~ "/lang - change your language\n" +#~ "/plural - pluralization example" +#~ msgstr "" + diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/ru/LC_MESSAGES/messages.po b/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/ru/LC_MESSAGES/messages.po new file mode 100644 index 000000000..6d330b059 --- /dev/null +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/ru/LC_MESSAGES/messages.po @@ -0,0 +1,82 @@ +# Russian translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-19 18:37+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: ru\n" +"Language-Team: ru \n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "Клик" + +#: keyboards.py:29 +msgid "My user id" +msgstr "Мой user id" + +#: keyboards.py:30 +msgid "My user name" +msgstr "Мой user name" + +#: keyboards.py:31 +msgid "My first name" +msgstr "Мой first name" + +#: main.py:97 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example\n" +"/menu - text menu example" +msgstr "" +"Привет, {user_fist_name}!\n" +"Это пример мультиязычного бота.\n" +"Доступные команды:\n" +"\n" +"/lang - изменить язык\n" +"/plural - пример плюрализации\n" +"/menu - Пример текстового меню" + +#: main.py:121 +msgid "Language has been changed" +msgstr "Язык был сменён" + +#: main.py:130 main.py:150 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "У вас {number} клик" +msgstr[1] "У вас {number} клика" +msgstr[2] "У вас {number} кликов" + +#: main.py:135 main.py:155 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Это кликер.\n" +"\n" + +#: main.py:163 +msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot." +msgstr "Это пример ReplyKeyboardMarkup меню в мультиязычном боте." + +#: main.py:203 +msgid "Seems you confused language" +msgstr "Кажется, вы перепутали язык" + diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/uz_Latn/LC_MESSAGES/messages.po b/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/uz_Latn/LC_MESSAGES/messages.po new file mode 100644 index 000000000..c5bd0004e --- /dev/null +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/locales/uz_Latn/LC_MESSAGES/messages.po @@ -0,0 +1,80 @@ +# Uzbek (Latin) translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-19 18:37+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: uz_Latn\n" +"Language-Team: uz_Latn \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "clik" + +#: keyboards.py:29 +msgid "My user id" +msgstr "Mani user id" + +#: keyboards.py:30 +msgid "My user name" +msgstr "Mani user name" + +#: keyboards.py:31 +msgid "My first name" +msgstr "Mani first name" + +#: main.py:97 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example\n" +"/menu - text menu example" +msgstr "" +"Salom, {user_fist_name}!\n" +"Bu multilanguage bot misoli.\n" +"Mavjud buyruqlar:\n" +"\n" +"/lang - tilni ozgartirish\n" +"/plural - pluralizatsiya misoli\n" +"/menu - text menu misoli" + +#: main.py:121 +msgid "Language has been changed" +msgstr "Til ozgartirildi" + +#: main.py:130 main.py:150 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "Sizda {number}ta clik" +msgstr[1] "Sizda {number}ta clik" + +#: main.py:135 main.py:155 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Bu clicker.\n" +"\n" + +#: main.py:163 +msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot." +msgstr "Bu multilanguage bot da replykeyboardmarkup menyu misoli." + +#: main.py:203 +msgid "Seems you confused language" +msgstr "Tilni adashtirdiz" + diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py new file mode 100644 index 000000000..2cee2c711 --- /dev/null +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py @@ -0,0 +1,208 @@ +""" +In this example you will learn how to adapt your bot to different languages +Using built-in middleware I18N. + +You need to install babel package 'https://pypi.org/project/Babel/' +Babel provides a command-line interface for working with message catalogs +After installing babel package you have a script called 'pybabel' +Too see all the commands open terminal and type 'pybabel --help' +Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html' + +Create a directory 'locales' where our translations will be stored + +First we need to extract texts: + pybabel extract -o locales/{domain_name}.pot --input-dirs . +{domain_name}.pot - is the file where all translations are saved +The name of this file should be the same as domain which you pass to I18N class +In this example domain_name will be 'messages' + +For gettext (singular texts) we use '_' alias and it works perfect +You may also you some alias for ngettext (plural texts) but you can face with a problem that +your plural texts are not being extracted +That is because by default 'pybabel extract' recognizes the following keywords: + _, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_ +To add your own keyword you can use '-k' flag +In this example for 'ngettext' i will assign double underscore alias '__' + +Full command with pluralization support will look so: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . + +Then create directories with translations (get list of all locales: 'pybabel --list-locales'): + pybabel init -i locales/{domain_name}.pot -d locales -l en + pybabel init -i locales/{domain_name}.pot -d locales -l ru + pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn + +Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po +After you translated all the texts you need to compile .po files: + pybabel compile -d locales + +When you delete/update your texts you also need to update them in .po files: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . + pybabel update -i locales/{domain_name}.pot -d locales + - translate + pybabel compile -d locales +""" + +import asyncio +from typing import Union + +import keyboards +from telebot import types +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_filters import TextMatchFilter, TextFilter +from telebot.asyncio_middlewares import I18N +from telebot.asyncio_storage.memory_storage import StateMemoryStorage + + +class I18NMiddleware(I18N): + + def process_update_types(self) -> list: + """ + Here you need to return a list of update types which you want to be processed + """ + return ['message', 'callback_query'] + + async def get_user_language(self, obj: Union[types.Message, types.CallbackQuery]): + """ + This method is called when new update comes (only updates which you return in 'process_update_types' method) + Returned language will be used in 'pre_process' method of parent class + Returned language will be set to context language variable. + If you need to get translation with user's actual language you don't have to pass it manually + It will be automatically passed from context language value. + However if you need some other language you can always pass it. + """ + + user_id = obj.from_user.id + + if user_id not in users_lang: + users_lang[user_id] = 'en' + + return users_lang[user_id] + + +storage = StateMemoryStorage() +bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds", state_storage=storage) + +i18n = I18NMiddleware(translations_path='locales', domain_name='messages') +_ = i18n.gettext # for singular translations +__ = i18n.ngettext # for plural translations + +# These are example storages, do not use it in a production development +users_lang = {} +users_clicks = {} + + +@bot.message_handler(commands='start') +async def start_handler(message: types.Message): + text = _("Hello, {user_fist_name}!\n" + "This is the example of multilanguage bot.\n" + "Available commands:\n\n" + "/lang - change your language\n" + "/plural - pluralization example\n" + "/menu - text menu example") + + # remember don't use f string for interpolation, use .format method instead + text = text.format(user_fist_name=message.from_user.first_name) + await bot.send_message(message.from_user.id, text) + + +@bot.message_handler(commands='lang') +async def change_language_handler(message: types.Message): + await bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang", + reply_markup=keyboards.languages_keyboard()) + + +@bot.callback_query_handler(func=None, text=TextFilter(contains=['en', 'ru', 'uz_Latn'])) +async def language_handler(call: types.CallbackQuery): + lang = call.data + users_lang[call.from_user.id] = lang + + # When you changed user language, you have to pass it manually beacause it is not changed in context + await bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id) + + +@bot.message_handler(commands='plural') +async def pluralization_handler(message: types.Message): + if not users_clicks.get(message.from_user.id): + users_clicks[message.from_user.id] = 0 + clicks = users_clicks[message.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks + ) + text = _("This is clicker.\n\n") + text.format(number=clicks) + + await bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_)) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='click')) +async def click_handler(call: types.CallbackQuery): + if not users_clicks.get(call.from_user.id): + users_clicks[call.from_user.id] = 1 + else: + users_clicks[call.from_user.id] += 1 + + clicks = users_clicks[call.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks + ) + text = _("This is clicker.\n\n") + text.format(number=clicks) + + await bot.edit_message_text(text, call.from_user.id, call.message.message_id, + reply_markup=keyboards.clicker_keyboard(_)) + + +@bot.message_handler(commands='menu') +async def menu_handler(message: types.Message): + text = _("This is ReplyKeyboardMarkup menu example in multilanguage bot.") + await bot.send_message(message.chat.id, text, reply_markup=keyboards.menu_keyboard(_)) + + +# For lazy tranlations +# lazy gettext is used when you don't know user's locale +# It can be used for example to handle text buttons in multilanguage bot +# The actual translation will be delayed until update comes and context language is set +l_ = i18n.lazy_gettext + + +# Handlers below will handle text according to user's language +@bot.message_handler(text=l_("My user id")) +async def return_user_id(message: types.Message): + await bot.send_message(message.chat.id, str(message.from_user.id)) + + +@bot.message_handler(text=l_("My user name")) +async def return_user_id(message: types.Message): + username = message.from_user.username + if not username: + username = '-' + await bot.send_message(message.chat.id, username) + + +# You can make it case insensitive +@bot.message_handler(text=TextFilter(equals=l_("My first name"), ignore_case=True)) +async def return_user_id(message: types.Message): + await bot.send_message(message.chat.id, message.from_user.first_name) + + +all_menu_texts = [] +for language in i18n.available_translations: + for menu_text in ("My user id", "My user name", "My first name"): + all_menu_texts.append(_(menu_text, language)) + + +# When user confused language. (handles all menu buttons texts) +@bot.message_handler(text=TextFilter(contains=all_menu_texts, ignore_case=True)) +async def missed_message(message: types.Message): + await bot.send_message(message.chat.id, _("Seems you confused language"), reply_markup=keyboards.menu_keyboard(_)) + + +if __name__ == '__main__': + bot.middlewares.append(i18n) + bot.add_custom_filter(TextMatchFilter()) + asyncio.run(bot.infinity_polling()) From 10b5886dcc20b2beb5ff498726bf85e79d03ea0c Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 18:56:27 +0500 Subject: [PATCH 0874/1808] Completed I18N examples descriptions --- .../middleware/i18n_middleware_example/main.py | 8 +++++++- examples/i18n_class_example/main.py | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py index 2cee2c711..34cfc0194 100644 --- a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py @@ -41,6 +41,12 @@ pybabel update -i locales/{domain_name}.pot -d locales - translate pybabel compile -d locales + +If you have any exceptions check: + - you have installed babel + - translations are ready, so you just compiled it + - in the commands above you replaced {domain_name} to messages + - you are writing commands from correct path in terminal """ import asyncio @@ -81,7 +87,7 @@ async def get_user_language(self, obj: Union[types.Message, types.CallbackQuery] storage = StateMemoryStorage() -bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds", state_storage=storage) +bot = AsyncTeleBot("", state_storage=storage) i18n = I18NMiddleware(translations_path='locales', domain_name='messages') _ = i18n.gettext # for singular translations diff --git a/examples/i18n_class_example/main.py b/examples/i18n_class_example/main.py index aca71c27d..70718473a 100644 --- a/examples/i18n_class_example/main.py +++ b/examples/i18n_class_example/main.py @@ -41,6 +41,12 @@ pybabel update -i locales/{domain_name}.pot -d locales - translate pybabel compile -d locales + +If you have any exceptions check: + - you have installed babel + - translations are ready, so you just compiled it + - in the commands above you replaced {domain_name} to messages + - you are writing commands from correct path in terminal """ from functools import wraps From 30c43b557c4efe6770d125df210c2a1f7316fbb4 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 19 Feb 2022 21:56:51 +0500 Subject: [PATCH 0875/1808] Documentation Bump --- .gitignore | 5 +++ docs/Makefile | 20 +++++++++ docs/source/async_version/index.rst | 51 +++++++++++++++++++++ docs/source/calldata.rst | 13 ++++++ docs/source/conf.py | 66 ++++++++++++++++++++++++++++ docs/source/index.rst | 36 +++++++++++++++ docs/source/install.rst | 41 +++++++++++++++++ docs/source/logo.png | Bin 0 -> 58063 bytes docs/source/quick_start.rst | 16 +++++++ docs/source/sync_version/index.rst | 43 ++++++++++++++++++ docs/source/types.rst | 10 +++++ docs/source/util.rst | 12 +++++ 12 files changed, 313 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/source/async_version/index.rst create mode 100644 docs/source/calldata.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst create mode 100644 docs/source/install.rst create mode 100644 docs/source/logo.png create mode 100644 docs/source/quick_start.rst create mode 100644 docs/source/sync_version/index.rst create mode 100644 docs/source/types.rst create mode 100644 docs/source/util.rst diff --git a/.gitignore b/.gitignore index e2bc744b6..721966301 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,8 @@ testMain.py #VS Code .vscode/ .DS_Store + +# documentation +_build/ +_static/ +_templates/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..d0c3cbf10 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/source/async_version/index.rst b/docs/source/async_version/index.rst new file mode 100644 index 000000000..6b53c6ae7 --- /dev/null +++ b/docs/source/async_version/index.rst @@ -0,0 +1,51 @@ +==================== +AsyncTeleBot +==================== + +AsyncTeleBot methods +-------------------- + +.. automodule:: telebot.async_telebot + :members: + :undoc-members: + :show-inheritance: + + + +Asyncio filters +--------------- + +.. automodule:: telebot.asyncio_filters + :members: + :undoc-members: + :show-inheritance: + +Asynchronous storage for states +------------------------------- + +.. automodule:: telebot.asyncio_storage + :members: + :undoc-members: + :show-inheritance: + +asyncio_helper file +------------------- + +.. automodule:: telebot.asyncio_helper + :members: + :undoc-members: + :show-inheritance: + + +Asyncio handler backends +------------------------ + + + +.. automodule:: telebot.asyncio_handler_backends + :members: + :undoc-members: + :show-inheritance: + + + diff --git a/docs/source/calldata.rst b/docs/source/calldata.rst new file mode 100644 index 000000000..4919c9d46 --- /dev/null +++ b/docs/source/calldata.rst @@ -0,0 +1,13 @@ + +===================== +Callback data factory +===================== + + +callback\_data file +----------------------------- + +.. automodule:: telebot.callback_data + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 000000000..8a9f7395e --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,66 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'pyTelegramBotAPI' +copyright = '2022, coder2020official' +author = 'coder2020official' + +# The full version, including alpha/beta/rc tags +release = '1.0' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx_rtd_theme', + 'sphinx.ext.autosectionlabel', + 'sphinx.ext.autodoc', + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] +html_logo = 'logo.png' +html_theme_options = { + 'logo_only': True, + 'display_version': False, +} diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 000000000..a7ad1e19e --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,36 @@ +.. pyTelegramBotAPI documentation master file, created by + sphinx-quickstart on Fri Feb 18 20:58:37 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + + +Welcome to pyTelegramBotAPI's documentation! +============================================ + +======= +TeleBot +======= + +Contents +-------- +.. toctree:: + + install + quick_start + types + sync_version/index + async_version/index + calldata + util + + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + + \ No newline at end of file diff --git a/docs/source/install.rst b/docs/source/install.rst new file mode 100644 index 000000000..f195a43f8 --- /dev/null +++ b/docs/source/install.rst @@ -0,0 +1,41 @@ +================== +Installation Guide +================== + + +:toctree + +Using PIP +---------- +.. code-block:: bash + + $ pip install pyTelegramBotAPI + +Using pipenv +------------ +.. code-block:: bash + + $ pipenv install pyTelegramBotAPI + +By cloning repository +--------------------- +.. code-block:: bash + + $ git clone https://github.com/eternnoir/pyTelegramBotAPI.git + $ cd pyTelegramBotAPI + $ python setup.py install + +Directly using pip +------------------ +.. code-block:: bash + $ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git + +It is generally recommended to use the first option. + +While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling: + +.. code-block:: bash + + $ pip install pytelegrambotapi --upgrade + + diff --git a/docs/source/logo.png b/docs/source/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b80632a1e01599699b5325b8676e841de25c879f GIT binary patch literal 58063 zcmXt9bx@n#(@k(|(PBl41&3h8DNv-iyE_z$26t)S;*^BqQYc>BJy6`WxCgi54qy74 z`Todc=9$TT?%ut-d(PQSq`Ip73mi%u008hp;e(7O003n|Yh)^{fo- zFU*!7G<$W1g}nG?DHU}=-%B=;20?vy9tZCU z(d5A>omeSxfP8*3Nld^IuoiV(?$!bQ4ZfrjfD=%8UY(S!3>X1g1I%|KsXhZrfZD(! z^`Jo@421=#j+cW45OwJ|tHGC)2x*HT@G^BpV<3nD*r9y@vvp$qJit;{q#(G&7$=s* z2P^|2sN=6B?Y5Zvm^ZaB%#+V*P~l#HMSv4}B|01=jfFb7h9&prNbU`wxz>Mc?-~x9 zlmK7=gs>!o0gr$Soe<7Uj0oT=2H*hr3-vE%NL@ri>>fW>XHCH>7Mf&o(21B$c8ccU zaMXV&Kw?D zKm5*%B!mG-1zd9lv`u>OZ(L2M10PP+XM0d z5M@`gu*Jw&I1S((Fa^{6C7pHZoK2@eCl;eK8cL4Rl0K$)6fX))lk`!D2JSYf25@@H z;K92I(hdrG6XA5WV};hHnoo!o!j-gA5TEr~b8ve_0idcKq(A<__2&v!<(uRaShfo` z9seZ_j}LuCyd(f(8vLGh`kf-Hl|v!hyF#>y1l9}p9-7ax-a#+SG4AOiz*l5comkz0 z$oEIz6BJ&*fKk;?0V}hs_QWYWr(VmMzYQrqWbWU>-If!RM>@zS&N;2VEM##WFshl$ zI0a!0>#OHM;fOl-0A!=**jY`7X?^}@{SONq)oMzhBI{JR7%jXB5LYMi?&jJYEjI>k z41fXyq_O5-$7q95s^XBFtco*FGgD)2!-9W~a^M}2XYhFtiUII_3X;LP1;6}+sZucT z&(R?6g^|9Wu{7WAJ1L$0yuI&K~* zn|JHK0q1;#TTU$wdj1RxM@4UYqT(TUS#M}H8wpH1uPB!iohb?VTuMRq1(t<5MrHh0 zskDdmu#kvgElLSBk?hCR-Qa_A!CGQB@t;>`)#*PxRXu7)2NHsJ-paqedTtt-D7OKL zY}@=WGSU@hVZsN=UvVLwQF$iYNf;wJ{m;cIZ#3jXGzYC4%C&TkeFqAF`KA2Px#^>Eg9&6Yzy;@vCb}#i2U?z<6KTPvNy3`$8~L75{hf@&34( zJcLSoVJlZDo$BIv4kb{uzB|YH(|DW7AmEMvS;M><{4DG<#{$;R?ygs@meZ#ySpD-((N*|2yEyX|up)dk3`&9k2l#Fhyx)==(zAID5z&>5WOl$W_~nE|7%q%%(Eq`vf`&95Any&0W=QHuLFCSMnh;i z`qRYSG)Eq8AI9p18tshmjwVRO7ix0%+QnW1e6Zn;5d^wl-y-8S;ca;5w`~SVRvQ~< zXMk(c>X+PKC8Y1lmtqary)B;Z9zK|*jxyVVmE>-wHPZ(uQgbfK8Nljt_w`K_Lbcxh zcn7nmgRz+wo zW;2nCj%K=D=&-{y3k=(o?X0}4MxKodCzBb1vN-yYp2 zs!)H{V$f3K0>sD|f86iJ_RP%yb}gwS-x;g2+C(k~W4bod-VNOedEBZ7JRI2mTGB0w zko`z{9Wrowe#&unXKCYZF+TO#S5cy^=D+LC+dRyi!j3TgAk4qq(pX9|_;DovZiME| zmZoCr72*>h?rg{^sLuZNlc;w%Y;jwTB@m9_7=|rH{s2o)wMr28_IqgtE^Iz%U|Jk@ zEAxdG@kt{%i_8|GzR(Fk1~f4(`)Y;)YbIdrB_Qu{a__Pw`g9G!G}l--{YHQY zEbQ;={>?Y^)Y`qILi;V_fAK+?=dOze@*4@JM~}Sd2Jd!&K8Xap%W9b~?>>s70LKmD zzqyR&z^W0)3IOu)+YNImP(@G&X*TCvt{Zw!8p)?bG#bw4$sZZX_fgDY62c=68C1aA z<@BE~``5ZfGx&rbAG=V+pT>_HH#02k*DF4#U_j!DrS*yYTz|s@ z`7T-Z`?;Td_R?oq%1Y|=VLC_sKJt2Ol(30fb{!_7a!Z^OKc1t(xsh6svTEiF<5cjL zlqX+rNF1SMmfx38*6u$F_-)5@n?_F8dKh?q1v9S>tK{b?tDO@{_xp8titSbeAy4T1 zG$sdT3v48VX9oGjl%$D>UG=yJG&tG)EU;ES(*;7$aiDxGCQ8d9-rB(rHeD9>E5mxM z*uM_c06r-Mn(Kup9J`J_{}$SF%7K~UuLS|9A1|=(l}cR?T5v0!8|X!7XIe?Y!p?Q5 z-*y%8!NN3io=!=hnajz;fhK{4;08>1ctnEU<3$^KU=p&rh-8nTDen1Pb>3Ml5TAcS7=5b(lqRgb^Kts#I9_m9W z-QfYWdFa9GnpisMuhq^}-57BE7BQlwNa5k%9q$Ko`f69P5Q_=o2D0mZ{A7xoVqC%_ zYziN4Ba4Ga@eG+r<=tODb5t6Ul9Ad8(L_JL}11B;tWo@G6>P z+v)PMCDX5trdEGwfU!LhtXXIiNrZSqpLZ3r)JFL)r7{Iz8(*;hTR%H}{~*$1(2@FO zn^C&IA}Hvq_OU+zLKD~mn`8zq9Zwu%dq$&Y8K0gl!I{ZY$8Eu@?zQZ~TvcCjHfj2y zo1y8$OWNl(y?E42Oq*%YFB|*NmxO70G!lE+5d@%V0xFu<$bb{Ol4i*-zH?%p+XZN)g@ra8gD*UTT$f9=VgrZe}69ACaJV0$~mGCjw+*u88Mf-~q+} zyGNnN-AkXT?}yx0YHCnG%!HuptziiF*Vh{rV1cJd-bwYmC?il$OJ^*E#&W1>t*3kLd29o4f zA<^=Mq*lbb42Q=(xawW7jEk}G@f(DV-JD-Semd^oi)kBVVwAytz)CTeA+baI zHXQ~99_7DVh%cW`qT)5JnCGXO>0mHzUY9XCnPX<=8yUV2n4E>}*#aQg0ZFjQP{7lR zwMMj+w-ya0zlwi5s0GAFrHKR)LGUJ$M$!nx{3gILl;r8PJ3p^?tgZ%^2kFbuMPCpj z!_Q|lh16^-RnM2#;wmNM?dsWHX`cn`olV!T_9j~3k_?uCm8a$3lj9g9P&|&wvtfr# zvIAT3;54tQoaRNDm7fDeX>Og04}jz&X>YqqXJev17S!_Ls0Fp7nQ_cExw zR@~6|3|-Bi6vfD7W*y|NB`?`C6`@VuC*}il^oU(8y1y z?~QY#WT85*^yI^s1U{lp1v)Dl$V z;y~nrMM5RB_`Y2KHX)0FJU;sW>g;i1P{y+82xoZ7KmahI!blQOU~R*JzuwctDMmsc zX0fx_ti+yvM05RUFJe*k>(px$Tdvhme~BATyml!FQ@m#Bj8oFJxhDOGK&9r9Pz$m= zf9QhOaHn{wfz`0zMq=X#5MN*z13@W`RT@P=#r2P5aN!rBxR3Es6yybtzrm6udl&Fvx)TFK34^ou# zfn_&&iOIdZusIT8_P9ou?&nnH!Yk+bN7sxqs9}>dfD{kkjOdZOTvpQh;5{V|#0<9_ zs1Gf)hL>{lzx&ifyMTDLpp_824#VDWCZvKDpV4EiP`xGA*JU`IX1~H5l4eOY0Fy*ZY+kJsqhat!WcgWW~k5#}Ap?ST)&K<4vOoy4G%U z{elRu+JS088{%mf(Bq9E+Do)n{!$d9{&z;V5?L1mrx|{c8e$4>p;~lR-hJP1*5dd! zCCJ3QP<6*I$08D3#Qt!bc`*Y-$V}5U$&*sqr|!r-o*~+^Gv_u9keohbr19p7j){=i zQ6-Tx-A~b`hdv}Kwz)@1=E%SiG3nuRf$oN&!pD@LfDKD)TYicu( z4_=lsiv7av=I|G%;m0X;t=Y9AM2zVdT%Y5y<3f^@%X{vMvCdo-bgCa#mpD*BOyeB_ znMScK*-1maUDCgXH`hm=I$j=ag|2e->X895e*=HQ>e2qvgxrWGQeE*(B*zAtidS+YZI;TWZ%IhWNs7n#f{rhsFtS`^5Who-&2zM%ZGu-z;XfhrMjqX?zPXHo31?9F1pBp z%;vpIVf|KzY`Hf~RSFZfKytcrtBqrPXT4D z!TUO)ss%TqFGEyaf3f640b(E@Kf^2#Pji}xiejiI#D?+5x6b2SjX>A);>aqz_>L1L zumaeM@K5DQLf#Aor)QUovTB{$SxJd|CvVV1lANnvV8O7H?}=>QKKeiH zh62Y^)oz}Nq|045{XfjSMakjwtwEM?KmkCALC36paZKR2bFb)E`BcBdyd5%1guFg5 zivJ-H)=K^yH8sV&^HW>P=gC;e7X0+xl-JyA=P?;DN*6J` z*aoH^#Se))|IV7vk$YC7U4}+e5h?!k1G(i%utCKv@u(fFY{T<_8e zy6E}B-}50!1w{dBK5Sqs_~n~2ZR_P5wv@eUX-h~IkMPmuqv+Q9p=I(^+th_gUEIyoTP(BP46CqoICsIIWzhET*ZV-`RLeE?{Bn%vP zSmDpF6$IF#kSB5x!$)?+{+VJGGl~u>L~ofu6!&W6;MjJ zqI-LwXB*0w)`BW$=fB{B>PkdsU3SE-^*0Q#Yh4HJ1n6r^1KfF(&xmQ#;wDQBp&O-^ z62gy`zYI87$J=c@#C+DlRDVj#@=PT}Jy|85eBZA@HyzCVU<1YJ1?S-xD9j#Nq07Y) z2|B7=s`-wSG`mQ_Pgz(T&@@*lfZAMLvfw&x!}2mzD_J#RfCAc>%6)X0=f7T{_2o@V zlm7g7ru3`zbu}`J*-+l>8)UY$tHm-Fdi!@m_(7UtPUTGm*u!>##^D772=(w@$-eoE z2h;xZHJwu>_m!t@Db}|D3jTl0B=5eyZ&xaFqRh zM9`y^;M*fk1*n)X6p7w-#UdFKA0!JxCVDQ)3dC!ajB;=T+<5|pmZH@Hd_)x49WQe% ze}QRGp;KQq_D@_Kyw0OH6yS*AfEKbi8()Z&pA??iC&T;0NupiYuHttOq8o(ejeAUT z*2P~4i5@R)=3CxutNNJGfdP%gj7hGS(tos=)wn!UGn!?;o$ZdsW&4CgXNM-ITbg?t zi^y3OP>-|vx^p!1exP%`h2rsvcn7)zp<1Telrnm0pAxNUO^`J+pn6!ZFPCSEZH%id zL{&q~;td&Nc+1}&@^qowZxk$S71;6ulq27>tleAf$9D}gn|D+{B2%iJd&!jpeAoEV z+(1(pwWU@=UEy_(RxNu6zI7Y&AtaV($|e$#HwA8nGxxn$2`5lWV77BU{{ElZU>>e= zvU5=Du&0M~v%OUT@pvF17JtiarcAx(AOoMezG+#pyj>BP=0{5I7&N*Fvyiu|@t+U- zlnST(-}$1R#CheLe*3cNF*9?IiYOX_L-x+d9+>D5%E^lt)*cQ(h*WWA_i&pQO=?^& z&gThqWiE5~g=vDtYS%lQOJH8~qF@FoQ;;UjZdh)y+r1<`rNrd~g^+rI4P>EbP)2K# zQH=|kZ;>n-ABt;)h9XBcdm^+I-B7?kuA#^b^x-JiVzd|IM=2B&L6x559QnVyclQ5)sW|2_FI#Qw*e&gWC=YGcXP8s>V~h%!8A7rpVIp3yFS1%TS`&o!)U;7S>w+Qa$O(#+5E6a39o~|N>;9X9x{XL- zkISG}DAy?&Y?x%==7=&5Wwg74h-FSGK}F%5JQxjLh|aXNPFFZXCpaHLqC zOh#`XR(`9>d#fUg>P-hK+}D+t2_v@+3)Sc?=51`-RXk@|4w+y&d~e(2*+(R;%Xk=v z-Ke1mq|UEp3h@Py&!NkQ$%g=!Lp5LI;ihDqVOWB-26vf%=!x(OM^0$qC>-$qonYFJ zwtXX*MxdEd^t$(6q8I~W1{C642`8-`qYa&+Ne@NY&Wmnl@yMm&L}l^`<mb#NZx$I%FN2lA-}!L zC-)p7-7WXJXUF15amLq;7pq6_+Xzi5FhDbDDbB-ETPmzSSnUUTRFMaGA%BT{jnE8e>X%C&}lyzaf6WN zbxaHSK_JA6H}t0ORpda`&cNRAGU1!S0LXi!)5F@WMn9?2MFc=w7yE+j%utc1j2UtZ z5b2d5k;1KtgYU%$$377C?kXq7Zx);EeH|$N!dEuW6Bb7Kr3YT=W2;11iF86)fRX~m zRt8~}>cpC#9YZB1+*P_?*Djg)nXLnL86VKg6igmYQ%U&M8p3l^gaRs*F)$>ls>4H6 zEkGEf23TzsjVOyO&OP9}iS50^^bs*?e%w@E@CHdTv}~b(%}$#viqP}y2b{$-CA?6M zP_;NRI1yMxP1$G_qwt0ZdBA!=pDE%BiCq~m%1imR2j&9H#r8>LIbm92CVLHKPz+|W zfgvN>wV=swekOuoRe}BPCGuzT0R5syYvaIhDJx{x#T{)l*qyC@E7`r4TcG`Y-TNiQ zZ5+9Mk)y;7g~VKT-%w?|Lln8>{#1%fQ>QR$SuBIYR2pnZcf4c+767#c*|mbtGUV_! zPsfx){u?Mxpha#`<+3LtfkAwRA$s%%2l>Vdl*UmtRH_RBa$pT%0)M2?n3WbLF^OSA(-6!#S9zq_~eyG)ff6w z#{0|Ca^lZoK&zzqJ;Z$38C6{Y@ZAj|!c0Mv7i(bYe##vc#%;w8z%mD*kID8!SPL3M zLdw6rVi)qp%e8Z;oFRr&GKLRXxXAe!!6qPE!-ck?_y~fr9bW%`6%`-YID5Q{xr52D zc1eiE*Bml1pqxksr^n34;cH$HmmI+N6Lsbw?$j(-acFhpN)RBoU*Z1Va(lV(A2!{! zZ_{}e2GWRAK>PI>03#4r4!(z#aebrh#68Z7T#KTV;saQom5ZFcf0yk$Q1p1CgNVld zbQFQ@bTC?z5IVGr%YX3F`U=xM9++0|AtL(#S;me?NHsmGvXTp)+j0`7b~|0XV1 z45o=V&1r?DHTyNawUfrj_2q83>mtUP$WJw3iENy~AM!f9-t5E=k_vMByyU)ytQwG? zX`HDp>Lv2tqaTJE02mO_lkXInOkY_gRGuQ;`G2wO%CgVE`0(sKMG`zb9d zLh1ds8!S_dW-{PIPjvYAU5O*EUvM?Y>ougPIj5`sCDvb2T|=voHQZJ+g8!%5FHVC^ z#tv30^xZZ#G#)hbl<&P0u=aHfR~A0_M zl374xUAn}*eU^KMali$Wm;9g7cWoF6ulZ_EBZ|ANI1aMN}C`{&^8s~J!I z)?&%K+uQiIxvCChR@=eS+A`^Qot{rG=T5;WUlQ5di;(abCsf1;COe>~gJlWuj%Upx z&tv~h@FV*_P4Q+AYLv|Gl(G`dzuVy!8J@T?UMqh`Yrrn`614XIbmKyzI^Q`jg-019 z0`}_uJEKC@IzHepwDCp9@K+~K9CHcr+@wxDzmi&#h(H@X?>&F4b}5en65C7gXntGL zf}2>F+$KfmuwlTp$posB{S`nrdB_;aB zRWx%fx}Ut?1nZiU$wH590D}6SVmeQ4wbi7w&(+vwTnkZQi6xb#y;6Y!&RI|!Svwkr zT1AOJ9ACO*ML5X|!*^yh{oO+- ze$YZlRGQ!Ws>zUny}sp-@?|_qvPw}%_3k7kPx-n z1d3|y5B}U82dPSpd!Brc|DOfG7=L2R8A&}gb|;#@l9BGOm01BVw6$VDVy>>v2G_bv zX*y&5}M=p!!0S$2G!g-MWYDtps#Lz@iE1cm|Rwh*CzsOWWK0KM~IM6CH8Lu0Q z*&wdFAo_=O`Ao@&Y<%HzevfzX|Cpt zlAN}=ophzij9CCt@aSqlUj}-sre^%<8(wkTev{LeS4@!Pi$}+}=h&hb;`}y*CkTaW zqFs0P&t|FvJCEl?{Rda1BzYmrqFa;6{SW3sp>3Col8X`|@)E)tt~F;A=N#xL%Bo~h zy^M$VfVM0Q%AK!Vf~I0FT>lpJpG0PyK_Wb}zZeP&`k~ZS5Ni7$j++~aD%-z>x zOMT!faD+@sM$osbkFQWPvNKHY6W0PJBHXC+qBTR8zi&hW_+si=qbry7Wp6)9esi$9 zvNlhCb>I#gK5(InVDyYtV?)N2D2hJj8^Kf;I+| zn_b;@N%jRmyxnTeOprRklz!|7R`7&G?02=k^QMxW8e>rmuP5;p`UvC9U&qWZkjZEh z=%ahIu!n2Vq+;tXP)7yYdBZAF9K|f+5u;GeJH}x(_onY1EUST`ffwIxrE+U@)#gZ1 z$C*?5TW{V((nmlNs(}z+53v=5Y1Uz=gp8^$m+OdEgbW_r;7^zs?SUkKp*fW5zcEU- z+$05|gLtZBzq(Dm#pn?Kb+2f)8}Wu7GXwe87p6`wYIg<+wi5BmOrR91YA7lSGYyL|J;vss8r|{F}o$n8t#<%-o+PbqX*&+$M zVok`>_Ei>+(AKF@?{4Q=9sNn<_e)vT^X-Ba#9M@=ih;m2kpon?9P`aqGFvZXTYRGq z*3+{58jG%?_a4-HMf?(ioD`Ah{zz6O34;mY43)agD|u+B8zz}bAlLr9<02ODkEgwj z{Y8_RgGL_R3?t9{FV&bo8pVZ%BG7x}9uR1Dey}irqIpLk4U>+E^o_9wEN=qlzO&oJ zv74nv3y4Mq_97rxqYxw?#lSDsF?oM_=UX7F^ck&!L23Vk zM)D4hj|lnrUM-i&2cEBb`yokcqxQP9Vdi}YAznN8o-YE!2rRK;V^8uJNsu?;5e5mn zkSFU#x6^Q&Y~mxF=E-;4f>z7Eu%ISGDnv4A5sEb5U# z-S1R5n^ik0>G>logMA`NH5Ve>qw#9IYHU{%HsCOl#VLm(P7`&_OIC5U$`oNtQ_&0E z&O_#(7S!lk<;d`vZ!K$01}q$e$Yv?7S~ivnJ2>0@01C3aBt>Nb?fqn;)bkKCXeCP{3{hMT$%d zGxruT4Cl?m9DhEN;qX0e?1LZ$VJXUM20BT3RvW-hDt$mz99t=KA54u(j}F<}nJe$M zk>k^_}u4n_*YR_9S_odU&}T;Ep+6~)IP{gG;_ z*^7kEF-+ffcphJv(G(I&7xcC4M4~h|0p$AbEr@2Gnx4}lGcnnOgmX5edm^>Ouv3hY znl;l8s*rS>tc(^3VTKPTV6P&i%){3MS5sCMl~fT0qOK@SpY2=)ZWBt!dA4X+`MY6? zD6&R}J_@biCl+13d-NpEO08D}1A1hDU1a}6wKz4bS|G6o3XO~+Ueh+zBR6y(aF~dU zp-%SZb@OU2L0zZ8W=lQ7LJbJw`Zd@*^Z<7Hm zqX6W4&gqC|yxe48kf*bkmGEg}A$grRaz>29IJz7G)>}mkpi2lp@neNY9p(}9n>fek z#t>CZ16$H6CeSistIfW7>EC)xWhKwk?k2l-5;lBa`5cj8;VjVW$Xt$eKpQ#y{xl#& zrv7nbw)FH#?KK*TI+6-qKBD=$pAJHm0zrkN4EHgkh00tqRKmritv#x^UV&y9)fkNK z3XpTtWCCdk*P6}<9wa)p1pL=mdLHQ{*wIK-_m6z*pb} z$(PpQf7QFyQPs0szg)bqtTMRRgdX-1t$o?n-SFJI6dK~}&FF4`T)Tr%YZ&Gdf2?+)EfV9`ZY`r zP3QKvM$MJdXavu6oQt41Ql{EVC?WfLx63HEL*K~6>-i=v|2-O-=qg^VIG=`6-r}`^ zB$n{Pk(`0Q8xVPP^MHcxyx#-%=OeUF@bsFeua@RokVio`($y9l<6K zzshJ>(a9*5YBMa1Jiyzjd(jjCv8^i$^w0Q!b-OaM(uca+j^^5&o~dkW6dbstH*A)S zpG!)i1GV)WsL>s0Ff@xnAq6QCuTL@ttbU6;m!hCA^>bIyKSTxXELc`Fu>9TrHY292 zEiR0dg$ykoo<}J|mEzw``?0E1WU@3yW-+6Mk>39B+B!bu6~24`9feBK8pJ1gkEJML z3I85BZo)xUvHC4Tge@j?{!jacqO!IY4g{rTZySn~8M5g0r(Yw#-@6J5Uatn8k}FdQ zkNXLmOZLGuqz#jGHLAFH_!nih(evD7J4f|3+E$GuZMhFl&yFeNiKO-Y(%Ao7l)MIo zlp61)Vp95gkBYROEgQDGsD2<2G zSB!FnEO~R2<%s?C;zWTodkYP|{DRU*OZNppMw}iB^^>)@>5BL!r*!{}v*(xq603S) zR8Ez}`TjsoF{DP%EaD2-Dlsd^)%5Fs#4!Ba>TykRPefsHwR44~a?1&U;ZxF_Ui+cecBCF2(e8Qe}DUZW5 z{loSj+k9Z0fcjO(3KF%}L^!k)QOm%8gPWtEbii|dOo2}*ee9j*?~Jk>1SnhHg^?}X zxUcJqo%U3-u%d&amAk*hLP?yO_0g48lbO5GRq!#E7-yL65`#(CQL6dyiIdQ1#nI*S z6R8b%%=dqX*)}E$!$pOn8j72kfu`a|3=;-6;v=3x|8(om0AYEr~n^ob>fK}VDvc# zbxKcvo(fs!j$+Mg97Ld%+I3qN)Z&JfF{3OyUykJto$O^})17qR5jX>v)c|N8?YkG2 zyG`C*JvkJWJF1gscFGghWf_MBx{Iu4U^9?h?rxgB8> z?ryW?G3AUzro@j!2yN-HNgRE@(DAAh@tlxXbUIyJvgZqy!eeit#AvdnBi2@s{O?u! z7++CFno&>#jS_SM(~#T9i&zPSK`s9&9UlPi`W6VeO`h+!QI;;Y$Esg(PAHE?M+ZjS z_N`b}Q1pLD{Z-`I`m2_m8Z0~{`Nk#Xi2m&D=j|Vux$G(jZ4T|O7OP1C8&k8{VH23w zBu4aJ?=VS%s7)xCOy7_%IARCU<^q|$QZud|I`xlDmjV!rGD0_H7x9U9dQs#t6v z4?bE8x`66OtEN#%eJOC?D_Iljw0!^4PpSIFjpydrPXedb+PHgGez+Ke2dUk{K3|aZ zDp83TRBW>`l}VnQyKN}(fTdaYmSj%5k+#AST^bEt+h)J-Us+mahzT1FiWUzb$PKBd z%cxh{&c#mi&41fSj;s)TaL@F_Mwe2Zl}*i{=3_#&qrviO#M_1B-`x0~D_`{*5=uv9 zdU5OK+`j{G3$-IOBti9B3LJ-tM^?H&>n4J%o=A zA=^S$ywisR!39$7U2L1SIu9Uy__tSWeC9P2-oxmiI|GN5m{5S5{_+!C$j(KT;{~{D z@|V=!oqIh@@Kfb+p3z^fIANa2fP9~K;yieYt5_YJLt?#0MAMaQt9Bs(HKZ7Y-3UOcf=t`J{iU}8Uma5=| zNYSE>h)_+cwl^Wl_eK9@m z3|FWnLG5+&e}`R5P_(tP_1WNfaVR1oJp;l=nqwu!;;ZVszvVdku+uPSr2VR?2&8n% zxF;bj^-1DePfxV1;o`QyV=f7@+pOst5$I0nB^hvyNU~3zG7e~(`)ijYmm~+l@*-Sz zRrg~qP~70+6;3+!Y{#|_Zod)oyB?;O{=m+>?16?<(W3zYYlttV?Q%FWzOKzE?z-;A zRHaz}md{;W#2^hI z0Oet)y{N>t!Ra-P{YtoDxhs9)LCy+Bsc?U|*`qbk=wEl7o} z6KcjWgITEHSWwT8YzA9>`gplX$B~s--A!4>G0I$p zzI*{sfAj6X+58sl+wNZA3;9yQqJwLER(E=~YI!!mO1!}ORW^X;8!M&e^%rH-Gz3m) z7`|$Crq&MH?lz#(LhQP;lGUX3wk*5KbyFg++9!dz&^OdHru<^fa%`FE6s-moWVf^g z26_J}>bxS2P0S9Z#z*#qWqI^dNiH!dpWYhHzEQrxXD6@DucW#H0ozd7qmmbMD<2ajat=USYWu`Imcf zb}l3weR+@5-eMTA;kO2e&3;7g7Bp%wz#z-6Yg{HZ!;faFbvU{SOVSDVSBu^?Q-l-A zr%A5yM^s70-GRRt_BTJdVLAOs;zyf}1or%jCKKg&DH(1aQ*XI(X_o@&3~d+r4gMqp z9mhzak5RT)UM8bM3`niO(5nsI-aC$k>t?-m5t~je(rcP)yh;;0?DG$qmbKV(FEZV= z!@~B>bk0qpT>@|u0y*|P@pA>qJybw_+nu7`8PTE3==trnO1)34@ReEL0@65X#h8!=WG^JeO=OF}P`fu5fy zY&sH05k70A*pYkd)~d~1@eh_q?$JW;0hhF-!nIfPpSi`~w~X%^Vy-xj!EQh^4H0CVI02*7o#A+1lEgT-`w+-Y_sQ@Pl8x zcwx_&eOS1uE8Sm1PhF1+d51#IK3PON=Ij5*u=VLN4a+gDysPo#U|`+|35UtvfA$?x zN#xt24ra+BXCWEOkPBf~lR%kOffRN2YhwQ~!QqFc=&^q#gW}e)r=%sqcL%>M2-Bj<%5|Yz3OFF-d@Fa&>diAJCM){OT=3>A6AA_>?g?!X|4XD!VdSl2G4dVf zq4^iuJ5J8dv&$`(ieJeJ+%w$Q5#nSaf69!kx+1U}H=@{G5#QwLMMaI=*KZKJGFVk4 zU_w7iMOJo=`|oNrOB#YFx8^|Ga&sNr+SND|RS{v%KmpWXdp!*0QGE#&uHU_cz)GF@ zMT^KxEP8Y5t#meJEILIWFq{>rL_BLW8tqHJLEuaGQ;ja9P2~Ddr{l^ z!#`!O%UjPBmmpIr_A`UuysFQ=_a6rTHNuTdxNsoI4y(3PiJQ4|fEA7Y#LNfw=vTx_ zTmR%9gIOkIeR1eVU_e~{>#LB!&RndoG)&HYzAv{6?W*X}#7V@k9Av;EC5|)HdkHxI z!K0oSNh&2POKr=^)&--*0lO&h;NUoFn9dNO@S=~AfC^t0;(avCnR>~OD!Sd!vi{}u z--{`=`<@u#&jel5t$8%xCFO0}jWFH}mGHcKzV8_u?JjuGv$1$Aixj4lGV^r(J(aqZzi4nZ%_RF>DD2N)3Qlf2q~@zo%xfn1l%Abq*u9^xAi|7 zKP>MhH_SAChYe3YTlDBMI;VtODtbw8-jIq%JWKk(cU#}~kYgSbXf8d22)t0d_7Z6V zu<$cj*p$g5>tOIl>RY)UD9~hay?v1(S;4a!)ZFt$`K=JcCn?aHc z-|f_@j)t2CR~$OVl8<#*<5e(}?}iDJeK(lx(!QQ$#<|RY2e7x9ki4ZLWkcqaQ6QCy zPtDY0Uqi_AM(ViiwUIL>+jq)b4uO&AG*;QMXfmviD0M9N!B-Tl5)F5L=0oL*57ob9 zcolfF7Yj0zSlG&-e#9@vYoE8R$LoSbub%s8{5hWPSDzKzMdNA5h@_oklP1d0%(ZN0 zHh*{Rt<2pQIsbh)AKO$5zJs=$ zOg{e6C6xXmgd`XaE`YAC)Z^=SZk%yV*Gk9%(&m z?bUi|(|whrSuBJ;-Kgqj$&xylv!>;+dg$-nK}DX!R6*6-*Cz+}1k!I)ml;d;5;gr^ zgv@R(zvr%f7cgm;Hn>5m%+)(7j$MIA!)ex6tLa8`j&MR(oKwpD8od$epoqfVl}ViM zz-~5$?y47!&BxbPe)pxmV?9WY5si)Z5L^DDCt@AbqC4mK^ge=p7HH%QbsQNGrh9;e z6-fx2TEn2$E~e%RZ+=wi7}Lf4C|lA0VspR-4))~DwLBR{8S|$ zGqY1*_i;a)KG|OY2ikY2PqIxYPZerVW2?~QN%O9pDWY|qWLE;<`mK+4Jn1+#<)TT# zz}Ns!ER~rI+~k@RIc`b43cyq2YZ^B!HEBh+&ZFPfxajHlG{g}@If*KdfA}R26Y}26 zmQSx;d`gaO7S-t4B8z)R3Pg6+#QH6!uI|N+l_PqPu|DJDLsdSC=vnD|TLs|~UOd-@ zs;@H~#Ur=hQS~U59B}UZ|AxQX(#8mTNiAZ2xj&Pb7}Xb#k4;A=iY`*{rAu2w^9hTm z+5S~368sdc4bL=bZY#=ka%?$ne{Oxf@a?*8L-g8y@Ky8R8c5kBVO}Q0B#O7`4`=mw zp%(ka(Ilz3NUh9RT-kVAAk9MU2lFo)17hYoP|5ePfgrLp{3WVnSAS=gESB@qAMLX7Eg##heLY3Jk73hVVnK&=5s+#mbe((;;JCc`-Snr85Gpa1w5wnOvP%>ph!<-T zxSuh9AlZ z?r|LRM~fsH{ABG7j(())6x0`23(fQ;XU>zCxu=^;jwxq|oTA{-KHDj$@nhH6)kxRz zuF0W_Y^N{QIg%~Zy3vU};+Ix-41TWM)$^di)AtrHLz7 zCd@YO7%g+N&%P4+<&Al< zbt+`%D$XO-9i)7Dj-cXgaz1kB`KYc(^`{UuZg&wlKeBm$)`d z-vC!-P(+3u+3sZTyFLui@NR1#Vwy$e_JC($UI>UaA;~8_JcYBA)a#Qkr<}(iK>gKL zLbKd1$Ln-XO2}4Sk2XL_ip+!>NUGBGto7zO_-!4#??ipBgzy22^88Nk@Wa{E@nA*o zv@rV3&0+wDfzg-25**Z6b8PotwZ6sU^Y--~)-wf)X9Zt`2}8_-3Ihe|PP z0hb6{mERj9hXJ0;_PdZ5rfJ3zi!L41b#-+(i~NEnyg61j0quAD?AErn3wvJGqXL&x zZd14{`M;J!O>E*Jw9Y9}uxC%bpMuhX^lyVAJgTsjZ}55YMm$0-rreUbrtCf`C2<(>`voFROS|IqnK>F2z;EW zwqtX2BYZj?c5l7iP9OBxX8`}chB0u>yNw9UuH`42CWh$;xZ}}B{NlNq!P_lIc@r;} zM)mt7gTBo;lRE45ec`Ksx2##LRAN=zqp3dZ3xS`oLqy4t8Xi1{G(OG{-(<4ec4NeVGXvo<}N<^$hNbm-n0 z(FZsi))d}7bL9Lq^WOFt>h1s0ME;hMEZpDjhl7^wfriS@VIrn*YL}B?^8GBi!p)72 z;MnY-I|W6)uiM#HPUXJm+dhX?eXi#t9L7inIunubHPPKxvQIKD)MSBb7(&6LP-`U6 zPVyxRgVI0Owm%}TeeyT6V2;u2CEt|^V7O9xh2nY6{{am_^1kIuf#M$kmNrmd;oMAV zyZ)SE-B+@)Nh3KGpst?c_tmBNe*OW>U0jUKyDG7BZ#AlGWx!B7wL?bmMxZ7Z3?#^hve$?(2@>&_~C{^LG;^6g%%SYM9nng$8Wqb*MjCGz1sj_(sTTWlSDd zFJ+c3#^?qP9Ei!2C*z=lTomSx|J+_l2szji=qRNuhPyES{qfcv7|=x=aWLB%_Lr_E zMSC&2vU96!JbC3<^zY*Ngi~)#NWb-_?f_7|6R2DRFo0t}4Xli%1>>+?Vn;OKB%GAZ zfTXyr9tDLpnEQJXzM5SiRpTxx)BD_vsQ`)ax#eaxMw`5*IP9Pt$>(9+Dl?&?X>TvJ9 z_ktB*_2F?MA>?~2pqm8XF&{o$OkFUWjjg-jAPtFP3uL7~nCpBsXn7lj%_XFy7lRni4LurA}h0T7}TN#o{2JFlcaez4g=0xiSml- zP|%$jqhn@EmP|f%>QtO|+G$>|FwR>h?jnRdZUuCc0i0uNkx7A*Pk3-D$|`FkD~t~;a%$_ z-Cz>>-*J3epF#-HiJkCFm#n6b%T*KO^E7U@I`Od?*jLP= z!CV$9HgF>@#fTa$#d}KaB5q@`39V^jJAF?wxrzlq>5o9=d`*z7VE{$JP4qlBcG^5M zQ?&QNeeHtg%(#M}iD-c+$cvuG0XNl$R$I%=k~P?|w@QkX{PySll37wMRI28=CDP}n znkd_}Y>F=JGjQafT%0%}4`YtVL%Y1Dq0e6ZrsUUaHE(TL6fES={_@mDy!`22OQ2Iz ztVWC&A*CT`!OzY0HqLErCWNqBs-Z&%bd*w-xmytDPI$8UbD#PKJNK7|^Vk9=r@SZa z#}<7DIeb88Ja_GQ_up?_K2Z8SP&Nx-K@Y~w2yZiy6c7^)ln_17PfX6h4DD_XA}*H- z-h@aUGdVKz+p<4Ov2dmAZ(H_98TRh4(fQm|l@je9B9;kh)RE-*+*-G2iXnZoarnR- zj5xRj4(XqRo}Du!HYuMkx_LG^Psl86ZXU0yZou$AFURur6+&i9#`x~J=N`#Fr{)#M zGKNXIwk;uqh5AZ|5$M<|C5Ppt*>UM}GqGWJk$_Jp`eGlhdY~OZCp$CV3y77;@k-?I za!d`X`~upQ2X$$M*Zy*{R7t?Kqx=`3^jq0H$pts79Wih5jb$-$V5UqYlM@a&i87Bw zHw{9Iul@02YFd)dO-X)cQd>VREW|fI?Z^D(B@)=N=*jv9z4CrMbtT!5$f*fTc|;zd zW=#nWJt#*~CSk0tcbDeq(55MxXJ~wGqMI$AET8RsLGu8el^e=2`kIv}tq4b2Mk~0= zSlo|4{)l6bJ=V`acRV5F2Q#4i8-QnATc~{0(A6)`!m4eBoZ_pziq7fgljDVmhj)<8 zGn(L|+fPTwRxVW`uH6F^ya-e?B_Ah?a*XxuM9ph1Ek@CNQK3&LuI9)X8HdzK1M{~? zUe<~;NG8SxS$LTFdKrkV+E9uQXE2}JZtU1wE!~0Tc&p`SlI3&j)UFxMJE0x^bZQ42 zIV3mq*pkmpk_J=xgH0bX4-_45Cc+CZi$4AEy8>MG@CMX3G(ugSdBp>&zpnDs@pMCsrj=O&ITXV5sUA`{uhZBn~x6gC@hHT{N#pKwxc;7`M zaq3Zh9FKkI!un#McnVPbl|{6mpO_qPryYZ@jXTP2Z3aXcunh}EluZL$gxNn=SJxxI zunt>yR$|VgB7FNx0e)XoipnaPKBx^eTF2v#n{`Z!&{(L zZa7^=Hto3xZoExH#A~sL;?=BA?0@fkaSNV#e>W)115xC0W)fn+$tRzT>C>nCna#1s z_zxlEN;9Ba0N|+DS1kXvaPJ4dVdnDfoQUj5?MYp}T}msSL}zmB3m-GMtCSEP^HDY% z*#9O#E0m^OSiDW61>+D<5yN8SxX7<=(BnB?u`s^yq8uir6m@kJ+xJvSem3TBTez}V zQYhJXK=!%GKoeQdEgoqA+F@k(WRa2w_szzUgXLOoha8lPuI-!2J~soVg{37OZocgL zGNVSXYMz8uF$V>!nRn@Z>+#L6Oanw_Y-SplD$w11_uZ0zPEItJ!Ow@sLPE$eGoUL6 zkmbXNin+^Af4Kycf02_ZO_PWNCKndI2GFKOCO*0I^w{dgvHZ7!=jBuZN9H-Jr1k{+ z;|AKn5zJ7=POtEOnyOitP+%tsdym*o)LgnV~; z&=NCm>z(OL$a&?)N?i89I;`5r3c^OOD9*-&r4@Yi(MLGrj5A_hQrvWf zvMJ!wVFWnU$EOCBbnldb3s3HV%TMbd2`7gEkSUY66g4TddA1+8V-h*1?A7IYGkx9x zT>IEYpJTrBv zh4!SCZ^g!rm+R9rEH9mgInN~V_M^wLtBE?c$4qX`lp&FV=>ppxM_$mXNvpe zv}W-7`UbrC$!^^F(pITRETyt3n0K%-Vn8=;+&HOTUFXi7r9b=pIE@f8-3WBI0{EBD zAIx$0zg@Bo4}82pcEE<}$vKIxRQFlocK91Q!JWG4;^RbC%4+el_Q}bMEAZ5x$71x5 zZqCM=<;Cr}4`9DThs{OVmQ0(Cq-y=7M=maQ$r zZ!3ziXjKVTt}jDjiOk-#N4!ju(t@TGMO7Dcwt#nR(+orVXXB{BEu^55q5X5vxotBk z%2S^4^0#W}W$gKH{10EgO;leS?>n?$d?9R==OHs&mQ^<3@6T<<+f(K5%naz%jR5n< zU3cAec=E|7k(HI@XaAf!8hzu7z{=aI$`n|aM&ABFO>j9I~2H^DV zoQL;ro9OJNS8oFLJ|qh)yXIrE!K7nJ9f%tUiYe8&H(sGtQE$Nz@EsW|SQix4VAlKt z5^H0Tk~{OOq)ObZsbu=xai6+f)q>l&o6|KFAu z<9}c6##giV>B`+`g^aaRNo<1f&oZi$sLYN1R()=b8xb^X+6eu6Wa5%jJK*f`?a-@B z^RUmYz9Hl@bX8eLU~`bHQbj8^l;ilDR->SZv1(SX$J}hm3axhR*b#5N^%lmDAD;?r zj!lnQgb+!pNz3TA0_Yygg!J>ZRW)@Od++D+BSed?G!0p|FgGO2C#UO{eZ>=9Dg`Lo zVgXcMW)jR(R)NXT;$?uEv^;Wth9T5I-&` z#JVlzQbhoEDX-j(^clq%R1y-y38fq8r(H&~M(Erw1FRDF$U|FV+^{^!JjwoEy1(K8 z)TIRInn)ogr&(WA*{=7$-iON{T(79P@cnaIv*nN>L!?5hefsoC6-Kv>5Yi(Abd=Hz z09DRbuhhBfl!vCGu)JDt+_KD*Gx5o#Ad_Q{bouzfxM9NK_FsCz%Rre{T`uv=#ITEr z{G)14vYjHpmtTqq36Hkfu}mpQ+;}mcRZR`W+D+wBe%migi?CvS8P;v72ni=^&64qy z-%@>UYIz;@(pcy}tKr7LjmdBg=#`DOc};ZwxsYknQOb&dvW{VMpeDa7spg^Gmb_Uo zq$W5vTZ+vy$-LZ%;O3_{;oqO`kwM=i5Jg*P^~^KR#D^b#=yydF+g)`~K-aCKqk9Vx#C~ zM8FBxVi2i0>WWX!g30MCSOYuvR^jV;1(-g!04vs&N@lOp^16_T(2S|M!QRy2=2ahP z)uIWGJt7Yi$F#-BLt3C+>kQ=OG!Dsb>3nXYn+6xmy2jno&iooli?ew~BlPc{X~@5` zv2KXgH;l5r9DT)dELy{=yhjH*wf-DS5@%x0KCM__wk8cCgsf44?i2ts?5$p@clMp{ z{({-7b}Ae3z$d3)%P9`;(-AM|QFkvES@| zs?Tk3-)#Kpw2nCAxVGrpAwy9nk)sOLJU#A&OaPOU^96z0dMXJ|zdCsjeppZ>v0nBZ zL;7UnZNUy2O~A))&acult{~1eMi_3N?!)H|$2VW=p1m^3_*g z;nY)4O*No9l@Ri^3Un+oHOh1V|YJ%FTUX4ipf$n2gOq9I)Y-krr==^IW=t5;|YR^V{)p`t+=EH+xApp z)%r3i&u#X?15&Y4RZB9FJ~yq8FEgXDRLhN3-)8LXxTErLH{$irS)o-ipc^%66lTw!ohs3m+L`e< zA>=L<=>7-bQah7370+F^c^|HOb#|!Sy{TYpG$Ga$WO8gF?AL`Uq1eow%YTxiVQG`W z*oqdk!MGDyVn@3QYC{Pl99uR6PB@En$mgc4qMLVAV#eHk_EiAG5+XQIDKq8DT$l?xka`bP@?3yi83_N^BiuTw3`N2+5BJs+kwe53$S@t zm1Lq+<-~Yvq6+fN$2k9;L!=1Nn3BZ>0-CC->T%@d%dvWMWhj$^_deD9Ire?uci(-u z|Ni^^Ua^4p3f?1x{7D769|5=^LGN$)J5W}IQy-eDVRU-T)2b-Q^^Y}&-2j7RDV{`I zbWNBXV?vi6Hwb^3c!c;1uGt3c`m6l_6o-qErQohhvPxosC2ljh=uv=Da<;_aAVo`7 z*EV3roPGFk#%_H7%YLv5)9gr=&#k_mqG=N%h5a*AqGOB6MGTbu7osvt(Z)HJ=*noD3f&;uWOB-b$v!Kfu&55(_Ebue zTR;AGK&lR~WoLLd&-1yt$=K9KQOSmSzwFG$QUFQcZdn*}R345yvK0;=+#&>SQvVh6 zq<%Q8l!%nGT?0YGL{S2H7-$?Qsl%Mb#dzbh-I%$cND7oOvbbntsRg?0&*_AJ-`Pjm zl&G5_eWogJTIfU@cpsl;9N3e*H)9{JeSDKt3aP$9&UNG+s*jEpT3x?>z2v7$!DAgE zidR7pq8T4_h|B#NSi&zPLbV19V)Lh*7u^`*d$s{mTZ z5A2B<#B69WMl&-f1jKAE$|li*5@S>BU~B_YSyhh}>&o!k@*@1QqzJ1wmSN-eN>o+L z5tG_OyU@7}aK=10*2jZ-W=Zl}BMxbaLk`N3{+!pMsiGqr-hYGzh&i9BYak}DsnXnD z^`WtGoVBnB?|!uxKQ1a#)J2s2lLk1f##044hUqt7*cs3Ly?0!goVu(qW_Q^0U>SN$C(I=5!wg43vId!iQv=p-t;CEu z`BJXiADhc1pBsz*Vu}_m$msX*Ay}RtPGnrQK=#euqsi&Uud!f~6)3^l# zy1oF^mECI2r~JQ9U$hyIe6lcLavCOd!r?vf@SlzqfA_31LFI}_vt`U6mcmRssgZt?dihT^4M%P$@3Q3NurdCz92;bfb!!Y}Djb=%0 zWG4H!rcD}QNWUD6K0Hs-mK@kC8@;+_g0VR{uS}6Okx)U@49Dq>iEq*v-0X%dnmYE( zz+347VPa2t?Z1DJ0#c&O zL~-pd=EV;K%-eq!f zJvSK~YHRATazi=3n7KzXO|nQymgB|}lGXAv($g9qwA zY>v~$w8c3mw8xM>InpA{5}(ISK=%&-OmR0jlo5FKn-zFxmOJ|IyAM;o+lxgji&0&p&vR3QP_$D)Z1DMK8BJ;G)Y6LgGKR*IwvRci zCC(n-0ppHtg_gNZ<@(za%|WC(6Z57|6|PjBC&Ch$sKAXV}c`vYC?i&NuF;#OP~R zVebJ}Xw_V?h0Ql+wrta;4c>U;4V-q`X{pTS*ibyqfbL}gH;Aod>2X$NO&yNAXNvr= zY0TuORS zhW`Egr#jHR!hr650NQQ1*rJx=XI=B}nOL!Iren@JGa9B{&Bk4+b;b9 zD0oSXd5MA>f3-DiLM+UWq5&tun;PZzx(e0=HP!W4vZe%|&)h3XaP5sO7bE}xAOJ~3 zK~&7ILOI9hChec%=y&7!+6$wiL8 zn&f`l^IP!jhr2_{EoS~X)q=nh#Tn2s6K0AZA26W%3czV8{-CxW@9mkZ@cMTv`9)ZH zJ4v4^o32?6lMse9)YIto(bPs` z*(8WA@(uz^!V^;NpjyI*X;Lm*Uxvrt+=j1zJ|Hn@H83RfNHGTU$jm6*1J*vPpErJ3 z9zOf$KxAY{N|x@JEw!CC`WZA|C7WX-uUd%(tIL#4RAl`*mC-T(91F;qG-(nZee_YW zZ`am!6oF>7L_9oSp@4R-|fQ(U*}_wChyPrffFs?93jeow@`GQX zsI1Dwd}+m4ECL?5J2e4Nh+}kRSbI>v#TE`m*>x9Cy+(O5n_wrTN$Ivv&Ya2Vd~N^= z3TrTbDf77^NOoOWeZYY9&NVLvXod%bY|xfCw6$^)K74Cqz?803Fga{mb) z`Q$fDTevxLmYRnCo9vXM?SQ%INQyZ|O2~Cmg&=k&CkHRej8PcqwqFO-<-4CO?kWgI zXB_GqWM?s9UlvI6*>`)Sbb$N=HImPbNh>js(dM1Gy{H zyBer3&?L>7Mz`%+pq?>0#k?k z;)h@NOJ#3VzX#9k6)RIPE}F(<@m4LG;^;$KO3ZE8!7b6NOC~zEZyrkGHdo&=>@MO4 zV($8wLMsZ#9iVQs7^9xQq8KlKvI}z-6`2N_xNWB7fgJ1R@Pl*l!fm~A_`sZ`GC798 zY?Jo!%mcXoiA^Z4Y>>>i+WD(pvDLCXUAuP0;>C;6wr$&VS11CjFM<1+2#Z+2%?*TM1o+9 zsRJy9(0JBrsT7B(ZxuIKI@Gcftd0XS?xPEvhxsyUN=qxZhH15drT zOA#aBn=NI~W;v6MaMDR9Ve;h3QvRF@eyP2R0|C$pyx}t*or=9htbB)w+W!BucOB4G z6m9fQ5(q7n&_Q|;6|rIkMFkZLie2no6jZRFqGCa@f`E#Oh!wknAShz5{5C|C-h1x^ z5|a0yJNxEk-@e)QO6txz90=Jm`|a#^=YBO&Cg)VUu^5@0+%bkinC_L)t>_EnE_bes zlGMNwugS}?$1#tL`ENHrG)v~dC9CG4l?9l8R_u*+5XszFZi~!~0VQl!n!#RWAyvi1 z;&YRX$AbkR=D0Y@ofU~sF1)ZUOpXkjt#dYR&BNp$H)8PUrLts~xy)9w%*Sq@zSX?k zxL!rP_gFg|eNdLE_GgTp-neY!zf)J>nzz`pZ7;5QbRi~uzgZ^#8=JCN?qZn3jo%L# zFhJJ0WErpk=-gI8Ir{zE-_FL!&t^m~UMI#-+L)Y*?hHp+Dki6NkatGc2gqAdyiCq9 z;3Qh<+&63vo_b}mjG{FA;NoEiZaz0I33dcv%NwQv&^&oayXvx#vQj`QWZpgxF$Lz| zq(oP0%$ZsrlXG+>*BuKJ6X4#SyiS@anIC6m)bYF@zv{}$_PjbbWx_6%LFp!H{>q0Y47RY_C#RIBUnQ#ZxtT$; zPOXYK=8&2)f%}LKHPF0KWz?^eDU(K+#GSOjouXuN9s?!Zq6PVTnvc`c)39exKBi6I zjOX85iQoU-E*YK9WbF6-c=)_y4l#oqf7z~i6+ClW3v@fGrmTQsEUV&Ua)kv+foapX zN}yY_DIfXyEOVy!L@Bdnixw^L)?06t^+v0)?=r_MqtIsWJ*||lfCdlL>>(XN&iO%fi)tngzlUufWH|~3WF+Tln1GepmZJkq8 zrb@ZvP?ATJyw>;!Z)Rd(*&Su#3y z7U$39Wdw}QfssEEJ_6$_PNFFYm0f{_`i=euzsy||R^mRv&7}N`Ho7u?Ca31<@|BX=QV6J~#fJ9Rc=lT}|e=9oeC#EX7r`y3wpu6&fyV-J@GJZcI)GZa&Pv z32X-J%E3kNVZ4J|BuWq=EML7xmg}M)?zexpMLs0+JMhY`bvbTkkc;O!ta`Ffy-YlI zb2IenUeBMIGNGh-@0$j0ur_VW!@2h?#g8+#$PJeh&@t$Q{y7Gr^zYxlT>0nZ<}@%} zdWXK>=C48Dm%i4Q`7wW8c6o9oJi0QP$*GJAAB=7}O_$4~&AU3^kz)bo)SK5vja!DB z9-1X1EmavW>ngBga`kIx;@CrL$!N)r9crRU{Y=!WQ_+aRi#jPD5VMKujbX@MQ!q?V zRg6r|Fo6R(dkXO07wcqQ%D?CB#ExD0F`XTxEx<#~$75S13fcX?Hh1~{X0Hf|cS zKqi&jy<79AYrbAaxbU5RX~v2Q zmq*iSGiADDqt(fiC!=-i)&ZmQr}oR=(F;~@!KKekj(zYl9;EY>m zVcn*}h)pL(XTGND4nFYeeQ@_xP25`=#k@wdd=p@DQ7x|Ym5*qJAh7p)>p+Zn{?+iN zK_79cK#uRDTeoib^wUqvL!;H07Z8*Hivz149QQLm|8t3@DQnqda*7amW^~K?0DG6a zJMO(QIY(s@f3o=C7v|&s;occtjjRkDb4V?m+NlmsI5HdiHm@rEdxfS-hy1XpX_gNr z$F2hNm*?P)~OceFWZfqA76mEOLs*v zIXj@U2AvTxa=>`{3wvM#xfLPK-{a70`b- zaNs~Z^2j4`XuMK?iYx&Z6m|Cb9ye^u!TFC*L{9EreFt(c<(iz#8ct=CQWjkq%jDEF zd@(w6WfWT_Zxc|FfH|^IX}BjZzw*IyTzB6;o_%zj78M4gE1C3te8*b2tVd&+AE(;q zM4~FoXwz9YrL4-vu^9Gq@26L@8)i0a#bs(;gR0WaO1fRBx5)78G274JpAKd=Htl^|L0hi zskHj!tgapJjP7P&?+SY*QOrzE>5{L0UXQL<{pJ{Tsg{H_=t23G2ymw#Uk7(z*Ahn_ zSVJZ~Gx=Gh$#$pCWzZ5yG4PwL>^1)_PM@3tIGWQYe!Bs;JUJiBR_~FfHG^pseq3I_ z%}~P8AEffbRjeRz*A-20=jHn#t13IohOmBf9y(q9Hx{nQ@zkcr>XYNoeJ*H-A$^-8 zBaJB|oVZ9C;w$*lYiKRUAyyR^5NWMtJ_p4z@yw7)UJqb zN7a%nu0xw@;r@HTyk$8!|E~XJS8;RTiC1$a+1ur(*Tc~MEkRZnVRA`Xj~+qM50m4X zYX2rem0O`tpFS8hYLqL`DdyItOBa0g)mPT=o5np&47An!}PWYgy2w#Q~;#G6ZeRYq~j*{YC{hK}v4+W?t#(}sbN_Ln4DS@?7lwzpY0eoWi3AYej`?I*c17b{IWP*pb0NVzZn7UiZkk? z^P$y|RV71iE@&p6F?SbkeQE)Ip1I98$nkzGtbOfS_3`M<%}}j!TJbYEFI^s54g%_& ziXA(4fWY%SA|z6Ef6A_~7epF8%;fAG>5Gu;C) zG|JXovrcW?O@oO=eWaZK{`+s~Yg@m5 zeI!ZT$#lsax@ps<;rQdrt(yOiKsOP<8QwHmaFk90*9`w0|1R4oO)4rRU+FGG=W-Wn zIE*8I>xfQN7Xe+Ii~0JAS3FU9a8 zl*z?PA$m2n=}c~_?j@UrR;VCw>>;&q?{zKE?bzBz3!ZTDv|&FG{BIbHDrjRr`>C+CI9 z)j9!a^H^j<@y?NO&e841sUCEkQ8l1lh?o< zugAq9FFA8rYAPQ;&)9+y<5tMlIeYV&$5r@jeq?2p6G4tZ*0^3K&gxPdcU-;?8rQ3& zHTSMqw+Fo+osY>sZ3-*>F#OrPCoA*j`|NgPO}uh{8#Jz4G4)K&3y7J`*-^_a{93w5k)NM$8;BxTRk+bA+28x`zhABzty;d^aRj;-0QC0$Ne4&j zBrtpVM%?h+ROIeuStC=Zeu-dmN|o9KoM`78W|RPa^=i75jLw`#m-=#Emhi^tSL0!) zy<*L7JT+z!UU+AjtqIc);28?0a2n=+Gd-b0+bncFIvd^S$2%YkRV!zNEpo=Y@z+T_ z4s)9aCZ{Tz?AVznf$iYGVBZ;*I zSh!*j?jEripM0|+ZjfWul#zF}M3Z_I^**`CX29#Z^%!vCH}Fgy-|%vvNug?%^({AT z!gbeOhY1rVL?SKS8m-=Q&pmkX!3X6tmzPGX79Bl`Ku1n|PXJ$uOW=8c!gI2nt% z?M@4y78oO*@pZb+#@XDoUG{kAg)rIPq!VcTfed=uTx%@`odH{V@laeHK=G^TV z_SOn~HGQ)TG%3v0ie27rUyjvBMiXV_%ru;Nd~NB6W52iR)zVDmwhAkPP#!DS=i=@W zi}2A`85~fwo|vDF3~u)9*|_Se zt1x5649V!6+Vb$7RjynaW5yA!~ZZi_Yey*CMbGjkd4e(k#w z;*(2^$wexocx7};nMY@-jFOa4?bL#x(fm_j)!N;7^uBr+ZbpI^r$2;Try2#8-gAuxK^tg*+tcjR^U6`D@G|iHaj$R@`ZtM2FQMrcZ zJ%*RB!3uE1T0UBvrj_w@zm__g&0zDPVGD`73iB?au!#+<~_~U4_Ztug8*=IoO$Fn4{y3EOY`qm4MEi zu0!_Ak__*}qq5PwVI@?lobCGg2kISo;_R>g2|mKn^WncS8gdog6pay?_SqJ&fFw0(vlqunlx!r zt{Sac1b7XB?koV~ivtkK@;xW~IvnZTo(uLTP?B+iXyBg`#Blj#;tqt#B0m(%$qACT_&vavg!{rljHA> zY@daZceTQy`&NlIi&}GeyaqsNkJD)}pJqx5yUr;<^Sglbs#>*3cFQQs+i$-eqeqXH z$GA0G<(eY<8#ZhhSQ(|9mHjfy@oWO!F#vul%b-(S&-T~CwHPq=TWrh8H6@itsENB- z$_XrzQ1Qf#$r-vQgz3Yt@g_nBqr}v@bn`J4FO!P`MGMvv4D#~}uwnCF%wM__<33xB zPo}NKx{bL~9;(B5JfL6zX0%uf6}4fuY*42n4rynwyzZT9Bdc151UpJpM%>4-nJ|*q zh3xSC<6m3x>|4w6%gn8^vp}+1=ctIxtg7kgb!t6ae_lhhX;KAME7K2Gm?{tzc@vI4 z%-j_v!`fFHdSlvy)gCgjHQc>R5wSHZ29Ho-Y3)kk)XbYx_thePm9Xz3x~Wba-AcP{RGelgye zyjC_tRkb7|TQCo46;_ZofZvb>qf0v-TmwV;w!oqLRWtdkB@Eei{UDPglYZ)r zF*-LUR|Ltp+7}duSt-0LQNYj3u3DFacRpK-DL<^o%y~Poaf`w7?7+g0X;T-tq7vHM zwWx~oPpOaY$7Z8#v#O|(l`ffO;f-VE%6l_fapCeDS1Z)=>?v1W3a zocYCCcd5E{X6KVjs{+(N4`|Q}VBm>|p$bhfQT)`0TUKWI#$e zHd|60)#Vow3Wc@@VjgybgMs`h>$ui8EW{W5l;_CIeczg01d@yw# zX3pCwLC^T^?Sa|uGq8d%ufaf*L-)(VWvADdDFaN#R?616j>G1HtS!F)yLN~0+hHj{gmx z{<YNT_ zp8%v+j$79-@Z``#55>%xGhGWgv$iEu511s*Dk|li&CL@+?5lw88vtG6eha~gz9q0= z^=5qV{cKE_zQ72xjxaj2ai@5hoMN1DGrB~4AgKbuBrv&1b!L<9HfmVX_nL-HTXQk` zgOzyola*MyYPV!iDnLVL!c2x)lMAqE!%DdE{KmNP{6<)`VmIy`xe#AZ-yCVk_<0;@J|$Mc{4iLd@z5*>wExSXRL>Z!<+ zv-;$$g{h~H@Dp`uW_0m%5|4A|9A*f%T_C*VG1&!5Xqa5IInHFAlLe5C);w!B=3&Cu z>+tckb@+F|4y@gn8!0np1vfunXE3_x@5fwwk_>Ox?gFgalp9l%(lC3ue|y&JD|T8l zD+3)5$U^V)8{*`nYok&{!h6_Us*U^5GdcS1@(X~^e%OfX9+{6#+wy#yEBVZ|vNABF zPcvM3Mt!7_O)BBYhfDzM3ehAsA6DL@B2c9@z^KVeO@YcSfNJf53fb<$^>Ks(vp3&- z6IWh&rKkRLJf~*OnwUFxF6!2;>*H=^^3b=05SJ;S3(9~M^E=+1yB9zFyBuSu&cwn@ zy=7P%(bhH$A-EPP?(XgsE$%MGiWexw-MzTG6?dn&Lvi=w?heJ_9nN{qcYVJJ$)1^H z?X_=PoaxBHnsS28H0!_6E(Q)f)gb2_4E)nN})q@8pfHKKcXSan$!@AQ5#d6B3i$Qaha5Y`sC17^X@cb&HsgingZb`_0{z37Iu zz!cE0_C%g?K{@8crZPh2#Sm;l)`!Cc5pV9F^wXQL!jo(ga&zOTQu?4|j{2Ku-IyUt zq2#j`+T0vgJkHwFxV_pra>n%!)IPn^C%Q8U5)5(;jczbvOFfn2qy6{p#}^( zNV)NkJLG=!5^r^csYOO731P0ALD*K>oPD|2mJI<_Mhb8Sj8cM7~)rYC{!!~Q9Bu7NIlHF@Vvx` zGh85N?PzP<4?U3ghBnFCWQe1Z9jXJl<| zwq><6esFnp@ZeBGIS?eZJDs_K9r@9{s|wN#5)qhiy#?qkXjN(71OZr^G8AoN5FFRPF?${Rm1Xnc-BZri z=?2Z~{}&2XfMkiliYEycT48BYSNfWDA%G)6%RhOPIG0Ut4e(Q_Xwafg(muG`rlRGh z>fRNip3*~5Cp|aAvNP16YyHI^)cZwGYb3`TTRa31sLF`~qtV-W<*9`6sw9M5dOF0i zwBB_8fL6v&1Yc3`fg(jxT!`o`yQ^q1LbI^Q*pAK~n4RH|Chu1Qr?1uKT7wk%2JG#g zsr0n?SQer)s~g<`rsPw#C-Ll6upRpVaHi-?lB2yWo2Q=UyzYL?nBg$aOQ|7`x!Kq8 zQ8dE@8kpy_q7)bpXQMt(L4vzlyOUsGPlu01+2}eNo1_LZm`eO;b^s1jNVKucgyx_# zapdb#-Kh7tv%#ekwA=a&|B>O$>fe$ZBtQG)2LUbE%ksMMIO|{PWg4{XP_^V-B1f&~ z9pNnK{cplh=%CxA3 zde~TxacKUy(8>{&LcZq1X=lh;MR1g8{ReV)$SJzk&5eJ}B~zJJ*U-BozV30DdcWK* zq_BMTU)iNk+Min&-yN_J-|HoG-SC@6qQUa3L<(EZC-9zlsMf_wCF@>7NvTM#9uKhI zs~I2?Yj?km6C3~&G4B@9VeAgZlACTs8aXHw>f0Db@g31b;B%uqozy8dFJoHh51)#L z{?f?dC-nUahay4Z=Sq=-qc=^jvgl}{AW9kEmrL(7I+^!lI%w*!uPT9HM3`0Q8u*<- zVr{;?-gORc+6XeAJ(`cE_`Te%8$5cbY#xg~sva8 z!+$xY*_dfJV2A2B$PG6!;+1b3T5IdeLl1k%U=Kvg*7mE!@`dgfsM+{epbd(fVr2FN zeXo!mgK{_ZEMYVMn?^jR%cz{sTFHmMt~Yu}g*GaaBwe1gZUReM~3YjQ@4yjnxyAI zZsMe&+5wC?s+?5ta|jsTy%iInpw9(eX=*~@i+J{oP3+w~USrF_{OjnezPA}v+do@= zS=vF@E9g92M#L{wt1;oPV%VGQCC7;PIU)Uqn#MCLHX$KdYqa=^YtsY(7sepFITSJ~ zh-~4l$orV$i$$-ou@Qj!XKyTh*CG8Hr9yaMOhiK?$;8Jz!%jjb#hn)8m5CJ0e~yRG zcfBL_3++1zq3jYA^b8{{x>CeAeG`@`x@$tjmhzF{)nP^oP{j1Yb;TG+GLwR1x0AN# zsrOnznLQ)UjHMfQji_RY!{yUP>zB*X+R22-i|JM8@rbOO%aL{gEASx;U5HRRzrJ77 zZ63{RO8xpu{6kwpZkx2WNUHLf(RL^!V{1lEzor=Ux=B=iZt6NX#Y-(cbEx4`_;eP7 zcYpEId9YHuTdkf`pz7~#$P{gkaktg{^$ve6yIyG+tHA#Kc62f{tVItJ%pU~O%HL2e z-Vi1UGAtnY`=qY8wN0I58%W#&TKE;JjnB!{$#hivwYY;L^up;xGc}%($}59NYvqwZcK7^?MZ!{pu_{ zu+#F*F9!tQM+wQ+@@b^EBikhLflgj-JRxR3aq1Bm>~s)YD}n&|tpgME$tLyZkPzOf zO@?ojH=i?A=hIQ4Yq86q9X=-Zt@a2kuA^*7#EmLkDYqj#JlRoC zn!VMpjCMPI+J@PD-S9eJi095wI6Ap2O||h9jb`a)gaD)xBejEbYntHu(0YwKLb#9v z_(SpmU7+CyOfjN8aWsS=OQ=O9JZuS;eRL8(N^X!F{SE&WO=jwjFNU7#oEoKHG6|LW z$#W_hiS4>WIEx8V**z~iUw@%{mKJuEP#bfphII5FHf&qfXvO{(c533g_G(OKpEkT^ zw^xxUhi|L-Y-_I7j8Qx+^dDD7&9?Z!Eq;`Su|>`Z2&0Q-(S$^8|FRMBD*m)3^NGUc zp(bG+pu2qL)EOx4{zCPStw2pu5 zd1cSqb4*#Y3SIie@4p8cDD~F5@T%EX(PoisyT?;(ywJ{ZQ;A+Vsv}#!mu=!-d6|U= z929sq!JZGh$&m5v;3h4&sp)d#JRCio13|E@O{!X7w^)G&$iy*LC%N#;w=oB4DMA`H zs^p$oKZ@JqC#!LkjMa1Ea@}Nl?JQLqAul6&d^4lb9IvT|Nj$>|k*ah^Hk8MWKXO{( zt-;Kq<7XgvN3)gROl($DSZkf)z;XMfn~SGZ?6&%0?qCKx@!C+I8{G2ouE6;vDW>dv zr5U3r!ydu=^}&2$ManeSgmfPWn5Cg?kdY15dY0QJC+%~y5gsr}9C+5{^tachf6<3|EWDQ&CJj6(4zouF7a(X>b)*U@7l#?^59>s0P=fe4Id7D)Ed zJm?4L#TIshz3KK(ZWUl&>KxU=UPw)@{+9(#F*9-fKYhh=kknIw?16->2$K(KxGB8Z z4U`Dy#x6ocd1nJY3!}qbP2EpheNmh-u{OmFRA3y5`GWJ&v`0-6!koWapZ)=DY*w`nr=vf!lo!AhqOhXkmoG%p)J&h zl@k-3hQB;aujm|6Z1YUy5DQMyUn7r@`}}|mZfik`ZfpCThMw8Q#5e|#?VwN|u6*}9 zcB3~TfWbv}--31+_P+bg?M;i{tkQ&OX7EK%6tY4yzWmu2(n$?#S>m5l#C&Ux0cF<6 zBECT7c)_PSvP4dkLDJ_rsLuXycJZNrE(dWyv3&Lp54$~Nlm?jrf0d)1(L5oJd+ zF5kJc7=Qq97@7E!6VM8kTd?rKQb{J{O}AgxK)de7bCPJGAFK#RvG3IgoF#?hZC-Ya z#b$jJg`Vd9lAR%818zw|O-N!Ut#niAFtKCU8P(tq^2ieG7eP1I zcQZ85bu;q{>;g?~f`&hUlr5_Ta;!aWt~; zxa}DCyTtVMqCzB@!_|Z@d-m@0_ijK= z>*UMkUr(UEPyN;|keY6V+{3G*gyd=ck_yY$5uj<{rJlxgn9cr=tHw-LtWdP0Dlb}4 z2c!q+B|&c)m)e9RfuY?@6^CpL8*>nNn91WvXI~H~tM7w*hH0|m`@{W)+~`J6IJH-9 zvd|g!cSl^XP3P4e;5LUAl8n3R!cA;{>Ir7@B3vDy%w6`T1TfITOj!^iU`chZH=s@o z-2dQ~C_KbEIO(bs&y#W8?U?7e@?|hFlM(!uliOCk3pv7*M?UyrWO@ zNwUVUwiDaHQ<8D<`i6z^zf1MEl)5h~My2*#D_QU(gPK7C$a|ISuK55sr4ahN{q zFGqm}2rh31&u#FlKVD%E5C3(OnFWgrt*%@WDkl>{&3Z9*D8poqW^_Vne&Ru``*>!5 z^Tu0&YQ<{)s#{^t2cy3V*342yb*fux=%VV9WNU#PIe{(4tH^RcDq&MzDb}{Mg#7Am z-@k|B*&|T~WYB0D{^r=Z{)M*X*(vOT=(}m^YzmE<IUE9FGnIrI#`5mOWABB;$QRPv!imH)*Iw6CnLuw z@(}US$XorCBnWqMxrp$12^Axh8I9wh94*~!vkt$328eYCx3g_R=n^Al18A!0dm|K} zcRcSmLz*sn5F^`xbR#0l#$aUpEg+pF1}HIK{1S${MMFEiAoa8s{jw`Zb5#(CA|l4E zOcT@!P%tUuRmmJpOHq)<=&x@L$2s_-5Lb8|NrO8Y$l_*CVH~NQ%YQOU5?X*G)hv-` z`G|_WIg|=2II>wAk8{JBdKL_PPK{J%s%ZHQvN)xJ1~`AD{9J%(h^_FTQ#Ud5;Q6b@ z))JypZ4v$!hj8%t@04B@x((oeNcREaP&5ewF}U3)sNBZH1G%zSFCtfT}xPI}O-GFmfv2)os>*WoZ9qMUNw*UTUp)#~nND%7w z?;WINCffBsAQ>W(Nw38aF!=tK6QjdWbpeU_1WH#5+OpUaJG4Med{Ys^xYM7*!Oqsj zJ_%-$tcds3K=sfGF1XBTYZGuhSyblIp`3L4RuA|qHq{E*e~sLM9%jJyXKut!U`fC5X`D zk%6JEjNY|+N5*gnSt8`Jcr!cobB3(KUW*YVYn9>tB@gP}U~wB)l|7d5fWFUGRr9hQ zlq4IQ7Bef7uMUKLM_ut0rr>w5uNr6nvA6Fl$RwiY~k?;l--7E_o)InLP|z%{Gs1|BJ)r zKg_&%Kly|fFz0PZq+qt|7JP}tnDOb~hDAVVQsZ`Q7ERUQiFCG#Swy6Y2qnSL&Fy0y ziGPBV(?eA?;5^t9)9R6HJTX<9Wvq=4=4UfwGp~x&0AL&wxm31-@}FdD`wyCMn_B5h zh?B_Blgw+l;Qd5$_|K_KszW;rIVlkWytio?!%C?J1q<6`3!IYLg_Va4s*rH{itQap zcM-ZLi(iGsk1o&I$9bTP|3ruYE6dYb1t1oBSruAng0*J}M$2ob`}6Ywf*|9+P}fO& z8$;Y0TNSj&MtpSlU!4Vhdo8;+nNQl{Vq&Osl_PoT1Bz7W>-|E-?6a0srCPHO=8p%% zX$)JQnZ1E%FPY=FAjcvQ+;0@S4nKRRzhoK`+BPLaIMFJ1F^b7Zw%2Kj`}p8bdoz?n z)S69y5Qr_N@6v4#yOtI^=+_H`GGpVN07=S-iVCpKa3d#8T|4MC&ue)_xd55Qced{7 zm^!xkQ5th6r24z2AOI_WjP7Uwr8na9x+*_Grwe1%;l>ZBb}v+|LvXLTlAm=v+2DTD z`lvHKi75aGKQcVdnqwu(TE+t7Am1vLet`tvo*h_@*l<)pt3hbl)Ci7hwEPfi@PT;m z`wRe(u-~%Jq>?ih{~C$EPIQkkgb4eAcAcVFWNP2Cb3wskAWs>O>(ID8?dQPisZn|X!(&@pWs1A?^Im9B%rhPcK!>i zos-i>666&#nlhZADyN@6?FRPfUhd8xc1Tjnf-OKgSp)&E3e0C3c1mq1ksyS{3P>7r zk;z}{2l?>|q82ZoLkpruGN4k|si#>^ebVhlX2MxrzA30}Yp`D&&TEpkvM*8(q?%{Z zdQ><3Snp7ymSw*PjMO0ZdA4Ke?_#R(ohawRED!A2NX(fk)kPqNC)JfzcZY5%TiT~q zy7mL>;_9hY$YvYvhY$5Dv1X1pGFquiDaRo>ml)X#>R^5N;c_-S@LiF)Z&7jKGfqOL zfgEd)_sgSnf$Ze=#ot36Kht!s?-w(QLefIS_6tyNHxX>c7*Xboi$7rv^rL8neu$i{ zfwBwb#Pmu8*fq{N0j#sYr;OvVl*c^ji7fpIfPTm_5l*z~;;u7Y)@ZGCuRcLzDceS( zZK-pQonRd-_WP=<_|= zjqc=P|1bw19)r5!kvY9)dYi~%0 z-qM|Dt39L>NET%*ZXb08p3W@W1iTrYPow^k^1+s1meLp8+%1_CBjK<>0KJ{lrkCsx zw@i}2k2QfMi(3G8zL>R0)I$JJL5N=vJ3HYvG=VJ64k*1Clb6p*G&jh;Og7j^-Ud;W zELGG>)%=CMGMIBRi`3Y^&Fglh) z@nI&C8J({Po*fa2bs8+8xn9dPtFn+{azJKe>@N}uUQ?Khtk~x*$CdO*=-M|B)ra(| z$Are?5(t4^lZ~32|ICv=)0Oi0Ww`5<*%$f5`V88`xdQehI&~4ca`mCdq~rMt2M*Nh zhlh-tiK%3n^=}Z4_Zo1Uv}6OQldS!|B{0CzE+@IG&B527VCEd%oGMl)MEZ~^#)Q3K z+$Fh`;)3R9t7HLs%4=#ThWYv{aZ<(7>F|N8TUyV1^8C_hd|^GlO%vpDBl}@nR|6zX z>Ky-h8~H?J1Qs)d)moGuJ%bj3Hx{v19sGhot*|pAH`nZ%AX8nOrm*}Qnb_HqQ)_-Y zTO#vWqNaW)JBRu zogZ|&Z+ki;>N$in{L_y0)uBrNjsSQ3?UMTdbtrB~YryLKsmE0NxzPPBaiao^#1>ED zIs1eFx!C>TlRIFfA@Ba+>`e}IpGOJ>s{junD$(!{%J8A?Z?qpIT^ZNT*ONXc96p-{ z>o>8OYXrx(V%(G2XIwb3^(|nI#|P&udXnCqAzO zw^?GJY@WmJ{N-0RWXf-AN;`OH2Rbfz20*T*EQkv&N~D5h4aJBqM<}KJ)htu7(n19` zw>g{76oPjU$7s6o&64~Jw zv5_zSrv)f?#aXINpwY4? zqeqP!FUt=}h_X3j!37<=@P!^3uCRfPnaJry2<~oW{PSx4WQD*!{ z=gV*KKpeuFc=6>Kx~RbJt+%%y6N`a+qP3fV&5M@d_R|`D-aTAu@|K)7Wjxr^C2K5I zFpnkPaK6CpqNB&s~);vYbf-|lJ$_=1BkE0O7zz`1c7EBD%MIRhm_${beD`q3ir~C z?x@v=8=0M^=|4NVP#SRyr55Jn`t|(#E&z0PJZ-n41Z{$%UaA%=LSrLbgn2_-l;ICl zDx5@mO(xv}k46x?LVQkIHtI8{3v8CV6_(($rLkOsX)Z(&)OTpGi9`7|FW?>_aGVsO z?D)ypuS?;Bb>) z9-IbApR;v8u<;Jd?v=s&6|L*~L6U!z9JV>^>L174Q!5Z-#n4y<_|D$Ek9OVb*kmpP zY)Ee3p^0$oda%R884t=!fAyv5okA$4mGxk=h3o@_@-ffb^ED+sox(M7>+d?xRJT`W zDpTmdnL_z+pcfx+PKNkCCk$6JKOx`=rMKwG3n`2$#ElTeNR6)bMpeD}U3S`4KyF-; zJ`3(vs%s(XhQc;mIb~=O>0q=Ibno^@!?F357RN1LxQAjSwj$2xH(7^{uYX*(=T zR|1X+WZb5u&1{G1kv8o&+1tE;gDdf*N+J^PXIo3H?OBm%Xx+1*RGY91tFN8+kBNIrTuwj9W4QYHT_tix~I+pLy-?YV03*r7PZ zt8v%M+;jZTToRzg&Ic1eym>TkRTQ-t9pF2#SZy-F+nX6)BiM({UEY^Xl!xO#qV!BN z-d+NYfxk6P+h&y9QZy|)Jw4((MEXwb(nqE&WozP6*c0skZFTvf%`}EMd8sd#m;xwF zf3mbvU7bktG$x~M5q2*8L_FvTk4Ww2d|6}R=^&6YdfgoyX)7H$!fD@ zZT}LR~8`gJ?jd^@k=0DLtEucF(=xD z^rrZ&fN%QNCn26HBw54#^{!gTP1p$4p@Fd-xxEoGNT8&qdA1mt*?T_wKZ9^@T_+r5 zS*%xNc$Z9A`M9(Te4>!Pjh72e-UT~cj@_6}GZ&9023OkZADd+pU@Sv}|8kTB?w?sl zVW3t;`Hv<^@muJA9;(u9MlYvf`wIF*6MDG0*Pl=#Tni|2YV$vmvp@Kh{PVIC6IMM3 z6x<;IdDrw?XsZq_0oW>J30!oWQJsB6;Ora((%3&p3?R^!C!hmRAwa3y^T)7{%m#9f z$2|N8VSsxa#=cSRTG}@A9onu^)Cs{S{z4MY|HRl}>eY;Zrgh_Xf3bOgHKTbkDHd>X z*H+Pi-c=z&4Q{^S@MGtj>w{W22?)^9gk1IbCT|0QX8>dcB)$O%NB&R(iFmLH^2D9d zu$l-_BpzoRl``P#4*N{k9d&IauT-nTX4vSzlz|xj-RF9Y+hV23&TWBX0D*L?C!@)a zY4pe&3XRZ4ea%4p;yD;hRz9wKJPozzrna`0%fiXr+aUTAN^GX%OLkCMyE9`oJOk@N zY~fk5EXUEEkBvx`pMz=0kD{bc`bo@wl}7zxIIa_Vi|w!GZCB$ii`7B$VtGsH#Hy6% z%f$-fH@uyKz=<9k`Iv={ZLDeImjMJ;ZU74$j>o@~Zb@PFvxiMNGEXCau}{hB}D?K|Lta`TdJOT6|kvvBm)L=^PM zKn%zGOzrrKx0;@Qw5)RDCa`jVJ%#s;F}7#)sQ(e{1`L_aPKQidO=}c@)kH}P5!yei zXN2B!;zh@yQzHjR$ht4|Ya_b0$57|Vcdtne@T@hxl|R#UPCVpjl}HWv;pe4q`kpAxw1zGL;Y=+wrVQ*I>Pq?Mb_Mh3qSldjmGDq% zG?+%pZB|>E!nXRj$z4gzzUrlsze2h&6-f@__;QRr`~U20$q~kkkO>04b9s7WBLqOQ z(WPgW#=Mz8=y9YhR++N%l=?JUB>X%cFAYf_uZXr(pN?Fubys};we}!*J6_7T{SZOBblMvxSr9VR0oiuSk`<{ZC^A+1!BBJ!4q)kLr zp&Ez~k(V0L`V(UP4grn}T9|uapdaAL4)g}wS`Wp_vc#iPLp~lmzkVYQI{Z1*od1qd z%Bmj;$E9ZcMVf58?=|>hH}rB{?QE9-VpC=7vKOdC7i~*AxTc0a=qZodovOpgRI>HPO%h(?GgLP4 z)kDc8+u3F{Puo%3O3lD>;>hgYHmhHQa{PD3*yRSbw=r|h&HMXA+7Vjk#SV)NjAEsU z<)Z%=&YVz_UUy@u5X!4!+xPd+J7*1&-rt6_3ZHJ( za4EL9*KGh?sWu$6-|dizlW9rB52y?skLSnk+R?p2D0iOaMMM7%rO2}RosmL3@@$xl zax_US#dKwvc1?Tuw%uL^=#{4E>h4JqhPqv>Vtt5b*ZEg`pzJ59uN^GH%ozV6dY&&~ zNXw8<6)=p>i+6_O#@P{f4{5t+g%y`8TqM;pIQsBW-Il+6v_cwb-`V%1EPBB9tP zJna5@W@d#E877OJAVz9upaT-hOQ?Ci){H*<)I^L!a%9i8{=d|60xA&`cN ztJNbz$HB?xM+jxY=Xl@?BtNx$wAb+N2HJB)4du-XE2>+@oH!TJ9$M%2}%tRz948GuXSayo)yqLm6+HI3XixrXrFuCBZ!g`IPJ9Ek%Cb2CdWe+0QWG-c19K(5otcydV5*Zh$}*c0H{)w* z{SwD4Z!e3wnF(dB5jtP;MtppHz}=b$>dVvJKolXrk?3o?go!;FAPSBbp^F$+GqlVi zIcK$AWcjwGHncSl%fem%36QX}nhq!AFD%PRS%|w_4T**_$Gf%WL+Pc=c_D7xn<#KJ zY4FB&RW^5(7Wu7)=jB2hLn*D`%5f;O!ImdbQDIBQwISx|)w-jiw7A0jJ|B**=3_1! z8CP4n4aLK8{=!~wse+3xmxsdf%f7%&_!?5!DR1hV9V#3BxDybWbg)oGoW!8Lg?qx| zVr9=;HPF^ho#hI;JzhvYiOd;r1UYlO{rgfXZoDY2TWUr3mT|@Sv9Ubt;&mOKw1wiy zy9CtY0O6T%G6b_gFNwZ*!CHOhlw)YVDP}d6ikFJX{hv)ILvB37NO*o z`08f!b`$4D;KEmj+4Ko3-}iG2UE-*`ETbG#d&_1>S+*9sE1gECk%7?5P5e!Rd^#5+ zI>Q!N=E%jY+87NU7e9UkUQMsXF}4dxzLwJkG~!xKw!e%?&9f#RSW2(s6$N7-+;?p= z=mYP0mL@XMa}dw93THIaPvBwS*EzV3Qtm~#$gu`}QV$HgJU*Fj$jN`)0nCX~>x*^* zKbUQMW1wBFh99L+d8_nb)nG5kmo6!#`BYy*=)rf#s=3^Q0__?O&4Ec*Ql8W2& z<$!62p1`R$=0y!JBYtzO2t$Cr8ZI0Eq*~_k&`!$lt{q3VK6?&#c+xfL{_8@d_bqVq z7iBL=Ho_tF0)soe^=l7Ao8FuF!f5??AJ&ZwM*K|TgG{;l6f=9{_SVzh*Z!7I%JQm- zi=$fl$zB51JWT=yr<=t;m(33tYhTDX!39Dd`ec;~P1%h&qITFjINv8G@3 z$OOg}GQa1Vx8s6&9*Wb74bAMnCHkIV`07xD8qk^849Mo|ua;||K;37_2HrnQDWSuCi!jB=*ISzTpR}8Zwp+&;3CiRzcKiUmco~*WrZuQ z3SKMD%24%xi`b?=!{r(==KNuW>6&A}-za=yfl~qo&Gi71h%mqxEj5$3lyC^++`<7> z^q9n4g9K5*h z?(eoGKJ3k|zJR>f$Vk~|#FbFOk6dv`E&6HZpG3I5# zR&r$9PQy73b82=ydzric`o}r-i)NWSRg3dqY)xHHzO>(;K0>SWlQ6Q=uBycSvkboy z4NI+g>VC(e)0=ff#(%CZjm;fR{?bot?M(9l9Zg{yr2xpE9k+GqDuw`-(X9NMu6 zDrLu1w42ortU?6|S|9g8x-e#u9q+Nt%7WUKmF5M8wcE!oe`^si;ew%Sln^PKEnvuK%Hw1f7HrbS16I`mM}KWVf{;3!k|goW0ex*Ajg4l4unU7Paro9Re-ALaR2ukSImliU)S3+Gkp%VZcK(t4fYOH>Z5aM7 zGyOB#4UR1t9TcuQB$Zi_I;;IW-7E~k0O&h73N$H_ z7-2e!9JvHAgEJ$}C--_JEmqhPEfGad)rR>2ljbVG2dEEAAdR3i8W0Rp7m0GK<3|6P_L%Em8v zhpoP!Z0=Wp)M^086gG)-1AYmUm3sUcz`%PRKxS`h;M0Ll!m8-$;rvohKvhf6p5cO$PWyH=aJ4SL)$9b zLQ}4R)WbQ^hqRTq6L7Vi`0roA=rnBb5g$Z#znHkxtrR!;MM2WaaSzflL4xm)|NB8C zehD&${1Am0ENBH<5;Y*8KUoDCy4DDI+0Oj$O{5yY?|8D@t$aqM!HF<7OzSyBew)9O z9RgpI%YRog{0rQHA?iqbw$DT>?1DsYANB>O`vnUA{~k>VI*$b0rL0O7&OpBmcoMi0 z42ZL^ZCrm<(Eq(8XGeNNnzYY1j->(}%JFNp@oC0KN)eWBg8#jM#0H+>qiB7^(+Nek z*0gU_`%fyW2i$-W=>I4|D}9K`R*Q9MTfhPPexrb2OdMDO zp43KWx*^U!H4e@(5pooyWMuI}g@RC*B1ZWAI!p~fjzSICqB{xsl8ko3*k(>A)IuE; z@N_A%|GVK$G8@*Uec*+>uzO#0`1ghSf6XRKlC*98-{*uXf+pZeAOE;mT29an2`8f% zGI?MZXn?!t{=eHH0$)9F&>HghAoXP(V1$|E{N&Vp1MX*{F3Y z!6FASV6_+GOmTkMwskxLm;GWpTF40KMN>X`aLre~>*;iIG^_LvR+{UU54GCeIf*<@ zSOMD#EYS1i7CB@vwU~~498Kp&z22V~c)UHXv07kS8nt`+cWQ5QVzE;FRV=@#XvBy6 z#~J}}bYZrfFQ;YxQlJjji_5O*(|Ai-|0r)8Z z{bBrs{SamHpjA5T`5kah+yW^39!OXMwC19+7!Fulq72mi>aV>pw%))>1$tF8v;B<+<+UhdEI#8Vx^|Zn726$sG0Z88vP}yGT5b`;8y}NOs?v19BuX9%Q zTRETpRp&Z$#Meay;J0jDa?kS$od!{7=ikfUSkDu#_MbMcl?4?7tTq7mOYCa>!}y|d zII&N#@JlQt7eF(wJo4p^7OKu&dA+F-KQhFuU#4+d?QmxT zh5T)gT}8i50`LxYAFG-@oi=8BW@qrPbMyOq;3n`cEKV25?mlhbx%~ZBG<2R_d%=!A zC*ldKn>Q=unM8&Y4M$2u6^@ST=vfP}=A6D5Ti!Cs7)wZYfD!3GZ#Ror1eLnCktP2Cms1NghFsBOU>gSE#X&2JnBh`K8;m z!1KGDMLbQ_)z-d?gg1cP%1GT_0m&-tLQ%kf1iRxoo5Evfw6j#)C-`*2PT*1grr+Y} zOqbH`ap#^HrS=)c_rygR!Lk{_ZnC1Wj~+IZ#M~Mx$WV$-EW1f zq%I?q2C6Y}uh>EX;XeWw=2ZbQ$uhC{D90PY+Xs2kN%%C>4;_FI;$57Vr?6kR0rWyW zmjlG--{{^=I=!eu45r;q1H5+aXu6}uK*Sl2pl1`97v#UZv6pK;k#$AT8TR&*I;#u# zKfK%TSId6^-P3NNInW}Y-;ZAH74888yJPVuTw$wRmQ0hszEkC2e&e4|j7MGo0ze!G zxT@|3FW?z%1J=6V?GkKGlfd(*?L;2VFnm9KssUkY`m9ZvcYq4xb7Mf*)7@HUpHap) zz$=8>WdMQTF-2`B2<#0)FsMW)4`D0qK!(G$edBk+mDp@R?4$fP7M&h8%coJH69f=8 zN@xCa8zE1?N6GBU^#hd67HyF31^5?msL@^v9z6t_<^ez!a{Q3$M&dGa-gb>L&bv?G zw$^^P7EUS_{JB!OOoOr6Vb9KcfOA2YJbU>(2jm9yB@IQ&m%5Hq;#rui+RTZ+6M5}i zybcSJACuGph{tx~)7_v$W3LMwoS<=eJ%{$Sq`ACza97UlQO!LhCE+=C^HTdB@-U8e~5&a1Q<%8&` z!j)6Jce4tl<7RaD@-J5DWC&k?$&*j(er7=!57-j`Wi3(kt}9TO=eq;mE9D9co~UqjK${7oU4C)#Y)Z1EU3ewwf1->9}{5x zLam0L&xYvM01M)`S0_AA=d5F{!Fmg#nN*5X->8Lr(@mhvIk!F2SGAEXRoK|FcgqLi zt>CEe4zR;LGq+v%)B8^4faG5Prv>nop%06SbUB)pPRnhyS;b53ssA>U|5FXwhUEt^ zdcw63#{p&Es`0SxxU%&En8N1Pg32h_f^5&`6ocaW&42d~bC!H@Z-5?o;6*3@Ek(%= zk(#A%R5T$%P;t;oql?|(0nk0c$WzRVU{K04soa$fL4Ue^jy=7>LTN;^IoV;>0fOnDVY37HJvfUQ?isvcg^u{on#Abn$_{Ox?=O{G zrG*wc06yN@rA=YsS5>5(B=Y0tV9Vh;JF34xBC$60nBL8*7VNGy3}r30KQC_c_-9Ep z_8&N|!2D=4G!pVOL9dpZ{UzkraZ9dFpePh54z)~?s^;U)@ZB>#C^&5cHK-5pDj(hR z6(fz=a}dkTWVoqHbkbI!K7ZjJB(ku10Xp*fPU=N3pmIkQF2WLfqM?4eQ5t>V7`SdF zp>b$0`I<@29rois4J-!%EkWv z!CBe)>fUxUEgPHtATnufM99H=sY}P4#sJ9MQTfi?DxK~oqSNF8Iv~ z)16|z+MfBm+-fG&QZHVuSzhGT(Fw6h(OLTQor~|v3c+>}N0T0(P&e67w$$yQ>&7jeV3q^P^!;wBl!w#f?;Kp~AU4KzdT1iQHDa)W=fKRQv z)loaJBrk{;g2HqxlqZsXV;Kv3zu`MTz#_pdeWeIYXbil);9hrwSo|{(;)lXURB3g* zVP*nI4t{rx0;%GbUP;5c^1-HN8%Q0$Iu`_ZB#H90SoZrlpF7`P6cLXS7{_9660cg> zQCC~{&YNR59a09yxYv=6pfjc8A;tK*{!AU7 zIew*c9R4FfqdGi)OwyaP^%b1wh6V@J@rgknW%&45tq?Z+FK^lD=+JR{>&jp09I7AQ zO8P2flG6z?eIlt$VQREs7f=@>&VX@ITI3*Jm%77o_fJF<*N$S?gGQgCAS&Z=7x3io z_e8ps{76iiO&&aErJUYU4U?d#e7a({sGT{Tyl2=ukZQGxbJB778_|+CG&nK|Nr;t} znLTit#(|2S4?GcnpmbL(m&C5ebF#Kf@Rjcg@^93#w_mN68WLSMz!!6P2<%M~rkrSzbVZ(w6AO9jfQ!BqtNBE)AQSNU3a8*q8RgqeP43;iOiDE-F8 z3LsaP%C2@h;4*7F2do(%9Qa%1Gal0OIsG-@uP-e5iXJlMX82ClVl(BMai?n78$N9Q z`Sa)r7Sf?CC)eg1w84<1N9G23=lXF@cvYa0QP!Ym30?7^FeC~O>>~w<_Codrv;83j zXXSn|0aKPm`?XTCky`bUxx}0K^p|Vb;tWX-uumJYJ>(eb`^8#9j&JVFRj zn5X3Nq>^x0sRg<)*pY4}C5J<|$aG4U{sEfzbc2e?oUg)UTl&e|20tu*V19@7BUk5J z&8T+QC9o=Gs*a(#h2}#q(OKMGTX*e#fYLfIT`&Z9%akF5fV>R)lIV{y555xyrkJlm zQxnZpDwu&FN5HWQBi98+&!wMO_4fFUrkxLl0V=x4MjbQfJ*b2kz`=LRbPk%7>;{v4 z$!N?&nb8>F!$>;%UF@P3d7d46VE_={nRzfRL9u1ZQ)h~{`gEzF%_#s%aa;YtR(7k4 zz9pNY(l`4xDFgaot%E@qoJ+jJ3Uu@-w{G29yAJryRIVd8K-S7F1b%B^h;Cjw|1^(d z5D7O7UI)_-c#e4xWK>!UzinDxtb^Ybtpw(gEFI(obTp&tOz9*HQxcV>E-#3Ri6#^})fre|^{bih$sMRQ@{BuKMzY!M?s+~_37Asg zchJSy!%Y}}2zC9%`A$>eg~u#1fJ zYmU-go1y*;oG=dtW)SQduw++x#eBS7a1%{mG=Z51!A}MH*!c&o3`-ZDP5`3GkR`g* zF#GoyoIp@9uTcUCH&V>IGy9k9f>{WNG}p#6n1jmigyH*Ykaf^!Z5@n8HnTjPPm+n! z=9B}wIdS8pV~k|u*2rI{=h8*^^4g40=Ah2du`8VqGg&&wF6xr=p;_0-k8AU+-Gva* zK8ko=b}^n)>Vb~33xz`T)kQlVdgfV( zm}pFPz^GK3z8IL}WFBQ?r5P;Tp2zxo29=OK(v-+~lEINhQij}Na~|eF1X=E+|1IlKp!|=8}u1kBUzRF)IGV8u$GR~rE4Th2hrTj8nwDX zixlVYW85l)hz^DJadJsHv`he980-=XPSHh27t*gY-SJ!%AVYS-1YR;# zG8uCXQJTEypX481T|mBDYs!Mx_r-`@G9Jaiy_9xSN%_17144AQRjmw>sVP>|B3TbX z(wf()q5^d+nhcPjX$3Hv3IF&bHmck%Lei^ztmbY8AL$BqbJJzsY^a)!=KBy-boD~b z$JiBX;4yAyf4eA4^YyI-40IbpZXD^yGqb&9=^*;3tp(WC{229XtCuaVU@nd>7tib;aJr6NQZ>yQ zG~LkTLLVi44fG@GhAvmoZrAVjOmVZ zXmCTqTs(7u0-ArxAav_iMdn%Ap3Xeax!6@OA-K{HOvcQ=X{MwLz?yc!YnTJ5>^41C zU{?jG&2qDwqxl$XElBh4xDK7VV$fPbz;zq+74zNkd*mip=e{7zYuU18)!qs@xVnKX9uL z$1XA1<8)=DmoA4+XA;p}lYT2*F=Pgy=*lkXuA!@3VNJ~T`aE_yV15{)UClvrQnHQ( z=;p{0TU7KM1bZ?nCts`i8U!f%)mWuOXY#cRQ1B`%bsU)~1E3Tupij-3T+e`wgAYDf zMq`?TENCXC-_e?&PZO;QFtX-?@;+oC)@RafFzhS=WT#}!EJtM?^uN(8$;|};f|Wh! zM>Y?u>5`^fet+h{^Q>lIt6c4J=y)6x#O=&}{5{i)xWP!ID}tJjRR<(X2hl%BE0b=x zG1l-oEMwDI2vJx_)bHXfVaJy#prdRD69lZRTJANuM z0t(GOZo@7*lI9g%WSQIb)bH)eBY6$4wu_YWQ`v-U%Nl?~rRpW6!up(cMW&g%W>t!I zGGc2IbDdCi9BW0Y{4!mVF^{7^ii(^Y60#5aw3w^LK{KcNxB1?zvUHGBpqYo@ zw=DpU6GFt+Y;${{68!rz2XtYu>khzkQ8(_?xns9hy0*EV2;*vnlJm7>8f-vP(oK(M0YJJe``r0Mj~2w;$Gxt7eepHmr3@gD6}H6+=>N7XXa<+{<7Naw#fqEN|#%!qzF7Jv5A&&^JT1a3xta_9;G@cpp# z6W0m>(J9!6^Q84c1^Va|y4Bpq``RT%lUeAB3-cVE>71r;0<+UKm4BA=4eT`HOn$#H2s=8Rk)pF{XJa-jPeZYtfc86yUHlMkGVXc&3sQ2LL?l; z?pGG6KE6x>U3lSmZtG*WFmz>>{Js#K?}@B1o?c$+-mJZ6w(XneWZo4!&DD-k_^{zi zg=jV5Fz1aM2Djd4*10hZo;z*pV1R|!94))*`!vs~L~8mVX(qJ}GD@2AbiOCA<%<1Jb9;>SuW4(r5V3C)IP4l&MDJOpP zgE{ItnL3DT(5}%cH54S-5W56fKSI_u z)byIvYIh$-585@~v@4Qpy%x(8=_2U-Fgh|)D|=H-Ua72Twk4zBF$6w-fHZsY-(+9r zka@QK(G|M&!|3e7vk7v14>V!gbyjf7FS08DY%Qmx^P4(V=;wxjg#xSvf$77duam5W zzALgY259J-cA9;vI?yy*4(v7)YHgT7lmDly`avdS?N-hd23_}TMh0`8anRpQWzJv} z27_2D>Zm!GQ)4ItvS#z3ACjfcbah&J9M_~1%c4vP0y=-I-(m+kQ!#T(St*8Wmi|H( zF`)UH=3wjQ*6e>ywhp4jN7swdK|kCpgedHy>_^F!f@8}x(1pQ{^|~C(@v|C-_2cEa z@s{Q?zjyXMv-S?NtJL9jz356k=@f<2^>qFK9?u9t?!u{9$ut-t$6Yo<&CL}RXl|pq z*C~nK>HgN@xw_DJo=+1icfOntl?R#A|JytF*l)`@j=w+iM??y9SVPJwhp-Y0k5f)z z9$`kAQLM)?!;qon6z050LuiQ^Sq(YmP-G4@q?JQYinU@6iP9hKW3RgJ+}+Q8e-FRk z_i!KH*Y@4BJ-_>VU7zc9-QW9tU)S}yJ|7+mJR}guYH51`;9-jc0y7!{CD#iNVIGe4 z?BeBhZC-jfPgJ8`v^hDn1-fpF0D`khJq~Bv1G@Sm&?Iy`>`G@9M5dg9))}yD#|*1_ zjDguG?5hRwBYal1AbuaYjpupBs zl5J+3$3vPj zB2+_3)rF4s5Q%EE`Zmth{i2c=c>wECXqqyW&lA9W(pWItu7^Tv%6iTVN!z{4i%~s- zO|{KQfv?-Lf4AApSk+(j+KwkQIZ9Qt7%ci3#J+6P(lGnNwfu&ki-?;i;+*z_>EYi; zA#{b1Oe z%XmE^4~{MfOg#zjtYg4+h%(ERP4^`Z6wKbMNrT(!p5`^o>I$nYO{Kc$I?eXBSg{Cj zQ-qzivyk$4vCZQvk9tqGhm=ljDE3^9W0q4 zzU6F*F=N(J4>sLaCqMe8gZI_zsS5$Bi+Vj%G;MPZttoX4{x#RHZ-zDbE!>37p{6~qa4O&+pM_Luw3)qOD9g>91~7WV{6aqG@o)ji2Y zK7*R#r_v8z7`PWDku30u5pn&r@fpJ3M&*))rpRu_LS_k7rc;-Njy zaw^~F;eyjkbqSHNWXWw>a9_L(Ibc0Y3Lr!c&%)}<0SuYtRtvLI_K)g&H*H>>#7AW> zxC}~wBMKqH+6prkyi=?e*^=a?I8d=6e0~ofnd(Z0yx~cqw4%4pFHxoaOvo-5#<_U=J?? z6!vRbVQ3ReM2(lxYB<2^QsN??L7WF=b%StjRWUBkrIK>w$q}*RhV?8z+8$mrDxoXH zZudg$b~x?LQVmWK%)nT3oRpGhp0TDr0`#)fMH>hmKgwBhzD;U}nFzB2V)ya(&OQsm zI&0w}&r(k5*+vu?mIGNvR?FE{Ac}tW1xCJ36yO1cbk{kI1Z#87xa^{7ZcZ6SKhGcH8X+8{0xQF@6XM z&MOEp{bL4*BMXAv=Frf_4-sb8!h;rJ;9z`HaA`!5mnN3N=r2o-%!JxpzFEG)p#$dx z`Z=v|NT1}XJj4(-mR=b%UOMoA#M6~)SfZOX%FU!UnL*NT_Uw3hn8u9CmVU~~J z;mpJRK4~^d-Q0)Vmjr{RAIxO2y)27<2Em9uOMav5)o$Ci?KfMuZbjrSpSQ1lzJ7E< zSBM<}=9s!S2XXB4h}!nf5MYMG!)4mTZ_x%LOn4qvlbJ5M4cFI80++FM6A{~cY5gXQ z9;MI~Vz+lOv$NQ3;s)|pO~3?tK>(3sCU;RUtX;5?qZPVB?8sb7s4$D&f^RHWX96bB z83KqLp}j8Xj72LpWYj`eh#h-%#Ducgt@!41b0%N{ODBNHJuM<8N4j3R8S8Ce^g`#E z-Fk08w{8L^urvarpUIVEEp!Em%=Dh!(tdBQ*aS>q#RNvZCs$`}p{o$dOuR8qlQcci z+=}m7H)jGSU;;A|V3~637`-do_*m#_WHOU;cEm^8;ZXUEH<>Rp0TVERbrIk&_`zd4 zCpQhYg|2~65kckDws2w7p!)A7U;-wv1_GQ7e%6>Gp3KVFLf1knGl`A)m9{|8vjX;Y zCSU?4umS>XxjcI;u`B20X`!1!Y+8z+d4c%uR%mhrp1eQ(U%@J|zvKtvf{T8}8MlmxF zll191zT!(wzywTS83YcGh!dAl@eu2@(9JojnR$?;QzVhw&cy^wzywxFfE;p%M8p}Z zY_#ufS?D$}f|6@8}&>?s(CSU?4ut)+&M#M)KS>YJ#vd}GP)HCxbl0GBpE(@yC)tG<@jF-R_ zl3pAU-yAQ3#r51mw>Xx{%snLG^85G&R_m%vzy!uc;Cqq|i-=3d)$Qf=)WAEk|GF%JfQ4=eQ0rA@<|nw3SJ0H3FaZ8)#M+D)2(39OgEJ0jw3>os9~ty}2UH0GK4 z1WD}O-E&PQxLFf0fz=VXSkktL`0nbObW0@x3*8#;h0MH#qz_AaX!(wYL$l&~@P6$jq&hJ}7Cg4wz#TOuz&dLtwk4*G9x?i>c1FY?Oe7ZlmND z{JqS)m81_ydf9@jc6BCT0;?x*yrkDf#I>t$+AY=zSm-)-Ph=+f?TA*sXU9ylIVNBN z(+ONc+OCKof1FE42w3PkG7xn$GV{HX-q2C=Y?cX_z>Wmo7ZGnXnC=m<&~CT^*T0#{3V zixZ=Ef4D7l-5;YHEX+(2y1r8qSrJ@Jzy#JyfGh)u90^igI!?es*YN?Wo0pjfN;*c; zBYS4L%{74`5csjAqa)(XA#`ReeX!8=bfhzri04O3Vy5R}0w%Cr0?gcwi3p+@x%7;H zg|24cP?UPzNyFEu<0hybpr2^M2yO7x^AfLYbIc!8^E2BnfGSDK++2b&>eTd z1iC`tB=*H4;`d!K$aZXufQ4>j+&cNK%w*s0C`nK2q=7cd1o}bX>m2Ke2=?P#20*|< zH-JH`cOf$oIu5RG8DRI^B@|FJ!n|Mf_5CDZq3h?kZOZD*d=?Sb9Yb?d z82#n`5%?jQK_cSR{+VuzH$%Wew;6gp|88bJvB+-zjGlMJ=9|DI0;fwlE+W2TOyeYA zp&RG8->lb}xnGg+)p0a^KMaWg@hFdvh;xV3DfeZ50v5XYnGNBB!>TwlcP(OR5>NA? z!|I)T(=!6UkVG8HlOp1(p4o15*FeBRw+85aZ?c%>9V&@~=)3j3KeoRY1pXZfY1T1vJ8qd!4rVu@rHTgWYa|YU=brSf2 zB$g&mx45ly7>3`rg>Lv`+?gI`=3OK`wfGz)Y3I%uXG2y&;2KG1Ncsk!h`3@Ejk*mJ zu+aG)SPOy7qH@a*B7fAYc@%dInaQ4l`1XfDGLbp=fZmf+!X6_~F zKuM1)KFt1H`bU6S+V_jknGvzQe_gP}LnC0J8`_8tq(7PY;3AFC7D@FB#cP(@y z9CUa2Mgp0+lO()7?;&ZALip|`>8_GULd)kiJ>m6lNhBKO^Jhtal7z5bD(R1swnxNu zJ?V?hG=YT@u+S|Orz;y8fy~@R5^+55B5FbFiR*4%ER(WSdP_+N zDc8+X{vin=`mdyGCH<%PTrKHel1N9x{utM>q{!#5lKvv;%81}P7ZWgn1_J*B2fMM+ TzU!jl00000NkvXXu0mjfDAAo4 literal 0 HcmV?d00001 diff --git a/docs/source/quick_start.rst b/docs/source/quick_start.rst new file mode 100644 index 000000000..b422b20d3 --- /dev/null +++ b/docs/source/quick_start.rst @@ -0,0 +1,16 @@ + +=========== +Quick start +=========== + +Synchronous TeleBot +------------------- +.. literalinclude:: ../examples/echo_bot.py + :language: python + +Asynchronous TeleBot +-------------------- +.. literalinclude:: ../examples/asynchronous_telebot/echo_bot.py + :language: python + + diff --git a/docs/source/sync_version/index.rst b/docs/source/sync_version/index.rst new file mode 100644 index 000000000..84e490db3 --- /dev/null +++ b/docs/source/sync_version/index.rst @@ -0,0 +1,43 @@ +=============== +TeleBot version +=============== + +TeleBot methods +--------------- +.. automodule:: telebot + :members: + :undoc-members: + :show-inheritance: + +custom_filters file +------------------------------ + +.. automodule:: telebot.custom_filters + :members: + :undoc-members: + :show-inheritance: + +Synchronous storage for states +------------------------------- + +.. automodule:: telebot.storage + :members: + :undoc-members: + :show-inheritance: + +handler_backends file +-------------------------------- + +.. automodule:: telebot.handler_backends + :members: + :undoc-members: + :show-inheritance: + +apihelper +------------------------ + +.. automodule:: telebot.apihelper + :members: + :undoc-members: + :show-inheritance: + diff --git a/docs/source/types.rst b/docs/source/types.rst new file mode 100644 index 000000000..074b12ab9 --- /dev/null +++ b/docs/source/types.rst @@ -0,0 +1,10 @@ +============ +Types of API +============ + + + +.. automodule:: telebot.types + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/util.rst b/docs/source/util.rst new file mode 100644 index 000000000..027dfe78d --- /dev/null +++ b/docs/source/util.rst @@ -0,0 +1,12 @@ +============ +Utils +============ + + +util file +------------------- + +.. automodule:: telebot.util + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file From 4521982837063fd5594bda77b19821218706e980 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 19 Feb 2022 22:17:29 +0500 Subject: [PATCH 0876/1808] Create .readthedocs.yml --- .readthedocs.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..a06f74c92 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,19 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Optionally build your docs in additional formats such as PDF and ePub +formats: all + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 + install: + - requirements: requirements.txt \ No newline at end of file From 7f43f26886ae4aada362bb053a1b5e9971d86282 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 19 Feb 2022 22:21:15 +0500 Subject: [PATCH 0877/1808] Add telebot --- .readthedocs.yml | 2 +- doc_req.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 doc_req.txt diff --git a/.readthedocs.yml b/.readthedocs.yml index a06f74c92..859c13484 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -16,4 +16,4 @@ formats: all python: version: 3.7 install: - - requirements: requirements.txt \ No newline at end of file + - requirements: doc_req.txt \ No newline at end of file diff --git a/doc_req.txt b/doc_req.txt new file mode 100644 index 000000000..0f0e93813 --- /dev/null +++ b/doc_req.txt @@ -0,0 +1 @@ +pytelegrambotapi \ No newline at end of file From 7a8e60ddc2cb9eafcd2c6f7c3f4031dbe772658f Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 19 Feb 2022 22:41:09 +0500 Subject: [PATCH 0878/1808] Update index.rst --- docs/source/index.rst | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index a7ad1e19e..98efb79ad 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -10,8 +10,35 @@ Welcome to pyTelegramBotAPI's documentation! ======= TeleBot ======= +TeleBot is synchronous and asynchronous implementation of `Telegram Bot API `_. -Contents +Chats +----- +English chat: `Private chat `__ + +Russian chat: `@pytelegrambotapi_talks_ru `__ + +News: `@pyTelegramBotAPI `__ + +Pypi: `Pypi `__ + +Source: `Github repository `__ + +Some features: +------------- +Easy to learn and use. + +Easy to understand. + +Both sync and async. + +Examples on features. + +States + +And more... + +Content -------- .. toctree:: @@ -33,4 +60,4 @@ Indices and tables * :ref:`modindex` * :ref:`search` - \ No newline at end of file + From 9bfc0b2c6f85228b51ee5dba0ffb3991d79e47d8 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 23:37:03 +0500 Subject: [PATCH 0879/1808] preventet breaking change --- telebot/asyncio_handler_backends.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 51ed603a7..866c5ed63 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -1,9 +1,13 @@ +from telebot.asyncio_middlewares import BaseMiddleware + + class State: def __init__(self) -> None: self.name = None + def __str__(self) -> str: return self.name - + class StatesGroup: def __init_subclass__(cls) -> None: From 6c31b53cd965ee200aba80449c4d3ff5adaecd45 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 19 Feb 2022 21:39:02 +0300 Subject: [PATCH 0880/1808] Fix check of the regexp and commands types --- telebot/__init__.py | 74 ++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c18f7dc5a..e068a9023 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2741,13 +2741,15 @@ def register_middleware_handler(self, callback, update_types=None): """ self.add_middleware_handler(callback, update_types) - def __check_commands_input(self, commands, method_name): + @staticmethod + def check_commands_input(commands, method_name): if not isinstance(commands, list) or not all(isinstance(item, str) for item in commands): logger.error(f"{method_name}: Commands filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use the supplied type.") - - def __check_regexp_input(self, regexp, method_name): + + @staticmethod + def check_regexp_input(regexp, method_name): if not isinstance(regexp, str): - logger.error(f"{method_name}: Regexp filter should be string. Not able to use the supplied type.") + logger.error(f"{method_name}: Regexp filter should be string. Not able to use the supplied type.") def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ @@ -2794,10 +2796,12 @@ def default_command(message): method_name = "message_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2839,10 +2843,12 @@ def register_message_handler(self, callback, content_types=None, commands=None, method_name = "register_message_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2875,10 +2881,12 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ method_name = "edited_message_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2920,10 +2928,12 @@ def register_edited_message_handler(self, callback, content_types=None, commands method_name = "register_edited_message_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_edited_message_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2956,10 +2966,12 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty method_name = "channel_post_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -2999,10 +3011,12 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N method_name = "register_channel_post_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -3033,10 +3047,12 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con method_name = "edited_channel_post_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") @@ -3076,10 +3092,12 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com method_name = "register_edited_channel_post_handler" if commands is not None: - self.__check_commands_input(commands, method_name) - + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + if regexp is not None: - self.__check_regexp_input(regexp, method_name) + self.check_regexp_input(regexp, method_name) if isinstance(content_types, str): logger.warning("register_edited_channel_post_handler: 'content_types' filter should be List of strings (content types), not string.") From 94be2abdbd7b471f4ecf223f009159c767b790b9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 19 Feb 2022 21:39:52 +0300 Subject: [PATCH 0881/1808] Typo --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e068a9023..567cfcb7b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2456,7 +2456,7 @@ def add_sticker_to_set( :param emojis: :param png_sticker: Required if `tgs_sticker` is None :param tgs_sticker: Required if `png_sticker` is None - :webm_sticker: + :param webm_sticker: :param mask_position: :return: """ From 9b20f41ece7688f4c3f961ecf1616252d56e03b3 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 19 Feb 2022 23:57:21 +0500 Subject: [PATCH 0882/1808] I18N class removed from telebot.util.py --- examples/i18n_class_example/i18n_class.py | 66 +++++++++++++++ examples/i18n_class_example/main.py | 3 +- telebot/util.py | 98 +++-------------------- 3 files changed, 80 insertions(+), 87 deletions(-) create mode 100644 examples/i18n_class_example/i18n_class.py diff --git a/examples/i18n_class_example/i18n_class.py b/examples/i18n_class_example/i18n_class.py new file mode 100644 index 000000000..aa02612a3 --- /dev/null +++ b/examples/i18n_class_example/i18n_class.py @@ -0,0 +1,66 @@ +import gettext +import os + + +class I18N: + """ + This class provides high-level tool for internationalization + It is based on gettext util. + """ + + def __init__(self, translations_path, domain_name: str): + self.path = translations_path + self.domain = domain_name + self.translations = self.find_translations() + + @property + def available_translations(self): + return list(self.translations) + + def gettext(self, text: str, lang: str = None): + """ + Singular translations + """ + if not lang or lang not in self.translations: + return text + + translator = self.translations[lang] + return translator.gettext(text) + + def ngettext(self, singular: str, plural: str, lang: str = None, n=1): + """ + Plural translations + """ + if not lang or lang not in self.translations: + if n == 1: + return singular + return plural + + translator = self.translations[lang] + return translator.ngettext(singular, plural, n) + + def find_translations(self): + """ + Looks for translations with passed 'domain' in passed 'path' + """ + if not os.path.exists(self.path): + raise RuntimeError(f"Translations directory by path: {self.path!r} was not found") + + result = {} + + for name in os.listdir(self.path): + translations_path = os.path.join(self.path, name, 'LC_MESSAGES') + + if not os.path.isdir(translations_path): + continue + + po_file = os.path.join(translations_path, self.domain + '.po') + mo_file = po_file[:-2] + 'mo' + + if os.path.isfile(po_file) and not os.path.isfile(mo_file): + raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") + + with open(mo_file, 'rb') as file: + result[name] = gettext.GNUTranslations(file) + + return result diff --git a/examples/i18n_class_example/main.py b/examples/i18n_class_example/main.py index 70718473a..23794f281 100644 --- a/examples/i18n_class_example/main.py +++ b/examples/i18n_class_example/main.py @@ -53,7 +53,7 @@ import keyboards from telebot import TeleBot, types, custom_filters from telebot.storage.memory_storage import StateMemoryStorage -from telebot.util import I18N +from i18n_class import I18N storage = StateMemoryStorage() bot = TeleBot("", state_storage=storage) @@ -71,6 +71,7 @@ def get_user_language(func): """ This decorator will pass to your handler current user's language """ + @wraps(func) def inner(*args, **kwargs): obj = args[0] diff --git a/telebot/util.py b/telebot/util.py index ebf746633..ae5d8bdc2 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -35,14 +35,12 @@ thread_local = threading.local() content_type_media = [ - 'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', - 'poll', + 'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll', 'venue', 'location' ] content_type_service = [ - 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', - 'group_chat_created', + 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', 'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', 'voice_chat_participants_invited', 'message_auto_delete_timer_changed' @@ -183,7 +181,7 @@ def wait(self): class CustomRequestResponse(): - def __init__(self, json_text, status_code=200, reason=""): + def __init__(self, json_text, status_code = 200, reason = ""): self.status_code = status_code self.text = json_text self.reason = reason @@ -286,7 +284,7 @@ def split_string(text: str, chars_per_string: int) -> List[str]: return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)] -def smart_split(text: str, chars_per_string: int = MAX_MESSAGE_LENGTH) -> List[str]: +def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]: """ Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. This is very useful for splitting one giant message into multiples. @@ -311,12 +309,9 @@ def _text_before_last(substr: str) -> str: part = text[:chars_per_string] - if "\n" in part: - part = _text_before_last("\n") - elif ". " in part: - part = _text_before_last(". ") - elif " " in part: - part = _text_before_last(" ") + if "\n" in part: part = _text_before_last("\n") + elif ". " in part: part = _text_before_last(". ") + elif " " in part: part = _text_before_last(" ") parts.append(part) text = text[len(part):] @@ -334,7 +329,7 @@ def escape(text: str) -> str: return text -def user_link(user: types.User, include_id: bool = False) -> str: +def user_link(user: types.User, include_id: bool=False) -> str: """ Returns an HTML user link. This is useful for reports. Attention: Don't forget to set parse_mode to 'HTML'! @@ -348,10 +343,10 @@ def user_link(user: types.User, include_id: bool = False) -> str: """ name = escape(user.first_name) return (f"{name}" - + (f" (
{user.id}
)" if include_id else "")) + + (f" (
{user.id}
)" if include_id else "")) -def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int = 2) -> types.InlineKeyboardMarkup: +def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup: """ Returns a reply markup from a dict in this format: {'text': kwargs} This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' @@ -452,26 +447,22 @@ def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) -def deprecated(warn: bool = True, alternative: Optional[Callable] = None): +def deprecated(warn: bool=True, alternative: Optional[Callable]=None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. :param warn: If True a warning is logged else an info :param alternative: The new function to use instead """ - def decorator(function): def wrapper(*args, **kwargs): - info = f"`{function.__name__}` is deprecated." + ( - f" Use `{alternative.__name__}` instead" if alternative else "") + info = f"`{function.__name__}` is deprecated." + (f" Use `{alternative.__name__}` instead" if alternative else "") if not warn: logger.info(info) else: logger.warning(info) return function(*args, **kwargs) - return wrapper - return decorator @@ -513,68 +504,3 @@ def antiflood(function, *args, **kwargs): msg = function(*args, **kwargs) finally: return msg - - -def find_translations(path, domain): - """ - Looks for translations with passed 'domain' in passed 'path' - """ - if not os.path.exists(path): - raise RuntimeError(f"Translations directory by path: {path!r} was not found") - - result = {} - - for name in os.listdir(path): - translations_path = os.path.join(path, name, 'LC_MESSAGES') - - if not os.path.isdir(translations_path): - continue - - po_file = os.path.join(translations_path, domain + '.po') - mo_file = po_file[:-2] + 'mo' - - if os.path.isfile(po_file) and not os.path.isfile(mo_file): - raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") - - with open(mo_file, 'rb') as file: - result[name] = gettext.GNUTranslations(file) - - return result - - -class I18N: - """ - This class provides high-level tool for internationalization - It is based on gettext util. - """ - - def __init__(self, translations_path, domain_name: str): - self.path = translations_path - self.domain = domain_name - self.translations = find_translations(self.path, self.domain) - - @property - def available_translations(self): - return list(self.translations) - - def gettext(self, text: str, lang: str = None): - """ - Singular translations - """ - if not lang or lang not in self.translations: - return text - - translator = self.translations[lang] - return translator.gettext(text) - - def ngettext(self, singular: str, plural: str, lang: str = None, n=1): - """ - Plural translations - """ - if not lang or lang not in self.translations: - if n == 1: - return singular - return plural - - translator = self.translations[lang] - return translator.ngettext(singular, plural, n) From 74e9780b30cff65a5f28c8bc18a1a06db16ce9bd Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sun, 20 Feb 2022 00:08:14 +0500 Subject: [PATCH 0883/1808] BaseMiddleware returned to it's original place && I18N middleware is now only in examples --- .../middleware/flooding_middleware.py | 2 +- .../asynchronous_telebot/middleware/i18n.py | 3 +- .../i18n_base_midddleware.py | 51 +++++++++++-------- .../i18n_middleware_example/main.py | 4 +- telebot/asyncio_handler_backends.py | 17 ++++++- 5 files changed, 50 insertions(+), 27 deletions(-) rename telebot/asyncio_middlewares.py => examples/asynchronous_telebot/middleware/i18n_middleware_example/i18n_base_midddleware.py (72%) diff --git a/examples/asynchronous_telebot/middleware/flooding_middleware.py b/examples/asynchronous_telebot/middleware/flooding_middleware.py index 761190d21..b8f589ede 100644 --- a/examples/asynchronous_telebot/middleware/flooding_middleware.py +++ b/examples/asynchronous_telebot/middleware/flooding_middleware.py @@ -1,6 +1,6 @@ # Just a little example of middleware handlers -from telebot.asyncio_middlewares import BaseMiddleware +from telebot.asyncio_handler_backends import BaseMiddleware from telebot.async_telebot import AsyncTeleBot from telebot.async_telebot import CancelUpdate bot = AsyncTeleBot('TOKEN') diff --git a/examples/asynchronous_telebot/middleware/i18n.py b/examples/asynchronous_telebot/middleware/i18n.py index cbbd82bb0..81281bc44 100644 --- a/examples/asynchronous_telebot/middleware/i18n.py +++ b/examples/asynchronous_telebot/middleware/i18n.py @@ -7,7 +7,6 @@ # But this example just to show the work of middlewares. import telebot -import telebot.asyncio_middlewares from telebot.async_telebot import AsyncTeleBot from telebot import asyncio_handler_backends import logging @@ -28,7 +27,7 @@ bot = AsyncTeleBot('TOKEN') -class LanguageMiddleware(telebot.asyncio_middlewares.BaseMiddleware): +class LanguageMiddleware(asyncio_handler_backends.BaseMiddleware): def __init__(self): self.update_types = ['message'] # Update types that will be handled by this middleware. async def pre_process(self, message, data): diff --git a/telebot/asyncio_middlewares.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/i18n_base_midddleware.py similarity index 72% rename from telebot/asyncio_middlewares.py rename to examples/asynchronous_telebot/middleware/i18n_middleware_example/i18n_base_midddleware.py index d5d0e4adb..c8502881f 100644 --- a/telebot/asyncio_middlewares.py +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/i18n_base_midddleware.py @@ -1,4 +1,8 @@ import contextvars +import gettext +import os + +from telebot.asyncio_handler_backends import BaseMiddleware try: from babel.support import LazyProxy @@ -7,25 +11,6 @@ except ImportError: babel_imported = False -from telebot import util - - -class BaseMiddleware: - """ - Base class for middleware. - - Your middlewares should be inherited from this class. - """ - - def __init__(self): - pass - - async def pre_process(self, message, data): - raise NotImplementedError - - async def post_process(self, message, data, exception): - raise NotImplementedError - class I18N(BaseMiddleware): """ @@ -41,7 +26,7 @@ def __init__(self, translations_path, domain_name: str): self.path = translations_path self.domain = domain_name - self.translations = util.find_translations(self.path, self.domain) + self.translations = self.find_translations() @property def available_translations(self): @@ -107,3 +92,29 @@ async def pre_process(self, message, data): async def post_process(self, message, data, exception): pass + + def find_translations(self): + """ + Looks for translations with passed 'domain' in passed 'path' + """ + if not os.path.exists(self.path): + raise RuntimeError(f"Translations directory by path: {self.path!r} was not found") + + result = {} + + for name in os.listdir(self.path): + translations_path = os.path.join(self.path, name, 'LC_MESSAGES') + + if not os.path.isdir(translations_path): + continue + + po_file = os.path.join(translations_path, self.domain + '.po') + mo_file = po_file[:-2] + 'mo' + + if os.path.isfile(po_file) and not os.path.isfile(mo_file): + raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") + + with open(mo_file, 'rb') as file: + result[name] = gettext.GNUTranslations(file) + + return result diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py index 34cfc0194..c4cd54c64 100644 --- a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py @@ -56,7 +56,7 @@ from telebot import types from telebot.async_telebot import AsyncTeleBot from telebot.asyncio_filters import TextMatchFilter, TextFilter -from telebot.asyncio_middlewares import I18N +from i18n_base_midddleware import I18N from telebot.asyncio_storage.memory_storage import StateMemoryStorage @@ -87,7 +87,7 @@ async def get_user_language(self, obj: Union[types.Message, types.CallbackQuery] storage = StateMemoryStorage() -bot = AsyncTeleBot("", state_storage=storage) +bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds", state_storage=storage) i18n = I18NMiddleware(translations_path='locales', domain_name='messages') _ = i18n.gettext # for singular translations diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 866c5ed63..c6db03b02 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -1,4 +1,17 @@ -from telebot.asyncio_middlewares import BaseMiddleware +class BaseMiddleware: + """ + Base class for middleware. + Your middlewares should be inherited from this class. + """ + + def __init__(self): + pass + + async def pre_process(self, message, data): + raise NotImplementedError + + async def post_process(self, message, data, exception): + raise NotImplementedError class State: @@ -15,4 +28,4 @@ def __init_subclass__(cls) -> None: for name, value in cls.__dict__.items(): if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable - value.name = ':'.join((cls.__name__, name)) + value.name = ':'.join((cls.__name__, name)) \ No newline at end of file From 5d7ae385ec32d7465a6011d7a10f8cff055e41d4 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sun, 20 Feb 2022 00:12:14 +0500 Subject: [PATCH 0884/1808] token removed. --- .../middleware/i18n_middleware_example/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py index c4cd54c64..cf06c079c 100644 --- a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py @@ -87,7 +87,7 @@ async def get_user_language(self, obj: Union[types.Message, types.CallbackQuery] storage = StateMemoryStorage() -bot = AsyncTeleBot("1254795383:AAE7gbj1aas4lEDHB1eVuZZhSGPWcH1B5ds", state_storage=storage) +bot = AsyncTeleBot("", state_storage=storage) i18n = I18NMiddleware(translations_path='locales', domain_name='messages') _ = i18n.gettext # for singular translations From 38bff65cafbb183668fe775178191e83a88c3551 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sun, 20 Feb 2022 00:28:27 +0500 Subject: [PATCH 0885/1808] removed unused imports from util.py --- telebot/util.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index ae5d8bdc2..bec041162 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -import gettext -import os import random import re import string From 4adac4d8520a07f5b22ac05110d6c2d353c6abaf Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 20 Feb 2022 00:40:25 +0500 Subject: [PATCH 0886/1808] Update quick_start.rst --- docs/source/quick_start.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/quick_start.rst b/docs/source/quick_start.rst index b422b20d3..27a779562 100644 --- a/docs/source/quick_start.rst +++ b/docs/source/quick_start.rst @@ -5,12 +5,12 @@ Quick start Synchronous TeleBot ------------------- -.. literalinclude:: ../examples/echo_bot.py +.. literalinclude:: ../../examples/echo_bot.py :language: python Asynchronous TeleBot -------------------- -.. literalinclude:: ../examples/asynchronous_telebot/echo_bot.py +.. literalinclude:: ../../examples/asynchronous_telebot/echo_bot.py :language: python From 7875ff293d6a0766e7d353317c6df6df1fde9a11 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 20 Feb 2022 00:44:08 +0500 Subject: [PATCH 0887/1808] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ea982e882..61015afc7 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ ##

Supporting Bot API version: 5.7! +

Official documentation

+ ## Contents * [Getting started](#getting-started) From 7993e1d1c961123adf02bfe9bda1a8e4150e5eeb Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Mon, 21 Feb 2022 20:08:03 +0500 Subject: [PATCH 0888/1808] corrected setup middleware in async i18n middleware example --- .../middleware/i18n_middleware_example/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py index cf06c079c..1faeaad42 100644 --- a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py @@ -209,6 +209,6 @@ async def missed_message(message: types.Message): if __name__ == '__main__': - bot.middlewares.append(i18n) + bot.setup_middleware(i18n) bot.add_custom_filter(TextMatchFilter()) asyncio.run(bot.infinity_polling()) From 102fe3a8fb7b87fa0bf5002e7ab99a111a101e3c Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 23 Feb 2022 13:07:25 +0500 Subject: [PATCH 0889/1808] Update asyncio_filters.py --- telebot/asyncio_filters.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 232870a7d..6c1fc4bf9 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -278,7 +278,10 @@ async def check(self, message, text): if text == '*': return True if isinstance(text, list): - new_text = [i.name for i in text] + new_text = [] + for i in text: + if isclass(i): i = i.name + new_text.append(i) text = new_text elif isinstance(text, object): text = text.name From b960a9e5748f250c2455ee31552d7e32aff235e7 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 23 Feb 2022 13:08:02 +0500 Subject: [PATCH 0890/1808] Update custom_filters.py --- telebot/custom_filters.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index e6a1531aa..030567370 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -282,7 +282,10 @@ def check(self, message, text): if text == '*': return True if isinstance(text, list): - new_text = [i.name for i in text] + new_text = [] + for i in text: + if isclass(i): i = i.name + new_text.append(i) text = new_text elif isinstance(text, object): text = text.name From dde9cd323cae35d8091acf947e3ee0b20d41fd9b Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 25 Feb 2022 19:45:52 +0500 Subject: [PATCH 0891/1808] Update asyncio_filters.py --- telebot/asyncio_filters.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 6c1fc4bf9..ec0baad3f 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -1,5 +1,6 @@ from abc import ABC from typing import Optional, Union +from telebot.asyncio_handler_backends import State from telebot import types @@ -280,10 +281,10 @@ async def check(self, message, text): if isinstance(text, list): new_text = [] for i in text: - if isclass(i): i = i.name + if isinstance(text, State): i = i.name new_text.append(i) text = new_text - elif isinstance(text, object): + elif isinstance(text, State): text = text.name if message.chat.type == 'group': From 70b9fc86d24356e649b6d07835bf00ef3f77ad4d Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 25 Feb 2022 19:46:49 +0500 Subject: [PATCH 0892/1808] Update custom_filters.py --- telebot/custom_filters.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 030567370..70bf8edf8 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -1,5 +1,6 @@ from abc import ABC from typing import Optional, Union +from telebot.handler_backends import State from telebot import types @@ -284,10 +285,10 @@ def check(self, message, text): if isinstance(text, list): new_text = [] for i in text: - if isclass(i): i = i.name + if isinstance(text, State): i = i.name new_text.append(i) text = new_text - elif isinstance(text, object): + elif isinstance(text, State): text = text.name if message.chat.type == 'group': group_state = self.bot.current_states.get_state(message.chat.id, message.from_user.id) From 75baf6dd96eb9bd3e6366f195e7b7cd0ed3d0975 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 25 Feb 2022 19:52:56 +0500 Subject: [PATCH 0893/1808] Update custom_filters.py --- telebot/custom_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 70bf8edf8..6d69865f1 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -285,7 +285,7 @@ def check(self, message, text): if isinstance(text, list): new_text = [] for i in text: - if isinstance(text, State): i = i.name + if isinstance(i, State): i = i.name new_text.append(i) text = new_text elif isinstance(text, State): From f4c76553ed9770cf010faaf680bb7eee128fda76 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 25 Feb 2022 19:53:17 +0500 Subject: [PATCH 0894/1808] Update asyncio_filters.py --- telebot/asyncio_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index ec0baad3f..037ec9d01 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -281,7 +281,7 @@ async def check(self, message, text): if isinstance(text, list): new_text = [] for i in text: - if isinstance(text, State): i = i.name + if isinstance(i, State): i = i.name new_text.append(i) text = new_text elif isinstance(text, State): From e585c77830268328f571c8feab8ede15b1b411ee Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 26 Feb 2022 22:43:03 +0500 Subject: [PATCH 0895/1808] Fix --- telebot/asyncio_filters.py | 21 +++++++++++++++++---- telebot/custom_filters.py | 26 ++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 6c1fc4bf9..1b397613e 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -1,5 +1,6 @@ from abc import ABC from typing import Optional, Union +from telebot.asyncio_handler_backends import State from telebot import types @@ -277,17 +278,29 @@ def __init__(self, bot): async def check(self, message, text): if text == '*': return True + # needs to work with callbackquery + if isinstance(message, types.Message): + chat_id = message.chat.id + user_id = message.from_user.id + + if isinstance(message, types.CallbackQuery): + + chat_id = message.message.chat.id + user_id = message.from_user.id + message = message.message + + if isinstance(text, list): new_text = [] for i in text: - if isclass(i): i = i.name + if isinstance(i, State): i = i.name new_text.append(i) text = new_text - elif isinstance(text, object): + elif isinstance(text, State): text = text.name if message.chat.type == 'group': - group_state = await self.bot.current_states.get_state(message.chat.id, message.from_user.id) + group_state = await self.bot.current_states.get_state(user_id, chat_id) if group_state == text: return True elif group_state in text and type(text) is list: @@ -295,7 +308,7 @@ async def check(self, message, text): else: - user_state = await self.bot.current_states.get_state(message.chat.id, message.from_user.id) + user_state = await self.bot.current_states.get_state(user_id, chat_id) if user_state == text: return True elif type(text) is list and user_state in text: diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 030567370..8442be43c 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -1,9 +1,12 @@ from abc import ABC from typing import Optional, Union +from telebot.handler_backends import State from telebot import types + + class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -280,17 +283,32 @@ def __init__(self, bot): def check(self, message, text): if text == '*': return True + + # needs to work with callbackquery + if isinstance(message, types.Message): + chat_id = message.chat.id + user_id = message.from_user.id + + if isinstance(message, types.CallbackQuery): + + chat_id = message.message.chat.id + user_id = message.from_user.id + message = message.message + + + if isinstance(text, list): new_text = [] for i in text: - if isclass(i): i = i.name + if isinstance(i, State): i = i.name new_text.append(i) text = new_text - elif isinstance(text, object): + elif isinstance(text, State): text = text.name + if message.chat.type == 'group': - group_state = self.bot.current_states.get_state(message.chat.id, message.from_user.id) + group_state = self.bot.current_states.get_state(user_id, chat_id) if group_state == text: return True elif group_state in text and type(text) is list: @@ -298,7 +316,7 @@ def check(self, message, text): else: - user_state = self.bot.current_states.get_state(message.chat.id, message.from_user.id) + user_state = self.bot.current_states.get_state(user_id, chat_id) if user_state == text: return True elif type(text) is list and user_state in text: From 1bfc082d46a0cf44af16df3ba1c17d5283ce54b9 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 26 Feb 2022 22:48:03 +0500 Subject: [PATCH 0896/1808] Update documentation --- docs/source/install.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/install.rst b/docs/source/install.rst index f195a43f8..f2589c914 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -28,8 +28,10 @@ By cloning repository Directly using pip ------------------ .. code-block:: bash + $ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git + It is generally recommended to use the first option. While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling: From 995e28e9d88a180373c89eb67218468adec7cabb Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 26 Feb 2022 22:50:55 +0500 Subject: [PATCH 0897/1808] Remove unnecessary thing --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 61015afc7..e2415f00b 100644 --- a/README.md +++ b/README.md @@ -769,7 +769,6 @@ See more examples in our [examples](https://github.com/eternnoir/pyTelegramBotAP Telegram Bot API support new type Chat for message.chat. - Check the ```type``` attribute in ```Chat``` object: -- ```python if message.chat.type == "private": # private chat message From c84896391eac80eb08b17d0636999499e921723e Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Tue, 1 Mar 2022 14:39:00 +0500 Subject: [PATCH 0898/1808] argument of 'func' parameter in handlers can be async function --- telebot/async_telebot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index be9e08d4c..464f65be7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5,6 +5,7 @@ import re import time import traceback +import inspect from typing import Any, List, Optional, Union # this imports are used to avoid circular import error @@ -593,6 +594,8 @@ async def _test_filter(self, message_filter, filter_value, message): elif message_filter == 'chat_types': return message.chat.type in filter_value elif message_filter == 'func': + if inspect.iscoroutinefunction(filter_value): + return await filter_value(message) return filter_value(message) elif self.custom_filters and message_filter in self.custom_filters: return await self._check_filter(message_filter,filter_value,message) From 4f654d9e12fbd56a6fbb93e13f4bd9632111fd3b Mon Sep 17 00:00:00 2001 From: WuerfelDev Date: Sun, 6 Mar 2022 04:26:40 +0100 Subject: [PATCH 0899/1808] Add TranslateThisBot to the bot list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e2415f00b..e093925d4 100644 --- a/README.md +++ b/README.md @@ -862,5 +862,6 @@ Here are some examples of template: * [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search. * [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. * [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. +* [TranslateThisBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 388477686b5a5ccab5a333245a128fed46731718 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Mar 2022 18:39:41 +0500 Subject: [PATCH 0900/1808] Added middlewares. Bumped middlewares --- telebot/__init__.py | 144 +++++++++++++++++++++++++++++++----- telebot/handler_backends.py | 41 +++++++++- 2 files changed, 165 insertions(+), 20 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 567cfcb7b..800d2f64b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -16,12 +16,16 @@ # storage from telebot.storage import StatePickleStorage, StateMemoryStorage + + logger = logging.getLogger('TeleBot') formatter = logging.Formatter( '%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"' ) +import inspect + console_output_handler = logging.StreamHandler(sys.stderr) console_output_handler.setFormatter(formatter) logger.addHandler(console_output_handler) @@ -29,7 +33,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, CancelUpdate, SkipHandler from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter @@ -147,7 +151,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, state_storage=StateMemoryStorage() + suppress_middleware_excepions=False, state_storage=StateMemoryStorage(), use_class_middlewares=False ): """ :param token: bot API token @@ -193,7 +197,8 @@ def __init__( self.current_states = state_storage - if apihelper.ENABLE_MIDDLEWARE: + self.use_class_middlewares = use_class_middlewares + if apihelper.ENABLE_MIDDLEWARE and not use_class_middlewares: self.typed_middleware_handlers = { 'message': [], 'edited_message': [], @@ -211,6 +216,13 @@ def __init__( 'chat_join_request': [] } self.default_middleware_handlers = [] + if apihelper.ENABLE_MIDDLEWARE and use_class_middlewares: + logger.warning( + 'You are using class based middlewares, but you have ' + 'ENABLE_MIDDLEWARE set to True. This is not recommended.' + ) + self.middlewares = [] if use_class_middlewares else None + self.threaded = threaded if self.threaded: @@ -440,6 +452,7 @@ def process_new_updates(self, updates): else: if update.update_id > self.last_update_id: self.last_update_id = update.update_id continue + if update.update_id > self.last_update_id: self.last_update_id = update.update_id @@ -519,46 +532,46 @@ def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) self._notify_reply_handlers(new_messages) self.__notify_update(new_messages) - self._notify_command_handlers(self.message_handlers, new_messages) + self._notify_command_handlers(self.message_handlers, new_messages, 'message') def process_new_edited_messages(self, edited_message): - self._notify_command_handlers(self.edited_message_handlers, edited_message) + self._notify_command_handlers(self.edited_message_handlers, edited_message, 'edited_message') def process_new_channel_posts(self, channel_post): - self._notify_command_handlers(self.channel_post_handlers, channel_post) + self._notify_command_handlers(self.channel_post_handlers, channel_post, 'channel_post') def process_new_edited_channel_posts(self, edited_channel_post): - self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post) + self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') def process_new_inline_query(self, new_inline_querys): - self._notify_command_handlers(self.inline_handlers, new_inline_querys) + self._notify_command_handlers(self.inline_handlers, new_inline_querys, 'inline_query') def process_new_chosen_inline_query(self, new_chosen_inline_querys): - self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_querys) + self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') def process_new_callback_query(self, new_callback_querys): - self._notify_command_handlers(self.callback_query_handlers, new_callback_querys) + self._notify_command_handlers(self.callback_query_handlers, new_callback_querys, 'callback_query') def process_new_shipping_query(self, new_shipping_querys): - self._notify_command_handlers(self.shipping_query_handlers, new_shipping_querys) + self._notify_command_handlers(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') def process_new_pre_checkout_query(self, pre_checkout_querys): - self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_querys) + self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') def process_new_poll(self, polls): - self._notify_command_handlers(self.poll_handlers, polls) + self._notify_command_handlers(self.poll_handlers, polls, 'poll') def process_new_poll_answer(self, poll_answers): - self._notify_command_handlers(self.poll_answer_handlers, poll_answers) + self._notify_command_handlers(self.poll_answer_handlers, poll_answers, 'poll_answer') def process_new_my_chat_member(self, my_chat_members): - self._notify_command_handlers(self.my_chat_member_handlers, my_chat_members) + self._notify_command_handlers(self.my_chat_member_handlers, my_chat_members, 'my_chat_member') def process_new_chat_member(self, chat_members): - self._notify_command_handlers(self.chat_member_handlers, chat_members) + self._notify_command_handlers(self.chat_member_handlers, chat_members, 'chat_member') def process_new_chat_join_request(self, chat_join_request): - self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request) + self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') def process_middlewares(self, update): for update_type, middlewares in self.typed_middleware_handlers.items(): @@ -2535,6 +2548,20 @@ def register_next_step_handler( chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) + + def setup_middleware(self, middleware: BaseMiddleware): + """ + Register middleware + :param middleware: Subclass of `telebot.handler_backends.BaseMiddleware` + :return: None + """ + if not self.use_class_middlewares: + logger.warning('Middleware is not enabled. Pass use_class_middlewares=True to enable it.') + return + self.middlewares.append(middleware) + + + def set_state(self, user_id: int, state: Union[int, str], chat_id: int=None) -> None: """ Sets a new state of a user. @@ -3500,16 +3527,95 @@ def _check_filter(self, message_filter, filter_value, message): logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") return False - def _notify_command_handlers(self, handlers, new_messages): + # middleware check-up method + def _check_middleware(self, update_type): + """ + Check middleware + :param message: + :return: + """ + if self.middlewares: middlewares = [i for i in self.middlewares if update_type in i.update_types] + if not middlewares: return + return middlewares + + def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, **kwargs): + """This class is made to run handler and middleware in queue. + :param handler: handler that should be executed. + :param middleware: middleware that should be executed. + :return: + """ + data = {} + params =[] + handler_error = None + skip_handler = False + if middlewares: + for middleware in middlewares: + result = middleware.pre_process(message, data) + # We will break this loop if CancelUpdate is returned + # Also, we will not run other middlewares + if isinstance(result, CancelUpdate): + return + elif isinstance(result, SkipHandler) and skip_handler is False: + skip_handler = True + + + + + try: + if handlers and not skip_handler: + for handler in handlers: + process_handler = self._test_message_handler(handler, message) + if not process_handler: continue + else: + for i in inspect.signature(handler['function']).parameters: + params.append(i) + if len(params) == 1: + handler['function'](message) + + elif len(params) == 2: + if handler.get('pass_bot') is True: + handler['function'](message, self) + + elif handler.get('pass_bot') is False: + handler['function'](message, data) + + elif len(params) == 3: + if params[2] == 'bot' and handler.get('pass_bot') is True: + handler['function'](message, data, self) + + else: + handler['function'](message, self, data) + + except Exception as e: + handler_error = e + + if not middlewares: + if self.exception_handler: + return self.exception_handler.handle(e) + logging.error(str(e)) + return + if middlewares: + for middleware in middlewares: + middleware.post_process(message, data, handler_error) + + + + + def _notify_command_handlers(self, handlers, new_messages, update_type): """ Notifies command handlers :param handlers: :param new_messages: :return: """ - if len(handlers) == 0: + if len(handlers) == 0 and not self.use_class_middlewares: return + for message in new_messages: + middleware = self._check_middleware(update_type) + if self.use_class_middlewares and middleware: + self._exec_task(self._run_middlewares_and_handler, message, handlers=handlers, middlewares=middleware) + return for message_handler in handlers: if self._test_message_handler(message_handler, message): self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index d88457b3a..f696d6bd2 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -165,4 +165,43 @@ def __init_subclass__(cls) -> None: # change value of that variable value.name = ':'.join((cls.__name__, name)) - \ No newline at end of file + +class BaseMiddleware: + """ + Base class for middleware. + Your middlewares should be inherited from this class. + """ + + def __init__(self): + pass + + def pre_process(self, message, data): + raise NotImplementedError + + def post_process(self, message, data, exception): + raise NotImplementedError + + +class SkipHandler: + """ + Class for skipping handlers. + Just return instance of this class + in middleware to skip handler. + Update will go to post_process, + but will skip execution of handler. + """ + + def __init__(self) -> None: + pass + +class CancelUpdate: + """ + Class for canceling updates. + Just return instance of this class + in middleware to skip update. + Update will skip handler and execution + of post_process in middlewares. + """ + + def __init__(self) -> None: + pass \ No newline at end of file From 441a5793ccca63b5f525fd2babb9d4aa52094d69 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Mar 2022 19:41:54 +0500 Subject: [PATCH 0901/1808] Update docstrings to correct documentation. --- telebot/__init__.py | 193 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 167 insertions(+), 26 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 800d2f64b..c00be67ca 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -389,6 +389,7 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, long_polling_timeout: int=20) -> List[types.Update]: """ Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. + :param allowed_updates: Array of string. List the types of updates you want your bot to receive. :param offset: Integer. Identifier of the first update to be returned. :param limit: Integer. Limits the number of updates to be retrieved. @@ -402,6 +403,7 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, def __skip_updates(self): """ Get and discard all pending updates before first poll of the bot + :return: """ self.get_updates(offset=-1) @@ -410,6 +412,7 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update """ Retrieves any updates from the Telegram API. Registered listeners and applicable message handlers will be notified when a new message arrives. + :raises ApiException when a call has failed. """ if self.skip_pending: @@ -422,6 +425,11 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update self.process_new_updates(updates) def process_new_updates(self, updates): + """ + Processes new updates. Just pass list of subclasses of Update to this method. + + :param updates: List of Update objects + """ upd_count = len(updates) logger.debug('Received {0} new updates'.format(upd_count)) if upd_count == 0: return @@ -645,6 +653,7 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim Warning: Do not call this function more than once! Always get updates. + :param interval: Delay between two update retrivals :param non_stop: Do not stop polling when an ApiException occurs. :param timeout: Request connection timeout @@ -829,10 +838,17 @@ def get_file(self, file_id: str) -> types.File: On success, a File object is returned. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling get_file again. + + :param file_id: File identifier """ return types.File.de_json(apihelper.get_file(self.token, file_id)) def get_file_url(self, file_id: str) -> str: + """ + Get a valid URL for downloading a file. + + :param file_id: File identifier to get download URL for. + """ return apihelper.get_file_url(self.token, file_id) def download_file(self, file_path: str) -> bytes: @@ -864,7 +880,8 @@ def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, """ Retrieves the user profile photos of the person with 'user_id' See https://core.telegram.org/bots/api#getuserprofilephotos - :param user_id: + + :param user_id: Integer - Unique identifier of the target user :param offset: :param limit: :return: API reply. @@ -876,6 +893,7 @@ def get_chat(self, chat_id: Union[int, str]) -> types.Chat: """ 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. + :param chat_id: :return: """ @@ -885,6 +903,7 @@ def get_chat(self, chat_id: Union[int, str]) -> types.Chat: def leave_chat(self, chat_id: Union[int, str]) -> bool: """ Use this method for your bot to leave a group, supergroup or channel. Returns True on success. + :param chat_id: :return: """ @@ -896,6 +915,7 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) :return: @@ -914,6 +934,7 @@ def get_chat_members_count(self, chat_id: Union[int, str]) -> int: def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ Use this method to get the number of members in a chat. Returns Int on success. + :param chat_id: :return: """ @@ -926,6 +947,7 @@ def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param sticker_set_name: Name of the sticker set to be set as the group sticker set @@ -939,6 +961,7 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :return: @@ -949,6 +972,7 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. + :param chat_id: :param user_id: :return: @@ -1002,6 +1026,7 @@ def forward_message( timeout: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. + :param disable_notification: :param chat_id: which chat to forward :param from_chat_id: which chat message from @@ -1028,6 +1053,7 @@ def copy_message( timeout: Optional[int]=None) -> int: """ Use this method to copy messages of any kind. + :param chat_id: which chat to forward :param from_chat_id: which chat message from :param message_id: message id @@ -1051,6 +1077,7 @@ def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: """ Use this method to delete message. Returns True on success. + :param chat_id: in which chat to delete :param message_id: which message to delete :param timeout: @@ -1068,6 +1095,7 @@ def send_dice( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send dices. + :param chat_id: :param emoji: :param disable_notification: @@ -1096,6 +1124,7 @@ def send_photo( timeout: Optional[int]=None,) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. + :param chat_id: :param photo: :param caption: @@ -1134,6 +1163,7 @@ def send_audio( """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. + :param chat_id:Unique identifier for the message recipient :param audio:Audio file to send. :param caption: @@ -1174,6 +1204,7 @@ def send_voice( """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. + :param chat_id:Unique identifier for the message recipient. :param voice: :param caption: @@ -1214,6 +1245,7 @@ def send_document( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send general files. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param document: (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 @@ -1258,6 +1290,7 @@ def send_sticker( data: Union[Any, str]=None) -> types.Message: """ Use this method to send .webp stickers. + :param chat_id: :param sticker: :param data: @@ -1300,6 +1333,7 @@ def send_video( data: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param video: Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video file using multipart/form-data. :param duration: Duration of sent video in seconds @@ -1346,6 +1380,7 @@ def send_animation( timeout: Optional[int]=None, ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server @@ -1387,6 +1422,7 @@ def send_video_note( """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server @@ -1417,7 +1453,8 @@ def send_media_group( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: """ - send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + Send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + :param chat_id: :param media: :param disable_notification: @@ -1448,6 +1485,7 @@ def send_location( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send point on the map. + :param chat_id: :param latitude: :param longitude: @@ -1481,7 +1519,8 @@ def edit_message_live_location( heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None) -> types.Message: """ - Use this method to edit live location + Use this method to edit live location. + :param latitude: :param longitude: :param chat_id: @@ -1509,6 +1548,7 @@ def stop_message_live_location( """ Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires + :param chat_id: :param message_id: :param inline_message_id: @@ -1537,6 +1577,7 @@ def send_venue( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel :param latitude: Float : Latitude of the venue :param longitude: Float : Longitude of the venue @@ -1584,6 +1625,7 @@ def send_chat_action( Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). + :param chat_id: :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', @@ -1612,6 +1654,7 @@ def ban_chat_member( In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless unbanned first. Returns True on success. + :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup :param user_id: Int : Unique identifier of the target user :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or @@ -1657,7 +1700,7 @@ def restrict_chat_member( The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. - :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :param user_id: Int : Unique identifier of the target user :param until_date: Date when restrictions will be lifted for the user, unix time. @@ -1755,7 +1798,6 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i for this to work and must have the appropriate administrator rights. Returns True on success. - :params: :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param sender_chat_id: Unique identifier of the target sender chat :return: True on success. @@ -1896,6 +1938,7 @@ def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param photo: InputFile: New chat photo, uploaded using multipart/form-data @@ -1910,6 +1953,7 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) """ @@ -1920,6 +1964,7 @@ def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, """ Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :param language_code: A two-letter ISO 639-1 language code. If empty, @@ -1934,6 +1979,7 @@ def set_my_commands(self, commands: List[types.BotCommand], language_code: Optional[str]=None) -> bool: """ Use this method to change the list of the bot's commands. + :param commands: List of BotCommand. At most 100 commands can be specified. :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. @@ -1950,6 +1996,7 @@ def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, Use this method to delete the list of the bot's commands for the given scope and user language. After deletion, higher level commands will be shown to affected users. Returns True on success. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :param language_code: A two-letter ISO 639-1 language code. If empty, @@ -1965,6 +2012,7 @@ def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param title: New chat title, 1-255 characters @@ -1991,6 +2039,7 @@ def pin_chat_message( Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param message_id: Int: Identifier of a message to pin @@ -2005,6 +2054,7 @@ def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int] Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param message_id: Int: Identifier of a message to unpin @@ -2017,6 +2067,7 @@ def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: Use this method to unpin a all pinned messages in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :return: @@ -2034,6 +2085,7 @@ def edit_message_text( reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. + :param text: :param chat_id: :param message_id: @@ -2062,6 +2114,7 @@ def edit_message_media( If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. + :param media: :param chat_id: :param message_id: @@ -2081,6 +2134,7 @@ def edit_message_reply_markup( reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. + :param chat_id: :param message_id: :param inline_message_id: @@ -2102,6 +2156,7 @@ def send_game( protect_content: Optional[bool]=None) -> types.Message: """ Used to send the game + :param chat_id: :param game_short_name: :param disable_notification: @@ -2126,7 +2181,8 @@ def set_game_score( inline_message_id: Optional[str]=None, disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: """ - Sets the value of points in the game to a specific user + Sets the value of points in the game to a specific user. + :param user_id: :param score: :param force: @@ -2148,6 +2204,7 @@ def get_game_high_scores( inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ Gets top points and game play + :param user_id: :param chat_id: :param message_id: @@ -2179,7 +2236,8 @@ def send_invoice( suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Sends invoice + Sends invoice. + :param chat_id: Unique identifier for the target private chat :param title: Product name :param description: Product description @@ -2248,7 +2306,8 @@ def send_poll( explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Send polls + Sends a poll. + :param chat_id: :param question: :param options: array of str with answers @@ -2286,7 +2345,8 @@ def stop_poll( self, chat_id: Union[int, str], message_id: int, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: """ - Stops poll + Stops a poll. + :param chat_id: :param message_id: :param reply_markup: @@ -2299,7 +2359,8 @@ def answer_shipping_query( shipping_options: Optional[List[types.ShippingOption]]=None, error_message: Optional[str]=None) -> bool: """ - Asks for an answer to a shipping question + Asks for an answer to a shipping question. + :param shipping_query_id: :param ok: :param shipping_options: @@ -2312,7 +2373,8 @@ def answer_pre_checkout_query( self, pre_checkout_query_id: int, ok: bool, error_message: Optional[str]=None) -> bool: """ - Response to a request for pre-inspection + Response to a request for pre-inspection. + :param pre_checkout_query_id: :param ok: :param error_message: @@ -2328,7 +2390,8 @@ def edit_message_caption( caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ - Use this method to edit captions of messages + Use this method to edit captions of messages. + :param caption: :param chat_id: :param message_id: @@ -2349,6 +2412,7 @@ def edit_message_caption( def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` + :param message: :param text: :param kwargs: @@ -2367,6 +2431,7 @@ def answer_inline_query( """ Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. + :param inline_query_id: Unique identifier for the answered query :param results: Array of results for the inline query :param cache_time: The maximum amount of time in seconds that the result of the inline query @@ -2390,6 +2455,7 @@ def answer_callback_query( """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. + :param callback_query_id: :param text: :param show_alert: @@ -2404,12 +2470,17 @@ def set_sticker_set_thumb( """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. + + :param name: Sticker set name + :param user_id: User identifier + :param thumb: """ return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. + :param name: :return: """ @@ -2420,6 +2491,7 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> typ """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. + :param user_id: :param png_sticker: :return: @@ -2439,6 +2511,7 @@ def create_new_sticker_set( Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success. + :param user_id: :param name: :param title: @@ -2464,6 +2537,7 @@ def add_sticker_to_set( Use this method to add a new sticker to a set created by the bot. It's required to pass `png_sticker` or `tgs_sticker`. Returns True on success. + :param user_id: :param name: :param emojis: @@ -2479,6 +2553,7 @@ def add_sticker_to_set( def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. + :param sticker: :param position: :return: @@ -2488,6 +2563,7 @@ def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. + :param sticker: :return: """ @@ -2523,6 +2599,7 @@ def register_for_reply_by_message_id( def _notify_reply_handlers(self, new_messages) -> None: """ Notify handlers of the answers + :param new_messages: :return: """ @@ -2552,6 +2629,7 @@ def register_next_step_handler( def setup_middleware(self, middleware: BaseMiddleware): """ Register middleware + :param middleware: Subclass of `telebot.handler_backends.BaseMiddleware` :return: None """ @@ -2565,6 +2643,7 @@ def setup_middleware(self, middleware: BaseMiddleware): def set_state(self, user_id: int, state: Union[int, str], chat_id: int=None) -> None: """ Sets a new state of a user. + :param user_id: :param state: new state. can be string or integer. :param chat_id: @@ -2576,6 +2655,7 @@ def set_state(self, user_id: int, state: Union[int, str], chat_id: int=None) -> def reset_data(self, user_id: int, chat_id: int=None): """ Reset data for a user in chat. + :param user_id: :param chat_id: """ @@ -2586,6 +2666,7 @@ def reset_data(self, user_id: int, chat_id: int=None): def delete_state(self, user_id: int, chat_id: int=None) -> None: """ Delete the current state of a user. + :param user_id: :param chat_id: :return: @@ -2602,6 +2683,7 @@ def retrieve_data(self, user_id: int, chat_id: int=None) -> Optional[Union[int, def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str]]: """ Get current state of a user. + :param user_id: :param chat_id: :return: state of a user @@ -2613,6 +2695,7 @@ def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str] def add_data(self, user_id: int, chat_id:int=None, **kwargs): """ Add data to states. + :param user_id: :param chat_id: """ @@ -2672,6 +2755,7 @@ def clear_reply_handlers_by_message_id(self, message_id: int) -> None: def _notify_next_handlers(self, new_messages): """ Description: TBD + :param new_messages: :return: """ @@ -2690,6 +2774,7 @@ def _notify_next_handlers(self, new_messages): def _build_handler_dict(handler, pass_bot=False, **filters): """ Builds a dictionary for a handler + :param handler: :param filters: :return: @@ -2850,6 +2935,8 @@ def decorator(handler): def add_message_handler(self, handler_dict): """ Adds a message handler + Note that you should use register_message_handler to add message_handler to the bot. + :param handler_dict: :return: """ @@ -2858,6 +2945,7 @@ def add_message_handler(self, handler_dict): def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers message handler. + :param callback: function to be called :param content_types: list of content_types :param commands: list of commands @@ -2894,6 +2982,7 @@ def register_message_handler(self, callback, content_types=None, commands=None, def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Edit message handler decorator + :param commands: :param regexp: :param func: @@ -2935,6 +3024,7 @@ def decorator(handler): def add_edited_message_handler(self, handler_dict): """ Adds the edit message handler + Note that you should use register_edited_message_handler to add edited_message_handler to the bot. :param handler_dict: :return: """ @@ -2943,6 +3033,7 @@ def add_edited_message_handler(self, handler_dict): def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers edited message handler. + :param callback: function to be called :param content_types: list of content_types :param commands: list of commands @@ -2980,6 +3071,7 @@ def register_edited_message_handler(self, callback, content_types=None, commands def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Channel post handler decorator + :param commands: :param regexp: :param func: @@ -3019,6 +3111,8 @@ def decorator(handler): def add_channel_post_handler(self, handler_dict): """ Adds channel post handler + Note that you should use register_channel_post_handler to add channel_post_handler to the bot. + :param handler_dict: :return: """ @@ -3027,6 +3121,7 @@ def add_channel_post_handler(self, handler_dict): def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers channel post message handler. + :param callback: function to be called :param content_types: list of content_types :param commands: list of commands @@ -3061,6 +3156,7 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Edit channel post handler decorator + :param commands: :param regexp: :param func: @@ -3100,6 +3196,8 @@ def decorator(handler): def add_edited_channel_post_handler(self, handler_dict): """ Adds the edit channel post handler + Note that you should use register_edited_channel_post_handler to add edited_channel_post_handler to the bot. + :param handler_dict: :return: """ @@ -3142,6 +3240,7 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com def inline_handler(self, func, **kwargs): """ Inline call handler decorator + :param func: :param kwargs: :return: @@ -3156,6 +3255,8 @@ def decorator(handler): def add_inline_handler(self, handler_dict): """ Adds inline call handler + Note that you should use register_inline_handler to add inline_handler to the bot. + :param handler_dict: :return: """ @@ -3164,6 +3265,7 @@ def add_inline_handler(self, handler_dict): def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers inline handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3175,6 +3277,7 @@ def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): def chosen_inline_handler(self, func, **kwargs): """ Description: TBD + :param func: :param kwargs: :return: @@ -3189,6 +3292,8 @@ def decorator(handler): def add_chosen_inline_handler(self, handler_dict): """ Description: TBD + Note that you should use register_chosen_inline_handler to add chosen_inline_handler to the bot. + :param handler_dict: :return: """ @@ -3208,6 +3313,7 @@ def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwarg def callback_query_handler(self, func, **kwargs): """ Callback request handler decorator + :param func: :param kwargs: :return: @@ -3222,6 +3328,8 @@ def decorator(handler): def add_callback_query_handler(self, handler_dict): """ Adds a callback request handler + Note that you should use register_callback_query_handler to add callback_query_handler to the bot. + :param handler_dict: :return: """ @@ -3229,7 +3337,8 @@ def add_callback_query_handler(self, handler_dict): def register_callback_query_handler(self, callback, func, pass_bot=False, **kwargs): """ - Registers callback query handler.. + Registers callback query handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3241,6 +3350,7 @@ def register_callback_query_handler(self, callback, func, pass_bot=False, **kwar def shipping_query_handler(self, func, **kwargs): """ Shipping request handler + :param func: :param kwargs: :return: @@ -3254,7 +3364,9 @@ def decorator(handler): def add_shipping_query_handler(self, handler_dict): """ - Adds a shipping request handler + Adds a shipping request handler. + Note that you should use register_shipping_query_handler to add shipping_query_handler to the bot. + :param handler_dict: :return: """ @@ -3263,6 +3375,7 @@ def add_shipping_query_handler(self, handler_dict): def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers shipping query handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3274,6 +3387,7 @@ def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwar def pre_checkout_query_handler(self, func, **kwargs): """ Pre-checkout request handler + :param func: :param kwargs: :return: @@ -3288,6 +3402,8 @@ def decorator(handler): def add_pre_checkout_query_handler(self, handler_dict): """ Adds a pre-checkout request handler + Note that you should use register_pre_checkout_query_handler to add pre_checkout_query_handler to the bot. + :param handler_dict: :return: """ @@ -3296,6 +3412,7 @@ def add_pre_checkout_query_handler(self, handler_dict): def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers pre-checkout request handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3307,6 +3424,7 @@ def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, ** def poll_handler(self, func, **kwargs): """ Poll request handler + :param func: :param kwargs: :return: @@ -3321,6 +3439,8 @@ def decorator(handler): def add_poll_handler(self, handler_dict): """ Adds a poll request handler + Note that you should use register_poll_handler to add poll_handler to the bot. + :param handler_dict: :return: """ @@ -3329,6 +3449,7 @@ def add_poll_handler(self, handler_dict): def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3340,6 +3461,7 @@ def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): def poll_answer_handler(self, func=None, **kwargs): """ Poll_answer request handler + :param func: :param kwargs: :return: @@ -3353,7 +3475,9 @@ def decorator(handler): def add_poll_answer_handler(self, handler_dict): """ - Adds a poll_answer request handler + Adds a poll_answer request handler. + Note that you should use register_poll_answer_handler to add poll_answer_handler to the bot. + :param handler_dict: :return: """ @@ -3362,6 +3486,7 @@ def add_poll_answer_handler(self, handler_dict): def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll answer handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3372,7 +3497,8 @@ def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs) def my_chat_member_handler(self, func=None, **kwargs): """ - my_chat_member handler + my_chat_member handler. + :param func: :param kwargs: :return: @@ -3386,7 +3512,9 @@ def decorator(handler): def add_my_chat_member_handler(self, handler_dict): """ - Adds a my_chat_member handler + Adds a my_chat_member handler. + Note that you should use register_my_chat_member_handler to add my_chat_member_handler to the bot. + :param handler_dict: :return: """ @@ -3395,6 +3523,7 @@ def add_my_chat_member_handler(self, handler_dict): def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers my chat member handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3405,7 +3534,8 @@ def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, * def chat_member_handler(self, func=None, **kwargs): """ - chat_member handler + chat_member handler. + :param func: :param kwargs: :return: @@ -3419,7 +3549,9 @@ def decorator(handler): def add_chat_member_handler(self, handler_dict): """ - Adds a chat_member handler + Adds a chat_member handler. + Note that you should use register_chat_member_handler to add chat_member_handler to the bot. + :param handler_dict: :return: """ @@ -3428,6 +3560,7 @@ def add_chat_member_handler(self, handler_dict): def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat member handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3439,6 +3572,7 @@ def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kw def chat_join_request_handler(self, func=None, **kwargs): """ chat_join_request handler + :param func: :param kwargs: :return: @@ -3452,7 +3586,9 @@ def decorator(handler): def add_chat_join_request_handler(self, handler_dict): """ - Adds a chat_join_request handler + Adds a chat_join_request handler. + Note that you should use register_chat_join_request_handler to add chat_join_request_handler to the bot. + :param handler_dict: :return: """ @@ -3472,6 +3608,7 @@ def register_chat_join_request_handler(self, callback, func=None, pass_bot=False def _test_message_handler(self, message_handler, message): """ Test message handler + :param message_handler: :param message: :return: @@ -3485,16 +3622,19 @@ def _test_message_handler(self, message_handler, message): return True - def add_custom_filter(self, custom_filter): + def add_custom_filter(self, custom_filter: Union[SimpleCustomFilter, AdvancedCustomFilter]): """ Create custom filter. + custom_filter: Class with check(message) method. + :param custom_filter: Custom filter class with key. """ self.custom_filters[custom_filter.key] = custom_filter def _test_filter(self, message_filter, filter_value, message): """ Test filters + :param message_filter: Filter type passed in handler :param filter_value: Filter value passed in handler :param message: Message to test @@ -3531,6 +3671,7 @@ def _check_filter(self, message_filter, filter_value, message): def _check_middleware(self, update_type): """ Check middleware + :param message: :return: """ @@ -3539,7 +3680,9 @@ def _check_middleware(self, update_type): return middlewares def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, **kwargs): - """This class is made to run handler and middleware in queue. + """ + This class is made to run handler and middleware in queue. + :param handler: handler that should be executed. :param middleware: middleware that should be executed. :return: @@ -3557,9 +3700,6 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** return elif isinstance(result, SkipHandler) and skip_handler is False: skip_handler = True - - - try: if handlers and not skip_handler: @@ -3603,7 +3743,8 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** def _notify_command_handlers(self, handlers, new_messages, update_type): """ - Notifies command handlers + Notifies command handlers. + :param handlers: :param new_messages: :return: From 3c7d3c019613d56b08dbe3a18d75d1b8f137df54 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Mar 2022 19:52:42 +0500 Subject: [PATCH 0902/1808] Fix tests --- telebot/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c00be67ca..6762af880 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3675,8 +3675,9 @@ def _check_middleware(self, update_type): :param message: :return: """ - if self.middlewares: middlewares = [i for i in self.middlewares if update_type in i.update_types] - if not middlewares: return + middlewares = None + if self.middlewares: + middlewares = [i for i in self.middlewares if update_type in i.update_types] return middlewares def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, **kwargs): From 78bdf1ca4e9dd9117b557733f6e3a02acf43aa39 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Mar 2022 23:14:07 +0500 Subject: [PATCH 0903/1808] Update docstrings --- telebot/__init__.py | 207 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 193 insertions(+), 14 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6762af880..0d9c22778 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -340,6 +340,8 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setwebhook + :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration :param certificate: Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details. @@ -355,7 +357,7 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ resolved through DNS :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout - :return: + :return: API reply. """ return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout) @@ -364,6 +366,8 @@ def delete_webhook(self, drop_pending_updates=None, timeout=None): """ Use this method to remove webhook integration if you decide to switch back to getUpdates. + Telegram documentation: https://core.telegram.org/bots/api#deletewebhook + :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout :return: bool @@ -375,6 +379,8 @@ def get_webhook_info(self, timeout: Optional[int]=None): Use this method to get current webhook status. Requires no parameters. If the bot is using getUpdates, will return an object with the url field empty. + Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo + :param timeout: Integer. Request connection timeout :return: On success, returns a WebhookInfo object. """ @@ -390,6 +396,8 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, """ Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. + Telegram documentation: https://core.telegram.org/bots/api#getupdates + :param allowed_updates: Array of string. List the types of updates you want your bot to receive. :param offset: Integer. Identifier of the first update to be returned. :param limit: Integer. Limits the number of updates to be retrieved. @@ -827,6 +835,8 @@ def set_update_listener(self, listener): def get_me(self) -> types.User: """ Returns basic information about the bot in form of a User object. + + Telegram documentation: https://core.telegram.org/bots/api#getme """ result = apihelper.get_me(self.token) return types.User.de_json(result) @@ -839,6 +849,8 @@ def get_file(self, file_id: str) -> types.File: It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling get_file again. + Telegram documentation: https://core.telegram.org/bots/api#getfile + :param file_id: File identifier """ return types.File.de_json(apihelper.get_file(self.token, file_id)) @@ -862,6 +874,8 @@ def log_out(self) -> bool: After a successful call, you can immediately log in on a local server, but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#logout """ return apihelper.log_out(self.token) @@ -872,6 +886,8 @@ def close(self) -> bool: after server restart. The method will return error 429 in the first 10 minutes after the bot is launched. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#close """ return apihelper.close(self.token) @@ -879,7 +895,8 @@ def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ Retrieves the user profile photos of the person with 'user_id' - See https://core.telegram.org/bots/api#getuserprofilephotos + + Telegram documentation: https://core.telegram.org/bots/api#getuserprofilephotos :param user_id: Integer - Unique identifier of the target user :param offset: @@ -894,8 +911,10 @@ def get_chat(self, chat_id: Union[int, str]) -> types.Chat: 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. + Telegram documentation: https://core.telegram.org/bots/api#getchat + :param chat_id: - :return: + :return: API reply. """ result = apihelper.get_chat(self.token, chat_id) return types.Chat.de_json(result) @@ -904,8 +923,10 @@ def leave_chat(self, chat_id: Union[int, str]) -> bool: """ Use this method for your bot to leave a group, supergroup or channel. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#leavechat + :param chat_id: - :return: + :return: API reply. """ result = apihelper.leave_chat(self.token, chat_id) return result @@ -916,9 +937,11 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. + Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) - :return: + :return: API reply. """ result = apihelper.get_chat_administrators(self.token, chat_id) return [types.ChatMember.de_json(r) for r in result] @@ -935,8 +958,10 @@ def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ Use this method to get the number of members in a chat. Returns Int on success. + Telegram documentation: https://core.telegram.org/bots/api#getchatmembercount + :param chat_id: - :return: + :return: API reply. """ result = apihelper.get_chat_member_count(self.token, chat_id) return result @@ -948,10 +973,12 @@ def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param sticker_set_name: Name of the sticker set to be set as the group sticker set - :return: + :return: API reply. """ result = apihelper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) return result @@ -962,9 +989,11 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) - :return: + :return: API reply. """ result = apihelper.delete_chat_sticker_set(self.token, chat_id) return result @@ -973,9 +1002,11 @@ def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatM """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. + Telegram documentation: https://core.telegram.org/bots/api#getchatmember + :param chat_id: :param user_id: - :return: + :return: API reply. """ result = apihelper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) @@ -998,6 +1029,8 @@ def send_message( If you must send more than 4000 characters, use the `split_string` or `smart_split` function in util.py. + Telegram documentation: https://core.telegram.org/bots/api#sendmessage + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param text: Text of the message to be sent :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. @@ -1009,7 +1042,7 @@ def send_message( :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found :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 timeout: - :return: + :return: API reply. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1027,6 +1060,8 @@ def forward_message( """ Use this method to forward messages of any kind. + Telegram documentation: https://core.telegram.org/bots/api#forwardmessage + :param disable_notification: :param chat_id: which chat to forward :param from_chat_id: which chat message from @@ -1054,6 +1089,8 @@ def copy_message( """ Use this method to copy messages of any kind. + Telegram documentation: https://core.telegram.org/bots/api#copymessage + :param chat_id: which chat to forward :param from_chat_id: which chat message from :param message_id: message id @@ -1078,6 +1115,8 @@ def delete_message(self, chat_id: Union[int, str], message_id: int, """ Use this method to delete message. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#deletemessage + :param chat_id: in which chat to delete :param message_id: which message to delete :param timeout: @@ -1096,6 +1135,8 @@ def send_dice( """ Use this method to send dices. + Telegram documentation: https://core.telegram.org/bots/api#senddice + :param chat_id: :param emoji: :param disable_notification: @@ -1124,6 +1165,8 @@ def send_photo( timeout: Optional[int]=None,) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. + + Telegram documentation: https://core.telegram.org/bots/api#sendphoto :param chat_id: :param photo: @@ -1163,6 +1206,8 @@ def send_audio( """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. + + Telegram documentation: https://core.telegram.org/bots/api#sendaudio :param chat_id:Unique identifier for the message recipient :param audio:Audio file to send. @@ -1204,11 +1249,13 @@ def send_voice( """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. + + Telegram documentation: https://core.telegram.org/bots/api#sendvoice - :param chat_id:Unique identifier for the message recipient. + :param chat_id: Unique identifier for the message recipient. :param voice: :param caption: - :param duration:Duration of sent audio in seconds + :param duration: Duration of sent audio in seconds :param reply_to_message_id: :param reply_markup: :param parse_mode @@ -1245,6 +1292,8 @@ def send_document( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send general files. + + Telegram documentation: https://core.telegram.org/bots/api#senddocument :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param document: (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 @@ -1291,6 +1340,8 @@ def send_sticker( """ Use this method to send .webp stickers. + Telegram documentation: https://core.telegram.org/bots/api#sendsticker + :param chat_id: :param sticker: :param data: @@ -1334,6 +1385,8 @@ def send_video( """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). + Telegram documentation: https://core.telegram.org/bots/api#sendvideo + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param video: Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video file using multipart/form-data. :param duration: Duration of sent video in seconds @@ -1381,6 +1434,8 @@ def send_animation( """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + Telegram documentation: https://core.telegram.org/bots/api#sendanimation + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server @@ -1422,6 +1477,8 @@ def send_video_note( """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. + + Telegram documentation: https://core.telegram.org/bots/api#sendvideonote :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend @@ -1455,6 +1512,8 @@ def send_media_group( """ Send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup + :param chat_id: :param media: :param disable_notification: @@ -1486,6 +1545,8 @@ def send_location( """ Use this method to send point on the map. + Telegram documentation: https://core.telegram.org/bots/api#sendlocation + :param chat_id: :param latitude: :param longitude: @@ -1521,6 +1582,8 @@ def edit_message_live_location( """ Use this method to edit live location. + Telegram documentation: https://core.telegram.org/bots/api#editmessagelivelocation + :param latitude: :param longitude: :param chat_id: @@ -1548,6 +1611,8 @@ def stop_message_live_location( """ Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires + + Telegram documentation: https://core.telegram.org/bots/api#stopmessagelivelocation :param chat_id: :param message_id: @@ -1578,6 +1643,8 @@ def send_venue( """ Use this method to send information about a venue. + Telegram documentation: https://core.telegram.org/bots/api#sendvenue + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel :param latitude: Float : Latitude of the venue :param longitude: Float : Longitude of the venue @@ -1613,6 +1680,25 @@ def send_contact( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: + """ + Use this method to send phone contacts. + + Telegram documentation: https://core.telegram.org/bots/api#sendcontact + + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel + :param phone_number: String : Contact's phone number + :param first_name: String : Contact's first name + :param last_name: String : Contact's last name + :param vcard: String : Additional data about the contact in the form of a vCard, 0-2048 bytes + :param disable_notification: + :param reply_to_message_id: + :param reply_markup: + :param timeout: + :param allow_sending_without_reply: + :param protect_content: + :return: + """ + return types.Message.de_json( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, @@ -1626,6 +1712,8 @@ def send_chat_action( The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). + Telegram documentation: https://core.telegram.org/bots/api#sendchataction + :param chat_id: :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', @@ -1655,6 +1743,8 @@ def ban_chat_member( own using invite links, etc., unless unbanned first. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#banchatmember + :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup :param user_id: Int : Unique identifier of the target user :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or @@ -1676,6 +1766,8 @@ def unban_chat_member( the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. + Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username) :param user_id: Unique identifier of the target user @@ -1700,6 +1792,8 @@ def restrict_chat_member( The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. + Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :param user_id: Int : Unique identifier of the target user @@ -1746,6 +1840,8 @@ def promote_chat_member( in the chat for this to work and must have the appropriate admin rights. Pass False for all boolean parameters to demote a user. + Telegram documentation: https://core.telegram.org/bots/api#promotechatmember + :param chat_id: Unique identifier for the target chat or username of the target channel ( in the format @channelusername) :param user_id: Int : Unique identifier of the target user @@ -1780,6 +1876,8 @@ def set_chat_administrator_custom_title( Use this method to set a custom title for an administrator in a supergroup promoted by the bot. + Telegram documentation: https://core.telegram.org/bots/api#setchatadministratorcustomtitle + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param user_id: Unique identifier of the target user @@ -1798,6 +1896,8 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i for this to work and must have the appropriate administrator rights. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#banchatsenderchat + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param sender_chat_id: Unique identifier of the target sender chat :return: True on success. @@ -1811,6 +1911,8 @@ def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union administrator rights. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#unbanchatsenderchat + :params: :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param sender_chat_id: Unique identifier of the target sender chat @@ -1825,6 +1927,8 @@ def set_chat_permissions( The bot must be an administrator in the group or a supergroup for this to work and must have the can_restrict_members admin rights. + Telegram documentation: https://core.telegram.org/bots/api#setchatpermissions + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param permissions: New default chat permissions @@ -1842,6 +1946,8 @@ def create_chat_invite_link( Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#createchatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param name: Invite link name; 0-32 characters @@ -1865,6 +1971,8 @@ def edit_chat_invite_link( Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#editchatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param name: Invite link name; 0-32 characters @@ -1885,6 +1993,8 @@ def revoke_chat_invite_link( Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param invite_link: The invite link to revoke @@ -1899,6 +2009,8 @@ def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#exportchatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :return: exported invite link as String on success. @@ -1911,6 +2023,8 @@ def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#approvechatjoinrequest + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param user_id: Unique identifier of the target user @@ -1924,6 +2038,8 @@ def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#declinechatjoinrequest + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param user_id: Unique identifier of the target user @@ -1939,6 +2055,8 @@ def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + Telegram documentation: https://core.telegram.org/bots/api#setchatphoto + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param photo: InputFile: New chat photo, uploaded using multipart/form-data @@ -1954,6 +2072,8 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) """ @@ -1965,6 +2085,8 @@ def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. + Telegram documentation: https://core.telegram.org/bots/api#getmycommands + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :param language_code: A two-letter ISO 639-1 language code. If empty, @@ -1980,6 +2102,8 @@ def set_my_commands(self, commands: List[types.BotCommand], """ Use this method to change the list of the bot's commands. + Telegram documentation: https://core.telegram.org/bots/api#setmycommands + :param commands: List of BotCommand. At most 100 commands can be specified. :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. @@ -1996,6 +2120,8 @@ def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, Use this method to delete the list of the bot's commands for the given scope and user language. After deletion, higher level commands will be shown to affected users. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletemycommands :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. @@ -2013,6 +2139,8 @@ def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + Telegram documentation: https://core.telegram.org/bots/api#setchattitle + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param title: New chat title, 1-255 characters @@ -2025,6 +2153,8 @@ def set_chat_description(self, chat_id: Union[int, str], description: Optional[s Use this method to change the description of a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#setchatdescription + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param description: Str: New chat description, 0-255 characters @@ -2040,6 +2170,8 @@ def pin_chat_message( The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param message_id: Int: Identifier of a message to pin @@ -2055,6 +2187,8 @@ def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int] The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#unpinchatmessage + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param message_id: Int: Identifier of a message to unpin @@ -2068,6 +2202,8 @@ def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#unpinallchatmessages + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :return: @@ -2086,6 +2222,8 @@ def edit_message_text( """ Use this method to edit text and game messages. + Telegram documentation: https://core.telegram.org/bots/api#editmessagetext + :param text: :param chat_id: :param message_id: @@ -2115,6 +2253,8 @@ def edit_message_media( Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. + Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia + :param media: :param chat_id: :param message_id: @@ -2135,6 +2275,8 @@ def edit_message_reply_markup( """ Use this method to edit only the reply markup of messages. + Telegram documentation: https://core.telegram.org/bots/api#editmessagereplymarkup + :param chat_id: :param message_id: :param inline_message_id: @@ -2155,7 +2297,9 @@ def send_game( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Used to send the game + Used to send the game. + + Telegram documentation: https://core.telegram.org/bots/api#sendgame :param chat_id: :param game_short_name: @@ -2183,6 +2327,8 @@ def set_game_score( """ Sets the value of points in the game to a specific user. + Telegram documentation: https://core.telegram.org/bots/api#setgamecore + :param user_id: :param score: :param force: @@ -2203,7 +2349,9 @@ def get_game_high_scores( message_id: Optional[int]=None, inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ - Gets top points and game play + Gets top points and game play. + + Telegram documentation: https://core.telegram.org/bots/api#getgamehighscores :param user_id: :param chat_id: @@ -2238,6 +2386,8 @@ def send_invoice( """ Sends invoice. + Telegram documentation: https://core.telegram.org/bots/api#sendinvoice + :param chat_id: Unique identifier for the target private chat :param title: Product name :param description: Product description @@ -2308,6 +2458,8 @@ def send_poll( """ Sends a poll. + Telegram documentation: https://core.telegram.org/bots/api#sendpoll + :param chat_id: :param question: :param options: array of str with answers @@ -2347,6 +2499,8 @@ def stop_poll( """ Stops a poll. + Telegram documentation: https://core.telegram.org/bots/api#stoppoll + :param chat_id: :param message_id: :param reply_markup: @@ -2361,6 +2515,8 @@ def answer_shipping_query( """ Asks for an answer to a shipping question. + Telegram documentation: https://core.telegram.org/bots/api#answershippingquery + :param shipping_query_id: :param ok: :param shipping_options: @@ -2375,6 +2531,8 @@ def answer_pre_checkout_query( """ Response to a request for pre-inspection. + Telegram documentation: https://core.telegram.org/bots/api#answerprecheckoutquery + :param pre_checkout_query_id: :param ok: :param error_message: @@ -2392,6 +2550,8 @@ def edit_message_caption( """ Use this method to edit captions of messages. + Telegram documentation: https://core.telegram.org/bots/api#editmessagecaption + :param caption: :param chat_id: :param message_id: @@ -2432,6 +2592,8 @@ def answer_inline_query( Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. + Telegram documentation: https://core.telegram.org/bots/api#answerinlinequery + :param inline_query_id: Unique identifier for the answered query :param results: Array of results for the inline query :param cache_time: The maximum amount of time in seconds that the result of the inline query @@ -2456,6 +2618,8 @@ def answer_callback_query( Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. + Telegram documentation: https://core.telegram.org/bots/api#answercallbackquery + :param callback_query_id: :param text: :param show_alert: @@ -2471,6 +2635,8 @@ def set_sticker_set_thumb( Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb + :param name: Sticker set name :param user_id: User identifier :param thumb: @@ -2481,6 +2647,8 @@ def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. + Telegram documentation: https://core.telegram.org/bots/api#getstickerset + :param name: :return: """ @@ -2492,6 +2660,8 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> typ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. + Telegram documentation: https://core.telegram.org/bots/api#uploadstickerfile + :param user_id: :param png_sticker: :return: @@ -2512,6 +2682,8 @@ def create_new_sticker_set( The bot will be able to edit the created sticker set. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#createnewstickerset + :param user_id: :param name: :param title: @@ -2538,6 +2710,8 @@ def add_sticker_to_set( It's required to pass `png_sticker` or `tgs_sticker`. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#addstickertoset + :param user_id: :param name: :param emojis: @@ -2554,6 +2728,8 @@ def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setstickerpositioninset + :param sticker: :param position: :return: @@ -2564,6 +2740,8 @@ def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#deletestickerfromset + :param sticker: :return: """ @@ -3206,6 +3384,7 @@ def add_edited_channel_post_handler(self, handler_dict): def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers edited channel post message handler. + :param callback: function to be called :param content_types: list of content_types :param commands: list of commands From c45e06c694aa748b00f8a59f5be8167444008445 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Mar 2022 23:23:33 +0500 Subject: [PATCH 0904/1808] Updated description for TeleBot class --- telebot/__init__.py | 89 +++++++-------------------------------------- 1 file changed, 14 insertions(+), 75 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0d9c22778..6cdea4160 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -72,81 +72,20 @@ def handle(self, exception): class TeleBot: - """ This is TeleBot Class - Methods: - getMe - logOut - close - sendMessage - forwardMessage - copyMessage - deleteMessage - sendPhoto - sendAudio - sendDocument - sendSticker - sendVideo - sendVenue - sendAnimation - sendVideoNote - sendLocation - sendChatAction - sendDice - sendContact - sendInvoice - sendMediaGroup - getUserProfilePhotos - getUpdates - getFile - sendPoll - stopPoll - sendGame - setGameScore - getGameHighScores - editMessageText - editMessageCaption - editMessageMedia - editMessageReplyMarkup - editMessageLiveLocation - stopMessageLiveLocation - banChatMember - unbanChatMember - restrictChatMember - promoteChatMember - setChatAdministratorCustomTitle - setChatPermissions - createChatInviteLink - editChatInviteLink - revokeChatInviteLink - exportChatInviteLink - setChatStickerSet - deleteChatStickerSet - createNewStickerSet - addStickerToSet - deleteStickerFromSet - setStickerPositionInSet - uploadStickerFile - setStickerSetThumb - getStickerSet - setChatPhoto - deleteChatPhoto - setChatTitle - setChatDescription - pinChatMessage - unpinChatMessage - leaveChat - getChat - getChatAdministrators - getChatMemberCount - getChatMember - answerCallbackQuery - getMyCommands - setMyCommands - deleteMyCommands - answerInlineQuery - answerShippingQuery - answerPreCheckoutQuery - """ + """ + This is the main class to deal with bot. + It allows you to add handlers for different kind of updates. + + Usage: + .. code-block:: python + + from telebot import TeleBot + bot = TeleBot('token') # get token from @BotFather + + See more examples in examples/ directory: + https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples + + """ def __init__( self, token, parse_mode=None, threaded=True, skip_pending=False, num_threads=2, From f69a2ba044983fbd7b072946df7512d0e2904cd6 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 7 Mar 2022 00:18:11 +0500 Subject: [PATCH 0905/1808] Update docstrings for asynctelebot. --- telebot/__init__.py | 4 +- telebot/async_telebot.py | 535 ++++++++++++++++++++-------- telebot/asyncio_handler_backends.py | 27 +- 3 files changed, 412 insertions(+), 154 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6cdea4160..b61b076c0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -73,10 +73,12 @@ def handle(self, exception): class TeleBot: """ - This is the main class to deal with bot. + This is the main synchronous class for Bot. + It allows you to add handlers for different kind of updates. Usage: + .. code-block:: python from telebot import TeleBot diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 464f65be7..18a000d00 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5,7 +5,6 @@ import re import time import traceback -import inspect from typing import Any, List, Optional, Union # this imports are used to avoid circular import error @@ -15,6 +14,7 @@ # storages from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage +from telebot.asyncio_handler_backends import CancelUpdate, SkipHandler from inspect import signature @@ -58,106 +58,23 @@ def handle(self, exception): return False -class SkipHandler: - """ - Class for skipping handlers. - Just return instance of this class - in middleware to skip handler. - Update will go to post_process, - but will skip execution of handler. +class AsyncTeleBot: """ + This is the main asynchronous class for Bot. - def __init__(self) -> None: - pass + It allows you to add handlers for different kind of updates. -class CancelUpdate: - """ - Class for canceling updates. - Just return instance of this class - in middleware to skip update. - Update will skip handler and execution - of post_process in middlewares. - """ + Usage: - def __init__(self) -> None: - pass + .. code-block:: python -class AsyncTeleBot: - """ This is AsyncTeleBot Class - Methods: - getMe - logOut - close - sendMessage - forwardMessage - copyMessage - deleteMessage - sendPhoto - sendAudio - sendDocument - sendSticker - sendVideo - sendVenue - sendAnimation - sendVideoNote - sendLocation - sendChatAction - sendDice - sendContact - sendInvoice - sendMediaGroup - getUserProfilePhotos - getUpdates - getFile - sendPoll - stopPoll - sendGame - setGameScore - getGameHighScores - editMessageText - editMessageCaption - editMessageMedia - editMessageReplyMarkup - editMessageLiveLocation - stopMessageLiveLocation - banChatMember - unbanChatMember - restrictChatMember - promoteChatMember - setChatAdministratorCustomTitle - setChatPermissions - createChatInviteLink - editChatInviteLink - revokeChatInviteLink - exportChatInviteLink - setChatStickerSet - deleteChatStickerSet - createNewStickerSet - addStickerToSet - deleteStickerFromSet - setStickerPositionInSet - uploadStickerFile - setStickerSetThumb - getStickerSet - setChatPhoto - deleteChatPhoto - setChatTitle - setChatDescription - pinChatMessage - unpinChatMessage - leaveChat - getChat - getChatAdministrators - getChatMemberCount - getChatMember - answerCallbackQuery - getMyCommands - setMyCommands - deleteMyCommands - answerInlineQuery - answerShippingQuery - answerPreCheckoutQuery - """ + from telebot.async_telebot import AsyncTeleBot + bot = AsyncTeleBot('token') # get token from @BotFather + + See more examples in examples/ directory: + https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples + + """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, exception_handler=None, state_storage=StateMemoryStorage()) -> None: # TODO: ADD TYPEHINTS @@ -195,6 +112,10 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, self.middlewares = [] async def close_session(self): + """ + Closes existing session of aiohttp. + Use this function if you stop polling. + """ await asyncio_helper.session_manager.session.close() async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> List[types.Update]: @@ -210,6 +131,7 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= Warning: Do not call this function more than once! Always get updates. + :param interval: Delay between two update retrivals :param non_stop: Do not stop polling when an ApiException occurs. :param timeout: Request connection timeout @@ -275,6 +197,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: request_timeout: int=20, allowed_updates: Optional[List[str]]=None): """ Function to process polling. + :param non_stop: Do not stop polling when an ApiException occurs. :param interval: Delay between two update retrivals :param timeout: Request connection timeout @@ -338,6 +261,7 @@ def _loop_create_task(self, coro): async def _process_updates(self, handlers, messages, update_type): """ Process updates. + :param handlers: :param messages: :return: @@ -397,6 +321,13 @@ async def _run_middlewares_and_handlers(self, handlers, message, middleware): await middleware.post_process(message, data, handler_error) # update handling async def process_new_updates(self, updates): + """ + Process new updates. + Just pass list of updates - each update should be + instance of Update object. + + :param updates: list of updates + """ upd_count = len(updates) logger.info('Received {0} new updates'.format(upd_count)) if upd_count == 0: return @@ -546,7 +477,8 @@ async def __notify_update(self, new_messages): async def _test_message_handler(self, message_handler, message): """ - Test message handler + Test message handler. + :param message_handler: :param message: :return: @@ -561,18 +493,25 @@ async def _test_message_handler(self, message_handler, message): return True def set_update_listener(self, func): + """ + Update listener is a function that gets any update. + + :param func: function that should get update. + """ self.update_listener.append(func) def add_custom_filter(self, custom_filter): """ Create custom filter. + custom_filter: Class with check(message) method. """ self.custom_filters[custom_filter.key] = custom_filter async def _test_filter(self, message_filter, filter_value, message): """ - Test filters + Test filters. + :param message_filter: Filter type passed in handler :param filter_value: Filter value passed in handler :param message: Message to test @@ -594,8 +533,6 @@ async def _test_filter(self, message_filter, filter_value, message): elif message_filter == 'chat_types': return message.chat.type in filter_value elif message_filter == 'func': - if inspect.iscoroutinefunction(filter_value): - return await filter_value(message) return filter_value(message) elif self.custom_filters and message_filter in self.custom_filters: return await self._check_filter(message_filter,filter_value,message) @@ -604,7 +541,8 @@ async def _test_filter(self, message_filter, filter_value, message): async def _check_filter(self, message_filter, filter_value, message): """ - Check up the filter + Check up the filter. + :param message_filter: :param filter_value: :param message: @@ -623,8 +561,9 @@ async def _check_filter(self, message_filter, filter_value, message): def setup_middleware(self, middleware): """ - Setup middleware - :param middleware: + Setup middleware. + + :param middleware: Middleware-class. :return: """ self.middlewares.append(middleware) @@ -695,7 +634,9 @@ def decorator(handler): def add_message_handler(self, handler_dict): """ - Adds a message handler + Adds a message handler. + Note that you should use register_message_handler to add message_handler. + :param handler_dict: :return: """ @@ -704,6 +645,7 @@ def add_message_handler(self, handler_dict): def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers message handler. + :param callback: function to be called :param content_types: list of content_types :param commands: list of commands @@ -735,7 +677,8 @@ def register_message_handler(self, callback, content_types=None, commands=None, def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ - Edit message handler decorator + Edit message handler decorator. + :param commands: :param regexp: :param func: @@ -771,7 +714,9 @@ def decorator(handler): def add_edited_message_handler(self, handler_dict): """ - Adds the edit message handler + Adds the edit message handler. + Note that you should use register_edited_message_handler to add edited_message_handler. + :param handler_dict: :return: """ @@ -780,6 +725,7 @@ def add_edited_message_handler(self, handler_dict): def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): """ Registers edited message handler. + :param pass_bot: :param callback: function to be called :param content_types: list of content_types @@ -810,7 +756,8 @@ def register_edited_message_handler(self, callback, content_types=None, commands def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ - Channel post handler decorator + Channel post handler decorator. + :param commands: :param regexp: :param func: @@ -843,7 +790,9 @@ def decorator(handler): def add_channel_post_handler(self, handler_dict): """ - Adds channel post handler + Adds channel post handler. + Note that you should use register_channel_post_handler to add channel_post_handler. + :param handler_dict: :return: """ @@ -852,6 +801,7 @@ def add_channel_post_handler(self, handler_dict): def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers channel post message handler. + :param pass_bot: :param callback: function to be called :param content_types: list of content_types @@ -879,7 +829,8 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ - Edit channel post handler decorator + Edit channel post handler decorator. + :param commands: :param regexp: :param func: @@ -912,7 +863,9 @@ def decorator(handler): def add_edited_channel_post_handler(self, handler_dict): """ - Adds the edit channel post handler + Adds the edit channel post handler. + Note that you should use register_edited_channel_post_handler to add edited_channel_post_handler. + :param handler_dict: :return: """ @@ -921,6 +874,7 @@ def add_edited_channel_post_handler(self, handler_dict): def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): """ Registers edited channel post message handler. + :param pass_bot: :param callback: function to be called :param content_types: list of content_types @@ -948,7 +902,8 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com def inline_handler(self, func, **kwargs): """ - Inline call handler decorator + Inline call handler decorator. + :param func: :param kwargs: :return: @@ -963,7 +918,9 @@ def decorator(handler): def add_inline_handler(self, handler_dict): """ - Adds inline call handler + Adds inline call handler. + Note that you should use register_inline_handler to add inline_handler. + :param handler_dict: :return: """ @@ -972,6 +929,7 @@ def add_inline_handler(self, handler_dict): def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers inline handler. + :param pass_bot: :param callback: function to be called :param func: @@ -982,6 +940,7 @@ def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): def chosen_inline_handler(self, func, **kwargs): """ + Description: TBD :param func: :param kwargs: @@ -998,6 +957,8 @@ def decorator(handler): def add_chosen_inline_handler(self, handler_dict): """ Description: TBD + Note that you should use register_chosen_inline_handler to add chosen_inline_handler. + :param handler_dict: :return: """ @@ -1006,6 +967,7 @@ def add_chosen_inline_handler(self, handler_dict): def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers chosen inline handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1016,7 +978,8 @@ def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwarg def callback_query_handler(self, func, **kwargs): """ - Callback request handler decorator + Callback request handler decorator. + :param func: :param kwargs: :return: @@ -1031,7 +994,9 @@ def decorator(handler): def add_callback_query_handler(self, handler_dict): """ - Adds a callback request handler + Adds a callback request handler. + Note that you should use register_callback_query_handler to add callback_query_handler. + :param handler_dict: :return: """ @@ -1039,7 +1004,8 @@ def add_callback_query_handler(self, handler_dict): def register_callback_query_handler(self, callback, func, pass_bot=False, **kwargs): """ - Registers callback query handler.. + Registers callback query handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1050,7 +1016,8 @@ def register_callback_query_handler(self, callback, func, pass_bot=False, **kwar def shipping_query_handler(self, func, **kwargs): """ - Shipping request handler + Shipping request handler. + :param func: :param kwargs: :return: @@ -1065,7 +1032,9 @@ def decorator(handler): def add_shipping_query_handler(self, handler_dict): """ - Adds a shipping request handler + Adds a shipping request handler. + Note that you should use register_shipping_query_handler to add shipping_query_handler. + :param handler_dict: :return: """ @@ -1074,6 +1043,7 @@ def add_shipping_query_handler(self, handler_dict): def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers shipping query handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1084,7 +1054,8 @@ def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwar def pre_checkout_query_handler(self, func, **kwargs): """ - Pre-checkout request handler + Pre-checkout request handler. + :param func: :param kwargs: :return: @@ -1099,7 +1070,9 @@ def decorator(handler): def add_pre_checkout_query_handler(self, handler_dict): """ - Adds a pre-checkout request handler + Adds a pre-checkout request handler. + Note that you should use register_pre_checkout_query_handler to add pre_checkout_query_handler. + :param handler_dict: :return: """ @@ -1108,6 +1081,7 @@ def add_pre_checkout_query_handler(self, handler_dict): def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers pre-checkout request handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1118,7 +1092,8 @@ def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, ** def poll_handler(self, func, **kwargs): """ - Poll request handler + Poll request handler. + :param func: :param kwargs: :return: @@ -1133,7 +1108,9 @@ def decorator(handler): def add_poll_handler(self, handler_dict): """ - Adds a poll request handler + Adds a poll request handler. + Note that you should use register_poll_handler to add poll_handler. + :param handler_dict: :return: """ @@ -1142,6 +1119,7 @@ def add_poll_handler(self, handler_dict): def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1152,7 +1130,8 @@ def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): def poll_answer_handler(self, func=None, **kwargs): """ - Poll_answer request handler + Poll_answer request handler. + :param func: :param kwargs: :return: @@ -1167,7 +1146,9 @@ def decorator(handler): def add_poll_answer_handler(self, handler_dict): """ - Adds a poll_answer request handler + Adds a poll_answer request handler. + Note that you should use register_poll_answer_handler to add poll_answer_handler. + :param handler_dict: :return: """ @@ -1176,6 +1157,7 @@ def add_poll_answer_handler(self, handler_dict): def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers poll answer handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1186,7 +1168,8 @@ def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs) def my_chat_member_handler(self, func=None, **kwargs): """ - my_chat_member handler + my_chat_member handler. + :param func: :param kwargs: :return: @@ -1201,7 +1184,9 @@ def decorator(handler): def add_my_chat_member_handler(self, handler_dict): """ - Adds a my_chat_member handler + Adds a my_chat_member handler. + Note that you should use register_my_chat_member_handler to add my_chat_member_handler. + :param handler_dict: :return: """ @@ -1210,6 +1195,7 @@ def add_my_chat_member_handler(self, handler_dict): def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers my chat member handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1220,7 +1206,8 @@ def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, * def chat_member_handler(self, func=None, **kwargs): """ - chat_member handler + chat_member handler. + :param func: :param kwargs: :return: @@ -1235,7 +1222,9 @@ def decorator(handler): def add_chat_member_handler(self, handler_dict): """ - Adds a chat_member handler + Adds a chat_member handler. + Note that you should use register_chat_member_handler to add chat_member_handler. + :param handler_dict: :return: """ @@ -1244,6 +1233,7 @@ def add_chat_member_handler(self, handler_dict): def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat member handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1254,7 +1244,8 @@ def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kw def chat_join_request_handler(self, func=None, **kwargs): """ - chat_join_request handler + chat_join_request handler. + :param func: :param kwargs: :return: @@ -1269,7 +1260,9 @@ def decorator(handler): def add_chat_join_request_handler(self, handler_dict): """ - Adds a chat_join_request handler + Adds a chat_join_request handler. + Note that you should use register_chat_join_request_handler to add chat_join_request_handler. + :param handler_dict: :return: """ @@ -1278,6 +1271,7 @@ def add_chat_join_request_handler(self, handler_dict): def register_chat_join_request_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat join request handler. + :param pass_bot: :param callback: function to be called :param func: @@ -1289,7 +1283,8 @@ def register_chat_join_request_handler(self, callback, func=None, pass_bot=False @staticmethod def _build_handler_dict(handler, pass_bot=False, **filters): """ - Builds a dictionary for a handler + Builds a dictionary for a handler. + :param handler: :param filters: :return: @@ -1303,6 +1298,10 @@ def _build_handler_dict(handler, pass_bot=False, **filters): } async def skip_updates(self): + """ + Skip existing updates. + Only last update will remain on server. + """ await self.get_updates(-1) return True @@ -1311,6 +1310,8 @@ async def skip_updates(self): async def get_me(self) -> types.User: """ Returns basic information about the bot in form of a User object. + + Telegram documentation: https://core.telegram.org/bots/api#getme """ result = await asyncio_helper.get_me(self.token) return types.User.de_json(result) @@ -1322,10 +1323,15 @@ async def get_file(self, file_id: str) -> types.File: On success, a File object is returned. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling get_file again. + + Telegram documentation: https://core.telegram.org/bots/api#getfile + + :param file_id: """ return types.File.de_json(await asyncio_helper.get_file(self.token, file_id)) async def get_file_url(self, file_id: str) -> str: + return await asyncio_helper.get_file_url(self.token, file_id) async def download_file(self, file_path: str) -> bytes: @@ -1339,6 +1345,8 @@ async def log_out(self) -> bool: After a successful call, you can immediately log in on a local server, but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#logout """ return await asyncio_helper.log_out(self.token) @@ -1349,6 +1357,8 @@ async def close(self) -> bool: after server restart. The method will return error 429 in the first 10 minutes after the bot is launched. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#close """ return await asyncio_helper.close(self.token) @@ -1370,6 +1380,8 @@ async def set_webhook(self, url=None, certificate=None, max_connections=None, al In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setwebhook + :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration :param certificate: Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details. @@ -1396,6 +1408,8 @@ async def delete_webhook(self, drop_pending_updates=None, timeout=None): """ Use this method to remove webhook integration if you decide to switch back to getUpdates. + Telegram documentation: https://core.telegram.org/bots/api#deletewebhook + :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout :return: bool @@ -1413,6 +1427,8 @@ async def get_webhook_info(self, timeout=None): Use this method to get current webhook status. Requires no parameters. If the bot is using getUpdates, will return an object with the url field empty. + Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo + :param timeout: Integer. Request connection timeout :return: On success, returns a WebhookInfo object. """ @@ -1423,7 +1439,9 @@ async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None limit: Optional[int]=None) -> types.UserProfilePhotos: """ Retrieves the user profile photos of the person with 'user_id' - See https://core.telegram.org/bots/api#getuserprofilephotos + + Telegram documentation: https://core.telegram.org/bots/api#getuserprofilephotos + :param user_id: :param offset: :param limit: @@ -1436,6 +1454,9 @@ async def get_chat(self, chat_id: Union[int, str]) -> types.Chat: """ 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. + + Telegram documentation: https://core.telegram.org/bots/api#getchat + :param chat_id: :return: """ @@ -1445,6 +1466,9 @@ async def get_chat(self, chat_id: Union[int, str]) -> types.Chat: async def leave_chat(self, chat_id: Union[int, str]) -> bool: """ Use this method for your bot to leave a group, supergroup or channel. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#leavechat + :param chat_id: :return: """ @@ -1456,6 +1480,9 @@ async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types. Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. + + Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) :return: @@ -1474,6 +1501,9 @@ async def get_chat_members_count(self, chat_id: Union[int, str]) -> int: async def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ Use this method to get the number of members in a chat. Returns Int on success. + + Telegram documentation: https://core.telegram.org/bots/api#getchatmemberscount + :param chat_id: :return: """ @@ -1486,6 +1516,9 @@ async def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param sticker_set_name: Name of the sticker set to be set as the group sticker set @@ -1499,9 +1532,12 @@ async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) - :return: + :return: API reply. """ result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) return result @@ -1509,9 +1545,12 @@ async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. + + Telegram documentation: https://core.telegram.org/bots/api#getchatmember + :param chat_id: :param user_id: - :return: + :return: API reply. """ result = await asyncio_helper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) @@ -1536,6 +1575,8 @@ async def send_message( If you must send more than 4000 characters, use the `split_string` or `smart_split` function in util.py. + Telegram documentation: https://core.telegram.org/bots/api#sendmessage + :param chat_id: :param text: :param disable_web_page_preview: @@ -1564,6 +1605,9 @@ async def forward_message( timeout: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. + + Telegram documentation: https://core.telegram.org/bots/api#forwardmessage + :param disable_notification: :param chat_id: which chat to forward :param from_chat_id: which chat message from @@ -1590,6 +1634,9 @@ async def copy_message( timeout: Optional[int]=None) -> int: """ Use this method to copy messages of any kind. + + Telegram documentation: https://core.telegram.org/bots/api#copymessage + :param chat_id: which chat to forward :param from_chat_id: which chat message from :param message_id: message id @@ -1613,6 +1660,9 @@ async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: """ Use this method to delete message. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletemessage + :param chat_id: in which chat to delete :param message_id: which message to delete :param timeout: @@ -1630,6 +1680,9 @@ async def send_dice( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send dices. + + Telegram documentation: https://core.telegram.org/bots/api#senddice + :param chat_id: :param emoji: :param disable_notification: @@ -1658,6 +1711,9 @@ async def send_photo( timeout: Optional[int]=None,) -> types.Message: """ Use this method to send photos. + + Telegram documentation: https://core.telegram.org/bots/api#sendphoto + :param chat_id: :param photo: :param caption: @@ -1695,13 +1751,16 @@ async def send_audio( """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. - :param chat_id:Unique identifier for the message recipient - :param audio:Audio file to send. + + Telegram documentation: https://core.telegram.org/bots/api#sendaudio + + :param chat_id: Unique identifier for the message recipient + :param audio: Audio file to send. :param caption: - :param duration:Duration of the audio in seconds - :param performer:Performer + :param duration: Duration of the audio in seconds + :param performer: Performer :param title:Track name - :param reply_to_message_id:If the message is a reply, ID of the original message + :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: :param parse_mode :param disable_notification: @@ -1734,10 +1793,13 @@ async def send_voice( """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. - :param chat_id:Unique identifier for the message recipient. + + Telegram documentation: https://core.telegram.org/bots/api#sendvoice + + :param chat_id: Unique identifier for the message recipient. :param voice: :param caption: - :param duration:Duration of sent audio in seconds + :param duration: Duration of sent audio in seconds :param reply_to_message_id: :param reply_markup: :param parse_mode @@ -1773,6 +1835,9 @@ async def send_document( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send general files. + + Telegram documentation: https://core.telegram.org/bots/api#senddocument + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param document: (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 @@ -1814,6 +1879,9 @@ async def send_sticker( data: Union[Any, str]=None) -> types.Message: """ Use this method to send .webp stickers. + + Telegram documentation: https://core.telegram.org/bots/api#sendsticker + :param chat_id: :param sticker: :param reply_to_message_id: @@ -1854,6 +1922,9 @@ async def send_video( data: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). + + Telegram documentation: https://core.telegram.org/bots/api#sendvideo + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param video: Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video file using multipart/form-data. :param duration: Duration of sent video in seconds @@ -1900,6 +1971,9 @@ async def send_animation( timeout: Optional[int]=None, ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + + Telegram documentation: https://core.telegram.org/bots/api#sendanimation + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server @@ -1940,6 +2014,9 @@ async def send_video_note( """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. + + Telegram documentation: https://core.telegram.org/bots/api#sendvideonote + :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server @@ -1971,6 +2048,9 @@ async def send_media_group( allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: """ send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + + Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup + :param chat_id: :param media: :param disable_notification: @@ -2002,6 +2082,9 @@ async def send_location( """ Use this method to send point on the map. + + Telegram documentation: https://core.telegram.org/bots/api#sendlocation + :param chat_id: :param latitude: :param longitude: @@ -2035,7 +2118,10 @@ async def edit_message_live_location( heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None) -> types.Message: """ - Use this method to edit live location + Use this method to edit live location. + + Telegram documentation: https://core.telegram.org/bots/api#editmessagelivelocation + :param latitude: :param longitude: :param chat_id: @@ -2062,7 +2148,10 @@ async def stop_message_live_location( timeout: Optional[int]=None) -> types.Message: """ Use this method to stop updating a live location message sent by the bot - or via the bot (for inline bots) before live_period expires + or via the bot (for inline bots) before live_period expires. + + Telegram documentation: https://core.telegram.org/bots/api#stopmessagelivelocation + :param chat_id: :param message_id: :param inline_message_id: @@ -2090,6 +2179,9 @@ async def send_venue( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. + + Telegram documentation: https://core.telegram.org/bots/api#sendvenue + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel :param latitude: Float : Latitude of the venue :param longitude: Float : Longitude of the venue @@ -2127,6 +2219,9 @@ async def send_contact( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send phone contacts. + + Telegram documentation: https://core.telegram.org/bots/api#sendcontact + :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel :param phone_number: String : Contact's phone number :param first_name: String : Contact's first name @@ -2152,6 +2247,9 @@ async def send_chat_action( Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). + + Telegram documentation: https://core.telegram.org/bots/api#sendchataction + :param chat_id: :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', @@ -2180,6 +2278,9 @@ async def ban_chat_member( In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless unbanned first. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#banchatmember + :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup :param user_id: Int : Unique identifier of the target user :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or @@ -2201,6 +2302,8 @@ async def unban_chat_member( the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. + Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username) :param user_id: Unique identifier of the target user @@ -2225,6 +2328,8 @@ async def restrict_chat_member( The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. + Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :param user_id: Int : Unique identifier of the target user @@ -2271,6 +2376,8 @@ async def promote_chat_member( in the chat for this to work and must have the appropriate admin rights. Pass False for all boolean parameters to demote a user. + Telegram documentation: https://core.telegram.org/bots/api#promotechatmember + :param chat_id: Unique identifier for the target chat or username of the target channel ( in the format @channelusername) :param user_id: Int : Unique identifier of the target user @@ -2305,6 +2412,8 @@ async def set_chat_administrator_custom_title( Use this method to set a custom title for an administrator in a supergroup promoted by the bot. + Telegram documentation: https://core.telegram.org/bots/api#setchatadministratorcustomtitle + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param user_id: Unique identifier of the target user @@ -2324,7 +2433,8 @@ async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: U for this to work and must have the appropriate administrator rights. Returns True on success. - :params: + Telegram documentation: https://core.telegram.org/bots/api#banchatsenderchat + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param sender_chat_id: Unique identifier of the target sender chat :return: True on success. @@ -2338,6 +2448,8 @@ async def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: administrator rights. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#unbanchatsenderchat + :params: :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param sender_chat_id: Unique identifier of the target sender chat @@ -2352,6 +2464,8 @@ async def set_chat_permissions( The bot must be an administrator in the group or a supergroup for this to work and must have the can_restrict_members admin rights. + Telegram documentation: https://core.telegram.org/bots/api#setchatpermissions + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param permissions: New async default chat permissions @@ -2369,6 +2483,8 @@ async def create_chat_invite_link( Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#createchatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param name: Invite link name; 0-32 characters @@ -2392,6 +2508,8 @@ async def edit_chat_invite_link( Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#editchatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param name: Invite link name; 0-32 characters @@ -2412,6 +2530,8 @@ async def revoke_chat_invite_link( Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param invite_link: The invite link to revoke @@ -2426,6 +2546,8 @@ async def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#exportchatinvitelink + :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :return: exported invite link as String on success. @@ -2439,6 +2561,8 @@ async def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Uni The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#approvechatjoinrequest + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param user_id: Unique identifier of the target user @@ -2452,6 +2576,8 @@ async def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Uni The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#declinechatjoinrequest + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param user_id: Unique identifier of the target user @@ -2466,6 +2592,9 @@ async def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + + Telegram documentation: https://core.telegram.org/bots/api#setchatphoto + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param photo: InputFile: New chat photo, uploaded using multipart/form-data @@ -2480,6 +2609,9 @@ async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + + Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) """ @@ -2490,6 +2622,9 @@ async def get_my_commands(self, scope: Optional[types.BotCommandScope], """ Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. + + Telegram documentation: https://core.telegram.org/bots/api#getmycommands + :param scope: The scope of users for which the commands are relevant. async defaults to BotCommandScopeasync default. :param language_code: A two-letter ISO 639-1 language code. If empty, @@ -2504,6 +2639,9 @@ async def set_my_commands(self, commands: List[types.BotCommand], language_code: Optional[str]=None) -> bool: """ Use this method to change the list of the bot's commands. + + Telegram documentation: https://core.telegram.org/bots/api#setmycommands + :param commands: List of BotCommand. At most 100 commands can be specified. :param scope: The scope of users for which the commands are relevant. async defaults to BotCommandScopeasync default. @@ -2520,6 +2658,9 @@ async def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, Use this method to delete the list of the bot's commands for the given scope and user language. After deletion, higher level commands will be shown to affected users. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletemycommands + :param scope: The scope of users for which the commands are relevant. async defaults to BotCommandScopeasync default. :param language_code: A two-letter ISO 639-1 language code. If empty, @@ -2535,6 +2676,9 @@ async def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + + Telegram documentation: https://core.telegram.org/bots/api#setchattitle + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param title: New chat title, 1-255 characters @@ -2547,6 +2691,8 @@ async def set_chat_description(self, chat_id: Union[int, str], description: Opti Use this method to change the description of a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Telegram documentation: https://core.telegram.org/bots/api#setchatdescription + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param description: Str: New chat description, 0-255 characters @@ -2561,6 +2707,9 @@ async def pin_chat_message( Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param message_id: Int: Identifier of a message to pin @@ -2575,6 +2724,9 @@ async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optiona Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unpinchatmessage + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param message_id: Int: Identifier of a message to unpin @@ -2587,6 +2739,9 @@ async def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: Use this method to unpin a all pinned messages in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unpinallchatmessages + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :return: @@ -2604,6 +2759,9 @@ async def edit_message_text( reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. + + Telegram documentation: https://core.telegram.org/bots/api#editmessagetext + :param text: :param chat_id: :param message_id: @@ -2632,6 +2790,9 @@ async def edit_message_media( If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. + + Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia + :param media: :param chat_id: :param message_id: @@ -2651,6 +2812,9 @@ async def edit_message_reply_markup( reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. + + Telegram documentation: https://core.telegram.org/bots/api#editmessagereplymarkup + :param chat_id: :param message_id: :param inline_message_id: @@ -2671,7 +2835,10 @@ async def send_game( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Used to send the game + Used to send the game. + + Telegram documentation: https://core.telegram.org/bots/api#sendgame + :param chat_id: :param game_short_name: :param disable_notification: @@ -2696,7 +2863,10 @@ async def set_game_score( inline_message_id: Optional[str]=None, disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: """ - Sets the value of points in the game to a specific user + Sets the value of points in the game to a specific user. + + Telegram documentation: https://core.telegram.org/bots/api#setgamescore + :param user_id: :param score: :param force: @@ -2717,7 +2887,10 @@ async def get_game_high_scores( message_id: Optional[int]=None, inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ - Gets top points and game play + Gets top points and game play. + + Telegram documentation: https://core.telegram.org/bots/api#getgamehighscores + :param user_id: :param chat_id: :param message_id: @@ -2748,7 +2921,10 @@ async def send_invoice( suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Sends invoice + Sends invoice. + + Telegram documentation: https://core.telegram.org/bots/api#sendinvoice + :param chat_id: Unique identifier for the target private chat :param title: Product name :param description: Product description @@ -2816,7 +2992,10 @@ async def send_poll( explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Send polls + Send polls. + + Telegram documentation: https://core.telegram.org/bots/api#sendpoll + :param chat_id: :param question: :param options: array of str with answers @@ -2855,7 +3034,10 @@ async def stop_poll( self, chat_id: Union[int, str], message_id: int, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: """ - Stops poll + Stops poll. + + Telegram documentation: https://core.telegram.org/bots/api#stoppoll + :param chat_id: :param message_id: :param reply_markup: @@ -2868,7 +3050,10 @@ async def answer_shipping_query( shipping_options: Optional[List[types.ShippingOption]]=None, error_message: Optional[str]=None) -> bool: """ - Asks for an answer to a shipping question + Asks for an answer to a shipping question. + + Telegram documentation: https://core.telegram.org/bots/api#answershippingquery + :param shipping_query_id: :param ok: :param shipping_options: @@ -2881,7 +3066,10 @@ async def answer_pre_checkout_query( self, pre_checkout_query_id: int, ok: bool, error_message: Optional[str]=None) -> bool: """ - Response to a request for pre-inspection + Response to a request for pre-inspection. + + Telegram documentation: https://core.telegram.org/bots/api#answerprecheckoutquery + :param pre_checkout_query_id: :param ok: :param error_message: @@ -2897,7 +3085,10 @@ async def edit_message_caption( caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: """ - Use this method to edit captions of messages + Use this method to edit captions of messages. + + Telegram documentation: https://core.telegram.org/bots/api#editmessagecaption + :param caption: :param chat_id: :param message_id: @@ -2918,6 +3109,7 @@ async def edit_message_caption( async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` + :param message: :param text: :param kwargs: @@ -2936,6 +3128,9 @@ async def answer_inline_query( """ Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. + + Telegram documentation: https://core.telegram.org/bots/api#answerinlinequery + :param inline_query_id: Unique identifier for the answered query :param results: Array of results for the inline query :param cache_time: The maximum amount of time in seconds that the result of the inline query @@ -2959,6 +3154,9 @@ async def answer_callback_query( """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. + + Telegram documentation: https://core.telegram.org/bots/api#answercallbackquery + :param callback_query_id: :param text: :param show_alert: @@ -2973,12 +3171,24 @@ async def set_sticker_set_thumb( """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb + + :param name: Sticker set name + :param user_id: User identifier + :param thumb: A PNG image with the thumbnail, must be up to 128 kilobytes in size and have width and height + exactly 100px, or a TGS animation with the thumbnail up to 32 kilobytes in size; + see https://core.telegram.org/animated_stickers#technical-requirements + """ return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) async def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. + + Telegram documentation: https://core.telegram.org/bots/api#getstickerset + :param name: :return: """ @@ -2989,6 +3199,10 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. + + + Telegram documentation: https://core.telegram.org/bots/api#uploadstickerfile + :param user_id: :param png_sticker: :return: @@ -3008,6 +3222,9 @@ async def create_new_sticker_set( Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#createnewstickerset + :param user_id: :param name: :param title: @@ -3034,6 +3251,9 @@ async def add_sticker_to_set( Use this method to add a new sticker to a set created by the bot. It's required to pass `png_sticker` or `tgs_sticker`. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#addstickertoset + :param user_id: :param name: :param emojis: @@ -3050,6 +3270,9 @@ async def add_sticker_to_set( async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setstickerpositioninset + :param sticker: :param position: :return: @@ -3059,6 +3282,9 @@ async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool async def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletestickerfromset + :param sticker: :return: """ @@ -3068,6 +3294,7 @@ async def delete_sticker_from_set(self, sticker: str) -> bool: async def set_state(self, user_id: int, state: str, chat_id: int=None): """ Sets a new state of a user. + :param user_id: :param chat_id: :param state: new state. can be string or integer. @@ -3079,6 +3306,7 @@ async def set_state(self, user_id: int, state: str, chat_id: int=None): async def reset_data(self, user_id: int, chat_id: int=None): """ Reset data for a user in chat. + :param user_id: :param chat_id: """ @@ -3089,6 +3317,7 @@ async def reset_data(self, user_id: int, chat_id: int=None): async def delete_state(self, user_id: int, chat_id:int=None): """ Delete the current state of a user. + :param user_id: :param chat_id: :return: @@ -3105,6 +3334,7 @@ def retrieve_data(self, user_id: int, chat_id: int=None): async def get_state(self, user_id, chat_id: int=None): """ Get current state of a user. + :param user_id: :param chat_id: :return: state of a user @@ -3116,6 +3346,7 @@ async def get_state(self, user_id, chat_id: int=None): async def add_data(self, user_id: int, chat_id: int=None, **kwargs): """ Add data to states. + :param user_id: :param chat_id: """ diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index c6db03b02..17e28d04f 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -28,4 +28,29 @@ def __init_subclass__(cls) -> None: for name, value in cls.__dict__.items(): if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable - value.name = ':'.join((cls.__name__, name)) \ No newline at end of file + value.name = ':'.join((cls.__name__, name)) + + +class SkipHandler: + """ + Class for skipping handlers. + Just return instance of this class + in middleware to skip handler. + Update will go to post_process, + but will skip execution of handler. + """ + + def __init__(self) -> None: + pass + +class CancelUpdate: + """ + Class for canceling updates. + Just return instance of this class + in middleware to skip update. + Update will skip handler and execution + of post_process in middlewares. + """ + + def __init__(self) -> None: + pass \ No newline at end of file From fc72576aaa196d52ed580d52cfab1a27b2c4979e Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 00:39:25 +0500 Subject: [PATCH 0906/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e2415f00b..f2e5b394e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) [![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/) +[![PyPi status](https://img.shields.io/pypi/status/pytelegrambotapi.svg?style=flat-square)](https://pypi.python.org/pypi/pytelegrambotapi) #

pyTelegramBotAPI From 436422e4daa60b2f031a745facdac00985b6c8e4 Mon Sep 17 00:00:00 2001 From: WuerfelDev Date: Sun, 6 Mar 2022 22:29:57 +0100 Subject: [PATCH 0907/1808] TranslateThisVideo Bot --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e093925d4..e8ce2e2c9 100644 --- a/README.md +++ b/README.md @@ -862,6 +862,6 @@ Here are some examples of template: * [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search. * [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. * [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. -* [TranslateThisBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English - +* [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English + **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From be0557c2b5eeebf4945ed9fe4f15d24aa190c428 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 7 Mar 2022 13:30:39 +0500 Subject: [PATCH 0908/1808] Multiple middlewares allowed for async --- telebot/__init__.py | 2 ++ telebot/async_telebot.py | 56 +++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b61b076c0..eec844b99 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3088,6 +3088,8 @@ def register_message_handler(self, callback, content_types=None, commands=None, logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, chat_types=chat_types, content_types=content_types, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 18a000d00..8305931b2 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -268,21 +268,23 @@ async def _process_updates(self, handlers, messages, update_type): """ tasks = [] for message in messages: - middleware = await self.process_middlewares(message, update_type) + middleware = await self.process_middlewares(update_type) tasks.append(self._run_middlewares_and_handlers(handlers, message, middleware)) await asyncio.gather(*tasks) - async def _run_middlewares_and_handlers(self, handlers, message, middleware): + async def _run_middlewares_and_handlers(self, handlers, message, middlewares): handler_error = None data = {} process_handler = True - if middleware: - middleware_result = await middleware.pre_process(message, data) - if isinstance(middleware_result, SkipHandler): - await middleware.post_process(message, data, handler_error) - process_handler = False - if isinstance(middleware_result, CancelUpdate): - return + + if middlewares: + for middleware in middlewares: + middleware_result = await middleware.pre_process(message, data) + if isinstance(middleware_result, SkipHandler): + await middleware.post_process(message, data, handler_error) + process_handler = False + if isinstance(middleware_result, CancelUpdate): + return for handler in handlers: if not process_handler: break @@ -299,15 +301,20 @@ async def _run_middlewares_and_handlers(self, handlers, message, middleware): if len(params) == 1: await handler['function'](message) break - if params[1] == 'data' and handler.get('pass_bot') is True: - await handler['function'](message, data, self) - break - elif params[1] == 'data' and handler.get('pass_bot') is False: - await handler['function'](message, data) - break - elif params[1] != 'data' and handler.get('pass_bot') is True: - await handler['function'](message, self) - break + elif len(params) == 2: + if handler['pass_bot']: + await handler['function'](message, self) + break + else: + await handler['function'](message, data) + break + elif len(params) == 3: + if handler['pass_bot'] and params[1] == 'bot': + await handler['function'](message, self, data) + break + else: + await handler['function'](message, data) + break except Exception as e: handler_error = e @@ -317,8 +324,9 @@ async def _run_middlewares_and_handlers(self, handlers, message, middleware): logging.error(str(e)) return - if middleware: - await middleware.post_process(message, data, handler_error) + if middlewares: + for middleware in middlewares: + await middleware.post_process(message, data, handler_error) # update handling async def process_new_updates(self, updates): """ @@ -463,10 +471,10 @@ async def process_new_chat_member(self, chat_members): async def process_chat_join_request(self, chat_join_request): await self._process_updates(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') - async def process_middlewares(self, update, update_type): - for middleware in self.middlewares: - if update_type in middleware.update_types: - return middleware + async def process_middlewares(self, update_type): + if self.middlewares: + middlewares = [middleware for middleware in self.middlewares if update_type in middleware.update_types] + return middlewares return None async def __notify_update(self, new_messages): From c8fb83c97cab4e2d120dec067830b52d0325ee89 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 7 Mar 2022 14:24:28 +0500 Subject: [PATCH 0909/1808] Fix documentation --- docs/source/async_version/index.rst | 8 --- docs/source/index.rst | 2 +- docs/source/install.rst | 2 - docs/source/sync_version/index.rst | 8 --- telebot/__init__.py | 75 ++++++++++----------- telebot/async_telebot.py | 100 +++++++++++++--------------- telebot/types.py | 14 ++-- telebot/util.py | 52 +++++++++------ 8 files changed, 123 insertions(+), 138 deletions(-) diff --git a/docs/source/async_version/index.rst b/docs/source/async_version/index.rst index 6b53c6ae7..0e0418f79 100644 --- a/docs/source/async_version/index.rst +++ b/docs/source/async_version/index.rst @@ -28,14 +28,6 @@ Asynchronous storage for states :undoc-members: :show-inheritance: -asyncio_helper file -------------------- - -.. automodule:: telebot.asyncio_helper - :members: - :undoc-members: - :show-inheritance: - Asyncio handler backends ------------------------ diff --git a/docs/source/index.rst b/docs/source/index.rst index 98efb79ad..596f901c5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -25,7 +25,7 @@ Pypi: `Pypi `__ Source: `Github repository `__ Some features: -------------- +-------------- Easy to learn and use. Easy to understand. diff --git a/docs/source/install.rst b/docs/source/install.rst index f2589c914..7998ff8f5 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -3,8 +3,6 @@ Installation Guide ================== -:toctree - Using PIP ---------- .. code-block:: bash diff --git a/docs/source/sync_version/index.rst b/docs/source/sync_version/index.rst index 84e490db3..590cfe549 100644 --- a/docs/source/sync_version/index.rst +++ b/docs/source/sync_version/index.rst @@ -33,11 +33,3 @@ handler_backends file :undoc-members: :show-inheritance: -apihelper ------------------------- - -.. automodule:: telebot.apihelper - :members: - :undoc-members: - :show-inheritance: - diff --git a/telebot/__init__.py b/telebot/__init__.py index eec844b99..cf1be1ff7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -343,7 +343,7 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, :param offset: Integer. Identifier of the first update to be returned. :param limit: Integer. Limits the number of updates to be retrieved. :param timeout: Integer. Request connection timeout - :param long_polling_timeout. Timeout in seconds for long polling. + :param long_polling_timeout: Timeout in seconds for long polling. :return: array of Updates """ json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates, long_polling_timeout) @@ -876,7 +876,7 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe """ Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains - information about all chat administrators except other bots. + information about all chat administrators except other bots. Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators @@ -932,8 +932,7 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :return: API reply. """ result = apihelper.delete_chat_sticker_set(self.token, chat_id) @@ -1199,7 +1198,7 @@ def send_voice( :param duration: Duration of sent audio in seconds :param reply_to_message_id: :param reply_markup: - :param parse_mode + :param parse_mode: :param disable_notification: :param timeout: :param caption_entities: @@ -1663,7 +1662,7 @@ def send_chat_action( :return: API reply. :type: boolean """ return apihelper.send_chat_action(self.token, chat_id, action, timeout) - + def kick_chat_member( self, chat_id: Union[int, str], user_id: int, until_date:Optional[Union[int, datetime]]=None, @@ -1742,11 +1741,10 @@ def restrict_chat_member( If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues - :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes + :param can_send_media_messages: Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages - :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and - use inline bots, implies can_send_media_messages + :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, implies can_send_media_messages :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. @@ -1932,7 +1930,7 @@ def revoke_chat_invite_link( """ Use this method to revoke an invite link created by the bot. Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. + in the chat for this to work and must have the appropriate admin rights. Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink @@ -1994,7 +1992,7 @@ def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. + setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#setchatphoto @@ -2010,8 +2008,7 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. - Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto @@ -2078,7 +2075,7 @@ def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. + setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#setchattitle @@ -2989,30 +2986,32 @@ def message_handler(self, commands=None, regexp=None, func=None, content_types=N All message handlers are tested in the order they were added. Example: - - bot = TeleBot('TOKEN') - - # Handles all messages which text matches regexp. - @bot.message_handler(regexp='someregexp') - def command_help(message): - bot.send_message(message.chat.id, 'Did someone call for help?') - - # Handles messages in private chat - @bot.message_handler(chat_types=['private']) # You can add more chat types - def command_help(message): - bot.send_message(message.chat.id, 'Private chat detected, sir!') - - # Handle all sent documents of type 'text/plain'. - @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', - content_types=['document']) - def command_handle_document(message): - bot.send_message(message.chat.id, 'Document received, sir!') - - # Handle all other messages. - @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', - 'text', 'location', 'contact', 'sticker']) - def default_command(message): - bot.send_message(message.chat.id, "This is the default command handler.") + + .. code-block:: python + + bot = TeleBot('TOKEN') + + # Handles all messages which text matches regexp. + @bot.message_handler(regexp='someregexp') + def command_help(message): + bot.send_message(message.chat.id, 'Did someone call for help?') + + # Handles messages in private chat + @bot.message_handler(chat_types=['private']) # You can add more chat types + def command_help(message): + bot.send_message(message.chat.id, 'Private chat detected, sir!') + + # Handle all sent documents of type 'text/plain'. + @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', + content_types=['document']) + def command_handle_document(message): + bot.send_message(message.chat.id, 'Document received, sir!') + + # Handle all other messages. + @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', + 'text', 'location', 'contact', 'sticker']) + def default_command(message): + bot.send_message(message.chat.id, "This is the default command handler.") :param commands: Optional list of strings (commands to handle). :param regexp: Optional regular expression. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8305931b2..f654083f0 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -584,29 +584,31 @@ def message_handler(self, commands=None, regexp=None, func=None, content_types=N Example: - bot = TeleBot('TOKEN') - - # Handles all messages which text matches regexp. - @bot.message_handler(regexp='someregexp') - async def command_help(message): - bot.send_message(message.chat.id, 'Did someone call for help?') - - # Handles messages in private chat - @bot.message_handler(chat_types=['private']) # You can add more chat types - async def command_help(message): - bot.send_message(message.chat.id, 'Private chat detected, sir!') - - # Handle all sent documents of type 'text/plain'. - @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', - content_types=['document']) - async def command_handle_document(message): - bot.send_message(message.chat.id, 'Document received, sir!') - - # Handle all other messages. - @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', - 'text', 'location', 'contact', 'sticker']) - async def async default_command(message): - bot.send_message(message.chat.id, "This is the async default command handler.") + .. code-block:: python + + bot = TeleBot('TOKEN') + + # Handles all messages which text matches regexp. + @bot.message_handler(regexp='someregexp') + async def command_help(message): + bot.send_message(message.chat.id, 'Did someone call for help?') + + # Handles messages in private chat + @bot.message_handler(chat_types=['private']) # You can add more chat types + async def command_help(message): + bot.send_message(message.chat.id, 'Private chat detected, sir!') + + # Handle all sent documents of type 'text/plain'. + @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', + content_types=['document']) + async def command_handle_document(message): + bot.send_message(message.chat.id, 'Document received, sir!') + + # Handle all other messages. + @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', + 'text', 'location', 'contact', 'sticker']) + async def async default_command(message): + bot.send_message(message.chat.id, "This is the async default command handler.") :param commands: Optional list of strings (commands to handle). :param regexp: Optional regular expression. @@ -950,6 +952,7 @@ def chosen_inline_handler(self, func, **kwargs): """ Description: TBD + :param func: :param kwargs: :return: @@ -1487,13 +1490,12 @@ async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types. """ Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains - information about all chat administrators except other bots. + information about all chat administrators except other bots. Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators - :param chat_id: Unique identifier for the target chat or username - of the target supergroup or channel (in the format @channelusername) - :return: + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :return: API reply. """ result = await asyncio_helper.get_chat_administrators(self.token, chat_id) return [types.ChatMember.de_json(r) for r in result] @@ -1527,10 +1529,9 @@ async def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param sticker_set_name: Name of the sticker set to be set as the group sticker set - :return: + :return: API reply. """ result = await asyncio_helper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) return result @@ -1543,8 +1544,7 @@ async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :return: API reply. """ result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) @@ -1767,10 +1767,10 @@ async def send_audio( :param caption: :param duration: Duration of the audio in seconds :param performer: Performer - :param title:Track name + :param title: Track name :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: - :param parse_mode + :param parse_mode: :param disable_notification: :param timeout: :param thumb: @@ -1810,7 +1810,7 @@ async def send_voice( :param duration: Duration of sent audio in seconds :param reply_to_message_id: :param reply_markup: - :param parse_mode + :param parse_mode: :param disable_notification: :param timeout: :param caption_entities: @@ -2338,24 +2338,23 @@ async def restrict_chat_member( Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember - :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup - or channel (in the format @channelusername) + :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :param user_id: Int : Unique identifier of the target user :param until_date: Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes - and voice notes, implies can_send_messages + and voice notes, implies can_send_messages :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and - use inline bots, implies can_send_media_messages + use inline bots, implies can_send_media_messages :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, - implies can_send_media_messages + implies can_send_media_messages :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. - Ignored in public supergroups + Ignored in public supergroups :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, - implies can_invite_users + implies can_invite_users :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups :return: True on success """ @@ -2536,14 +2535,13 @@ async def revoke_chat_invite_link( """ Use this method to revoke an invite link created by the bot. Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. + in the chat for this to work and must have the appropriate admin rights. Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink - :param chat_id: Id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Id: Unique identifier for the target chat or username of the target channel(in the format @channelusername) :param invite_link: The invite link to revoke - :return: + :return: API reply. """ return types.ChatInviteLink.de_json( await asyncio_helper.revoke_chat_invite_link(self.token, chat_id, invite_link) @@ -2598,13 +2596,11 @@ async def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. - Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#setchatphoto - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :param photo: InputFile: New chat photo, uploaded using multipart/form-data :return: """ @@ -2616,7 +2612,7 @@ async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. + setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto @@ -2683,7 +2679,7 @@ async def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. + setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#setchattitle diff --git a/telebot/types.py b/telebot/types.py index 15cd2d144..081c0ed8f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1041,9 +1041,9 @@ def de_json(cls, json_string): def __init__(self, keyboard=None, row_width=3): """ This object represents an inline keyboard that appears - right next to the message it belongs to. + right next to the message it belongs to. - :return: + :return: None """ if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases @@ -1058,10 +1058,10 @@ def add(self, *args, row_width=None): This method adds buttons to the keyboard without exceeding row_width. E.g. InlineKeyboardMarkup.add("A", "B", "C") yields the json result: - {keyboard: [["A"], ["B"], ["C"]]} + {keyboard: [["A"], ["B"], ["C"]]} when row_width is set to 1. When row_width is set to 2, the result: - {keyboard: [["A", "B"], ["C"]]} + {keyboard: [["A", "B"], ["C"]]} See https://core.telegram.org/bots/api#inlinekeyboardmarkup :param args: Array of InlineKeyboardButton to append to the keyboard @@ -1085,10 +1085,10 @@ def add(self, *args, row_width=None): def row(self, *args): """ Adds a list of InlineKeyboardButton to the keyboard. - This method does not consider row_width. + This method does not consider row_width. InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: - '{keyboard: [["A"], ["B", "C"]]}' + '{keyboard: [["A"], ["B", "C"]]}' See https://core.telegram.org/bots/api#inlinekeyboardmarkup :param args: Array of InlineKeyboardButton to append to the keyboard @@ -1100,7 +1100,7 @@ def row(self, *args): def to_json(self): """ Converts this object to its json representation - following the Telegram API guidelines described here: + following the Telegram API guidelines described here: https://core.telegram.org/bots/api#inlinekeyboardmarkup :return: """ diff --git a/telebot/util.py b/telebot/util.py index bec041162..f7e9e4afa 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -350,24 +350,27 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' Example: - quick_markup({ - 'Twitter': {'url': 'https://twitter.com'}, - 'Facebook': {'url': 'https://facebook.com'}, - 'Back': {'callback_data': 'whatever'} - }, row_width=2): - returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook - and a back button below - - kwargs can be: - { - 'url': None, - 'callback_data': None, - 'switch_inline_query': None, - 'switch_inline_query_current_chat': None, - 'callback_game': None, - 'pay': None, - 'login_url': None - } + + .. code-block:: python + + quick_markup({ + 'Twitter': {'url': 'https://twitter.com'}, + 'Facebook': {'url': 'https://facebook.com'}, + 'Back': {'callback_data': 'whatever'} + }, row_width=2): + # returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook + # and a back button below + + # kwargs can be: + { + 'url': None, + 'callback_data': None, + 'switch_inline_query': None, + 'switch_inline_query_current_chat': None, + 'callback_game': None, + 'pay': None, + 'login_url': None + } :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} :param row_width: int row width @@ -484,12 +487,17 @@ def antiflood(function, *args, **kwargs): """ Use this function inside loops in order to avoid getting TooManyRequests error. Example: - - from telebot.util import antiflood - for chat_id in chat_id_list: + + .. code-block:: python3 + + from telebot.util import antiflood + for chat_id in chat_id_list: msg = antiflood(bot.send_message, chat_id, text) - You want get the + :param function: + :param args: + :param kwargs: + :return: None """ from telebot.apihelper import ApiTelegramException from time import sleep From dd589e249091539c68ee0708a98563281ab9c20d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 7 Mar 2022 16:10:44 +0500 Subject: [PATCH 0910/1808] Updated documentation to another theme. --- .gitignore | 2 -- docs/source/{ => _static}/logo.png | Bin docs/source/_static/logo2.png | Bin 0 -> 58905 bytes docs/source/conf.py | 16 +++++++++++----- telebot/__init__.py | 27 +++++++++++++++------------ telebot/async_telebot.py | 15 ++++++--------- telebot/util.py | 2 +- 7 files changed, 33 insertions(+), 29 deletions(-) rename docs/source/{ => _static}/logo.png (100%) create mode 100644 docs/source/_static/logo2.png diff --git a/.gitignore b/.gitignore index 721966301..2586c7116 100644 --- a/.gitignore +++ b/.gitignore @@ -67,5 +67,3 @@ testMain.py # documentation _build/ -_static/ -_templates/ diff --git a/docs/source/logo.png b/docs/source/_static/logo.png similarity index 100% rename from docs/source/logo.png rename to docs/source/_static/logo.png diff --git a/docs/source/_static/logo2.png b/docs/source/_static/logo2.png new file mode 100644 index 0000000000000000000000000000000000000000..df0e0d7b02b00ffc700b02ec8f7045219d22e503 GIT binary patch literal 58905 zcmXtfbyQT}`}L5LiULC;y@WB5`GQ~{OUtb3M<9&CXOBsOzt*MqG%Y1n_d0zYy?_{^qgPNm{ zTpKL|ZAesN$})iuE){BlSutrb)}C<^8G z`?m_0^ueVCN#&@OF9I1a>4)~IxGByeF0~SA1P`!x06Rtb` zbFP@XX@m~*Y@PnzU>@!SK6iMX?aK*|!_Ug>`TdJbIM(Y3g6ddRA36|M1@29Tn&g04 z(xfbNeq69*c9>)HE(B=;1YECqj`rg6@*_OZH|cvyz4lqABF0s` zHI%Icc=Qr$smH~|ReVb-f8xXrLP8k=w%(y2OWjCl zf5kR8Hw%|{-1|TzX@Q+#n4zHLVVtuJ`O;7bIOvn&gs`JK(dPg01Ff9ZhkIQ!fU%xS zIy`YIncHu3pDIvl3CRz}II{;Hx(YzE@Oy}%5MY0X`H=#o|02Q@S;g0r`^^RgtabNg ze5b_*_V0#1HzpTrPRM}7kW{ry&E{DcJ)0{ygv z>JPe?!@A0a4wp)&1Dh4B4+N?9LkJmxRii|x$HSQONwD8x;U+}_R%i(BUSIX}@K{l0 z8E43(7kIQAg)qsTfwmSgNJ~|r0{y#>*W(_Fbd7Ooe=Q)9$W;U1!@8RM>rWd^OEtjK zmv%Y{S__TNn+fyt^Ks85#c;khF}ze2NvZ^`qJB)8T-8ulzqNck99(*AT!zO-xC9!( zpBQ$X6n6Vj{N$45fCK@WdoCHkHhkvV1@1yU1-8Mm9XyEoL1N7ul!Zz_Q1Bc~0G6u^ zL|r3^%n^aw(L=xL0oF>!Ee7(6sRe6RELIf+8oEl4_X0INEb_H=#fXdT0CV6%a~@ z)R%yb7uvJm{_Y2yfBtjjQ-6ly1=OAmg2^Q%D_sA9#jD! z5x(?rcNb-Ws){|X3OUwI$dl?`KbZ;se}E>UA%R++y>l2yS#9 z12)QQRWLRlBP1lWAWA*11*%j9pArHeJy3=bK=m}NV{v9?CUJ2wvSn)$a6VU?L&{ZF z0xUoL?^-CF8g&2nuW=ZbAU|0|lH<}3nV z2*&sa{;7<0g@g{z;i7#;0yzr#Z3^YU2q8`kDi4{t{&{|`N;h&;)QIB3N-*q;x} zm!!P#T@^PB4CF9+yelWy2i8kcKX`$>+ zb)fX5zc?Y)PaBK9-wFQEEx?2N5^W~MENRx^qs0nb4GltK@QrC^UKML|zF^qO@o@_j|ay zMXgL49UXuB-SubN-x{Nah35!khcjlf)A~2YB98 zoj&y#E;=d&$}Q;oq{vdcvE$%(*XjT=2szTU*#Ti%I!Tlm?Yao*;Z;nF=;N73cdY8H zI84cqm~neq%y^mOk)y{syX21|4AL)S8Y);?R$XEy1QOb4kI!o%0lMN7deHIM1RL%> zf3%JA99xh1GTq2Dd-w-Nj(EngvO)JKBp0C)7LMzxnDm7?hi$({wvUTIYZrccl;f{Z zC&60`+U-xcYE~Z4Q1VjdIrgm-6aKuJH9(axn|0J&6EGfeZFMH@OVgy+UM@xqTbF)H zx$|%e{_z)JUnhp&RqAwLrgbu$S*bwc> z=cxsb6%RkTY)V*22<$6Djrmoz{s;k`%P6w#vmWkDw$rE5(%pb|7I>vFaTKH^XS@r@ zn&m>Yz}1y{6%v%-l01bkmt#ZY%T7X50^|N!45GRSQr_EXY0Z^G4UF{p(H<@G-D_TG z=J?V`8LJ6XLJe3SZ!V+ID70$!BbS<}K!WBw(6dh1)kPuLy)1=$g0wJoGI&NeyE)$8 znsgqLkW_u?jIX9pu?P5~>t6@j^7Q{}M(Us$H74DmmRaNdpg;aP&C&>cWRrue?ktV^ zOyLLV(0uBk2;D~KrW(2~>%`eQh)FA`xZrPgfH{RNV~DaZQmnB9{bOf|L-TGy_Tye> zvw{2}#H(HZ6v#8C3cDP`ujxGtsxdwuGMdyEzfxwA<3w1w=R~+y&70@=lmVIH?uIskV%cZ{r>h2+#5<2KA3hPBVO&<|Q)-#Z!@BKd zja#=Qq?p*%AgM5OS`80}RG0Thtl;_R2=b>qtkkFbu;uWG)HhSSAp2@(BBU+EBfIXw z+pCbk&*>&AFX_Iprsh1#!AMJgJmqo6HD5|(Z|?5vfM!Qvt5lhHrS@g$HZ}HM*@p2O zv1Dr)n!sm|@^XeA4UvruM-^cKR{@U~km7OO@@V6|gC%vY33E8_bv)@(SLO-P_JJ?? z%+!tkU1(3QwpXeM`O`4*+4=dapi0?*$J{RN5w?2#z)Ab70o3YEE@|jU0Wrj57epb% zXC>sb=ZTEKf!JDxHYc<@t9AKa?DoSFxh4c(y}zhzp0Ky3_93GtAKJfV?Mw>;D#r}4 z?#&Co`k?m=D0$vq(g7V0o?A;rA?{kcdVWZjb*r}D`v&UO28=Ap)Br5w}YeOwkO8M#?k`?s16i6v~G{P zr^%zi_FK;nBDtd53G-%3%!!I>vVlA7!KNZ2&Rz~vp34Y^*WWWDc$kI4E!NTeg(#mI z&CkqSz`^Bg?1NQ6lu#-~Nzj}GfJP2gk>&c9+k@Bnq}JkO(}Y}aP{HBJg{<>U*Xd(s z@T4aNt-*z}_f~HO?mc4LyQ-V<&oka@Ba*~FL}nqYj|{(ka7B9NKLAM&Ju<)Wm^3^o?J)HRU;6QrhIQ&4rKqJI7-4F<>x(+wCz-N$f>Smm9Z>BxtH>Nb ze%v|YFT9W4F8Et_Ukn9FsARUds>1l_hCR;xWa-0By9G(lq!b3>=v(s~ryAEoZp2hx zK9N#)#aCzZcb(%~rj#_azu18`;e%2d{~>$dDM@!S#wW{NHg`9-^rKU+at^Bx7?!$7 z?dg_V$>)T8yq-}-N+r7}s0I#z-5P?-aY|Ij>0`SnoiLOYhj(9cCg5 zhGCoBW1Qa}n5UJoHj>JoWe}V_*FoxZ<^F531}Yvqd7=Re0aj(-qNEM_cc;a5QX2!- zYFD12F*bjQExginz7^N!<%_OM_6kMkFE`maOh!XTY(x8dsB#8As|gGmstON~g-jbv ze9dXI@u8=kFb$dA_N8o_g#wn8^_PW3yyiv9yhZvZZYtkxP^{iklqQq)1+6?WxaRJ) zNYIvm1h>i}EhcWpLYV0Sr!nXi5^7Ubeb55U3>x+2^-Wj1csQ3U1sNBbR(=q%6kx95 zt90=Qqj<3r{86XZM||Af^LF;*d^)Y}SzNVTAe@6XyUQ4E94XM)R^-ho*0{i zeql+OvSLm9(Xx?HYP;hjSyT8jfSAv$ zHB)*5OU}G;7!A$Vn)vZR2Xrp7f78k{qdJ>pX|-p_Z6eu{3%wi6IIS|13z1IwZ6pvU zamUro*p(_lL3>>N0-#LRjV<&w7(S?~Lz@~Qdq_^fW2`vQo^PZzoM?tqFdmRlEaNb++a9lsAFIQz`2%yy;L zN1>7TKW&5sA$Ic#RkrzS~toBPdQ zGm&OIm7x4TEg2aZMx$#lEWC?Bs$mtPfNRcLy>$1|ou^z`4R`^e(3A-ZKeE{wJ8XY8 zLnvCl;B0hLlw7W7tsMRW&TDD53F&4)9YOOqOmy^oQM`TZP4KyfzP#Pnxhe_#&qx|^ zulRGI9Z2vC4a6nSEl%HpNr*cbz(3nIRD)T`=^WHz2PDaJ(3JegbDU2s=IqCoe)uBO z!`@1xRkYN3bhvf)c#wQ?G0PbvtlR7N$Z?nZQ|fVtCg9s+B7x$nw!(J zioh<>k2n$jgi?|<+O0QZrK2d$Chd<YOMNt=*L%7IX1&)2KmyPKjO^! z-;&mEo&F5)Xt@4pIq>^P)55hn&~!_cmR{EK5_b5HU3SyS=Oj~lrzbVqwUg$l0bg-( z1bOm3yTPIU-Rse1{SlpU?6XiZM+MVa`^8Ii+k(s2{&gGw@Yp1oogeQ)q!~621V7<0vqn zGgIq!9j{)<`uaXxrh4m=b0OLo@;&UrbdM4aSD}eK`aannX+hBuE-$>@eyRwqFhsw` z@e#`2##%~quv#J_GhL}Cj!N%`ZF-B{Joxi;Td&?Y)qe|W3~<4t9nyq{?1G zTS(s+mFZkDy%GEI){12hW`^Yd;GG^?vFk6$d?>#p0^Mszqb654$B&xgt^?&K`A6>y z3kyqc{uDIBp^4!M2f!x5_cB!2)XjlMSv%gzKf0ke+X0E1Y@Hju&$o{}I)1&HS39&9 z$DgXh6_h-}CK@B481;awb0W;%HDUO^1Y2zI(hsH3lX2!8CSOCAnvVPeS~Ts|TPl*%)X&2W zDZL=2gG9I>W<_X-c7T(t^kG$GyR&-=$ASgsC?#hBtJJ zn+OP%&RTDOm=r8D31wYnqauZczVatL)u5&u9RcqvgLU%x(0;xLQS<+>BN+Eq zdNj$HPy3RB;7T={3bcQYSmnRh2CP}|UhY_~9~iDbNyLkGJS*egXW-J$yqWL|*fLj%lRK66h!RlXqUzYf4nYtJ3AZx6p=22~6e~%rqwSTS5^_17X zn-Qkxn)F)Ens(5qX1cOW5m_5Odiu$=t|;~D=Ujm9FQd{u4%al}#Mbf|)AUz8u!~s_ zd^5*vi=Y0ZTcs+M+>Iz|666#V!r%0_(1*ctujHGRtaq9pLOd&0224E0k_(@!?{xS- zVfj{*)+GGH0}qm_ncDIl3u1esykm7vu&P@Y@JFc@uVdprk66pC7ZskSh1|l`Z_-=>mvdP>$;-(qOk0I-)HQ3hp_!;yKxdP4 zlzEZjiMl@m?#J{HT!|S0NgrL~`n0=?2fSjw9#>-MS6S?U-@thdIpsMg4+2^o3^G)& z7stlN;+ovsOdFMTP>O)w`YG)%*4`7-NcU(inN%`DKFB|H- zKx6SWHR%N$tAZuSvC#QZ8@W0P*yCD`b(v9Cisc5wt6mnpSqkil;7g`Uaz4`d=;SvF z6VUa2KvVJJr#Dlcg-SfMZoX;LXbn<0TzLJ$w4t0ZM%ApQ&#@K5Zs=~SUYWNh*}C+P zTup*;9+}dOUxr^w4pZ8h`?>yd!>RhCBlSeCJkS}4*KfqwGwRz^O9SZ(g$4y|87jLe%=+BzSGvO7F?y=RpJ~y%SAR@wR z{jNuC#+8fnQahMsFOmTQfDylHm+r>!_=VJi5YM*&T~4HfRMcFpGJo8$f&%kCx}V+p zXvriM;!*+I#L+^sH`GvFWn;OSKgBv|gW%>_wvM{W(VGh|0T!#!b%5_7NtYNPcM zr!~Saumu|zd!Phqj9RENlFvP>#j0`D2qllv>F+`diU(gbSEUfRR0HI?^c|5rrcw3d z!Q6NL`K@@GPkhGXUV&Fw)t{8l#&4D*07J-kH=li*C#CYtxNo&Xx8mgUpV z&P2~Xf3P%Zk#h|$3~&?Hl|lamz9u*3YJbTRd6rN2J~Vj!3*{$;1h-fDe?0U5=0t9J zH*29w_(#XPz`*E)F~gY3D_?V6ki}p=EE!$su*8t_3rR7c*wwu{<%|zw zVx~C+D7@v((4ar7MBX*l9_wjnx#=(69-%fLw>Gj=cZM{Q@#;CG3OI1wSQ6y z+nEWYYkz8f{I!W6)@|kH?!FALcIw;`&CZ*BA)Ur2;vgh)XqdTg=eOYm%3cW+^FjDNi#hUa-t>KS z&XdXi;O2YiUlIh%riyu9UoWS;=V$aB3u8K+!7*YkgxDz`w^imkK1ZbQhk>hjH~W@I zY3vaBRu~^0sZCMo^Yop6fjLa?S+;jrZT~arEn_o5>Ju7}o43t3akeuUfTJZWC#%cS2w1<1374_ z9jhHkf-b*ZI(FT_nlj%j%(q7Nc`t3!eU+ct7r(axL0-KiRH8;>mxUB_t14u(P+-%r zeXm%Vp$%cmm-X=QXkXjYsPU@;R+3Mk&Q2;Ht~Th|nyUSNwDpDVR|&t{F#*?vHnlot z+7msliAnRE>+Lt3lf2?Xbiy$NXNAyVM&hZ;D4a)=Jj13nVuGN3S=4U%VuZR^62(Mu zS|f4sTj#}UtYMnzzr|z++s_$kFF7ZOu6bsFc4m5{ag-XrQ*PLbFZ(}|sK%N+jtKuT zAi47Y7;W)s;!;J{u?9}>3h4CoAJYU=LWa+rvc;tkAR8Kje>{Pg#LZm2{{l0& zCV{seYe5G%7J^U zgGge8{=>iLa>b{aDS4v=lf9rUGgW9hfVfmo;Z zy_sr{kaq%8ueI`a9;6USp5VQoxQ}hnHBJ7XW=Xzu(l3>P6pt^31hr$vXS{>EDI{#M zkrq+#lzUaYVZJlAva)*Pam^z480yxSULsJ@iYhOo7Sg85z&Z?{g>i|>9IgWNd=E7) zh%K3-m)8h8J3@A}yiVZ{cer_v=dLrK@EjGS_}fQp8C%gTh1>9TfX;dQs2yjETD?Ji zJOheAPmuk7 z+7b=nKM6tu%uxCKdrXKW6Bo6~2Rm(J&l@rwB6lrx2Pq0UzaBJIr0^FoF5dtu)0Wuafq)M*F*!M366 z`mA6bz#H6x;w|5S!OQL`Z@b@ALsh^-1GxXi)HpV+7ICC=k6`5 z&M(@@rJ`yJS=W;bb(*!c9!unWW@4|Czrrpbfbz50ld8aYpF99&Y~$368cNh1?u>=| zs|)opSrzsLCo-zfTjZVVb_pI15LA2&y!x!0wBPr%-gE6YdgnXgHZRwP-zUjYj)3%> zwE&QcZ`qG5+~a~!R41rM_cS8jb4cSJ7&>7}ewL9*C5I2iN9=J&;}gQ)&85bd>yUK` zhzX8nv5QzRuf(@vq84m!eCJb!xac)JY5D8({eI| zN#LSnxT+ApA_8wsF0(~>rR>r9*W)KPWIe<3Q?a{47jOUitqlKocr;^?NBQWmprD=c zSiEfm{`s7aG0K)k#+ao4YZ5IvUg`A;#+$XLb+9Bc_UdV!1X@19(26rC~zMlT`f zQk%g0$AX_w5ucnwE$pfvN9q|6`BQJ*-Fc9v6JoQ^EoG$U5TyJ|+M+arVn3E1z~rUB zdX$u8qIqTE>dSa3p3((m~_@8;tjkz~^hmXWvS5aA$jef6tM*>%C zQ(y3kdH-91v< zkPym}lM3Jc9o$09`RniFjN_i%R#c#^y+kOea9lSeucHNv5|h4rO==cXLQr1F$EJvs>e^id`O#;b&Ed}8g)fa}pStnU$ z59?RZL?{dAeEy~BHtODe>6k!p0rwu`J3SL^nd0&3|-3Ocp_G#Tv zLM?NTVUl}JP=u{1j%A%efAlmT#FjprsVF?`yoY}CNmW~al;eF9HKE#Tqv9y<`MbIt zrmTT2v|oP?9~$bj{Kf4A#Ymg-e~&?nzk{?-(C%)S#&38|O43I|$*t zs$nC9`)ftdb!RAc*o6f$Ble;sx&;Z%D|Z(xbXxkdEIimbAgSUIawD|Mp{LK<&*v~l z&|RC&Ne-_SneRFP5bn9NhvM>A6PR0=^%lvLb-6IUY9DvI(Er0GhC8hnPe!QGVG&iT zrIt~o7b#6AJg0xQ7HHE`Hn>e~Ge0oKw^}(3;PDy5V{|!j!TdHco>cN**OR4$d^U=U zSmNdYH!T&nw0$Me`fqGCIH7pCP86sG(?BgKT;Ak)&2IG{j>A~nfMe=Gca*gnOtOYx ztVVgqIq(mDN*&s(k+z?*5r%hntX!{kVcaj1LMj0Y4eB(%ac|Q zfGT?9sxZOXxhI#wt;$L!-U8>%xU!Hi2z$l3go#Px^)CeHb|Z@}Z4LrnDrFja#UD5N zyD}a0*x={@3Swroj4lHs55b|HpT`x~MA9p=v6}cAYQh-s?)k!8p0=O+3?B8=0EAHK z70cW3g(eYH#~0740DWOjOV~6IGFOgkycZq;fukRa|HPP5KgUAvXluf}6lH}`97sqC zQi`>dl-61EXZ2Yp6n zN7%R_x(p7Z>UR=}Lp#LQ;)vxo9ZjwYw-{r%^J{jlKo$p@9Cc`3~Y=73eU&g z{`V8XZBkvm@9IJcCCB0|Ucd7en#Y`6WXj*s6E)|0xB=!8{%{DXaZcDZ@?j7vfe0OJ z6eR^{NcLa`gJbSt5qneLU(>t4iOvS4Sl3FBqNf>Mtjj(SyyDU%mBeZ?064bNSjEfL z8qCMsX%2dpFI;IV7*{8@SYwLTWw>o9uU1u9xCemkt*xv^T|mq&%s%Tb_0tfqpR$ae z9*~15)WRZ_EkhEI=RZ@S5M2zvg^T1x!3Z zU~=5HeHk-C2tbznXy~$)dOH^(#6iX_u)aNABSY97{h1U9N%t=id#f$Qb?;c`nZg}g z#g*roG(>sddzuei*X>)-2JAO=kra1tpq`1Tz=g0_{qS)+23QaJKjJ-Nsf&zfqhI6hYrtQ%DkXt5I5_m^Y z>T9fB5InI*nQ$)_pEVi(Q*_*;f&@9JxGUc+?xyF6Tu^281K0FGsgZ#c@5s&fYlvH0 z)ixR@@3CMSwv0THz8AME#+(XJa;^-;&1__ZxEgyVpWffb4m@Aj+vtjz(6iJ<#^#r8x?nn&Wy|gP;hp*PpuaFK4 zwY&DiXjGJILZ7bh?vDv2%RM+lbA)(*74>8if1x`l<0Yh@%K6+pEyao0Ve15_9cr|- zK(OqQ65#F@Uwlw01q#TgPGXqB7L1T$HasqZ>1q#q(5?cQUMbryscOkcX#8E_JC9s+ zNaS7BRa;qqSV_e@mf{Oum5+Drfd1=ozEY05`&r9Shl`W*2kuvMf4ovlx7rJ13T&Am zfDV2_97ackc}FW>NmmfODqkaPG7Z{`L$S#q3?sOaS^NEf+;h! z7~R_xlZdl_9;+w}N&R5=%8~2Ws~;$h8?tPqS!YBGXFArK^EpgIx(%3H-l12m3ue0iLpn_ren7Et~i@oWp zQ}Qa&VxZDd0!t2jsD^e}x~I={wEWi>J^{5`yVscBT}Fgj0FxiRnX2%?cRK}|ear_3 zOComE`cq?USqJ)~HStWXtAf{!Ex z*f|Ii6j4B?$FU7?s9Xq9);jM4d|qYkcWp4TB7>7!U;XEt2+`1H&rfTc9ky)Kuj)Sa z&3t1TT7o}G;(9!AU?BHCbyouC;`UPoCP4J^GRfr=(I;Dv#Pun?jXV%)*l3>U{_XaIF!9MdvBW=;^|cO*AVOPb=#AkLrq;|cc_oiVBj zwPxWF)UZnxUcgZUSTZoeIH&xh{F%4;*7J3@kMi8lc+{XSSrL2DZbv^{3scA{_1Sr4 zgiL-ocnAy6p^A8)uk3D5mQx6Xg#L+nHl4E{udC=Af#wvK!?a^=8W#QNDj%8iIV`vX zCB+*aE4KPLSEKvX;Mi7OZR7w}Dwt=}l$4PtnP#zX|Gh2x=JZ#)1ZG~2*F`CwOWSXH zU?HXmNqAxjSw@N-1Z@h#KQ5P>*P+RrvY9))qQ;zF(fJcEDTHHoFe*a`iSkPXX~3y8 zNp}?E@bdm9j?Q<^rK{3LVt^KKCc7Q&6)rNAM<#_Gq{W9j<^Rbpo#c$Wp9Q3tb$U(o->-55TO;Z$vT#X0{%X8?_mkNo$YxJ~cR zJOZoAbl&fmK&p?7*=!6m$H{X<(MBGP;@-7Z^=XjmI+;c$d|`;v4)Tmr6JShcic8o} z5a7aaFZcbaU{3eU;h?n-w%H^6r*tluAU)ALx##=^arKTjY!U%gX!HKD+`Mu^1ilcp zd^afQJPH*%e~N2CuNNU@!#QimHKG1WD#Df%A$$k3`My~ZDVEO<8G9;?KrRZ78PI%Y z*hN*?E!$mbBput=^CE}rgZq7UaUiP6TUj=H9N@2!$n1-fV|!Y)WSs zN4i3S>L=J>iME0s^ky$rAl_3{BV_-`u981hFi2;OP8u=URa;B;=u|q&$Ltn2$GUaT zPc&hqMO=Mq#_(yG9Mw0yFtw%$94=u2lUDv3Hu@@7wl_@jBf=E^HautHL}0=hsu%e1 z8Ee0^vMq?y9`B0xHXdV?2!3cvU9z=M4xMu*k_fK8s`(es>$GS!oSRgS@JZtvMAGF? zky$rw-A77FG~W#?P#_}>-2GaO@BBx@FG?v2c!_pW)Iw7T76iKrVr87r29T4S6C(WI z_<1bT;M0gU7Zmdyhz1e2-&MU?*un>UsqtKog4Nkc=dIpE!JESQ=^>f&KA3$Fo#P)5 z!bnPP1bz3zkMSL2FZ~{Qdw3=RZJrKti;JSa88z=^UXtO?AIn}hk*OIf8&Td^d7zO! z&4ToC+xKAAL5C!Z2)AbHberzd?SUZP>UB_KSNr5_DwH{+?r?X8TOzEXIy5ED98d_$6RFC{2oLX`q%v|UGV}@Bwf8Ma% zab8m9{^Q&IjyGJn^mqi=w=EOj;?ItZPiM18# zbIGFwiz^WTGN9*CQBg$!Lw$?Nj_UCKu!v_Zmy6aXu~?6yoc^ao;q1CzI49yVnW;r} z^PdNAoMS^<@W6v3L#!8)k#MG)dAuF*;n@DgMV<%aTR}?0$(fnD;iJ<7!Tx9QCpCt5tp@0+l`f+{9Qf^vWCW_Ib;g|?rH|h0Iv==S4BQO1M zG&&J_!vkj+w70*_HgzqvAb3*_;0iGohq*7e+=#m)vqbdc2b)2K?8Q_P<)?K2H;_b` zvH8o`qm)7Znd;+h4nr;~@ktjaIGEEy(;}8Q>MS#3IO^(TrPP`D1sCFTcI1J6xjr2L zU!@owtudpnU)HtxrMpk#iMdo6UMJ8d7of`<2E~siTmNa+4DAF3>)D$)O!Ly!z0I`E^6^mi3&ap65N z01e26D`>Cu*%q;0uJP2I2pEsqLky;RI1~qD#VUsWyOT_lX~?#jY~G9#ixI0gLH+jf zdGrlkqTTiZw`M;bE}8jhGXWtVM}De7hp!oZrmq?U<$MTP)_8*{aEk)&G%!2<{1YbX z2Nkq`DP^oAr*8nABgjcwDo0Ns_qr9Y))hb_h#})->!*q=FUU0)#8%-NF!*#?vNbA3 zPmceyZbdTAg`2yLVy@bTJ?lGfPK5nLqeFM}Xjq=kQz z@DUH3qQoos{ZxZ6M$<7mu#otlbG3kdpy71JXPztJ=Y(vT$=2j9@0*S`63`$+;W~G} z*lT0$>)Jx6=YmhamFPfc96XV5-LLa*n+q`+mJtzqtkeGE8tV-Wd<*sYf&|Z)B3+sd zgnwfGXI?zakINn$`lbC!TI0PduXd7`o5=uFm}I(PYFnS=T&lpm(PvT;6$Pl)J5>W? zF)V!f38TN18O`1k9zJrg+PMUfrW zx#MB^_b(NWRaSD&Nwz3}ZLI;@wj3sR^Z!qX^>5t|s@B4APDL9$c2UYHb<$?hj z3hO~$>S~BPG%Zs$FBxC}xk|Er%k2N$k3}Y^TroPGE$A$#r37F{vvMKA{B-~da(NSu zN0OXQ=e4S`EmUpQ^b(GB6%JgzSg86)NYj?0>Jy(41wQ9`gsin&60W}(QuV(a9+5rb z6;0++XkO zsI(`E>j8COGCygOy(H>$SH~r^1HCiS=Wr1W=N)9Ni^taqcL|GCgRn#-%CbK|4MNrJyB-4h$)qTw)#g}FRg|on~IG;N2 zT>@i)Htw+oG|MQ)MKisTcdH-Ug${J8-f$u`3}Y@?i6MJX3Y{KUjTR%h&gKjSl+^3R z=QsUSrB3VEQ;oN+Hp)}ph7XADR(3*2v9PIwcMj(Js|M?{LxOJ}ERoeCdz6a*6QM=y z-LqByT|Q0*HT;H4HN>gLMhKxLcI`cmdNXq&FmuZ2$2N29MvML+Zuh2gt&6OV7Ue`B zU+8e_Q>@KHMt~qEXAVC7J)9^wI zm!EtPL!Oz}XbnDH{a~H5XHtEjzidGL)F$&)Txy|Wd1Q3wj2qqUy8<-RHsce5h&Hf#v$cV8m@N^+ns>z1 zDPz8~mqC%>cT!}wA7i#SA!4F8IqeoVgcG?MU`Z6?&gYFrwnjWao`c6A+x zb({2YG8b3{PDJRmwwvL2K3EZWii$<=G$@;SXqMEK6&6ZJNuh0zWkiMGlH`<^E8jVy zz9lR;6OGJCPvrkp{(pBV9fIs_BcFjY;=C#knW7sgt&bRO7O*UJv#i|Pv(usvA(Kt2 zPz8F5NmWi!lg~UIyo(MzB|-?Rv?rY~*KU|zn9-ZNKh!%fZGS~8&MIw+?!faAX9+T3 z$9iBU+dQX=g`_?OWEn&x?4h#$xdC#LwIh2n;7adhvM<=jC{;Yu)r{wyKEuk&IArz7Z;2t0 znpJjPT^&nuxo#Z}o_;Tf?T2$>=_Yyu8jFB+4rCP9CA$bAkNf#+Z$3@}%@8N!~}-5Kzp4vdFKb7`QK1SMnO5j zgQl5kQ$)F&Z(3XZQ3aM)4BAaOG!>bV)a(S*|pZsu|y*fEF;0x|J>u3rqOVggUOi!ZfBwL;x2qOxPN9YsRH$n-k%LJ>xm1h~5#V?dbNPp&woaS%oa^C*oQ_)2aMZ#OQ#3*F)5KzyM%<=XFP1 zeL_ta%jH{}{ZZpY#Y_7{j28G~2W|R9>MqM2LCdIbHx-4}g9$6GCvu`!^_cZ(Nb%~I z+@_S4ykL{|p@0?uZm@N!FE1`*)_-VZH$s<9O>>+xnTGaIcz;ZrWo8qc%pS>${Jjm|tG3`87ieMR z;w}DMmWK*woh!@#fd7P3z)??+BKGw*Y2{CwrU#~clE;g#T{Tjwk5gL~#4j!S<{#%8 z`$@i#CMaiEFPZA9-@$-xtvMpn2TPE~YQcQIOkn%DgIOszReVI3dCts~e__^v%pts^ z(YlkOPT5;{({a%KOU z1t0^XsqUSUlNRYTw$s$O@(@uCG?^lXAiKRNkSeHbCQHWi;IiuoB$%))b64v8-``sE z`%0LY6B3Ljc(W7H!;6wO@k041n8Si0lu ztIa3FLiRmrNT!t>TB7Z@4M-NhB-KN&&g!{D9oQ{+ zpI+@Vp9wfoR5e32L5#P|k9QvY(Q_W~wR^^}Y4M6Sz7Gbz-<0UEWk7rj+-ePF2uZEG z)g+pOlN{iVuHa6(E6kvX#rnBe9U$4z>OE4D+K1K(_MLYAf>*5$N%6p4uTTD)2%VCI z=-V!KHwWu?RKQqXUXo35!me?Y}{ zvm}V=C6!YOHvt{`n+qu+epuIu*F`d8WyO2U2C=2Mj@7znD($>Es5>Ljkh$UQuiMYp zOj4!N6-m8By!IYCwpC*%4BJ+w_x!oP9EFe=G6kyhOn(7rkZc2*L-Zde7Hg_Emj)#q zJaunb4Irru*-RF5(Y!UTHQ!n60l;3 zk+yas-%g-$3W!p$cRzf&J6p3knc$@C+WwBFu0xyg9pOk5chH$YAsz0(-w?=_AF^re zp`TTl5)t`zW+dVHCyEm=k5u|cRQhL_kv1Rf3y0TGP^wU1Vuh+QQ}3NL)#FAwOhyV| z7v>)W@N}~{+0v9QdW$`;rr}?&b4BrcseHlxEN5yuWRc@kuS!-L`>UXMG(;!bzSdfY zRaI~8+ukCN=~pj>;(O zD$RgO-?|4$))supU6kd1p}m%R0GR%#e863YgmS%AZLX2Qsj-1n9@VNk2gOKgiOm99qP$GOuoGU^dE@5U>c$J1?c$~?n9i(A{*15)<01)Tanh31 zeidJ;{?#9%&Nw+gYbchte)kaoA!U5wKKv47)UeL4Es_Jf24v#bl`9iSc z{+F94&kCf&14KoSM_Ndu*CI6MoOmfQYlayf%^+fo8C z`W>09Mt2JK|3v?>n!eqR%&F7C&M<7BS!3D|jn>W%@7HI5ru#*rr&Af%OvcEDEomS| zazg5Cc|67Tg(e(g9}YxU!gQ@hPl|NNMM@ zMWZ|itnBEtk{Q;@cjpw2#w|_!>&pH}dlHw^3}n*!dVD-Qhac1F9M^?*rlV__++nZ7 z9n6>e_QrB1Cb_IFYzR}l_KT)HuXhdC;IUmosXu4+EGDLd3Vv`jh+1QPQsYgTjYj$$ zGp)|ts(r)Oo=$@|lm;j!pU@jg#Y$Ukj2gZ~{92maeh9JA7{ivBLz+rj*}#xFKfsau`Vy{rsX;oti02xJDUUg)j9MLuT!<}lDpKdSPxmriR})kO zY^|z@uN@pN!F%!Fi+_kf`p#atX=B*mjiTcXUJ3WCc?)46XW#ew&`?Xd)dB`3Kh6IW z2X<7U8r4Z{SJlKt@+rZD_|{uhCQvXe_x;hw z&@CC>4%WZup%C7+JPfjY!VD*XMNx3hmc0Q+T{?R7ik-GC3M&p0MAK${UvFjFebxD~I0u!?H67mQ0 zE4p>YO9Lm)&Fw4gRp9?qOm};O%A5>&nN9X`3udZT#_ZN2Fkl)>!rtoO?xTdE*2``V zVxf1m&Tl`}e%2AKMaIj6S^watRv$}y*Qo(x{Q7TUOMeJS&=a$Bo6sZX=vgG+*MswuyGZCo}DKjea zADee4K^k|KC>)O+(B3SSkZh34!Vx$Q5oD;^6F!V1fP zkYg0f5$E`d{y}qgU-c#<@7!Ir{IgC=)#eAon(J|^aV?a2K@~hbo|=#H6zkvIRBeaK z193O`X9g^LAQhL#yM3Sha~4xS6K~6 zFpSJkrLjXQx=~@?OZf)_+uGprN5j(Avtd&2C9+hGLX33*r zu&R}R(GCr8<|~pomZ_=|?)vg4L-HQ}#~G#xezNLhW$FDJg;Q#07AruE}& z66HN0D*I)r>3=US7$Hn?JiNyqQ))U|?<`$T;~b+uh!Bcrs#k?}1y`Ot$+1s)Ha`0C zdp}mGZt@Sp6Qj0L;_E~0y!O#g*Nr5=?GaDw?YlU!OlYIoYVV%FV?VpOrHCbJ;Zs{l zj&C^=7Q?qBajg$jCsz8v3ZobanC|uF5csPi>AlNqCOxfckv=ZjT%!%9+8$>`b_xxv z%{vr(kTP&!n%jC2#H9$5Cq${T)K#kUaY4ky};axlA8loPbws~JXUc268 zgq5}}q?Is{m9kf1THL^N8J?mHbyy)*>|5Sm6k-o&{JloImE5rl@u-}&52Xx#icXAD ze@d+MyN3|}r)tuL6LW&K6L0rjXC>zC){?75rc z8fj&;YRVv?Xus|>1}qU!Tx5ImO6}P8#!6DHY>pZ9#{GjIO33nzXe3H5@o!N4?M}L` z9H(H>j5tVvDigNb5f&P>SAy*At9y5Sbjn#MEOP0|Rw7cu^yqP8>)Jl#KylWvN(NZ< zR?&DK23FoXpHiK7O89R|9Wty04PW(Mj}+;%X_jrl(yuTiQZ4QOBz6Wwt+0;}w0V{0 zpl1z}vEYCqt8Fk(7)hzMKmY2J{%X4C`9o)8^=uH_^9ORWVInbdPEWaK?y@#4m(}-hNX_#D|twlk_H}K)kR!3|me$kKia<o=*l)FSI$vk-QPhkYUT?3of%tw;67pGnIJ;TWnCKzcO|;Oc4(QXYud9=e*AJC+w_itW?$zVZbt`m}-5Jqk|X#eMt`3P$j$NZ!#2`wzjypAQ4eFb!bdBh|zMUVUZIM(g5v0 z)~}MQ(!bj}tEKC3n>?+L9n`xm%r83AS@z!aHwxVG*C90aI^oh6z3TyuwC_8f8(Bd{ z`cYa$MZPijv|#w5m_u+!97_2ASxc<0uRAOa?E4z&Y2fPg$s=iMJV7QxW`7`kukI1ZZ1l}pS@oDk4w_0~aW ze81i9Q+Qx`FU+b$g&)Pp0jfwdYvR$c-fugY9%Vsjiolpc>&MQ@6yf!JM|LZG-c?>% zya9@Q*oh-6o?d$%@i5tH#O;Ye-T5V5EqU(hE3a9=M1Xy&p!H5G;!hQJz^9KtuEv6H zwxK3x>X|U*KC5>h~q0AgWy_3(mf3?#0yT){e&sV3(vUH<_3}YVx?C)uEf;`aG9EgUr z*kiW`t1JjfXO>HjRY?AUxU6zJ(@J1wq0679eOXGC8$FoAq_d@T7*!iR9R5-WaR!>OxgB%G2Wm&PwR)ZfVRRaxKly)*sHgu!?T)QrQb1pyB$;~qoeDO$kp-Pee+7gyQsBJ0XY4Guviaj_@^*ym#sYnrW4Eb<_*6R4gu^K zn&vca88@P9htliaW!_i;XArZh(ON{&8E@L!u2!a5!!U11s9`2Iil@$&v(Po4>xJE_ zyR5|=u4n+a_mXbQ?hJvP_9vAlJF`2}?Ga+18iOXKZ>6ydF!x-beoBpQFw7+Y3zKVc z$kLq+9|z7Cc#R-&T2V{WqdHE~kM3M`iMs-LcGj3&=!nUj|FsYPFQ+F}u5GLn7)4=( zCl^o^nOnHC=+O&zir-;_LBnkZNjPjA`_nKW(?ml#A<@uRYmoA90ImPwgjKY4KRBS+ z(^4+qb5DEJ@+!h6n|N-%x^yYoR}?WBkNmWy68S9^!wrWo4A&=t<;`hVEPs%tYE8BNS+bPq3P)22|*lqBO)^|LH&U`zT)c`L8?w#RW z472oAL$S!2EJ0Ed0r;-%wg!fvZcpELVpHXt&ins4IvD&YnrXu7lAPmrh}s zmEH@@COVRMksx3$sGscF#oY58GE8iZI-uo;`YhJT7faBYYKcGbcS(usDn8sg(h3;g%Gt+&gJE4kzuWj@$u() zGdX}=ROCTwWPRGsIQn3Pb6mx3cDyKmD68S{<&zd=2 z4CMN)7D}O!P*goMuXmE}>Cgh7-qq}Yov$gMk3Wf=(XCsG2KN{H@lE)OYk~4XLzedf zXx4*CPBENQ=bMb{S1uGzex+E3#uPdm-wXEoO3`?U<{AwOtLOm!Lynz<(#i!`C8P^6 z(W;L9k>R}NPWE^iC;Cz`2M4@uU(TGA>T3m0HiZko@VdG_wePbTtxsAl>s>%Wl8mPK zj$I^?+mH6p!R5BQ+pEC#DlD-FQ=J&eR*x!c`(ri;Oy^RbC4>^))40 zEWXYf_+A!lOmiSeGBgh$e1B6Dcj^qlI`=QcvcNI8w&?=Uu94|zV-mni-|=_kl3$Bh zdXR1U?fhr-)t3Z+YN=E9)HEfze5vd!y(KK-0LYFu9#+a!`Q9^1{^EE2b98mZtx{hn ze5%v$b78}py4GeKSessbBL271$}^k5&u8FY?%b(gHx0Nos{FB* z-POc5Zx@zybQmjQKz#VQQ{iq?qPjf)JR94~az0V4z_Lt#n{q6lU9KmJ`gfWkA+N^8 zf&HOu@n0tu39zUh~Z#f#w_3-8ZYo%}X0!I%eMYh?hE22b7fg_~Pd zr$R4hN%odItq9}AxI9vOvl`^KHCYICDMOv!JuPvir-p1^yIj5{in}f1p zsFdTsvedZ15FtliC*lWf9^p*B#e6zV=TWZITwfpbrH%OzKMIQ9)m;W&8QzA+PKQvX z_)$R5tI3$o3`&=*ktT-+%Xva8$@9^D%g$=h-74~Ib2DG_`PUm==lz9M9I^@b;KFx;hiCL>=ttI`q5@UNZ*X zJ@R13%o?4tAPs^NL1StFo~92!G7`$T)Js>I=Nin`-?qipz5TGxeJUxm5(Mmhzm=!Q)}whrJ&qP3VmVl02);gT%>{9IK(A`k{|v200=i}|1N zJDbT*xhEYH$&Vp0hxv_njlP{d=jz3=cRDBdMwJ&@mb1DIbN@c*OL4@}yU%A$OEcd@ zK5+o?<;>|9`NUoJ~o}-AqS{GaPMQV94qmaeAB%%WENqq?DPNWxV{wW(DqB7(qAM>ln~ zjL}I?i|jT_NWx?3AbN0LKBeb*x;E|Od62opS@l1lS4`}sd5#m8>W06N{_KUW?w$@# zD3HBuuxyxT@20@eFqq1{P!-k0Yh0<$M*bm)G*j19KAXy9<5&Eu%FLU##>~ z5sxI2?amH-_;so!a~gqV)g%1O)9#iRx98d5LvlH3X8%?BuuV!bSeTiMo|1^Ob3;~K zvAJwifVxkvxi3BIquJPrh4=0cDv`U>PsW>kVUEsf=3s-dR^GQ5w6X;VxakwkT=N2a zSnSoe{ibq}EmRonSBn5$c<*Ve`vS2bTT799ja%<3F{C7FQLZx$345zk~rm zuJ2mX8XlBnJ+Zl3Oqs_qNoPc2O|DddZ*9piXWvu(@3dEfpf;Q(2dIWcJ1|nNUHx^0 zK9^;^9r!D^BRoIXH0GuPt~-0^k=Ae2^kj$7e)pD^RgG>G|G$fILj1c;=jZI)9=4c! z=ZJ@-EbAg6nDdo2u{o*e6t8I*57VB%K65Y=JZFwd%y~dQ2C<_5T)Sv&=mprlL%D|m zd^L^~x)wPseo0LtH*g^2@TZp`hIEd4sVrFP{|4;{1bA6n|t4jo0k zkII6-NBVFron6^y)?U3JftBmq+{z(Rr|35-oNDKEp&f(#jT{Y`0+#&^iQgmZG2qui z0Tb#kU@iW@rxwsMzd(hPs-8bTQAdk81bRo0eP^|cKDFVESk5GpAete}sj+v!hKt;M zo;4uc^H0b)&}}xwFqQY8^;5fYDqGWQKs161{sXGpnX&wb$2<G8J_luMV_*%;)m-NDI=*=%@qa1~Zub{dp&OE^1x9|rxl>gHL zd?}7GTu*;a;mqwqnDe$6M$GZ^K*SQ|jR?;rAJ}|LKdN&3d_4~@QFCoc?NokmHnl0imxCEl7v*p%Dpg_8VH|;|F?Hrze1&&K~{9C9GWL z7uVi;X{jnr2x_2qO=Z*UXN#^s#$=iTbBG_tCqplHN%mTgH$}@&Qmj_%iPeCYH7zI> zDI>DuT0OaOYRdT9d%v~Tw#EI(VN9Z~{2ScV-X(pE43&&PZ7LJjVZP_ zJs>{_C7AckYAj{vvL8#A+w%uuNEI#HTb?k{PciiE!bVm0onK*wi>8H-y{%P2kDMB? zb64X&yP4ZF=p~&ph1H$&BqepQqMDN^ioMXXl8*?-i#_?n7hgGMW_eSV(v~1%FOf#9 zY_I#>GJL zaw6+DODS&Id)34XY>wL-+qhLf_E)D`onXcye}1@Fy1HyAXkKq;5gU~u#1zzb>R>D(##;^_66P2G_BjqwUQVmR*pJT$ojlMw>8Ed>rb{nCbI%Q$t$s_9Dhn890f`T4qA0JjxQXy?Knbzp($|DxGWG&AN z7Tuux`@L+F&5RqOJ@MVAC*P($v}Z$@gp}2_GsM_J;tBtom7NJBR84!YP{$2jc=VqI z!~vr!3l>$|#qyu&tMKa)hu9rv2#e|D-#70_`4t;Tu@g>sCe;A1+ryjgsZ zeVWgINq+Y`9YM=wA9XlZWccBE0{0*t5nFzJA}_nw%+q=oa}x6&bQvG&+am@JYkbhe zJFeavyvSTMweR2g{j!-!yB-4(Mp$7Zz@~w)VV-`sH3q);n)@K2Bub2W*+J zqFNer8EOd%3Zg6QNfgL7Fb8MDX||dltdix6n&@u&Fp!NA4}C_l9cl$QPzO)^F<-Yr z?{vGxuBBfjhZs!~7p7*-DY&U=EvKX#NqL&>-2`#kKz!rKhA_E5c1XwASm_{~F;?Yc zi7{KiJ@tp}!K)cMRCiAZ3#eqSAlux5h{_xnD!)`|kt!#;tSY8!b5N(gW-l^|^>TE@ zetmdKGm}hQ`W)6^g6jYo%|{k=4O%vm3>VMcxk9gh z0Zf6m479X$fc6=U6X-eClks+maa&6FW7(JOEbGQeT&>?fsibWmE_bZsnF6ucgEirf zKYtm;P5hWF0WzmczDskW%s9B3)bSK8^8bbHH2rtL7N|C#qoR!wjOi?TyV??AAG!W; zjhw-gam9)z=z6;=jv>KkbO5pb;0RCo+sxL)4#RN;Bx_Q2H|QPMP@FXy|9q<1{fYxe zEPSbbXM6ivTW8~?ng{=wZTokd$ND-;fGNaxpN@MZuh2jX@7D*irr+NyCpC9&CwV6# zE7~}j9S^uwe_P;@Prn=be=|1JlY7>2ZJDVJDbHmZsAJfaJX9Hj99|GBWNW@a*KN5q zaIX8$>xGD)8WvbIx=d)owkqqz)I@K!ttoUCS(fD4gzRWta3zTI#Z8}6pqnplBPR+x z>K7TD)y)$!3p%Ra*|F!ZItUnN1nn!X`;$!=iF~_?{l1w`n}$CpennuCmlf0i`wx85 zfYsM4BKH)Pw})Jg#+fBTycC^|@>q%`jC<|Vp$XY`3O%axEjIK@rjNsTnI%&`EQPBI zdc#y*f2p(3xgi4U9O2pvo&}}cS2@k+Wkm#AjQL(-!V2P!6(6+?BR!XY-oYUhqtW?p z&O7)XJ#{DLrs95Z{CLNttCpoz9X!S0&yb+0ym&oA_0%Koa8NDI^wCEis{`?8#-D#K zcLL>r3z8?iBczaUE%7knzLT)MN4{SvmXPk@D^6o$$^ht{@90ExFUPHvq5q-XtAMQW z8VE6;b+jlA2|Kl4qtyE<5WiZ}vo&r-L_-5X4;`<@m$5Wet8t+PoJoJqT^Pg!;t&l# z$eD>n;B&GJnvQ17EW{?56 z`ug%5!II8rMaHgISH`NfuUTboGZ_^*CH4Bud}=Qwd{gJ3Uq>;h`hP{ZQ&q{b6%`c~ z5HFM)F=duiLMQzmB8G}7Ea;#XyIeX8e{S}n1viH_p0m34adpzM7p8NK0BX@cwgjCy zAmT?i0#ROApQ@GUpl1%rqO=3jq6mI3G46=#el7c6Fx@(Jnzt-3OXXZ$#p*y0Ur5Te z{!2X766MBnN9j8eM=9T=OcmE`iE~X!mP4GhB(nuX9SgWLs@+%`Dnjvu-A_?r`=JsO zE<;maHUM*1Pb>`7SoZjxADpYiS7V5crY33r5Lv)wcRKA3P022;_aWB)yn{5w_gf}^ zXz<>v6B3*fXrfxf#!xiK9H;IhD?JX6%dUj>70Q-eJ_HY??Bw?iS!djlRj7DNsLnU_pex#agl}f#0|?N z_e)=N`N!OhtdW%ZrNrHH{&HhkXvzX}kqsfDkQN?vxV<4iSq@SGQvh|s$7M*=Rzkk6 zIpE9WGi{q85jFCe^ru@uKDppXFb(R^%H#aMSgRKscSrwVHT7X5h{M0>5B|3n zg0Fi)&$h>2@)%^1H=6{*^yE2?4>}wObYTFO}fLwjN9fMzLGqJ1;F9b9xRI{3Pj7VPg?Jgj|KB ze%7_k;l8utw4kCRPO@z47q4C&Y_M*Lk;4`e=fb*?L+ z1tg0&Y)#K-BYUY&m-z>7T(gfo_IaCvriEs`JU~8;33A6`RcTV%UGbU@8`fy}Sm2~i z6_fg_Gwmsyk6xchLLAmO4yH6i7+JhQa{W}~NNO?GCNsX{Bo+9YVc&ZJVI;x<@tNf7@vKNY(z{md#R>`=9L$rWI<3Wcf0~)9tuoA^*Ejs47u%^{8m&3}UEZ zcox)vJJS|UCi3X04!QjwyWw%Rm3-@bdm8y^!JoPlWxO&Ug-KBjcl$)x^rXoJ7Z!_8 z$u_YcCcD;h(ioV-_uZt_`TRWaxr}WJ9{HEFoKrUYh-wMHJf@$5BJ=ql3yc4z^HQNR=E5) z>bZHgJXR+rA%oYFQ;O=sxXTyy6E6<_;l+Onf=K(xyLQfR>(iqeJJ^LMfh_n^*aq-8 zI(K@3at3N$`K!E^h4?wSx+-=FkxYc|ud4+-C!@~Ce>ihhv{$mKoMwIb)OZ=Z&Jq<$ z;zle{u~o@e(x2`W^4F^sSz1vQv89;pT}h?;`O!1g4^do}UX zbI#{Q7mH*zG1_Q(zV8Pwp{yQSAq*BS0m3s|&ZqL+^w#yQGt-D~*EMfGAj2)!<>>KlBSJ>9jgahM)F;5I{#*_Cmj>qgYx;huLEz%x>^cZY%l8ngMi_F zY+$|v*L2}C#KncXl6s@!v?~ZMm94$*ZUhajBOFGPs{lqsd^&h8omMTlUy< z%{}{RXx=xCp_HEQ{B17PrOfRXpb+H`m-kam6B1G{L|@CqWTOgII#eCY=cKoOusU#q zLtPf}kq-KDycDH0@`Hsm$e`!Rm!0|kcIdZeihBNEQ;<5R8}l~N#Pp3&D0lE1oAF>YVQSQY$2+p&pU<(r8Rf|* z8tp+`aFk6lE|VNVffkb_NFuADLPRZ}7S!Dw9T+fWa8OoJ_&CRgKuwpCnJx2c$kS}I zc~9P2^J1N5^f=?_dKbO@4*-*j!G?84JoUaIRz)0{Pb@d`TWR~EvNIc#g%7Nb)>g#R ziKQBO<+LEb%)SVNjyO@4%u9wO17o*AP{(J-JXNFQt2Lf{e-A6ySv9YB(7B8*^ zIxn>D@@y`RqE&#i@d8^zf`Nd@r4a7%Z8V(ag%6nf^-d*95RjgML7nCs2kC z~G+DYdGJP>j3gil;f1 z)Z6^iahc!N(SMJA#!ydQc#r@D0iAZ4A(7A@*eenAvAx0sERD^R%FmkdKJQ5>^x zW@;9ipWckHPH4^Uth=oDFw0E{J`zg!Opkzqc;y=L2Yv3f%!CN|qB+>3aVzRS1>k+V zar^SUT9XG2TOdg^u6*_%uWXN@^3a#(%{D6Ifk1_A>q7nojf-=Q@m?HiYmawvzPXKf z*zaKS_{trc#>V6KRaYoL3O{?AKY2wrQ@hQKug7JC(Loru$%!tioF&sscg`@EG*1^l zX?BQAN9B+txjb3^EDG&*zBM~>&-SI3r`-jJna$gV=D4XNz4Ypc=D4npHy{&xp5GMd zsTy6f#(pN-V!SwKkY$S)9!r_;!r|XEv^S_GJa<9Xk)rB2t%V*I8A*r{Z-Ok z&MUn`B{bI(_3tH{%gQzEEJdsx6=mIhx^=Q>wjz|}NW$LYA+Y=Usl*SO|78t0l@jfs zogolUnyYzlAG9I3O>Nc^Pr2i41n}^#NZ!AeV}T5pv+YRzuexT0DrMp$pK_#&DLiTB z$k>agcL$?K>{D>2a|_8YOlJ!f$Hkz+SrIXE8sE!dqt4!`zvw0*pQ`t37v2_8>la}B zR(dtgR2rhZYaw=9APRjU`r$s;Ua==~%dz&}aVG}9|HUM$^fT<(1sKac9Zh)5N3SSQ z|L+6Sl?x09{@aJoa2;$h>+(!yv|agWzJ~BEN3@A~!_^}wg9E~?%=GQ=e}A9^+A?cB zk4UVzYk>Je8HySLMrU!%pE_{3(4l)+E4L)xnC_W8&(&+O78r_-Yr)_-J%AYL)KSU5 z;cLgPq$Vp%myStVRx;A_5DaSp510yyn<96&dLH%3MK9O7aWCN1D`vUENR0&~IkCgP zY=9aS_cRR1)sl;-b>x6G*j&$G>w(7uAS-8(S zXegL6w!_RMcDF+xeW9_Ujoa7#+B$QD*jQjX!!GIQc;3f1_bJuFbes+je4(%E89i@I zGsU2VzwKxAPj;E29&F2pMK*`iFPYCcdUoF;#5^sl3GJnno9L*7-ieG5!YWUXX(BU3tw|;{|>F=9%&U$CKfy z9!ioe#1!JxUP_S7#g*M|^| zz`#tsc@aP+JrQwUVsV~b@fNrFC=_d~pEB~-$k|v&)Ah@%b6BI zWI_LVNjLdr(mMf21GnG)PfAr6E9D{ZE@$T&OKan|S7e{20&HpSL z8T;B}fjpKBnhid8*1*x!Flp)^7&NiJ#hTBfqGGEM&;2;>Y^RvWN;+dsI8amFcDvhF zSnWls=acxhSDVj4g^$JbSTE7_avs>ecU3jchq_jTFX3ZVlET}I@gCMJAUGn4wKbg^ z=o-jYL^H^FNR`5{86;3qeKp(0UtKT-ByWP1CapWyC029XUYLb!$F8^p1e0{-sh^c> z+@c5QM*C!R9TbWmFFY_v^*k>&RkniOMS1z2unp3hl9F&U1l{PA|4+158Xq_RCVp+NL=)s;uHDxE>-H)55{ zgf+@=UcoT*IH#m#`Z`CHiz zJDZ<1)2XeI0M@rH>V#h~Ruq}MT5+>J?AB6fzg&VZa~d-cI;YZcxQ-t?P&B5TBI|4* zq?C5nIIrdbT%vc~STX@M(#0Mpi!;S4nJ?jKm(jTPpK{u9=v8rlbo$_z-ii?@At51g zlVT3bFV^7c+S010rYRN){)!}ZEf+0s;?S_FSmZePiO`}dsyRRNSLcjLf_NVwqtfxg zW^&mjoM0k)t)VB3JkX5fb?+7w#$?R=ZPZi)Pe1R?Idm$9G)t9Rbp(=LTY#xtGN0rv ze)_nn!hxJYrexkPlN=+1mwbyZU;QAHyJM_ODWB9E?hmNe_GrI)lzHvBoAjM2HX% z50A#L`%0@V&J_1_EFXQ`qDQ~V*DMKZeQh%;u+lfSdIY-$S0jQG+h>E<75jRcs4k2K zVPS>mj=2dLWnMZEFdbdFJU13al?Cm>+si!uYFkYPJQZPTJBtC0X}L(z6dK-0f8_s= z)5F5(sgZ&P)e|{a*TA8jg+)eQgt;TfCnZ&*q$Z<=D*@4`QSzt1C)DE!3i>!RQ79^k zU#Hu&8q&#dZ^gK0Nz2{+RW>*3b&I=Dpht1^%^EZG;icX186m|U7AY>0Gp4!1w9(`p zMdG)|IrHd%d0!2;1)Ms9r-5hC(e-Vaq#x?$RGr1}QO5;rgs^=6DxYpTW%Org{N3q_ z)PD5}>{EyN&2L_K!p8 z-=@w?zociFOBvpPT9M@q=lJa|d2!n0ttr+x(1u+HNEOqM;_rF(4H_>o_6Kghwg3_< z%9*{QF_pd=o0v#i2}X~Wvl<7ok9?2y*2Kvy{WqG|{5s|)Oc;?u;rt=0Nhdn>5CN;1 z>{)x?yHWCy@#c@p>v83XJ}$*YbPqrSt8ii+clRsbt>K9!Iq{HYUN_si?#<{$%>msV4Sl3H)Q&c%)1zCe}?) zXqUw@hA~6YZ4s}xQbtcUNnR4gzOQ1s$>j>k2h8Wv{1$gSiJ6ayY&Js9nNB`d%gJZ* zxtm-3=)1oW&}g~>zgpLl!Bnv`Z??tFOPa^zsXf3GN0c#12qnU4D0MdG@=%K0L(m#? zjR(seUemF`cBm{f=3K%1hRR7Z5GrKs`iYXyrIc*`aAKXFJ)KQZIJ}zePk|E%`!$~s z+Aqa{BWf@sfz?@?N&a|2Zb%vR7)O#ZL>fJm^^}qc0^@F z?zW)z&mE7fAx|&k2q9I(Hr<9&&*mMSIW9^WQl3sNc|mLlyz(=AgOnJ^D1W4~Nw}{y zI~tKNEhcIHAz?C3!2hZ202E4w1xFhpDVR}{IqrjJQq}(BRa|jr@DuUbdv$e@Ol|W2 zUl{{kTl*ycM74x472co$D`mJzawGd=z8*3JTh4`~IeWro4#}~TiOfo5dI4wcDUzlQ zDPS@RDw9|FQX6WGXQb}!@Gqr_fnrz1NC>XM^AAE}!oE|Kk#K2=QlOyeV^CAzj z-8lbSyG6K2L>ddM)RPRyB-_E#R79MXd4EYKf;rgX?#$3a{w+Tq9OJOzP75e{}z3ro%1}aC_WVN)BA%? z$VBRf+f)OhD;e*LC^CP+dGdsiu$`+Q&ABmFxRDMH_Y1G1L}7FtpUHTluxl(Z#CY1+7{M(u!O((J5nG#htP8-ZxLQx_JXv`*;fn3PGjJK$9lKn zf8DV&S!Cq?v@nS<#pN$SkH5?1^dZZm@v6O?-d>W<0CY$>m3n-0-a=#z!F&mPd2ltT zw?swIZWxMUp`qn$VjC?%R6D;2gC4nCACTo4f3 zfO)lPKDN2}q}%3^m$Ax$hh#FTmO=MO>D(GyR{5_O)N4q)=trOI8UTqDFX20#} zYP^te*LpH@QrO}5H)R5ONRoqGeEF;jp9vdAwTh z)7{lwbvEe5i=)eXntW~Yd^Srz?~L$cD_x(!FP!aS$;W@~pP zBy23QA*{^So3_#=i=YTe4X=XF*tQ}be^U+lg1?};)y=X(rXXGPqySZPJ-dm}Rhb{S zc2pyf3V03|U|vSLT4NUN%FXjezai3d(bdFijb(lzB8&uRh7$S7NeeP28TW0dPqFw~ zsQN$o2)Z=D6m#kT$|@|}kVNHsmE|OUQ1*7wNV?kdnf-O|TM*~o@5$!*&F)7Vvkhm; zoSMo6tKLyg?*I!v@WRQOXzT+fjEq*5?g0A2ZF6U*0N;MH`rn+b8EzJ?GYGIQ*z5U|EU4w0$|A1S@;z~i;r>=caMWKSiWnrAQ zZvs!4mFfK4#9WlNwIWRg_t6J0`wEZAla3UIJTn1X+3BFo&Vl&V z&K+f+oTbW$Sf=FAP+u6BeeegmY$LX; zy`n?Se*OA&jtFI_;0;8b55#R1v1Pg)eb-z40y6r~%?KE#+ zTu7>RyU(-tu{oNgX_+lCBB{%sRh~jYoQ4c^m-}Xh7fIat1+;HT1%3A3o;(>J`P{iE zNJ5fld$A>kwQxXMo{jM>^jm=q!#c5Lvz8wZ6hH9`jhJG|eBwP9P z5Y`o`v1A}trJUEHnDA$QOx(jR5G zUj7IrOU&P6ofl%}rSvN&l6rI|XY zioVLBF2Kxwlrj}xfTVw`n)E{o(*^Gp)%UrPefolT$gLcKfsRyOJ3W5qi7(8U?Y}=- zz@a=+{9B?DtA|f#sYW_C@|XMS5!Lhdt+?yGU)J$eo%h&@zJN>Gou~IUkKfUai*rKu zT*)Fz$;yu4dIsyvs&~Et9J(%a$|F zjgpK^-b%w~00im$+B~D2Sor49;^VVz2lyXo0@&FFNwEl#@)4bh!R1LV$J&FW0Kosq zrU9GTJAwvDiVx}UjSzb0a>#9OpXs^qG5*28F*ZSW*bei@>i9>ryPwP0o+rV9%~sKl z#J#lnR(MwdB)l5ZMpbkr14hJ!Fk>h)lZp5NxY3sz%?H>KjgSu`q~fBHdSQ%HX+{QK9pS?PQ{RO>;22 zGX#y31>=50x%h9Qq&~a(4ogfzGY0jj$sUqixRo&hY)4vG|1O2cMOAT&u%O7hXG4hG zXnA@mX6-JzGm`7qB^vbHf+!j1dX~)@W-GeD$ih?5%*lz39w|NviE@E-oFys#h_9~5 z_f*G7vCUk+2fVQw+r{K2P5{RTG!-a&mP?UaO!CIjLwk6b(1pxtr{X%PpLK}k^r!96 zL1tC*M`v1uxc%0HR@@_Q-SuEgd!gIa1=d+ythB{!c?RSBuyFWd6X4SQ!B1;=lJ}Cs zU}l(}l9Ix>uV)LP0YeF)2~>?SBVb=PD$J^P7DPuRjVUNEAH|L!mq!gW{TT=WC~hom zg%*3nEkP;(D`Iis8K7RGWPu+dv!zp+TT}IoMRu0FWlfMDUM5?KCd$WXNs~lwyu(d~ z6jYdwQo|(Ik~?`TflQfF*XMbtYnq>Vo%|6|ki}vEp9#b7Ns;{3$AqT5s;Ve45q-78 zgXMNuBQZe_>?UOkV~hcNdbU_a07Xy9@5s(l3T1n?aecMZ$9T+_h@|Z&H4e4n4U0;w zK=a)R(k-OXBOdLn0N!th={GX+ue;+pKoH;~pZr)}0;CB#Hpbk+0?j7(h(zx&(5I6` zFetc8#uIokkJVK#`nSU}+`bNigDbP4|9UUzXdE$OF#V-YFw_ndyXw|7GE$cQMo6ad z2&#p&%B-LhL0Ck%XPPep`qRaP*2juX`7I6pMj_-C`3FPvLM_lo(BB3|;fWwN6IOYx zcqUtRKs)PpxHH*rcIsf>IB=I`ZOJ4S5^Oj;?&*?d1MNJ;ZpS21$so)SSV@t8uQsW= z>hfTUaw*l8q*#(K?h3ngd72GKxOK|l_@A2<%Bcs!R6Gbp=buEox5UB+?QBp z8s~uMKvt&tY8h0{u;t@83a)I1)chVI$n3sS(@g{SLrYdD2Xr@j+8C9XaX#HX3g6;i0^3ra3JUXNTP--ud-H)YZ6<2vZImDc ze651w14V9=S=1N}$Bq-jPpmI&yDc)s18Km_vxlI$`+e&1?OfN%p{U#i34Z!&^;qHNwT z9DL;w!(mP*QSuwzJ z-oWXQO-sOMqO!|}4+gOU=EMdj>HM^FZrcQr{tWMnnT*f=REr7QfJvbd=jw)^ySYgIXaEK&~JPo3*l!L$?6W>{?3X86C|4 z&<7roQW}#6fhb~W&ocv@f=}=iTbhTpkr$dqBM3)uw6z-ieVa`p(fL@M3Ho2j0%Z_JR6F^oBJ`}7PJrn30*&K-s*Cw z#7sKS)aCP73^e9+lgYUDhf+OytRLEITZfl%B+CykaKd1dfJ+ICODLC*l4#S=ZQxK9 zjBnZ=AaM4=7-s& zd?ICcVL4lN0d8lNTwY{Cc}^ftPUA7{rny;D%Azvr-#fc>wxdIayYgI)3@sGz*AM7V z9Q^cXuV;&HzyFjn+L`)N^$Ma-8hJZ-cpq<)De53+(2l1!w{^z1_S_j0Z3qMqbhSN+ zsE_Ks7fG#rT-cwUzCRn$xtqTCI}YilV+2#bJ&v51KQH|nZ+^C3&g4PY%-cnBBy;4A zjwKP!ae_aN2uIZuYyOhJIMxsy6$N>``PHrvfZe=M!uy`lW(Otusm|3QA*4o;@h5Os z(J7tz^U#2%exfW}m96_&<5xOgeZzxueC>HQuC^`JNx@g1fV}N)qhhaA9kHI#`c^4( z$z-9*e=kMHesN2vF=g}5S-ky6!PmmcySO#D>Fw_9uP9Wxf8%FO0yVyHiYI=n>3$wf zj-{6;a91_wj4Ha-epzp;6iitSRF7n%ct~7KW<8McZwU ztk5fgN?i7tRQE1&ieKv-3h45{)-yNgN<9@UPRqbS_T(gr?$=J8@z$1S#;OJ*lMsy? ziR&rYm=KE?d6Mud+n6qE+%vQ{q8>*K`R|2;(n+0)TEXo9d9Dk2>sWeyxXJ|;YiXX3 zMx(aXV-@|Kg9XTZhpzfdV0Oa`q4i(dww_p$2jykyOa)bq1skT%env4 z-;|G)K_xd&w7>i+>vP`jY7FjJlo9wX)3FW~7c~aHD-owEvAkJ-L>^b30mlWjQy2At z=+xDQ+5K)tXj0J^`nVBMKhK!bK*%Oyw{BcC^g))*9e2h>Ttsbf0=JY7WK4p)I^uUJ zhCgC@run_rpcECHv3oWqOs>ZT;S{Tjdt0ks=UjC;UQ$V96qnODVS>!2!!9AOP-uMUv_8k+r$1wGbI74=brrYjJL4SBO)Gzg; zQFvyYXlId9fDSnGLqZmo1YM_P_IE##_!aTWe!;Tv77%p#ULqu~(kaoPrL-aA1sdpV z$auV((Fp44i@Dt|iekFpE%S%I-5&P3-bCDeyt-2y58szxES?q?vU)E!7~scg(p4o3 z*Q#Pb^x~OX1S&cPt)2UieUQ1e(0TBS%|}4cPX2d!uoLJQYx+vIpO=w9b=2t+jr~kB z6JX9NMW!wvPUQjGr(J| zcYzc9Gbm(ZB~dD+zKl_$*cdQnuKRc1>12)~GN_0c1N_r}gc>($ODp-4(h_U=jR&3-6 z*oDojyOgE~{?fD2MjR$PU@oq)rSeD6hSliuva>Evjy@X%J=4`gsPNtGjHi0X;b*cQ?6kGyMs8ztxprP8wmoxGEMWKHKuo%n=19 zh`A3Nhd!wm2LHB~`4NF%RX3b6&L564T{FLT9m2bJlIrfnmn|uwF%1zyPRurn4sTNE zy|-p+OgNtKkD6+jkWdui8CAgSgW&Z@)tKA&aLi)~;2m5x;U$HkP?Y~NjnRUd4}{a# z9HQrA?95b;y*J{n3td@9KNGyixK#sBw*O*g2B-&Qequ{nv4v95D3SZdUNN;;I6X)7 zQ>wgB^Y6_Vi;gK$gT#k;)J|6ul8FhdzcizIneMGX16FlhXO;Pme|8ZYK^$`((tq`z z*Smbwt{)lyYa}d&i|sevO(~WXE$({lSuLDo_K(PLg+Ceow!Pl?w!fSRB_~G&RN$JV z_en{nq!sJ8WiHxPo2zSHn7Inysa}M?Pb$QfEL{i(P2z54?Cx`zzjqrx3{KZ2pGy-Rp?@8+F= zcrA8gFvtgZm}2;RkK;z}=HU!h_dN>yX+ek2_ovon~*^@)dIRuvZmbeEJrbBJ&N+< zYsXp9qenaeYYNhFJWiz^ky5$wQj(~s1%EpwNGP~eeF|pBn*uh(rh1xkL4zPQj*kO3 z5=M6&{)VW}YmP5!-^Gier&YzXtV-8Ys}-#uMG!y2AW7zX*Q#xXdDjouAz{^k)C|jX zE@6}=pL0UD)z*I4u0

?fq}edU!EIp_&HKS)bMJt>#q9#ZdFX< z>ve)41u0njxW+&*{gHXTnNiv{!n*}Qf#m7Y0RVptf<8zI*kLM{qXej4@SAw@bv+IB za<(cvla@gzMM0$dcQa??SRINWZm-vw!Kt5T29+vTqeD$6De=itmd9OJGmPD==jZ1? zGC~~xIn2$)BOwW(9CX3xYvDi`@98e5Yd~vZs)b3nsua^>OcuEf3gfz5-+p^`_Bkrr zaD6dbOTUd*6)0Pcen?KhyZeJwz)8m1i=tkCX5qe6aj~J`7~mc;roI%Bviq*}v(YHd z-21IMh|}aB@y8~#t|6wp=Xv+1J=q<(5Tx{1H2=9uuf_TDgUazYBL398T`VSdbSo>X zqnn2{N0KJ!!lN@+z8>oQBr_f^HfNtCkXd18Ml6sflx5gy9~-%>>P@sBtAV>;xp98!qfh{%xWY(H0Ic<}~Yd0q$i zt`%6@#kFhnO=x0|qpj0|;n>u=XgDp5RX^AxEMnlBk4Gkos{&EDoWTsh|5;tF*Oav{ z5_m#MofyC@ZwC1CPGo$yNI%8?!7?C^GQ;)gSbP%&>)>e67dzB*OFPcX_30aP=3MNX zE0_h4W|6aQlN!fI3K?y62VvINs}B>Hu*hhz1acPZmH+Tdrc?d3v3l$}NtO@8mYsv! zX&_4)l5irFJrJKpdbQvYGNa6ZV;>*mY|Ysv9;!%vg5i>+PosY$=)$YkcnZ9>##tT) zDaw!fYvGYIHO9y>TJFiEcM-vFe-6042lwRJ@R_AjK8xC_!|rZHj&ptf+v=p z==X!o#>r|bMnm>AJRS!C3G7EkMmX(PIZa6RvC?1tKLY_xXfy8d_fIZ~YU=70#i;=w};oa=#v`$mTR^RxAorg1X+m0?zNFsz~uR*GGPEx-^vI4UWLu3}io4HtIT) zvr9k4`}}!Uy7T1!$8Sg3N!7;PP?zI`c4k?4+-quTy6d(&nN%^64oCt%0UO$cUFvaE zR-@Oy$3h{)%Gk$&+Rc8=BtI(b&qfj4@z<=SP>Z-IU99bttZbR+H?|xh!?{pRI~f!& zpP_F=1x4~{YW7t-hQF#+16BX%K{#<-8W#2Ja<239L|$~}(-?SK^2jN}@KZhSmY92= zbyae|$KnctI*V0@J32ep@9?nlz~~EEhNOHFK~o?5Qll4!FTGC9*oFL=3+F_0es*ZY?wWXqO~25O~Jx_woRMpFpwY zuX1v73TdT~5q>v`+Pk6IFA3I-bTp2>s-^(BPds(O|27_+{DP zsiooC2G48L@(LtbHvF5P-L@9(?^=I2U*?%zUoiNsM&xqK)da(x$_?+&A#MzHie)M0 zjt>t%4Z|^7>&ksIpegUy<;z*i{i=>4>g#8guKHTWLULr5bb9x>H^DjD zjv>~QjSi0v21De}%$PSqzE7-=)L+RrYZCyTaGkti?)nqJg~YCe>WiE>w=u_Cv@P4Q zUU$U*{JW(YX31S&)cqS!m4Xvt#uPLTA+lDeR~v6Ke?Pp{_qkq^EY4k*OU*f91sYje zQbWzg#Ke%2N%UFH8-3 zP7ufYX+}Bvs;a7@5<-ukW{)9?keS2YJV>M0JTgmqo2 zT5yzLl&qTjUCPETegsYW?jce9E-Soc?oC_>s}3AzxYgyShdsNyLwx?@t$0IhGlE1> zR+i0&Y1iWg-AX}TUf$=pD9tzQjWrfY=1bSuzPDEDzRLtGW{-y*0yM`g>nB&HAStWR zn(bLVdZ}Tg9Pd{~njFV$1x({$@+}`gy<6_XGn07vEt8MXWMOb%VCHD1018FAGIRXx z6x7ATuhCHh8XjFOUnNooJ9|i&uY#%|pbMJz*SQ|EC4;;gv}jbd-N(mYwwh0x0IG z*DBC4S3RR}A4loY+!v~=qkHf?1MO0^Bx@|MY%t6;*ib!v)1gUk> zs1EZtzW|NAdR#j;u29+S*G2e%v{Yh159p&PjY-7U2>tf@f+(ll1fSz=HGo?Iqhj}k zx_mFse2lwN)Wh(g8mhjO<#~9_=V)r`tZ>zv#5mOB%G#8#loArV{eQxv%!|1a)Cvhm z(!sFzN8nKS^Yqtqm!+znRQ(nq5`2(C01_hTzzfi8P7{edr8L1lB3+S%=kOb<7h{4u z*cb4mg8qMbk$ma^wE}zC9N4E8t>ub=wbRen*LR9%(=*ui+69X4;=q+J?c+@2;`6qt zArniG)|c*HngH8LBOxnBHvq{M&D*(@s#;F5E|R_PN6r0;|vU@SMuby%IS@dc3o#pFUR zH|;r!N+m32xLgw(QmAJQeaXb!hGqzrX8Bp%RN;*^z25D||IgE<%`pPt_GZC*>bRj> z+{+EX#qIREedgxmv^ylyzdQ9r_k4JeJ4v)8Yf$ayRt@%?1-f5dnC^RTws|XX#^BVz zWR-UFaH#&dhRwWPBRQ@h>C^hES~H(OAw3%i0cEw(?$+paYk7f8j4(eGOCX)i=Z47o zt3j!}kfHeW@LMRnW=`zTH`@nr?+k+bW+kJM;pXC~5>^KXI-A{E z^Tb&lWRJKX$^pwiCEK|3(9_eCzhme1Aj&2Z69gTsTbsVTg^p0{BW%9WP~uODC9rDl z#g)KWghHd2nBLczA@64M=HI?I(gstu@AaWMKDNSR?&g*>11Ys#y*@Y#hA6}W!+?uz zWG?j;*x+i}q2tdE{Buj zc+zEaE4sayb|jIwD#L4)bo6!tIP=R@_Zj0k1x@e3eZUTcUkjrG=b&TcdPWy@LxvhDML$17(#rV!}rHQySF6>X7>+BQviOK>_7fyP%XD#T!M$uwPqqI3kl1(Xo zIU6X@*8I@fw}T#rykKp0ssJZjXyX1ua#calLM5HE6q#cooL%gc*~16>9S_+=2jDu0 z)61x^AZ4PZJ=%BTb=PZ)JX-rg>9iB0OP{Vgm3uIm_&oa+$I)2pz!lR5JHF{?f)J*O zXTGF@TXS|K?HBA&8Z<=Sa)lPl*Yj^Jz0_VOWhG(#uA4qHqWG&7lQ_~RPScyS_}&-U zP{6kPY3LB?W(8;zCN`bHLnK*Rk}yA12ztQLQHoPQ~>u9=J4 z1|Q!{9mOsK#zzyZ$uk}Q_0q~whv@bjSA+Q1V-dlzzQklOau_rzxWa8NvFmOZ`6a`q zwJyPG0nm~^rTG2+^IIh%&4Cf5kwHLM8s7;4aK6D2i?eo05NaVfI-hDqCX=WYZPFrN z^!iZf{!8_(o#>wd4V;0?-|^luKj0-zI zRgLpSiw+P>^?2HG_kJ5u@AyG(#zK7)6q{1_dhf;K3H1V+e{&%BaNuyKawY+rS8z7$$2CUBe;JW=S{QIg8e zf&oVWrlMdCW|mVYH7;tsLvvt*?fLzDRSti8u10#E8a!>Hu&TJlI#hiVy#E`C`y_I3 zlzKHr0dqWgz*Y`j@McR8@aEQ_tpA)rCe9!V?Zt3F8rI9@O4qC_8_W?L1*JVq`>~wW z^^4sj`pL`q9mS4gAOwcYd#f1i_2?yLkPG4LA6IgQQ16gV<7+pic(o*S$0kx*v~M_M zdsba38zafE70w;|hDI>kP(Y&3*-0V%J&VzGR;;j{y^w;;Au}wi`T?zrDW;bh8A;#& zKDMyWd7ep<;ZgZZuAqrB{?z^X?pV}jLzYHk`giZkQSbZN506x01W;bK#64&M$bM*d zJC=a^#_deS4F71SA{;DO5cNR~kDG^fh3R)}+hmTQ9_G!r#b3xohhV_FtIOV@fS7fO zI1=RN!CGo`MEGs)eSEwHhRr%5hnNE{!+ox+RDtNiTsn+cd(~SY>6G&#AKci{m@3F6 z?cjPL8Kv@}KZhQaFf$^*=TlX181*>2S_S?fvG8Fz2ARQ5BfIns=g_^N@l5pY-kat1 zZcdqTO^p&61uW=&XKbT3fKc{nh0Z*b@#e7x#NT-@{wrC`S#v24e;gg@Yog4!B`Mix85s6p$+~I~+|_HXDHE(5XecSmjkalMG)bLm!)+QUsj!0d(5c(9_P?3 z)4*#SUOKb~Vf|>e;V38B3d$R+X_LHk!OdxdbN25eNP#kLZd_36R6m52y#>#gidjW# z)t41_R-&m@|3N>BZw5nm~=<8T$9cRKK2Lr*;bA=X8Jw3oC+Y z?DSuWS6jabR;}q;)AfE`EQ2S-Ka4;6K9X@a#Nabi8k7zHCh5q0sW4#l8CnRp=SC#dh=lMhMHq(aAQ=z9V+o>hj^?hre3%l1%*29|y!Cbt@cHviJw?9RcEO-!1m@z)!X#Eam&WsLbN|V1+5NK86)}pT`TTdBuGM3N~+QD%+1Ju(f}vX=sLG zGE67aSx_K40VFq7^;!}WXef}Gp=2qv;}Wr7t5pj%3Kx{Fh5}0Y|jMq&rmEK&Lz*_VbY*|ijUVyJ#KKz zJ~|Fn#Il&bRh!QrM(6|t#l`IVZksbgx#%c!O}1Kt^(Mlx&4Owsv!&(1P$0)(drX+| zHQg@o=ArF~KhQ@-O+zDu7ITfsl=Qw~7D{4%;eoL~^js6N&7kvcJ{D_2tB}7Czg4c0 zC^?e;x)3gIey$1C4w=&Os*cY`UKu$=j{Pr47?q<~GX?3RWR{Rna87#>3G!L?xHB|^ z0w%Hal5nm(l{ESs8>ll=A&vVX(P(Ue-}PjTQ(Yr&;sZn? zdD;9P=mKUG*25$pI;;Vfp}>YGw1w^F^alGT%n#GXAUQO;i9&<&#-?CN==Zg!L!9e{ zDui0vnA=yzF#_`JnAYxBx$R7s3hM*edw)xOL8mJ;m4E&~%Y`bxj1YwQh{4^Pz z_l@^;ms-2m8+Uo-18fR&^!?~ux%2gmxrxMF&aOnq0sLNa9wqOVEI{t-u z{??L`WyORZY;GCP0jd0xP#*U3A&EJMc8f9WXQ1_4D;|e{8${s=U(Grs8d0B|NMa!w z5Vn~WnnoeUo1z0Gs{uf=#=45_K0h!7b+A+wCP4l2ymn?&O`1+UU<|^pm`3=Cz;9?4U)E`+mCvPu3BLu5o6b8a%T2~0KTjVl?Y z>{?tDfC!ysliwlRcN&_sh^{@L?5$dn~1)OMoub>fa zvwGO_C1Tp8lKpzu-s&px_5u0sqP|a`YT>L}&^|Rk#4~abS++=zb5|hGSG4qi$n_EwS7e z=j4*HkrA{9)sAS@_jVFMIx%d8j0YwK0*9Uii$5b)ib^qaLR8ZYwiINH#kNw=#`p}> zHu44)aEab1Qa}YsotKH-UWg78 zr^Fl;(IQ{?OdeM4byy*Ct`j|cavp9{U) zoYj-|j(WF#&q+44t9jF-)8o?AJei6k3O@M%R?u)9p;M9pFokgknCo4(?DHuF~yP})LqW9q7O*ZMHxXobc zw&eA0C^ycduPGV^(Vi1Wn9qxYDdbug5kWwP%)Q^y-4_|VK*IJ>cMQbhOTRR!_dhW# z;{z9;3j*=Zl0fR_V!I=Zm>2$SZEYVEQWRu%KvPO&j+7=Uh+asnO)m$*HAxnK93>?s zd*UXox>fU03U3c4PB*5fYZ)?=FwL4A5x%>~F&S`=UH<(w_H&gIV?ljywGLVOAaFm$ zBGdB^{NdXMGVTqf#5 zGXn&H-vR&kTSRr#fy99JxgO-J59hSvB>hRwzPSwBlU6zau3!`*pp87)5gjLp zg3>#y)M;ovubrUpZ}w?81Z-$wCdYpskB<7txlL-UzLS1632P5nNXo%6&dWP|J`c63 zfX_>+k6|##M1LAxt)i*oW2fCme>_OR$NYAhe#a)s^9oot&q^p4>jtP3!Xam+%d8u%AEZ0H}%oI=`YE|POsApzIalXL9bpYWx#~ z&t@7@J%RZMvbu$&OQ)49$yhNeU`l%s$f1-qTX%Rou@W{@2n3wu4bqaCoLbzPS9xQR zIN0X}FYr7~NgbU`?mv}4bmrd2nkD;?SFuEJ>0%J_L3cr6s+2)1Ko)SpYST@Pva9V+ z^0sjZdYh5pPxFW3YQwqNndbYT(o8o*vmciTPK=4g<|sl;zP>ziu)@eUAr&Kv{6au5 zMwCJ*p^Slxe*;7U$3GzosSa&xw+TOCCPctB)Lfb^# zP=60!#dco=QkIT#Y`y1>&E{mCx8D)$7u?XhUhMI^-tF6qzPB;ewL>_8!C*#dmZ;FR ziIBo^K|#T6mA4OEN@Y`szq#P6SE=tu!cxJY=6yUP=0ld|GpF`Tf&(p0lkjg+ZHX*kv#Sn$(tkC(U zGLK-p;7_U8Kn<4OUsIOmb7nk`KOC}whD8J7TqWQ4!01f+DM}=j3bv~Jr3;e(TnlYt zu(`1~7gV>?Bd8$+16?2(VDlh8gXqo*0FmB(Exb?NGq(7Pe4=ad+W8ypO@b~+=8JsE zTw6p0dASgAML5o0vm$~50a%`6aA6jq>o3Ap%B<&6nX-W#4zR+L0c8dl~xKdllNC7Zm!~e(#fnxW6 zKAN6=qYEKsi6#rztcqDF$NdA%c$ z2Bn&IkG~*L>?Tg0i(mu}YFok$uhcDJTNGqXIcxqY8>emKVkIHg)ZeWEWGyP%`ysp! zjpMcPk69Ekkt`(5Ls^OH>#4Owk2n52#)%PDgk+*QnM?#>9za--y=w66nx`w`PN3G~ zuCCF}&4P!HJM|VDOt)53w*({K+zo`V6i&rDP^jik>}4BmeAw+*)8cd1lQf=}C~4qBEV@=sr}A}7R#la0f|4^w3wBEkC} z^k?7uFW7pS3HZts%ES3HG!OY^O?Y>67ZQVF{5I+uT~|kvFnE|qaqjBNC44;&jh8dM zgVDM>4jgS<1o7CImRvA5k=6TO7U9)+)|Va9c4dfg4ZwQOdtP4YonEa zgVG#H;9Yei4Qf7*D^c{>vtIKDs;bj8C^$v(IJX_P=E3pz*=(e$RcKUtM=G8~<_Lx0Oo zVQ%Q@aF-(2O|j;H{>y02sMZfsL5lKuNS0EU^YrPcYx#~h2GVtFUQl9&(;-Q!=8k%ag$V%EH#97@)6=dKggNKTmt897PxOu{sZ0g| zjG#p-h=CROz{kBfVRjlye%Z?}g6XvNB**~Xh>ng99U=8rp8Mb_&=cv4`-YZDB!h&A zcmpKJqmUym&|OdI-C?4C1cntBdZ6^>#YP8S`Y#8YgP96emA|p$lECBl8Ekg7l`qK{ z#85EIJOIGA5s+DHN>b|s zbydxqRRXOJC$sSwuuj%XJ2!;^EaoMjJuy^N7h2}kI4Y3wmA)05Si}|UzcaB$C?#tA zsUj~Yrws|mOGOuX@}oJF$HCB$`2K7qiONs}W70s?$7fBDSoys)*3u*oXy^@up>2#~ zEV}6xNA#4Wz?d$l%7c;geA-2Q3#e5pd{@r|O&@j30rgCP2Zlw`L!)~}_k#?ZVA|My z8RJ;Vts?ji{l8yNfDR9r2q{wVjzT4?yX)RD74bIgOVLdG&j`LBIPs?5{NHE)EgXEx zKTFjGQmHD7%;)j}OJ!zc^p#31wAGaZ?RJYn{&(1GK>`S}o`%NVSgP+=Y`9q@mQL1| z?rwh$v`DoprZ)8cu2AR@V65gqe*-YL58ki$OY(ZoCrl`Xo}R$Z3uYXPYbqrD-^YLt zrb3(RbpI$VJ&H`tajO8S38yosS$L_*BT1|SH^hHN-n~2!3$p-D)9*#rIZdd-U)QBv zjBspl|2;a*WI?I!tZMRQ3m`gbu(tM7I;AX6W9$;GCEb4(5m-iabiiA<24X=1z1>ak zM?3I808PzYXH!JtPnD#IDe?b)V3iysKY(AIxOY_OjaBMm#f#Ab@)BaSu1#HN$^)|h z{Xg%2;I#k{3R#|Jps7|s0kiHFlbFy%{-9HH~%+LPJA{>C~*7Qk~ZT_i)qvcRR2ABs+2$} zj(y-3VQD!yw7(ZQ(Yh9kNNctfjcWVfcS;yQz5}h*(%L| z>>uLv{qJLqN(g6(l-7l7=gf}TbGDh_ERdSQ(!TRVip8JoMP?N?;s3v0LBs#bfBZ+= zMovQaK+%Xt8cGYyjwF%tcyT#ea$WR)2cqcT-Nhv=X0*Bs69XRy*dP(Y8(7la zEV!d10sIuG?Ls5yUdUBC0eKgbA|L)=hLy(cDKn_mUW9m$oUKSiMp-Gzj ztb7K1w#@&1Qw-Hrsnqk_&HuCjKwikF!N-U{SLcv2WK_U?@dLTrMS1&HJ;@?t0xe*7 zZ%=27*`Qqo=)^kBG>x-lYWKG{U@zmECr2-l@&V4Qu&}W9^D$5o@TH|aB`s|Z5Dn1r z@T9S`vk&h$E&FS(liY$2LV>?vBN0errxB1hH6`CaJPa2|o0*6s{3Q1BaBk+@-vC@* ztZjEkGr=Q>X1AQfzQAMP`gCOiKszSR;R=aKNh`PFD7b45eXG5MY(~T692`mQ?&qU# zSL0}xz?ubhsdsWzcD3@_{!s$fVud3)IavkJ`mAt-*?LN+^IXK*ehFB|c$DsYIazPx z@ClPy90E#6{)1)g1pWe2JOrJ%RWH+XW1<~Y^RI0?uX~Q2!hd5u;8u1wO!bEqyuv*n z&d%(rmk>T}cMJ)9Ve#eBMolkC@%R`k2yY8NwN$0YBma2dt)fU%v*ZJmLAgffUuqt- zeRBW0$moipodR4=tB-5OG2G3LJ+Gc05Vzyz_*>+JGmyAz2UWM~ezq*S<@cCfG)4+&p#>6PKj40RXpbe} znFR!I$QBW?^+rAZXL;s#N_9KcIt^h!*_6J(jZz3&@5c*8;4MGg3{0s*Ap#hKG-eBe zu4ahJFSG<~rV^mMyMw^{di%BJyxhv}v?-yN7$VH%OAs7(B}tVXxBY7W;wMmY3B-5O zG?TtC9Y_sd0&Rl$uIDvqUWhx;zdSe8KZC41!%$0W_VN4&(T+m;P*$Q2AUpfu_1ZyJ zX+r84UCu|+VWcfgb574I|K+UP&s(E_-C`FJFJo77Oi1ls1-B&QFgqUGJWEo1EYO*NLg*>X&a=Q zo?reWsjk3jdU!iWmmza4nfoLBL^^N-bL9)a`eKGLB<1xBfd)Ez5kif`?@m?4$PxMo1uGw5E$Z3n%q@kKjvwXhs)D{i>0^TPow>AW8$W2`i{QnmS%aPiqH)4ggt=u(Bd@!! zlpv&RutV-m27l#g`aW)Wv~i*u^LWO2Pr%Et4K+*&Wm(Jo;uoG^rAAfX0Y3 z?BhXn>(rT>RNW8;7HCsjJ6Jp%g)58v^3nkiyl`wz+Lsx1zyN+=3hmgb&+ZOF zJt92H>_NtaIh#J0U3q_O4&lKJi^-b)0=9B%aJPbo-(6f;{9evm#2fiN597VM z{hIGw#hws^*JUImA73md`t)&zCy`R|OUK3_H%b;%OW4>dQ@mI!bDC`)W;BNL-$}}rlPq$?V`aOT2*S+_g^SVQ~Nq;;1s5kA)%((sHH1q<&Cih?U-uIkDlw{HG9L2BF;}w>FW;=I@ zykv#DgJ2M~&t$6^z=OCOHEvagZLa02;1ZJ}uL){2WvfUaCB(cBWKX`UKHMJN&G73H ztH-$g?3&3^D-NVQyXU_k^$B%uHL)e7l&W-o>TF`GTnF4)pxoI)NPO|{|KzN{vsy)r zl?3kvDPi)9hP^U%TNC)E20uPXdT@p=|*AJ5)fhHZ8C}p zp*YXlU(-@AZ6q6k?5+EZ{j*1Un@#%mx5r2x9^K*N8*vXGZFfB)Z8CG|B<0e@`(Cd_ zi|4MAmVQb^-Hac_RKKeWbk3ha@!ou%?DOu)1_W=h`E6G#z=LLg8AzY0 zRGO^D!Tq=4fO^qr`@(>OVQ4aX^swtW&0TsHd7b^UuW3D}d5%(5iwlL0!o%6-?`Ft+ z#c5b(7|Q{w`{zCoYS9O_zox_*XW2?Ns_&}<8E!j43mI!sO*FWD;70 zQ3)@XE_>~{Itg;_dWMghN8_j}vlqt|Bzfb9PeQ-lK$taIf%A!6O(D#(oL|XaNiM6= zs>l8c8m(n^#2L94vCEr3P&ay_h!2rL-&m3FG|YlU8l;dU1Mg!RW84wfG`-CJC1| zrUKB*9BpB$9|{3d=bb;|PT83IzxEYVY?4P<6k+P;ixNs68TrPKa=}>c`9UOR!+_ct z>h!6G)B$O5{BOsmmlX)$C?PoGtm+Kg3_>`_o%J1$*KDZR<^P%hIL-_TE{wR}(_MTF zxH>Ns@lY*eF0teuR^0vx+>|ISqr*n+dhxZ*X+KWJEdql`pqvPRVxKCtXzBbYkU^g0 zxYATtk#fAFQ;CWcAPX92et@n4$`&S4Yi;2T}||%)#SPGbx?{qYu%= z;&7i8$~=-6btjzMh~iPqP$ugC3;wMxXS#RU$kg|1fM1bafNO0;$Ce&32OcT&k#MA5IveY4yqc=8{BE82E-8S6#!wbF2&!kk0!tl>6 zprafC{aO6+q;MUH-LQ+SM7}56bghKn7j@$tNWNCsZ|~|WF8e_A(_-= z$G~L@4UFre%c)F8l(l6+w#*4lNNF_`9b36sY!cgp#}ngVwDV$V+Jg<5E4)U&(2whK z*Q{5moz!nb^n-3UN4zZ%90=eS5NH{Ambv}sl!i3lU-&Wt$kC0Oq#8H=vY`R_WF|#Z zw8T4cI!Vm$k4NY}G^--10+$Y{V?y3|vHHhs@im!JAl0@~6NVMEZt zs?!nOgi=(d>i!oaUom-=?YlA6<_P^ntA%-&CQz+jo8@s^BivVLrNGHWA{d|0unywi z^_?Qok-FE}|75l<=aFzFQQ!F8k0eQB!+m8z^KVjuMVXxOzLIwEl7tO|%?#%{v*{lp zF@X%H!aBnUncWVcbYlGEg=uQT>kb-7KC}V99{QQV1CLG5{B&FH?KI1qw8{I#+tkO; zFBWk$Qz4d}nYO(TZokAmU_}p(B{CNWj1Px-cfNV-EEq_u*E(b6&aG1c37K;GIa3YV zONmRkoZ@2~E@^r0EYc~eTYA4orb^YNiSh&NDYNFVH}zXE{gj>1_=#R#%8R>7x?B2MbxO>F_nTMQ&wJ@uvCpbiDV3EA=z_nhi5yT$usvqGQNT1T zJH<$uFw0G;)V}eQIl>j}=`)AOt213N;f%M#9CxEvVF%8!!Tj zh4KsBM6fV|tA}qu=1ckcBUtrQ$me>?SC;TMa_W&RWkkX;%8xPu-kk)=2M3M#d;&QPm+$u+E1|)A~Ls_*Y$Yv zQ*+p0Y^|MBIQ#rMJfQT6Zk6}GqoSOtlI68xuv_w)MGPX3E6;@Q4>;g3RbqeF$~83E z;)zfT?+fy~tSr#8Ka}h7yW&=k+0=f;xUT^J`+xp*mn-c$iEC36E|?W-){<=Z8SkM5 z+LZv^(9S>IXS**!%+h*8hudfeN!r=i9`PeCYD%MOZ$A)Pq;RgJ3-0cekTMqH(QdW& za358a22t~+Eh#ZE>E#feKUoN&ZC?Ot7~(4Q=~|~H+f1xU`B(00;GLOZJPxwb2;Zp3 zpZ9wZ_TAT*IRzI5UviYq{<(#pe-6L;r4K%y5t{y#iSEk?6s_28Wgo?P=0ye6*{4^$ zI-_xP624Rz2$aCKJ$ATiZ|fF#7K09ySL|;tArdABX!4;fyf}WnkOrsk^7p`e#Dxxv ztaN7{0XGqk+8{odAn83OCGu2Ar{jt$Cjs#*LUYjIC(+e&o281lQ5x*q`U$O>@Ow}y zi}aUPBhHg_Rc`h&N>3yDe+}BNY5iem^*grREKvtBH8PfW4lkg@E9E-MX4zfI2I8CR z`#$I|M?=lV+4TzEa0|$~Rf9#lxAwaP2iT2&mpfHAF7IYA=BIRL_&!T$W2y*YOxe(Qmbl772ggcx_&0jAeRT;y+Jy8n{X7pN9?p=T+L zI>+0BX0D5~VRDtSZ-i^evxv&@zA!HhU409uf*?b3MrKCm6xOh;Sqx^vvDe)o*P?_(&*|SHi%*sd zPsuuW>CCj^r;Udcn6ui#!%#waJ2A0bi=y@y8{)0bfqH3Ay>QJEwB^VG`48{GyYpzWa*5H&gIK|ApV%Fke;OZ@1}X+0>gdpajE zlIzB&9|JMri<<){p0Bz+ou9txeZy@!=TmpG*y+!dP`dNp4)umeI4+kp|4yhwC&v@Z z`z)?&SKnKFYEVHIBCrmL`5b3HkE^Y=pDH^FIYeqBfBvJD{XWQKZFn+-ka`vAZ2J(N z|NYBz?q^Ew21*ob_%g2fupyCzFqB18H-7Ltp@d+%HSlE5& zJ<65s;4_kG%%&FyJ0y+$>e=7AZ~GTqY=2%P`naVSCf6bE)cQu;)9jeNE+}gn9d+L8 zgKG(*9iI3V)SA&kYC!3iUE$3%jk`h1B@wO992MAehG(FsP+3we4WESs^cqPvGMw-9 z)L&jl{|qT9$u#^MLa9C6TM;}?@}w+P@;@TEr|WVbZkVx6*b>|LA$*Ho={V5X_6bH; z$M;wq+^J4gfWG2Zn+al*(e>QcpEK_k_r1`O9D&q<&sM4^(sf+3DR^aUPEP2h(b;zi znNFc4-i<-83^l!w@=NlA6uIhUfI#bfnlAGWQOc>0M zc+6mn`{;7K9CW(7BXf44f&f1`ui#e>Xz-qv^haH}na~u^nenOOJk6EW*Jf_v)hFiRg_{0@>e!H=byk?o9J0lmC`i9DR ztb`8gl`t8iRo|hgMHBT1Dm8t!6<9f)OXJkxo(##dTDfz|^dywF;Pu(y6;}?BrK};G ztQkMk`Cx$=0y}$)AyxDf0x_g|Vw&WlRUF>LwYZy`nOn_V*aY6<{;x}qiCYUb9gue5 zUDQZ@SaYdYFnmV5xOG$jpbWLL2?VS5zRUWav?-HB6H2v7ZND0{B(ac~Zw!eZrz*Zu zT-=l(Bg}n3NR;2}VTT~dFOL4edzMFXDh=`Xwn%Vw{9JNn&w}<0Njbr!p{f7=PBTxZ5oCU`mnL@(NEHpKTWBF@>c=&B#Yb ze2K~Hxt{)2`@{1@*jnfUS5~vc3PJ(l=Zuy2h)zR)=4R)V;49r>>Cn1z>!X9_&U?n+ zSj5WtHFuwwJ0$pOeI3#Y050QxT>?4Tt&$$5EtAhlZHHp=Y)D|2hDF4QzQ`kZPL_vu znBtMeMo@T9iL_&nDzk-Xc%j4X-QEK$zqiv1(AU3Pjl~=8J)9CQR1c~&GiX)gqzdKt z(-qMibmV9+GI3~w3N&;;6RK~{9w!CUWToS7tCyC+Y zC(@MGwO`OeN1(!ZIUL<)$p(!H>lH2l^bec;K2xix05e*jZ9vf&%Kb!=;m0X?6Unv)3X9^sheIWotDWdZ~pLcYSj69j$t%& zLT~Hgo_uy^N5q42pF)x9C0yW3gQUOA0wo|h7o3ZvUlwmfER7+y4IjGW!sutvrz0K^xc7F<^k-ZK1U zgKFO%xcXY5Qm3l`9Jh0fEnG^}&=ibf1aWNB$J5U6C^vZMp+)VN6$%YdLiwT0^a3Z4 z1~2b|D+{GA-q7210ya+QU2Gf;LO{aO1|nR`RYeZ_aOU|jW$1co*-5Q2m?nh?_MV&? z0+P=L`5}yAnrR4m_rNsf+!4aT{CJsZ&e6DHpO80b8kJB%|Lma=3#Ohu6qMLxjS7}4 z)rWRC0^PldU<&Q6Yuu4f>IRV!6a(|fKi@lLn1M)$#85qZb4HYvBZFF?t>s&(DtVIJ zNil)NA=ZdA3;9SUg{r#eMHu}$pn8XL3*F=#Hu>)GW*n6biP^|IdHT>YemJ?@;&87g zwAIv+eGk)BkDi3B&|@a%yk4JzK-g~3;Qk8(m&PWh%-#jEs}b7Ze!mZytr#N67U|i} zUM#0nzrXXS?+6L4jBv~Gr13+~=%<4}&u5A_Gw7NZUA)9$pMAbf)ioobu|#0CjFHG4 zhp2F<-*mo3T^oPPL?I$P5c@J?_1qGndScX%QjjJgJWhPsKj@sch{CAAw7?8OJxETZ z^S~h?s)ue`t4lBbyOfA+nyC{J5x>uPxqT3PK z;{Q(Q2&Ezq2&Dz$f`Blo9WhtBdI;s%18d-@%)yjEOE*dwWp*9Qt-M1V6^InRMSLo9 z7$i90lyM;I=){#B>;@X((!g6|``nd}hGr9)Mk+mW$oYt)aCERUF;TNb%OCr<-i=09 zpyGvW((qz)w~!pT<3g$pomOoy14spJNd9_|P$M|-TOeLAIx6|7G}x_^{sAjZKpfWJ zy>rga6X-RYT#z!T6yU@_FioVMhlDZ4+G+Jkz4!`SqeLEWxaiUo3Gw3!TN<|)D%mRJ49J6M@62vY z{%{mbOQGczZhH}s(yyHQs|0IC$G%=00Y+ZcNiKZ24Loa*ym}72+ts6*R1sVU!hhiD zM-39f$>MW){&|v&3jo;g;mbS4<=)?c{=A6?_Z}yWHaE+;%y%a z*Pp6@nIi08@3QPoalwP&TrQHL9U=oYwBK-oH~Ul$dGakBkRu2yDvRz@h@;o-}9yFjET`~~KeD`mXQK>B0Bj2iIuJ&O=jRRc6SQ!J%h;(H)QrNbgNcy)luyJj zUo-qGd%Vbtf>L@s36lBqli zLyic5Sx`-aQRaQ5M?Z1Al8kRtbYs&Q}0~2i#93 zoOel2yptdU!SfNKr~xjq$BEG{vh=Xe=m*?3m4Jm{O{7z7E8hODrOkRPdfXG@T50#q?}|dA=Y* z9uv7X2UsX6Di(T3U z{ks*x?z48^%3mCok>kQo%%-?7pzL-G{1Z>)jjmJ{qj}o$cpI9Mqa}zL#p9={>aA9l@~y?)|p>@*mEr+MU`z^7KciGV_`Zscf&% z+1#1pU`#mu<9(ITaKFg!E(Ea5KxaMu=R#YbOn{ylyElucD16`Fz_oV^*^-?4Z;ugm zw0M4QNDa8$AjEZ3?Q!C=G+;E{}ryUD943v0`oSNY|)XJ_X}1$?Tw^4eQidY&oMtf_nggY7)U%p9oVGbM?FhUrI#2L0TCJPkD<7381;Azzy~%>`J} z!=^^^{X{3)#lLb&$$vw0o8XiFEqaVCO#ZQQK#-YXaxZUEKby9*Ew>6<#*~_q`=wzQb_rkllWt!h0M}^OQ^FVjv8r%l{~3W5 z5sL#-R)*J3{|;NOa9FHRDKD#uV=MM`1qKOI$hWOn0!o`9t8FnH;ewBni^8e?GX?i2 zjYt}<_`>yyT)s>^K#H!U{76xm2|8*RHEjfI%ECI`&h6kIIM4-bqHCC(VRT1Dx(Qh& z52z*teYRbaN}g{Ug)Xp;?mHj!le>DVJpbdFon&Uo_3M>~%F8r_57;ZYf%{;p^*r)A zMgRrn8Ag_6ltI7}``XH%QFO^^f`ccQgEM!RJ9jSXB?Iy8$Tc(US{8PVLuFYA*6a$b zxJ<_l5;|a`e>)DNMq$nUN5sI*(o^Nt#oNgFfx%&D!Y)@Xu%A_^P;vX5%%`5uD=$ll zUf+Q&mQ)YFHaz)pK03+H5ahuLtTk+hMK8;UK;%CFWq<9S4#N^^Bi9_P{7bMhVc0$2 zG0MId%TP0(T;J4lQ`k8%Y_5ColtJZ7V%?-)CwLD-?aYgQ3SCIS) U_Gl2hLj-(}Rh}zXD58V^4}S81LI3~& literal 0 HcmV?d00001 diff --git a/docs/source/conf.py b/docs/source/conf.py index 8a9f7395e..9f558c698 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,13 +16,14 @@ # -- Project information ----------------------------------------------------- +import telebot project = 'pyTelegramBotAPI' copyright = '2022, coder2020official' author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '1.0' +release = telebot.__version__ # -- General configuration --------------------------------------------------- @@ -36,6 +37,7 @@ 'sphinx.ext.autodoc', "sphinx.ext.autosummary", "sphinx.ext.napoleon", + "sphinx_copybutton", ] @@ -53,14 +55,18 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = 'furo' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] -html_logo = 'logo.png' +#html_logo = 'logo.png' html_theme_options = { - 'logo_only': True, - 'display_version': False, + "light_css_variables": { + "color-brand-primary": "#7C4DFF", + "color-brand-content": "#7C4DFF", + }, + "light_logo": "logo.png", + "dark_logo": "logo2.png", } diff --git a/telebot/__init__.py b/telebot/__init__.py index cf1be1ff7..afa4e0971 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -46,6 +46,8 @@ Module : telebot """ +__version__ = '4.4.0' + class Handler: """ @@ -916,8 +918,7 @@ def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :param sticker_set_name: Name of the sticker set to be set as the group sticker set :return: API reply. """ @@ -2913,17 +2914,19 @@ def middleware_handler(self, update_types=None): Example: - bot = TeleBot('TOKEN') + .. code-block:: python3 - # Print post message text before entering to any post_channel handlers - @bot.middleware_handler(update_types=['channel_post', 'edited_channel_post']) - def print_channel_post_text(bot_instance, channel_post): - print(channel_post.text) + bot = TeleBot('TOKEN') - # Print update id before entering to any handlers - @bot.middleware_handler() - def print_channel_post_text(bot_instance, update): - print(update.update_id) + # Print post message text before entering to any post_channel handlers + @bot.middleware_handler(update_types=['channel_post', 'edited_channel_post']) + def print_channel_post_text(bot_instance, channel_post): + print(channel_post.text) + + # Print update id before entering to any handlers + @bot.middleware_handler() + def print_channel_post_text(bot_instance, update): + print(update.update_id) :param update_types: Optional list of update types that can be passed into the middleware handler. """ @@ -2986,7 +2989,7 @@ def message_handler(self, commands=None, regexp=None, func=None, content_types=N All message handlers are tested in the order they were added. Example: - + .. code-block:: python bot = TeleBot('TOKEN') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f654083f0..2ab6f4ab7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2344,17 +2344,14 @@ async def restrict_chat_member( If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues - :param can_send_media_messages Pass True, if the user can send audios, documents, photos, videos, video notes - and voice notes, implies can_send_messages + :param can_send_media_messages: Pass True, if the user can send audios, documents, photos, videos, video notes + and voice notes, implies can_send_messages :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and - use inline bots, implies can_send_media_messages - :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, - implies can_send_media_messages - :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. - Ignored in public supergroups - :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, - implies can_invite_users + use inline bots, implies can_send_media_messages + :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, implies can_send_media_messages + :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, implies can_invite_users :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups :return: True on success """ diff --git a/telebot/util.py b/telebot/util.py index f7e9e4afa..14ed36017 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -283,7 +283,7 @@ def split_string(text: str, chars_per_string: int) -> List[str]: def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]: - """ + r""" Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. This is very useful for splitting one giant message into multiples. If `chars_per_string` > 4096: `chars_per_string` = 4096. From 3dda5cff0689b942af4d2959f9c4455caf51ef10 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:14:25 +0500 Subject: [PATCH 0911/1808] Create README.md --- examples/middleware/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 examples/middleware/README.md diff --git a/examples/middleware/README.md b/examples/middleware/README.md new file mode 100644 index 000000000..2673cf6e6 --- /dev/null +++ b/examples/middleware/README.md @@ -0,0 +1,34 @@ +# Middlewares + +## Type of middlewares in pyTelegramBotAPI +Currently, synchronous version of pyTelegramBotAPI has two types of middlewares: + +- Class-based middlewares +- Function-based middlewares + +## Purpose of middlewares +Middlewares are designed to get update before handler's execution. + +## Class-based middlewares +This type of middleware has more functionality compared to function-based one. + +Class based middleware should be instance of `telebot.handler_backends.BaseMiddleware.` + +Each middleware should have 2 main functions: + +`pre_process` -> is a method of class which receives update, and data. + +Data - is a dictionary, which could be passed right to handler, and `post_process` function. + +`post_process` -> is a function of class which receives update, data, and exception, that happened in handler. If handler was executed correctly - then exception will equal to None. + +## Function-based middlewares +To use function-based middleware, you should set `apihelper.ENABLE_MIDDLEWARE = True`. +This type of middleware is created by using a decorator for middleware. +With this type middleware, you can retrieve update immediately after update came. You should set update_types as well. + +## Why class-based middlewares are better? +- You can pass data between post, pre_process functions, and handler. +- If there is an exception in handler, you will get exception parameter with exception class in post_process. +- Has post_process -> method which works after the handler's execution. + From 403028bf3569620dbf1476661feee07fe69e7d33 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:14:51 +0500 Subject: [PATCH 0912/1808] Update README.md --- examples/middleware/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/middleware/README.md b/examples/middleware/README.md index 2673cf6e6..23ef1bf8e 100644 --- a/examples/middleware/README.md +++ b/examples/middleware/README.md @@ -32,3 +32,4 @@ With this type middleware, you can retrieve update immediately after update came - If there is an exception in handler, you will get exception parameter with exception class in post_process. - Has post_process -> method which works after the handler's execution. +## Take a look at examples for more. From b292b275cb111bb86609f63fdba761e1eaec9a18 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:21:59 +0500 Subject: [PATCH 0913/1808] Create basic_example.py --- .../middleware/class_based/basic_example.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 examples/middleware/class_based/basic_example.py diff --git a/examples/middleware/class_based/basic_example.py b/examples/middleware/class_based/basic_example.py new file mode 100644 index 000000000..353bebdc9 --- /dev/null +++ b/examples/middleware/class_based/basic_example.py @@ -0,0 +1,32 @@ +from telebot import TeleBot +from telebot.handler_backends import BaseMiddleware + +bot = TeleBot('TOKEN', use_class_middlewares=True) # set use_class_middlewares to True! +# otherwise, class-based middlewares won't execute. + +# You can use this classes for cancelling update or skipping handler: +# from telebot.handler_backends import CancelUpdate, SkipHandler + +class Middleware(BaseMiddleware): + def __init__(self): + self.update_types = ['message'] + def pre_process(self, message, data): + data['foo'] = 'Hello' # just for example + # we edited the data. now, this data is passed to handler. + # return SkipHandler() -> this will skip handler + # return CancelUpdate() -> this will cancel update + def post_process(self, message, data, exception=None): + print(data['foo']) + if exception: # check for exception + print(exception) + +@bot.message_handler(commands=['start']) +def start(message, data: dict): # you don't have to put data parameter in handler if you don't need it. + bot.send_message(message.chat.id, data['foo']) + data['foo'] = 'Processed' # we changed value of data.. this data is now passed to post_process. + + +# Setup middleware +bot.setup_middleware(Middleware()) + +bot.infinity_polling() From 60a23665cbac9f1851ebb6e3ed958fab47df811b Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:23:55 +0500 Subject: [PATCH 0914/1808] Update flooding_middleware.py --- .../asynchronous_telebot/middleware/flooding_middleware.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/asynchronous_telebot/middleware/flooding_middleware.py b/examples/asynchronous_telebot/middleware/flooding_middleware.py index b8f589ede..cc9d9f859 100644 --- a/examples/asynchronous_telebot/middleware/flooding_middleware.py +++ b/examples/asynchronous_telebot/middleware/flooding_middleware.py @@ -1,8 +1,7 @@ # Just a little example of middleware handlers -from telebot.asyncio_handler_backends import BaseMiddleware +from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate from telebot.async_telebot import AsyncTeleBot -from telebot.async_telebot import CancelUpdate bot = AsyncTeleBot('TOKEN') @@ -36,4 +35,4 @@ async def start(message): await bot.send_message(message.chat.id, 'Hello!') import asyncio -asyncio.run(bot.polling()) \ No newline at end of file +asyncio.run(bot.polling()) From 8fab55e9379659580a6b94e37f3fbb23c3a8a603 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:30:10 +0500 Subject: [PATCH 0915/1808] Create antiflood_middleware.py --- .../class_based/antiflood_middleware.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 examples/middleware/class_based/antiflood_middleware.py diff --git a/examples/middleware/class_based/antiflood_middleware.py b/examples/middleware/class_based/antiflood_middleware.py new file mode 100644 index 000000000..3743f097d --- /dev/null +++ b/examples/middleware/class_based/antiflood_middleware.py @@ -0,0 +1,39 @@ +# Just a little example of middleware handlers + +from telebot.handler_backends import BaseMiddleware +from telebot import TeleBot +from telebot.handler_backends import CancelUpdate +bot = TeleBot('TOKEN', + use_class_middlewares=True) # if you don't set it to true, middlewares won't work + + +class SimpleMiddleware(BaseMiddleware): + def __init__(self, limit) -> None: + self.last_time = {} + self.limit = limit + self.update_types = ['message'] + # Always specify update types, otherwise middlewares won't work + + + def pre_process(self, message, data): + if not message.from_user.id in self.last_time: + # User is not in a dict, so lets add and cancel this function + self.last_time[message.from_user.id] = message.date + return + if message.date - self.last_time[message.from_user.id] < self.limit: + # User is flooding + bot.send_message(message.chat.id, 'You are making request too often') + return CancelUpdate() + self.last_time[message.from_user.id] = message.date + + + def post_process(self, message, data, exception): + pass + +bot.setup_middleware(SimpleMiddleware(2)) + +@bot.message_handler(commands=['start']) +def start(message): # you don't have to put data in handler. + bot.send_message(message.chat.id, 'Hello!') + +bot.infinity_polling() From 5f03253398394e08afc67a69ca21ca4582d71035 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 7 Mar 2022 17:31:02 +0500 Subject: [PATCH 0916/1808] Fix comments --- telebot/asyncio_handler_backends.py | 2 +- telebot/handler_backends.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 17e28d04f..e5e2e4f08 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -24,7 +24,7 @@ def __str__(self) -> str: class StatesGroup: def __init_subclass__(cls) -> None: - # print all variables of a subclass + for name, value in cls.__dict__.items(): if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index f696d6bd2..b838231b8 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -159,7 +159,6 @@ def __str__(self) -> str: class StatesGroup: def __init_subclass__(cls) -> None: - # print all variables of a subclass for name, value in cls.__dict__.items(): if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable From f15101fc6f1b5f1d2b2806138f558cc082b00863 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:32:42 +0500 Subject: [PATCH 0917/1808] Update doc_req.txt --- doc_req.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc_req.txt b/doc_req.txt index 0f0e93813..2ba5ef4e6 100644 --- a/doc_req.txt +++ b/doc_req.txt @@ -1 +1,2 @@ -pytelegrambotapi \ No newline at end of file +pytelegrambotapi +furo From dcd0df93daf8b85eb876d55ffe219bbd139b6069 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:39:42 +0500 Subject: [PATCH 0918/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f2e5b394e..5dc02ea36 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) +[![Documentation Status](https://readthedocs.org/projects/pytba/badge/?version=latest)](https://pytba.readthedocs.io/en/latest/?badge=latest) [![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) [![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/) [![PyPi status](https://img.shields.io/pypi/status/pytelegrambotapi.svg?style=flat-square)](https://pypi.python.org/pypi/pytelegrambotapi) From 60a96d14004f256f84e2d21ce73ad7e5b7bb302d Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 17:42:47 +0500 Subject: [PATCH 0919/1808] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5dc02ea36..41f07693c 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,9 @@ def start(message): assert message.another_text == message.text + ':changed' ``` There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory. - + +#### Class-based middlewares +There are class-based middlewares. Check out in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/middleware/class_based) #### Custom filters Also, you can use built-in custom filters. Or, you can create your own filter. @@ -759,7 +761,7 @@ As you can see here, keywords are await and async. Asynchronous tasks depend on processor performance. Many asynchronous tasks can run parallelly, while thread tasks will block each other. ### Differences in AsyncTeleBot -AsyncTeleBot has different middlewares. See example on [middlewares](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot/middleware) +AsyncTeleBot is asynchronous. It uses aiohttp instead of requests module. ### Examples See more examples in our [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder From 05c3cb2c1d54f085d2929f7a3a9ef6296d262e07 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 18:08:41 +0500 Subject: [PATCH 0920/1808] Update doc_req.txt --- doc_req.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc_req.txt b/doc_req.txt index 2ba5ef4e6..54eba373d 100644 --- a/doc_req.txt +++ b/doc_req.txt @@ -1,2 +1,3 @@ pytelegrambotapi furo +sphinx_copybutton From 7b62915a5be1014d3aee102095528c01a8b08ca7 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 18:54:59 +0500 Subject: [PATCH 0921/1808] Create PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..16c56c894 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ +## Description +Include changes, new features and etc: + +## Describe your tests +How did you test your change? + +Python version: + +OS: + +## Checklist: +- [ ] I added/edited example on new feature/change (if exists) +- [ ] My changes won't break backend compatibility + From 5f91c3d4e6cfd7661ded50aa8d05a83afdbeca6b Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 18:56:16 +0500 Subject: [PATCH 0922/1808] Added python 3.10 to tests --- .github/workflows/setup_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 111090843..e1a92fa1d 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.7', 'pypy-3.8' ] #'pypy-3.9' NOT SUPPORTED NOW + python-version: [ '3.6','3.7','3.8','3.9', '3.10', 'pypy-3.7', 'pypy-3.8' ] #'pypy-3.9' NOT SUPPORTED NOW name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 From 4875bb6188af35349b13d2693ec5095f57336428 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 19:04:12 +0500 Subject: [PATCH 0923/1808] Update requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8e06e278b..b5086dc40 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -pytest==3.0.2 +pytest requests==2.20.0 wheel==0.24.0 -aiohttp>=3.8.0,<3.9.0 \ No newline at end of file +aiohttp>=3.8.0,<3.9.0 From 41025ba97bec9fec757df234f52e3325fc3b8c8a Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 20:26:11 +0500 Subject: [PATCH 0924/1808] Update setup_python.yml --- .github/workflows/setup_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index e1a92fa1d..d1ef9640b 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.6','3.7','3.8','3.9', '3.10', 'pypy-3.7', 'pypy-3.8' ] #'pypy-3.9' NOT SUPPORTED NOW + python-version: [ '3.6','3.7','3.8','3.9', '3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 From 886c9b9bc0b895657255f1801b94c27677281226 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 7 Mar 2022 20:38:22 +0500 Subject: [PATCH 0925/1808] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 41f07693c..d81885b00 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ #

pyTelegramBotAPI

A simple, but extensible Python implementation for the Telegram Bot API.

-

Supports both sync and async ways.

+

Both synchronous and asynchronous.

-##

Supporting Bot API version: 5.7! +##

Supported Bot API version: 5.7!

Official documentation

From 244b0586487a52b14232c482e484eb78022cdefb Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 7 Mar 2022 21:13:30 +0500 Subject: [PATCH 0926/1808] Fix --- docs/source/conf.py | 4 +--- telebot/__init__.py | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 9f558c698..e962ae337 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,14 +16,13 @@ # -- Project information ----------------------------------------------------- -import telebot project = 'pyTelegramBotAPI' copyright = '2022, coder2020official' author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = telebot.__version__ +release = '4.4.0' # -- General configuration --------------------------------------------------- @@ -32,7 +31,6 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx_rtd_theme', 'sphinx.ext.autosectionlabel', 'sphinx.ext.autodoc', "sphinx.ext.autosummary", diff --git a/telebot/__init__.py b/telebot/__init__.py index afa4e0971..312cb94d0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -46,8 +46,6 @@ Module : telebot """ -__version__ = '4.4.0' - class Handler: """ From 477d02468dfc4d13ca9fbb5f89fc586820eb2e03 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 7 Mar 2022 21:40:39 +0500 Subject: [PATCH 0927/1808] Fixed middlewares --- telebot/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 312cb94d0..9d4fde334 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3876,11 +3876,12 @@ def _notify_command_handlers(self, handlers, new_messages, update_type): return for message in new_messages: - middleware = self._check_middleware(update_type) - if self.use_class_middlewares and middleware: + if self.use_class_middlewares: + middleware = self._check_middleware(update_type) self._exec_task(self._run_middlewares_and_handler, message, handlers=handlers, middlewares=middleware) return - for message_handler in handlers: - if self._test_message_handler(message_handler, message): - self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') - break + else: + for message_handler in handlers: + if self._test_message_handler(message_handler, message): + self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') + break From 1cdf9640d7a7d56eb370a269b819b773889a3d52 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 8 Mar 2022 12:07:00 +0500 Subject: [PATCH 0928/1808] Fix example on chat_member, fix middleware-exception for async --- examples/asynchronous_telebot/chat_member_example.py | 2 +- telebot/async_telebot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/asynchronous_telebot/chat_member_example.py b/examples/asynchronous_telebot/chat_member_example.py index 30f47e279..b2f545c21 100644 --- a/examples/asynchronous_telebot/chat_member_example.py +++ b/examples/asynchronous_telebot/chat_member_example.py @@ -31,4 +31,4 @@ async def my_chat_m(message: types.ChatMemberUpdated): async def delall(message: types.Message): await bot.delete_message(message.chat.id,message.message_id) import asyncio -asyncio.run(bot.polling()) +asyncio.run(bot.polling(allowed_updates=util.update_types)) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2ab6f4ab7..7b45ed942 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -318,7 +318,7 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): except Exception as e: handler_error = e - if not middleware: + if not middlewares: if self.exception_handler: return self.exception_handler.handle(e) logging.error(str(e)) From 73b2813512907682e2d16ac25d01b02dbcdd97e6 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 10 Mar 2022 20:22:42 +0500 Subject: [PATCH 0929/1808] Update doc_req.txt --- doc_req.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc_req.txt b/doc_req.txt index 54eba373d..8e53619c1 100644 --- a/doc_req.txt +++ b/doc_req.txt @@ -1,3 +1,4 @@ pytelegrambotapi furo sphinx_copybutton +aiohttp From b652a9f6dc67b56ffaa8a84bdfef7c260d0edc89 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 19 Mar 2022 13:26:09 +0500 Subject: [PATCH 0930/1808] Update doc_req.txt --- doc_req.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc_req.txt b/doc_req.txt index 8e53619c1..ff01a213f 100644 --- a/doc_req.txt +++ b/doc_req.txt @@ -1,4 +1,5 @@ -pytelegrambotapi +-r requirements.txt + furo sphinx_copybutton -aiohttp +git+https://github.com/eternnoir/pyTelegramBotAPI.git From a6477541c0e5b64822544041c00c9bad9449b637 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 19 Mar 2022 13:49:36 +0500 Subject: [PATCH 0931/1808] Documentation incorrect display is fixed now. --- telebot/__init__.py | 22 +++++++++++++--------- telebot/callback_data.py | 3 +++ telebot/types.py | 4 ++++ telebot/util.py | 4 +++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9d4fde334..2a1bce932 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1148,15 +1148,15 @@ def send_audio( Telegram documentation: https://core.telegram.org/bots/api#sendaudio - :param chat_id:Unique identifier for the message recipient - :param audio:Audio file to send. + :param chat_id: Unique identifier for the message recipient + :param audio: Audio file to send. :param caption: - :param duration:Duration of the audio in seconds - :param performer:Performer - :param title:Track name - :param reply_to_message_id:If the message is a reply, ID of the original message + :param duration: Duration of the audio in seconds + :param performer: Performer + :param title: Track name + :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: - :param parse_mode + :param parse_mode: :param disable_notification: :param timeout: :param thumb: @@ -2936,7 +2936,8 @@ def decorator(handler): def add_middleware_handler(self, handler, update_types=None): """ - Add middleware handler + Add middleware handler. + :param handler: :param update_types: :return: @@ -3146,6 +3147,7 @@ def add_edited_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_message_handler to add edited_message_handler to the bot. + :param handler_dict: :return: """ @@ -3424,6 +3426,7 @@ def add_chosen_inline_handler(self, handler_dict): def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwargs): """ Registers chosen inline handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3719,6 +3722,7 @@ def add_chat_join_request_handler(self, handler_dict): def register_chat_join_request_handler(self, callback, func=None, pass_bot=False, **kwargs): """ Registers chat join request handler. + :param callback: function to be called :param func: :param pass_bot: Pass TeleBot to handler. @@ -3748,7 +3752,7 @@ def add_custom_filter(self, custom_filter: Union[SimpleCustomFilter, AdvancedCus """ Create custom filter. - custom_filter: Class with check(message) method. + :param custom_filter: Class with check(message) method. :param custom_filter: Custom filter class with key. """ self.custom_filters[custom_filter.key] = custom_filter diff --git a/telebot/callback_data.py b/telebot/callback_data.py index ecbe81e8c..17bf27f5d 100644 --- a/telebot/callback_data.py +++ b/telebot/callback_data.py @@ -10,6 +10,7 @@ def __init__(self, factory, config: typing.Dict[str, str]): def check(self, query): """ Checks if query.data appropriates to specified config + :param query: telebot.types.CallbackQuery :return: bool """ @@ -50,6 +51,7 @@ def __init__(self, *parts, prefix: str, sep=':'): def new(self, *args, **kwargs) -> str: """ Generate callback data + :param args: positional parameters of CallbackData instance parts :param kwargs: named parameters :return: str @@ -87,6 +89,7 @@ def new(self, *args, **kwargs) -> str: def parse(self, callback_data: str) -> typing.Dict[str, str]: """ Parse data from the callback data + :param callback_data: string, use to telebot.types.CallbackQuery to parse it from string to a dict :return: dict parsed from callback data """ diff --git a/telebot/types.py b/telebot/types.py index 081c0ed8f..0c6c3bc2a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -69,6 +69,7 @@ def check_json(json_type, dict_copy = True): """ Checks whether json_type is a dict or a string. If it is already a dict, it is returned as-is. If it is not, it is converted to a dict by means of json.loads(json_type) + :param json_type: input json or parsed dict :param dict_copy: if dict is passed and it is changed outside - should be True! :return: Dictionary parsed from json or original dict @@ -943,6 +944,7 @@ def add(self, *args, row_width=None): when row_width is set to 1. When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} See https://core.telegram.org/bots/api#replykeyboardmarkup + :param args: KeyboardButton to append to the keyboard :param row_width: width of row :return: self, to allow function chaining. @@ -974,6 +976,7 @@ def row(self, *args): Adds a list of KeyboardButton to the keyboard. This function does not consider row_width. ReplyKeyboardMarkup#row("A")#row("B", "C")#to_json() outputs '{keyboard: [["A"], ["B", "C"]]}' See https://core.telegram.org/bots/api#replykeyboardmarkup + :param args: strings :return: self, to allow function chaining. """ @@ -2399,6 +2402,7 @@ def __init__(self, id, title): def add_price(self, *args): """ Add LabeledPrice to ShippingOption + :param args: LabeledPrices """ for price in args: diff --git a/telebot/util.py b/telebot/util.py index 14ed36017..79ed2b1b6 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -226,8 +226,9 @@ def pil_image_to_file(image, extension='JPEG', quality='web_low'): def is_command(text: str) -> bool: - """ + r""" Checks if `text` is a command. Telegram chat commands start with the '/' character. + :param text: Text to check. :return: True if `text` is a command, else False. """ @@ -452,6 +453,7 @@ def deprecated(warn: bool=True, alternative: Optional[Callable]=None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. + :param warn: If True a warning is logged else an info :param alternative: The new function to use instead """ From 7b1b1a7caa4cf5dd3e05dda1dc251366cb74484d Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 19 Mar 2022 14:06:07 +0500 Subject: [PATCH 0932/1808] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 16c56c894..b46f5a645 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,4 +11,5 @@ OS: ## Checklist: - [ ] I added/edited example on new feature/change (if exists) - [ ] My changes won't break backend compatibility +- [ ] I made changes for async and sync From 625da4cdd9e247b68868e01a06ab4f285a995fb5 Mon Sep 17 00:00:00 2001 From: Igor Vaiman Date: Sun, 27 Mar 2022 18:32:05 +0400 Subject: [PATCH 0933/1808] missing await inserted --- 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 591410cfd..e8a986717 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1200,7 +1200,7 @@ async def edit_message_caption(token, caption, chat_id=None, message_id=None, in async def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): method_url = r'editMessageMedia' - media_json, file = convert_input_media(media) + media_json, file = await convert_input_media(media) payload = {'media': media_json} if chat_id: payload['chat_id'] = chat_id From 22b4e636e2e3ee6669f77d38af7d9b912d564fd7 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 16 Apr 2022 00:13:14 +0500 Subject: [PATCH 0934/1808] Road to release(1st part) --- telebot/__init__.py | 3 +++ telebot/asyncio_filters.py | 2 +- telebot/asyncio_helper.py | 1 + telebot/custom_filters.py | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2a1bce932..55d322e0e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3849,6 +3849,9 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** elif len(params) == 3: if params[2] == 'bot' and handler.get('pass_bot') is True: handler['function'](message, data, self) + + elif not handler.get('pass_bot'): + raise RuntimeError('Your handler accepts 3 parameters but pass_bot is False. Please re-check your handler.') else: handler['function'](message, self, data) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 1b397613e..98874b98e 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -303,7 +303,7 @@ async def check(self, message, text): group_state = await self.bot.current_states.get_state(user_id, chat_id) if group_state == text: return True - elif group_state in text and type(text) is list: + elif type(text) is list and group_state in text: return True diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index e8a986717..aaac114e8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -67,6 +67,7 @@ async def _process_request(token, url, method='get', params=None, files=None, re logger.debug("Request: method={0} url={1} params={2} files={3} request_timeout={4} current_try={5}".format(method, url, params, files, request_timeout, current_try).replace(token, token.split(':')[0] + ":{TOKEN}")) json_result = await _check_result(url, resp) if json_result: + got_result = True return json_result['result'] except (ApiTelegramException,ApiInvalidJSONException, ApiHTTPException) as e: raise e diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 8442be43c..1bd80b360 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -311,7 +311,7 @@ def check(self, message, text): group_state = self.bot.current_states.get_state(user_id, chat_id) if group_state == text: return True - elif group_state in text and type(text) is list: + elif type(text) is list and group_state in text: return True From 9652fdbecb578a72d539f3b680d9496b26f7cf13 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 16 Apr 2022 23:50:45 +0500 Subject: [PATCH 0935/1808] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e0183ba1..a0e283f97 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 5.7! +##

Supported Bot API version: 6.0!

Official documentation

@@ -699,7 +699,7 @@ Result will be: ## API conformance - +* ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022) * ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022) * ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021) * ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021) From 5e28f277642e049600359856685f45bf94384aaf Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 16 Apr 2022 23:17:19 +0300 Subject: [PATCH 0936/1808] Bump version to 4.4.1 --- telebot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/version.py b/telebot/version.py index 0a15dc1a4..e7fe97a98 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.4.0' +__version__ = '4.4.1' From 1a40f1da7aec587570c416ad2afdd5bf0217bd58 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 16 Apr 2022 23:21:25 +0300 Subject: [PATCH 0937/1808] Bump version to 4.4.1 --- docs/source/conf.py | 2 +- examples/custom_states.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index e962ae337..104db8f3e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.4.0' +release = '4.4.1' # -- General configuration --------------------------------------------------- diff --git a/examples/custom_states.py b/examples/custom_states.py index a18de1f8a..2f9a2bc60 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -7,7 +7,7 @@ from telebot.storage import StateMemoryStorage -# Beginning from version 4.4.0+, we support storages. +# Starting from version 4.4.0+, we support storages. # StateRedisStorage -> Redis-based storage. # StatePickleStorage -> Pickle-based storage. # For redis, you will need to install redis. From a1bf961fd2edfb6d0d3e20bb04d6677023c251ee Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 17 Apr 2022 16:39:09 +0500 Subject: [PATCH 0938/1808] Bump Bot API 6.0(Beta) --- telebot/__init__.py | 91 ++++++++++++++++- telebot/apihelper.py | 49 ++++++++- telebot/async_telebot.py | 90 ++++++++++++++++- telebot/asyncio_helper.py | 48 ++++++++- telebot/types.py | 208 ++++++++++++++++++++++++++++++++++---- tests/test_types.py | 23 +++++ 6 files changed, 481 insertions(+), 28 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 55d322e0e..55be7af47 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1772,6 +1772,7 @@ def promote_chat_member( can_promote_members: Optional[bool]=None, is_anonymous: Optional[bool]=None, can_manage_chat: Optional[bool]=None, + can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator @@ -1798,15 +1799,20 @@ def promote_chat_member( message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege - :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats + :param can_manage_video_chats: Bool: Pass True, if the administrator can manage voice chats For now, bots can use this privilege only for passing to other administrators. + :param can_manage_voice_chats: Deprecated, use can_manage_video_chats. + :return: True on success. """ + if can_manage_voice_chats and not can_manage_video_chats is None: + can_manage_video_chats = can_manage_voice_chats + return apihelper.promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_voice_chats) + is_anonymous, can_manage_chat, can_manage_video_chats) def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: @@ -2033,6 +2039,71 @@ def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, result = apihelper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + def set_chat_menu_button(self, chat_id: Union[int, str]=None, + menu_button: types.MenuButton=None) -> bool: + """ + Use this method to change the bot's menu button in a private chat, + or the default menu button. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be changed + :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault + + """ + return apihelper.set_chat_menu_button(self.token, chat_id, menu_button) + + + def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButton: + """ + Use this method to get the current value of the bot's menu button + in a private chat, or the default menu button. + Returns MenuButton on success. + + Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be returned + :return: types.MenuButton + + """ + return types.MenuButton.de_json(apihelper.get_chat_menu_button(self.token, chat_id)) + + + def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, + for_channels: bool=None) -> bool: + """ + Use this method to change the default administrator rights requested by the bot + when it's added as an administrator to groups or channels. + These rights will be suggested to users, but they are are free to modify + the list before adding the bot. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights + + :param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared. + :param for_channels: Pass True to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + """ + + return apihelper.set_my_default_administrator_rights(self.token, rights, for_channels) + + + def get_my_default_administrator_rights(self, for_channels: bool=None) -> types.ChatAdministratorRights: + """ + Use this method to get the current default administrator rights of the bot. + Returns ChatAdministratorRights on success. + + Telegram documentation: https://core.telegram.org/bots/api#getmydefaultadministratorrights + + :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. + :return: types.ChatAdministratorRights + """ + + return types.ChatAdministratorRights.de_json(apihelper.get_my_default_administrator_rights(self.token, for_channels)) + + def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: @@ -2684,6 +2755,22 @@ def delete_sticker_from_set(self, sticker: str) -> bool: """ return apihelper.delete_sticker_from_set(self.token, sticker) + def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryResultBase) -> types.SentWebAppMessage: + """ + Use this method to set the result of an interaction with a Web App and + send a corresponding message on behalf of the user to the chat from which + the query originated. + On success, a SentWebAppMessage object is returned. + + Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery + + :param web_app_query_id: Unique identifier for the query to be answered + :param result: A JSON-serialized object describing the message to be sent + :return: + """ + + return apihelper.answer_web_app_query(self.token, web_app_query_id, result) + def register_for_reply( self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 67a8e7c00..118b4962f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -971,7 +971,7 @@ def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, - is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None): + is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -994,8 +994,8 @@ def promote_chat_member( payload['is_anonymous'] = is_anonymous if can_manage_chat is not None: payload['can_manage_chat'] = can_manage_chat - if can_manage_voice_chats is not None: - payload['can_manage_voice_chats'] = can_manage_voice_chats + if can_manage_video_chats is not None: + payload['can_manage_video_chats'] = can_manage_video_chats return _make_request(token, method_url, params=payload, method='post') @@ -1139,6 +1139,43 @@ def get_my_commands(token, scope=None, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload) +def set_chat_menu_button(token, chat_id=None, menu_button=None): + method_url = r'setChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + if menu_button: + payload['menu_button'] = menu_button.to_json() + + return _make_request(token, method_url, params=payload, method='post') + +def get_chat_menu_button(token, chat_id=None): + method_url = r'getChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + + return _make_request(token, method_url, params=payload, method='post') + + +def set_my_default_administrator_rights(token, rights=None, for_channels=None): + method_url = r'setMyDefaultAdministratorRights' + payload = {} + if rights: + payload['rights'] = rights.to_json() + if for_channels: + payload['for_channels'] = for_channels + + return _make_request(token, method_url, params=payload, method='post') + +def get_my_default_administrator_rights(token, for_channels=None): + method_url = r'getMyDefaultAdministratorRights' + payload = {} + if for_channels: + payload['for_channels'] = for_channels + + return _make_request(token, method_url, params=payload, method='post') + def set_my_commands(token, commands, scope=None, language_code=None): method_url = r'setMyCommands' @@ -1590,6 +1627,11 @@ def delete_sticker_from_set(token, sticker): payload = {'sticker': sticker} return _make_request(token, method_url, params=payload, method='post') +def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResultBase): + method_url = 'answerWebAppQuery' + result = result.to_json() + payload = {'query_id': web_app_query_id, 'result': result} + return _make_request(token, method_url, params=payload, method='post') # noinspection PyShadowingBuiltins def send_poll( @@ -1663,6 +1705,7 @@ def _convert_list_json_serializable(results): return '[' + ret + ']' + def _convert_markup(markup): if isinstance(markup, types.JsonSerializable): return markup.to_json() diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7b45ed942..4dd78a75d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1550,6 +1550,22 @@ async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) return result + async def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryResultBase) -> types.SentWebAppMessage: + """ + Use this method to set the result of an interaction with a Web App and + send a corresponding message on behalf of the user to the chat from which + the query originated. + On success, a SentWebAppMessage object is returned. + + Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery + + :param web_app_query_id: Unique identifier for the query to be answered + :param result: A JSON-serialized object describing the message to be sent + :return: + """ + + return await asyncio_helper.answer_web_app_query(self.token, web_app_query_id, result) + async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. @@ -2374,6 +2390,7 @@ async def promote_chat_member( can_promote_members: Optional[bool]=None, is_anonymous: Optional[bool]=None, can_manage_chat: Optional[bool]=None, + can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator @@ -2400,15 +2417,20 @@ async def promote_chat_member( message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege - :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats + :param can_manage_video_chats: Bool: Pass True, if the administrator can manage voice chats For now, bots can use this privilege only for passing to other administrators. + :param can_manage_voice_chats: Deprecated, use can_manage_video_chats :return: True on success. """ + + if can_manage_voice_chats and not can_manage_video_chats is None: + can_manage_video_chats = can_manage_voice_chats + return await asyncio_helper.promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_voice_chats) + is_anonymous, can_manage_chat, can_manage_video_chats) async def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: @@ -2635,6 +2657,70 @@ async def get_my_commands(self, scope: Optional[types.BotCommandScope], result = await asyncio_helper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + async def set_chat_menu_button(self, chat_id: Union[int, str]=None, + menu_button: types.MenuButton=None) -> bool: + """ + Use this method to change the bot's menu button in a private chat, + or the default menu button. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be changed + :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault + + """ + return await asyncio_helper.set_chat_menu_button(self.token, chat_id, menu_button) + + + async def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButton: + """ + Use this method to get the current value of the bot's menu button + in a private chat, or the default menu button. + Returns MenuButton on success. + + Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be returned + :return: types.MenuButton + + """ + return types.MenuButton.de_json(await asyncio_helper.get_chat_menu_button(self.token, chat_id)) + + + async def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, + for_channels: bool=None) -> bool: + """ + Use this method to change the default administrator rights requested by the bot + when it's added as an administrator to groups or channels. + These rights will be suggested to users, but they are are free to modify + the list before adding the bot. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights + + :param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared. + :param for_channels: Pass True to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + """ + + return await asyncio_helper.set_my_default_administrator_rights(self.token, rights, for_channels) + + + async def get_my_default_administrator_rights(self, for_channels: bool=None) -> types.ChatAdministratorRights: + """ + Use this method to get the current default administrator rights of the bot. + Returns ChatAdministratorRights on success. + + Telegram documentation: https://core.telegram.org/bots/api#getmydefaultadministratorrights + + :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. + :return: types.ChatAdministratorRights + """ + + return types.ChatAdministratorRights.de_json(await asyncio_helper.get_my_default_administrator_rights(self.token, for_channels)) + async def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index aaac114e8..eae41a50f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -356,6 +356,12 @@ async def delete_chat_sticker_set(token, chat_id): payload = {'chat_id': chat_id} return await _process_request(token, method_url, params=payload) +async def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResultBase): + method_url = 'answerWebAppQuery' + result = result.to_json() + payload = {'query_id': web_app_query_id, 'result': result} + return await _process_request(token, method_url, params=payload, method='post') + async def get_chat_member(token, chat_id, user_id): method_url = r'getChatMember' @@ -941,7 +947,7 @@ async def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, - is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None): + is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -964,8 +970,8 @@ async def promote_chat_member( payload['is_anonymous'] = is_anonymous if can_manage_chat is not None: payload['can_manage_chat'] = can_manage_chat - if can_manage_voice_chats is not None: - payload['can_manage_voice_chats'] = can_manage_voice_chats + if can_manage_video_chats is not None: + payload['can_manage_video_chats'] = can_manage_video_chats return await _process_request(token, method_url, params=payload, method='post') @@ -1106,6 +1112,42 @@ async def get_my_commands(token, scope=None, language_code=None): payload['language_code'] = language_code return await _process_request(token, method_url, params=payload) +async def set_chat_menu_button(token, chat_id=None, menu_button=None): + method_url = r'setChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + if menu_button: + payload['menu_button'] = menu_button.to_json() + + return await _process_request(token, method_url, params=payload, method='post') + +async def get_chat_menu_button(token, chat_id=None): + method_url = r'getChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_my_default_administrator_rights(token, rights=None, for_channels=None): + method_url = r'setMyDefaultAdministratorRights' + payload = {} + if rights: + payload['rights'] = rights.to_json() + if for_channels: + payload['for_channels'] = for_channels + + return await _process_request(token, method_url, params=payload, method='post') + +async def get_my_default_administrator_rights(token, for_channels=None): + method_url = r'getMyDefaultAdministratorRights' + payload = {} + if for_channels: + payload['for_channels'] = for_channels + + return await _process_request(token, method_url, params=payload, method='post') async def set_my_commands(token, commands, scope=None, language_code=None): method_url = r'setMyCommands' diff --git a/telebot/types.py b/telebot/types.py index 0c6c3bc2a..307d7c595 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -194,14 +194,15 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, - last_error_date=None, last_error_message=None, max_connections=None, - allowed_updates=None, **kwargs): + last_error_date=None, last_error_message=None, last_synchronization_error_date=None, + max_connections=None, allowed_updates=None, **kwargs): self.url = url self.has_custom_certificate = has_custom_certificate self.pending_update_count = pending_update_count self.ip_address = ip_address self.last_error_date = last_error_date self.last_error_message = last_error_message + self.last_synchronization_error_date = last_synchronization_error_date self.max_connections = max_connections self.allowed_updates = allowed_updates @@ -313,6 +314,20 @@ def __init__(self, message_id, **kwargs): self.message_id = message_id +class WebAppData(JsonDeserializable): + def __init__(self, data, button_text): + self.data = data + self.button_text = button_text + def to_dict(self): + return {'data': self.data, 'button_text': self.button_text} + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + class Message(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -457,18 +472,25 @@ def de_json(cls, json_string): opts['proximity_alert_triggered'] = ProximityAlertTriggered.de_json(obj[ 'proximity_alert_triggered']) content_type = 'proximity_alert_triggered' - if 'voice_chat_scheduled' in obj: - opts['voice_chat_scheduled'] = VoiceChatScheduled.de_json(obj['voice_chat_scheduled']) - content_type = 'voice_chat_scheduled' - if 'voice_chat_started' in obj: - opts['voice_chat_started'] = VoiceChatStarted.de_json(obj['voice_chat_started']) - content_type = 'voice_chat_started' - if 'voice_chat_ended' in obj: - opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) - content_type = 'voice_chat_ended' - if 'voice_chat_participants_invited' in obj: - opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['voice_chat_participants_invited']) - content_type = 'voice_chat_participants_invited' + if 'video_chat_scheduled' in obj: + opts['video_chat_scheduled'] = VoiceChatScheduled.de_json(obj['video_chat_scheduled']) + opts['voice_chat_scheduled'] = opts['video_chat_scheduled'] + content_type = 'video_chat_scheduled' + if 'video_chat_started' in obj: + opts['video_chat_started'] = VoiceChatStarted.de_json(obj['video_chat_started']) + opts['voice_chat_started'] = opts['video_chat_started'] + content_type = 'video_chat_started' + if 'video_chat_ended' in obj: + opts['video_chat_ended'] = VoiceChatEnded.de_json(obj['video_chat_ended']) + opts['voice_chat_ended'] = opts['video_chat_ended'] + content_type = 'video_chat_ended' + if 'video_chat_participants_invited' in obj: + opts['video_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['video_chat_participants_invited']) + opts['voice_chat_participants_invited'] = opts['video_chat_participants_invited'] + content_type = 'video_chat_participants_invited' + if 'web_app_data' in obj: + opts['web_app_data'] = WebAppData.de_json(obj['web_app_data']) + content_type = 'web_app_data' if 'message_auto_delete_timer_changed' in obj: opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(obj['message_auto_delete_timer_changed']) content_type = 'message_auto_delete_timer_changed' @@ -919,6 +941,20 @@ def to_json(self): return json.dumps(json_dict) +class WebAppInfo(JsonDeserializable): + @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, url, **kwargs): + self.url: str = url + + def to_dict(self): + return {'url': self.url} + + class ReplyKeyboardMarkup(JsonSerializable): max_row_keys = 12 @@ -1011,11 +1047,13 @@ def to_dict(self): class KeyboardButton(Dictionaryable, JsonSerializable): def __init__(self, text: str, request_contact: Optional[bool]=None, - request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None): + request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, + web_app: WebAppInfo=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location self.request_poll: KeyboardButtonPollType = request_poll + self.web_app: WebAppInfo = web_app def to_json(self): return json.dumps(self.to_dict()) @@ -1028,6 +1066,8 @@ def to_dict(self): json_dict['request_location'] = self.request_location if self.request_poll is not None: json_dict['request_poll'] = self.request_poll.to_dict() + if self.web_app is not None: + json_dict['web_app'] = self.web_app.to_dict() return json_dict @@ -1122,13 +1162,17 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'login_url' in obj: obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) + if 'web_app' in obj: + obj['web_app'] = WebAppInfo.de_json(obj.get('web_app')) + return cls(**obj) - def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, + def __init__(self, text, url=None, callback_data=None, web_app=None, switch_inline_query=None, switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): self.text: str = text self.url: str = url self.callback_data: str = callback_data + self.web_app: WebAppInfo = web_app self.switch_inline_query: str = switch_inline_query self.switch_inline_query_current_chat: str = switch_inline_query_current_chat self.callback_game = callback_game # Not Implemented @@ -1144,6 +1188,8 @@ def to_dict(self): json_dict['url'] = self.url if self.callback_data: json_dict['callback_data'] = self.callback_data + if self.web_app: + json_dict['web_url'] = self.web_app.to_dict() if self.switch_inline_query is not None: json_dict['switch_inline_query'] = self.switch_inline_query if self.switch_inline_query_current_chat is not None: @@ -1235,7 +1281,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed can_invite_users=None, can_pin_messages=None, is_member=None, can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, - can_manage_chat=None, can_manage_voice_chats=None, + can_manage_chat=None, can_manage_video_chats=None, until_date=None, **kwargs): self.user: User = user self.status: str = status @@ -1257,7 +1303,8 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_send_other_messages: bool = can_send_other_messages self.can_add_web_page_previews: bool = can_add_web_page_previews self.can_manage_chat: bool = can_manage_chat - self.can_manage_voice_chats: bool = can_manage_voice_chats + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_manage_voice_chats: bool = self.can_manage_video_chats self.until_date: int = until_date @@ -1689,6 +1736,23 @@ def to_dict(self): return json_dict +class SentWebAppMessage(JsonDeserializable): + @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, inline_message_id): + self.inline_message_id = inline_message_id + + def to_dict(self): + return {'inline_message_id': self.inline_message_id} + + + + + 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): @@ -2890,3 +2954,111 @@ def de_json(cls, json_string): def __init__(self, message_auto_delete_time, **kwargs): self.message_auto_delete_time = message_auto_delete_time + + +class MenuButton(JsonDeserializable, JsonSerializable): + """ + Base class for MenuButtons. + """ + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + map = { + 'commands': MenuButtonCommands, + 'web_app': MenuButtonWebApp, + 'default': MenuButtonDefault + } + return map[obj['type']](**obj) + + def to_json(self): + raise NotImplementedError + + +class MenuButtonCommands(MenuButton): + + def __init__(self, type): + self.type = type + + def to_dict(self): + return {'type': self.type} + + def to_json(self): + return json.dumps(self.to_dict()) + +class MenuButtonWebApp(MenuButton): + + def __init__(self, type, text, web_app): + self.type: str = type + self.text: str = text + self.web_app: WebAppInfo = web_app + + def to_dict(self): + return {'type': self.type, 'text': self.text, 'web_app': self.web_app.to_dict()} + + def to_json(self): + return json.dumps(self.to_dict()) + +class MenuButtonDefault(MenuButton): + + def __init__(self, type): + self.type: str = type + + def to_dict(self): + return {'type': self.type} + + def to_json(self): + return json.dumps(self.to_dict()) + + +class ChatAdministratorRights(JsonDeserializable, JsonSerializable): + """ + Class representation of: + https://core.telegram.org/bots/api#chatadministratorrights + """ + + @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, is_anonymous: bool, can_manage_chat: bool, + can_delete_messages: bool, can_manage_video_chats: bool, can_restrict_members: bool, + can_promote_members: bool, can_change_info: bool, can_invite_users: bool, + can_post_messages: bool=None, can_edit_messages: bool=None, + can_pin_messages: bool=None) -> None: + + self.is_anonymous: bool = is_anonymous + self.can_manage_chat: bool = can_manage_chat + self.can_delete_messages: bool = can_delete_messages + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_post_messages: bool = can_post_messages + self.can_edit_messages: bool = can_edit_messages + self.can_pin_messages: bool = can_pin_messages + + def to_dict(self): + data = { + 'is_anonymous': self.is_anonymous, + 'can_manage_chat': self.can_manage_chat, + 'can_delete_messages': self.can_delete_messages, + 'can_manage_video_chats': self.can_manage_video_chats, + 'can_restrict_members': self.can_restrict_members, + 'can_promote_members': self.can_promote_members, + 'can_change_info': self.can_change_info, + 'can_invite_users': self.can_invite_users, + 'can_post_messages': self.can_post_messages, + 'can_edit_messages': self.can_edit_messages, + 'can_pin_messages': self.can_pin_messages + } + return data + + def to_json(self): + return json.dumps(self.to_dict()) + + + diff --git a/tests/test_types.py b/tests/test_types.py index 43200a8eb..dedee19dd 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -245,3 +245,26 @@ def test_chat_member_updated(): assert cm_updated.old_chat_member.status == "member" assert cm_updated.new_chat_member.status == "administrator" + +def test_webhook_info(): + json_string = r'{"url": "https://example.com/webhook", "has_custom_certificate": true, "pending_update_count": 1, "last_error_date": 0, "last_error_message": "", "last_synchronization_error_date": 489309, "max_connections": 40, "allowed_updates": ["message"]}' + webhook_info = types.WebhookInfo.de_json(json_string) + print(webhook_info) + assert webhook_info.url == 'https://example.com/webhook' + assert webhook_info.has_custom_certificate is True + assert webhook_info.pending_update_count == 1 + assert webhook_info.last_error_date == 0 + assert webhook_info.last_error_message == '' + assert webhook_info.max_connections == 40 + assert webhook_info.last_synchronization_error_date == 489309 + assert webhook_info.allowed_updates == ['message'] + + +def test_sent_web_app_message(): + json_string = r'{"inline_message_id": "29430"}' + sent_web_app_message = types.SentWebAppMessage.de_json(json_string) + assert sent_web_app_message.inline_message_id == '29430' + + + + From b146df346dab963f9c574e59681a4f05a47e0861 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 17 Apr 2022 16:46:38 +0500 Subject: [PATCH 0939/1808] Indentation fix to fit documentation. --- telebot/__init__.py | 6 +++--- telebot/async_telebot.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 55be7af47..a33b2659f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2049,7 +2049,7 @@ def set_chat_menu_button(self, chat_id: Union[int, str]=None, Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be changed + If not specified, default bot's menu button will be changed. :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault """ @@ -2065,7 +2065,7 @@ def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButto Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be returned + If not specified, default bot's menu button will be returned. :return: types.MenuButton """ @@ -2100,7 +2100,7 @@ def get_my_default_administrator_rights(self, for_channels: bool=None) -> types. :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. :return: types.ChatAdministratorRights """ - + return types.ChatAdministratorRights.de_json(apihelper.get_my_default_administrator_rights(self.token, for_channels)) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4dd78a75d..a2fdd7575 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2667,7 +2667,7 @@ async def set_chat_menu_button(self, chat_id: Union[int, str]=None, Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be changed + If not specified, default bot's menu button will be changed. :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault """ @@ -2683,7 +2683,7 @@ async def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.Men Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be returned + If not specified, default bot's menu button will be returned. :return: types.MenuButton """ From 4812dcb02b69949e4fd003d95ad842d28a1e3e6f Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 22 Apr 2022 23:06:11 +0500 Subject: [PATCH 0940/1808] Fix typo in types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 307d7c595..c659fd4d7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1189,7 +1189,7 @@ def to_dict(self): if self.callback_data: json_dict['callback_data'] = self.callback_data if self.web_app: - json_dict['web_url'] = self.web_app.to_dict() + json_dict['web_app'] = self.web_app.to_dict() if self.switch_inline_query is not None: json_dict['switch_inline_query'] = self.switch_inline_query if self.switch_inline_query_current_chat is not None: From 3a5db47c1b08659d3d414413318a1dab6027ee07 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 23 Apr 2022 10:44:35 +0500 Subject: [PATCH 0941/1808] removed old class based i18n middleware --- examples/i18n_class_example/i18n_class.py | 66 -------- examples/i18n_class_example/keyboards.py | 23 --- .../locales/en/LC_MESSAGES/messages.po | 51 ------ .../locales/ru/LC_MESSAGES/messages.po | 60 ------- .../locales/uz_Latn/LC_MESSAGES/messages.po | 58 ------- examples/i18n_class_example/main.py | 153 ------------------ 6 files changed, 411 deletions(-) delete mode 100644 examples/i18n_class_example/i18n_class.py delete mode 100644 examples/i18n_class_example/keyboards.py delete mode 100644 examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po delete mode 100644 examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po delete mode 100644 examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po delete mode 100644 examples/i18n_class_example/main.py diff --git a/examples/i18n_class_example/i18n_class.py b/examples/i18n_class_example/i18n_class.py deleted file mode 100644 index aa02612a3..000000000 --- a/examples/i18n_class_example/i18n_class.py +++ /dev/null @@ -1,66 +0,0 @@ -import gettext -import os - - -class I18N: - """ - This class provides high-level tool for internationalization - It is based on gettext util. - """ - - def __init__(self, translations_path, domain_name: str): - self.path = translations_path - self.domain = domain_name - self.translations = self.find_translations() - - @property - def available_translations(self): - return list(self.translations) - - def gettext(self, text: str, lang: str = None): - """ - Singular translations - """ - if not lang or lang not in self.translations: - return text - - translator = self.translations[lang] - return translator.gettext(text) - - def ngettext(self, singular: str, plural: str, lang: str = None, n=1): - """ - Plural translations - """ - if not lang or lang not in self.translations: - if n == 1: - return singular - return plural - - translator = self.translations[lang] - return translator.ngettext(singular, plural, n) - - def find_translations(self): - """ - Looks for translations with passed 'domain' in passed 'path' - """ - if not os.path.exists(self.path): - raise RuntimeError(f"Translations directory by path: {self.path!r} was not found") - - result = {} - - for name in os.listdir(self.path): - translations_path = os.path.join(self.path, name, 'LC_MESSAGES') - - if not os.path.isdir(translations_path): - continue - - po_file = os.path.join(translations_path, self.domain + '.po') - mo_file = po_file[:-2] + 'mo' - - if os.path.isfile(po_file) and not os.path.isfile(mo_file): - raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") - - with open(mo_file, 'rb') as file: - result[name] = gettext.GNUTranslations(file) - - return result diff --git a/examples/i18n_class_example/keyboards.py b/examples/i18n_class_example/keyboards.py deleted file mode 100644 index 4a942686c..000000000 --- a/examples/i18n_class_example/keyboards.py +++ /dev/null @@ -1,23 +0,0 @@ -from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton - - -def languages_keyboard(): - return InlineKeyboardMarkup( - keyboard=[ - [ - InlineKeyboardButton(text="English", callback_data='en'), - InlineKeyboardButton(text="Русский", callback_data='ru'), - InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn') - ] - ] - ) - - -def clicker_keyboard(_, lang): - return InlineKeyboardMarkup( - keyboard=[ - [ - InlineKeyboardButton(text=_("click", lang=lang), callback_data='click'), - ] - ] - ) diff --git a/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po deleted file mode 100644 index fc6565a4a..000000000 --- a/examples/i18n_class_example/locales/en/LC_MESSAGES/messages.po +++ /dev/null @@ -1,51 +0,0 @@ -# English translations for PROJECT. -# Copyright (C) 2022 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-02-18 17:54+0500\n" -"PO-Revision-Date: 2022-02-18 16:22+0500\n" -"Last-Translator: FULL NAME \n" -"Language: en\n" -"Language-Team: en \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" - -#: keyboards.py:20 -msgid "click" -msgstr "" - -#: main.py:78 -msgid "" -"Hello, {user_fist_name}!\n" -"This is the example of multilanguage bot.\n" -"Available commands:\n" -"\n" -"/lang - change your language\n" -"/plural - pluralization example" -msgstr "" - -#: main.py:102 -msgid "Language has been changed" -msgstr "" - -#: main.py:114 -#, fuzzy -msgid "You have {number} click" -msgid_plural "You have {number} clicks" -msgstr[0] "" -msgstr[1] "" - -#: main.py:120 -msgid "" -"This is clicker.\n" -"\n" -msgstr "" - diff --git a/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po deleted file mode 100644 index 1733ef152..000000000 --- a/examples/i18n_class_example/locales/ru/LC_MESSAGES/messages.po +++ /dev/null @@ -1,60 +0,0 @@ -# Russian translations for PROJECT. -# Copyright (C) 2022 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-02-18 17:54+0500\n" -"PO-Revision-Date: 2022-02-18 16:22+0500\n" -"Last-Translator: FULL NAME \n" -"Language: ru\n" -"Language-Team: ru \n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" - -#: keyboards.py:20 -msgid "click" -msgstr "Клик" - -#: main.py:78 -msgid "" -"Hello, {user_fist_name}!\n" -"This is the example of multilanguage bot.\n" -"Available commands:\n" -"\n" -"/lang - change your language\n" -"/plural - pluralization example" -msgstr "" -"Привет, {user_fist_name}!\n" -"Это пример мультиязычного бота.\n" -"Доступные команды:\n" -"\n" -"/lang - изменить язык\n" -"/plural - пример плюрализации" - -#: main.py:102 -msgid "Language has been changed" -msgstr "Язык был сменён" - -#: main.py:114 -msgid "You have {number} click" -msgid_plural "You have {number} clicks" -msgstr[0] "У вас {number} клик" -msgstr[1] "У вас {number} клика" -msgstr[2] "У вас {number} кликов" - -#: main.py:120 -msgid "" -"This is clicker.\n" -"\n" -msgstr "" -"Это кликер.\n" -"\n" - diff --git a/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po b/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po deleted file mode 100644 index e694b7810..000000000 --- a/examples/i18n_class_example/locales/uz_Latn/LC_MESSAGES/messages.po +++ /dev/null @@ -1,58 +0,0 @@ -# Uzbek (Latin) translations for PROJECT. -# Copyright (C) 2022 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-02-18 17:54+0500\n" -"PO-Revision-Date: 2022-02-18 16:22+0500\n" -"Last-Translator: FULL NAME \n" -"Language: uz_Latn\n" -"Language-Team: uz_Latn \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" - -#: keyboards.py:20 -msgid "click" -msgstr "clik" - -#: main.py:78 -msgid "" -"Hello, {user_fist_name}!\n" -"This is the example of multilanguage bot.\n" -"Available commands:\n" -"\n" -"/lang - change your language\n" -"/plural - pluralization example" -msgstr "" -"Salom, {user_fist_name}!\n" -"Bu multilanguage bot misoli.\n" -"Mavjud buyruqlar:\n" -"\n" -"/lang - tilni ozgartirish\n" -"/plural - pluralizatsiya misoli" - -#: main.py:102 -msgid "Language has been changed" -msgstr "Til ozgartirildi" - -#: main.py:114 -msgid "You have {number} click" -msgid_plural "You have {number} clicks" -msgstr[0] "Sizda {number}ta clik" -msgstr[1] "Sizda {number}ta clik" - -#: main.py:120 -msgid "" -"This is clicker.\n" -"\n" -msgstr "" -"Bu clicker.\n" -"\n" - diff --git a/examples/i18n_class_example/main.py b/examples/i18n_class_example/main.py deleted file mode 100644 index 23794f281..000000000 --- a/examples/i18n_class_example/main.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -In this example you will learn how to adapt your bot to different languages -Using built-in class I18N. - -You need to install babel package 'https://pypi.org/project/Babel/' -Babel provides a command-line interface for working with message catalogs -After installing babel package you have a script called 'pybabel' -Too see all the commands open terminal and type 'pybabel --help' -Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html' - -Create a directory 'locales' where our translations will be stored - -First we need to extract texts: - pybabel extract -o locales/{domain_name}.pot --input-dirs . -{domain_name}.pot - is the file where all translations are saved -The name of this file should be the same as domain which you pass to I18N class -In this example domain_name will be 'messages' - -For gettext (singular texts) we use '_' alias and it works perfect -You may also you some alias for ngettext (plural texts) but you can face with a problem that -your plural texts are not being extracted -That is because by default 'pybabel extract' recognizes the following keywords: - _, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_ -To add your own keyword you can use '-k' flag -In this example for 'ngettext' i will assign double underscore alias '__' - -Full command with pluralization support will look so: - pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . - -Then create directories with translations (get list of all locales: 'pybabel --list-locales'): - pybabel init -i locales/{domain_name}.pot -d locales -l en - pybabel init -i locales/{domain_name}.pot -d locales -l ru - pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn - -Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po -After you translated all the texts you need to compile .po files: - pybabel compile -d locales - -When you delete/update your texts you also need to update them in .po files: - pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . - pybabel update -i locales/{domain_name}.pot -d locales - - translate - pybabel compile -d locales - -If you have any exceptions check: - - you have installed babel - - translations are ready, so you just compiled it - - in the commands above you replaced {domain_name} to messages - - you are writing commands from correct path in terminal -""" - -from functools import wraps -import keyboards -from telebot import TeleBot, types, custom_filters -from telebot.storage.memory_storage import StateMemoryStorage -from i18n_class import I18N - -storage = StateMemoryStorage() -bot = TeleBot("", state_storage=storage) - -i18n = I18N(translations_path='locales', domain_name='messages') -_ = i18n.gettext # for singular translations -__ = i18n.ngettext # for plural translations - -# These are example storages, do not use it in a production development -users_lang = {} -users_clicks = {} - - -def get_user_language(func): - """ - This decorator will pass to your handler current user's language - """ - - @wraps(func) - def inner(*args, **kwargs): - obj = args[0] - kwargs.update(lang=users_lang.get(obj.from_user.id, 'en')) - return func(*args, **kwargs) - - return inner - - -@bot.message_handler(commands='start') -@get_user_language -def start_handler(message: types.Message, lang): - text = _("Hello, {user_fist_name}!\n" - "This is the example of multilanguage bot.\n" - "Available commands:\n\n" - "/lang - change your language\n" - "/plural - pluralization example", lang=lang) - - # remember don't use f string for interpolation, use .format method instead - text = text.format(user_fist_name=message.from_user.first_name) - bot.send_message(message.from_user.id, text) - - -@bot.message_handler(commands='lang') -def change_language_handler(message: types.Message): - bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang", - reply_markup=keyboards.languages_keyboard()) - - -@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(contains=['en', 'ru', 'uz_Latn'])) -def language_handler(call: types.CallbackQuery): - lang = call.data - users_lang[call.from_user.id] = lang - - bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id) - bot.delete_state(call.from_user.id) - - -@bot.message_handler(commands='plural') -@get_user_language -def pluralization_handler(message: types.Message, lang): - if not users_clicks.get(message.from_user.id): - users_clicks[message.from_user.id] = 0 - clicks = users_clicks[message.from_user.id] - - text = __( - singular="You have {number} click", - plural="You have {number} clicks", - n=clicks, - lang=lang - ) - text = _("This is clicker.\n\n", lang=lang) + text.format(number=clicks) - bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_, lang)) - - -@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(equals='click')) -@get_user_language -def click_handler(call: types.CallbackQuery, lang): - if not users_clicks.get(call.from_user.id): - users_clicks[call.from_user.id] = 1 - else: - users_clicks[call.from_user.id] += 1 - - clicks = users_clicks[call.from_user.id] - - text = __( - singular="You have {number} click", - plural="You have {number} clicks", - n=clicks, - lang=lang - ) - text = _("This is clicker.\n\n", lang=lang) + text.format(number=clicks) - bot.edit_message_text(text, call.from_user.id, call.message.message_id, - reply_markup=keyboards.clicker_keyboard(_, lang)) - - -if __name__ == '__main__': - bot.add_custom_filter(custom_filters.TextMatchFilter()) - bot.infinity_polling() From ab64e174646c7829e9c2934b7c70cfb8110c0408 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 23 Apr 2022 11:48:58 +0500 Subject: [PATCH 0942/1808] added new i18n class based middleware --- .../i18n_class_based/i18n_base_midddleware.py | 121 ++++++++++ .../class_based/i18n_class_based/keyboards.py | 34 +++ .../locales/en/LC_MESSAGES/messages.po | 81 +++++++ .../locales/ru/LC_MESSAGES/messages.po | 82 +++++++ .../locales/uz_Latn/LC_MESSAGES/messages.po | 80 +++++++ .../class_based/i18n_class_based/main.py | 211 ++++++++++++++++++ 6 files changed, 609 insertions(+) create mode 100644 examples/middleware/class_based/i18n_class_based/i18n_base_midddleware.py create mode 100644 examples/middleware/class_based/i18n_class_based/keyboards.py create mode 100644 examples/middleware/class_based/i18n_class_based/locales/en/LC_MESSAGES/messages.po create mode 100644 examples/middleware/class_based/i18n_class_based/locales/ru/LC_MESSAGES/messages.po create mode 100644 examples/middleware/class_based/i18n_class_based/locales/uz_Latn/LC_MESSAGES/messages.po create mode 100644 examples/middleware/class_based/i18n_class_based/main.py diff --git a/examples/middleware/class_based/i18n_class_based/i18n_base_midddleware.py b/examples/middleware/class_based/i18n_class_based/i18n_base_midddleware.py new file mode 100644 index 000000000..43d7b65f8 --- /dev/null +++ b/examples/middleware/class_based/i18n_class_based/i18n_base_midddleware.py @@ -0,0 +1,121 @@ +import gettext +import os +import threading + +from telebot.handler_backends import BaseMiddleware + +try: + from babel.support import LazyProxy + + babel_imported = True +except ImportError: + babel_imported = False + + +class I18N(BaseMiddleware): + """ + This middleware provides high-level tool for internationalization + It is based on gettext util. + """ + + context_lang = threading.local() + + def __init__(self, translations_path, domain_name: str): + super().__init__() + self.update_types = self.process_update_types() + + self.path = translations_path + self.domain = domain_name + self.translations = self.find_translations() + + @property + def available_translations(self): + return list(self.translations) + + def gettext(self, text: str, lang: str = None): + """ + Singular translations + """ + + if lang is None: + lang = self.context_lang.language + + if lang not in self.translations: + return text + + translator = self.translations[lang] + return translator.gettext(text) + + def ngettext(self, singular: str, plural: str, lang: str = None, n=1): + """ + Plural translations + """ + if lang is None: + lang = self.context_lang.language + + if lang not in self.translations: + if n == 1: + return singular + return plural + + translator = self.translations[lang] + return translator.ngettext(singular, plural, n) + + def lazy_gettext(self, text: str, lang: str = None): + if not babel_imported: + raise RuntimeError('babel module is not imported. Check that you installed it.') + return LazyProxy(self.gettext, text, lang, enable_cache=False) + + def lazy_ngettext(self, singular: str, plural: str, lang: str = None, n=1): + if not babel_imported: + raise RuntimeError('babel module is not imported. Check that you installed it.') + return LazyProxy(self.ngettext, singular, plural, lang, n, enable_cache=False) + + def get_user_language(self, obj): + """ + You need to override this method and return user language + """ + raise NotImplementedError + + def process_update_types(self) -> list: + """ + You need to override this method and return any update types which you want to be processed + """ + raise NotImplementedError + + def pre_process(self, message, data): + """ + context language variable will be set each time when update from 'process_update_types' comes + value is the result of 'get_user_language' method + """ + + self.context_lang.language = self.get_user_language(obj=message) + + def post_process(self, message, data, exception): + pass + + def find_translations(self): + """ + Looks for translations with passed 'domain' in passed 'path' + """ + if not os.path.exists(self.path): + raise RuntimeError(f"Translations directory by path: {self.path!r} was not found") + + result = {} + + for name in os.listdir(self.path): + translations_path = os.path.join(self.path, name, 'LC_MESSAGES') + + if not os.path.isdir(translations_path): + continue + + po_file = os.path.join(translations_path, self.domain + '.po') + mo_file = po_file[:-2] + 'mo' + + if os.path.isfile(po_file) and not os.path.isfile(mo_file): + raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") + + with open(mo_file, 'rb') as file: + result[name] = gettext.GNUTranslations(file) + + return result diff --git a/examples/middleware/class_based/i18n_class_based/keyboards.py b/examples/middleware/class_based/i18n_class_based/keyboards.py new file mode 100644 index 000000000..14d0473d0 --- /dev/null +++ b/examples/middleware/class_based/i18n_class_based/keyboards.py @@ -0,0 +1,34 @@ +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton + + +def languages_keyboard(): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text="English", callback_data='en'), + InlineKeyboardButton(text="Русский", callback_data='ru'), + InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn') + ] + ] + ) + + +def clicker_keyboard(_): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text=_("click"), callback_data='click'), + ] + ] + ) + + +def menu_keyboard(_): + keyboard = ReplyKeyboardMarkup(resize_keyboard=True) + keyboard.add( + KeyboardButton(text=_("My user id")), + KeyboardButton(text=_("My user name")), + KeyboardButton(text=_("My first name")) + ) + + return keyboard diff --git a/examples/middleware/class_based/i18n_class_based/locales/en/LC_MESSAGES/messages.po b/examples/middleware/class_based/i18n_class_based/locales/en/LC_MESSAGES/messages.po new file mode 100644 index 000000000..2cf8eef5d --- /dev/null +++ b/examples/middleware/class_based/i18n_class_based/locales/en/LC_MESSAGES/messages.po @@ -0,0 +1,81 @@ +# English translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-19 18:37+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "" + +#: keyboards.py:29 +msgid "My user id" +msgstr "" + +#: keyboards.py:30 +msgid "My user name" +msgstr "" + +#: keyboards.py:31 +msgid "My first name" +msgstr "" + +#: main.py:97 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example\n" +"/menu - text menu example" +msgstr "" + +#: main.py:121 +msgid "Language has been changed" +msgstr "" + +#: main.py:130 main.py:150 +#, fuzzy +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "" +msgstr[1] "" + +#: main.py:135 main.py:155 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" + +#: main.py:163 +msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot." +msgstr "" + +#: main.py:203 +msgid "Seems you confused language" +msgstr "" + +#~ msgid "" +#~ "Hello, {user_fist_name}!\n" +#~ "This is the example of multilanguage bot.\n" +#~ "Available commands:\n" +#~ "\n" +#~ "/lang - change your language\n" +#~ "/plural - pluralization example" +#~ msgstr "" + diff --git a/examples/middleware/class_based/i18n_class_based/locales/ru/LC_MESSAGES/messages.po b/examples/middleware/class_based/i18n_class_based/locales/ru/LC_MESSAGES/messages.po new file mode 100644 index 000000000..6d330b059 --- /dev/null +++ b/examples/middleware/class_based/i18n_class_based/locales/ru/LC_MESSAGES/messages.po @@ -0,0 +1,82 @@ +# Russian translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-19 18:37+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: ru\n" +"Language-Team: ru \n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "Клик" + +#: keyboards.py:29 +msgid "My user id" +msgstr "Мой user id" + +#: keyboards.py:30 +msgid "My user name" +msgstr "Мой user name" + +#: keyboards.py:31 +msgid "My first name" +msgstr "Мой first name" + +#: main.py:97 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example\n" +"/menu - text menu example" +msgstr "" +"Привет, {user_fist_name}!\n" +"Это пример мультиязычного бота.\n" +"Доступные команды:\n" +"\n" +"/lang - изменить язык\n" +"/plural - пример плюрализации\n" +"/menu - Пример текстового меню" + +#: main.py:121 +msgid "Language has been changed" +msgstr "Язык был сменён" + +#: main.py:130 main.py:150 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "У вас {number} клик" +msgstr[1] "У вас {number} клика" +msgstr[2] "У вас {number} кликов" + +#: main.py:135 main.py:155 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Это кликер.\n" +"\n" + +#: main.py:163 +msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot." +msgstr "Это пример ReplyKeyboardMarkup меню в мультиязычном боте." + +#: main.py:203 +msgid "Seems you confused language" +msgstr "Кажется, вы перепутали язык" + diff --git a/examples/middleware/class_based/i18n_class_based/locales/uz_Latn/LC_MESSAGES/messages.po b/examples/middleware/class_based/i18n_class_based/locales/uz_Latn/LC_MESSAGES/messages.po new file mode 100644 index 000000000..c5bd0004e --- /dev/null +++ b/examples/middleware/class_based/i18n_class_based/locales/uz_Latn/LC_MESSAGES/messages.po @@ -0,0 +1,80 @@ +# Uzbek (Latin) translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-19 18:37+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: uz_Latn\n" +"Language-Team: uz_Latn \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "clik" + +#: keyboards.py:29 +msgid "My user id" +msgstr "Mani user id" + +#: keyboards.py:30 +msgid "My user name" +msgstr "Mani user name" + +#: keyboards.py:31 +msgid "My first name" +msgstr "Mani first name" + +#: main.py:97 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example\n" +"/menu - text menu example" +msgstr "" +"Salom, {user_fist_name}!\n" +"Bu multilanguage bot misoli.\n" +"Mavjud buyruqlar:\n" +"\n" +"/lang - tilni ozgartirish\n" +"/plural - pluralizatsiya misoli\n" +"/menu - text menu misoli" + +#: main.py:121 +msgid "Language has been changed" +msgstr "Til ozgartirildi" + +#: main.py:130 main.py:150 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "Sizda {number}ta clik" +msgstr[1] "Sizda {number}ta clik" + +#: main.py:135 main.py:155 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Bu clicker.\n" +"\n" + +#: main.py:163 +msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot." +msgstr "Bu multilanguage bot da replykeyboardmarkup menyu misoli." + +#: main.py:203 +msgid "Seems you confused language" +msgstr "Tilni adashtirdiz" + diff --git a/examples/middleware/class_based/i18n_class_based/main.py b/examples/middleware/class_based/i18n_class_based/main.py new file mode 100644 index 000000000..66ae420ba --- /dev/null +++ b/examples/middleware/class_based/i18n_class_based/main.py @@ -0,0 +1,211 @@ +""" +In this example you will learn how to adapt your bot to different languages +Using built-in middleware I18N. + +You need to install babel package 'https://pypi.org/project/Babel/' +Babel provides a command-line interface for working with message catalogs +After installing babel package you have a script called 'pybabel' +Too see all the commands open terminal and type 'pybabel --help' +Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html' + +Create a directory 'locales' where our translations will be stored + +First we need to extract texts: + pybabel extract -o locales/{domain_name}.pot --input-dirs . +{domain_name}.pot - is the file where all translations are saved +The name of this file should be the same as domain which you pass to I18N class +In this example domain_name will be 'messages' + +For gettext (singular texts) we use '_' alias and it works perfect +You may also you some alias for ngettext (plural texts) but you can face with a problem that +your plural texts are not being extracted +That is because by default 'pybabel extract' recognizes the following keywords: + _, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_ +To add your own keyword you can use '-k' flag +In this example for 'ngettext' i will assign double underscore alias '__' + +Full command with pluralization support will look so: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . + +Then create directories with translations (get list of all locales: 'pybabel --list-locales'): + pybabel init -i locales/{domain_name}.pot -d locales -l en + pybabel init -i locales/{domain_name}.pot -d locales -l ru + pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn + +Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po +After you translated all the texts you need to compile .po files: + pybabel compile -d locales + +When you delete/update your texts you also need to update them in .po files: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . + pybabel update -i locales/{domain_name}.pot -d locales + - translate + pybabel compile -d locales + +If you have any exceptions check: + - you have installed babel + - translations are ready, so you just compiled it + - in the commands above you replaced {domain_name} to messages + - you are writing commands from correct path in terminal +""" + +import asyncio +from typing import Union + +import keyboards +from i18n_base_midddleware import I18N +from telebot import TeleBot +from telebot import types, StateMemoryStorage +from telebot.custom_filters import TextMatchFilter, TextFilter + +class I18NMiddleware(I18N): + + def process_update_types(self) -> list: + """ + Here you need to return a list of update types which you want to be processed + """ + return ['message', 'callback_query'] + + def get_user_language(self, obj: Union[types.Message, types.CallbackQuery]): + """ + This method is called when new update comes (only updates which you return in 'process_update_types' method) + Returned language will be used in 'pre_process' method of parent class + Returned language will be set to context language variable. + If you need to get translation with user's actual language you don't have to pass it manually + It will be automatically passed from context language value. + However if you need some other language you can always pass it. + """ + + user_id = obj.from_user.id + + if user_id not in users_lang: + users_lang[user_id] = 'en' + + return users_lang[user_id] + + +storage = StateMemoryStorage() +bot = TeleBot("", state_storage=storage, use_class_middlewares=True) + +i18n = I18NMiddleware(translations_path='locales', domain_name='messages') +_ = i18n.gettext # for singular translations +__ = i18n.ngettext # for plural translations + +# These are example storages, do not use it in a production development +users_lang = {} +users_clicks = {} + +@bot.message_handler(commands=['start']) +def start_handler(message: types.Message): + text = _("Hello, {user_fist_name}!\n" + "This is the example of multilanguage bot.\n" + "Available commands:\n\n" + "/lang - change your language\n" + "/plural - pluralization example\n" + "/menu - text menu example") + + # remember don't use f string for interpolation, use .format method instead + text = text.format(user_fist_name=message.from_user.first_name) + bot.send_message(message.from_user.id, text) + + +@bot.message_handler(commands=['lang']) +def change_language_handler(message: types.Message): + bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang", + reply_markup=keyboards.languages_keyboard()) + + +@bot.callback_query_handler(func=None, text=TextFilter(contains=['en', 'ru', 'uz_Latn'])) +def language_handler(call: types.CallbackQuery): + lang = call.data + users_lang[call.from_user.id] = lang + + # When you changed user language, you have to pass it manually beacause it is not changed in context + bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id) + + +@bot.message_handler(commands=['plural']) +def pluralization_handler(message: types.Message): + if not users_clicks.get(message.from_user.id): + users_clicks[message.from_user.id] = 0 + clicks = users_clicks[message.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks + ) + text = _("This is clicker.\n\n") + text.format(number=clicks) + + bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_)) + + +@bot.callback_query_handler(func=None, text=TextFilter(equals='click')) +def click_handler(call: types.CallbackQuery): + if not users_clicks.get(call.from_user.id): + users_clicks[call.from_user.id] = 1 + else: + users_clicks[call.from_user.id] += 1 + + clicks = users_clicks[call.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks + ) + text = _("This is clicker.\n\n") + text.format(number=clicks) + + bot.edit_message_text(text, call.from_user.id, call.message.message_id, + reply_markup=keyboards.clicker_keyboard(_)) + + +@bot.message_handler(commands=['menu']) +def menu_handler(message: types.Message): + text = _("This is ReplyKeyboardMarkup menu example in multilanguage bot.") + bot.send_message(message.chat.id, text, reply_markup=keyboards.menu_keyboard(_)) + + +# For lazy tranlations +# lazy gettext is used when you don't know user's locale +# It can be used for example to handle text buttons in multilanguage bot +# The actual translation will be delayed until update comes and context language is set +l_ = i18n.lazy_gettext + + +# Handlers below will handle text according to user's language +@bot.message_handler(text=l_("My user id")) +def return_user_id(message: types.Message): + bot.send_message(message.chat.id, str(message.from_user.id)) + + +@bot.message_handler(text=l_("My user name")) +def return_user_id(message: types.Message): + username = message.from_user.username + if not username: + username = '-' + bot.send_message(message.chat.id, username) + + +# You can make it case-insensitive +@bot.message_handler(text=TextFilter(equals=l_("My first name"), ignore_case=True)) +def return_user_id(message: types.Message): + bot.send_message(message.chat.id, message.from_user.first_name) + + +all_menu_texts = [] +for language in i18n.available_translations: + for menu_text in ("My user id", "My user name", "My first name"): + all_menu_texts.append(_(menu_text, language)) + + +# When user confused language. (handles all menu buttons texts) +@bot.message_handler(text=TextFilter(contains=all_menu_texts, ignore_case=True)) +def missed_message(message: types.Message): + bot.send_message(message.chat.id, _("Seems you confused language"), reply_markup=keyboards.menu_keyboard(_)) + + +if __name__ == '__main__': + bot.setup_middleware(i18n) + bot.add_custom_filter(TextMatchFilter()) + asyncio.run(bot.infinity_polling()) From b25d2846e935dfab19d611f39f8317bcaaa36cd9 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sat, 23 Apr 2022 11:53:55 +0500 Subject: [PATCH 0943/1808] TextFilter class supports case insensitiveness with lazy translations --- telebot/asyncio_filters.py | 2 +- telebot/custom_filters.py | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 98874b98e..312b290ae 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -39,7 +39,7 @@ class TextFilter: """ Advanced text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) - example of usage is in examples/custom_filters/advanced_text_filter.py + example of usage is in examples/asynchronous_telebot/custom_filters/advanced_text_filter.py """ def __init__(self, diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 1bd80b360..21a079a1b 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -69,7 +69,7 @@ def __init__(self, self.ends_with = self._check_iterable(ends_with, filter_name='ends_with') self.ignore_case = ignore_case - def _check_iterable(self, iterable, filter_name: str): + def _check_iterable(self, iterable, filter_name): if not iterable: pass elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): @@ -95,39 +95,33 @@ def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery if self.ignore_case: text = text.lower() - - if self.equals: - self.equals = self.equals.lower() - elif self.contains: - self.contains = tuple(map(str.lower, self.contains)) - elif self.starts_with: - self.starts_with = tuple(map(str.lower, self.starts_with)) - elif self.ends_with: - self.ends_with = tuple(map(str.lower, self.ends_with)) + prepare_func = lambda string: str(string).lower() + else: + prepare_func = str if self.equals: - result = self.equals == text + result = prepare_func(self.equals) == text if result: return True elif not result and not any((self.contains, self.starts_with, self.ends_with)): return False if self.contains: - result = any([i in text for i in self.contains]) + result = any([prepare_func(i) in text for i in self.contains]) if result: return True elif not result and not any((self.starts_with, self.ends_with)): return False if self.starts_with: - result = any([text.startswith(i) for i in self.starts_with]) + result = any([text.startswith(prepare_func(i)) for i in self.starts_with]) if result: return True elif not result and not self.ends_with: return False if self.ends_with: - return any([text.endswith(i) for i in self.ends_with]) + return any([text.endswith(prepare_func(i)) for i in self.ends_with]) return False From dd25432359b61f82f088feebadbae3f42473a9f2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 23 Apr 2022 15:03:54 +0300 Subject: [PATCH 0944/1808] Bot API 6.0. Deprecation fixes --- .../chat_member_example.py | 4 +- examples/chat_member_example.py | 4 +- telebot/__init__.py | 29 ++++---- telebot/apihelper.py | 31 +++------ telebot/async_telebot.py | 10 ++- telebot/asyncio_helper.py | 7 +- telebot/types.py | 67 +++++++++++++------ telebot/util.py | 13 ++-- 8 files changed, 92 insertions(+), 73 deletions(-) diff --git a/examples/asynchronous_telebot/chat_member_example.py b/examples/asynchronous_telebot/chat_member_example.py index b2f545c21..160c2eb17 100644 --- a/examples/asynchronous_telebot/chat_member_example.py +++ b/examples/asynchronous_telebot/chat_member_example.py @@ -23,8 +23,8 @@ async def my_chat_m(message: types.ChatMemberUpdated): #content_Type_service is: #'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', #'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', -#'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', -#'voice_chat_participants_invited', 'message_auto_delete_timer_changed' +#'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', +#'video_chat_participants_invited', 'message_auto_delete_timer_changed' # this handler deletes service messages @bot.message_handler(content_types=util.content_type_service) diff --git a/examples/chat_member_example.py b/examples/chat_member_example.py index 36dbfb2b1..675d956ed 100644 --- a/examples/chat_member_example.py +++ b/examples/chat_member_example.py @@ -23,8 +23,8 @@ def my_chat_m(message: types.ChatMemberUpdated): #content_Type_service is: #'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', #'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', -#'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', -#'voice_chat_participants_invited', 'message_auto_delete_timer_changed' +#'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', +#'video_chat_participants_invited', 'message_auto_delete_timer_changed' # this handler deletes service messages @bot.message_handler(content_types=util.content_type_service) diff --git a/telebot/__init__.py b/telebot/__init__.py index a33b2659f..f726bcc66 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -179,6 +179,7 @@ def user(self) -> types.User: self._user = self.get_me() return self._user + @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): """ Enable saving next step handlers (by default saving disabled) @@ -187,8 +188,6 @@ def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/s compatibility whose purpose was to enable file saving capability for handlers. And the same implementation is now available with FileHandlerBackend - Most probably this function should be deprecated in future major releases - :param delay: Delay between changes in handlers and saving :param filename: Filename of save file """ @@ -203,6 +202,7 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): self.current_states = StatePickleStorage(file_path=filename) self.current_states.create_dir() + @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): """ Enable saving reply handlers (by default saving disable) @@ -211,13 +211,12 @@ def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply compatibility whose purpose was to enable file saving capability for handlers. And the same implementation is now available with FileHandlerBackend - Most probably this function should be deprecated in future major releases - :param delay: Delay between changes in handlers and saving :param filename: Filename of save file """ self.reply_backend = FileHandlerBackend(self.reply_backend.handlers, filename, delay) + @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def disable_save_next_step_handlers(self): """ Disable saving next step handlers (by default saving disable) @@ -225,11 +224,10 @@ def disable_save_next_step_handlers(self): This function is left to keep backward compatibility whose purpose was to disable file saving capability for handlers. For the same purpose, MemoryHandlerBackend is reassigned as a new next_step_backend backend instead of FileHandlerBackend. - - Most probably this function should be deprecated in future major releases """ self.next_step_backend = MemoryHandlerBackend(self.next_step_backend.handlers) + @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def disable_save_reply_handlers(self): """ Disable saving next step handlers (by default saving disable) @@ -237,11 +235,10 @@ def disable_save_reply_handlers(self): This function is left to keep backward compatibility whose purpose was to disable file saving capability for handlers. For the same purpose, MemoryHandlerBackend is reassigned as a new reply_backend backend instead of FileHandlerBackend. - - Most probably this function should be deprecated in future major releases """ self.reply_backend = MemoryHandlerBackend(self.reply_backend.handlers) + @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): """ Load next step handlers from save file @@ -250,13 +247,12 @@ def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_fil help of FileHandlerBackend and is only recommended to use if next_step_backend was assigned as FileHandlerBackend before entering this function - Most probably this function should be deprecated in future major releases - :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ self.next_step_backend.load_handlers(filename, del_file_after_loading) + @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): """ Load reply handlers from save file @@ -265,8 +261,6 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a help of FileHandlerBackend and is only recommended to use if reply_backend was assigned as FileHandlerBackend before entering this function - Most probably this function should be deprecated in future major releases - :param filename: Filename of the file where handlers was saved :param del_file_after_loading: Is passed True, after loading save file will be deleted """ @@ -620,6 +614,7 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim :return: """ if none_stop is not None: + logger.warning("polling: none_stop parameter is deprecated. Use non_stop instead.") non_stop = none_stop if skip_pending: @@ -887,11 +882,11 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe result = apihelper.get_chat_administrators(self.token, chat_id) return [types.ChatMember.de_json(r) for r in result] + @util.deprecated(deprecation_text="Use get_chat_member_count instead") def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ This function is deprecated. Use `get_chat_member_count` instead """ - logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead.') result = apihelper.get_chat_member_count(self.token, chat_id) return result @@ -1662,6 +1657,7 @@ def send_chat_action( """ return apihelper.send_chat_action(self.token, chat_id, action, timeout) + @util.deprecated(deprecation_text="Use ban_chat_member instead") def kick_chat_member( self, chat_id: Union[int, str], user_id: int, until_date:Optional[Union[int, datetime]]=None, @@ -1669,7 +1665,6 @@ def kick_chat_member( """ This function is deprecated. Use `ban_chat_member` instead """ - logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.') return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) def ban_chat_member( @@ -1805,8 +1800,10 @@ def promote_chat_member( :return: True on success. """ - if can_manage_voice_chats and not can_manage_video_chats is None: - can_manage_video_chats = can_manage_voice_chats + if can_manage_voice_chats is not None: + logger.warning("promote_chat_member: can_manage_voice_chats parameter is deprecated. Use can_manage_video_chats instead.") + if can_manage_video_chats is None: + can_manage_video_chats = can_manage_voice_chats return apihelper.promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 118b4962f..55d6dbb51 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -47,7 +47,6 @@ ENABLE_MIDDLEWARE = False - def _get_req_session(reset=False): if SESSION_TIME_TO_LIVE: # If session TTL is set - check time passed @@ -94,20 +93,14 @@ def _make_request(token, method_name, method='get', params=None, files=None): if 'timeout' in params: read_timeout = params.pop('timeout') connect_timeout = read_timeout -# if 'connect-timeout' in params: -# connect_timeout = params.pop('connect-timeout') + 10 if 'long_polling_timeout' in params: - # For getUpdates: it's the only function with timeout parameter on the BOT API side + # For getUpdates. It's the only function with timeout parameter on the BOT API side long_polling_timeout = params.pop('long_polling_timeout') params['timeout'] = long_polling_timeout # Long polling hangs for a given time. Read timeout should be greater that long_polling_timeout read_timeout = max(long_polling_timeout + 5, read_timeout) - # Lets stop suppose that user is stupid and assume that he knows what he do... - # read_timeout = read_timeout + 10 - # connect_timeout = connect_timeout + 10 params = params or None # Set params to None if empty - result = None if RETRY_ON_ERROR and RETRY_ENGINE == 1: got_result = False @@ -134,6 +127,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): timeout=(connect_timeout, read_timeout), proxies=proxy) elif RETRY_ON_ERROR and RETRY_ENGINE == 2: http = _get_req_session() + # noinspection PyUnresolvedReferences retry_strategy = requests.packages.urllib3.util.retry.Retry( total=MAX_RETRIES, ) @@ -1146,7 +1140,6 @@ def set_chat_menu_button(token, chat_id=None, menu_button=None): payload['chat_id'] = chat_id if menu_button: payload['menu_button'] = menu_button.to_json() - return _make_request(token, method_url, params=payload, method='post') def get_chat_menu_button(token, chat_id=None): @@ -1154,7 +1147,6 @@ def get_chat_menu_button(token, chat_id=None): payload = {} if chat_id: payload['chat_id'] = chat_id - return _make_request(token, method_url, params=payload, method='post') @@ -1163,17 +1155,16 @@ def set_my_default_administrator_rights(token, rights=None, for_channels=None): payload = {} if rights: payload['rights'] = rights.to_json() - if for_channels: + if for_channels is not None: payload['for_channels'] = for_channels - return _make_request(token, method_url, params=payload, method='post') + def get_my_default_administrator_rights(token, for_channels=None): method_url = r'getMyDefaultAdministratorRights' payload = {} if for_channels: payload['for_channels'] = for_channels - return _make_request(token, method_url, params=payload, method='post') @@ -1326,7 +1317,6 @@ def send_game( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content - return _make_request(token, method_url, params=payload) @@ -1573,7 +1563,6 @@ def create_new_sticker_set( contains_masks=None, mask_position=None, webm_sticker=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} - stype = None if png_sticker: stype = 'png_sticker' elif webm_sticker: @@ -1598,7 +1587,6 @@ def create_new_sticker_set( def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} - stype = None if png_sticker: stype = 'png_sticker' elif webm_sticker: @@ -1705,7 +1693,6 @@ def _convert_list_json_serializable(results): return '[' + ret + ']' - def _convert_markup(markup): if isinstance(markup, types.JsonSerializable): return markup.to_json() @@ -1778,7 +1765,8 @@ def __init__(self, msg, function_name, result): super(ApiException, self).__init__("A request to the Telegram API was unsuccessful. {0}".format(msg)) self.function_name = function_name self.result = result - + + class ApiHTTPException(ApiException): """ This class represents an Exception thrown when a call to the @@ -1790,7 +1778,8 @@ def __init__(self, function_name, result): .format(result.status_code, result.reason, result.text.encode('utf8')), function_name, result) - + + class ApiInvalidJSONException(ApiException): """ This class represents an Exception thrown when a call to the @@ -1802,7 +1791,8 @@ def __init__(self, function_name, result): .format(result.text.encode('utf8')), function_name, result) - + + class ApiTelegramException(ApiException): """ This class represents an Exception thrown when a Telegram API returns error code. @@ -1816,4 +1806,3 @@ def __init__(self, function_name, result, result_json): self.result_json = result_json self.error_code = result_json['error_code'] self.description = result_json['description'] - diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a2fdd7575..6496e1b9d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -149,6 +149,7 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= :return: """ if none_stop is not None: + logger.warning("polling: none_stop parameter is deprecated. Use non_stop instead.") non_stop = none_stop if skip_pending: @@ -1919,7 +1920,9 @@ async def send_sticker( """ if data and not(sticker): # function typo miss compatibility + logger.warning("send_sticker: data parameter is deprecated. Use sticker instead.") sticker = data + return types.Message.de_json( await asyncio_helper.send_data( self.token, chat_id, sticker, 'sticker', @@ -1970,6 +1973,7 @@ async def send_video( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode if data and not(video): # function typo miss compatibility + logger.warning("send_sticker: data parameter is deprecated. Use video instead.") video = data return types.Message.de_json( @@ -2423,8 +2427,10 @@ async def promote_chat_member( :return: True on success. """ - if can_manage_voice_chats and not can_manage_video_chats is None: - can_manage_video_chats = can_manage_voice_chats + if can_manage_voice_chats is not None: + logger.warning("promote_chat_member: can_manage_voice_chats parameter is deprecated. Use can_manage_video_chats instead.") + if can_manage_video_chats is None: + can_manage_video_chats = can_manage_voice_chats return await asyncio_helper.promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index eae41a50f..40a4ca976 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -11,7 +11,6 @@ from datetime import datetime -import telebot from telebot import util, logger @@ -26,8 +25,6 @@ LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates) REQUEST_TIMEOUT = 10 MAX_RETRIES = 3 -logger = telebot.logger - REQUEST_LIMIT = 50 @@ -1136,7 +1133,7 @@ async def set_my_default_administrator_rights(token, rights=None, for_channels=N payload = {} if rights: payload['rights'] = rights.to_json() - if for_channels: + if for_channels is not None: payload['for_channels'] = for_channels return await _process_request(token, method_url, params=payload, method='post') @@ -1543,7 +1540,6 @@ async def create_new_sticker_set( contains_masks=None, mask_position=None, webm_sticker=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} - stype = None if png_sticker: stype = 'png_sticker' elif webm_sticker: @@ -1568,7 +1564,6 @@ async def create_new_sticker_set( async def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): method_url = 'addStickerToSet' payload = {'user_id': user_id, 'name': name, 'emojis': emojis} - stype = None if png_sticker: stype = 'png_sticker' elif webm_sticker: diff --git a/telebot/types.py b/telebot/types.py index c659fd4d7..ef014bfcc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -473,20 +473,20 @@ def de_json(cls, json_string): 'proximity_alert_triggered']) content_type = 'proximity_alert_triggered' if 'video_chat_scheduled' in obj: - opts['video_chat_scheduled'] = VoiceChatScheduled.de_json(obj['video_chat_scheduled']) - opts['voice_chat_scheduled'] = opts['video_chat_scheduled'] + opts['video_chat_scheduled'] = VideoChatScheduled.de_json(obj['video_chat_scheduled']) + opts['voice_chat_scheduled'] = opts['video_chat_scheduled'] # deprecated, for backward compatibility content_type = 'video_chat_scheduled' if 'video_chat_started' in obj: - opts['video_chat_started'] = VoiceChatStarted.de_json(obj['video_chat_started']) - opts['voice_chat_started'] = opts['video_chat_started'] + opts['video_chat_started'] = VideoChatStarted.de_json(obj['video_chat_started']) + opts['voice_chat_started'] = opts['video_chat_started'] # deprecated, for backward compatibility content_type = 'video_chat_started' if 'video_chat_ended' in obj: - opts['video_chat_ended'] = VoiceChatEnded.de_json(obj['video_chat_ended']) - opts['voice_chat_ended'] = opts['video_chat_ended'] + opts['video_chat_ended'] = VideoChatEnded.de_json(obj['video_chat_ended']) + opts['voice_chat_ended'] = opts['video_chat_ended'] # deprecated, for backward compatibility content_type = 'video_chat_ended' if 'video_chat_participants_invited' in obj: - opts['video_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['video_chat_participants_invited']) - opts['voice_chat_participants_invited'] = opts['video_chat_participants_invited'] + opts['video_chat_participants_invited'] = VideoChatParticipantsInvited.de_json(obj['video_chat_participants_invited']) + opts['voice_chat_participants_invited'] = opts['video_chat_participants_invited'] # deprecated, for backward compatibility content_type = 'video_chat_participants_invited' if 'web_app_data' in obj: opts['web_app_data'] = WebAppData.de_json(obj['web_app_data']) @@ -1304,7 +1304,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_add_web_page_previews: bool = can_add_web_page_previews self.can_manage_chat: bool = can_manage_chat self.can_manage_video_chats: bool = can_manage_video_chats - self.can_manage_voice_chats: bool = self.can_manage_video_chats + self.can_manage_voice_chats: bool = self.can_manage_video_chats # deprecated, for backward compatibility self.until_date: int = until_date @@ -1743,11 +1743,14 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, inline_message_id): + def __init__(self, inline_message_id=None): self.inline_message_id = inline_message_id def to_dict(self): - return {'inline_message_id': self.inline_message_id} + json_dict = {} + if self.inline_message_id: + json_dict['inline_message_id'] = self.inline_message_id + return json_dict @@ -2897,7 +2900,7 @@ def __init__(self, traveler, watcher, distance, **kwargs): self.distance: int = distance -class VoiceChatStarted(JsonDeserializable): +class VideoChatStarted(JsonDeserializable): @classmethod def de_json(cls, json_string): return cls() @@ -2909,8 +2912,13 @@ def __init__(self): """ pass +class VoiceChatStarted(VideoChatStarted): + def __init__(self): + logger.warning('VoiceChatStarted is deprecated. Use VideoChatStarted instead.') + super().__init__() + -class VoiceChatScheduled(JsonDeserializable): +class VideoChatScheduled(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -2920,8 +2928,13 @@ def de_json(cls, json_string): def __init__(self, start_date, **kwargs): self.start_date: int = start_date +class VoiceChatScheduled(VideoChatScheduled): + def __init__(self, *args, **kwargs): + logger.warning('VoiceChatScheduled is deprecated. Use VideoChatScheduled instead.') + super().__init__(*args, **kwargs) -class VoiceChatEnded(JsonDeserializable): + +class VideoChatEnded(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -2931,8 +2944,14 @@ def de_json(cls, json_string): def __init__(self, duration, **kwargs): self.duration: int = duration +class VoiceChatEnded(VideoChatEnded): + def __init__(self, *args, **kwargs): + logger.warning('VoiceChatEnded is deprecated. Use VideoChatEnded instead.') + super().__init__(*args, **kwargs) + -class VoiceChatParticipantsInvited(JsonDeserializable): + +class VideoChatParticipantsInvited(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -2944,6 +2963,11 @@ def de_json(cls, json_string): def __init__(self, users=None, **kwargs): self.users: List[User] = users +class VoiceChatParticipantsInvited(VideoChatParticipantsInvited): + def __init__(self, *args, **kwargs): + logger.warning('VoiceChatParticipantsInvited is deprecated. Use VideoChatParticipantsInvited instead.') + super().__init__(*args, **kwargs) + class MessageAutoDeleteTimerChanged(JsonDeserializable): @classmethod @@ -3042,7 +3066,7 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, self.can_pin_messages: bool = can_pin_messages def to_dict(self): - data = { + json_dict = { 'is_anonymous': self.is_anonymous, 'can_manage_chat': self.can_manage_chat, 'can_delete_messages': self.can_delete_messages, @@ -3051,11 +3075,14 @@ def to_dict(self): 'can_promote_members': self.can_promote_members, 'can_change_info': self.can_change_info, 'can_invite_users': self.can_invite_users, - 'can_post_messages': self.can_post_messages, - 'can_edit_messages': self.can_edit_messages, - 'can_pin_messages': self.can_pin_messages } - return data + if 'can_post_messages' is not None: + json_dict['can_post_messages'] = self.can_post_messages + if 'can_edit_messages' is not None: + json_dict['can_edit_messages'] = self.can_edit_messages + if 'can_pin_messages' is not None: + json_dict['can_pin_messages'] = self.can_pin_messages + return json_dict def to_json(self): return json.dumps(self.to_dict()) diff --git a/telebot/util.py b/telebot/util.py index 79ed2b1b6..f116d9e3b 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -40,8 +40,8 @@ content_type_service = [ 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', - 'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended', - 'voice_chat_participants_invited', 'message_auto_delete_timer_changed' + 'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', + 'video_chat_participants_invited', 'message_auto_delete_timer_changed' ] update_types = [ @@ -449,17 +449,22 @@ def generate_random_token(): return ''.join(random.sample(string.ascii_letters, 16)) -def deprecated(warn: bool=True, alternative: Optional[Callable]=None): +def deprecated(warn: bool=True, alternative: Optional[Callable]=None, deprecation_text=None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. :param warn: If True a warning is logged else an info :param alternative: The new function to use instead + :param deprecation_text: Custom deprecation text """ def decorator(function): def wrapper(*args, **kwargs): - info = f"`{function.__name__}` is deprecated." + (f" Use `{alternative.__name__}` instead" if alternative else "") + info = f"`{function.__name__}` is deprecated." + if alternative: + info += f" Use `{alternative.__name__}` instead" + if deprecation_text: + info += " " + deprecation_text if not warn: logger.info(info) else: From e444bc2a0b551be62d8b6b31929430f1eb1c56ee Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 23 Apr 2022 16:27:53 +0300 Subject: [PATCH 0945/1808] Payment example fix --- examples/payments_example.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/payments_example.py b/examples/payments_example.py index c8dbfc594..0b931c506 100644 --- a/examples/payments_example.py +++ b/examples/payments_example.py @@ -38,21 +38,20 @@ def command_pay(message): "Real cards won't work with me, no money will be debited from your account." " Use this test card number to pay for your Time Machine: `4242 4242 4242 4242`" "\n\nThis is your demo invoice:", parse_mode='Markdown') - bot.send_invoice(message.chat.id, title='Working Time Machine', - description='Want to visit your great-great-great-grandparents?' - ' Make a fortune at the races?' - ' Shake hands with Hammurabi and take a stroll in the Hanging Gardens?' - ' Order our Working Time Machine today!', - provider_token=provider_token, - currency='usd', + bot.send_invoice( + message.chat.id, #chat_id + 'Working Time Machine', #title + ' Want to visit your great-great-great-grandparents? Make a fortune at the races? Shake hands with Hammurabi and take a stroll in the Hanging Gardens? Order our Working Time Machine today!', #description + 'HAPPY FRIDAYS COUPON', #invoice_payload + provider_token, #provider_token + 'usd', #currency + prices, #prices photo_url='http://erkelzaar.tsudao.com/models/perrotta/TIME_MACHINE.jpg', photo_height=512, # !=0/None or picture won't be shown photo_width=512, photo_size=512, is_flexible=False, # True If you need to set up Shipping Fee - prices=prices, - start_parameter='time-machine-example', - invoice_payload='HAPPY FRIDAYS COUPON') + start_parameter='time-machine-example') @bot.shipping_query_handler(func=lambda query: True) From 9b1b324ab4d160bdaef03f30673e0de777debe97 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 23 Apr 2022 16:33:59 +0300 Subject: [PATCH 0946/1808] Bump version to 4.5.0 --- docs/source/conf.py | 4 ++-- setup.py | 3 ++- telebot/version.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 104db8f3e..745c32868 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -17,12 +17,12 @@ # -- Project information ----------------------------------------------------- -project = 'pyTelegramBotAPI' +project = 'pyTelegramBotAPI Documentation' copyright = '2022, coder2020official' author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.4.1' +release = '4.5.0' # -- General configuration --------------------------------------------------- diff --git a/setup.py b/setup.py index 0ee43843f..eb1c9ee65 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,8 @@ def read(filename): extras_require={ 'json': 'ujson', 'PIL': 'Pillow', - 'redis': 'redis>=3.4.1' + 'redis': 'redis>=3.4.1', + 'aiohttp': 'aiohttp', }, classifiers=[ 'Development Status :: 5 - Production/Stable', diff --git a/telebot/version.py b/telebot/version.py index e7fe97a98..75ff2fa93 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.4.1' +__version__ = '4.5.0' From ad5b92b6504384873c5f49e3dc2367412b60186a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 23 Apr 2022 19:35:38 +0300 Subject: [PATCH 0947/1808] Remove incorrect deprecation --- telebot/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f726bcc66..1a6766732 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -179,7 +179,6 @@ def user(self) -> types.User: self._user = self.get_me() return self._user - @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): """ Enable saving next step handlers (by default saving disabled) @@ -202,7 +201,6 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): self.current_states = StatePickleStorage(file_path=filename) self.current_states.create_dir() - @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): """ Enable saving reply handlers (by default saving disable) @@ -216,7 +214,6 @@ def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply """ self.reply_backend = FileHandlerBackend(self.reply_backend.handlers, filename, delay) - @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def disable_save_next_step_handlers(self): """ Disable saving next step handlers (by default saving disable) @@ -227,7 +224,6 @@ def disable_save_next_step_handlers(self): """ self.next_step_backend = MemoryHandlerBackend(self.next_step_backend.handlers) - @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def disable_save_reply_handlers(self): """ Disable saving next step handlers (by default saving disable) @@ -238,7 +234,6 @@ def disable_save_reply_handlers(self): """ self.reply_backend = MemoryHandlerBackend(self.reply_backend.handlers) - @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): """ Load next step handlers from save file @@ -252,7 +247,6 @@ def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_fil """ self.next_step_backend.load_handlers(filename, del_file_after_loading) - @util.deprecated(warn=False, deprecation_text="Most probably this function will be removed in future major releases") def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): """ Load reply handlers from save file From a54b21cb508bd5f664cda2f091869d77a5bda52e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 23 Apr 2022 20:19:25 +0300 Subject: [PATCH 0948/1808] type --- telebot/async_telebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6496e1b9d..fedad40c6 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1501,11 +1501,11 @@ async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types. result = await asyncio_helper.get_chat_administrators(self.token, chat_id) return [types.ChatMember.de_json(r) for r in result] + @util.deprecated(deprecation_text="Use get_chat_member_count instead") async def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ This function is deprecated. Use `get_chat_member_count` instead """ - logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead.') result = await asyncio_helper.get_chat_member_count(self.token, chat_id) return result @@ -1513,7 +1513,7 @@ async def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ Use this method to get the number of members in a chat. Returns Int on success. - Telegram documentation: https://core.telegram.org/bots/api#getchatmemberscount + Telegram documentation: https://core.telegram.org/bots/api#getchatmembercount :param chat_id: :return: From 3b386965ea212e687d48a50b563bc18db35a48a5 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sun, 24 Apr 2022 11:52:01 +0500 Subject: [PATCH 0949/1808] sync middleware examples separated into two folders --- .../i18n_base_midddleware.py | 0 .../{i18n_class_based => i18n_middleware}/keyboards.py | 0 .../locales/en/LC_MESSAGES/messages.po | 0 .../locales/ru/LC_MESSAGES/messages.po | 0 .../locales/uz_Latn/LC_MESSAGES/messages.po | 0 .../class_based/{i18n_class_based => i18n_middleware}/main.py | 0 examples/middleware/{ => function_based}/i18n.py | 0 examples/middleware/{ => function_based}/session.py | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename examples/middleware/class_based/{i18n_class_based => i18n_middleware}/i18n_base_midddleware.py (100%) rename examples/middleware/class_based/{i18n_class_based => i18n_middleware}/keyboards.py (100%) rename examples/middleware/class_based/{i18n_class_based => i18n_middleware}/locales/en/LC_MESSAGES/messages.po (100%) rename examples/middleware/class_based/{i18n_class_based => i18n_middleware}/locales/ru/LC_MESSAGES/messages.po (100%) rename examples/middleware/class_based/{i18n_class_based => i18n_middleware}/locales/uz_Latn/LC_MESSAGES/messages.po (100%) rename examples/middleware/class_based/{i18n_class_based => i18n_middleware}/main.py (100%) rename examples/middleware/{ => function_based}/i18n.py (100%) rename examples/middleware/{ => function_based}/session.py (100%) diff --git a/examples/middleware/class_based/i18n_class_based/i18n_base_midddleware.py b/examples/middleware/class_based/i18n_middleware/i18n_base_midddleware.py similarity index 100% rename from examples/middleware/class_based/i18n_class_based/i18n_base_midddleware.py rename to examples/middleware/class_based/i18n_middleware/i18n_base_midddleware.py diff --git a/examples/middleware/class_based/i18n_class_based/keyboards.py b/examples/middleware/class_based/i18n_middleware/keyboards.py similarity index 100% rename from examples/middleware/class_based/i18n_class_based/keyboards.py rename to examples/middleware/class_based/i18n_middleware/keyboards.py diff --git a/examples/middleware/class_based/i18n_class_based/locales/en/LC_MESSAGES/messages.po b/examples/middleware/class_based/i18n_middleware/locales/en/LC_MESSAGES/messages.po similarity index 100% rename from examples/middleware/class_based/i18n_class_based/locales/en/LC_MESSAGES/messages.po rename to examples/middleware/class_based/i18n_middleware/locales/en/LC_MESSAGES/messages.po diff --git a/examples/middleware/class_based/i18n_class_based/locales/ru/LC_MESSAGES/messages.po b/examples/middleware/class_based/i18n_middleware/locales/ru/LC_MESSAGES/messages.po similarity index 100% rename from examples/middleware/class_based/i18n_class_based/locales/ru/LC_MESSAGES/messages.po rename to examples/middleware/class_based/i18n_middleware/locales/ru/LC_MESSAGES/messages.po diff --git a/examples/middleware/class_based/i18n_class_based/locales/uz_Latn/LC_MESSAGES/messages.po b/examples/middleware/class_based/i18n_middleware/locales/uz_Latn/LC_MESSAGES/messages.po similarity index 100% rename from examples/middleware/class_based/i18n_class_based/locales/uz_Latn/LC_MESSAGES/messages.po rename to examples/middleware/class_based/i18n_middleware/locales/uz_Latn/LC_MESSAGES/messages.po diff --git a/examples/middleware/class_based/i18n_class_based/main.py b/examples/middleware/class_based/i18n_middleware/main.py similarity index 100% rename from examples/middleware/class_based/i18n_class_based/main.py rename to examples/middleware/class_based/i18n_middleware/main.py diff --git a/examples/middleware/i18n.py b/examples/middleware/function_based/i18n.py similarity index 100% rename from examples/middleware/i18n.py rename to examples/middleware/function_based/i18n.py diff --git a/examples/middleware/session.py b/examples/middleware/function_based/session.py similarity index 100% rename from examples/middleware/session.py rename to examples/middleware/function_based/session.py From 24ae38cca64ebb9d97e11b1955c8e29d17f0ae1d Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Sun, 24 Apr 2022 12:51:01 +0500 Subject: [PATCH 0950/1808] added function based middleware i18n example --- .../function_based/i18n_gettext/i18n_class.py | 77 ++++++++++ .../function_based/i18n_gettext/keyboards.py | 23 +++ .../locales/en/LC_MESSAGES/messages.po | 50 +++++++ .../locales/ru/LC_MESSAGES/messages.po | 59 ++++++++ .../locales/uz_Latn/LC_MESSAGES/messages.po | 57 ++++++++ .../function_based/i18n_gettext/main.py | 134 ++++++++++++++++++ 6 files changed, 400 insertions(+) create mode 100644 examples/middleware/function_based/i18n_gettext/i18n_class.py create mode 100644 examples/middleware/function_based/i18n_gettext/keyboards.py create mode 100644 examples/middleware/function_based/i18n_gettext/locales/en/LC_MESSAGES/messages.po create mode 100644 examples/middleware/function_based/i18n_gettext/locales/ru/LC_MESSAGES/messages.po create mode 100644 examples/middleware/function_based/i18n_gettext/locales/uz_Latn/LC_MESSAGES/messages.po create mode 100644 examples/middleware/function_based/i18n_gettext/main.py diff --git a/examples/middleware/function_based/i18n_gettext/i18n_class.py b/examples/middleware/function_based/i18n_gettext/i18n_class.py new file mode 100644 index 000000000..68b3ffd4c --- /dev/null +++ b/examples/middleware/function_based/i18n_gettext/i18n_class.py @@ -0,0 +1,77 @@ +import gettext +import os +import threading + + +class I18N: + """ + This class provides high-level tool for internationalization + It is based on gettext util. + """ + + context_lang = threading.local() + + def __init__(self, translations_path, domain_name: str): + self.path = translations_path + self.domain = domain_name + self.translations = self.find_translations() + + @property + def available_translations(self): + return list(self.translations) + + def gettext(self, text: str, lang: str = None): + """ + Singular translations + """ + + if lang is None: + lang = self.context_lang.language + + if lang not in self.translations: + return text + + translator = self.translations[lang] + return translator.gettext(text) + + def ngettext(self, singular: str, plural: str, lang: str = None, n=1): + """ + Plural translations + """ + if lang is None: + lang = self.context_lang.language + + if lang not in self.translations: + if n == 1: + return singular + return plural + + translator = self.translations[lang] + return translator.ngettext(singular, plural, n) + + + def find_translations(self): + """ + Looks for translations with passed 'domain' in passed 'path' + """ + if not os.path.exists(self.path): + raise RuntimeError(f"Translations directory by path: {self.path!r} was not found") + + result = {} + + for name in os.listdir(self.path): + translations_path = os.path.join(self.path, name, 'LC_MESSAGES') + + if not os.path.isdir(translations_path): + continue + + po_file = os.path.join(translations_path, self.domain + '.po') + mo_file = po_file[:-2] + 'mo' + + if os.path.isfile(po_file) and not os.path.isfile(mo_file): + raise FileNotFoundError(f"Translations for: {name!r} were not compiled!") + + with open(mo_file, 'rb') as file: + result[name] = gettext.GNUTranslations(file) + + return result diff --git a/examples/middleware/function_based/i18n_gettext/keyboards.py b/examples/middleware/function_based/i18n_gettext/keyboards.py new file mode 100644 index 000000000..cdda2ae29 --- /dev/null +++ b/examples/middleware/function_based/i18n_gettext/keyboards.py @@ -0,0 +1,23 @@ +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton + + +def languages_keyboard(): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text="English", callback_data='en'), + InlineKeyboardButton(text="Русский", callback_data='ru'), + InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn') + ] + ] + ) + + +def clicker_keyboard(_): + return InlineKeyboardMarkup( + keyboard=[ + [ + InlineKeyboardButton(text=_("click"), callback_data='click'), + ] + ] + ) diff --git a/examples/middleware/function_based/i18n_gettext/locales/en/LC_MESSAGES/messages.po b/examples/middleware/function_based/i18n_gettext/locales/en/LC_MESSAGES/messages.po new file mode 100644 index 000000000..5931c28bf --- /dev/null +++ b/examples/middleware/function_based/i18n_gettext/locales/en/LC_MESSAGES/messages.po @@ -0,0 +1,50 @@ +# English translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-18 17:54+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: en\n" +"Language-Team: en \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "" + +#: main.py:78 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example" +msgstr "" + +#: main.py:102 +msgid "Language has been changed" +msgstr "" + +#: main.py:114 +#, fuzzy +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "" +msgstr[1] "" + +#: main.py:120 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" \ No newline at end of file diff --git a/examples/middleware/function_based/i18n_gettext/locales/ru/LC_MESSAGES/messages.po b/examples/middleware/function_based/i18n_gettext/locales/ru/LC_MESSAGES/messages.po new file mode 100644 index 000000000..79db9b536 --- /dev/null +++ b/examples/middleware/function_based/i18n_gettext/locales/ru/LC_MESSAGES/messages.po @@ -0,0 +1,59 @@ +# Russian translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-18 17:54+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: ru\n" +"Language-Team: ru \n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "Клик" + +#: main.py:78 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example" +msgstr "" +"Привет, {user_fist_name}!\n" +"Это пример мультиязычного бота.\n" +"Доступные команды:\n" +"\n" +"/lang - изменить язык\n" +"/plural - пример плюрализации" + +#: main.py:102 +msgid "Language has been changed" +msgstr "Язык был сменён" + +#: main.py:114 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "У вас {number} клик" +msgstr[1] "У вас {number} клика" +msgstr[2] "У вас {number} кликов" + +#: main.py:120 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Это кликер.\n" +"\n" \ No newline at end of file diff --git a/examples/middleware/function_based/i18n_gettext/locales/uz_Latn/LC_MESSAGES/messages.po b/examples/middleware/function_based/i18n_gettext/locales/uz_Latn/LC_MESSAGES/messages.po new file mode 100644 index 000000000..ce7b502e0 --- /dev/null +++ b/examples/middleware/function_based/i18n_gettext/locales/uz_Latn/LC_MESSAGES/messages.po @@ -0,0 +1,57 @@ +# Uzbek (Latin) translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-02-18 17:54+0500\n" +"PO-Revision-Date: 2022-02-18 16:22+0500\n" +"Last-Translator: FULL NAME \n" +"Language: uz_Latn\n" +"Language-Team: uz_Latn \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: keyboards.py:20 +msgid "click" +msgstr "clik" + +#: main.py:78 +msgid "" +"Hello, {user_fist_name}!\n" +"This is the example of multilanguage bot.\n" +"Available commands:\n" +"\n" +"/lang - change your language\n" +"/plural - pluralization example" +msgstr "" +"Salom, {user_fist_name}!\n" +"Bu multilanguage bot misoli.\n" +"Mavjud buyruqlar:\n" +"\n" +"/lang - tilni ozgartirish\n" +"/plural - pluralizatsiya misoli" + +#: main.py:102 +msgid "Language has been changed" +msgstr "Til ozgartirildi" + +#: main.py:114 +msgid "You have {number} click" +msgid_plural "You have {number} clicks" +msgstr[0] "Sizda {number}ta clik" +msgstr[1] "Sizda {number}ta clik" + +#: main.py:120 +msgid "" +"This is clicker.\n" +"\n" +msgstr "" +"Bu clicker.\n" +"\n" diff --git a/examples/middleware/function_based/i18n_gettext/main.py b/examples/middleware/function_based/i18n_gettext/main.py new file mode 100644 index 000000000..c40b00cab --- /dev/null +++ b/examples/middleware/function_based/i18n_gettext/main.py @@ -0,0 +1,134 @@ +""" +In this example you will learn how to adapt your bot to different languages +Using built-in class I18N. +You need to install babel package 'https://pypi.org/project/Babel/' +Babel provides a command-line interface for working with message catalogs +After installing babel package you have a script called 'pybabel' +Too see all the commands open terminal and type 'pybabel --help' +Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html' +Create a directory 'locales' where our translations will be stored +First we need to extract texts: + pybabel extract -o locales/{domain_name}.pot --input-dirs . +{domain_name}.pot - is the file where all translations are saved +The name of this file should be the same as domain which you pass to I18N class +In this example domain_name will be 'messages' +For gettext (singular texts) we use '_' alias and it works perfect +You may also you some alias for ngettext (plural texts) but you can face with a problem that +your plural texts are not being extracted +That is because by default 'pybabel extract' recognizes the following keywords: + _, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_ +To add your own keyword you can use '-k' flag +In this example for 'ngettext' i will assign double underscore alias '__' +Full command with pluralization support will look so: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . +Then create directories with translations (get list of all locales: 'pybabel --list-locales'): + pybabel init -i locales/{domain_name}.pot -d locales -l en + pybabel init -i locales/{domain_name}.pot -d locales -l ru + pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn +Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po +After you translated all the texts you need to compile .po files: + pybabel compile -d locales +When you delete/update your texts you also need to update them in .po files: + pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs . + pybabel update -i locales/{domain_name}.pot -d locales + - translate + pybabel compile -d locales +If you have any exceptions check: + - you have installed babel + - translations are ready, so you just compiled it + - in the commands above you replaced {domain_name} to messages + - you are writing commands from correct path in terminal +""" + +from telebot import TeleBot, types, custom_filters +from telebot import apihelper +from telebot.storage.memory_storage import StateMemoryStorage + +import keyboards +from i18n_class import I18N + +apihelper.ENABLE_MIDDLEWARE = True +storage = StateMemoryStorage() +# IMPORTANT! This example works only if polling is non-threaded. +bot = TeleBot("", state_storage=storage, threaded=False) + +i18n = I18N(translations_path='locales', domain_name='messages') +_ = i18n.gettext # for singular translations +__ = i18n.ngettext # for plural translations + +# These are example storages, do not use it in a production development +users_lang = {} +users_clicks = {} + + +@bot.middleware_handler(update_types=['message', 'callback_query']) +def set_contex_language(bot_instance, message): + i18n.context_lang.language = users_lang.get(message.from_user.id, 'en') + + +@bot.message_handler(commands=['start']) +def start_handler(message: types.Message): + text = _("Hello, {user_fist_name}!\n" + "This is the example of multilanguage bot.\n" + "Available commands:\n\n" + "/lang - change your language\n" + "/plural - pluralization example") + + # remember don't use f string for interpolation, use .format method instead + text = text.format(user_fist_name=message.from_user.first_name) + bot.send_message(message.from_user.id, text) + + +@bot.message_handler(commands=['lang']) +def change_language_handler(message: types.Message): + bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang", + reply_markup=keyboards.languages_keyboard()) + + +@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(contains=['en', 'ru', 'uz_Latn'])) +def language_handler(call: types.CallbackQuery): + lang = call.data + users_lang[call.from_user.id] = lang + + # When you change user's language, pass language explicitly coz it's not changed in context + bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id) + bot.delete_state(call.from_user.id) + + +@bot.message_handler(commands=['plural']) +def pluralization_handler(message: types.Message): + if not users_clicks.get(message.from_user.id): + users_clicks[message.from_user.id] = 0 + clicks = users_clicks[message.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks, + ) + text = _("This is clicker.\n\n") + text.format(number=clicks) + bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_)) + + +@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(equals='click')) +def click_handler(call: types.CallbackQuery): + if not users_clicks.get(call.from_user.id): + users_clicks[call.from_user.id] = 1 + else: + users_clicks[call.from_user.id] += 1 + + clicks = users_clicks[call.from_user.id] + + text = __( + singular="You have {number} click", + plural="You have {number} clicks", + n=clicks + ) + text = _("This is clicker.\n\n") + text.format(number=clicks) + bot.edit_message_text(text, call.from_user.id, call.message.message_id, + reply_markup=keyboards.clicker_keyboard(_)) + + +if __name__ == '__main__': + bot.add_custom_filter(custom_filters.TextMatchFilter()) + bot.infinity_polling() From bd002c6429613e22a8a85278f06749272fab7a6c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 24 Apr 2022 11:28:20 +0300 Subject: [PATCH 0951/1808] i18n middleware - file revert --- telebot/custom_filters.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 21a079a1b..1bd80b360 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -69,7 +69,7 @@ def __init__(self, self.ends_with = self._check_iterable(ends_with, filter_name='ends_with') self.ignore_case = ignore_case - def _check_iterable(self, iterable, filter_name): + def _check_iterable(self, iterable, filter_name: str): if not iterable: pass elif not isinstance(iterable, str) and not isinstance(iterable, list) and not isinstance(iterable, tuple): @@ -95,33 +95,39 @@ def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery if self.ignore_case: text = text.lower() - prepare_func = lambda string: str(string).lower() - else: - prepare_func = str + + if self.equals: + self.equals = self.equals.lower() + elif self.contains: + self.contains = tuple(map(str.lower, self.contains)) + elif self.starts_with: + self.starts_with = tuple(map(str.lower, self.starts_with)) + elif self.ends_with: + self.ends_with = tuple(map(str.lower, self.ends_with)) if self.equals: - result = prepare_func(self.equals) == text + result = self.equals == text if result: return True elif not result and not any((self.contains, self.starts_with, self.ends_with)): return False if self.contains: - result = any([prepare_func(i) in text for i in self.contains]) + result = any([i in text for i in self.contains]) if result: return True elif not result and not any((self.starts_with, self.ends_with)): return False if self.starts_with: - result = any([text.startswith(prepare_func(i)) for i in self.starts_with]) + result = any([text.startswith(i) for i in self.starts_with]) if result: return True elif not result and not self.ends_with: return False if self.ends_with: - return any([text.endswith(prepare_func(i)) for i in self.ends_with]) + return any([text.endswith(i) for i in self.ends_with]) return False From b43b636ba0b757743ea791d4d533c2c95d54eec7 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 24 Apr 2022 11:33:19 +0300 Subject: [PATCH 0952/1808] Bugfix in answer_web_app_query --- telebot/apihelper.py | 5 +++-- telebot/async_telebot.py | 2 -- telebot/asyncio_helper.py | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 55d6dbb51..8d4b2f765 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1615,12 +1615,13 @@ def delete_sticker_from_set(token, sticker): payload = {'sticker': sticker} return _make_request(token, method_url, params=payload, method='post') + def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResultBase): method_url = 'answerWebAppQuery' - result = result.to_json() - payload = {'query_id': web_app_query_id, 'result': result} + payload = {'web_app_query_id': web_app_query_id, 'result': result.to_json()} return _make_request(token, method_url, params=payload, method='post') + # noinspection PyShadowingBuiltins def send_poll( token, chat_id, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fedad40c6..f6d8cc488 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1580,8 +1580,6 @@ async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types result = await asyncio_helper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) - - async def send_message( self, chat_id: Union[int, str], text: str, parse_mode: Optional[str]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 40a4ca976..be6223bc0 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -353,10 +353,10 @@ async def delete_chat_sticker_set(token, chat_id): payload = {'chat_id': chat_id} return await _process_request(token, method_url, params=payload) + async def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResultBase): method_url = 'answerWebAppQuery' - result = result.to_json() - payload = {'query_id': web_app_query_id, 'result': result} + payload = {'web_app_query_id': web_app_query_id, 'result': result.to_json()} return await _process_request(token, method_url, params=payload, method='post') From e1dc6d7beb252a705181da1863d3b8acf4d001bb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 24 Apr 2022 23:41:08 +0300 Subject: [PATCH 0953/1808] Mistake in ChatAdministratorRights --- telebot/types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ef014bfcc..4700c28e8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3076,11 +3076,11 @@ def to_dict(self): 'can_change_info': self.can_change_info, 'can_invite_users': self.can_invite_users, } - if 'can_post_messages' is not None: + if self.can_post_messages is not None: json_dict['can_post_messages'] = self.can_post_messages - if 'can_edit_messages' is not None: + if self.can_edit_messages is not None: json_dict['can_edit_messages'] = self.can_edit_messages - if 'can_pin_messages' is not None: + if self.can_pin_messages is not None: json_dict['can_pin_messages'] = self.can_pin_messages return json_dict From 5688aaa03b42cf17962aeedccc26078d5d861161 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 1 May 2022 00:28:00 +0500 Subject: [PATCH 0954/1808] Markdown & Html functions added(Beta version, still in progress) --- docs/source/formatting.rst | 10 ++ docs/source/index.rst | 1 + telebot/formatting.py | 201 +++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 docs/source/formatting.rst create mode 100644 telebot/formatting.py diff --git a/docs/source/formatting.rst b/docs/source/formatting.rst new file mode 100644 index 000000000..89d35f20f --- /dev/null +++ b/docs/source/formatting.rst @@ -0,0 +1,10 @@ +================== +Formatting options +================== + + + +.. automodule:: telebot.formatting + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index 596f901c5..515627192 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -49,6 +49,7 @@ Content async_version/index calldata util + formatting diff --git a/telebot/formatting.py b/telebot/formatting.py new file mode 100644 index 000000000..fc003c198 --- /dev/null +++ b/telebot/formatting.py @@ -0,0 +1,201 @@ +""" +Markdown & HTML formatting functions. + +.. versionadded:: 4.5.1 +""" + +import html +import re + + +def format_text(*args, separator="\n"): + """ + Formats a list of strings into a single string. + + .. code:: python + + format_text( # just an example + mbold('Hello'), + mitalic('World') + ) + + :param args: Strings to format. + :param separator: The separator to use between each string. + """ + return separator.join(args) + + + +def escape_html(content: str) -> str: + """ + Escapes HTML characters in a string of HTML. + + :param content: The string of HTML to escape. + """ + return html.escape(content) + + +def escape_markdown(content: str) -> str: + """ + Escapes Markdown characters in a string of Markdown. + + Credits: simonsmh + + :param content: The string of Markdown to escape. + """ + + parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!])", r"\\\1", content) + reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!])", r"\1", parse) + return reparse + + +def mbold(content: str, escape: bool=False) -> str: + """ + Returns a Markdown-formatted bold string. + + :param content: The string to bold. + :param escape: True if you need to escape special characters. + """ + return '*{}*'.format(escape_markdown(content) if escape else content) + + +def hbold(content: str, escape: bool=False) -> str: + """ + Returns an HTML-formatted bold string. + + :param content: The string to bold. + :param escape: True if you need to escape special characters. + """ + return '{}'.format(escape_html(content) if escape else content) + + +def mitalic(content: str, escape: bool=False) -> str: + """ + Returns a Markdown-formatted italic string. + + :param content: The string to italicize. + :param escape: True if you need to escape special characters. + """ + return '_{}_\r'.format(escape_markdown(content) if escape else content) + + +def hitalic(content: str, escape: bool=False) -> str: + """ + Returns an HTML-formatted italic string. + + :param content: The string to italicize. + :param escape: True if you need to escape special characters. + """ + return '{}'.format(escape_html(content) if escape else content) + + +def munderline(content: str, escape: bool=False) -> str: + """ + Returns a Markdown-formatted underline string. + + :param content: The string to underline. + :param escape: True if you need to escape special characters. + """ + return '__{}__'.format(escape_markdown(content) if escape else content) + + +def hunderline(content: str, escape: bool=False) -> str: + """ + Returns an HTML-formatted underline string. + + :param content: The string to underline. + :param escape: True if you need to escape special characters. + """ + return '{}'.format(escape_html(content) if escape else content) + + +def mstrikethrough(content: str, escape: bool=False) -> str: + """ + Returns a Markdown-formatted strikethrough string. + + :param content: The string to strikethrough. + :param escape: True if you need to escape special characters. + """ + return '~{}~'.format(escape_markdown(content) if escape else content) + + +def hstrikethrough(content: str, escape: bool=False) -> str: + """ + Returns an HTML-formatted strikethrough string. + + :param content: The string to strikethrough. + :param escape: True if you need to escape special characters. + """ + return '{}'.format(escape_html(content) if escape else content) + + +def mspoiler(content: str, escape: bool=False) -> str: + """ + Returns a Markdown-formatted spoiler string. + + :param content: The string to spoiler. + :param escape: True if you need to escape special characters. + """ + return '||{}||'.format(escape_markdown(content) if escape else content) + + +def hspoiler(content: str, escape: bool=False) -> str: + """ + Returns an HTML-formatted spoiler string. + + :param content: The string to spoiler. + :param escape: True if you need to escape special characters. + """ + return '{}'.format(escape_html(content) if escape else content) + + +def mlink(content: str, url: str, escape: bool=False) -> str: + """ + Returns a Markdown-formatted link string. + + :param content: The string to link. + :param url: The URL to link to. + :param escape: True if you need to escape special characters. + """ + return '[{}]({})'.format(escape_markdown(content), escape_markdown(url) if escape else content) + + +def hlink(content: str, url: str, escape: bool=False) -> str: + """ + Returns an HTML-formatted link string. + + :param content: The string to link. + :param url: The URL to link to. + :param escape: True if you need to escape special characters. + """ + return '{}'.format(escape_html(url), escape_html(content) if escape else content) + + +def mcode(content: str, language: str="", escape: bool=False) -> str: + """ + Returns a Markdown-formatted code string. + + :param content: The string to code. + :param escape: True if you need to escape special characters. + """ + return '```{}\n{}```'.format(language, escape_markdown(content) if escape else content) + + +def hcode(content: str, escape: bool=False) -> str: + """ + Returns an HTML-formatted code string. + + :param content: The string to code. + :param escape: True if you need to escape special characters. + """ + return '{}'.format(escape_html(content) if escape else content) + + +def hpre(content: str, escape: bool=False, language: str="") -> str: + """ + Returns an HTML-formatted preformatted string. + + :param content: The string to preformatted. + :param escape: True if you need to escape special characters. + """ + return '
{}
'.format(language, escape_html(content) if escape else content) \ No newline at end of file From 191164cba0b468d188437ffacc0416adfc3a8559 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 1 May 2022 00:45:34 +0500 Subject: [PATCH 0955/1808] Fix traceback --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1a6766732..d6b98da94 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -645,7 +645,7 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po else: handled = False if not handled: - logger.error(e) + logger.error(traceback.format_exc()) if not non_stop: self.__stop_polling.set() logger.info("Exception occurred. Stopping.") From 532011138c08b0e3119d2cccaeabd6dc4281b40e Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 1 May 2022 00:58:06 +0500 Subject: [PATCH 0956/1808] Added examples for formatting --- .../formatting_example.py | 52 +++++++++++++++++++ examples/formatting_example.py | 51 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 examples/asynchronous_telebot/formatting_example.py create mode 100644 examples/formatting_example.py diff --git a/examples/asynchronous_telebot/formatting_example.py b/examples/asynchronous_telebot/formatting_example.py new file mode 100644 index 000000000..2e418eaea --- /dev/null +++ b/examples/asynchronous_telebot/formatting_example.py @@ -0,0 +1,52 @@ +from telebot.async_telebot import AsyncTeleBot +from telebot import formatting + +bot = AsyncTeleBot('token') + + +@bot.message_handler(commands=['start']) +async def start_message(message): + await bot.send_message( + message.chat.id, + # function which connects all strings + formatting.format_text( + formatting.mbold(message.from_user.first_name, escape=True), # pass escape=True to escape special characters + formatting.mitalic(message.from_user.first_name, escape=True), + formatting.munderline(message.from_user.first_name, escape=True), + formatting.mstrikethrough(message.from_user.first_name, escape=True), + formatting.mcode(message.from_user.first_name, escape=True), + separator=" " # separator separates all strings + ), + parse_mode='MarkdownV2' + ) + + # just a bold text using markdownv2 + await bot.send_message( + message.chat.id, + formatting.mbold(message.from_user.first_name, escape=True), + parse_mode='MarkdownV2' + ) + + # html + await bot.send_message( + message.chat.id, + formatting.format_text( + formatting.hbold(message.from_user.first_name, escape=True), + formatting.hitalic(message.from_user.first_name, escape=True), + formatting.hunderline(message.from_user.first_name, escape=True), + formatting.hstrikethrough(message.from_user.first_name, escape=True), + formatting.hcode(message.from_user.first_name, escape=True), + separator=" " + ), + parse_mode='HTML' + ) + + # just a bold text in html + await bot.send_message( + message.chat.id, + formatting.hbold(message.from_user.first_name, escape=True), + parse_mode='HTML' + ) + +import asyncio +asyncio.run(bot.polling()) \ No newline at end of file diff --git a/examples/formatting_example.py b/examples/formatting_example.py new file mode 100644 index 000000000..29df2d3b6 --- /dev/null +++ b/examples/formatting_example.py @@ -0,0 +1,51 @@ +from telebot import TeleBot +from telebot import formatting + +bot = TeleBot('TOKEN') + + +@bot.message_handler(commands=['start']) +def start_message(message): + bot.send_message( + message.chat.id, + # function which connects all strings + formatting.format_text( + formatting.mbold(message.from_user.first_name, escape=True), # pass escape=True to escape special characters + formatting.mitalic(message.from_user.first_name, escape=True), + formatting.munderline(message.from_user.first_name, escape=True), + formatting.mstrikethrough(message.from_user.first_name, escape=True), + formatting.mcode(message.from_user.first_name, escape=True), + separator=" " # separator separates all strings + ), + parse_mode='MarkdownV2' + ) + + # just a bold text using markdownv2 + bot.send_message( + message.chat.id, + formatting.mbold(message.from_user.first_name, escape=True), + parse_mode='MarkdownV2' + ) + + # html + bot.send_message( + message.chat.id, + formatting.format_text( + formatting.hbold(message.from_user.first_name, escape=True), + formatting.hitalic(message.from_user.first_name, escape=True), + formatting.hunderline(message.from_user.first_name, escape=True), + formatting.hstrikethrough(message.from_user.first_name, escape=True), + formatting.hcode(message.from_user.first_name, escape=True), + separator=" " + ), + parse_mode='HTML' + ) + + # just a bold text in html + bot.send_message( + message.chat.id, + formatting.hbold(message.from_user.first_name, escape=True), + parse_mode='HTML' + ) + +bot.infinity_polling() \ No newline at end of file From db0c9467809735b34096492c5656176ed39cf2c1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 1 May 2022 00:17:14 +0300 Subject: [PATCH 0957/1808] Polling exception logging updated Polling exception logging arranged with infinity_polling mode --- telebot/__init__.py | 87 +++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d6b98da94..551fe281d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -542,15 +542,15 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, logger_level=logging.ERROR, - allowed_updates: Optional[List[str]]=None, *args, **kwargs): + def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, + logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. :param timeout: Request connection timeout :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :param skip_pending: skip old updates - :param logger_level: Custom logging level for infinity_polling logging. + :param logger_level: Custom (different from logger itself) logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. @@ -567,7 +567,7 @@ def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polli while not self.__stop_polling.is_set(): try: self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, - allowed_updates=allowed_updates, *args, **kwargs) + logger_level=logger_level, allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) @@ -580,9 +580,9 @@ def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polli if logger_level and logger_level >= logging.INFO: logger.error("Break infinity polling") - def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, - long_polling_timeout: int=20, allowed_updates: Optional[List[str]]=None, - none_stop: Optional[bool]=None): + def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, long_polling_timeout: int=20, + logger_level=logging.ERROR, 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. @@ -596,6 +596,8 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim :param timeout: Request connection timeout :param skip_pending: skip old updates :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :param logger_level: Custom (different from logger itself) logging level for infinity_polling logging. + Use logger levels from logging as a value. None/NOTSET = no error logging :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -615,12 +617,20 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim self.__skip_updates() if self.threaded: - self.__threaded_polling(non_stop, interval, timeout, long_polling_timeout, allowed_updates) + self.__threaded_polling(non_stop=non_stop, interval=interval, timeout=timeout, long_polling_timeout=long_polling_timeout, + logger_level=logger_level, allowed_updates=allowed_updates) else: - self.__non_threaded_polling(non_stop, interval, timeout, long_polling_timeout, allowed_updates) + self.__non_threaded_polling(non_stop=non_stop, interval=interval, timeout=timeout, long_polling_timeout=long_polling_timeout, + logger_level=logger_level, allowed_updates=allowed_updates) - def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None, allowed_updates=None): - logger.info('Started polling.') + def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, long_polling_timeout = None, + logger_level=logging.ERROR, allowed_updates=None): + if not(logger_level) or (logger_level < logging.INFO): + warning = "\n Warning: this message appearance will be changed. Set logger_level=logging.INFO to continue seeing it." + else: + warning = "" + #if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info('Started polling.' + warning) self.__stop_polling.clear() error_interval = 0.25 @@ -645,14 +655,17 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po else: handled = False if not handled: - logger.error(traceback.format_exc()) + if logger_level and logger_level >= logging.ERROR: + logger.error("Threaded polling exception: %s", str(e)) + if logger_level and logger_level >= logging.DEBUG: + logger.error("Exception traceback:\n%s", traceback.format_exc()) if not non_stop: self.__stop_polling.set() - logger.info("Exception occurred. Stopping.") + # if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info("Exception occurred. Stopping." + warning) else: - # polling_thread.clear_exceptions() - # self.worker_pool.clear_exceptions() - logger.info("Waiting for {0} seconds until retry".format(error_interval)) + # if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info("Waiting for {0} seconds until retry".format(error_interval) + warning) time.sleep(error_interval) if error_interval * 2 < 60: error_interval *= 2 @@ -665,7 +678,8 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po polling_thread.clear_exceptions() #* self.worker_pool.clear_exceptions() #* except KeyboardInterrupt: - logger.info("KeyboardInterrupt received.") + # if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info("KeyboardInterrupt received." + warning) self.__stop_polling.set() break except Exception as e: @@ -684,12 +698,19 @@ def __threaded_polling(self, non_stop=False, interval=0, timeout = None, long_po time.sleep(error_interval) polling_thread.stop() - polling_thread.clear_exceptions() #* - self.worker_pool.clear_exceptions() #* - logger.info('Stopped polling.') - - def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None, allowed_updates=None): - logger.info('Started polling.') + polling_thread.clear_exceptions() + self.worker_pool.clear_exceptions() + #if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info('Stopped polling.' + warning) + + def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None, + logger_level=logging.ERROR, allowed_updates=None): + if not(logger_level) or (logger_level < logging.INFO): + warning = "\n Warning: this message appearance will be changed. Set logger_level=logging.INFO to continue seeing it." + else: + warning = "" + #if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info('Started polling.' + warning) self.__stop_polling.clear() error_interval = 0.25 @@ -704,18 +725,24 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ handled = False if not handled: - logger.error(e) + if logger_level and logger_level >= logging.ERROR: + logger.error("Polling exception: %s", str(e)) + if logger_level and logger_level >= logging.DEBUG: + logger.error("Exception traceback:\n%s", traceback.format_exc()) if not non_stop: self.__stop_polling.set() - logger.info("Exception occurred. Stopping.") + # if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info("Exception occurred. Stopping." + warning) else: - logger.info("Waiting for {0} seconds until retry".format(error_interval)) + # if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info("Waiting for {0} seconds until retry".format(error_interval) + warning) time.sleep(error_interval) error_interval *= 2 else: time.sleep(error_interval) except KeyboardInterrupt: - logger.info("KeyboardInterrupt received.") + # if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info("KeyboardInterrupt received." + warning) self.__stop_polling.set() break except Exception as e: @@ -727,8 +754,8 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ raise e else: time.sleep(error_interval) - - logger.info('Stopped polling.') + #if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error + logger.info('Stopped polling.' + warning) def _exec_task(self, task, *args, **kwargs): if kwargs and kwargs.get('task_type') == 'handler': @@ -3876,7 +3903,7 @@ def _check_middleware(self, update_type): """ Check middleware - :param message: + :param update_type: :return: """ middlewares = None From 76c0197ab73651b0ac5bc35b6f1c059c21d881fd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 1 May 2022 16:11:00 +0500 Subject: [PATCH 0958/1808] Key for custom filters --- telebot/asyncio_filters.py | 8 ++++++++ telebot/custom_filters.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 312b290ae..c594d31be 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -10,8 +10,12 @@ class SimpleCustomFilter(ABC): Simple Custom Filter base class. Create child class with check() method. Accepts only message, returns bool value, that is compared with given in handler. + + Child classes should have .key property. """ + key: str = None + async def check(self, message): """ Perform a check. @@ -26,8 +30,12 @@ class AdvancedCustomFilter(ABC): Accepts two parameters, returns bool: True - filter passed, False - filter failed. message: Message class text: Filter value given in handler + + Child classes should have .key property. """ + key: str = None + async def check(self, message, text): """ Perform a check. diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 1bd80b360..abeffdd9f 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -12,8 +12,12 @@ class SimpleCustomFilter(ABC): Simple Custom Filter base class. Create child class with check() method. Accepts only message, returns bool value, that is compared with given in handler. + + Child classes should have .key property. """ + key: str = None + def check(self, message): """ Perform a check. @@ -28,8 +32,12 @@ class AdvancedCustomFilter(ABC): Accepts two parameters, returns bool: True - filter passed, False - filter failed. message: Message class text: Filter value given in handler + + Child classes should have .key property. """ + key: str = None + def check(self, message, text): """ Perform a check. From 9b959373dbf74c702ce20fd3e76d57e510c50043 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 1 May 2022 18:43:07 +0500 Subject: [PATCH 0959/1808] Fixed proxy for asynctelebot --- telebot/asyncio_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index be6223bc0..dde8c8544 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -60,7 +60,7 @@ async def _process_request(token, url, method='get', params=None, files=None, re while not got_result and current_try Date: Sun, 1 May 2022 19:13:49 +0500 Subject: [PATCH 0960/1808] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index a0e283f97..3412d80d0 100644 --- a/README.md +++ b/README.md @@ -656,6 +656,8 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. ``` ### Proxy +For sync: + You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument. ```python @@ -670,6 +672,14 @@ If you want to use socket5 proxy you need install dependency `pip install reques apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'} ``` +For async: +```python +from telebot import asyncio_helper + +asyncio_helper.proxy = 'http://127.0.0.1:3128' #url +``` + + ### Testing You can disable or change the interaction with real Telegram server by using ```python From c022d4999609e0c92b062916f1901e24fa45f95c Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 1 May 2022 23:19:46 +0500 Subject: [PATCH 0961/1808] SEO improvements for documentation --- docs/source/async_version/index.rst | 6 ++++++ docs/source/calldata.rst | 4 ++++ docs/source/formatting.rst | 4 +++- docs/source/index.rst | 5 +++++ docs/source/install.rst | 4 ++++ docs/source/quick_start.rst | 4 ++++ docs/source/sync_version/index.rst | 4 ++++ docs/source/util.rst | 4 ++++ 8 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/source/async_version/index.rst b/docs/source/async_version/index.rst index 0e0418f79..88b382417 100644 --- a/docs/source/async_version/index.rst +++ b/docs/source/async_version/index.rst @@ -2,6 +2,12 @@ AsyncTeleBot ==================== + +.. meta:: + :description: Asynchronous pyTelegramBotAPI + :keywords: ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation + + AsyncTeleBot methods -------------------- diff --git a/docs/source/calldata.rst b/docs/source/calldata.rst index 4919c9d46..f471c34ec 100644 --- a/docs/source/calldata.rst +++ b/docs/source/calldata.rst @@ -3,6 +3,10 @@ Callback data factory ===================== +.. meta:: + :description: Callback data factory in pyTelegramBotAPI + :keywords: ptba, pytba, pyTelegramBotAPI, callbackdatafactory, guide, callbackdata, factory + callback\_data file ----------------------------- diff --git a/docs/source/formatting.rst b/docs/source/formatting.rst index 89d35f20f..d127cf777 100644 --- a/docs/source/formatting.rst +++ b/docs/source/formatting.rst @@ -2,7 +2,9 @@ Formatting options ================== - +.. meta:: + :description: Formatting options in pyTelegramBotAPI + :keywords: html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI .. automodule:: telebot.formatting :members: diff --git a/docs/source/index.rst b/docs/source/index.rst index 515627192..7db23a2fa 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,6 +7,11 @@ Welcome to pyTelegramBotAPI's documentation! ============================================ +.. meta:: + :description: Official documentation of pyTelegramBotAPI + :keywords: ptba, pytba, pyTelegramBotAPI, documentation, guide + + ======= TeleBot ======= diff --git a/docs/source/install.rst b/docs/source/install.rst index 7998ff8f5..906e8bfde 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -2,6 +2,10 @@ Installation Guide ================== +.. meta:: + :description: Installation of pyTelegramBotAPI + :keywords: ptba, pytba, pyTelegramBotAPI, installation, guide + Using PIP ---------- diff --git a/docs/source/quick_start.rst b/docs/source/quick_start.rst index 27a779562..188a70775 100644 --- a/docs/source/quick_start.rst +++ b/docs/source/quick_start.rst @@ -3,6 +3,10 @@ Quick start =========== +.. meta:: + :description: Quickstart guide + :keywords: ptba, pytba, pyTelegramBotAPI, quickstart, guide + Synchronous TeleBot ------------------- .. literalinclude:: ../../examples/echo_bot.py diff --git a/docs/source/sync_version/index.rst b/docs/source/sync_version/index.rst index 590cfe549..8b805dc52 100644 --- a/docs/source/sync_version/index.rst +++ b/docs/source/sync_version/index.rst @@ -2,6 +2,10 @@ TeleBot version =============== +.. meta:: + :description: Synchronous pyTelegramBotAPI documentation + :keywords: ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync + TeleBot methods --------------- .. automodule:: telebot diff --git a/docs/source/util.rst b/docs/source/util.rst index 027dfe78d..2986df1b2 100644 --- a/docs/source/util.rst +++ b/docs/source/util.rst @@ -2,6 +2,10 @@ Utils ============ +.. meta:: + :description: Utils in pyTelegramBotAPI + :keywords: ptba, pytba, pyTelegramBotAPI, utils, guide + util file ------------------- From 836130a7183fb4b21d4eda00288dd120c02a26b5 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 2 May 2022 02:08:48 +0500 Subject: [PATCH 0962/1808] Allow only state objects --- telebot/asyncio_storage/memory_storage.py | 4 +++- telebot/asyncio_storage/pickle_storage.py | 4 ++-- telebot/asyncio_storage/redis_storage.py | 4 +++- telebot/storage/memory_storage.py | 4 +++- telebot/storage/pickle_storage.py | 4 ++-- telebot/storage/redis_storage.py | 3 ++- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py index 58a6e354e..b4d8e16c0 100644 --- a/telebot/asyncio_storage/memory_storage.py +++ b/telebot/asyncio_storage/memory_storage.py @@ -1,5 +1,7 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext +from telebot.asyncio_handler_backends import State + class StateMemoryStorage(StateStorageBase): def __init__(self) -> None: self.data = {} @@ -8,7 +10,7 @@ def __init__(self) -> None: async def set_state(self, chat_id, user_id, state): - if isinstance(state, object): + if isinstance(state, State): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index dd6419e7e..fab8e81c8 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -1,7 +1,7 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext import os - +from telebot.asyncio_handler_backends import State import pickle @@ -47,7 +47,7 @@ def update_data(self): file.close() async def set_state(self, chat_id, user_id, state): - if isinstance(state, object): + if isinstance(state, State): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index f2a26065f..e90c89f6f 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,6 +1,8 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext import json +from telebot.asyncio_handler_backends import State + redis_installed = True try: import aioredis @@ -65,7 +67,7 @@ async def set_state(self, chat_id, user_id, state): """ response = await self.get_record(chat_id) user_id = str(user_id) - if isinstance(state, object): + if isinstance(state, State): state = state.name if response: if user_id in response: diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 45d4da3cc..73f4c4ff1 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -1,5 +1,7 @@ from telebot.storage.base_storage import StateStorageBase, StateContext +from telebot.handler_backends import State + class StateMemoryStorage(StateStorageBase): def __init__(self) -> None: self.data = {} @@ -8,7 +10,7 @@ def __init__(self) -> None: def set_state(self, chat_id, user_id, state): - if isinstance(state, object): + if isinstance(state, State): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index a2736903f..0a569072a 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -1,6 +1,6 @@ from telebot.storage.base_storage import StateStorageBase, StateContext import os - +from telebot.handler_backends import State import pickle @@ -53,7 +53,7 @@ def update_data(self): file.close() def set_state(self, chat_id, user_id, state): - if isinstance(state, object): + if isinstance(state, State): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index ff21b6e13..e46c6f617 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -1,6 +1,7 @@ from pyclbr import Class from telebot.storage.base_storage import StateStorageBase, StateContext import json +from telebot.handler_backends import State redis_installed = True try: @@ -65,7 +66,7 @@ def set_state(self, chat_id, user_id, state): """ response = self.get_record(chat_id) user_id = str(user_id) - if isinstance(state, object): + if isinstance(state, State): state = state.name if response: From f9cd0d7e081e4a29e0f19b596581931d96681d55 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 2 May 2022 14:45:43 +0500 Subject: [PATCH 0963/1808] Avoid circular import --- telebot/asyncio_storage/memory_storage.py | 4 +--- telebot/asyncio_storage/pickle_storage.py | 3 +-- telebot/asyncio_storage/redis_storage.py | 3 +-- telebot/storage/memory_storage.py | 3 +-- telebot/storage/pickle_storage.py | 3 +-- telebot/storage/redis_storage.py | 3 +-- 6 files changed, 6 insertions(+), 13 deletions(-) diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py index b4d8e16c0..45c2ad914 100644 --- a/telebot/asyncio_storage/memory_storage.py +++ b/telebot/asyncio_storage/memory_storage.py @@ -1,7 +1,5 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext -from telebot.asyncio_handler_backends import State - class StateMemoryStorage(StateStorageBase): def __init__(self) -> None: self.data = {} @@ -10,7 +8,7 @@ def __init__(self) -> None: async def set_state(self, chat_id, user_id, state): - if isinstance(state, State): + if hasattr(state, 'name'): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index fab8e81c8..4928d4a1b 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -1,7 +1,6 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext import os -from telebot.asyncio_handler_backends import State import pickle @@ -47,7 +46,7 @@ def update_data(self): file.close() async def set_state(self, chat_id, user_id, state): - if isinstance(state, State): + if hasattr(state, 'name'): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index e90c89f6f..0cf52b90f 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,7 +1,6 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext import json -from telebot.asyncio_handler_backends import State redis_installed = True try: @@ -67,7 +66,7 @@ async def set_state(self, chat_id, user_id, state): """ response = await self.get_record(chat_id) user_id = str(user_id) - if isinstance(state, State): + if hasattr(state, 'name'): state = state.name if response: if user_id in response: diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 73f4c4ff1..67cd984b5 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -1,6 +1,5 @@ from telebot.storage.base_storage import StateStorageBase, StateContext -from telebot.handler_backends import State class StateMemoryStorage(StateStorageBase): def __init__(self) -> None: @@ -10,7 +9,7 @@ def __init__(self) -> None: def set_state(self, chat_id, user_id, state): - if isinstance(state, State): + if hasattr(state, 'name'): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 0a569072a..39b10a3aa 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -1,6 +1,5 @@ from telebot.storage.base_storage import StateStorageBase, StateContext import os -from telebot.handler_backends import State import pickle @@ -53,7 +52,7 @@ def update_data(self): file.close() def set_state(self, chat_id, user_id, state): - if isinstance(state, State): + if hasattr(state, 'name'): state = state.name if chat_id in self.data: if user_id in self.data[chat_id]: diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index e46c6f617..23a1f4305 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -1,7 +1,6 @@ from pyclbr import Class from telebot.storage.base_storage import StateStorageBase, StateContext import json -from telebot.handler_backends import State redis_installed = True try: @@ -66,7 +65,7 @@ def set_state(self, chat_id, user_id, state): """ response = self.get_record(chat_id) user_id = str(user_id) - if isinstance(state, State): + if hasattr(state, 'name'): state = state.name if response: From 8a12ae3565f30d82bfb858c96f0108fb45fdd7e0 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 4 May 2022 19:55:43 +0500 Subject: [PATCH 0964/1808] Update redis_storage.py --- telebot/storage/redis_storage.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 23a1f4305..cd4058777 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -1,4 +1,3 @@ -from pyclbr import Class from telebot.storage.base_storage import StateStorageBase, StateContext import json @@ -177,4 +176,4 @@ def save(self, chat_id, user_id, data): response[user_id]['data'] = dict(data, **response[user_id]['data']) self.set_record(chat_id, response) return True - \ No newline at end of file + From 2c8793b7947c5e602b84ec168c087c2b65825a31 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 7 May 2022 22:40:26 +0300 Subject: [PATCH 0965/1808] Bump version to 4.5.1 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 745c32868..8344e6d42 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.5.0' +release = '4.5.1' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 75ff2fa93..6293a1798 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.5.0' +__version__ = '4.5.1' From 77c3587012b2f9b6483ef1c6e2af84dc8fa84cd0 Mon Sep 17 00:00:00 2001 From: Burzum Date: Sun, 8 May 2022 00:00:46 +0300 Subject: [PATCH 0966/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3412d80d0..585e0ef0e 100644 --- a/README.md +++ b/README.md @@ -877,5 +877,6 @@ Here are some examples of template: * [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. * [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. * [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English +* [Zyprexa](https://t.me/mathemathicsBot) [(source)](https://github.com/atif5/zyprexa) Zyprexa can solve, help you seolve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From ab4140ba9f250f3bb90c63b839efb3e6dc7ff3b0 Mon Sep 17 00:00:00 2001 From: Burzum Date: Sun, 8 May 2022 00:01:45 +0300 Subject: [PATCH 0967/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 585e0ef0e..7d5b234c2 100644 --- a/README.md +++ b/README.md @@ -877,6 +877,6 @@ Here are some examples of template: * [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. * [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. * [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English -* [Zyprexa](https://t.me/mathemathicsBot) [(source)](https://github.com/atif5/zyprexa) Zyprexa can solve, help you seolve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. +* [Zyprexa](https://t.me/mathemathicsBot) ([source])(https://github.com/atif5/zyprexa) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 096d7a4eea5357a1869338b4cfecb194e2457eb5 Mon Sep 17 00:00:00 2001 From: Burzum Date: Sun, 8 May 2022 00:02:40 +0300 Subject: [PATCH 0968/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d5b234c2..ff8afeb51 100644 --- a/README.md +++ b/README.md @@ -877,6 +877,6 @@ Here are some examples of template: * [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. * [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. * [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English -* [Zyprexa](https://t.me/mathemathicsBot) ([source])(https://github.com/atif5/zyprexa) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. +* [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From caae6bb93fdadb259f1c71ce7b83cc89ad93a24b Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 8 May 2022 18:13:07 +0500 Subject: [PATCH 0969/1808] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3412d80d0..84e1d13e6 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ * [Telegram Channel](#telegram-channel) * [More examples](#more-examples) * [Code Template](#code-template) - * [Bots using this API](#bots-using-this-api) + * [Bots using this library](#bots-using-this-library) ## Getting started @@ -826,7 +826,7 @@ Here are some examples of template: * [TeleBot template](https://github.com/coder2020official/telebot_template) -## Bots using this API +## Bots using this library * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. From 7e6872147538d2c68dcd3720837dd71a8429c610 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 8 May 2022 23:34:56 +0300 Subject: [PATCH 0970/1808] class File parse fix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 4700c28e8..9793417ea 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -909,7 +909,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) - def __init__(self, file_id, file_unique_id, file_size, file_path, **kwargs): + def __init__(self, file_id, file_unique_id, file_size=None, file_path=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.file_size: int = file_size From 42955d18866a39236058f492b0b1c0cf52ad0bf5 Mon Sep 17 00:00:00 2001 From: abdullaev388 Date: Wed, 11 May 2022 10:50:33 +0500 Subject: [PATCH 0971/1808] added examples of multibot --- .../asynchronous_telebot/multibot/README.MD | 17 ++++++ .../asynchronous_telebot/multibot/config.py | 6 ++ .../asynchronous_telebot/multibot/handlers.py | 15 +++++ .../asynchronous_telebot/multibot/main.py | 56 +++++++++++++++++++ .../multibot/nginx_conf.conf | 8 +++ examples/multibot/README.MD | 17 ++++++ examples/multibot/config.py | 6 ++ examples/multibot/handlers.py | 14 +++++ examples/multibot/main.py | 48 ++++++++++++++++ examples/multibot/nginx_conf.conf | 8 +++ 10 files changed, 195 insertions(+) create mode 100644 examples/asynchronous_telebot/multibot/README.MD create mode 100644 examples/asynchronous_telebot/multibot/config.py create mode 100644 examples/asynchronous_telebot/multibot/handlers.py create mode 100644 examples/asynchronous_telebot/multibot/main.py create mode 100644 examples/asynchronous_telebot/multibot/nginx_conf.conf create mode 100644 examples/multibot/README.MD create mode 100644 examples/multibot/config.py create mode 100644 examples/multibot/handlers.py create mode 100644 examples/multibot/main.py create mode 100644 examples/multibot/nginx_conf.conf diff --git a/examples/asynchronous_telebot/multibot/README.MD b/examples/asynchronous_telebot/multibot/README.MD new file mode 100644 index 000000000..3693d363f --- /dev/null +++ b/examples/asynchronous_telebot/multibot/README.MD @@ -0,0 +1,17 @@ +You probably have seen bots which allow you to send them token of your bot and then handle updates providing some functionality for your bot. +
+This type of bots are called multibots. They are created using webhooks. +
+ +This is the example of simple multibot.
+In order to reproduce this example you need to have domain and ssl connection. +
+If you have, go to config.py and specify your data. +
+There is also file called nginx_conf.conf, we will use nginx as proxy-server and this file is example nginx.conf file. +
+Make sure that server_name and port are the same in both config and nginx_conf +
+(nginx_conf.conf IS NOT complete, you would probably use tools like certbot to add ssl connection to it) +
+Also, in this example I used dictionary as tokens storage, but in production you should use database so that you can re-set webhooks in case bot restarts. \ No newline at end of file diff --git a/examples/asynchronous_telebot/multibot/config.py b/examples/asynchronous_telebot/multibot/config.py new file mode 100644 index 000000000..17e7141e3 --- /dev/null +++ b/examples/asynchronous_telebot/multibot/config.py @@ -0,0 +1,6 @@ +MAIN_BOT_TOKEN = "your_main_bot_token" + +WEBHOOK_HOST = "your_domain.com" +WEBHOOK_PATH = "telegram_webhook" +WEBAPP_HOST = "0.0.0.0" +WEBAPP_PORT = 3500 diff --git a/examples/asynchronous_telebot/multibot/handlers.py b/examples/asynchronous_telebot/multibot/handlers.py new file mode 100644 index 000000000..c98a6de00 --- /dev/null +++ b/examples/asynchronous_telebot/multibot/handlers.py @@ -0,0 +1,15 @@ +from telebot.async_telebot import AsyncTeleBot +from telebot import types + + +async def hello_handler(message: types.Message, bot: AsyncTeleBot): + await bot.send_message(message.chat.id, "Hi :)") + + +async def echo_handler(message: types.Message, bot: AsyncTeleBot): + await bot.send_message(message.chat.id, message.text) + + +def register_handlers(bot: AsyncTeleBot): + bot.register_message_handler(hello_handler, func=lambda message: message.text == 'Hello', pass_bot=True) + bot.register_message_handler(echo_handler, pass_bot=True) diff --git a/examples/asynchronous_telebot/multibot/main.py b/examples/asynchronous_telebot/multibot/main.py new file mode 100644 index 000000000..19d6666fd --- /dev/null +++ b/examples/asynchronous_telebot/multibot/main.py @@ -0,0 +1,56 @@ +import asyncio + +from aiohttp import web +from telebot import types, util +from telebot.async_telebot import AsyncTeleBot +from handlers import register_handlers + +import config + +main_bot = AsyncTeleBot(config.MAIN_BOT_TOKEN) +app = web.Application() +tokens = {config.MAIN_BOT_TOKEN: True} + + +async def webhook(request): + token = request.match_info.get('token') + if not tokens.get(token): + return web.Response(status=404) + + if request.headers.get('content-type') != 'application/json': + return web.Response(status=403) + + json_string = await request.json() + update = types.Update.de_json(json_string) + if token == main_bot.token: + await main_bot.process_new_updates([update]) + return web.Response() + + from_update_bot = AsyncTeleBot(token) + register_handlers(from_update_bot) + await from_update_bot.process_new_updates([update]) + return web.Response() + + +app.router.add_post("/" + config.WEBHOOK_PATH + "/{token}", webhook) + + +@main_bot.message_handler(commands=['add_bot']) +async def add_bot(message: types.Message): + token = util.extract_arguments(message.text) + tokens[token] = True + + new_bot = AsyncTeleBot(token) + await new_bot.delete_webhook() + await new_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{token}") + + await new_bot.send_message(message.chat.id, "Webhook was set.") + + +async def main(): + await main_bot.delete_webhook() + await main_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{config.MAIN_BOT_TOKEN}") + web.run_app(app, host=config.WEBAPP_HOST, port=config.WEBAPP_PORT) + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/examples/asynchronous_telebot/multibot/nginx_conf.conf b/examples/asynchronous_telebot/multibot/nginx_conf.conf new file mode 100644 index 000000000..776fa4c77 --- /dev/null +++ b/examples/asynchronous_telebot/multibot/nginx_conf.conf @@ -0,0 +1,8 @@ +server { + server_name your_domain.com; + + location /telegram_webhook/ { + proxy_pass http://localhost:3500; + } + +} \ No newline at end of file diff --git a/examples/multibot/README.MD b/examples/multibot/README.MD new file mode 100644 index 000000000..3693d363f --- /dev/null +++ b/examples/multibot/README.MD @@ -0,0 +1,17 @@ +You probably have seen bots which allow you to send them token of your bot and then handle updates providing some functionality for your bot. +
+This type of bots are called multibots. They are created using webhooks. +
+ +This is the example of simple multibot.
+In order to reproduce this example you need to have domain and ssl connection. +
+If you have, go to config.py and specify your data. +
+There is also file called nginx_conf.conf, we will use nginx as proxy-server and this file is example nginx.conf file. +
+Make sure that server_name and port are the same in both config and nginx_conf +
+(nginx_conf.conf IS NOT complete, you would probably use tools like certbot to add ssl connection to it) +
+Also, in this example I used dictionary as tokens storage, but in production you should use database so that you can re-set webhooks in case bot restarts. \ No newline at end of file diff --git a/examples/multibot/config.py b/examples/multibot/config.py new file mode 100644 index 000000000..17e7141e3 --- /dev/null +++ b/examples/multibot/config.py @@ -0,0 +1,6 @@ +MAIN_BOT_TOKEN = "your_main_bot_token" + +WEBHOOK_HOST = "your_domain.com" +WEBHOOK_PATH = "telegram_webhook" +WEBAPP_HOST = "0.0.0.0" +WEBAPP_PORT = 3500 diff --git a/examples/multibot/handlers.py b/examples/multibot/handlers.py new file mode 100644 index 000000000..8fa418244 --- /dev/null +++ b/examples/multibot/handlers.py @@ -0,0 +1,14 @@ +from telebot import types, TeleBot + + +def hello_handler(message: types.Message, bot: TeleBot): + bot.send_message(message.chat.id, "Hi :)") + + +def echo_handler(message: types.Message, bot: TeleBot): + bot.send_message(message.chat.id, message.text) + + +def register_handlers(bot: TeleBot): + bot.register_message_handler(hello_handler, func=lambda message: message.text == 'Hello', pass_bot=True) + bot.register_message_handler(echo_handler, pass_bot=True) diff --git a/examples/multibot/main.py b/examples/multibot/main.py new file mode 100644 index 000000000..91e46736a --- /dev/null +++ b/examples/multibot/main.py @@ -0,0 +1,48 @@ +from flask import Flask +from flask import request, abort +from telebot import TeleBot, types, util +from handlers import register_handlers + +import config + +main_bot = TeleBot(config.MAIN_BOT_TOKEN) +app = Flask(__name__) +tokens = {config.MAIN_BOT_TOKEN: True} + + +@app.route(f"/{config.WEBHOOK_PATH}/", methods=['POST']) +def webhook(token: str): + if not tokens.get(token): + return abort(404) + + if request.headers.get('content-type') != 'application/json': + return abort(403) + + json_string = request.get_data().decode('utf-8') + update = types.Update.de_json(json_string) + if token == main_bot.token: + main_bot.process_new_updates([update]) + return '' + + from_update_bot = TeleBot(token) + register_handlers(from_update_bot) + from_update_bot.process_new_updates([update]) + return '' + + +@main_bot.message_handler(commands=['add_bot']) +def add_bot(message: types.Message): + token = util.extract_arguments(message.text) + tokens[token] = True + + new_bot = TeleBot(token) + new_bot.delete_webhook() + new_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{token}") + + new_bot.send_message(message.chat.id, "Webhook was set.") + + +if __name__ == '__main__': + main_bot.delete_webhook() + main_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{config.MAIN_BOT_TOKEN}") + app.run(host=config.WEBAPP_HOST, port=config.WEBAPP_PORT) diff --git a/examples/multibot/nginx_conf.conf b/examples/multibot/nginx_conf.conf new file mode 100644 index 000000000..776fa4c77 --- /dev/null +++ b/examples/multibot/nginx_conf.conf @@ -0,0 +1,8 @@ +server { + server_name your_domain.com; + + location /telegram_webhook/ { + proxy_pass http://localhost:3500; + } + +} \ No newline at end of file From e051dda11388d19ca1c379412584c6219428a41f Mon Sep 17 00:00:00 2001 From: AHOHNMYC <24810600+AHOHNMYC@users.noreply.github.com> Date: Sat, 14 May 2022 15:46:05 +0300 Subject: [PATCH 0972/1808] CopyMessage return type fix --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 551fe281d..5917ca734 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1040,7 +1040,7 @@ def copy_message( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> int: + timeout: Optional[int]=None) -> types.MessageID: """ Use this method to copy messages of any kind. From 3f28bb6e9d5bc724031f310c1539ecd64c6e56c2 Mon Sep 17 00:00:00 2001 From: AHOHNMYC <24810600+AHOHNMYC@users.noreply.github.com> Date: Sat, 14 May 2022 15:48:27 +0300 Subject: [PATCH 0973/1808] CopyMessage return type fix [async] --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f6d8cc488..35c2da8c1 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1654,7 +1654,7 @@ async def copy_message( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> int: + timeout: Optional[int]=None) -> types.MessageID: """ Use this method to copy messages of any kind. From 42efb8488c96dad6ee1463976c76eef08b726614 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 14 May 2022 20:32:28 +0500 Subject: [PATCH 0974/1808] Set escape=True by default. --- .../formatting_example.py | 24 +++++++-------- examples/formatting_example.py | 24 +++++++-------- telebot/formatting.py | 30 +++++++++---------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/examples/asynchronous_telebot/formatting_example.py b/examples/asynchronous_telebot/formatting_example.py index 2e418eaea..95c3d35b0 100644 --- a/examples/asynchronous_telebot/formatting_example.py +++ b/examples/asynchronous_telebot/formatting_example.py @@ -10,11 +10,11 @@ async def start_message(message): message.chat.id, # function which connects all strings formatting.format_text( - formatting.mbold(message.from_user.first_name, escape=True), # pass escape=True to escape special characters - formatting.mitalic(message.from_user.first_name, escape=True), - formatting.munderline(message.from_user.first_name, escape=True), - formatting.mstrikethrough(message.from_user.first_name, escape=True), - formatting.mcode(message.from_user.first_name, escape=True), + formatting.mbold(message.from_user.first_name), + formatting.mitalic(message.from_user.first_name), + formatting.munderline(message.from_user.first_name), + formatting.mstrikethrough(message.from_user.first_name), + formatting.mcode(message.from_user.first_name), separator=" " # separator separates all strings ), parse_mode='MarkdownV2' @@ -23,7 +23,7 @@ async def start_message(message): # just a bold text using markdownv2 await bot.send_message( message.chat.id, - formatting.mbold(message.from_user.first_name, escape=True), + formatting.mbold(message.from_user.first_name), parse_mode='MarkdownV2' ) @@ -31,11 +31,11 @@ async def start_message(message): await bot.send_message( message.chat.id, formatting.format_text( - formatting.hbold(message.from_user.first_name, escape=True), - formatting.hitalic(message.from_user.first_name, escape=True), - formatting.hunderline(message.from_user.first_name, escape=True), - formatting.hstrikethrough(message.from_user.first_name, escape=True), - formatting.hcode(message.from_user.first_name, escape=True), + formatting.hbold(message.from_user.first_name), + formatting.hitalic(message.from_user.first_name), + formatting.hunderline(message.from_user.first_name), + formatting.hstrikethrough(message.from_user.first_name), + formatting.hcode(message.from_user.first_name), separator=" " ), parse_mode='HTML' @@ -44,7 +44,7 @@ async def start_message(message): # just a bold text in html await bot.send_message( message.chat.id, - formatting.hbold(message.from_user.first_name, escape=True), + formatting.hbold(message.from_user.first_name), parse_mode='HTML' ) diff --git a/examples/formatting_example.py b/examples/formatting_example.py index 29df2d3b6..1068acc32 100644 --- a/examples/formatting_example.py +++ b/examples/formatting_example.py @@ -10,11 +10,11 @@ def start_message(message): message.chat.id, # function which connects all strings formatting.format_text( - formatting.mbold(message.from_user.first_name, escape=True), # pass escape=True to escape special characters - formatting.mitalic(message.from_user.first_name, escape=True), - formatting.munderline(message.from_user.first_name, escape=True), - formatting.mstrikethrough(message.from_user.first_name, escape=True), - formatting.mcode(message.from_user.first_name, escape=True), + formatting.mbold(message.from_user.first_name), + formatting.mitalic(message.from_user.first_name), + formatting.munderline(message.from_user.first_name), + formatting.mstrikethrough(message.from_user.first_name), + formatting.mcode(message.from_user.first_name), separator=" " # separator separates all strings ), parse_mode='MarkdownV2' @@ -23,7 +23,7 @@ def start_message(message): # just a bold text using markdownv2 bot.send_message( message.chat.id, - formatting.mbold(message.from_user.first_name, escape=True), + formatting.mbold(message.from_user.first_name), parse_mode='MarkdownV2' ) @@ -31,11 +31,11 @@ def start_message(message): bot.send_message( message.chat.id, formatting.format_text( - formatting.hbold(message.from_user.first_name, escape=True), - formatting.hitalic(message.from_user.first_name, escape=True), - formatting.hunderline(message.from_user.first_name, escape=True), - formatting.hstrikethrough(message.from_user.first_name, escape=True), - formatting.hcode(message.from_user.first_name, escape=True), + formatting.hbold(message.from_user.first_name), + formatting.hitalic(message.from_user.first_name), + formatting.hunderline(message.from_user.first_name), + formatting.hstrikethrough(message.from_user.first_name), + formatting.hcode(message.from_user.first_name), separator=" " ), parse_mode='HTML' @@ -44,7 +44,7 @@ def start_message(message): # just a bold text in html bot.send_message( message.chat.id, - formatting.hbold(message.from_user.first_name, escape=True), + formatting.hbold(message.from_user.first_name), parse_mode='HTML' ) diff --git a/telebot/formatting.py b/telebot/formatting.py index fc003c198..d13ba88bf 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -49,7 +49,7 @@ def escape_markdown(content: str) -> str: return reparse -def mbold(content: str, escape: bool=False) -> str: +def mbold(content: str, escape: bool=True) -> str: """ Returns a Markdown-formatted bold string. @@ -59,7 +59,7 @@ def mbold(content: str, escape: bool=False) -> str: return '*{}*'.format(escape_markdown(content) if escape else content) -def hbold(content: str, escape: bool=False) -> str: +def hbold(content: str, escape: bool=True) -> str: """ Returns an HTML-formatted bold string. @@ -69,7 +69,7 @@ def hbold(content: str, escape: bool=False) -> str: return '{}'.format(escape_html(content) if escape else content) -def mitalic(content: str, escape: bool=False) -> str: +def mitalic(content: str, escape: bool=True) -> str: """ Returns a Markdown-formatted italic string. @@ -79,7 +79,7 @@ def mitalic(content: str, escape: bool=False) -> str: return '_{}_\r'.format(escape_markdown(content) if escape else content) -def hitalic(content: str, escape: bool=False) -> str: +def hitalic(content: str, escape: bool=True) -> str: """ Returns an HTML-formatted italic string. @@ -89,7 +89,7 @@ def hitalic(content: str, escape: bool=False) -> str: return '{}'.format(escape_html(content) if escape else content) -def munderline(content: str, escape: bool=False) -> str: +def munderline(content: str, escape: bool=True) -> str: """ Returns a Markdown-formatted underline string. @@ -99,7 +99,7 @@ def munderline(content: str, escape: bool=False) -> str: return '__{}__'.format(escape_markdown(content) if escape else content) -def hunderline(content: str, escape: bool=False) -> str: +def hunderline(content: str, escape: bool=True) -> str: """ Returns an HTML-formatted underline string. @@ -109,7 +109,7 @@ def hunderline(content: str, escape: bool=False) -> str: return '{}'.format(escape_html(content) if escape else content) -def mstrikethrough(content: str, escape: bool=False) -> str: +def mstrikethrough(content: str, escape: bool=True) -> str: """ Returns a Markdown-formatted strikethrough string. @@ -119,7 +119,7 @@ def mstrikethrough(content: str, escape: bool=False) -> str: return '~{}~'.format(escape_markdown(content) if escape else content) -def hstrikethrough(content: str, escape: bool=False) -> str: +def hstrikethrough(content: str, escape: bool=True) -> str: """ Returns an HTML-formatted strikethrough string. @@ -129,7 +129,7 @@ def hstrikethrough(content: str, escape: bool=False) -> str: return '{}'.format(escape_html(content) if escape else content) -def mspoiler(content: str, escape: bool=False) -> str: +def mspoiler(content: str, escape: bool=True) -> str: """ Returns a Markdown-formatted spoiler string. @@ -139,7 +139,7 @@ def mspoiler(content: str, escape: bool=False) -> str: return '||{}||'.format(escape_markdown(content) if escape else content) -def hspoiler(content: str, escape: bool=False) -> str: +def hspoiler(content: str, escape: bool=True) -> str: """ Returns an HTML-formatted spoiler string. @@ -149,7 +149,7 @@ def hspoiler(content: str, escape: bool=False) -> str: return '{}'.format(escape_html(content) if escape else content) -def mlink(content: str, url: str, escape: bool=False) -> str: +def mlink(content: str, url: str, escape: bool=True) -> str: """ Returns a Markdown-formatted link string. @@ -160,7 +160,7 @@ def mlink(content: str, url: str, escape: bool=False) -> str: return '[{}]({})'.format(escape_markdown(content), escape_markdown(url) if escape else content) -def hlink(content: str, url: str, escape: bool=False) -> str: +def hlink(content: str, url: str, escape: bool=True) -> str: """ Returns an HTML-formatted link string. @@ -171,7 +171,7 @@ def hlink(content: str, url: str, escape: bool=False) -> str: return '{}'.format(escape_html(url), escape_html(content) if escape else content) -def mcode(content: str, language: str="", escape: bool=False) -> str: +def mcode(content: str, language: str="", escape: bool=True) -> str: """ Returns a Markdown-formatted code string. @@ -181,7 +181,7 @@ def mcode(content: str, language: str="", escape: bool=False) -> str: return '```{}\n{}```'.format(language, escape_markdown(content) if escape else content) -def hcode(content: str, escape: bool=False) -> str: +def hcode(content: str, escape: bool=True) -> str: """ Returns an HTML-formatted code string. @@ -191,7 +191,7 @@ def hcode(content: str, escape: bool=False) -> str: return '{}'.format(escape_html(content) if escape else content) -def hpre(content: str, escape: bool=False, language: str="") -> str: +def hpre(content: str, escape: bool=True, language: str="") -> str: """ Returns an HTML-formatted preformatted string. From 91b665ea9496dd6b18ee4cf391ce54305b66cc6b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 15 May 2022 00:59:35 +0300 Subject: [PATCH 0975/1808] Poll type parameter parse fix Plus some typo --- .github/PULL_REQUEST_TEMPLATE.md | 5 ++--- telebot/asyncio_storage/redis_storage.py | 6 +++--- telebot/storage/redis_storage.py | 6 +++--- telebot/types.py | 14 ++++++++++---- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b46f5a645..a34f4059e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,6 +10,5 @@ OS: ## Checklist: - [ ] I added/edited example on new feature/change (if exists) -- [ ] My changes won't break backend compatibility -- [ ] I made changes for async and sync - +- [ ] My changes won't break backward compatibility +- [ ] I made changes both for sync and async diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 0cf52b90f..e31ddb0a1 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -35,7 +35,7 @@ async def get_record(self, key): """ Function to get record from database. It has nothing to do with states. - Made for backend compatibility + Made for backward compatibility """ result = await self.redis.get(self.prefix+str(key)) if result: return json.loads(result) @@ -45,7 +45,7 @@ async def set_record(self, key, value): """ Function to set record to database. It has nothing to do with states. - Made for backend compatibility + Made for backward compatibility """ await self.redis.set(self.prefix+str(key), json.dumps(value)) @@ -55,7 +55,7 @@ async def delete_record(self, key): """ Function to delete record from database. It has nothing to do with states. - Made for backend compatibility + Made for backward compatibility """ await self.redis.delete(self.prefix+str(key)) return True diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index cd4058777..2a5fb4a37 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -28,7 +28,7 @@ def get_record(self, key): """ Function to get record from database. It has nothing to do with states. - Made for backend compatibility + Made for backward compatibility """ connection = Redis(connection_pool=self.redis) result = connection.get(self.prefix+str(key)) @@ -40,7 +40,7 @@ def set_record(self, key, value): """ Function to set record to database. It has nothing to do with states. - Made for backend compatibility + Made for backward compatibility """ connection = Redis(connection_pool=self.redis) connection.set(self.prefix+str(key), json.dumps(value)) @@ -51,7 +51,7 @@ def delete_record(self, key): """ Function to delete record from database. It has nothing to do with states. - Made for backend compatibility + Made for backward compatibility """ connection = Redis(connection_pool=self.redis) connection.delete(self.prefix+str(key)) diff --git a/telebot/types.py b/telebot/types.py index 9793417ea..ff08fc697 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2774,23 +2774,29 @@ def de_json(cls, json_string): obj['explanation_entities'] = Message.parse_entities(obj['explanation_entities']) return cls(**obj) + # noinspection PyShadowingBuiltins def __init__( self, question, options, - poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, + 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, **kwargs): + open_period=None, close_date=None, poll_type=None, **kwargs): self.id: str = poll_id self.question: str = question self.options: List[PollOption] = options self.total_voter_count: int = total_voter_count self.is_closed: bool = is_closed self.is_anonymous: bool = is_anonymous - self.type: str = poll_type + self.type: str = type + if poll_type is not None: + # Wrong param name backward compatibility + logger.warning("Poll: poll_type parameter is deprecated. Use type instead.") + if type is None: + self.type: str = poll_type self.allows_multiple_answers: bool = allows_multiple_answers self.correct_option_id: int = correct_option_id self.explanation: str = explanation - self.explanation_entities: List[MessageEntity] = explanation_entities # Default state of entities is None. if (explanation_entities is not None) else [] + self.explanation_entities: List[MessageEntity] = explanation_entities self.open_period: int = open_period self.close_date: int = close_date From 3ff7e28467e5a5f610a7a4650cf9e28efe7e7894 Mon Sep 17 00:00:00 2001 From: everpcpc Date: Sun, 15 May 2022 14:29:29 +0800 Subject: [PATCH 0976/1808] fix: warning none_stop parameter is deprecated --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5917ca734..09b30027a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -566,7 +566,7 @@ def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polli while not self.__stop_polling.is_set(): try: - self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, + self.polling(non_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, logger_level=logger_level, allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: From f526a9d8a4def9dd8ba435af836c558534f92589 Mon Sep 17 00:00:00 2001 From: m-cyx <62351148+m-cyx@users.noreply.github.com> Date: Sun, 15 May 2022 22:46:15 +0300 Subject: [PATCH 0977/1808] Small srtring formatter fix --- examples/custom_states.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/custom_states.py b/examples/custom_states.py index 2f9a2bc60..72c96df9b 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -60,7 +60,7 @@ def name_get(message): """ State 1. Will process when user's state is MyStates.name. """ - bot.send_message(message.chat.id, f'Now write me a surname') + bot.send_message(message.chat.id, 'Now write me a surname') bot.set_state(message.from_user.id, MyStates.surname, message.chat.id) with bot.retrieve_data(message.from_user.id, message.chat.id) as data: data['name'] = message.text @@ -83,7 +83,11 @@ def ready_for_answer(message): State 3. Will process when user's state is MyStates.age. """ with bot.retrieve_data(message.from_user.id, message.chat.id) as data: - bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") + msg = ("Ready, take a look:\n" + f"Name: {data['name']}\n" + f"Surname: {data['surname']}\n" + f"Age: {message.text}") + bot.send_message(message.chat.id, msg, parse_mode="html") bot.delete_state(message.from_user.id, message.chat.id) #incorrect number @@ -99,4 +103,4 @@ def age_incorrect(message): bot.add_custom_filter(custom_filters.StateFilter(bot)) bot.add_custom_filter(custom_filters.IsDigitFilter()) -bot.infinity_polling(skip_pending=True) \ No newline at end of file +bot.infinity_polling(skip_pending=True) From 8fefd7b5b336f0333a05a9f6b364f404c9077e3b Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Fri, 20 May 2022 23:00:38 +0530 Subject: [PATCH 0978/1808] Create create_invite_link.py Added create_chat_invite_link() func example --- examples/create_invite_link.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 examples/create_invite_link.py diff --git a/examples/create_invite_link.py b/examples/create_invite_link.py new file mode 100644 index 000000000..71c6ed460 --- /dev/null +++ b/examples/create_invite_link.py @@ -0,0 +1,36 @@ +import telebot, threading +from time import sleep, time +from telebot import InlineKeyboardMarkup as ikm #Only for creating Inline Buttons, not necessary for creating Invite Links +from telebot import InlineKeyboardButton as ikb #Only for creating Inline Buttons, not necessary for creating Invite Links + +Token = "api_token" #Your Bot Access Token +Group_ID = -1234567890 #Group ID for which invite link is to be created + +bot = telebot.TeleBot(Token, parse_mode="HTML") + +#/start command message +@bot.message_handler(commands=['start']) +def startmsg(msg): + bot.reply_to(msg, "Hey there, I'm a bot made by pyTelegramBotAPI!") + +#Get notified of incoming members in group +@bot.message_handler(content_types=['new_chat_members']) +def newmember(msg): + #Create an invite link class that contains info about the created invite link using create_chat_invite_link() with parameters + invite = bot.create_chat_invite_link(Group_ID, member_limit=1, expire_date=int(time())+45) #Here, the link will auto-expire in 45 seconds + InviteLink = invite.invite_link #Get the actual invite link from 'invite' class + mrkplink = ikm() #Created Inline Markup Keyboard + mrkplink.add(ikb("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Markup Keyboard + bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group." reply_markup=mrkplink) + + #This will send a message with the newly-created invite link as markup button. + #The member limit will be 1 and expiring time will be 45 sec. + + + + +while True: + try: + bot.infinity_polling() + except: + sleep(0.04) From 28662876a22e813c608acebf258db2051d9cef6d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 21 May 2022 17:09:59 +0500 Subject: [PATCH 0979/1808] Add workspace exception in gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2586c7116..fa4a91e77 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ testMain.py #VS Code .vscode/ .DS_Store +*.code-workspace # documentation _build/ From 33375ac135c41d1e1cd03df2ec84e55155f2a569 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 21 May 2022 17:10:29 +0500 Subject: [PATCH 0980/1808] Added ability to set default parse_mode for explanation_parse_mode in send_poll --- telebot/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 09b30027a..abadfe557 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2510,6 +2510,8 @@ def send_poll( if isinstance(question, types.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 + return types.Message.de_json( apihelper.send_poll( self.token, chat_id, From d954f8f5b33c71d184c25670ccc4e5b385933d76 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 21 May 2022 17:10:45 +0500 Subject: [PATCH 0981/1808] Fixed default parse mode in asynctelebot --- telebot/async_telebot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 35c2da8c1..e2df52509 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1674,6 +1674,8 @@ async def copy_message( :param protect_content: :return: API reply. """ + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, @@ -3112,6 +3114,8 @@ async def send_poll( if isinstance(question, types.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 + return types.Message.de_json( await asyncio_helper.send_poll( self.token, chat_id, From d6e93f85f1a29ba27e395becf7653b9ea5168dfb Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 21 May 2022 17:10:57 +0500 Subject: [PATCH 0982/1808] Improved the comment for quick_markup --- telebot/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index f116d9e3b..31522e5a5 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -370,7 +370,8 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I 'switch_inline_query_current_chat': None, 'callback_game': None, 'pay': None, - 'login_url': None + 'login_url': None, + 'web_app': None } :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} From ccc09ffaf33c6af10c8ab268c5d8d4485f14ae0e Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 21 May 2022 17:38:16 +0500 Subject: [PATCH 0983/1808] Aiohttp client session management improvements. --- telebot/asyncio_helper.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index dde8c8544..84837d971 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1,6 +1,7 @@ import asyncio # for future uses import aiohttp from telebot import types +import ssl, certifi try: import ujson as json @@ -19,21 +20,23 @@ FILE_URL = None -CONNECT_TIMEOUT = 15 -READ_TIMEOUT = 30 - -LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates) -REQUEST_TIMEOUT = 10 +REQUEST_TIMEOUT = None MAX_RETRIES = 3 REQUEST_LIMIT = 50 class SessionManager: def __init__(self) -> None: - self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=REQUEST_LIMIT)) + self.ssl_context = ssl.create_default_context(cafile=certifi.where()) + self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector( + limit=REQUEST_LIMIT, ssl=self.ssl_context + )) + async def create_session(self): - self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=REQUEST_LIMIT)) + self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector( + limit=REQUEST_LIMIT, ssl=self.ssl_context + )) return self.session async def get_session(self): @@ -61,10 +64,11 @@ async def _process_request(token, url, method='get', params=None, files=None, re current_try +=1 try: async with session.request(method=method, url=API_URL.format(token, url), data=params, timeout=timeout, proxy=proxy) as resp: + got_result = True logger.debug("Request: method={0} url={1} params={2} files={3} request_timeout={4} current_try={5}".format(method, url, params, files, request_timeout, current_try).replace(token, token.split(':')[0] + ":{TOKEN}")) + json_result = await _check_result(url, resp) if json_result: - got_result = True return json_result['result'] except (ApiTelegramException,ApiInvalidJSONException, ApiHTTPException) as e: raise e From 5e3fd17436a2a97e3cb0bd7a90eff3ef07857f13 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 21 May 2022 17:39:45 +0500 Subject: [PATCH 0984/1808] Added ability to hide a link in text --- telebot/formatting.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index d13ba88bf..abec962d7 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -198,4 +198,14 @@ def hpre(content: str, escape: bool=True, language: str="") -> str: :param content: The string to preformatted. :param escape: True if you need to escape special characters. """ - return '
{}
'.format(language, escape_html(content) if escape else content) \ No newline at end of file + return '
{}
'.format(language, escape_html(content) if escape else content) + + +def hide_link(url: str) -> str: + """ + Hide url of an image. + + :param url: + :return: str + """ + return f'' \ No newline at end of file From 388306b7fea7344bd163ce19b2d38857d0270b26 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 21 May 2022 17:45:59 +0500 Subject: [PATCH 0985/1808] Updated examples --- examples/asynchronous_telebot/formatting_example.py | 2 ++ examples/formatting_example.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/asynchronous_telebot/formatting_example.py b/examples/asynchronous_telebot/formatting_example.py index 95c3d35b0..687daea0d 100644 --- a/examples/asynchronous_telebot/formatting_example.py +++ b/examples/asynchronous_telebot/formatting_example.py @@ -36,6 +36,8 @@ async def start_message(message): formatting.hunderline(message.from_user.first_name), formatting.hstrikethrough(message.from_user.first_name), formatting.hcode(message.from_user.first_name), + # hide_link is only for html + formatting.hide_link('https://telegra.ph/file/c158e3a6e2a26a160b253.jpg'), separator=" " ), parse_mode='HTML' diff --git a/examples/formatting_example.py b/examples/formatting_example.py index 1068acc32..ae92252bf 100644 --- a/examples/formatting_example.py +++ b/examples/formatting_example.py @@ -36,6 +36,8 @@ def start_message(message): formatting.hunderline(message.from_user.first_name), formatting.hstrikethrough(message.from_user.first_name), formatting.hcode(message.from_user.first_name), + # hide_link is only for html + formatting.hide_link('https://telegra.ph/file/c158e3a6e2a26a160b253.jpg'), separator=" " ), parse_mode='HTML' From 0c59d1187ecaaea1cc4a167623476e9d1ccd36b7 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 22 May 2022 00:11:16 +0500 Subject: [PATCH 0986/1808] Update requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index b5086dc40..53bf95b43 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ pytest requests==2.20.0 wheel==0.24.0 aiohttp>=3.8.0,<3.9.0 +certifi From 8da749ee051046717adf8c46efeac80531784272 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 22 May 2022 01:02:55 +0500 Subject: [PATCH 0987/1808] Remove ssl --- requirements.txt | 3 +-- telebot/asyncio_helper.py | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 53bf95b43..d2b82566c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ pytest requests==2.20.0 wheel==0.24.0 -aiohttp>=3.8.0,<3.9.0 -certifi +aiohttp>=3.8.0,<3.9.0 \ No newline at end of file diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 84837d971..63c1a532d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1,7 +1,6 @@ import asyncio # for future uses import aiohttp from telebot import types -import ssl, certifi try: import ujson as json @@ -27,15 +26,14 @@ class SessionManager: def __init__(self) -> None: - self.ssl_context = ssl.create_default_context(cafile=certifi.where()) self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector( - limit=REQUEST_LIMIT, ssl=self.ssl_context + limit=REQUEST_LIMIT )) async def create_session(self): self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector( - limit=REQUEST_LIMIT, ssl=self.ssl_context + limit=REQUEST_LIMIT )) return self.session From 6bb47e9a449a0058ffd9c8bd1605116ec1ff5472 Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Sun, 22 May 2022 01:38:32 +0530 Subject: [PATCH 0988/1808] Update create_invite_link.py Added .types while importing inline markup keyboards (fix) Removed threading import since message is not to be deleted --- examples/create_invite_link.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/create_invite_link.py b/examples/create_invite_link.py index 71c6ed460..9413549e8 100644 --- a/examples/create_invite_link.py +++ b/examples/create_invite_link.py @@ -1,7 +1,7 @@ -import telebot, threading +import telebot from time import sleep, time -from telebot import InlineKeyboardMarkup as ikm #Only for creating Inline Buttons, not necessary for creating Invite Links -from telebot import InlineKeyboardButton as ikb #Only for creating Inline Buttons, not necessary for creating Invite Links +from telebot.types import InlineKeyboardMarkup as ikm #Only for creating Inline Buttons, not necessary for creating Invite Links +from telebot.types import InlineKeyboardButton as ikb #Only for creating Inline Buttons, not necessary for creating Invite Links Token = "api_token" #Your Bot Access Token Group_ID = -1234567890 #Group ID for which invite link is to be created From 7edaa51995604fb11d3b6cf298c6b5510a98d178 Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Sun, 22 May 2022 01:43:24 +0530 Subject: [PATCH 0989/1808] Update create_invite_link.py Removed while loop --- examples/create_invite_link.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/create_invite_link.py b/examples/create_invite_link.py index 9413549e8..e7fc2ed6b 100644 --- a/examples/create_invite_link.py +++ b/examples/create_invite_link.py @@ -29,8 +29,4 @@ def newmember(msg): -while True: - try: - bot.infinity_polling() - except: - sleep(0.04) +bot.infinity_polling() From d6ec104829d30c611ae42c12c6efda5cea7c76c2 Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Sun, 22 May 2022 01:48:48 +0530 Subject: [PATCH 0990/1808] Update create_invite_link.py Imported both inline keyboards in same line:| --- examples/create_invite_link.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/create_invite_link.py b/examples/create_invite_link.py index e7fc2ed6b..6259b9273 100644 --- a/examples/create_invite_link.py +++ b/examples/create_invite_link.py @@ -1,7 +1,6 @@ import telebot from time import sleep, time -from telebot.types import InlineKeyboardMarkup as ikm #Only for creating Inline Buttons, not necessary for creating Invite Links -from telebot.types import InlineKeyboardButton as ikb #Only for creating Inline Buttons, not necessary for creating Invite Links +from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton #Only for creating Inline Buttons, not necessary for creating Invite Links Token = "api_token" #Your Bot Access Token Group_ID = -1234567890 #Group ID for which invite link is to be created @@ -19,8 +18,8 @@ def newmember(msg): #Create an invite link class that contains info about the created invite link using create_chat_invite_link() with parameters invite = bot.create_chat_invite_link(Group_ID, member_limit=1, expire_date=int(time())+45) #Here, the link will auto-expire in 45 seconds InviteLink = invite.invite_link #Get the actual invite link from 'invite' class - mrkplink = ikm() #Created Inline Markup Keyboard - mrkplink.add(ikb("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Markup Keyboard + mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup + mrkplink.add(InlineKeyboardButton("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Keyboard bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group." reply_markup=mrkplink) #This will send a message with the newly-created invite link as markup button. From 74d0604c053329d77c1a795f67557c8ac6ad7f80 Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Sun, 22 May 2022 01:54:02 +0530 Subject: [PATCH 0991/1808] Update create_invite_link.py Bug fix (Real FINAL FIX!!) --- examples/create_invite_link.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/create_invite_link.py b/examples/create_invite_link.py index 6259b9273..ffbc8b977 100644 --- a/examples/create_invite_link.py +++ b/examples/create_invite_link.py @@ -20,7 +20,7 @@ def newmember(msg): InviteLink = invite.invite_link #Get the actual invite link from 'invite' class mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup mrkplink.add(InlineKeyboardButton("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Keyboard - bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group." reply_markup=mrkplink) + bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group.", reply_markup=mrkplink) #This will send a message with the newly-created invite link as markup button. #The member limit will be 1 and expiring time will be 45 sec. From 1d0efce76ec8695498f788348bd21f34f232d9d3 Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Sun, 22 May 2022 02:17:21 +0530 Subject: [PATCH 0992/1808] Update create_invite_link.py Fix --- examples/create_invite_link.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/create_invite_link.py b/examples/create_invite_link.py index ffbc8b977..467989817 100644 --- a/examples/create_invite_link.py +++ b/examples/create_invite_link.py @@ -1,6 +1,7 @@ import telebot from time import sleep, time from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton #Only for creating Inline Buttons, not necessary for creating Invite Links +mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup Token = "api_token" #Your Bot Access Token Group_ID = -1234567890 #Group ID for which invite link is to be created @@ -18,8 +19,9 @@ def newmember(msg): #Create an invite link class that contains info about the created invite link using create_chat_invite_link() with parameters invite = bot.create_chat_invite_link(Group_ID, member_limit=1, expire_date=int(time())+45) #Here, the link will auto-expire in 45 seconds InviteLink = invite.invite_link #Get the actual invite link from 'invite' class - mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup + mrkplink.add(InlineKeyboardButton("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Keyboard + bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group.", reply_markup=mrkplink) #This will send a message with the newly-created invite link as markup button. From 72aaf44dc76bdab03ab626f1e51340fba008687f Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 22 May 2022 01:50:38 +0500 Subject: [PATCH 0993/1808] Update README.md --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f0b03badb..a1a9f2dd6 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,25 @@ def start(message): There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory. #### Class-based middlewares -There are class-based middlewares. Check out in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/middleware/class_based) +There are class-based middlewares. +Basic class-based middleware looks like this: +```python +class Middleware(BaseMiddleware): + def __init__(self): + self.update_types = ['message'] + def pre_process(self, message, data): + data['foo'] = 'Hello' # just for example + # we edited the data. now, this data is passed to handler. + # return SkipHandler() -> this will skip handler + # return CancelUpdate() -> this will cancel update + def post_process(self, message, data, exception=None): + print(data['foo']) + if exception: # check for exception + print(exception) +``` +Class-based middleware should have to functions: post and pre process. +So, as you can see, class-based middlewares work before and after handler execution. +For more, check out in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/middleware/class_based) #### Custom filters Also, you can use built-in custom filters. Or, you can create your own filter. From ee7adb00dfcb9495ddbf078638b8b5391f0ade29 Mon Sep 17 00:00:00 2001 From: Advik Singh Somvanshi Date: Mon, 23 May 2022 13:27:48 +0530 Subject: [PATCH 0994/1808] Re-fix inlinekeyboardmarkup --- examples/create_invite_link.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/create_invite_link.py b/examples/create_invite_link.py index 467989817..eb8cebf89 100644 --- a/examples/create_invite_link.py +++ b/examples/create_invite_link.py @@ -1,7 +1,6 @@ import telebot from time import sleep, time from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton #Only for creating Inline Buttons, not necessary for creating Invite Links -mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup Token = "api_token" #Your Bot Access Token Group_ID = -1234567890 #Group ID for which invite link is to be created @@ -20,6 +19,7 @@ def newmember(msg): invite = bot.create_chat_invite_link(Group_ID, member_limit=1, expire_date=int(time())+45) #Here, the link will auto-expire in 45 seconds InviteLink = invite.invite_link #Get the actual invite link from 'invite' class + mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup mrkplink.add(InlineKeyboardButton("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Keyboard bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group.", reply_markup=mrkplink) From 82f056e88af8233b9b413d8aec1c1c322c09d9bc Mon Sep 17 00:00:00 2001 From: Soham Datta <83786816+TECH-SAVVY-GUY@users.noreply.github.com> Date: Mon, 30 May 2022 14:52:49 +0530 Subject: [PATCH 0995/1808] Added `WebApp` functions `validate_WebApp_data()` - Use to validate the data received by the WebApp from Telegram. `parse_WebApp_data()` - Use to parse the data sent to the WebApp from the bot's backend. --- telebot/util.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index 31522e5a5..f8e4db588 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -5,6 +5,10 @@ import threading import traceback from typing import Any, Callable, List, Dict, Optional, Union +import hmac +import json +from hashlib import sha256 +from urllib.parse import parse_qsl # noinspection PyPep8Naming import queue as Queue @@ -518,3 +522,36 @@ def antiflood(function, *args, **kwargs): msg = function(*args, **kwargs) finally: return msg + + +def parse_WebApp_data(token: str, raw_init_data: str): + is_valid = validate_WebApp_data(token, raw_init_data) + if not is_valid: + return False + + result = {} + for key, value in parse_qsl(raw_init_data): + try: + value = json.loads(value) + except json.JSONDecodeError: + result[key] = value + else: + result[key] = value + return result + + +def validate_WebApp_data(token, raw_init_data): + try: + parsed_data = dict(parse_qsl(raw_init_data)) + except ValueError: + return False + if "hash" not in parsed_data: + return False + + init_data_hash = parsed_data.pop('hash') + data_check_string = "\n".join(f"{key}={value}" for key, value in sorted(parsed_data.items())) + secret_key = hmac.new(key=b"WebAppData", msg=token.encode(), digestmod=sha256) + + return hmac.new(secret_key.digest(), data_check_string.encode(), sha256).hexdigest() == init_data_hash + + From 4401780ba9754653ee0075dc6638b01a641d90f3 Mon Sep 17 00:00:00 2001 From: Soham Datta <83786816+TECH-SAVVY-GUY@users.noreply.github.com> Date: Mon, 30 May 2022 17:18:03 +0530 Subject: [PATCH 0996/1808] Update `WebApp()` functions Adjusted function name case to pythonic style. --- telebot/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index f8e4db588..43852289c 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -524,7 +524,7 @@ def antiflood(function, *args, **kwargs): return msg -def parse_WebApp_data(token: str, raw_init_data: str): +def parse_webapp_data(token: str, raw_init_data: str): is_valid = validate_WebApp_data(token, raw_init_data) if not is_valid: return False @@ -540,7 +540,7 @@ def parse_WebApp_data(token: str, raw_init_data: str): return result -def validate_WebApp_data(token, raw_init_data): +def validate_webapp_data(token, raw_init_data): try: parsed_data = dict(parse_qsl(raw_init_data)) except ValueError: From 42ce47914d7afe56cdfb1146c8cff423fc72f3c4 Mon Sep 17 00:00:00 2001 From: Soham Datta <83786816+TECH-SAVVY-GUY@users.noreply.github.com> Date: Mon, 30 May 2022 17:21:33 +0530 Subject: [PATCH 0997/1808] Fix typo in `WebApp()` functions --- telebot/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 43852289c..2561c21ad 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -524,7 +524,7 @@ def antiflood(function, *args, **kwargs): return msg -def parse_webapp_data(token: str, raw_init_data: str): +def parse_web_app_data(token: str, raw_init_data: str): is_valid = validate_WebApp_data(token, raw_init_data) if not is_valid: return False @@ -540,7 +540,7 @@ def parse_webapp_data(token: str, raw_init_data: str): return result -def validate_webapp_data(token, raw_init_data): +def validate_web_app_data(token, raw_init_data): try: parsed_data = dict(parse_qsl(raw_init_data)) except ValueError: From 28ae0867f834fde91c1c2d7267a195283aad4876 Mon Sep 17 00:00:00 2001 From: Tushar maharana <54012021+tusharhero@users.noreply.github.com> Date: Tue, 31 May 2022 22:58:03 +0530 Subject: [PATCH 0998/1808] Added my bot made using pyTelegramBotAPI source code available here https://github.com/tusharhero/bincode-telegram-bot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a1a9f2dd6..713f4776a 100644 --- a/README.md +++ b/README.md @@ -845,6 +845,7 @@ Here are some examples of template: ## Bots using this library +* [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. From 7cdb48c3e075bbe57ea49b3b460c7d5e6e25871c Mon Sep 17 00:00:00 2001 From: Tushar maharana <54012021+tusharhero@users.noreply.github.com> Date: Wed, 1 Jun 2022 10:22:22 +0530 Subject: [PATCH 0999/1808] brought my bot ot end --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 713f4776a..b344feb7e 100644 --- a/README.md +++ b/README.md @@ -845,7 +845,6 @@ Here are some examples of template: ## Bots using this library -* [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. @@ -897,5 +896,5 @@ Here are some examples of template: * [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. * [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English * [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. - +* [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 5a60846c7fd544c42312cea1013c8fac4b416ad8 Mon Sep 17 00:00:00 2001 From: Andrey M Date: Wed, 1 Jun 2022 19:07:54 +0300 Subject: [PATCH 1000/1808] add hydrolib_bot to Bots using this library --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b344feb7e..ef79d1e44 100644 --- a/README.md +++ b/README.md @@ -897,4 +897,6 @@ Here are some examples of template: * [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English * [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. * [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. +* [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...). + **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From e689e968dbd240b08ca88f8a487a33c25ce2e452 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Jun 2022 21:33:05 +0500 Subject: [PATCH 1001/1808] Fix bug with unsaving data --- telebot/asyncio_storage/pickle_storage.py | 2 ++ telebot/storage/pickle_storage.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index 4928d4a1b..49fe3bedd 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -51,9 +51,11 @@ async def set_state(self, chat_id, user_id, state): if chat_id in self.data: if user_id in self.data[chat_id]: self.data[chat_id][user_id]['state'] = state + self.update_data() return True else: self.data[chat_id][user_id] = {'state': state, 'data': {}} + self.update_data() return True self.data[chat_id] = {user_id: {'state': state, 'data': {}}} self.update_data() diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 39b10a3aa..ff72ac3b6 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -57,9 +57,11 @@ def set_state(self, chat_id, user_id, state): if chat_id in self.data: if user_id in self.data[chat_id]: self.data[chat_id][user_id]['state'] = state + self.update_data() return True else: self.data[chat_id][user_id] = {'state': state, 'data': {}} + self.update_data() return True self.data[chat_id] = {user_id: {'state': state, 'data': {}}} self.update_data() From b2662274f93d09326cc1502d9c109a0cb665ae7d Mon Sep 17 00:00:00 2001 From: e323w <88164836+e323w@users.noreply.github.com> Date: Wed, 15 Jun 2022 03:24:51 +0400 Subject: [PATCH 1002/1808] Fixed async long polling --- telebot/asyncio_helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 63c1a532d..a7bcccfa3 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -217,11 +217,11 @@ async def get_updates(token, offset=None, limit=None, params = {} if offset: params['offset'] = offset - elif limit: + if limit: params['limit'] = limit - elif timeout: + if timeout: params['timeout'] = timeout - elif allowed_updates: + if allowed_updates: params['allowed_updates'] = allowed_updates return await _process_request(token, method_name, params=params, request_timeout=request_timeout) From 24cdcb1bcc6c576b428180f1f463fdcffc69bb97 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 21 Jun 2022 14:22:57 +0500 Subject: [PATCH 1003/1808] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ef79d1e44..027139d09 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.0! +##

Supported Bot API version: 6.1!

Official documentation

@@ -727,6 +727,7 @@ Result will be: ## API conformance +* ✔ [Bot API 6.1](https://core.telegram.org/bots/api#june-20-2022) * ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022) * ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022) * ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021) From 0d0f9ef33015c8d6ced5ed218777b678dab3e979 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 21 Jun 2022 15:20:55 +0500 Subject: [PATCH 1004/1808] Added secret token and create_invoice_link --- telebot/__init__.py | 68 ++++++++++++++++++++++++++++++++++++++++++-- telebot/apihelper.py | 43 +++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index abadfe557..85454c5f4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -261,7 +261,7 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a self.reply_backend.load_handlers(filename, del_file_after_loading) def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, - drop_pending_updates = None, timeout=None): + drop_pending_updates = None, timeout=None, secret_token=None): """ Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, @@ -286,10 +286,11 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ resolved through DNS :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout + :param secret_token: Secret token to be used to verify the webhook request. :return: API reply. """ return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, - drop_pending_updates, timeout) + drop_pending_updates, timeout, secret_token) def delete_webhook(self, drop_pending_updates=None, timeout=None): """ @@ -2462,6 +2463,69 @@ def send_invoice( max_tip_amount, suggested_tip_amounts, protect_content) return types.Message.de_json(result) + + def create_invoice_link(self, + title: str, description: str, payload:str, provider_token: str, + currency: str, prices: List[types.LabeledPrice], + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[List[int]]=None, + provider_data: Optional[str]=None, + photo_url: Optional[str]=None, + photo_size: Optional[int]=None, + photo_width: Optional[int]=None, + photo_height: Optional[int]=None, + need_name: Optional[bool]=None, + need_phone_number: Optional[bool]=None, + need_email: Optional[bool]=None, + need_shipping_address: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, + is_flexible: Optional[bool]=None) -> str: + + """ + Use this method to create a link for an invoice. + Returns the created invoice link as String on success. + + Telegram documentation: + https://core.telegram.org/bots/api#createinvoicelink + + :param title: Product name, 1-32 characters + :param description: Product description, 1-255 characters + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, + use for your internal processes. + :param provider_token: Payments provider token, obtained via @Botfather + :param currency: Three-letter ISO 4217 currency code, + see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components + (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :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 + :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 photo_url: URL of the product photo for the invoice. Can be a photo of the goods + :param photo_size: Photo size in bytes + :param photo_width: Photo width + :param photo_height: Photo height + :param need_name: Pass True, if you require the user's full name to complete the order + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :param need_email: Pass True, if you require the user's email to complete the order + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :param is_flexible: Pass True, if the final price depends on the shipping method + + :return: Created invoice link as String on success. + """ + result = apihelper.create_invoice_link( + self.token, title, description, payload, provider_token, + currency, prices, max_tip_amount, suggested_tip_amounts, provider_data, + photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, + need_email, need_shipping_address, send_phone_number_to_provider, + send_email_to_provider, is_flexible) + return result + + + # noinspection PyShadowingBuiltins # TODO: rewrite this method like in API def send_poll( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8d4b2f765..ff8c8e231 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -268,7 +268,7 @@ def send_message( def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, - drop_pending_updates = None, timeout=None): + drop_pending_updates = None, timeout=None, secret_token=None): method_url = r'setWebhook' payload = { 'url': url if url else "", @@ -286,6 +286,8 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed payload['drop_pending_updates'] = drop_pending_updates if timeout: payload['timeout'] = timeout + if secret_token: + payload['secret_token'] = secret_token return _make_request(token, method_url, params=payload, files=files) @@ -1622,6 +1624,45 @@ def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResul return _make_request(token, method_url, params=payload, method='post') +def create_invoice_link(token, title, description, payload, provider_token, + currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, + photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, + need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, + send_email_to_provider=None, is_flexible=None): + method_url = r'createInvoiceLink' + payload = {'title': title, 'description': description, 'payload': payload, 'provider_token': provider_token, + 'currency': currency, 'prices': _convert_list_json_serializable(prices)} + if max_tip_amount: + payload['max_tip_amount'] = max_tip_amount + if suggested_tip_amounts: + payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) + if provider_data: + payload['provider_data'] = provider_data + if photo_url: + payload['photo_url'] = photo_url + if photo_size: + payload['photo_size'] = photo_size + if photo_width: + payload['photo_width'] = photo_width + if photo_height: + payload['photo_height'] = photo_height + if need_name: + payload['need_name'] = need_name + if need_phone_number: + payload['need_phone_number'] = need_phone_number + if need_email: + payload['need_email'] = need_email + if need_shipping_address: + payload['need_shipping_address'] = need_shipping_address + if send_phone_number_to_provider: + payload['send_phone_number_to_provider'] = send_phone_number_to_provider + if send_email_to_provider: + payload['send_email_to_provider'] = send_email_to_provider + if is_flexible: + payload['is_flexible'] = is_flexible + return _make_request(token, method_url, params=payload, method='post') + + # noinspection PyShadowingBuiltins def send_poll( token, chat_id, From f52124827fa813936c89c36543aedc37c01cf1ed Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 21 Jun 2022 15:21:35 +0500 Subject: [PATCH 1005/1808] Added all new changes for types (is_premium, added_to_attachment_menu, and etc) --- telebot/types.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ff08fc697..4b252b966 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -215,7 +215,8 @@ def de_json(cls, json_string): return cls(**obj) 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, **kwargs): + can_join_groups=None, can_read_all_group_messages=None, supports_inline_queries=None, + is_premium=None, added_to_attachment_menu=None, **kwargs): self.id: int = id self.is_bot: bool = is_bot self.first_name: str = first_name @@ -225,6 +226,9 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.can_join_groups: bool = can_join_groups self.can_read_all_group_messages: bool = can_read_all_group_messages self.supports_inline_queries: bool = supports_inline_queries + self.is_premium: bool = is_premium + self.added_to_attachment_menu: bool = added_to_attachment_menu + @property def full_name(self): @@ -280,7 +284,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, description=None, invite_link=None, pinned_message=None, permissions=None, slow_mode_delay=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, **kwargs): + can_set_sticker_set=None, linked_chat_id=None, location=None, + join_to_send_messages=None, join_by_request=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -289,6 +294,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.last_name: str = last_name self.photo: ChatPhoto = photo self.bio: str = bio + self.join_to_send_messages: bool = join_to_send_messages + self.join_by_request: bool = join_by_request self.has_private_forwards: bool = has_private_forwards self.description: str = description self.invite_link: str = invite_link @@ -2576,10 +2583,13 @@ def de_json(cls, json_string): obj['thumb'] = None if 'mask_position' in obj: obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) + if 'premium_animation': + obj['premium_animation'] = File.de_json(obj['premium_animation']) return cls(**obj) def __init__(self, file_id, file_unique_id, width, height, is_animated, - is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): + is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, + premium_animation=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.width: int = width @@ -2591,6 +2601,7 @@ def __init__(self, file_id, file_unique_id, width, height, is_animated, self.set_name: str = set_name self.mask_position: MaskPosition = mask_position self.file_size: int = file_size + self.premium_animation: File = premium_animation From 7d931abe376195f7fd8b8e859d213dacbd942125 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 21 Jun 2022 15:22:00 +0500 Subject: [PATCH 1006/1808] Added secret token and create_invoice_link for asynctelebot --- telebot/async_telebot.py | 66 +++++++++++++++++++++++++++++++++++++-- telebot/asyncio_helper.py | 45 +++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e2df52509..156665770 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1384,7 +1384,7 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): self.current_states = StatePickleStorage(file_path=filename) async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, - drop_pending_updates = None, timeout=None): + drop_pending_updates = None, timeout=None, secret_token=None): """ Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, @@ -1409,10 +1409,11 @@ async def set_webhook(self, url=None, certificate=None, max_connections=None, al resolved through DNS :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout + :param secret_token: Secret token to be used to verify the webhook :return: """ return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, - drop_pending_updates, timeout) + drop_pending_updates, timeout, secret_token) @@ -3066,6 +3067,67 @@ async def send_invoice( max_tip_amount, suggested_tip_amounts, protect_content) return types.Message.de_json(result) + + async def create_invoice_link(self, + title: str, description: str, payload:str, provider_token: str, + currency: str, prices: List[types.LabeledPrice], + max_tip_amount: Optional[int] = None, + suggested_tip_amounts: Optional[List[int]]=None, + provider_data: Optional[str]=None, + photo_url: Optional[str]=None, + photo_size: Optional[int]=None, + photo_width: Optional[int]=None, + photo_height: Optional[int]=None, + need_name: Optional[bool]=None, + need_phone_number: Optional[bool]=None, + need_email: Optional[bool]=None, + need_shipping_address: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, + is_flexible: Optional[bool]=None) -> str: + + """ + Use this method to create a link for an invoice. + Returns the created invoice link as String on success. + + Telegram documentation: + https://core.telegram.org/bots/api#createinvoicelink + + :param title: Product name, 1-32 characters + :param description: Product description, 1-255 characters + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, + use for your internal processes. + :param provider_token: Payments provider token, obtained via @Botfather + :param currency: Three-letter ISO 4217 currency code, + see https://core.telegram.org/bots/payments#supported-currencies + :param prices: Price breakdown, a list of components + (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :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 + :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 photo_url: URL of the product photo for the invoice. Can be a photo of the goods + :param photo_size: Photo size in bytes + :param photo_width: Photo width + :param photo_height: Photo height + :param need_name: Pass True, if you require the user's full name to complete the order + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :param need_email: Pass True, if you require the user's email to complete the order + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :param is_flexible: Pass True, if the final price depends on the shipping method + + :return: Created invoice link as String on success. + """ + result = await asyncio_helper.create_invoice_link( + self.token, title, description, payload, provider_token, + currency, prices, max_tip_amount, suggested_tip_amounts, provider_data, + photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, + need_email, need_shipping_address, send_phone_number_to_provider, + send_email_to_provider, is_flexible) + return result + # noinspection PyShadowingBuiltins async def send_poll( self, chat_id: Union[int, str], question: str, options: List[str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index a7bcccfa3..51b60e98f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -171,7 +171,7 @@ async def download_file(token, file_path): async def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, - drop_pending_updates = None, timeout=None): + drop_pending_updates = None, timeout=None, secret_token=None): method_url = r'setWebhook' payload = { 'url': url if url else "", @@ -189,6 +189,8 @@ async def set_webhook(token, url=None, certificate=None, max_connections=None, a payload['drop_pending_updates'] = drop_pending_updates if timeout: payload['timeout'] = timeout + if secret_token: + payload['secret_token'] = secret_token return await _process_request(token, method_url, params=payload, files=files) @@ -1599,6 +1601,47 @@ async def delete_sticker_from_set(token, sticker): return await _process_request(token, method_url, params=payload, method='post') + +async def create_invoice_link(token, title, description, payload, provider_token, + currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, + photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, + need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, + send_email_to_provider=None, is_flexible=None): + method_url = r'createInvoiceLink' + payload = {'title': title, 'description': description, 'payload': payload, 'provider_token': provider_token, + 'currency': currency, 'prices': await _convert_list_json_serializable(prices)} + if max_tip_amount: + payload['max_tip_amount'] = max_tip_amount + if suggested_tip_amounts: + payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) + if provider_data: + payload['provider_data'] = provider_data + if photo_url: + payload['photo_url'] = photo_url + if photo_size: + payload['photo_size'] = photo_size + if photo_width: + payload['photo_width'] = photo_width + if photo_height: + payload['photo_height'] = photo_height + if need_name: + payload['need_name'] = need_name + if need_phone_number: + payload['need_phone_number'] = need_phone_number + if need_email: + payload['need_email'] = need_email + if need_shipping_address: + payload['need_shipping_address'] = need_shipping_address + if send_phone_number_to_provider: + payload['send_phone_number_to_provider'] = send_phone_number_to_provider + if send_email_to_provider: + payload['send_email_to_provider'] = send_email_to_provider + if is_flexible: + payload['is_flexible'] = is_flexible + return await _process_request(token, method_url, params=payload, method='post') + + + # noinspection PyShadowingBuiltins async def send_poll( token, chat_id, From d7f34ae37052e2ae61adef97e4f8a9d800ac6c76 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 21 Jun 2022 15:27:45 +0500 Subject: [PATCH 1007/1808] Fix the typo --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 4b252b966..ee67cb99a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2583,7 +2583,7 @@ def de_json(cls, json_string): obj['thumb'] = None if 'mask_position' in obj: obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) - if 'premium_animation': + if 'premium_animation' in obj: obj['premium_animation'] = File.de_json(obj['premium_animation']) return cls(**obj) From a7fb8a2becd5a5c5de8c49e16a0664579598ac46 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 21 Jun 2022 15:28:01 +0500 Subject: [PATCH 1008/1808] Implemented some tests --- tests/test_types.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index dedee19dd..29a50752c 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -6,10 +6,13 @@ def test_json_user(): - jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","last_name":")))","username":"rdss_bot","is_bot":true}' + jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","last_name":")))","username":"rdss_bot","is_bot":true, "is_premium":true, "added_to_attachment_menu": true}' u = types.User.de_json(jsonstring) assert u.id == 101176298 assert u.full_name == 'RDSSBOT )))' + assert u.is_premium is True + assert u.added_to_attachment_menu is True + def test_json_message(): @@ -155,12 +158,14 @@ def test_json_update(): def test_json_chat(): - json_string = r'{"id": -111111,"title": "Test Title","type": "group"}' + json_string = r'{"id": -111111,"title": "Test Title","type": "group", "join_to_send_messages": true, "join_by_request": true}' chat = types.Chat.de_json(json_string) assert chat.id == -111111 assert chat.type == 'group' assert chat.title == 'Test Title' - + assert chat.join_to_send_messages is True + assert chat.join_by_request is True + def test_InlineQueryResultCachedPhoto(): iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid') From 87698027447bdb09db3b0ffc99f48e2d4f0cfc37 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 21 Jun 2022 15:44:42 +0500 Subject: [PATCH 1009/1808] Bump version to 4.6.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 8344e6d42..20ef458f2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.5.1' +release = '4.6.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 6293a1798..ff913fb13 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.5.1' +__version__ = '4.6.0' From dd8125cbd0f92963fc80ecfc1db395ad93cad79a Mon Sep 17 00:00:00 2001 From: Alex Poklonsky Date: Thu, 23 Jun 2022 09:09:37 +0300 Subject: [PATCH 1010/1808] Rename `midddleware` to `middleware` in examples. --- .../middleware/i18n_middleware_example/main.py | 2 +- .../{i18n_base_midddleware.py => i18n_base_middleware.py} | 0 examples/middleware/class_based/i18n_middleware/main.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename examples/middleware/class_based/i18n_middleware/{i18n_base_midddleware.py => i18n_base_middleware.py} (100%) diff --git a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py index 1faeaad42..675c14071 100644 --- a/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py +++ b/examples/asynchronous_telebot/middleware/i18n_middleware_example/main.py @@ -56,7 +56,7 @@ from telebot import types from telebot.async_telebot import AsyncTeleBot from telebot.asyncio_filters import TextMatchFilter, TextFilter -from i18n_base_midddleware import I18N +from i18n_base_middleware import I18N from telebot.asyncio_storage.memory_storage import StateMemoryStorage diff --git a/examples/middleware/class_based/i18n_middleware/i18n_base_midddleware.py b/examples/middleware/class_based/i18n_middleware/i18n_base_middleware.py similarity index 100% rename from examples/middleware/class_based/i18n_middleware/i18n_base_midddleware.py rename to examples/middleware/class_based/i18n_middleware/i18n_base_middleware.py diff --git a/examples/middleware/class_based/i18n_middleware/main.py b/examples/middleware/class_based/i18n_middleware/main.py index 66ae420ba..161f1a55f 100644 --- a/examples/middleware/class_based/i18n_middleware/main.py +++ b/examples/middleware/class_based/i18n_middleware/main.py @@ -53,7 +53,7 @@ from typing import Union import keyboards -from i18n_base_midddleware import I18N +from i18n_base_middleware import I18N from telebot import TeleBot from telebot import types, StateMemoryStorage from telebot.custom_filters import TextMatchFilter, TextFilter From 1342cab25933ff7904b918003bb503f73003e60b Mon Sep 17 00:00:00 2001 From: coder2020official Date: Thu, 23 Jun 2022 12:05:26 +0500 Subject: [PATCH 1011/1808] Fix boolean parameters --- telebot/apihelper.py | 2 +- telebot/asyncio_helper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ff8c8e231..fc76a0016 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1658,7 +1658,7 @@ def create_invoice_link(token, title, description, payload, provider_token, payload['send_phone_number_to_provider'] = send_phone_number_to_provider if send_email_to_provider: payload['send_email_to_provider'] = send_email_to_provider - if is_flexible: + if is_flexible is not None: payload['is_flexible'] = is_flexible return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 51b60e98f..1c01893bd 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1636,7 +1636,7 @@ async def create_invoice_link(token, title, description, payload, provider_token payload['send_phone_number_to_provider'] = send_phone_number_to_provider if send_email_to_provider: payload['send_email_to_provider'] = send_email_to_provider - if is_flexible: + if is_flexible is not None: payload['is_flexible'] = is_flexible return await _process_request(token, method_url, params=payload, method='post') From 7f9dac414796c99493bd09c5ff6ff553f64f2982 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Thu, 23 Jun 2022 12:46:27 +0500 Subject: [PATCH 1012/1808] Fix previous commit --- telebot/apihelper.py | 12 ++++++------ telebot/asyncio_helper.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index fc76a0016..07f2f57bc 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1646,17 +1646,17 @@ def create_invoice_link(token, title, description, payload, provider_token, payload['photo_width'] = photo_width if photo_height: payload['photo_height'] = photo_height - if need_name: + if need_name is not None: payload['need_name'] = need_name - if need_phone_number: + if need_phone_number is not None: payload['need_phone_number'] = need_phone_number - if need_email: + if need_email is not None: payload['need_email'] = need_email - if need_shipping_address: + if need_shipping_address is not None: payload['need_shipping_address'] = need_shipping_address - if send_phone_number_to_provider: + if send_phone_number_to_provider is not None: payload['send_phone_number_to_provider'] = send_phone_number_to_provider - if send_email_to_provider: + if send_email_to_provider is not None: payload['send_email_to_provider'] = send_email_to_provider if is_flexible is not None: payload['is_flexible'] = is_flexible diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 1c01893bd..168edff7e 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1624,17 +1624,17 @@ async def create_invoice_link(token, title, description, payload, provider_token payload['photo_width'] = photo_width if photo_height: payload['photo_height'] = photo_height - if need_name: + if need_name is not None: payload['need_name'] = need_name - if need_phone_number: + if need_phone_number is not None: payload['need_phone_number'] = need_phone_number - if need_email: + if need_email is not None: payload['need_email'] = need_email - if need_shipping_address: + if need_shipping_address is not None: payload['need_shipping_address'] = need_shipping_address - if send_phone_number_to_provider: + if send_phone_number_to_provider is not None: payload['send_phone_number_to_provider'] = send_phone_number_to_provider - if send_email_to_provider: + if send_email_to_provider is not None: payload['send_email_to_provider'] = send_email_to_provider if is_flexible is not None: payload['is_flexible'] = is_flexible From 1686ce4f445d3bf1982242c2eb887b490e1e8d98 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Jun 2022 21:48:44 +0500 Subject: [PATCH 1013/1808] Middleware update: everything in data will be passed to handler if needed. --- telebot/__init__.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 85454c5f4..278721efe 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3998,6 +3998,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** return elif isinstance(result, SkipHandler) and skip_handler is False: skip_handler = True + try: if handlers and not skip_handler: @@ -4009,24 +4010,23 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** params.append(i) if len(params) == 1: handler['function'](message) - - elif len(params) == 2: - if handler.get('pass_bot') is True: - handler['function'](message, self) - - elif handler.get('pass_bot') is False: - handler['function'](message, data) - - elif len(params) == 3: - if params[2] == 'bot' and handler.get('pass_bot') is True: - handler['function'](message, data, self) + else: + if "data" in params: + if len(params) == 2: + handler['function'](message, data) + elif len(params) == 3: + handler['function'](message, data=data, bot=self) + else: + logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) + return - elif not handler.get('pass_bot'): - raise RuntimeError('Your handler accepts 3 parameters but pass_bot is False. Please re-check your handler.') - else: - handler['function'](message, self, data) - + if len(data) > len(params) -2: + logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) + return + if handler.get('pass_bot'): data["bot"] = self + handler["function"](message, **data) + except Exception as e: handler_error = e From a2893945b2c80650c0745f63f9e8b6e8e4ae81df Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 25 Jun 2022 22:15:53 +0500 Subject: [PATCH 1014/1808] Async changes and sync improvements --- telebot/__init__.py | 11 ++++++++--- telebot/async_telebot.py | 37 ++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 278721efe..23887c190 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -9,6 +9,7 @@ import traceback from typing import Any, Callable, List, Optional, Union + # these imports are used to avoid circular import error import telebot.util import telebot.types @@ -3977,7 +3978,7 @@ def _check_middleware(self, update_type): middlewares = [i for i in self.middlewares if update_type in i.update_types] return middlewares - def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, **kwargs): + def _run_middlewares_and_handler(self, message, handlers, middlewares): """ This class is made to run handler and middleware in queue. @@ -4021,10 +4022,11 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** return else: - if len(data) > len(params) -2: + if handler.get('pass_bot'): data["bot"] = self + if len(data) > len(params) - 1: # remove the message parameter logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) return - if handler.get('pass_bot'): data["bot"] = self + handler["function"](message, **data) except Exception as e: @@ -4035,6 +4037,9 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** return self.exception_handler.handle(e) logging.error(str(e)) return + # remove the bot from data + if "bot" in data: + del data["bot"] if middlewares: for middleware in middlewares: middleware.post_process(message, data, handler_error) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 156665770..f41f5cdf3 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -277,7 +277,7 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): handler_error = None data = {} process_handler = True - + params = [] if middlewares: for middleware in middlewares: middleware_result = await middleware.pre_process(message, data) @@ -295,27 +295,27 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): continue elif process_update: try: - params = [] - for i in signature(handler['function']).parameters: params.append(i) if len(params) == 1: await handler['function'](message) - break - elif len(params) == 2: - if handler['pass_bot']: - await handler['function'](message, self) - break - else: - await handler['function'](message, data) - break - elif len(params) == 3: - if handler['pass_bot'] and params[1] == 'bot': - await handler['function'](message, self, data) - break + else: + if "data" in params: + if len(params) == 2: + await handler['function'](message, data) + elif len(params) == 3: + await handler['function'](message, data=data, bot=self) + else: + logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) + return + else: - await handler['function'](message, data) - break + + if handler.get('pass_bot'): data["bot"] = self + if len(data) > len(params) - 1: + logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) + return + await handler["function"](message, **data) except Exception as e: handler_error = e @@ -324,6 +324,9 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): return self.exception_handler.handle(e) logging.error(str(e)) return + # remove the bot from data + if "bot" in data: + del data["bot"] if middlewares: for middleware in middlewares: From 6e8abc709e592b2b8dcaccd579726bf2cb63767c Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 Jun 2022 19:51:51 +0500 Subject: [PATCH 1015/1808] Pass only the necessary data --- telebot/__init__.py | 15 ++++++++++----- telebot/async_telebot.py | 20 ++++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 23887c190..f034cca37 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4022,12 +4022,19 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): return else: - if handler.get('pass_bot'): data["bot"] = self - if len(data) > len(params) - 1: # remove the message parameter + + data_copy = data.copy() + + for key in list(data_copy): + # remove data from data_copy if handler does not accept it + if key not in params: + del data_copy[key] + if handler.get('pass_bot'): data_copy["bot"] = self + if len(data_copy) > len(params) - 1: # remove the message parameter logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) return - handler["function"](message, **data) + handler["function"](message, **data_copy) except Exception as e: handler_error = e @@ -4038,8 +4045,6 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): logging.error(str(e)) return # remove the bot from data - if "bot" in data: - del data["bot"] if middlewares: for middleware in middlewares: middleware.post_process(message, data, handler_error) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f41f5cdf3..5c7f50ca3 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -309,13 +309,20 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return - else: - - if handler.get('pass_bot'): data["bot"] = self - if len(data) > len(params) - 1: + else: + + data_copy = data.copy() + + for key in list(data_copy): + # remove data from data_copy if handler does not accept it + if key not in params: + del data_copy[key] + if handler.get('pass_bot'): data_copy["bot"] = self + if len(data_copy) > len(params) - 1: # remove the message parameter logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) return - await handler["function"](message, **data) + + handler["function"](message, **data_copy) except Exception as e: handler_error = e @@ -324,9 +331,6 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): return self.exception_handler.handle(e) logging.error(str(e)) return - # remove the bot from data - if "bot" in data: - del data["bot"] if middlewares: for middleware in middlewares: From fbb9a73fc040b54845c87c281cd7521c9843ad25 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 29 Jun 2022 14:52:37 +0500 Subject: [PATCH 1016/1808] F###, forgot to put await --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 5c7f50ca3..dce9decd1 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -322,7 +322,7 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) return - handler["function"](message, **data_copy) + await handler["function"](message, **data_copy) except Exception as e: handler_error = e From fec47cecafa511c25cec591547cda13fd933f9b4 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 29 Jun 2022 20:21:42 +0500 Subject: [PATCH 1017/1808] Add the possibility to getbase class of a State object --- telebot/asyncio_handler_backends.py | 1 + telebot/handler_backends.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index e5e2e4f08..4ef455588 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -29,6 +29,7 @@ def __init_subclass__(cls) -> None: if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable value.name = ':'.join((cls.__name__, name)) + value.group = cls class SkipHandler: diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index b838231b8..430431711 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -163,6 +163,10 @@ def __init_subclass__(cls) -> None: if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable value.name = ':'.join((cls.__name__, name)) + value.group = cls + + + class BaseMiddleware: From ca525b5bead9497d3bf531a7a94353cef0074f6d Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 29 Jun 2022 21:06:36 +0500 Subject: [PATCH 1018/1808] Copyright changes --- telebot/callback_data.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/telebot/callback_data.py b/telebot/callback_data.py index 17bf27f5d..952030cb0 100644 --- a/telebot/callback_data.py +++ b/telebot/callback_data.py @@ -1,3 +1,11 @@ +# https://github.com/aiogram/aiogram/blob/dev-2.x/LICENSE +""" +This file was added during the pull request. The maintainers overlooked that +it was copied "as is" from another project and they do not consider it as a +right way to develop a project. However, due to backward compatibility we had +to leave this file in the project with the above copyright added, +as it is required by the original project license. +""" import typing From c36f3a228e3b0a964c2e80977a9ffdb906070b26 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 29 Jun 2022 21:17:57 +0500 Subject: [PATCH 1019/1808] Update callback_data.py --- telebot/callback_data.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/telebot/callback_data.py b/telebot/callback_data.py index 952030cb0..c85b9e31e 100644 --- a/telebot/callback_data.py +++ b/telebot/callback_data.py @@ -1,4 +1,24 @@ -# https://github.com/aiogram/aiogram/blob/dev-2.x/LICENSE +""" +Copyright (c) 2017-2018 Alex Root Junior + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. +""" + """ This file was added during the pull request. The maintainers overlooked that it was copied "as is" from another project and they do not consider it as a From ce0a974c918a44d4a1fdc2006ddc9330ef8dad37 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 29 Jun 2022 19:24:27 +0300 Subject: [PATCH 1020/1808] Copyright update --- telebot/__init__.py | 10 +++++----- telebot/callback_data.py | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 85454c5f4..ee73b3f21 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -385,7 +385,7 @@ def process_new_updates(self, updates): new_poll_answers = None new_my_chat_members = None new_chat_members = None - chat_join_request = None + new_chat_join_request = None for update in updates: if apihelper.ENABLE_MIDDLEWARE: @@ -442,8 +442,8 @@ def process_new_updates(self, updates): if new_chat_members is None: new_chat_members = [] new_chat_members.append(update.chat_member) if update.chat_join_request: - if chat_join_request is None: chat_join_request = [] - chat_join_request.append(update.chat_join_request) + if new_chat_join_request is None: new_chat_join_request = [] + new_chat_join_request.append(update.chat_join_request) if new_messages: self.process_new_messages(new_messages) @@ -471,8 +471,8 @@ def process_new_updates(self, updates): self.process_new_my_chat_member(new_my_chat_members) if new_chat_members: self.process_new_chat_member(new_chat_members) - if chat_join_request: - self.process_new_chat_join_request(chat_join_request) + if new_chat_join_request: + self.process_new_chat_join_request(new_chat_join_request) def process_new_messages(self, new_messages): self._notify_next_handlers(new_messages) diff --git a/telebot/callback_data.py b/telebot/callback_data.py index 17bf27f5d..58fa0d511 100644 --- a/telebot/callback_data.py +++ b/telebot/callback_data.py @@ -1,3 +1,30 @@ +""" +Copyright (c) 2017-2018 Alex Root Junior + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + + +This file was added during the pull request. The maintainers overlooked that it was copied +"as is" from another project and they do not consider it as a right way to develop a project. +However, due to backward compatibility we had to leave this file in the project with the above +copyright added, as it is required by the original project license. +""" + import typing From 419bc5878f52ea84c6518b89e5c2d0cc69f1765c Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 30 Jun 2022 17:06:39 +0500 Subject: [PATCH 1021/1808] Fix typehint for ```set_state``` --- telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f034cca37..a7f36941b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -34,7 +34,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, CancelUpdate, SkipHandler +from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, CancelUpdate, SkipHandler, State from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter @@ -2927,7 +2927,7 @@ def setup_middleware(self, middleware: BaseMiddleware): - def set_state(self, user_id: int, state: Union[int, str], chat_id: int=None) -> None: + def set_state(self, user_id: int, state: Union[int, str, State], chat_id: int=None) -> None: """ Sets a new state of a user. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index dce9decd1..9571c0462 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -14,7 +14,7 @@ # storages from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage -from telebot.asyncio_handler_backends import CancelUpdate, SkipHandler +from telebot.asyncio_handler_backends import CancelUpdate, SkipHandler, State from inspect import signature @@ -3455,7 +3455,7 @@ async def delete_sticker_from_set(self, sticker: str) -> bool: return await asyncio_helper.delete_sticker_from_set(self.token, sticker) - async def set_state(self, user_id: int, state: str, chat_id: int=None): + async def set_state(self, user_id: int, state: Union[State, int, str], chat_id: int=None): """ Sets a new state of a user. From 6d12b1f2a7a3fb62337af62bf55d1e785e465146 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 30 Jun 2022 17:10:14 +0500 Subject: [PATCH 1022/1808] Update callback_data.py --- telebot/callback_data.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/telebot/callback_data.py b/telebot/callback_data.py index c85b9e31e..58fa0d511 100644 --- a/telebot/callback_data.py +++ b/telebot/callback_data.py @@ -17,15 +17,14 @@ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" + +This file was added during the pull request. The maintainers overlooked that it was copied +"as is" from another project and they do not consider it as a right way to develop a project. +However, due to backward compatibility we had to leave this file in the project with the above +copyright added, as it is required by the original project license. """ -This file was added during the pull request. The maintainers overlooked that -it was copied "as is" from another project and they do not consider it as a -right way to develop a project. However, due to backward compatibility we had -to leave this file in the project with the above copyright added, -as it is required by the original project license. -""" + import typing From d14064a84d6cd7a00c062d8364f10131109fe032 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 30 Jun 2022 17:51:58 +0500 Subject: [PATCH 1023/1808] Fix group and supergroup issues --- telebot/asyncio_filters.py | 2 +- telebot/custom_filters.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index c594d31be..8819be59f 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -307,7 +307,7 @@ async def check(self, message, text): elif isinstance(text, State): text = text.name - if message.chat.type == 'group': + if message.chat.type in ['group', 'supergroup']: group_state = await self.bot.current_states.get_state(user_id, chat_id) if group_state == text: return True diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index abeffdd9f..02edd0763 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -315,7 +315,7 @@ def check(self, message, text): elif isinstance(text, State): text = text.name - if message.chat.type == 'group': + if message.chat.type in ['group', 'supergroup']: group_state = self.bot.current_states.get_state(user_id, chat_id) if group_state == text: return True From 5939e754bbd1619566f66eeb0b50887e9590ccc5 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Jul 2022 15:29:34 +0500 Subject: [PATCH 1024/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 168edff7e..17575e70a 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -149,7 +149,7 @@ async def get_file(token, file_id): async def get_file_url(token, file_id): if FILE_URL is None: - return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id)['file_path']) + return "https://api.telegram.org/file/bot{0}/{1}".format(token, await get_file(token, file_id)['file_path']) else: # noinspection PyUnresolvedReferences return FILE_URL.format(token, get_file(token, file_id)['file_path']) @@ -1821,4 +1821,4 @@ class RequestTimeout(Exception): """ This class represents a request timeout. """ - pass \ No newline at end of file + pass From c920809fa9d3e800343f13eb74e2e44c5a9e9859 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Jul 2022 15:39:42 +0500 Subject: [PATCH 1025/1808] Update asyncio_helper.py --- 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 17575e70a..75492afa3 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -152,7 +152,7 @@ async def get_file_url(token, file_id): return "https://api.telegram.org/file/bot{0}/{1}".format(token, await get_file(token, file_id)['file_path']) else: # noinspection PyUnresolvedReferences - return FILE_URL.format(token, get_file(token, file_id)['file_path']) + return FILE_URL.format(token, await get_file(token, file_id)['file_path']) async def download_file(token, file_path): From ef86453126fe0603240fc21bb8cb27d39e10acdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=95=E8=B0=B7=E9=85=B1?= <74496778+GooGuJiang@users.noreply.github.com> Date: Sat, 2 Jul 2022 14:23:31 +0800 Subject: [PATCH 1026/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 027139d09..b1099da1f 100644 --- a/README.md +++ b/README.md @@ -899,5 +899,6 @@ Here are some examples of template: * [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. * [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. * [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...). +* [Gugumoe-bot](http://t.me/gugumoe_bot)([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) A functional robot. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From cf3a85d6997e2a35dd6e8f2bf58206ef6d0431a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=95=E8=B0=B7=E9=85=B1?= <74496778+GooGuJiang@users.noreply.github.com> Date: Sat, 2 Jul 2022 14:31:37 +0800 Subject: [PATCH 1027/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1099da1f..091c540c5 100644 --- a/README.md +++ b/README.md @@ -899,6 +899,6 @@ Here are some examples of template: * [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. * [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. * [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...). -* [Gugumoe-bot](http://t.me/gugumoe_bot)([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) A functional robot. +* [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) GuXiaoJiang Robot. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 6d52090ef9ccfafa76fe8e4f77614eba6ee4c909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=95=E8=B0=B7=E9=85=B1?= <74496778+GooGuJiang@users.noreply.github.com> Date: Sat, 2 Jul 2022 15:06:46 +0800 Subject: [PATCH 1028/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 091c540c5..f58349f10 100644 --- a/README.md +++ b/README.md @@ -899,6 +899,6 @@ Here are some examples of template: * [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. * [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. * [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...). -* [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) GuXiaoJiang Robot. +* [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) A multifunctional robot, such as OSU game information query, IP test, animation screenshot search and other functions. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From badf98214780f225c67b91cf3a3e41619fde8caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=95=E8=B0=B7=E9=85=B1?= <74496778+GooGuJiang@users.noreply.github.com> Date: Sat, 2 Jul 2022 15:08:06 +0800 Subject: [PATCH 1029/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f58349f10..1bc6978e9 100644 --- a/README.md +++ b/README.md @@ -899,6 +899,6 @@ Here are some examples of template: * [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. * [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. * [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...). -* [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) A multifunctional robot, such as OSU game information query, IP test, animation screenshot search and other functions. +* [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) GuXiaoJiang is a multi-functional robot, such as OSU game information query, IP test, animation screenshot search and other functions. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 1fb14e28d42daa81612cc95fccf277115e60bbd4 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 2 Jul 2022 21:07:00 +0500 Subject: [PATCH 1030/1808] Fix handler execution and #1594 --- telebot/async_telebot.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 9571c0462..3ca72070f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -118,7 +118,20 @@ async def close_session(self): """ await asyncio_helper.session_manager.session.close() async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, - timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> List[types.Update]: + timeout: Optional[int]=20, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> List[types.Update]: + """ + Use this method to receive incoming updates using long polling (wiki). + An Array of Update objects is returned. + + Telegram documentation: https://core.telegram.org/bots/api#making-requests + + :param allowed_updates: Array of string. List the types of updates you want your bot to receive. + :param offset: Integer. Identifier of the first update to be returned. + :param limit: Integer. Limits the number of updates to be retrieved. + :param timeout: Integer. Request connection timeout + :param request_timeout: Timeout in seconds for a request. + :return: array of Updates + """ json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] @@ -299,12 +312,15 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): params.append(i) if len(params) == 1: await handler['function'](message) + break else: if "data" in params: if len(params) == 2: await handler['function'](message, data) + break elif len(params) == 3: await handler['function'](message, data=data, bot=self) + break else: logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return @@ -323,14 +339,13 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): return await handler["function"](message, **data_copy) + break except Exception as e: handler_error = e - if not middlewares: - if self.exception_handler: - return self.exception_handler.handle(e) - logging.error(str(e)) - return + if self.exception_handler: + self.exception_handler.handle(e) + else: logger.error(str(e)) if middlewares: for middleware in middlewares: From f3b1f97362458f94b214d58a2a23306877d821ad Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 3 Jul 2022 23:33:55 +0300 Subject: [PATCH 1031/1808] ChatMember is now typed https://core.telegram.org/bots/api#chatmember --- telebot/async_telebot.py | 2 +- telebot/types.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index dce9decd1..a69f34bbe 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -108,7 +108,6 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, self.current_states = state_storage - self.middlewares = [] async def close_session(self): @@ -117,6 +116,7 @@ async def close_session(self): Use this function if you stop polling. """ await asyncio_helper.session_manager.session.close() + async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, timeout: Optional[int]=None, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> List[types.Update]: json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) diff --git a/telebot/types.py b/telebot/types.py index ee67cb99a..e49e86a90 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1280,7 +1280,22 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) - return cls(**obj) + member_type = obj['status'] + if member_type == "creator": + return ChatMemberOwner(**obj) + elif member_type == "administrator": + return ChatMemberAdministrator(**obj) + elif member_type == "member": + return ChatMemberMember(**obj) + elif member_type == "restricted": + return ChatMemberRestricted(**obj) + elif member_type == "left": + return ChatMemberLeft(**obj) + elif member_type == "kicked": + return ChatMemberBanned(**obj) + else: + # Should not be here + return cls(**obj) def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, @@ -1318,6 +1333,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed class ChatMemberOwner(ChatMember): pass + class ChatMemberAdministrator(ChatMember): pass From 0aa9f0fb42ad5e4c79fa49b7c42db31c5f84bd96 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 4 Jul 2022 22:26:24 +0300 Subject: [PATCH 1032/1808] Update type fix --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 2561c21ad..62ebe7f27 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -49,7 +49,7 @@ ] update_types = [ - "update_id", "message", "edited_message", "channel_post", "edited_channel_post", "inline_query", + "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" ] From 81cbddb8cd67368666ba0e708b60d3e9eefb6b13 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 4 Jul 2022 22:36:42 +0300 Subject: [PATCH 1033/1808] Added source data (json_string) to CallbackQuery --- telebot/types.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e49e86a90..5a8fcf2d6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1248,9 +1248,10 @@ def de_json(cls, json_string): obj['from_user'] = User.de_json(obj.pop('from')) if 'message' in obj: obj['message'] = Message.de_json(obj.get('message')) + obj['json_string'] = json_string return cls(**obj) - def __init__(self, id, from_user, data, chat_instance, message=None, inline_message_id=None, game_short_name=None, **kwargs): + def __init__(self, id, from_user, data, chat_instance, json_string, message=None, inline_message_id=None, game_short_name=None, **kwargs): self.id: int = id self.from_user: User = from_user self.message: Message = message @@ -1258,6 +1259,7 @@ def __init__(self, id, from_user, data, chat_instance, message=None, inline_mess self.chat_instance: str = chat_instance self.data: str = data self.game_short_name: str = game_short_name + self.json = json_string class ChatPhoto(JsonDeserializable): From 78251cdf43941a6dea5ce34d31a12433b63be542 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 4 Jul 2022 22:41:01 +0300 Subject: [PATCH 1034/1808] ChatMember type checking reordered --- telebot/types.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 5a8fcf2d6..5bab9c4cd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1283,20 +1283,21 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) member_type = obj['status'] - if member_type == "creator": - return ChatMemberOwner(**obj) - elif member_type == "administrator": - return ChatMemberAdministrator(**obj) - elif member_type == "member": + # Ordered according to estimated appearance frequency. + if member_type == "member": return ChatMemberMember(**obj) - elif member_type == "restricted": - return ChatMemberRestricted(**obj) elif member_type == "left": return ChatMemberLeft(**obj) elif member_type == "kicked": return ChatMemberBanned(**obj) + elif member_type == "restricted": + return ChatMemberRestricted(**obj) + elif member_type == "administrator": + return ChatMemberAdministrator(**obj) + elif member_type == "creator": + return ChatMemberOwner(**obj) else: - # Should not be here + # Should not be here. For "if something happen" compatibility return cls(**obj) def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, From 1df19e3b2dc99092b477cb97e99690b95b0e11d7 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 5 Jul 2022 21:18:42 +0500 Subject: [PATCH 1035/1808] CallbackQuery fixes for custom filters Now some custom filters support callback query messages --- telebot/asyncio_filters.py | 9 ++++++++- telebot/custom_filters.py | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index c594d31be..887a953cc 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -201,6 +201,8 @@ class ChatFilter(AdvancedCustomFilter): key = 'chat_id' async def check(self, message, text): + if isinstance(message, types.CallbackQuery): + return message.message.chat.id in text return message.chat.id in text @@ -216,7 +218,7 @@ class ForwardFilter(SimpleCustomFilter): key = 'is_forwarded' async def check(self, message): - return message.forward_from_chat is not None + return message.forward_date is not None class IsReplyFilter(SimpleCustomFilter): @@ -231,6 +233,8 @@ class IsReplyFilter(SimpleCustomFilter): key = 'is_reply' async def check(self, message): + if isinstance(message, types.CallbackQuery): + return message.message.reply_to_message is not None return message.reply_to_message is not None @@ -266,6 +270,9 @@ def __init__(self, bot): self._bot = bot async def check(self, message): + if isinstance(message, types.CallbackQuery): + result = await self._bot.get_chat_member(message.message.chat.id, message.from_user.id) + return result.status ('creator', 'administrator') result = await self._bot.get_chat_member(message.chat.id, message.from_user.id) return result.status in ['creator', 'administrator'] diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index abeffdd9f..407b3ec8e 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -208,6 +208,8 @@ class ChatFilter(AdvancedCustomFilter): key = 'chat_id' def check(self, message, text): + if isinstance(message, types.CallbackQuery): + return message.message.chat.id in text return message.chat.id in text @@ -223,7 +225,7 @@ class ForwardFilter(SimpleCustomFilter): key = 'is_forwarded' def check(self, message): - return message.forward_from_chat is not None + return message.forward_date is not None class IsReplyFilter(SimpleCustomFilter): @@ -238,6 +240,8 @@ class IsReplyFilter(SimpleCustomFilter): key = 'is_reply' def check(self, message): + if isinstance(message, types.CallbackQuery): + return message.message.reply_to_message is not None return message.reply_to_message is not None @@ -273,6 +277,8 @@ def __init__(self, bot): self._bot = bot def check(self, message): + if isinstance(message, types.CallbackQuery): + return self._bot.get_chat_member(message.message.chat.id, message.from_user.id).status in ['creator', 'administrator'] return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] From e1094c6f027921cf6d08587e2602553d89474a03 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 5 Jul 2022 21:50:11 +0500 Subject: [PATCH 1036/1808] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1bc6978e9..253692bae 100644 --- a/README.md +++ b/README.md @@ -398,7 +398,7 @@ Here is example of creating filter-class: ```python class IsAdmin(telebot.custom_filters.SimpleCustomFilter): # Class will check whether the user is admin or creator in group or not - key='is_admin' + key='is_chat_admin' @staticmethod def check(message: telebot.types.Message): return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator'] @@ -407,7 +407,7 @@ class IsAdmin(telebot.custom_filters.SimpleCustomFilter): bot.add_custom_filter(IsAdmin()) # Now, you can use it in handler. -@bot.message_handler(is_admin=True) +@bot.message_handler(is_chat_admin=True) def admin_of_group(message): bot.send_message(message.chat.id, 'You are admin of this group!') From eb4cd7aba0df59501bbf5d43c4853d46b207eb61 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 6 Jul 2022 21:31:03 +0500 Subject: [PATCH 1037/1808] Webhook processing function using flask for sync --- telebot/__init__.py | 75 +++++++++++++++++++++++++++++ telebot/extensions/webhooks.py | 87 ++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 telebot/extensions/webhooks.py diff --git a/telebot/__init__.py b/telebot/__init__.py index 9e6e12433..6c793af17 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -18,6 +18,13 @@ from telebot.storage import StatePickleStorage, StateMemoryStorage +# random module to generate random string +import random +import string + + +# webhooks module +from telebot.extensions.webhooks import SyncWebhookListener logger = logging.getLogger('TeleBot') @@ -293,6 +300,74 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout, secret_token) + + def run_webhooks(self, + listen: Optional[str]="127.0.0.1", + port: Optional[int]=443, + url_path: Optional[str]=None, + certificate: Optional[str]=None, + certificate_key: Optional[str]=None, + webhook_url: Optional[str]=None, + max_connections: Optional[int]=None, + allowed_updates: Optional[List]=None, + ip_address: Optional[str]=None, + drop_pending_updates: Optional[bool] = None, + timeout: Optional[int]=None, + secret_token: Optional[str]=None, + secret_token_length: Optional[int]=20, + debug: Optional[bool]=False): + """ + This class sets webhooks and listens to a given url and port. + + :param listen: IP address to listen to. Defaults to + 0.0.0.0 + :param port: A port which will be used to listen to webhooks. + :param url_path: Path to the webhook. Defaults to /token + :param certificate: Path to the certificate file. + :param certificate_key: Path to the certificate key file. + :param webhook_url: Webhook URL. + :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS + :param drop_pending_updates: Pass True to drop all pending updates + :param timeout: Integer. Request connection timeout + :param secret_token: Secret token to be used to verify the webhook request. + :return: + """ + + # generate secret token if not set + if not secret_token: + secret_token = ''.join(random.choices(string.ascii_uppercase + string.digits, k=secret_token_length)) + + + if not url_path: + url_path = self.token + '/' + if url_path[-1] != '/': url_path += '/' + + + + protocol = "https" if certificate else "http" + if not webhook_url: + webhook_url = "{}://{}:{}/{}".format(protocol, listen, port, url_path) + + + self.set_webhook( + url=webhook_url, + certificate=certificate, + max_connections=max_connections, + allowed_updates=allowed_updates, + ip_address=ip_address, + drop_pending_updates=drop_pending_updates, + timeout=timeout, + secret_token=secret_token + ) + + ssl_context = (certificate, certificate_key) if certificate else None + self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) + self.webhook_listener.run_app() + return self.webhook_listener + + def delete_webhook(self, drop_pending_updates=None, timeout=None): """ Use this method to remove webhook integration if you decide to switch back to getUpdates. diff --git a/telebot/extensions/webhooks.py b/telebot/extensions/webhooks.py new file mode 100644 index 000000000..e1c8e32cb --- /dev/null +++ b/telebot/extensions/webhooks.py @@ -0,0 +1,87 @@ +""" +This file is used by TeleBot.run_webhooks() & +AsyncTeleBot.run_webhooks() functions. + +Flask/Aiohttp is required to run this script. +""" + + +flask_installed = True +try: + import flask + from werkzeug.serving import _TSSLContextArg +except ImportError: + flask_installed = False + + +from telebot.types import Update + + +from typing import Optional + + + + + +class SyncWebhookListener: + def __init__(self, bot, + secret_token: str, host: Optional[str]="127.0.0.1", + port: Optional[int]=8000, + ssl_context: Optional[_TSSLContextArg]=None, + url_path: Optional[str]=None, + debug: Optional[bool]=False + ) -> None: + """ + Synchronous implementation of webhook listener + for synchronous version of telebot. + + :param bot: TeleBot instance + :param secret_token: Telegram secret token + :param host: Webhook host + :param port: Webhook port + :param ssl_context: SSL context + """ + if not flask_installed: + raise ImportError('Flask is not installed. Please install it via pip.') + self.app = flask.Flask(__name__) + self._secret_token = secret_token + self._bot = bot + self._port = port + self._host = host + self._ssl_context = ssl_context + self._url_path = url_path + self._debug = debug + self._prepare_endpoint_urls() + + + def _prepare_endpoint_urls(self): + self.app.add_url_rule(self._url_path, 'index', self.process_update, methods=['POST']) + + + def process_update(self): + """ + Processes updates. + """ + # header containsX-Telegram-Bot-Api-Secret-Token + if flask.request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: + # secret token didn't match + flask.abort(403) + if flask.request.headers.get('content-type') == 'application/json': + json_string = flask.request.get_data().decode('utf-8') + self._bot.process_new_updates([Update.de_json(json_string)]) + return '' + + flask.abort(403) + + + def run_app(self): + """ + Run app with the given parameters. + """ + self.app.run( + host=self._host, + port=self._port, + ssl_context=self._ssl_context, + debug=self._debug + ) + return self \ No newline at end of file From 3cf5305845aa8e052f6967284dc3a37c0f5e74a0 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 6 Jul 2022 22:06:49 +0500 Subject: [PATCH 1038/1808] Rename --- telebot/{extensions => extension}/webhooks.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename telebot/{extensions => extension}/webhooks.py (100%) diff --git a/telebot/extensions/webhooks.py b/telebot/extension/webhooks.py similarity index 100% rename from telebot/extensions/webhooks.py rename to telebot/extension/webhooks.py From 7b8c98d731519740945f3893306682d8420c0a24 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 6 Jul 2022 23:15:22 +0500 Subject: [PATCH 1039/1808] Test --- telebot/{extension => bababa}/webhooks.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename telebot/{extension => bababa}/webhooks.py (100%) diff --git a/telebot/extension/webhooks.py b/telebot/bababa/webhooks.py similarity index 100% rename from telebot/extension/webhooks.py rename to telebot/bababa/webhooks.py From ebca668979d171d530ff1768504aeb6aba6acf8a Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 6 Jul 2022 23:35:59 +0500 Subject: [PATCH 1040/1808] Create __init__.py --- telebot/bababa/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 telebot/bababa/__init__.py diff --git a/telebot/bababa/__init__.py b/telebot/bababa/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/telebot/bababa/__init__.py @@ -0,0 +1 @@ + From e353572c03a9233f20513979aaa2f4563d2f747c Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 7 Jul 2022 15:09:02 +0500 Subject: [PATCH 1041/1808] Update webhooks listeners. --- telebot/__init__.py | 2 +- telebot/extensions/__init__.py | 7 +++++++ telebot/{bababa => extensions}/webhooks.py | 0 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 telebot/extensions/__init__.py rename telebot/{bababa => extensions}/webhooks.py (100%) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c793af17..6c6366702 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -24,7 +24,7 @@ # webhooks module -from telebot.extensions.webhooks import SyncWebhookListener +from telebot.extensions import SyncWebhookListener logger = logging.getLogger('TeleBot') diff --git a/telebot/extensions/__init__.py b/telebot/extensions/__init__.py new file mode 100644 index 000000000..8fc3bc3cb --- /dev/null +++ b/telebot/extensions/__init__.py @@ -0,0 +1,7 @@ +from .webhooks import SyncWebhookListener + + + +__all__ = [ + 'SyncWebhookListener' +] \ No newline at end of file diff --git a/telebot/bababa/webhooks.py b/telebot/extensions/webhooks.py similarity index 100% rename from telebot/bababa/webhooks.py rename to telebot/extensions/webhooks.py From 140befc13298253d8fc40e7846161a02efc27c83 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 7 Jul 2022 15:15:24 +0500 Subject: [PATCH 1042/1808] Typehint fix if there is no flask installed --- telebot/extensions/webhooks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/extensions/webhooks.py b/telebot/extensions/webhooks.py index e1c8e32cb..ddfae41e9 100644 --- a/telebot/extensions/webhooks.py +++ b/telebot/extensions/webhooks.py @@ -12,6 +12,7 @@ from werkzeug.serving import _TSSLContextArg except ImportError: flask_installed = False + _TSSLContextArg = None from telebot.types import Update From f8f147f9f4cc03410fe4f35a584e06593b6a638e Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 7 Jul 2022 15:53:27 +0500 Subject: [PATCH 1043/1808] Fix certificate for webhooks --- telebot/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c6366702..4018e5256 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -26,6 +26,8 @@ # webhooks module from telebot.extensions import SyncWebhookListener +import ssl + logger = logging.getLogger('TeleBot') formatter = logging.Formatter( @@ -350,10 +352,17 @@ def run_webhooks(self, if not webhook_url: webhook_url = "{}://{}:{}/{}".format(protocol, listen, port, url_path) - + if certificate and certificate_key: + ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ssl_ctx.load_cert_chain(certificate, certificate_key) + else: + ssl_ctx = None + + # open certificate if it exists + cert_file = open(certificate, 'rb') if certificate else None self.set_webhook( url=webhook_url, - certificate=certificate, + certificate=cert_file, max_connections=max_connections, allowed_updates=allowed_updates, ip_address=ip_address, @@ -361,6 +370,7 @@ def run_webhooks(self, timeout=timeout, secret_token=secret_token ) + if cert_file: cert_file.close() ssl_context = (certificate, certificate_key) if certificate else None self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) From 2f3223668035e962f62b62d29aba40fa2dc1b996 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 7 Jul 2022 22:56:13 +0500 Subject: [PATCH 1044/1808] Added run_webhooks for asynctelebot --- .../webhooks/run_webhooks.py | 45 ++++++++ examples/webhook_examples/run_webhooks.py | 45 ++++++++ telebot/__init__.py | 2 +- telebot/async_telebot.py | 83 ++++++++++++++ telebot/extensions/__init__.py | 7 +- telebot/extensions/webhooks.py | 103 +++++++++++++++++- 6 files changed, 278 insertions(+), 7 deletions(-) create mode 100644 examples/asynchronous_telebot/webhooks/run_webhooks.py create mode 100644 examples/webhook_examples/run_webhooks.py diff --git a/examples/asynchronous_telebot/webhooks/run_webhooks.py b/examples/asynchronous_telebot/webhooks/run_webhooks.py new file mode 100644 index 000000000..fa145fc98 --- /dev/null +++ b/examples/asynchronous_telebot/webhooks/run_webhooks.py @@ -0,0 +1,45 @@ +#!/usr/bin/python + +# This is a simple echo bot using the decorator mechanism. +# It echoes any incoming text messages. +# Example on built-in function to receive and process webhooks. + +from telebot.async_telebot import AsyncTeleBot +import asyncio +bot = AsyncTeleBot('TOKEN') + + +WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate +WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key +DOMAIN = '123.12.33.22' # either domain, or ip address of vps + +# Quick'n'dirty SSL certificate generation: +# +# openssl genrsa -out webhook_pkey.pem 2048 +# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem +# +# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply +# with the same value in you put in WEBHOOK_HOST + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +async def send_welcome(message): + await bot.reply_to(message, """\ +Hi there, I am EchoBot. +I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\ +""") + + +# Handle all other messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +async def echo_message(message): + await bot.reply_to(message, message.text) + + +# it uses fastapi + uvicorn +asyncio.run(bot.run_webhooks( + listen=DOMAIN, + certificate=WEBHOOK_SSL_CERT, + certificate_key=WEBHOOK_SSL_PRIV +)) \ No newline at end of file diff --git a/examples/webhook_examples/run_webhooks.py b/examples/webhook_examples/run_webhooks.py new file mode 100644 index 000000000..8332d3050 --- /dev/null +++ b/examples/webhook_examples/run_webhooks.py @@ -0,0 +1,45 @@ +#!/usr/bin/python + +# This is a simple echo bot using the decorator mechanism. +# It echoes any incoming text messages. +# Example on built-in function to receive and process webhooks. + +import telebot + +API_TOKEN = 'TOKEN' + +bot = telebot.TeleBot(API_TOKEN) + +WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate +WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key +DOMAIN = '123.12.33.22' # either domain, or ip address of vps + +# Quick'n'dirty SSL certificate generation: +# +# openssl genrsa -out webhook_pkey.pem 2048 +# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem +# +# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply +# with the same value in you put in WEBHOOK_HOST + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +def send_welcome(message): + bot.reply_to(message, """\ +Hi there, I am EchoBot. +I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\ +""") + + +# Handle all other messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +def echo_message(message): + bot.reply_to(message, message.text) + + +bot.run_webhooks( + listen=DOMAIN, + certificate=WEBHOOK_SSL_CERT, + certificate_key=WEBHOOK_SSL_PRIV +) \ No newline at end of file diff --git a/telebot/__init__.py b/telebot/__init__.py index 4018e5256..cc624bc1f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -375,7 +375,7 @@ def run_webhooks(self, ssl_context = (certificate, certificate_key) if certificate else None self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) self.webhook_listener.run_app() - return self.webhook_listener + def delete_webhook(self, drop_pending_updates=None, timeout=None): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 000746e88..fd21d30c3 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -30,6 +30,13 @@ types.ReplyKeyboardRemove, types.ForceReply] +# for webhooks +from telebot.extensions import AsyncWebhookListener +import string +import random +import ssl + + """ Module : telebot """ @@ -1438,6 +1445,82 @@ async def set_webhook(self, url=None, certificate=None, max_connections=None, al drop_pending_updates, timeout, secret_token) + async def run_webhooks(self, + listen: Optional[str]="127.0.0.1", + port: Optional[int]=443, + url_path: Optional[str]=None, + certificate: Optional[str]=None, + certificate_key: Optional[str]=None, + webhook_url: Optional[str]=None, + max_connections: Optional[int]=None, + allowed_updates: Optional[List]=None, + ip_address: Optional[str]=None, + drop_pending_updates: Optional[bool] = None, + timeout: Optional[int]=None, + secret_token: Optional[str]=None, + secret_token_length: Optional[int]=20, + debug: Optional[bool]=False): + """ + This class sets webhooks and listens to a given url and port. + + :param listen: IP address to listen to. Defaults to + 0.0.0.0 + :param port: A port which will be used to listen to webhooks. + :param url_path: Path to the webhook. Defaults to /token + :param certificate: Path to the certificate file. + :param certificate_key: Path to the certificate key file. + :param webhook_url: Webhook URL. + :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS + :param drop_pending_updates: Pass True to drop all pending updates + :param timeout: Integer. Request connection timeout + :param secret_token: Secret token to be used to verify the webhook request. + :return: + """ + + # generate secret token if not set + if not secret_token: + secret_token = ''.join(random.choices(string.ascii_uppercase + string.digits, k=secret_token_length)) + + + if not url_path: + url_path = self.token + '/' + if url_path[-1] != '/': url_path += '/' + + + + protocol = "https" if certificate else "http" + if not webhook_url: + webhook_url = "{}://{}:{}/{}".format(protocol, listen, port, url_path) + + if certificate and certificate_key: + ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ssl_ctx.load_cert_chain(certificate, certificate_key) + else: + ssl_ctx = None + + # open certificate if it exists + cert_file = open(certificate, 'rb') if certificate else None + await self.set_webhook( + url=webhook_url, + certificate=cert_file, + max_connections=max_connections, + allowed_updates=allowed_updates, + ip_address=ip_address, + drop_pending_updates=drop_pending_updates, + timeout=timeout, + secret_token=secret_token + ) + if cert_file: cert_file.close() + + ssl_context = (certificate, certificate_key) if certificate else (None, None) + self.webhook_listener = AsyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) + # create a new loop, set it, and pass it + asyncio.set_event_loop(asyncio.new_event_loop()) + await self.webhook_listener.run_app() + + async def delete_webhook(self, drop_pending_updates=None, timeout=None): """ diff --git a/telebot/extensions/__init__.py b/telebot/extensions/__init__.py index 9eb87c7e9..9535cbebb 100644 --- a/telebot/extensions/__init__.py +++ b/telebot/extensions/__init__.py @@ -1,7 +1,8 @@ -from .webhooks import SyncWebhookListener +from .webhooks import SyncWebhookListener, AsyncWebhookListener __all__ = [ - 'SyncWebhookListener' -] + 'SyncWebhookListener', + 'AsyncWebhookListener' +] \ No newline at end of file diff --git a/telebot/extensions/webhooks.py b/telebot/extensions/webhooks.py index ddfae41e9..9f44c4854 100644 --- a/telebot/extensions/webhooks.py +++ b/telebot/extensions/webhooks.py @@ -2,11 +2,14 @@ This file is used by TeleBot.run_webhooks() & AsyncTeleBot.run_webhooks() functions. -Flask/Aiohttp is required to run this script. +Flask/fastapi is required to run this script. """ - +# modules required flask_installed = True +fastapi_installed = True +uvicorn_installed = True + try: import flask from werkzeug.serving import _TSSLContextArg @@ -14,7 +17,20 @@ flask_installed = False _TSSLContextArg = None +try: + import fastapi + from fastapi.responses import JSONResponse +except ImportError: + fastapi_installed = False + + +try: + from uvicorn import Server, Config +except ImportError: + uvicorn_installed = False + +import asyncio from telebot.types import Update @@ -85,4 +101,85 @@ def run_app(self): ssl_context=self._ssl_context, debug=self._debug ) - return self \ No newline at end of file + + + + +class AsyncWebhookListener: + def __init__(self, bot, + secret_token: str, host: Optional[str]="127.0.0.1", + port: Optional[int]=8000, + ssl_context: Optional[_TSSLContextArg]=None, + url_path: Optional[str]=None, + debug: Optional[bool]=False + ) -> None: + """ + Synchronous implementation of webhook listener + for synchronous version of telebot. + + :param bot: TeleBot instance + :param secret_token: Telegram secret token + :param host: Webhook host + :param port: Webhook port + :param ssl_context: SSL context + """ + self._check_dependencies() + + self.app = fastapi.FastAPI() + self._secret_token = secret_token + self._bot = bot + self._port = port + self._host = host + self._ssl_context = ssl_context + self._url_path = url_path + self._debug = debug + self._prepare_endpoint_urls() + + + def _check_dependencies(self): + if not fastapi_installed: + raise ImportError('Fastapi is not installed. Please install it via pip.') + if not uvicorn_installed: + raise ImportError('Uvicorn is not installed. Please install it via pip.') + + import starlette + if starlette.__version__ < '0.20.2': + raise ImportError('Starlette version is too old. Please upgrade it.') + return + + + def _prepare_endpoint_urls(self): + self.app.add_api_route(endpoint=self.process_update,path= self._url_path, methods=["POST"]) + + + async def process_update(self, request: fastapi.Request): + """ + Processes updates. + """ + # header containsX-Telegram-Bot-Api-Secret-Token + if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: + # secret token didn't match + return JSONResponse(status_code=403, content={"error": "Forbidden"}) + if request.headers.get('content-type') == 'application/json': + json_string = await request.json() + asyncio.create_task(self._bot.process_new_updates([Update.de_json(json_string)])) + return JSONResponse('', status_code=200) + + return JSONResponse(status_code=403, content={"error": "Forbidden"}) + + + async def run_app(self): + """ + Run app with the given parameters. + """ + + config = Config(app=self.app, + host=self._host, + port=self._port, + debug=self._debug, + ssl_certfile=self._ssl_context[0], + ssl_keyfile=self._ssl_context[1] + ) + server = Server(config) + await server.serve() + await self._bot.close_session() \ No newline at end of file From 0cf2a0fe77bbe8e6255abbc7fbfca847685f1b50 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 7 Jul 2022 23:02:51 +0500 Subject: [PATCH 1045/1808] Added extra dependencies and fixed tests --- setup.py | 4 ++++ telebot/extensions/webhooks.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index eb1c9ee65..5552a4329 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,11 @@ def read(filename): 'json': 'ujson', 'PIL': 'Pillow', 'redis': 'redis>=3.4.1', + 'aioredis': 'aioredis', 'aiohttp': 'aiohttp', + 'flask': 'flask', + 'fastapi': 'fastapi', + 'uvicorn': 'uvicorn', }, classifiers=[ 'Development Status :: 5 - Production/Stable', diff --git a/telebot/extensions/webhooks.py b/telebot/extensions/webhooks.py index 9f44c4854..1d3d7dbbd 100644 --- a/telebot/extensions/webhooks.py +++ b/telebot/extensions/webhooks.py @@ -152,7 +152,7 @@ def _prepare_endpoint_urls(self): self.app.add_api_route(endpoint=self.process_update,path= self._url_path, methods=["POST"]) - async def process_update(self, request: fastapi.Request): + async def process_update(self, request): """ Processes updates. """ From a7c420aa14274f20de1a41c22fbe68bf2105ad70 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 8 Jul 2022 11:15:50 +0300 Subject: [PATCH 1046/1808] parse_web_app_data function fix --- telebot/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 62ebe7f27..b9d7f7da2 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -6,7 +6,6 @@ import traceback from typing import Any, Callable, List, Dict, Optional, Union import hmac -import json from hashlib import sha256 from urllib.parse import parse_qsl @@ -525,7 +524,7 @@ def antiflood(function, *args, **kwargs): def parse_web_app_data(token: str, raw_init_data: str): - is_valid = validate_WebApp_data(token, raw_init_data) + is_valid = validate_web_app_data(token, raw_init_data) if not is_valid: return False From 970b9d6be4f71555d25f327ff018b237907b2dcf Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 8 Jul 2022 21:13:07 +0500 Subject: [PATCH 1047/1808] SyncWebhookListener was rewritten under fastapi. Extensions folder was divided into 2(namings are long, might be changed) --- telebot/__init__.py | 10 +- telebot/async_telebot.py | 9 +- telebot/extensions/__init__.py | 11 +-- telebot/extensions/asynchronous/__init__.py | 10 ++ telebot/extensions/asynchronous/webhooks.py | 103 ++++++++++++++++++++ telebot/extensions/synchronous/__init__.py | 10 ++ telebot/extensions/synchronous/webhooks.py | 101 +++++++++++++++++++ 7 files changed, 238 insertions(+), 16 deletions(-) create mode 100644 telebot/extensions/asynchronous/__init__.py create mode 100644 telebot/extensions/asynchronous/webhooks.py create mode 100644 telebot/extensions/synchronous/__init__.py create mode 100644 telebot/extensions/synchronous/webhooks.py diff --git a/telebot/__init__.py b/telebot/__init__.py index cc624bc1f..8d73f638f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -23,9 +23,6 @@ import string -# webhooks module -from telebot.extensions import SyncWebhookListener - import ssl logger = logging.getLogger('TeleBot') @@ -372,7 +369,12 @@ def run_webhooks(self, ) if cert_file: cert_file.close() - ssl_context = (certificate, certificate_key) if certificate else None + ssl_context = (certificate, certificate_key) if certificate else (None, None) + # webhooks module + try: + from telebot.extensions.synchronous import SyncWebhookListener + except NameError: + raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) self.webhook_listener.run_app() diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fd21d30c3..1257e73bc 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -30,8 +30,6 @@ types.ReplyKeyboardRemove, types.ForceReply] -# for webhooks -from telebot.extensions import AsyncWebhookListener import string import random import ssl @@ -1515,9 +1513,12 @@ async def run_webhooks(self, if cert_file: cert_file.close() ssl_context = (certificate, certificate_key) if certificate else (None, None) + # for webhooks + try: + from telebot.extensions.asynchronous import AsyncWebhookListener + except NameError: + raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") self.webhook_listener = AsyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) - # create a new loop, set it, and pass it - asyncio.set_event_loop(asyncio.new_event_loop()) await self.webhook_listener.run_app() diff --git a/telebot/extensions/__init__.py b/telebot/extensions/__init__.py index 9535cbebb..e417b38c2 100644 --- a/telebot/extensions/__init__.py +++ b/telebot/extensions/__init__.py @@ -1,8 +1,3 @@ -from .webhooks import SyncWebhookListener, AsyncWebhookListener - - - -__all__ = [ - 'SyncWebhookListener', - 'AsyncWebhookListener' -] \ No newline at end of file +""" +A folder with asynchronous and synchronous extensions. +""" diff --git a/telebot/extensions/asynchronous/__init__.py b/telebot/extensions/asynchronous/__init__.py new file mode 100644 index 000000000..3d2fd2654 --- /dev/null +++ b/telebot/extensions/asynchronous/__init__.py @@ -0,0 +1,10 @@ +""" +A folder with all the async extensions. +""" + +from .webhooks import AsyncWebhookListener + + +__all__ = [ + "AsyncWebhookListener" +] \ No newline at end of file diff --git a/telebot/extensions/asynchronous/webhooks.py b/telebot/extensions/asynchronous/webhooks.py new file mode 100644 index 000000000..13e28aba4 --- /dev/null +++ b/telebot/extensions/asynchronous/webhooks.py @@ -0,0 +1,103 @@ +""" +This file is used by AsyncTeleBot.run_webhooks() function. + +Fastapi and starlette(0.20.2+) libraries are required to run this script. +""" + +# modules required for running this script +fastapi_installed = True +try: + import fastapi + from fastapi.responses import JSONResponse + from fastapi.requests import Request + from uvicorn import Server, Config +except ImportError: + fastapi_installed = False + +import asyncio + + +from telebot.types import Update + + +from typing import Optional + + +class AsyncWebhookListener: + def __init__(self, bot, + secret_token: str, host: Optional[str]="127.0.0.1", + port: Optional[int]=443, + ssl_context: Optional[tuple]=None, + url_path: Optional[str]=None, + debug: Optional[bool]=False + ) -> None: + """ + Aynchronous implementation of webhook listener + for asynchronous version of telebot. + + :param bot: TeleBot instance + :param secret_token: Telegram secret token + :param host: Webhook host + :param port: Webhook port + :param ssl_context: SSL context + :param url_path: Webhook url path + :param debug: Debug mode + """ + self._check_dependencies() + + self.app = fastapi.FastAPI() + self._secret_token = secret_token + self._bot = bot + self._port = port + self._host = host + self._ssl_context = ssl_context + self._url_path = url_path + self._debug = debug + self._prepare_endpoint_urls() + + + def _check_dependencies(self): + if not fastapi_installed: + raise ImportError('Fastapi or uvicorn is not installed. Please install it via pip.') + + import starlette + if starlette.__version__ < '0.20.2': + raise ImportError('Starlette version is too old. Please upgrade it: `pip3 install starlette -U`') + return + + + def _prepare_endpoint_urls(self): + self.app.add_api_route(endpoint=self.process_update,path= self._url_path, methods=["POST"]) + + + async def process_update(self, request: Request, update: dict): + """ + Processes updates. + """ + # header containsX-Telegram-Bot-Api-Secret-Token + if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: + # secret token didn't match + return JSONResponse(status_code=403, content={"error": "Forbidden"}) + if request.headers.get('content-type') == 'application/json': + json_string = update + asyncio.create_task(self._bot.process_new_updates([Update.de_json(json_string)])) + return JSONResponse('', status_code=200) + + return JSONResponse(status_code=403, content={"error": "Forbidden"}) + + + async def run_app(self): + """ + Run app with the given parameters. + """ + + config = Config(app=self.app, + host=self._host, + port=self._port, + debug=self._debug, + ssl_certfile=self._ssl_context[0], + ssl_keyfile=self._ssl_context[1] + ) + server = Server(config) + await server.serve() + await self._bot.close_session() \ No newline at end of file diff --git a/telebot/extensions/synchronous/__init__.py b/telebot/extensions/synchronous/__init__.py new file mode 100644 index 000000000..f7728aec7 --- /dev/null +++ b/telebot/extensions/synchronous/__init__.py @@ -0,0 +1,10 @@ +""" +A folder with all the sync extensions. +""" + +from .webhooks import SyncWebhookListener + + +__all__ = [ + "SyncWebhookListener" +] \ No newline at end of file diff --git a/telebot/extensions/synchronous/webhooks.py b/telebot/extensions/synchronous/webhooks.py new file mode 100644 index 000000000..89a3ec928 --- /dev/null +++ b/telebot/extensions/synchronous/webhooks.py @@ -0,0 +1,101 @@ +""" +This file is used by TeleBot.run_webhooks() & +AsyncTeleBot.run_webhooks() functions. + +Flask/fastapi is required to run this script. +""" + +# modules required for running this script +fastapi_installed = True + +try: + import fastapi + from fastapi.responses import JSONResponse + from fastapi.requests import Request + import uvicorn +except ImportError: + fastapi_installed = False + + +from telebot.types import Update + + +from typing import Optional + + + + +class SyncWebhookListener: + def __init__(self, bot, + secret_token: str, host: Optional[str]="127.0.0.1", + port: Optional[int]=443, + ssl_context: Optional[tuple]=None, + url_path: Optional[str]=None, + debug: Optional[bool]=False + ) -> None: + """ + Synchronous implementation of webhook listener + for synchronous version of telebot. + + :param bot: TeleBot instance + :param secret_token: Telegram secret token + :param host: Webhook host + :param port: Webhook port + :param ssl_context: SSL context + :param url_path: Webhook url path + :param debug: Debug mode + """ + self._check_dependencies() + + self.app = fastapi.FastAPI() + self._secret_token = secret_token + self._bot = bot + self._port = port + self._host = host + self._ssl_context = ssl_context + self._url_path = url_path + self._debug = debug + self._prepare_endpoint_urls() + + + def _check_dependencies(self): + if not fastapi_installed: + raise ImportError('Fastapi or uvicorn is not installed. Please install it via pip.') + + import starlette + if starlette.__version__ < '0.20.2': + raise ImportError('Starlette version is too old. Please upgrade it: `pip3 install starlette -U`') + return + + + def _prepare_endpoint_urls(self): + self.app.add_api_route(endpoint=self.process_update,path= self._url_path, methods=["POST"]) + + + def process_update(self, request: Request, update: dict): + """ + Processes updates. + """ + # header containsX-Telegram-Bot-Api-Secret-Token + if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: + # secret token didn't match + return JSONResponse(status_code=403, content={"error": "Forbidden"}) + if request.headers.get('content-type') == 'application/json': + self._bot.process_new_updates([Update.de_json(update)]) + return JSONResponse('', status_code=200) + + return JSONResponse(status_code=403, content={"error": "Forbidden"}) + + + def run_app(self): + """ + Run app with the given parameters. + """ + + uvicorn.run(app=self.app, + host=self._host, + port=self._port, + debug=self._debug, + ssl_certfile=self._ssl_context[0], + ssl_keyfile=self._ssl_context[1] + ) From d67ee2a5c521194a9e3366aa48ab5f2ed8b810de Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 8 Jul 2022 21:16:01 +0500 Subject: [PATCH 1048/1808] Delete webhooks.py --- telebot/extensions/webhooks.py | 185 --------------------------------- 1 file changed, 185 deletions(-) delete mode 100644 telebot/extensions/webhooks.py diff --git a/telebot/extensions/webhooks.py b/telebot/extensions/webhooks.py deleted file mode 100644 index 1d3d7dbbd..000000000 --- a/telebot/extensions/webhooks.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -This file is used by TeleBot.run_webhooks() & -AsyncTeleBot.run_webhooks() functions. - -Flask/fastapi is required to run this script. -""" - -# modules required -flask_installed = True -fastapi_installed = True -uvicorn_installed = True - -try: - import flask - from werkzeug.serving import _TSSLContextArg -except ImportError: - flask_installed = False - _TSSLContextArg = None - -try: - import fastapi - from fastapi.responses import JSONResponse -except ImportError: - fastapi_installed = False - - -try: - from uvicorn import Server, Config -except ImportError: - uvicorn_installed = False - - -import asyncio -from telebot.types import Update - - -from typing import Optional - - - - - -class SyncWebhookListener: - def __init__(self, bot, - secret_token: str, host: Optional[str]="127.0.0.1", - port: Optional[int]=8000, - ssl_context: Optional[_TSSLContextArg]=None, - url_path: Optional[str]=None, - debug: Optional[bool]=False - ) -> None: - """ - Synchronous implementation of webhook listener - for synchronous version of telebot. - - :param bot: TeleBot instance - :param secret_token: Telegram secret token - :param host: Webhook host - :param port: Webhook port - :param ssl_context: SSL context - """ - if not flask_installed: - raise ImportError('Flask is not installed. Please install it via pip.') - self.app = flask.Flask(__name__) - self._secret_token = secret_token - self._bot = bot - self._port = port - self._host = host - self._ssl_context = ssl_context - self._url_path = url_path - self._debug = debug - self._prepare_endpoint_urls() - - - def _prepare_endpoint_urls(self): - self.app.add_url_rule(self._url_path, 'index', self.process_update, methods=['POST']) - - - def process_update(self): - """ - Processes updates. - """ - # header containsX-Telegram-Bot-Api-Secret-Token - if flask.request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: - # secret token didn't match - flask.abort(403) - if flask.request.headers.get('content-type') == 'application/json': - json_string = flask.request.get_data().decode('utf-8') - self._bot.process_new_updates([Update.de_json(json_string)]) - return '' - - flask.abort(403) - - - def run_app(self): - """ - Run app with the given parameters. - """ - self.app.run( - host=self._host, - port=self._port, - ssl_context=self._ssl_context, - debug=self._debug - ) - - - - -class AsyncWebhookListener: - def __init__(self, bot, - secret_token: str, host: Optional[str]="127.0.0.1", - port: Optional[int]=8000, - ssl_context: Optional[_TSSLContextArg]=None, - url_path: Optional[str]=None, - debug: Optional[bool]=False - ) -> None: - """ - Synchronous implementation of webhook listener - for synchronous version of telebot. - - :param bot: TeleBot instance - :param secret_token: Telegram secret token - :param host: Webhook host - :param port: Webhook port - :param ssl_context: SSL context - """ - self._check_dependencies() - - self.app = fastapi.FastAPI() - self._secret_token = secret_token - self._bot = bot - self._port = port - self._host = host - self._ssl_context = ssl_context - self._url_path = url_path - self._debug = debug - self._prepare_endpoint_urls() - - - def _check_dependencies(self): - if not fastapi_installed: - raise ImportError('Fastapi is not installed. Please install it via pip.') - if not uvicorn_installed: - raise ImportError('Uvicorn is not installed. Please install it via pip.') - - import starlette - if starlette.__version__ < '0.20.2': - raise ImportError('Starlette version is too old. Please upgrade it.') - return - - - def _prepare_endpoint_urls(self): - self.app.add_api_route(endpoint=self.process_update,path= self._url_path, methods=["POST"]) - - - async def process_update(self, request): - """ - Processes updates. - """ - # header containsX-Telegram-Bot-Api-Secret-Token - if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: - # secret token didn't match - return JSONResponse(status_code=403, content={"error": "Forbidden"}) - if request.headers.get('content-type') == 'application/json': - json_string = await request.json() - asyncio.create_task(self._bot.process_new_updates([Update.de_json(json_string)])) - return JSONResponse('', status_code=200) - - return JSONResponse(status_code=403, content={"error": "Forbidden"}) - - - async def run_app(self): - """ - Run app with the given parameters. - """ - - config = Config(app=self.app, - host=self._host, - port=self._port, - debug=self._debug, - ssl_certfile=self._ssl_context[0], - ssl_keyfile=self._ssl_context[1] - ) - server = Server(config) - await server.serve() - await self._bot.close_session() \ No newline at end of file From 90a90d4a340d162fd3e6e2c375523ecda5e532e8 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 9 Jul 2022 22:30:36 +0500 Subject: [PATCH 1049/1808] Divided async and sync versions into aio & sync folders --- examples/asynchronous_telebot/webhooks/run_webhooks.py | 2 +- examples/webhook_examples/run_webhooks.py | 2 +- setup.py | 1 - telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 4 ++-- telebot/{extensions => ext}/__init__.py | 0 telebot/{extensions/asynchronous => ext/aio}/__init__.py | 0 telebot/{extensions/asynchronous => ext/aio}/webhooks.py | 0 telebot/{extensions/synchronous => ext/sync}/__init__.py | 0 telebot/{extensions/synchronous => ext/sync}/webhooks.py | 0 10 files changed, 6 insertions(+), 7 deletions(-) rename telebot/{extensions => ext}/__init__.py (100%) rename telebot/{extensions/asynchronous => ext/aio}/__init__.py (100%) rename telebot/{extensions/asynchronous => ext/aio}/webhooks.py (100%) rename telebot/{extensions/synchronous => ext/sync}/__init__.py (100%) rename telebot/{extensions/synchronous => ext/sync}/webhooks.py (100%) diff --git a/examples/asynchronous_telebot/webhooks/run_webhooks.py b/examples/asynchronous_telebot/webhooks/run_webhooks.py index fa145fc98..3b74c33d1 100644 --- a/examples/asynchronous_telebot/webhooks/run_webhooks.py +++ b/examples/asynchronous_telebot/webhooks/run_webhooks.py @@ -11,7 +11,7 @@ WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key -DOMAIN = '123.12.33.22' # either domain, or ip address of vps +DOMAIN = '1.2.3.4' # either domain, or ip address of vps # Quick'n'dirty SSL certificate generation: # diff --git a/examples/webhook_examples/run_webhooks.py b/examples/webhook_examples/run_webhooks.py index 8332d3050..eeac2d142 100644 --- a/examples/webhook_examples/run_webhooks.py +++ b/examples/webhook_examples/run_webhooks.py @@ -12,7 +12,7 @@ WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key -DOMAIN = '123.12.33.22' # either domain, or ip address of vps +DOMAIN = '1.2.3.4' # either domain, or ip address of vps # Quick'n'dirty SSL certificate generation: # diff --git a/setup.py b/setup.py index 5552a4329..2e60d9142 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,6 @@ def read(filename): 'redis': 'redis>=3.4.1', 'aioredis': 'aioredis', 'aiohttp': 'aiohttp', - 'flask': 'flask', 'fastapi': 'fastapi', 'uvicorn': 'uvicorn', }, diff --git a/telebot/__init__.py b/telebot/__init__.py index 8d73f638f..1c88c179d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -372,8 +372,8 @@ def run_webhooks(self, ssl_context = (certificate, certificate_key) if certificate else (None, None) # webhooks module try: - from telebot.extensions.synchronous import SyncWebhookListener - except NameError: + from telebot.ext.sync import SyncWebhookListener + except (NameError, ImportError): raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) self.webhook_listener.run_app() diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1257e73bc..e9ee8899e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1515,8 +1515,8 @@ async def run_webhooks(self, ssl_context = (certificate, certificate_key) if certificate else (None, None) # for webhooks try: - from telebot.extensions.asynchronous import AsyncWebhookListener - except NameError: + from telebot.ext.aio import AsyncWebhookListener + except (NameError, ImportError): raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") self.webhook_listener = AsyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) await self.webhook_listener.run_app() diff --git a/telebot/extensions/__init__.py b/telebot/ext/__init__.py similarity index 100% rename from telebot/extensions/__init__.py rename to telebot/ext/__init__.py diff --git a/telebot/extensions/asynchronous/__init__.py b/telebot/ext/aio/__init__.py similarity index 100% rename from telebot/extensions/asynchronous/__init__.py rename to telebot/ext/aio/__init__.py diff --git a/telebot/extensions/asynchronous/webhooks.py b/telebot/ext/aio/webhooks.py similarity index 100% rename from telebot/extensions/asynchronous/webhooks.py rename to telebot/ext/aio/webhooks.py diff --git a/telebot/extensions/synchronous/__init__.py b/telebot/ext/sync/__init__.py similarity index 100% rename from telebot/extensions/synchronous/__init__.py rename to telebot/ext/sync/__init__.py diff --git a/telebot/extensions/synchronous/webhooks.py b/telebot/ext/sync/webhooks.py similarity index 100% rename from telebot/extensions/synchronous/webhooks.py rename to telebot/ext/sync/webhooks.py From 124606fdcbc5d4a2a34d6919c731324575d8acb4 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 11 Jul 2022 23:22:11 +0500 Subject: [PATCH 1050/1808] Extend exception handler behaviour with middlewares --- telebot/__init__.py | 9 ++++----- telebot/async_telebot.py | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 09b30027a..531cda41c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3964,11 +3964,10 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, *args, ** except Exception as e: handler_error = e - if not middlewares: - if self.exception_handler: - return self.exception_handler.handle(e) - logging.error(str(e)) - return + if self.exception_handler: + self.exception_handler.handle(e) + else: logging.error(str(e)) + if middlewares: for middleware in middlewares: middleware.post_process(message, data, handler_error) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 35c2da8c1..7f4904917 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -319,11 +319,10 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): except Exception as e: handler_error = e - if not middlewares: - if self.exception_handler: - return self.exception_handler.handle(e) - logging.error(str(e)) - return + if self.exception_handler: + self.exception_handler.handle(e) + else: logging.error(str(e)) + if middlewares: for middleware in middlewares: From 8c6f81546c30d5d1ae224715e1892caf7a281eba Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 11 Jul 2022 23:24:22 +0500 Subject: [PATCH 1051/1808] A typo fixed --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index efb42a8c0..eeffbb156 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -345,7 +345,7 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): if self.exception_handler: self.exception_handler.handle(e) - else: logging.error(str(e)) + else: logger.error(str(e)) if middlewares: From 16703161aa2dfc9fc40cfac55f1241fffc728afb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 12 Jul 2022 22:44:25 +0300 Subject: [PATCH 1052/1808] Typo --- telebot/__init__.py | 55 ++++++--------------------------------------- 1 file changed, 7 insertions(+), 48 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 048d9bf07..c0df4f4f6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -9,7 +9,6 @@ import traceback from typing import Any, Callable, List, Optional, Union - # these imports are used to avoid circular import error import telebot.util import telebot.types @@ -17,12 +16,10 @@ # storage from telebot.storage import StatePickleStorage, StateMemoryStorage - # random module to generate random string import random import string - import ssl logger = logging.getLogger('TeleBot') @@ -93,7 +90,6 @@ class TeleBot: See more examples in examples/ directory: https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples - """ def __init__( @@ -170,8 +166,6 @@ def __init__( 'ENABLE_MIDDLEWARE set to True. This is not recommended.' ) self.middlewares = [] if use_class_middlewares else None - - self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(self, num_threads=num_threads) @@ -299,7 +293,6 @@ def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_ return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout, secret_token) - def run_webhooks(self, listen: Optional[str]="127.0.0.1", port: Optional[int]=443, @@ -331,6 +324,8 @@ def run_webhooks(self, :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout :param secret_token: Secret token to be used to verify the webhook request. + :param secret_token_length: + :param debug: :return: """ @@ -338,13 +333,10 @@ def run_webhooks(self, if not secret_token: secret_token = ''.join(random.choices(string.ascii_uppercase + string.digits, k=secret_token_length)) - if not url_path: url_path = self.token + '/' if url_path[-1] != '/': url_path += '/' - - protocol = "https" if certificate else "http" if not webhook_url: webhook_url = "{}://{}:{}/{}".format(protocol, listen, port, url_path) @@ -352,8 +344,6 @@ def run_webhooks(self, if certificate and certificate_key: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(certificate, certificate_key) - else: - ssl_ctx = None # open certificate if it exists cert_file = open(certificate, 'rb') if certificate else None @@ -378,8 +368,6 @@ def run_webhooks(self, self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) self.webhook_listener.run_app() - - def delete_webhook(self, drop_pending_updates=None, timeout=None): """ Use this method to remove webhook integration if you decide to switch back to getUpdates. @@ -486,8 +474,7 @@ def process_new_updates(self, updates): else: if update.update_id > self.last_update_id: self.last_update_id = update.update_id continue - - + if update.update_id > self.last_update_id: self.last_update_id = update.update_id if update.message: @@ -1519,9 +1506,6 @@ def send_video_note( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send - video messages. - Telegram documentation: https://core.telegram.org/bots/api#sendvideonote :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id @@ -1742,7 +1726,6 @@ def send_contact( :param protect_content: :return: """ - return types.Message.de_json( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, @@ -2158,11 +2141,9 @@ def set_chat_menu_button(self, chat_id: Union[int, str]=None, :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be changed. :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault - """ return apihelper.set_chat_menu_button(self.token, chat_id, menu_button) - def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButton: """ Use this method to get the current value of the bot's menu button @@ -2174,11 +2155,9 @@ def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButto :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be returned. :return: types.MenuButton - """ return types.MenuButton.de_json(apihelper.get_chat_menu_button(self.token, chat_id)) - def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, for_channels: bool=None) -> bool: """ @@ -2193,9 +2172,7 @@ def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRig :param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared. :param for_channels: Pass True to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. """ - return apihelper.set_my_default_administrator_rights(self.token, rights, for_channels) - def get_my_default_administrator_rights(self, for_channels: bool=None) -> types.ChatAdministratorRights: """ @@ -2207,11 +2184,9 @@ def get_my_default_administrator_rights(self, for_channels: bool=None) -> types. :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. :return: types.ChatAdministratorRights """ - return types.ChatAdministratorRights.de_json(apihelper.get_my_default_administrator_rights(self.token, for_channels)) - - def set_my_commands(self, commands: List[types.BotCommand], + def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: """ @@ -2551,7 +2526,6 @@ def send_invoice( max_tip_amount, suggested_tip_amounts, protect_content) return types.Message.de_json(result) - def create_invoice_link(self, title: str, description: str, payload:str, provider_token: str, currency: str, prices: List[types.LabeledPrice], @@ -2612,8 +2586,6 @@ def create_invoice_link(self, send_email_to_provider, is_flexible) return result - - # noinspection PyShadowingBuiltins # TODO: rewrite this method like in API def send_poll( @@ -2940,11 +2912,9 @@ def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryR :param result: A JSON-serialized object describing the message to be sent :return: """ - return apihelper.answer_web_app_query(self.token, web_app_query_id, result) - def register_for_reply( - self, message: types.Message, callback: Callable, *args, **kwargs) -> None: + def register_for_reply(self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when a reply to `message` arrives. @@ -2984,8 +2954,7 @@ def _notify_reply_handlers(self, new_messages) -> None: for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) - def register_next_step_handler( - self, message: types.Message, callback: Callable, *args, **kwargs) -> None: + def register_next_step_handler(self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when new message arrives after `message`. @@ -2999,7 +2968,6 @@ def register_next_step_handler( chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) - def setup_middleware(self, middleware: BaseMiddleware): """ Register middleware @@ -3011,8 +2979,6 @@ def setup_middleware(self, middleware: BaseMiddleware): logger.warning('Middleware is not enabled. Pass use_class_middlewares=True to enable it.') return self.middlewares.append(middleware) - - def set_state(self, user_id: int, state: Union[int, str, State], chat_id: int=None) -> None: """ @@ -3035,8 +3001,8 @@ def reset_data(self, user_id: int, chat_id: int=None): """ if chat_id is None: chat_id = user_id - self.current_states.reset_data(chat_id, user_id) + def delete_state(self, user_id: int, chat_id: int=None) -> None: """ Delete the current state of a user. @@ -3449,7 +3415,6 @@ def register_edited_message_handler(self, callback, content_types=None, commands **kwargs) self.add_edited_message_handler(handler_dict) - def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Channel post handler decorator @@ -4086,7 +4051,6 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): return elif isinstance(result, SkipHandler) and skip_handler is False: skip_handler = True - try: if handlers and not skip_handler: @@ -4122,10 +4086,8 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): return handler["function"](message, **data_copy) - except Exception as e: handler_error = e - if self.exception_handler: self.exception_handler.handle(e) else: logging.error(str(e)) @@ -4134,9 +4096,6 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): for middleware in middlewares: middleware.post_process(message, data, handler_error) - - - def _notify_command_handlers(self, handlers, new_messages, update_type): """ Notifies command handlers. From b276bfacaff3ff5e32b210272cd79c1b0f1f4032 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 13 Jul 2022 12:30:13 +0300 Subject: [PATCH 1053/1808] Fix exception with typed_middleware_handlers + some additional checks --- telebot/__init__.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c0df4f4f6..b1260c20f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -161,6 +161,7 @@ def __init__( } self.default_middleware_handlers = [] if apihelper.ENABLE_MIDDLEWARE and use_class_middlewares: + self.typed_middleware_handlers = {} logger.warning( 'You are using class based middlewares, but you have ' 'ENABLE_MIDDLEWARE set to True. This is not recommended.' @@ -595,16 +596,17 @@ def process_new_chat_join_request(self, chat_join_request): self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') def process_middlewares(self, update): - for update_type, middlewares in self.typed_middleware_handlers.items(): - if getattr(update, update_type) is not None: - for typed_middleware_handler in middlewares: - try: - typed_middleware_handler(self, getattr(update, update_type)) - except Exception as e: - e.args = e.args + (f'Typed middleware handler "{typed_middleware_handler.__qualname__}"',) - raise - - if len(self.default_middleware_handlers) > 0: + if self.typed_middleware_handlers: + for update_type, middlewares in self.typed_middleware_handlers.items(): + if getattr(update, update_type) is not None: + for typed_middleware_handler in middlewares: + try: + typed_middleware_handler(self, getattr(update, update_type)) + except Exception as e: + e.args = e.args + (f'Typed middleware handler "{typed_middleware_handler.__qualname__}"',) + raise + + if self.default_middleware_handlers: for default_middleware_handler in self.default_middleware_handlers: try: default_middleware_handler(self, update) @@ -3170,10 +3172,13 @@ def add_middleware_handler(self, handler, update_types=None): if not apihelper.ENABLE_MIDDLEWARE: raise RuntimeError("Middleware is not enabled. Use apihelper.ENABLE_MIDDLEWARE before initialising TeleBot.") - if update_types: + added = False + if update_types and self.typed_middleware_handlers: for update_type in update_types: - self.typed_middleware_handlers[update_type].append(handler) - else: + if update_type in self.typed_middleware_handlers: + added = True + self.typed_middleware_handlers[update_type].append(handler) + if not added: self.default_middleware_handlers.append(handler) # function register_middleware_handler From a1bcd3c42ed7fa5a87b8fbce9c486508814aa781 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 13 Jul 2022 13:10:16 +0300 Subject: [PATCH 1054/1808] use_class_middlewares checks added --- telebot/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b1260c20f..b086cc95f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -161,10 +161,9 @@ def __init__( } self.default_middleware_handlers = [] if apihelper.ENABLE_MIDDLEWARE and use_class_middlewares: - self.typed_middleware_handlers = {} - logger.warning( - 'You are using class based middlewares, but you have ' - 'ENABLE_MIDDLEWARE set to True. This is not recommended.' + self.typed_middleware_handlers = None + logger.error( + 'You are using class based middlewares while having ENABLE_MIDDLEWARE set to True. This is not recommended.' ) self.middlewares = [] if use_class_middlewares else None self.threaded = threaded @@ -465,7 +464,7 @@ def process_new_updates(self, updates): new_chat_join_request = None for update in updates: - if apihelper.ENABLE_MIDDLEWARE: + if apihelper.ENABLE_MIDDLEWARE and not self.use_class_middlewares: try: self.process_middlewares(update) except Exception as e: @@ -3172,6 +3171,10 @@ def add_middleware_handler(self, handler, update_types=None): if not apihelper.ENABLE_MIDDLEWARE: raise RuntimeError("Middleware is not enabled. Use apihelper.ENABLE_MIDDLEWARE before initialising TeleBot.") + if self.use_class_middlewares: + logger.error("middleware_handler/register_middleware_handler/add_middleware_handler cannot be used with use_class_middlewares=True. Skipped.") + return + added = False if update_types and self.typed_middleware_handlers: for update_type in update_types: From 659501efefb74070946221c2059bfd5ec340f44e Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 13 Jul 2022 21:52:48 +0500 Subject: [PATCH 1055/1808] Update async_telebot.py --- telebot/async_telebot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 000746e88..9c7df21f8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -136,7 +136,7 @@ async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=Non return [types.Update.de_json(ju) for ju in json_updates] async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, - request_timeout: int=20, allowed_updates: Optional[List[str]]=None, + request_timeout: int=None, allowed_updates: Optional[List[str]]=None, none_stop: Optional[bool]=None): """ This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. @@ -169,7 +169,7 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= await self.skip_updates() await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) - async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=20, logger_level=logging.ERROR, + async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=None, logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -208,7 +208,7 @@ async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, requ logger.error("Break infinity polling") async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, - request_timeout: int=20, allowed_updates: Optional[List[str]]=None): + request_timeout: int=None, allowed_updates: Optional[List[str]]=None): """ Function to process polling. From 0c6f84c79a0215869d8a6f4b480218f6576a4575 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 15 Jul 2022 16:24:15 +0500 Subject: [PATCH 1056/1808] update_sensitive field for middlewares --- telebot/__init__.py | 32 ++++++++++++++++++++++++----- telebot/async_telebot.py | 32 +++++++++++++++++++++++------ telebot/asyncio_handler_backends.py | 27 ++++++++++++++++++++++++ telebot/handler_backends.py | 27 ++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1c88c179d..2d7b76284 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3008,8 +3008,17 @@ def setup_middleware(self, middleware: BaseMiddleware): :return: None """ if not self.use_class_middlewares: - logger.warning('Middleware is not enabled. Pass use_class_middlewares=True to enable it.') + logger.error('Middleware is not enabled. Pass use_class_middlewares=True to enable it.') return + + if not hasattr(middleware, 'update_types'): + logger.error('Middleware has no update_types parameter. Please add list of updates to handle.') + return + + if not hasattr(middleware, 'update_sensitive'): + logger.warning('Middleware has no update_sensitive parameter. Parameter was set to False.') + middleware.update_sensitive = False + self.middlewares.append(middleware) @@ -4065,7 +4074,7 @@ def _check_middleware(self, update_type): middlewares = [i for i in self.middlewares if update_type in i.update_types] return middlewares - def _run_middlewares_and_handler(self, message, handlers, middlewares): + def _run_middlewares_and_handler(self, message, handlers, middlewares, update_type): """ This class is made to run handler and middleware in queue. @@ -4079,7 +4088,14 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): skip_handler = False if middlewares: for middleware in middlewares: - result = middleware.pre_process(message, data) + if middleware.update_sensitive: + if hasattr(middleware, f'pre_process_{update_type}'): + result = getattr(middleware, f'pre_process_{update_type}')(message, data) + else: + logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) + result = None + else: + result = middleware.pre_process(message, data) # We will break this loop if CancelUpdate is returned # Also, we will not run other middlewares if isinstance(result, CancelUpdate): @@ -4134,7 +4150,13 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): # remove the bot from data if middlewares: for middleware in middlewares: - middleware.post_process(message, data, handler_error) + if middleware.update_sensitive: + if hasattr(middleware, f'post_process_{update_type}'): + result = getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) + else: + logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) + else: + result = middleware.post_process(message, data, handler_error) @@ -4153,7 +4175,7 @@ def _notify_command_handlers(self, handlers, new_messages, update_type): for message in new_messages: if self.use_class_middlewares: middleware = self._check_middleware(update_type) - self._exec_task(self._run_middlewares_and_handler, message, handlers=handlers, middlewares=middleware) + self._exec_task(self._run_middlewares_and_handler, message, handlers=handlers, middlewares=middleware, update_type=update_type) return else: for message_handler in handlers: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e9ee8899e..769ca6e16 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -14,7 +14,7 @@ # storages from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage -from telebot.asyncio_handler_backends import CancelUpdate, SkipHandler, State +from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate, SkipHandler, State from inspect import signature @@ -288,17 +288,24 @@ async def _process_updates(self, handlers, messages, update_type): tasks = [] for message in messages: middleware = await self.process_middlewares(update_type) - tasks.append(self._run_middlewares_and_handlers(handlers, message, middleware)) + tasks.append(self._run_middlewares_and_handlers(handlers, message, middleware, update_type)) await asyncio.gather(*tasks) - async def _run_middlewares_and_handlers(self, handlers, message, middlewares): + async def _run_middlewares_and_handlers(self, handlers, message, middlewares, update_type): handler_error = None data = {} process_handler = True params = [] if middlewares: for middleware in middlewares: - middleware_result = await middleware.pre_process(message, data) + if middleware.update_sensitive: + if hasattr(middleware, f'pre_process_{update_type}'): + middleware_result = await getattr(middleware, f'pre_process_{update_type}')(message, data) + else: + logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) + middleware_result = None + else: + middleware_result = await middleware.pre_process(message, data) if isinstance(middleware_result, SkipHandler): await middleware.post_process(message, data, handler_error) process_handler = False @@ -354,7 +361,12 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): if middlewares: for middleware in middlewares: - await middleware.post_process(message, data, handler_error) + if middleware.update_sensitive: + if hasattr(middleware, f'post_process_{update_type}'): + await getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) + else: + logger.error('Middleware {} does not have post_process_{} method. post_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) + else: await middleware.post_process(message, data, handler_error) # update handling async def process_new_updates(self, updates): """ @@ -595,13 +607,21 @@ async def _check_filter(self, message_filter, filter_value, message): logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") return False - def setup_middleware(self, middleware): + def setup_middleware(self, middleware: BaseMiddleware): """ Setup middleware. :param middleware: Middleware-class. :return: """ + if not hasattr(middleware, 'update_types'): + logger.error('Middleware has no update_types parameter. Please add list of updates to handle.') + return + + if not hasattr(middleware, 'update_sensitive'): + logger.warning('Middleware has no update_sensitive parameter. Parameter was set to False.') + middleware.update_sensitive = False + self.middlewares.append(middleware) def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 4ef455588..4f0d1743f 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -2,8 +2,35 @@ class BaseMiddleware: """ Base class for middleware. Your middlewares should be inherited from this class. + + Set update_sensitive=True if you want to get different updates on + different functions. For example, if you want to handle pre_process for + message update, then you will have to create pre_process_message function, and + so on. Same applies to post_process. + + .. code-block:: python + class MyMiddleware(BaseMiddleware): + def __init__(self): + self.update_sensitive = True + self.update_types = ['message', 'edited_message'] + + def pre_process_message(self, message, data): + # only message update here + pass + + def post_process_message(self, message, data, exception): + pass # only message update here for post_process + + def pre_process_edited_message(self, message, data): + # only edited_message update here + pass + + def post_process_edited_message(self, message, data, exception): + pass # only edited_message update here for post_process """ + update_sensitive: bool = False + def __init__(self): pass diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 430431711..598cf594f 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -173,8 +173,35 @@ class BaseMiddleware: """ Base class for middleware. Your middlewares should be inherited from this class. + + Set update_sensitive=True if you want to get different updates on + different functions. For example, if you want to handle pre_process for + message update, then you will have to create pre_process_message function, and + so on. Same applies to post_process. + + .. code-block:: python + class MyMiddleware(BaseMiddleware): + def __init__(self): + self.update_sensitive = True + self.update_types = ['message', 'edited_message'] + + def pre_process_message(self, message, data): + # only message update here + pass + + def post_process_message(self, message, data, exception): + pass # only message update here for post_process + + def pre_process_edited_message(self, message, data): + # only edited_message update here + pass + + def post_process_edited_message(self, message, data, exception): + pass # only edited_message update here for post_process """ + update_sensitive: bool = False + def __init__(self): pass From 1efe465e9dca9ec7bfef285cdb9cfdf491f9d005 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 15 Jul 2022 22:06:18 +0500 Subject: [PATCH 1057/1808] Update async_telebot.py --- telebot/async_telebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4d3812992..f1f1c8335 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -351,6 +351,7 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares): if self.exception_handler: self.exception_handler.handle(e) else: logger.error(str(e)) + break if middlewares: From ea1efad1ea42d79210236df3b7961a95c6a2cc9e Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 15 Jul 2022 22:28:43 +0500 Subject: [PATCH 1058/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 75492afa3..82534ab85 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1816,6 +1816,7 @@ def __init__(self, function_name, result, result_json): result) self.result_json = result_json self.error_code = result_json['error_code'] + self.description = result_json['description'] class RequestTimeout(Exception): """ From 3ffd06fcca62f27b47116824b807ee8703787f56 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 15 Jul 2022 23:26:55 +0500 Subject: [PATCH 1059/1808] Add feedback-bot to `Bots using this library` section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 253692bae..5a0ad888b 100644 --- a/README.md +++ b/README.md @@ -900,5 +900,6 @@ Here are some examples of template: * [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text. * [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...). * [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) GuXiaoJiang is a multi-functional robot, such as OSU game information query, IP test, animation screenshot search and other functions. +* [Feedback-bot](https://github.com/coder2020official/feedbackbot) A feedback bot for user-admin communication. Made on AsyncTeleBot, using [template](https://github.com/coder2020official/asynctelebot_template). **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 22beead3b5f74d835f0c5fe4835627d9b33d6d97 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 16 Jul 2022 20:09:52 +0500 Subject: [PATCH 1060/1808] Update __init__.py --- telebot/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b086cc95f..fa4ee1324 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2971,13 +2971,13 @@ def register_next_step_handler(self, message: types.Message, callback: Callable, def setup_middleware(self, middleware: BaseMiddleware): """ - Register middleware + Registers class-based middleware. :param middleware: Subclass of `telebot.handler_backends.BaseMiddleware` :return: None """ if not self.use_class_middlewares: - logger.warning('Middleware is not enabled. Pass use_class_middlewares=True to enable it.') + logger.warning('Class-based middlewares are not enabled. Pass use_class_middlewares=True to enable it.') return self.middlewares.append(middleware) @@ -3130,7 +3130,7 @@ def _build_handler_dict(handler, pass_bot=False, **filters): def middleware_handler(self, update_types=None): """ - Middleware handler decorator. + Function-based middleware handler decorator. This decorator can be used to decorate functions that must be handled as middlewares before entering any other message handlers @@ -3187,10 +3187,9 @@ def add_middleware_handler(self, handler, update_types=None): # function register_middleware_handler def register_middleware_handler(self, callback, update_types=None): """ - Middleware handler decorator. + Adds function-based middleware handler. - This function will create a decorator that can be used to decorate functions that must be handled as middlewares before entering any other - message handlers + This function will register your decorator function. Function-based middlewares are executed before handlers. But, be careful and check type of the update inside the handler if more than one update_type is given Example: @@ -4099,6 +4098,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares): if self.exception_handler: self.exception_handler.handle(e) else: logging.error(str(e)) + if middlewares: for middleware in middlewares: From e379708af62d2e3a7eddbbfb0c3628b38f5b463c Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 16 Jul 2022 20:12:38 +0500 Subject: [PATCH 1061/1808] Update __init__.py --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c39ce2aab..053a29e7d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2977,7 +2977,7 @@ def setup_middleware(self, middleware: BaseMiddleware): :return: None """ if not self.use_class_middlewares: - logger.warning('Class-based middlewares are not enabled. Pass use_class_middlewares=True to enable it.') + logger.error('Class-based middlewares are not enabled. Pass use_class_middlewares=True to enable it.') return if not hasattr(middleware, 'update_types'): From 147278733b74ee9a2b9001275493ec4242fef964 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 18 Jul 2022 13:47:14 +0500 Subject: [PATCH 1062/1808] Fix #1637 --- telebot/asyncio_helper.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 82534ab85..26c196633 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -26,9 +26,7 @@ class SessionManager: def __init__(self) -> None: - self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector( - limit=REQUEST_LIMIT - )) + self.session = None async def create_session(self): @@ -38,6 +36,10 @@ async def create_session(self): return self.session async def get_session(self): + if self.session is None: + self.session = await self.create_session() + return self.session + if self.session.closed: self.session = await self.create_session() From 49d3b463ed90d06bdfddb890615cce216339c496 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 18 Jul 2022 14:01:14 +0500 Subject: [PATCH 1063/1808] Fixes #1636 --- telebot/asyncio_helper.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 26c196633..843ee0b45 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1,5 +1,7 @@ import asyncio # for future uses +import ssl import aiohttp +import certifi from telebot import types try: @@ -27,11 +29,13 @@ class SessionManager: def __init__(self) -> None: self.session = None + self.ssl_context = ssl.create_default_context(cafile=certifi.where()) async def create_session(self): self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector( - limit=REQUEST_LIMIT + limit=REQUEST_LIMIT, + ssl_context=self.ssl_context )) return self.session From f6ec3493ad8528e59d3a9163270a8ce945c1556c Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 19 Jul 2022 00:27:21 +0500 Subject: [PATCH 1064/1808] Fixed 45% of typehints/docstrings for sync telebot --- telebot/__init__.py | 1083 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 855 insertions(+), 228 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 053a29e7d..17357fe98 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -14,7 +14,7 @@ import telebot.types # storage -from telebot.storage import StatePickleStorage, StateMemoryStorage +from telebot.storage import StatePickleStorage, StateMemoryStorage, StateStorageBase # random module to generate random string import random @@ -37,7 +37,7 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, CancelUpdate, SkipHandler, State +from telebot.handler_backends import HandlerBackend, MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, CancelUpdate, SkipHandler, State from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter @@ -46,6 +46,7 @@ types.ReplyKeyboardRemove, types.ForceReply] + """ Module : telebot """ @@ -53,7 +54,7 @@ class Handler: """ - Class for (next step|reply) handlers + Class for (next step|reply) handlers. """ def __init__(self, callback, *args, **kwargs): @@ -90,18 +91,53 @@ class TeleBot: See more examples in examples/ directory: https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples + + + :param token: Token of a bot, should be obtained from @BotFather + :type token: :obj:`str` + + :param parse_mode: Default parse mode, defaults to None + :type parse_mode: :obj:`str`, optional + + :param threaded: Threaded or not, defaults to True + :type threaded: :obj:`bool`, optional + + :param skip_pending: Skips pending updates, defaults to False + :type skip_pending: :obj:`bool`, optional + + :param num_threads: Number of maximum parallel threads, defaults to 2 + :type num_threads: :obj:`int`, optional + + :param next_step_backend: Next step backend class, defaults to None + :type next_step_backend: :class:`telebot.handler_backends.HandlerBackend`, optional + + :param reply_backend: Reply step handler class, defaults to None + :type reply_backend: :class:`telebot.handler_backends.HandlerBackend`, optional + + :param exception_handler: Exception handler to handle errors, defaults to None + :type exception_handler: :class:`telebot.ExceptionHandler`, optional + + :param last_update_id: Last update's id, defaults to 0 + :type last_update_id: :obj:`int`, optional + + :param suppress_middleware_excepions: Supress middleware exceptions, defaults to False + :type suppress_middleware_excepions: :obj:`bool`, optional + + :param state_storage: Storage for states, defaults to StateMemoryStorage() + :type state_storage: :class:`telebot.storage.StateStorageBase`, optional + + :param use_class_middlewares: Use class middlewares, defaults to False + :type use_class_middlewares: :obj:`bool`, optional """ 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, state_storage=StateMemoryStorage(), use_class_middlewares=False + self, token: str, parse_mode: Optional[str]=None, threaded: Optional[bool]=True, + skip_pending: Optional[bool]=False, num_threads: Optional[int]=2, + next_step_backend: Optional[HandlerBackend]=None, reply_backend: Optional[HandlerBackend]=None, + exception_handler: Optional[ExceptionHandler]=None, last_update_id: Optional[int]=0, + suppress_middleware_excepions: Optional[bool]=False, state_storage: Optional[StateStorageBase]=StateMemoryStorage(), + use_class_middlewares: Optional[bool]=False ): - """ - :param token: bot API token - :param parse_mode: default parse_mode - :return: Telebot object. - """ self.token = token self.parse_mode = parse_mode self.update_listener = [] @@ -174,13 +210,16 @@ def __init__( def user(self) -> types.User: """ The User object representing this bot. - Equivalent to bot.get_me() but the result is cached so only one API call is needed + Equivalent to bot.get_me() but the result is cached so only one API call is needed. + + :return: Bot's info. + :rtype: :class:`telebot.types.User` """ if not hasattr(self, "_user"): self._user = self.get_me() return self._user - def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"): + def enable_save_next_step_handlers(self, delay: Optional[int]=120, filename: Optional[str]="./.handler-saves/step.save"): """ Enable saving next step handlers (by default saving disabled) @@ -188,20 +227,32 @@ def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/s compatibility whose purpose was to enable file saving capability for handlers. And the same implementation is now available with FileHandlerBackend - :param delay: Delay between changes in handlers and saving - :param filename: Filename of save file + :param delay: Delay between changes in handlers and saving, defaults to 120 + :type delay: :obj:`int`, optional + + :param filename: Filename of save file, defaults to "./.handler-saves/step.save" + :type filename: :obj:`str`, optional + + :return: None """ self.next_step_backend = FileHandlerBackend(self.next_step_backend.handlers, filename, delay) - def enable_saving_states(self, filename="./.state-save/states.pkl"): + + def enable_saving_states(self, filename: Optional[str]="./.state-save/states.pkl"): """ Enable saving states (by default saving disabled) - :param filename: Filename of saving file + .. note:: + It is recommended to pass a :class:`~telebot.storage.StateMemoryStorage` instance as state_storage + to TeleBot class. + + :param filename: Filename of saving file, defaults to "./.state-save/states.pkl" + :type filename: :obj:`str`, optional """ self.current_states = StatePickleStorage(file_path=filename) self.current_states.create_dir() + def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"): """ Enable saving reply handlers (by default saving disable) @@ -210,11 +261,15 @@ def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply compatibility whose purpose was to enable file saving capability for handlers. And the same implementation is now available with FileHandlerBackend - :param delay: Delay between changes in handlers and saving - :param filename: Filename of save file + :param delay: Delay between changes in handlers and saving, defaults to 120 + :type delay: :obj:`int`, optional + + :param filename: Filename of save file, defaults to "./.handler-saves/reply.save" + :type filename: :obj:`str`, optional """ self.reply_backend = FileHandlerBackend(self.reply_backend.handlers, filename, delay) + def disable_save_next_step_handlers(self): """ Disable saving next step handlers (by default saving disable) @@ -225,6 +280,7 @@ def disable_save_next_step_handlers(self): """ self.next_step_backend = MemoryHandlerBackend(self.next_step_backend.handlers) + def disable_save_reply_handlers(self): """ Disable saving next step handlers (by default saving disable) @@ -235,6 +291,7 @@ def disable_save_reply_handlers(self): """ self.reply_backend = MemoryHandlerBackend(self.reply_backend.handlers) + def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True): """ Load next step handlers from save file @@ -243,11 +300,16 @@ def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_fil help of FileHandlerBackend and is only recommended to use if next_step_backend was assigned as FileHandlerBackend before entering this function - :param filename: Filename of the file where handlers was saved - :param del_file_after_loading: Is passed True, after loading save file will be deleted + + :param filename: Filename of the file where handlers was saved, defaults to "./.handler-saves/step.save" + :type filename: :obj:`str`, optional + + :param del_file_after_loading: If True is passed, after the loading file will be deleted, defaults to True + :type del_file_after_loading: :obj:`bool`, optional """ self.next_step_backend.load_handlers(filename, del_file_after_loading) + def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True): """ Load reply handlers from save file @@ -256,43 +318,72 @@ def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_a help of FileHandlerBackend and is only recommended to use if reply_backend was assigned as FileHandlerBackend before entering this function - :param filename: Filename of the file where handlers was saved - :param del_file_after_loading: Is passed True, after loading save file will be deleted + :param filename: Filename of the file where handlers was saved, defaults to "./.handler-saves/reply.save" + :type filename: :obj:`str`, optional + + :param del_file_after_loading: If True is passed, after the loading file will be deleted, defaults to True, defaults to True + :type del_file_after_loading: :obj:`bool`, optional """ self.reply_backend.load_handlers(filename, del_file_after_loading) - def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, - drop_pending_updates = None, timeout=None, secret_token=None): + + def set_webhook(self, url: Optional[str]=None, certificate: Optional[Union[str, Any]]=None, max_connections: Optional[int]=None, + allowed_updates: Optional[List[str]]=None, ip_address: Optional[str]=None, + drop_pending_updates: Optional[bool] = None, timeout: Optional[int]=None, secret_token: Optional[str]=None) -> bool: """ - Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an - update for the bot, we will send an HTTPS POST request to the specified url, - containing a JSON-serialized Update. - In case of an unsuccessful request, we will give up after a reasonable amount of attempts. - Returns True on success. + Use this method to specify a URL and receive incoming updates via an outgoing webhook. + Whenever there is an update for the bot, we will send an HTTPS POST request to the specified URL, + containing a JSON-serialized Update. In case of an unsuccessful request, we will give up after + a reasonable amount of attempts. Returns True on success. + + If you'd like to make sure that the webhook was set by you, you can specify secret data in the parameter secret_token. + If specified, the request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the secret token as content. + + Telegram Documentation: https://core.telegram.org/bots/api#setwebhook - Telegram documentation: https://core.telegram.org/bots/api#setwebhook - - :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration - :param certificate: Upload your public key certificate so that the root certificate in use can be checked. - See our self-signed guide for details. - :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook - for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, - and higher values to increase your bot's throughput. - :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates - of these types. See Update for a complete list of available update types. - Specify an empty list to receive all updates regardless of type (default). + :param url: HTTPS URL to send updates to. Use an empty string to remove webhook integration, defaults to None + :type url: :obj:`str`, optional + + :param certificate: Upload your public key certificate so that the root certificate in use can be checked, defaults to None + :type certificate: :class:`str`, optional + + :param max_connections: The maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. + Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput, + defaults to None + :type max_connections: :obj:`int`, optional + + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, + specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update + for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. + + Please note that this parameter doesn't affect updates created before the call to the setWebhook, so unwanted updates may be received + for a short period of time. Defaults to None + + :type allowed_updates: :obj:`list`, optional + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address - resolved through DNS - :param drop_pending_updates: Pass True to drop all pending updates - :param timeout: Integer. Request connection timeout - :param secret_token: Secret token to be used to verify the webhook request. - :return: API reply. + resolved through DNS, defaults to None + :type ip_address: :obj:`str`, optional + + :param drop_pending_updates: Pass True to drop all pending updates, defaults to None + :type drop_pending_updates: :obj:`bool`, optional + + :param timeout: Timeout of a request, defaults to None + :type timeout: :obj:`int`, optional + + :param secret_token: A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” in every webhook request, 1-256 characters. + Only characters A-Z, a-z, 0-9, _ and - are allowed. The header is useful to ensure that the request comes from a webhook set by you. Defaults to None + :type secret_token: :obj:`str`, optional + + :return: True on success. + :rtype: :obj:`bool` if the request was successful. """ + return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout, secret_token) + def run_webhooks(self, listen: Optional[str]="127.0.0.1", port: Optional[int]=443, @@ -311,22 +402,55 @@ def run_webhooks(self, """ This class sets webhooks and listens to a given url and port. - :param listen: IP address to listen to. Defaults to - 0.0.0.0 - :param port: A port which will be used to listen to webhooks. - :param url_path: Path to the webhook. Defaults to /token - :param certificate: Path to the certificate file. - :param certificate_key: Path to the certificate key file. - :param webhook_url: Webhook URL. - :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. - :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. - :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS - :param drop_pending_updates: Pass True to drop all pending updates - :param timeout: Integer. Request connection timeout - :param secret_token: Secret token to be used to verify the webhook request. - :param secret_token_length: - :param debug: - :return: + Requires fastapi, uvicorn, and latest version of starlette. + + :param listen: IP address to listen to, defaults to "127.0.0.1" + :type listen: Optional[str], optional + + :param port: A port which will be used to listen to webhooks., defaults to 443 + :type port: Optional[int], optional + + :param url_path: Path to the webhook. Defaults to /token, defaults to None + :type url_path: Optional[str], optional + + :param certificate: Path to the certificate file, defaults to None + :type certificate: Optional[str], optional + + :param certificate_key: Path to the certificate key file, defaults to None + :type certificate_key: Optional[str], optional + + :param webhook_url: Webhook URL to be set, defaults to None + :type webhook_url: Optional[str], optional + + :param max_connections: Maximum allowed number of simultaneous HTTPS connections + to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, + and higher values to increase your bot's throughput., defaults to None + :type max_connections: Optional[int], optional + + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] + to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). + If not specified, the previous setting will be used. defaults to None + :type allowed_updates: Optional[List], optional + + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS, defaults to None + :type ip_address: Optional[str], optional + + :param drop_pending_updates: Pass True to drop all pending updates, defaults to None + :type drop_pending_updates: Optional[bool], optional + + :param timeout: Request connection timeout, defaults to None + :type timeout: Optional[int], optional + + :param secret_token: Secret token to be used to verify the webhook request, defaults to None + :type secret_token: Optional[str], optional + + :param secret_token_length: Length of a secret token, defaults to 20 + :type secret_token_length: Optional[int], optional + + :param debug: set True if you want to set debugging on for webserver, defaults to False + :type debug: Optional[bool], optional + + :raises ImportError: If necessary libraries were not installed. """ # generate secret token if not set @@ -368,34 +492,53 @@ def run_webhooks(self, self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) self.webhook_listener.run_app() - def delete_webhook(self, drop_pending_updates=None, timeout=None): + + def delete_webhook(self, drop_pending_updates: Optional[bool]=None, timeout: Optional[int]=None) -> bool: """ Use this method to remove webhook integration if you decide to switch back to getUpdates. + Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletewebhook - :param drop_pending_updates: Pass True to drop all pending updates - :param timeout: Integer. Request connection timeout - :return: bool + :param drop_pending_updates: Pass True to drop all pending updates, defaults to None + :type drop_pending_updates: :obj: `bool`, optional + + :param timeout: Request connection timeout, defaults to None + :type timeout: :obj:`int`, optional + + :return: Returns True on success. + :rtype: :obj:`bool` """ return apihelper.delete_webhook(self.token, drop_pending_updates, timeout) - def get_webhook_info(self, timeout: Optional[int]=None): + + def get_webhook_info(self, timeout: Optional[int]=None) -> types.WebhookInfo: """ Use this method to get current webhook status. Requires no parameters. - If the bot is using getUpdates, will return an object with the url field empty. + On success, returns a WebhookInfo object. If the bot is using getUpdates, will return an object with the url field empty. Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo - :param timeout: Integer. Request connection timeout + :param timeout: Request connection timeout + :type timeout: :obj:`int`, optional + :return: On success, returns a WebhookInfo object. + :rtype: :class:`telebot.types.WebhookInfo` """ result = apihelper.get_webhook_info(self.token, timeout) return types.WebhookInfo.de_json(result) - def remove_webhook(self): + + def remove_webhook(self) -> bool: + """ + Deletes webhooks using set_webhook() function. + + :return: True on success. + :rtype: :obj:`bool` + """ return self.set_webhook() # No params resets webhook + def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, timeout: Optional[int]=20, allowed_updates: Optional[List[str]]=None, long_polling_timeout: int=20) -> List[types.Update]: @@ -405,18 +548,34 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, Telegram documentation: https://core.telegram.org/bots/api#getupdates :param allowed_updates: Array of string. List the types of updates you want your bot to receive. - :param offset: Integer. Identifier of the first update to be returned. - :param limit: Integer. Limits the number of updates to be retrieved. - :param timeout: Integer. Request connection timeout + :type allowed_updates: :obj:`list`, optional + + :param offset: Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. + By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset + higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from the end of the updates queue. + All previous updates will forgotten. + :type offset: :obj:`int`, optional + + :param limit: Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100. + :type limit: :obj:`int`, optional + + :param timeout: Request connection timeout + :type timeout: :obj:`int`, optional + :param long_polling_timeout: Timeout in seconds for long polling. - :return: array of Updates + :type long_polling_timeout: :obj:`int`, optional + + :return: An Array of Update objects is returned. + :rtype: :obj:`list` of :class:`telebot.types.Update` """ json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates, long_polling_timeout) return [types.Update.de_json(ju) for ju in json_updates] def __skip_updates(self): """ - Get and discard all pending updates before first poll of the bot + Get and discard all pending updates before first poll of the bot. + + :meta private: :return: """ @@ -426,6 +585,8 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update """ Retrieves any updates from the Telegram API. Registered listeners and applicable message handlers will be notified when a new message arrives. + + :meta private: :raises ApiException when a call has failed. """ @@ -438,11 +599,11 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update timeout=timeout, long_polling_timeout=long_polling_timeout) self.process_new_updates(updates) - def process_new_updates(self, updates): + def process_new_updates(self, updates: List[types.Update]): """ Processes new updates. Just pass list of subclasses of Update to this method. - :param updates: List of Update objects + :param updates: List of :class:`telebot.types.Update` objects. """ upd_count = len(updates) logger.debug('Received {0} new updates'.format(upd_count)) @@ -550,51 +711,96 @@ def process_new_updates(self, updates): self.process_new_chat_join_request(new_chat_join_request) def process_new_messages(self, new_messages): + """ + :meta private: + """ self._notify_next_handlers(new_messages) self._notify_reply_handlers(new_messages) self.__notify_update(new_messages) self._notify_command_handlers(self.message_handlers, new_messages, 'message') def process_new_edited_messages(self, edited_message): + """ + :meta private: + """ self._notify_command_handlers(self.edited_message_handlers, edited_message, 'edited_message') def process_new_channel_posts(self, channel_post): + """ + :meta private: + """ self._notify_command_handlers(self.channel_post_handlers, channel_post, 'channel_post') def process_new_edited_channel_posts(self, edited_channel_post): + """ + :meta private: + """ self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') def process_new_inline_query(self, new_inline_querys): + """ + :meta private: + """ self._notify_command_handlers(self.inline_handlers, new_inline_querys, 'inline_query') def process_new_chosen_inline_query(self, new_chosen_inline_querys): + """ + :meta private: + """ self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') def process_new_callback_query(self, new_callback_querys): + """ + :meta private: + """ self._notify_command_handlers(self.callback_query_handlers, new_callback_querys, 'callback_query') def process_new_shipping_query(self, new_shipping_querys): + """ + :meta private: + """ self._notify_command_handlers(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') def process_new_pre_checkout_query(self, pre_checkout_querys): + """ + :meta private: + """ self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') def process_new_poll(self, polls): + """ + :meta private: + """ self._notify_command_handlers(self.poll_handlers, polls, 'poll') def process_new_poll_answer(self, poll_answers): + """ + :meta private: + """ self._notify_command_handlers(self.poll_answer_handlers, poll_answers, 'poll_answer') def process_new_my_chat_member(self, my_chat_members): + """ + :meta private: + """ self._notify_command_handlers(self.my_chat_member_handlers, my_chat_members, 'my_chat_member') def process_new_chat_member(self, chat_members): + """ + :meta private: + """ self._notify_command_handlers(self.chat_member_handlers, chat_members, 'chat_member') def process_new_chat_join_request(self, chat_join_request): + """ + :meta private: + """ self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') def process_middlewares(self, update): + """ + :meta private: + """ if self.typed_middleware_handlers: for update_type, middlewares in self.typed_middleware_handlers.items(): if getattr(update, update_type) is not None: @@ -613,30 +819,45 @@ def process_middlewares(self, update): e.args = e.args + (f'Default middleware handler "{default_middleware_handler.__qualname__}"',) raise + def __notify_update(self, new_messages): + """ + :meta private: + """ if len(self.update_listener) == 0: return for listener in self.update_listener: self._exec_task(listener, new_messages) - def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, - logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): + + def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, long_polling_timeout: Optional[int]=20, + logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. - :param timeout: Request connection timeout + :param timeout: Request connection timeout. + :type timeout: :obj:`int` + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :type long_polling_timeout: :obj:`int` + :param skip_pending: skip old updates + :type skip_pending: :obj:`bool` + :param logger_level: Custom (different from logger itself) logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging + :type logger_level: :obj:`int`. + :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - 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. + :type allowed_updates: :obj:`list` of :obj:`str` + + :return: """ if skip_pending: self.__skip_updates() @@ -657,8 +878,10 @@ def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polli if logger_level and logger_level >= logging.INFO: logger.error("Break infinity polling") - def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, long_polling_timeout: int=20, - logger_level=logging.ERROR, allowed_updates: Optional[List[str]]=None, + + def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=False, interval: Optional[int]=0, + timeout: Optional[int]=20, long_polling_timeout: Optional[int]=20, + logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, none_stop: Optional[bool]=None): """ This function creates a new Thread that calls an internal __retrieve_updates function. @@ -668,13 +891,28 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim Always get updates. + .. deprecated:: 4.1.1 + Use :meth:`infinity_polling` instead. + :param interval: Delay between two update retrivals + :type interval: :obj:`int` + :param non_stop: Do not stop polling when an ApiException occurs. + :type non_stop: :obj:`bool` + :param timeout: Request connection timeout + :type timeout: :obj:`int` + :param skip_pending: skip old updates + :type skip_pending: :obj:`bool` + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) + :type long_polling_timeout: :obj:`int` + :param logger_level: Custom (different from logger itself) logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging + :type logger_level: :obj:`int` + :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -683,7 +921,11 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim 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 + :type allowed_updates: :obj:`list`] of :obj:`str` + + :param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility. + :type none_stop: :obj:`bool` + :return: """ if none_stop is not None: @@ -700,6 +942,7 @@ def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, tim self.__non_threaded_polling(non_stop=non_stop, interval=interval, timeout=timeout, long_polling_timeout=long_polling_timeout, logger_level=logger_level, allowed_updates=allowed_updates) + def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, long_polling_timeout = None, logger_level=logging.ERROR, allowed_updates=None): if not(logger_level) or (logger_level < logging.INFO): @@ -780,6 +1023,7 @@ def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, lon #if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error logger.info('Stopped polling.' + warning) + def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None, logger_level=logging.ERROR, allowed_updates=None): if not(logger_level) or (logger_level < logging.INFO): @@ -834,6 +1078,7 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ #if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error logger.info('Stopped polling.' + warning) + def _exec_task(self, task, *args, **kwargs): if kwargs and kwargs.get('task_type') == 'handler': pass_bot = kwargs.get('pass_bot') @@ -855,19 +1100,36 @@ def _exec_task(self, task, *args, **kwargs): if not handled: raise e + def stop_polling(self): + """ + Stops polling. + """ self.__stop_polling.set() + def stop_bot(self): + """ + Stops bot by stopping polling and closing the worker pool. + """ self.stop_polling() if self.threaded and self.worker_pool: self.worker_pool.close() - def set_update_listener(self, listener): + + def set_update_listener(self, listener: Callable): + """ + Sets a listener function to be called when a new update is received. + + :param listener: Listener function. + :type listener: Callable + """ self.update_listener.append(listener) + def get_me(self) -> types.User: """ + A simple method for testing your bot's authentication token. Requires no parameters. Returns basic information about the bot in form of a User object. Telegram documentation: https://core.telegram.org/bots/api#getme @@ -875,7 +1137,8 @@ def get_me(self) -> types.User: result = apihelper.get_me(self.token) return types.User.de_json(result) - def get_file(self, file_id: str) -> types.File: + + def get_file(self, file_id: Optional[str]) -> types.File: """ Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. @@ -886,20 +1149,30 @@ def get_file(self, file_id: str) -> types.File: Telegram documentation: https://core.telegram.org/bots/api#getfile :param file_id: File identifier + :type file_id: :obj:`str` + + :return: :class:`telebot.types.File` """ return types.File.de_json(apihelper.get_file(self.token, file_id)) - def get_file_url(self, file_id: str) -> str: + + def get_file_url(self, file_id: Optional[str]) -> str: """ Get a valid URL for downloading a file. :param file_id: File identifier to get download URL for. + :type file_id: :obj:`str` + + :return: URL for downloading the file. + :rtype: :obj:`str` """ return apihelper.get_file_url(self.token, file_id) + def download_file(self, file_path: str) -> bytes: return apihelper.download_file(self.token, file_path) + def log_out(self) -> bool: """ Use this method to log out from the cloud Bot API server before launching the bot locally. @@ -910,9 +1183,13 @@ def log_out(self) -> bool: Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#logout + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.log_out(self.token) + def close(self) -> bool: """ Use this method to close the bot instance before moving it from one local server to another. @@ -922,24 +1199,37 @@ def close(self) -> bool: Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#close + + :return: :obj:`bool` """ return apihelper.close(self.token) - def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, + + def get_user_profile_photos(self, user_id: Optional[int], offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ - Retrieves the user profile photos of the person with 'user_id' + Use this method to get a list of profile pictures for a user. + Returns a UserProfilePhotos object. Telegram documentation: https://core.telegram.org/bots/api#getuserprofilephotos - :param user_id: Integer - Unique identifier of the target user - :param offset: - :param limit: - :return: API reply. + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param offset: Sequential number of the first photo to be returned. By default, all photos are returned. + :type offset: :obj:`int` + + :param limit: Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100. + :type limit: :obj:`int` + + :return: `UserProfilePhotos `_ + :rtype: :class:`telebot.types.UserProfilePhotos` + """ result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit) return types.UserProfilePhotos.de_json(result) + def get_chat(self, chat_id: Union[int, str]) -> types.Chat: """ Use this method to get up to date information about the chat (current name of the user for one-on-one @@ -947,24 +1237,31 @@ def get_chat(self, chat_id: Union[int, str]) -> types.Chat: Telegram documentation: https://core.telegram.org/bots/api#getchat - :param chat_id: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: :class:`telebot.types.Chat` + :rtype: :class:`telebot.types.Chat` """ result = apihelper.get_chat(self.token, chat_id) return types.Chat.de_json(result) + def leave_chat(self, chat_id: Union[int, str]) -> bool: """ Use this method for your bot to leave a group, supergroup or channel. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#leavechat - :param chat_id: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: :obj:`bool` """ result = apihelper.leave_chat(self.token, chat_id) return result + def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMember]: """ Use this method to get a list of administrators in a chat. @@ -975,43 +1272,65 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) - :return: API reply. + :return: List made of ChatMember objects. + :rtype: :obj:`list` of :class:`telebot.types.ChatMember` """ result = apihelper.get_chat_administrators(self.token, chat_id) return [types.ChatMember.de_json(r) for r in result] + @util.deprecated(deprecation_text="Use get_chat_member_count instead") def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ - This function is deprecated. Use `get_chat_member_count` instead + This function is deprecated. Use `get_chat_member_count` instead. + + .. deprecated:: 4.0.0 + This function is deprecated. Use `get_chat_member_count` instead. + + Use this method to get the number of members in a chat. + + Telegram documentation: https://core.telegram.org/bots/api#getchatmembercount + + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: Number of members in the chat. + :rtype: :obj:`int` """ result = apihelper.get_chat_member_count(self.token, chat_id) return result def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ - Use this method to get the number of members in a chat. Returns Int on success. - + Use this method to get the number of members in a chat. + Telegram documentation: https://core.telegram.org/bots/api#getchatmembercount - :param chat_id: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: Number of members in the chat. + :rtype: :obj:`int` """ result = apihelper.get_chat_member_count(self.token, chat_id) return result def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: """ - Use this method to set a new group sticker set for a supergroup. The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. - Use the field can_set_sticker_set optionally returned in getChat requests to check - if the bot can use this method. Returns True on success. + Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat + for this to work and must have the appropriate administrator rights. Use the field can_set_sticker_set optionally returned + in getChat requests to check if the bot can use this method. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param sticker_set_name: Name of the sticker set to be set as the group sticker set - :return: API reply. + :type sticker_set_name: :obj:`str` + + :return: StickerSet object + :rtype: :class:`telebot.types.StickerSet` """ result = apihelper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) return result @@ -1025,7 +1344,10 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) - :return: API reply. + :type chat_id: :obj:`int` or :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` """ result = apihelper.delete_chat_sticker_set(self.token, chat_id) return result @@ -1036,9 +1358,14 @@ def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatM Telegram documentation: https://core.telegram.org/bots/api#getchatmember - :param chat_id: - :param user_id: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :return: Returns ChatMember object on success. + :rtype: :class:`telebot.types.ChatMember` """ result = apihelper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) @@ -1064,17 +1391,40 @@ def send_message( Telegram documentation: https://core.telegram.org/bots/api#sendmessage :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param text: Text of the message to be sent + :type text: :obj:`str` + :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. + :type parse_mode: :obj:`str` + :param entities: List of special entities that appear in message text, which can be specified instead of parse_mode + :type entities: Array of :class:`telebot.types.MessageEntity` + :param disable_web_page_preview: Disables link previews for links in this message + :type disable_web_page_preview: :obj:`bool` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + :param protect_content: If True, the message content will be hidden for all users except for the target user + :type protect_content: :obj:`bool` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :param allow_sending_without_reply: 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 timeout: - :return: API reply. + :type reply_markup: :obj:`telebot.REPLY_MARKUP_TYPES` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1086,7 +1436,7 @@ def send_message( def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], - message_id: int, disable_notification: Optional[bool]=None, + message_id: Optional[int], disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, timeout: Optional[int]=None) -> types.Message: """ @@ -1094,17 +1444,31 @@ def forward_message( Telegram documentation: https://core.telegram.org/bots/api#forwardmessage - :param disable_notification: - :param chat_id: which chat to forward - :param from_chat_id: which chat message from - :param message_id: message id + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + + :param message_id: Message identifier in the chat specified in from_chat_id + :type message_id: :obj:`int` + :param protect_content: Protects the contents of the forwarded message from forwarding and saving - :param timeout: - :return: API reply. + :type protect_content: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) + def copy_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -1123,19 +1487,44 @@ def copy_message( Telegram documentation: https://core.telegram.org/bots/api#copymessage - :param chat_id: which chat to forward - :param from_chat_id: which chat message from - :param message_id: message id - :param caption: - :param parse_mode: - :param caption_entities: - :param disable_notification: - :param protect_content: - :param reply_to_message_id: - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + :param message_id: Message identifier in the chat specified in from_chat_id + :type message_id: :obj:`int` + + :param caption: New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the new caption. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the new caption, which can be specified instead of parse_mode + :type caption_entities: Array of :class:`telebot.types.MessageEntity` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: 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. + :type reply_markup: :obj:`telebot.REPLY_MARKUP_TYPES` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, @@ -1145,14 +1534,29 @@ def copy_message( def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: """ - Use this method to delete message. Returns True on success. + Use this method to delete a message, including service messages, with the following limitations: + - A message can only be deleted if it was sent less than 48 hours ago. + - A dice message in a private chat can only be deleted if it was sent more than 24 hours ago. + - Bots can delete outgoing messages in private chats, groups, and supergroups. + - Bots can delete incoming messages in private chats. + - Bots granted can_post_messages permissions can delete outgoing messages in channels. + - If the bot is an administrator of a group, it can delete any message there. + - If the bot has can_delete_messages permission in a supergroup or a channel, it can delete any message there. + Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemessage - :param chat_id: in which chat to delete - :param message_id: which message to delete - :param timeout: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of the message to delete + :type message_id: :obj:`int` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` """ return apihelper.delete_message(self.token, chat_id, message_id, timeout) @@ -1165,19 +1569,38 @@ def send_dice( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send dices. + Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#senddice - :param chat_id: - :param emoji: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param protect_content: - :return: Message + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, or “🎰”. + Dice can have values 1-6 for “🎲”, “🎯” and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults to “🎲” + :type emoji: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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:`telebot.REPLY_MARKUP_TYPES` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( apihelper.send_dice( @@ -1185,6 +1608,7 @@ def send_dice( reply_markup, timeout, allow_sending_without_reply, protect_content) ) + def send_photo( self, chat_id: Union[int, str], photo: Union[Any, str], caption: Optional[str]=None, parse_mode: Optional[str]=None, @@ -1200,18 +1624,44 @@ def send_photo( Telegram documentation: https://core.telegram.org/bots/api#sendphoto - :param chat_id: - :param photo: - :param caption: - :param parse_mode: - :param caption_entities: - :param disable_notification: - :param protect_content: - :param reply_to_message_id: - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :return: Message + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param photo: Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), + pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data. + The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20. + :type photo: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Photo caption (may also be used when resending photos by file_id), 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the photo caption. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: 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. + :type reply_markup: :obj:`telebot.REPLY_MARKUP_TYPES` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1237,26 +1687,65 @@ def send_audio( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. - Your audio must be in the .mp3 format. + Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, + this limit may be changed in the future. + + For sending voice messages, use the send_voice method instead. Telegram documentation: https://core.telegram.org/bots/api#sendaudio - :param chat_id: Unique identifier for the message recipient - :param audio: Audio file to send. - :param caption: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param audio: Audio file to send. Pass a file_id as String to send an audio file that exists on the Telegram servers (recommended), + pass an HTTP URL as a String for Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data. + Audio must be in the .MP3 or .M4A format. + :type audio: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Audio caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + :param duration: Duration of the audio in seconds + :type duration: :obj:`int` + :param performer: Performer + :type performer: :obj:`str` + :param title: Track name + :type title: :obj:`str` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :param reply_markup: - :param parse_mode: - :param disable_notification: - :param timeout: - :param thumb: - :param caption_entities: - :param allow_sending_without_reply: - :param protect_content: - :return: Message + :type reply_markup: :obj:`telebot.REPLY_MARKUP_TYPES` + + :param parse_mode: Mode for parsing entities in the audio caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param thumb: 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 + :type thumb: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1279,24 +1768,51 @@ def send_voice( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send audio files, if you want Telegram clients to display the file - as a playable voice 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. Telegram documentation: https://core.telegram.org/bots/api#sendvoice - :param chat_id: Unique identifier for the message recipient. - :param voice: - :param caption: - :param duration: Duration of sent audio in seconds - :param reply_to_message_id: - :param reply_markup: - :param parse_mode: - :param disable_notification: - :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :param protect_content: - :return: Message + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param voice: Audio 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. + :type voice: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Voice message caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param duration: Duration of the voice message in seconds + :type duration: :obj:`int` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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:`telebot.REPLY_MARKUP_TYPES` + + :param parse_mode: Mode for parsing entities in the voice message caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1328,21 +1844,54 @@ def send_document( Telegram documentation: https://core.telegram.org/bots/api#senddocument :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :param document: (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 + :type chat_id: :obj:`int` or :obj:`str` + + :param document: (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 + :type document: :obj:`str` or :class:`telebot.types.InputFile` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :param caption: Document caption (may also be used when resending documents by file_id), 0-1024 characters after entities parsing - :param reply_markup: + :type caption: :obj:`str` + + :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:`telebot.REPLY_MARKUP_TYPES` + :param parse_mode: Mode for parsing entities in the document caption + :type parse_mode: :obj:`str` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param timeout: + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :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: + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param allow_sending_without_reply: 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 visible_file_name: allows to define file name that will be visible in the Telegram instead of original file name + :type visible_file_name: :obj:`str` + :param disable_content_type_detection: Disables automatic server-side content type detection for files uploaded using multipart/form-data + :type disable_content_type_detection: :obj:`bool` + :param data: function typo miss compatibility: do not use it - :param protect_content: - :return: API reply. + :type data: :obj:`str` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode if data and not(document): @@ -1358,6 +1907,7 @@ def send_document( disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content)) + # TODO: Rewrite this method like in API. def send_sticker( self, chat_id: Union[int, str], @@ -1370,21 +1920,42 @@ def send_sticker( protect_content:Optional[bool]=None, data: Union[Any, str]=None) -> types.Message: """ - Use this method to send .webp stickers. + Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. + On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendsticker - :param chat_id: - :param sticker: - :param data: - :param reply_to_message_id: - :param reply_markup: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param sticker: Sticker 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 .webp file from the Internet, or upload a new one using multipart/form-data. + :type sticker: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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:`telebot.REPLY_MARKUP_TYPES` + :param disable_notification: to disable the notification - :param timeout: timeout - :param allow_sending_without_reply: - :param protect_content: + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + :param data: function typo miss compatibility: do not use it - :return: API reply. + :type data: :obj:`str` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ if data and not(sticker): # function typo miss compatibility @@ -1420,22 +1991,59 @@ def send_video( Telegram documentation: https://core.telegram.org/bots/api#sendvideo :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param video: Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video file using multipart/form-data. + :type video: :obj:`str` or :class:`telebot.types.InputFile` + :param duration: Duration of sent video in seconds + :type duration: :obj:`int` + :param width: Video width + :type width: :obj:`int` + :param height: Video height + :type height: :obj:`int` + :param thumb: 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 . + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing + :type caption: :obj:`str` + :param parse_mode: Mode for parsing entities in the video caption - :param caption_entities: + :type parse_mode: :obj:`str` + + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param supports_streaming: Pass True, if the uploaded video is suitable for streaming + :type supports_streaming: :obj:`bool` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param protect_content: + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + :param reply_to_message_id: If the message is a reply, ID of the original message - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: 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. + :type reply_markup: :obj:`telebot.REPLY_MARKUP_TYPES` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :param data: function typo miss compatibility: do not use it + :type data: :obj:`str` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode if data and not(video): @@ -1465,26 +2073,45 @@ def send_animation( timeout: Optional[int]=None, ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. Telegram documentation: https://core.telegram.org/bots/api#sendanimation - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an - animation that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param width: Integer : Video width - :param height: Integer : Video height - :param thumb: InputFile or String : Thumbnail of the file sent - :param caption: String : Animation caption (may also be used when resending animation by file_id). - :param parse_mode: - :param protect_content: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param animation: Animation to send. Pass a file_id as String to send an animation that exists on the Telegram servers (recommended), + pass an HTTP URL as a String for Telegram to get an animation from the Internet, or upload a new animation using multipart/form-data. + :type animation: :obj:`str` or :class:`telebot.types.InputFile` + + :param duration: Duration of sent animation in seconds + :type duration: :obj:`int` + + :param width: Animation width + :type width: :obj:`int` + + :param height: Animation height + :type height: :obj:`int` + + :param thumb: 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 . + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Animation caption (may also be used when resending animation by file_id), 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the animation caption + :param protect_content: Protects the contents of the sent message from forwarding and saving + :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :param timeout: Timeout in seconds for the request. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode + :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode From c2cfe24426b9423c0ee4ab040ad95b8900f3a287 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 19 Jul 2022 23:49:05 +0500 Subject: [PATCH 1065/1808] Typehints & Docstrings completed for sync version up to 100%(visual) --- telebot/__init__.py | 1972 +++++++++++++++++++++++++++-------- telebot/handler_backends.py | 1 + 2 files changed, 1529 insertions(+), 444 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 17357fe98..a43aaee93 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -405,50 +405,50 @@ def run_webhooks(self, Requires fastapi, uvicorn, and latest version of starlette. :param listen: IP address to listen to, defaults to "127.0.0.1" - :type listen: Optional[str], optional + :type listen: :obj:`str`, optional :param port: A port which will be used to listen to webhooks., defaults to 443 - :type port: Optional[int], optional + :type port: :obj:`int`, optional :param url_path: Path to the webhook. Defaults to /token, defaults to None - :type url_path: Optional[str], optional + :type url_path: :obj:`str`, optional :param certificate: Path to the certificate file, defaults to None - :type certificate: Optional[str], optional + :type certificate: :obj:`str`, optional :param certificate_key: Path to the certificate key file, defaults to None - :type certificate_key: Optional[str], optional + :type certificate_key: :obj:`str`, optional :param webhook_url: Webhook URL to be set, defaults to None - :type webhook_url: Optional[str], optional + :type webhook_url: :obj:`str`, optional :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput., defaults to None - :type max_connections: Optional[int], optional + :type max_connections: :obj:`int`, optional :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. defaults to None - :type allowed_updates: Optional[List], optional + :type allowed_updates: :obj:`list`, optional :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS, defaults to None - :type ip_address: Optional[str], optional + :type ip_address: :obj:`str`, optional :param drop_pending_updates: Pass True to drop all pending updates, defaults to None - :type drop_pending_updates: Optional[bool], optional + :type drop_pending_updates: :obj:`bool`, optional :param timeout: Request connection timeout, defaults to None - :type timeout: Optional[int], optional + :type timeout: :obj:`int`, optional :param secret_token: Secret token to be used to verify the webhook request, defaults to None - :type secret_token: Optional[str], optional + :type secret_token: :obj:`str`, optional :param secret_token_length: Length of a secret token, defaults to 20 - :type secret_token_length: Optional[int], optional + :type secret_token_length: :obj:`int`, optional :param debug: set True if you want to set debugging on for webserver, defaults to False - :type debug: Optional[bool], optional + :type debug: :obj:`bool`, optional :raises ImportError: If necessary libraries were not installed. """ @@ -1205,7 +1205,7 @@ def close(self) -> bool: return apihelper.close(self.token) - def get_user_profile_photos(self, user_id: Optional[int], offset: Optional[int]=None, + def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ Use this method to get a list of profile pictures for a user. @@ -1396,7 +1396,7 @@ def send_message( :param text: Text of the message to be sent :type text: :obj:`str` - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. + :param parse_mode: Mode for parsing entities in the message text. :type parse_mode: :obj:`str` :param entities: List of special entities that appear in message text, which can be specified instead of parse_mode @@ -1418,7 +1418,8 @@ def send_message( :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. - :type reply_markup: :obj:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -1436,7 +1437,7 @@ def send_message( def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], - message_id: Optional[int], disable_notification: Optional[bool]=None, + message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, timeout: Optional[int]=None) -> types.Message: """ @@ -1518,7 +1519,8 @@ def copy_message( :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:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -1588,7 +1590,8 @@ def send_dice( :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:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -1655,7 +1658,8 @@ def send_photo( :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:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -1718,7 +1722,8 @@ def send_audio( :type reply_to_message_id: :obj:`int` :param reply_markup: - :type reply_markup: :obj:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param parse_mode: Mode for parsing entities in the audio caption. See formatting options for more details. :type parse_mode: :obj:`str` @@ -1792,7 +1797,8 @@ def send_voice( :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:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param parse_mode: Mode for parsing entities in the voice message caption. See formatting options for more details. :type parse_mode: :obj:`str` @@ -1858,7 +1864,8 @@ def send_document( :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:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param parse_mode: Mode for parsing entities in the document caption :type parse_mode: :obj:`str` @@ -1937,7 +1944,8 @@ def send_sticker( :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:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param disable_notification: to disable the notification :type disable_notification: :obj:`bool` @@ -2034,7 +2042,8 @@ def send_video( :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:`telebot.REPLY_MARKUP_TYPES` + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -2103,13 +2112,31 @@ def send_animation( :type caption: :obj:`str` :param parse_mode: Mode for parsing entities in the animation caption + :type parse_mode: :obj:`str` + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param allow_sending_without_reply: 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` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2134,27 +2161,59 @@ def send_video_note( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ + As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. + Use this method to send video messages. On success, the sent Message is returned. + Telegram documentation: https://core.telegram.org/bots/api#sendvideonote - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend - a video that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent - :param allow_sending_without_reply: - :param protect_content: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param data: Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) + or upload a new video using multipart/form-data. Sending video notes by a URL is currently unsupported + :type data: :obj:`str` or :class:`telebot.types.InputFile` + + :param duration: Duration of sent video in seconds + :type duration: :obj:`int` + + :param length: Video width and height, i.e. diameter of the video message + :type length: :obj:`int` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param thumb: 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 . + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( apihelper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, disable_notification, timeout, thumb, allow_sending_without_reply, protect_content)) + def send_media_group( self, chat_id: Union[int, str], media: List[Union[ @@ -2166,18 +2225,34 @@ def send_media_group( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: """ - Send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files + can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup - :param chat_id: - :param media: - :param disable_notification: - :param protect_content: - :param reply_to_message_id: - :param timeout: - :param allow_sending_without_reply: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param media: A JSON-serialized array describing messages to be sent, must include 2-10 items + :type media: :obj:`list` of :obj:`types.InputMedia` + + :param disable_notification: Sends the messages silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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` + + :return: On success, an array of Messages that were sent is returned. + :rtype: List[types.Message] """ result = apihelper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, @@ -2199,24 +2274,53 @@ def send_location( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send point on the map. + Use this method to send point on the map. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendlocation - :param chat_id: - :param latitude: - :param longitude: - :param live_period: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param horizontal_accuracy: - :param heading: - :param proximity_alert_radius: - :param allow_sending_without_reply: - :param protect_content: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param latitude: Latitude of the location + :type latitude: :obj:`float` + + :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. + :type live_period: :obj:`int` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param horizontal_accuracy: The radius of uncertainty for the location, measured in meters; 0-1500 + :type horizontal_accuracy: :obj:`float` + + :param heading: 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: 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 allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( apihelper.send_location( @@ -2234,23 +2338,47 @@ 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) -> types.Message or bool: """ - Use this method to edit live location. + 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 + is returned, otherwise True is returned. Telegram documentation: https://core.telegram.org/bots/api#editmessagelivelocation - :param latitude: - :param longitude: - :param chat_id: - :param message_id: - :param reply_markup: - :param timeout: - :param inline_message_id: - :param horizontal_accuracy: - :param heading: - :param proximity_alert_radius: - :return: + :param latitude: Latitude of new location + :type latitude: :obj:`float` + + :param longitude: Longitude of new location + :type longitude: :obj:`float` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the message to edit + :type message_id: :obj:`int` + + :param reply_markup: A JSON-serialized object for a new inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param horizontal_accuracy: The radius of uncertainty for the location, measured in meters; 0-1500 + :type horizontal_accuracy: :obj:`float` + + :param heading: 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: 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` + + :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 """ return types.Message.de_json( apihelper.edit_message_live_location( @@ -2263,19 +2391,31 @@ def stop_message_live_location( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> types.Message: + timeout: Optional[int]=None) -> types.Message or bool: """ - Use this method to stop updating a live location message sent by the bot - or via the bot (for inline bots) before live_period expires + Use this method to stop updating a live location message before live_period expires. + On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. Telegram documentation: https://core.telegram.org/bots/api#stopmessagelivelocation - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :param timeout: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the message with live location to stop + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message with live location to stop + :type inline_message_id: :obj:`str` + + :param reply_markup: A JSON-serialized object for a new inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. + :rtype: :class:`telebot.types.Message` or bool """ return types.Message.de_json( apihelper.stop_message_live_location( @@ -2284,7 +2424,7 @@ def stop_message_live_location( # TODO: Rewrite this method like in API. def send_venue( self, chat_id: Union[int, str], - latitude: float, longitude: float, + latitude: Optional[float], longitude: Optional[float], title: str, address: str, foursquare_id: Optional[str]=None, foursquare_type: Optional[str]=None, @@ -2297,27 +2437,61 @@ def send_venue( google_place_type: Optional[str]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send information about a venue. + Use this method to send information about a venue. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendvenue - :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel - :param latitude: Float : Latitude of the venue - :param longitude: Float : Longitude of the venue - :param title: String : Name of the venue - :param address: String : Address of the venue - :param foursquare_id: String : Foursquare identifier of the venue + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` or :obj:`str` + + :param latitude: Latitude of the venue + :type latitude: :obj:`float` + + :param longitude: Longitude of the venue + :type longitude: :obj:`float` + + :param title: Name of the venue + :type title: :obj:`str` + + :param address: Address of the venue + :type address: :obj:`str` + + :param foursquare_id: Foursquare identifier of the venue + :type foursquare_id: :obj:`str` + :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param google_place_id: - :param google_place_type: - :param protect_content: - :return: + :type foursquare_type: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified + replied-to messages is not found. + :type allow_sending_without_reply: :obj:`bool` + + :param google_place_id: Google Places identifier of the venue + :type google_place_id: :obj:`str` + + :param google_place_type: Google Places type of the venue. + :type google_place_type: :obj:`str` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( apihelper.send_venue( @@ -2325,6 +2499,7 @@ def send_venue( disable_notification, reply_to_message_id, reply_markup, timeout, allow_sending_without_reply, google_place_id, google_place_type, protect_content)) + # TODO: Rewrite this method like in API. def send_contact( self, chat_id: Union[int, str], phone_number: str, @@ -2337,22 +2512,48 @@ def send_contact( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send phone contacts. + Use this method to send phone contacts. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendcontact - :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel - :param phone_number: String : Contact's phone number - :param first_name: String : Contact's first name - :param last_name: String : Contact's last name - :param vcard: String : Additional data about the contact in the form of a vCard, 0-2048 bytes - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param protect_content: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` or :obj:`str` + + :param phone_number: Contact's phone number + :type phone_number: :obj:`str` + + :param first_name: Contact's first name + :type first_name: :obj:`str` + + :param last_name: Contact's last name + :type last_name: :obj:`str` + + :param vcard: Additional data about the contact in the form of a vCard, 0-2048 bytes + :type vcard: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified + replied-to messages is not found. + :type allow_sending_without_reply: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( apihelper.send_contact( @@ -2364,17 +2565,28 @@ def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. - The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear - its typing status). + The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). + Returns True on success. + + Example: The ImageBot needs some time to process a request and upload the image. Instead of sending a text message along the lines of + “Retrieving image, please wait…”, the bot may use sendChatAction with action = upload_photo. The user will see a “sending photo” status for the bot. Telegram documentation: https://core.telegram.org/bots/api#sendchataction - :param chat_id: - :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', - 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', - 'upload_video_note'. - :param timeout: - :return: API reply. :type: boolean + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` or :obj:`str` + + :param action: Type of action to broadcast. Choose one, depending on what the user is about + to receive: typing for text messages, upload_photo for photos, record_video or upload_video + for videos, record_voice or upload_voice for voice notes, upload_document for general files, + choose_sticker for stickers, find_location for location data, record_video_note or upload_video_note for video notes. + :type action: :obj:`str` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` """ return apihelper.send_chat_action(self.token, chat_id, action, timeout) @@ -2384,13 +2596,13 @@ def kick_chat_member( until_date:Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ - This function is deprecated. Use `ban_chat_member` instead + This function is deprecated. Use `ban_chat_member` instead. """ return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) def ban_chat_member( self, chat_id: Union[int, str], user_id: int, - until_date:Optional[Union[int, datetime]]=None, + until_date: Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ Use this method to ban a user in a group, a supergroup or a channel. @@ -2400,14 +2612,24 @@ def ban_chat_member( Telegram documentation: https://core.telegram.org/bots/api#banchatmember - :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup - :param user_id: Int : Unique identifier of the target user + :param chat_id: Unique identifier for the target group or username of the target supergroup + or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever + :type until_date: :obj:`int` or :obj:`datetime` + :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. - If False, the user will be able to see messages in the group that were sent before the user was removed. - Always True for supergroups and channels. - :return: boolean + If False, the user will be able to see messages in the group that were sent before the user was removed. + Always True for supergroups and channels. + :type revoke_messages: :obj:`bool` + + :return: Returns True on success. + :rtype: :obj:`bool` """ return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) @@ -2425,9 +2647,16 @@ def unban_chat_member( :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param only_if_banned: Do nothing if the user is not banned + :type only_if_banned: :obj:`bool` + :return: True on success + :rtype: :obj:`bool` """ return apihelper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) @@ -2449,25 +2678,48 @@ def restrict_chat_member( Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember - :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) - :param user_id: Int : Unique identifier of the target user + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param until_date: Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever + :type until_date: :obj:`int` or :obj:`datetime` + :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues + :type can_send_messages: :obj:`bool` + :param can_send_media_messages: Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages + :type can_send_media_messages: :obj:`bool` + :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages + :type can_send_polls: :obj:`bool` + :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages + :type can_send_other_messages: :obj:`bool` + :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, implies can_send_media_messages + :type can_add_web_page_previews: :obj:`bool` + :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups + :type can_change_info: :obj:`bool` + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, implies can_invite_users + :type can_invite_users: :obj:`bool` + :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups + :type can_pin_messages: :obj:`bool` + :return: True on success + :rtype: :obj:`bool` """ return apihelper.restrict_chat_member( self.token, chat_id, user_id, until_date, @@ -2477,7 +2729,7 @@ def restrict_chat_member( can_invite_users, can_pin_messages) def promote_chat_member( - self, chat_id: Union[int, str], user_id: int, + self, chat_id: Union[int, str], user_id: int, can_change_info: Optional[bool]=None, can_post_messages: Optional[bool]=None, can_edit_messages: Optional[bool]=None, @@ -2499,27 +2751,55 @@ def promote_chat_member( :param chat_id: Unique identifier for the target chat or username of the target channel ( in the format @channelusername) - :param user_id: Int : Unique identifier of the target user - :param can_change_info: Bool: Pass True, if the administrator can change chat title, photo and other settings - :param can_post_messages: Bool : Pass True, if the administrator can create channel posts, channels only - :param can_edit_messages: Bool : Pass True, if the administrator can edit messages of other users, channels only - :param can_delete_messages: Bool : Pass True, if the administrator can delete messages of other users - :param can_invite_users: Bool : Pass True, if the administrator can invite new users to the chat - :param can_restrict_members: Bool: Pass True, if the administrator can restrict, ban or unban chat members - :param can_pin_messages: Bool: Pass True, if the administrator can pin messages, supergroups only - :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param can_change_info: Pass True, if the administrator can change chat title, photo and other settings + :type can_change_info: :obj:`bool` + + :param can_post_messages: Pass True, if the administrator can create channel posts, channels only + :type can_post_messages: :obj:`bool` + + :param can_edit_messages: Pass True, if the administrator can edit messages of other users, channels only + :type can_edit_messages: :obj:`bool` + + :param can_delete_messages: Pass True, if the administrator can delete messages of other users + :type can_delete_messages: :obj:`bool` + + :param can_invite_users: Pass True, if the administrator can invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_restrict_members: Pass True, if the administrator can restrict, ban or unban chat members + :type can_restrict_members: :obj:`bool` + + :param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only + :type can_pin_messages: :obj:`bool` + + :param can_promote_members:Pass True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) - :param is_anonymous: Bool: Pass True, if the administrator's presence in the chat is hidden - :param can_manage_chat: Bool: Pass True, if the administrator can access the chat event log, chat statistics, + :type can_promote_members: :obj:`bool` + + :param is_anonymous: Pass True, if the administrator's presence in the chat is hidden + :type is_anonymous: :obj:`bool` + + :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege - :param can_manage_video_chats: Bool: Pass True, if the administrator can manage voice chats + :type can_manage_chat: :obj:`bool` + + :param can_manage_video_chats: Pass True, if the administrator can manage voice chats For now, bots can use this privilege only for passing to other administrators. + :type can_manage_video_chats: :obj:`bool` + :param can_manage_voice_chats: Deprecated, use can_manage_video_chats. + :type can_manage_voice_chats: :obj:`bool` :return: True on success. + :rtype: :obj:`bool` """ if can_manage_voice_chats is not None: logger.warning("promote_chat_member: can_manage_voice_chats parameter is deprecated. Use can_manage_video_chats instead.") @@ -2535,17 +2815,24 @@ def promote_chat_member( def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: """ - Use this method to set a custom title for an administrator - in a supergroup promoted by the bot. + Use this method to set a custom title for an administrator in a supergroup promoted by the bot. + Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setchatadministratorcustomtitle :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param custom_title: New custom title for the administrator; 0-16 characters, emoji are not allowed + :type custom_title: :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) @@ -2561,8 +2848,13 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i Telegram documentation: https://core.telegram.org/bots/api#banchatsenderchat :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param sender_chat_id: Unique identifier of the target sender chat + :type sender_chat_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id) @@ -2575,10 +2867,14 @@ def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union Telegram documentation: https://core.telegram.org/bots/api#unbanchatsenderchat - :params: :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :param sender_chat_id: Unique identifier of the target sender chat + :type chat_id: :obj:`int` or :obj:`str` + + :param sender_chat_id: Unique identifier of the target sender chat. + :type sender_chat_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) @@ -2593,8 +2889,13 @@ def set_chat_permissions( :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param permissions: New default chat permissions + :type permissions: :class:`telebot.types..ChatPermissions` + :return: True on success + :rtype: :obj:`bool` """ return apihelper.set_chat_permissions(self.token, chat_id, permissions) @@ -2605,18 +2906,31 @@ def create_chat_invite_link( member_limit: Optional[int]=None, creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ - Use this method to create an additional invite link for a chat. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and + must have the appropriate administrator rights. + The link can be revoked using the method revokeChatInviteLink. + Returns the new invite link as ChatInviteLink object. Telegram documentation: https://core.telegram.org/bots/api#createchatinvitelink :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + :param expire_date: Point in time (Unix timestamp) when the link will expire + :type expire_date: :obj:`int` or :obj:`datetime` + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :type member_limit: :obj:`int` + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified - :return: + :type creates_join_request: :obj:`bool` + + :return: Returns the new invite link as ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( apihelper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request) @@ -2637,12 +2951,25 @@ def edit_chat_invite_link( :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + :param invite_link: The invite link to edit + :type invite_link: :obj:`str` + :param expire_date: Point in time (Unix timestamp) when the link will expire + :type expire_date: :obj:`int` or :obj:`datetime` + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :type member_limit: :obj:`int` + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified - :return: + :type creates_join_request: :obj:`bool` + + :return: Returns the new invite link as ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( apihelper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) @@ -2659,8 +2986,13 @@ def revoke_chat_invite_link( :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param invite_link: The invite link to revoke - :return: + :type invite_link: :obj:`str` + + :return: Returns the new invite link as ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( apihelper.revoke_chat_invite_link(self.token, chat_id, invite_link) @@ -2675,7 +3007,10 @@ def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :return: exported invite link as String on success. + :rtype: :obj:`str` """ return apihelper.export_chat_invite_link(self.token, chat_id) @@ -2689,8 +3024,13 @@ def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.approve_chat_join_request(self.token, chat_id, user_id) @@ -2704,8 +3044,13 @@ def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.decline_chat_join_request(self.token, chat_id, user_id) @@ -2721,8 +3066,12 @@ def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param photo: InputFile: New chat photo, uploaded using multipart/form-data - :return: + :type photo: :obj:`typing.Union[file_like, str]` + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.set_chat_photo(self.token, chat_id, photo) @@ -2737,6 +3086,10 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.delete_chat_photo(self.token, chat_id) @@ -2750,9 +3103,15 @@ def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. + :type scope: :class:`telebot.types.BotCommandScope` + :param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands + :type language_code: :obj:`str` + + :return: List of BotCommand on success. + :rtype: :obj:`list` of :class:`telebot.types.BotCommand` """ result = apihelper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] @@ -2768,7 +3127,13 @@ def set_chat_menu_button(self, chat_id: Union[int, str]=None, :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be changed. + :type chat_id: :obj:`int` or :obj:`str` + :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault + :type menu_button: :class:`telebot.types.MenuButton` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.set_chat_menu_button(self.token, chat_id, menu_button) @@ -2782,12 +3147,15 @@ def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButto :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be returned. + :type chat_id: :obj:`int` or :obj:`str` + :return: types.MenuButton + :rtype: :class:`telebot.types.MenuButton` """ return types.MenuButton.de_json(apihelper.get_chat_menu_button(self.token, chat_id)) def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, - for_channels: bool=None) -> bool: + for_channels: Optional[bool]=None) -> bool: """ Use this method to change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. @@ -2797,12 +3165,20 @@ def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRig Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights - :param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared. - :param for_channels: Pass True to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + :param rights: A JSON-serialized object describing new default administrator rights. If not specified, + the default administrator rights will be cleared. + :type rights: :class:`telebot.types.ChatAdministratorRights` + + :param for_channels: Pass True to change the default administrator rights of the bot in channels. + Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + :type for_channels: :obj:`bool` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.set_my_default_administrator_rights(self.token, rights, for_channels) - def get_my_default_administrator_rights(self, for_channels: bool=None) -> types.ChatAdministratorRights: + def get_my_default_administrator_rights(self, for_channels: Optional[bool]=None) -> types.ChatAdministratorRights: """ Use this method to get the current default administrator rights of the bot. Returns ChatAdministratorRights on success. @@ -2810,7 +3186,10 @@ def get_my_default_administrator_rights(self, for_channels: bool=None) -> types. Telegram documentation: https://core.telegram.org/bots/api#getmydefaultadministratorrights :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. - :return: types.ChatAdministratorRights + :type for_channels: :obj:`bool` + + :return: Returns ChatAdministratorRights on success. + :rtype: :class:`telebot.types.ChatAdministratorRights` """ return types.ChatAdministratorRights.de_json(apihelper.get_my_default_administrator_rights(self.token, for_channels)) @@ -2823,12 +3202,19 @@ def set_my_commands(self, commands: List[types.BotCommand], Telegram documentation: https://core.telegram.org/bots/api#setmycommands :param commands: List of BotCommand. At most 100 commands can be specified. + :type commands: :obj:`list` of :class:`telebot.types.BotCommand` + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. + :type scope: :class:`telebot.types.BotCommandScope` + :param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands - :return: + :type language_code: :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.set_my_commands(self.token, commands, scope, language_code) @@ -2843,9 +3229,15 @@ def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. + :type scope: :class:`telebot.types.BotCommandScope` + :param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands + :type language_code: :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.delete_my_commands(self.token, scope, language_code) @@ -2859,10 +3251,15 @@ def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: Telegram documentation: https://core.telegram.org/bots/api#setchattitle - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param title: New chat title, 1-255 characters - :return: + :type title: :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.set_chat_title(self.token, chat_id, title) @@ -2873,10 +3270,15 @@ def set_chat_description(self, chat_id: Union[int, str], description: Optional[s Telegram documentation: https://core.telegram.org/bots/api#setchatdescription - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param description: Str: New chat description, 0-255 characters + :type description: :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.set_chat_description(self.token, chat_id, description) @@ -2890,12 +3292,19 @@ def pin_chat_message( Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :param message_id: Int: Identifier of a message to pin - :param disable_notification: Bool: Pass True, if it is not necessary to send a notification + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of a message to pin + :type message_id: :obj:`int` + + :param disable_notification: Pass True, if it is not necessary to send a notification to all group members about the new pinned message - :return: + :type disable_notification: :obj:`bool` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) @@ -2907,10 +3316,15 @@ def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int] Telegram documentation: https://core.telegram.org/bots/api#unpinchatmessage - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param message_id: Int: Identifier of a message to unpin - :return: + :type message_id: :obj:`int` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.unpin_chat_message(self.token, chat_id, message_id) @@ -2924,7 +3338,10 @@ def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :return: + :type chat_id: :obj:`int` or :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return apihelper.unpin_all_chat_messages(self.token, chat_id) @@ -2942,15 +3359,32 @@ def edit_message_text( Telegram documentation: https://core.telegram.org/bots/api#editmessagetext - :param text: - :param chat_id: - :param message_id: - :param inline_message_id: - :param parse_mode: - :param entities: - :param disable_web_page_preview: - :param reply_markup: - :return: + :param text: New text of the message, 1-4096 characters after entities parsing + :type text: :obj:`str` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param parse_mode: Mode for parsing entities in the message text. + :type parse_mode: :obj:`str` + + :param entities: List of special entities that appear in the message text, which can be specified instead of parse_mode + :type entities: List of :obj:`telebot.types.MessageEntity` + + :param disable_web_page_preview: Disables link previews for links in this message + :type disable_web_page_preview: :obj:`bool` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`InlineKeyboardMarkup` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2973,12 +3407,22 @@ def edit_message_media( Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia - :param media: - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :return: + :param media: A JSON-serialized object for a new media content of the message + :type media: :obj:`InputMedia` + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`telebot.types.InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` or :obj:`bool` """ result = apihelper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) if type(result) == bool: # if edit inline message return is bool not Message. @@ -2995,11 +3439,20 @@ def edit_message_reply_markup( Telegram documentation: https://core.telegram.org/bots/api#editmessagereplymarkup - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :return: + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` or :obj:`bool` """ result = apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) if type(result) == bool: @@ -3019,15 +3472,32 @@ def send_game( Telegram documentation: https://core.telegram.org/bots/api#sendgame - :param chat_id: - :param game_short_name: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param protect_content: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param game_short_name: Short name of the game, serves as the unique identifier for the game. Set up your games via @BotFather. + :type game_short_name: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + + :param timeout: Timeout in seconds for waiting for a response from the bot. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified replied-to messages is not found. + :type allow_sending_without_reply: :obj:`bool` + + :param protect_content: Pass True, if content of the message needs to be protected from being viewed by the bot. + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` """ result = apihelper.send_game( self.token, chat_id, game_short_name, disable_notification, @@ -3045,16 +3515,31 @@ def set_game_score( """ Sets the value of points in the game to a specific user. - Telegram documentation: https://core.telegram.org/bots/api#setgamecore + Telegram documentation: https://core.telegram.org/bots/api#setgamescore - :param user_id: - :param score: - :param force: - :param chat_id: - :param message_id: - :param inline_message_id: - :param disable_edit_message: - :return: + :param user_id: User identifier + :type user_id: :obj:`int` or :obj:`str` + + :param score: New score, must be non-negative + :type score: :obj:`int` + + :param force: Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters + :type force: :obj:`bool` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param disable_edit_message: Pass True, if the game message should not be automatically edited to include the current scoreboard + :type disable_edit_message: :obj:`bool` + + :return: On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. + :rtype: :obj:`types.Message` or :obj:`bool` """ result = apihelper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, message_id, inline_message_id) @@ -3071,11 +3556,20 @@ def get_game_high_scores( Telegram documentation: https://core.telegram.org/bots/api#getgamehighscores - :param user_id: - :param chat_id: - :param message_id: - :param inline_message_id: - :return: + :param user_id: User identifier + :type user_id: :obj:`int` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :return: On success, returns an Array of GameHighScore objects. + :rtype: List[types.GameHighScore] """ result = apihelper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) return [types.GameHighScore.de_json(r) for r in result] @@ -3107,43 +3601,100 @@ def send_invoice( Telegram documentation: https://core.telegram.org/bots/api#sendinvoice :param chat_id: Unique identifier for the target private chat - :param title: Product name - :param description: Product description + :type chat_id: :obj:`int` or :obj:`str` + + :param title: Product name, 1-32 characters + :type title: :obj:`str` + + :param description: Product description, 1-255 characters + :type description: :obj:`str` + :param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :type invoice_payload: :obj:`str` + :param provider_token: Payments provider token, obtained via @Botfather + :type provider_token: :obj:`str` + :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies + :type currency: :obj:`str` + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :type prices: List[:obj:`types.LabeledPrice`] + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter + :type start_parameter: :obj:`str` + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for. - :param photo_size: Photo size - :param photo_width: Photo width + :type photo_url: :obj:`str` + + :param photo_size: Photo size in bytes + :type photo_size: :obj:`int` + + :param photo_width: Photo width + :type photo_width: :obj:`int` + :param photo_height: Photo height + :type photo_height: :obj:`int` + :param need_name: Pass True, if you require the user's full name to complete the order + :type need_name: :obj:`bool` + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :type need_phone_number: :obj:`bool` + :param need_email: Pass True, if you require the user's email to complete the order + :type need_email: :obj:`bool` + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :type need_shipping_address: :obj:`bool` + :param is_flexible: Pass True, if the final price depends on the shipping method + :type is_flexible: :obj:`bool` + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :type send_phone_number_to_provider: :obj:`bool` + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :type send_email_to_provider: :obj:`bool` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :param reply_markup: 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 + :type reply_markup: :obj:`str` + :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 allow_sending_without_reply: + :type provider_data: :obj:`str` + + :param timeout: Timeout of a request, defaults to None + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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 max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency + :type max_tip_amount: :obj:`int` + :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: - :return: + :type suggested_tip_amounts: :obj:`list` of :obj:`int` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` """ result = apihelper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, @@ -3180,31 +3731,74 @@ def create_invoice_link(self, https://core.telegram.org/bots/api#createinvoicelink :param title: Product name, 1-32 characters + :type title: :obj:`str` + :param description: Product description, 1-255 characters + :type description: :obj:`str` + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :type payload: :obj:`str` + :param provider_token: Payments provider token, obtained via @Botfather + :type provider_token: :obj:`str` + :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies + :type currency: :obj:`str` + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :type prices: :obj:`list` of :obj:`types.LabeledPrice` + :param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency + :type max_tip_amount: :obj:`int` + :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. + :type suggested_tip_amounts: :obj:`list` of :obj:`int` + :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. + :type provider_data: :obj:`str` + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods + or a photo of the invoice. People like it better when they see a photo of what they are paying for. + :type photo_url: :obj:`str` + :param photo_size: Photo size in bytes + :type photo_size: :obj:`int` + :param photo_width: Photo width + :type photo_width: :obj:`int` + :param photo_height: Photo height + :type photo_height: :obj:`int` + :param need_name: Pass True, if you require the user's full name to complete the order + :type need_name: :obj:`bool` + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :type need_phone_number: :obj:`bool` + :param need_email: Pass True, if you require the user's email to complete the order + :type need_email: :obj:`bool` + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :type need_shipping_address: :obj:`bool` + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :type send_phone_number_to_provider: :obj:`bool` + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :type send_email_to_provider: :obj:`bool` + :param is_flexible: Pass True, if the final price depends on the shipping method + :type is_flexible: :obj:`bool` :return: Created invoice link as String on success. + :rtype: :obj:`str` """ result = apihelper.create_invoice_link( self.token, title, description, payload, provider_token, @@ -3234,30 +3828,74 @@ def send_poll( explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Sends a poll. + Use this method to send a native poll. + On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendpoll - :param chat_id: - :param question: - :param options: array of str with answers - :param is_anonymous: - :param type: - :param allows_multiple_answers: - :param correct_option_id: - :param explanation: - :param explanation_parse_mode: - :param open_period: - :param close_date: - :param is_closed: - :param disable_notification: - :param reply_to_message_id: - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :param explanation_entities: - :param protect_content: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :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 is_anonymous: True, if the poll needs to be anonymous, defaults to True + :type is_anonymous: :obj:`bool` + + :param type: Poll type, “quiz” or “regular”, defaults to “regular” + :type type: :obj:`str` + + :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 + :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 + :type explanation: :obj:`str` + + :param explanation_parse_mode: Mode for parsing entities in the explanation. See formatting options for more details. + :type explanation_parse_mode: :obj:`str` + + :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date. + :type open_period: :obj:`int` + + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. + :type close_date: :obj:`int` | :obj:`datetime` + + :param is_closed: Pass True, if the poll needs to be immediately closed. This can be useful for poll preview. + :type is_closed: :obj:`bool` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the poll allows multiple options to be voted simultaneously. + :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. + :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 + :type explanation_entities: :obj:`list` of :obj:`MessageEntity` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` """ if isinstance(question, types.Poll): raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") @@ -3277,14 +3915,21 @@ def stop_poll( self, chat_id: Union[int, str], message_id: int, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: """ - Stops a poll. + Use this method to stop a poll which was sent by the bot. On success, the stopped Poll is returned. Telegram documentation: https://core.telegram.org/bots/api#stoppoll - :param chat_id: - :param message_id: - :param reply_markup: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :param message_id: Identifier of the original message with the poll + :type message_id: :obj:`int` + + :param reply_markup: A JSON-serialized object for a new message markup. + :type reply_markup: :obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply` + + :return: On success, the stopped Poll is returned. + :rtype: :obj:`types.Poll` """ return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id, reply_markup)) @@ -3297,11 +3942,21 @@ def answer_shipping_query( Telegram documentation: https://core.telegram.org/bots/api#answershippingquery - :param shipping_query_id: - :param ok: - :param shipping_options: - :param error_message: - :return: + :param shipping_query_id: Unique identifier for the query to be answered + :type shipping_query_id: :obj:`str` + + :param ok: Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible) + :type ok: :obj:`bool` + + :param shipping_options: Required if ok is True. A JSON-serialized array of available shipping options. + :type shipping_options: :obj:`list` of :obj:`ShippingOption` + + :param error_message: Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order + (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. + :type error_message: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) @@ -3309,14 +3964,28 @@ def answer_pre_checkout_query( self, pre_checkout_query_id: int, ok: bool, error_message: Optional[str]=None) -> bool: """ - Response to a request for pre-inspection. + Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the + field pre_checkout_query. Use this method to respond to such pre-checkout queries. + On success, True is returned. + + .. note:: + The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. Telegram documentation: https://core.telegram.org/bots/api#answerprecheckoutquery - :param pre_checkout_query_id: - :param ok: - :param error_message: - :return: + :param pre_checkout_query_id: Unique identifier for the query to be answered + :type pre_checkout_query_id: :obj:`int` + + :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. + :type ok: :obj:`bool` + + :param error_message: Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout + (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different + color or garment!"). Telegram will display this message to the user. + :type error_message: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) @@ -3332,14 +4001,29 @@ def edit_message_caption( Telegram documentation: https://core.telegram.org/bots/api#editmessagecaption - :param caption: - :param chat_id: - :param message_id: - :param inline_message_id: - :param parse_mode: - :param caption_entities: - :param reply_markup: - :return: + :param caption: New caption of the message + :type caption: :obj:`str` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :param message_id: Required if inline_message_id is not specified. + :type message_id: :obj:`int` + + :param inline_message_id: Required if inline_message_id is not specified. Identifier of the inline message. + :type inline_message_id: :obj:`str` + + :param parse_mode: New caption of the message, 0-1024 characters after entities parsing + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized array of objects that describe how the caption should be parsed. + :type caption_entities: :obj:`list` of :obj:`types.MessageEntity` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`InlineKeyboardMarkup` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` | :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -3353,10 +4037,16 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` - :param message: - :param text: - :param kwargs: - :return: + :param message: Instance of :class:`telebot.types.Message` + :type message: :obj:`types.Message` + + :param text: Text of the message. + :type text: :obj:`str` + + :param kwargs: Additional keyword arguments which are passed to :meth:`telebot.TeleBot.send_message` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) @@ -3375,17 +4065,36 @@ def answer_inline_query( Telegram documentation: https://core.telegram.org/bots/api#answerinlinequery :param inline_query_id: Unique identifier for the answered query + :type inline_query_id: :obj:`str` + :param results: Array of results for the inline query + :type results: :obj:`list` of :obj:`types.InlineQueryResult` + :param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server. + :type cache_time: :obj:`int` + :param is_personal: Pass True, if results may be cached on the server side only for the user that sent the query. + :type is_personal: :obj:`bool` + :param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results. - :param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user - to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter - :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button - :return: True means success. + :type next_offset: :obj:`str` + + :param switch_pm_parameter: Deep-linking parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, + only A-Z, a-z, 0-9, _ and - are allowed. + Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. + To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a + private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a switch_inline + button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities. + :type switch_pm_parameter: :obj:`str` + + :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button + :type switch_pm_text: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, switch_pm_text, switch_pm_parameter) @@ -3400,12 +4109,24 @@ def answer_callback_query( Telegram documentation: https://core.telegram.org/bots/api#answercallbackquery - :param callback_query_id: - :param text: - :param show_alert: - :param url: - :param cache_time: - :return: + :param callback_query_id: Unique identifier for the query to be answered + :type callback_query_id: :obj:`int` + + :param text: Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters + :type text: :obj:`str` + + :param show_alert: If True, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false. + :type show_alert: :obj:`bool` + + :param url: URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @BotFather, specify the URL that opens your + game - note that this will only work if the query comes from a callback_game button. + :type url: :obj:`str` + + :param cache_time: The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching + starting in version 3.14. Defaults to 0. + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) @@ -3418,8 +4139,16 @@ def set_sticker_set_thumb( Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb :param name: Sticker set name + :type name: :obj:`str` + :param user_id: User identifier + :type user_id: :obj:`int` + :param thumb: + :type thumb: :obj:`filelike object` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) @@ -3429,8 +4158,11 @@ def get_sticker_set(self, name: str) -> types.StickerSet: Telegram documentation: https://core.telegram.org/bots/api#getstickerset - :param name: - :return: + :param name: Sticker set name + :type name: :obj:`str` + + :return: On success, a StickerSet object is returned. + :rtype: :class:`telebot.types.StickerSet` """ result = apihelper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) @@ -3442,9 +4174,15 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> typ Telegram documentation: https://core.telegram.org/bots/api#uploadstickerfile - :param user_id: - :param png_sticker: - :return: + :param user_id: User identifier of sticker set owner + :type user_id: :obj:`int` + + :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, + and either width or height must be exactly 512px. + :type png_sticker: :obj:`filelike object` + + :return: On success, the sent file is returned. + :rtype: :class:`telebot.types.File` """ result = apihelper.upload_sticker_file(self.token, user_id, png_sticker) return types.File.de_json(result) @@ -3464,16 +4202,39 @@ def create_new_sticker_set( Telegram documentation: https://core.telegram.org/bots/api#createnewstickerset - :param user_id: - :param name: - :param title: - :param emojis: - :param png_sticker: - :param tgs_sticker: - :param webm_sticker: - :param contains_masks: - :param mask_position: - :return: + :param user_id: User identifier of created sticker set owner + :type user_id: :obj:`int` + + :param name: Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). Can contain only English letters, + digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in "_by_". + is case insensitive. 1-64 characters. + :type name: :obj:`str` + + :param title: Sticker set title, 1-64 characters + :type title: :obj:`str` + + :param emojis: One or more emoji corresponding to the sticker + :type emojis: :obj:`str` + + :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width + or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. + :type png_sticker: :obj:`str` + + :param tgs_sticker: TGS animation with the sticker, uploaded using multipart/form-data. + :type tgs_sticker: :obj:`str` + + :param webm_sticker: WebM animation with the sticker, uploaded using multipart/form-data. + :type webm_sticker: :obj:`str` + + :param contains_masks: Pass True, if a set of mask stickers should be created + :type contains_masks: :obj:`bool` + + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces + :type mask_position: :class:`telebot.types.MaskPosition` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, @@ -3492,14 +4253,31 @@ def add_sticker_to_set( Telegram documentation: https://core.telegram.org/bots/api#addstickertoset - :param user_id: - :param name: - :param emojis: - :param png_sticker: Required if `tgs_sticker` is None - :param tgs_sticker: Required if `png_sticker` is None - :param webm_sticker: - :param mask_position: - :return: + :param user_id: User identifier of created sticker set owner + :type user_id: :obj:`int` + + :param name: Sticker set name + :type name: :obj:`str` + + :param emojis: One or more emoji corresponding to the sticker + :type emojis: :obj:`str` + + :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either + width or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, + 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. + :type png_sticker: :obj:`str` or :obj:`filelike object` + + :param tgs_sticker: TGS animation with the sticker, uploaded using multipart/form-data. + :type tgs_sticker: :obj:`str` or :obj:`filelike object` + + :param webm_sticker: WebM animation with the sticker, uploaded using multipart/form-data. + :type webm_sticker: :obj:`str` or :obj:`filelike object` + + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces + :type mask_position: :class:`telebot.types.MaskPosition` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.add_sticker_to_set( self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker) @@ -3510,9 +4288,14 @@ def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: Telegram documentation: https://core.telegram.org/bots/api#setstickerpositioninset - :param sticker: - :param position: - :return: + :param sticker: File identifier of the sticker + :type sticker: :obj:`str` + + :param position: New sticker position in the set, zero-based + :type position: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.set_sticker_position_in_set(self.token, sticker, position) @@ -3522,8 +4305,9 @@ def delete_sticker_from_set(self, sticker: str) -> bool: Telegram documentation: https://core.telegram.org/bots/api#deletestickerfromset - :param sticker: - :return: + :param sticker: File identifier of the sticker + :return: On success, True is returned. + :rtype: :obj:`bool` """ return apihelper.delete_sticker_from_set(self.token, sticker) @@ -3537,8 +4321,13 @@ def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryR Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery :param web_app_query_id: Unique identifier for the query to be answered + :type web_app_query_id: :obj:`str` + :param result: A JSON-serialized object describing the message to be sent - :return: + :type result: :class:`telebot.types.InlineQueryResultBase` + + :return: On success, a SentWebAppMessage object is returned. + :rtype: :class:`telebot.types.SentWebAppMessage` """ return apihelper.answer_web_app_query(self.token, web_app_query_id, result) @@ -3548,9 +4337,17 @@ def register_for_reply(self, message: types.Message, callback: Callable, *args, Warning: In case `callback` as lambda function, saving reply handlers will not work. - :param message: The message for which we are awaiting a reply. - :param callback: The callback function to be called when a reply arrives. Must accept one `message` - parameter, which will contain the replied message. + :param message: The message for which we are awaiting a reply. + :type message: :class:`telebot.types.Message` + + :param callback: The callback function to be called when a reply arrives. Must accept one `message` + parameter, which will contain the replied message. + :type callback: :obj:`Callable[[telebot.types.Message], None]` + + :param args: Optional arguments for the callback function. + :param kwargs: Optional keyword arguments for the callback function. + + :return: None """ message_id = message.message_id self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs) @@ -3562,9 +4359,17 @@ def register_for_reply_by_message_id( Warning: In case `callback` as lambda function, saving reply handlers will not work. - :param message_id: The id of the message for which we are awaiting a reply. - :param callback: The callback function to be called when a reply arrives. Must accept one `message` - parameter, which will contain the replied message. + :param message_id: The id of the message for which we are awaiting a reply. + :type message_id: :obj:`int` + + :param callback: The callback function to be called when a reply arrives. Must accept one `message` + parameter, which will contain the replied message. + :type callback: :obj:`Callable[[telebot.types.Message], None]` + + :param args: Optional arguments for the callback function. + :param kwargs: Optional keyword arguments for the callback function. + + :return: None """ self.reply_backend.register_handler(message_id, Handler(callback, *args, **kwargs)) @@ -3588,10 +4393,17 @@ def register_next_step_handler(self, message: types.Message, callback: Callable, Warning: In case `callback` as lambda function, saving next step handlers will not work. - :param message: The message for which we want to handle new message in the same chat. - :param callback: The callback function which next new message arrives. - :param args: Args to pass in callback func - :param kwargs: Args to pass in callback func + :param message: The message for which we want to handle new message in the same chat. + :type message: :class:`telebot.types.Message` + + :param callback: The callback function which next new message arrives. + :type callback: :obj:`Callable[[telebot.types.Message], None]` + + :param args: Args to pass in callback func + + :param kwargs: Args to pass in callback func + + :return: None """ chat_id = message.chat.id self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) @@ -3600,7 +4412,8 @@ def setup_middleware(self, middleware: BaseMiddleware): """ Registers class-based middleware. - :param middleware: Subclass of `telebot.handler_backends.BaseMiddleware` + :param middleware: Subclass of :class:`telebot.handler_backends.BaseMiddleware` + :type middleware: :class:`telebot.handler_backends.BaseMiddleware` :return: None """ if not self.use_class_middlewares: @@ -3621,9 +4434,16 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: int=No """ Sets a new state of a user. - :param user_id: - :param state: new state. can be string or integer. - :param chat_id: + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param state: new state. can be string, integer, or :class:`telebot.types.State` + :type state: :obj:`int` or :obj:`str` or :class:`telebot.types.State` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :return: None """ if chat_id is None: chat_id = user_id @@ -3633,8 +4453,13 @@ def reset_data(self, user_id: int, chat_id: int=None): """ Reset data for a user in chat. - :param user_id: - :param chat_id: + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :return: None """ if chat_id is None: chat_id = user_id @@ -3644,26 +4469,36 @@ def delete_state(self, user_id: int, chat_id: int=None) -> None: """ Delete the current state of a user. - :param user_id: - :param chat_id: - :return: + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :return: None """ if chat_id is None: chat_id = user_id self.current_states.delete_state(chat_id, user_id) - def retrieve_data(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str]]: + def retrieve_data(self, user_id: int, chat_id: int=None) -> Optional[Any]: if chat_id is None: chat_id = user_id return self.current_states.get_interactive_data(chat_id, user_id) - def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str]]: + def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str, State]]: """ - Get current state of a user. + Gets current state of a user. + Not recommended to use this method. But it is ok for debugging. + + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` - :param user_id: - :param chat_id: :return: state of a user + :rtype: :obj:`int` or :obj:`str` or :class:`telebot.types.State` """ if chat_id is None: chat_id = user_id @@ -3673,8 +4508,14 @@ def add_data(self, user_id: int, chat_id:int=None, **kwargs): """ Add data to states. - :param user_id: - :param chat_id: + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :param kwargs: Data to add + :return: None """ if chat_id is None: chat_id = user_id @@ -3688,10 +4529,17 @@ def register_next_step_handler_by_chat_id( Warning: In case `callback` as lambda function, saving next step handlers will not work. - :param chat_id: The chat for which we want to handle new message. - :param callback: The callback function which next new message arrives. - :param args: Args to pass in callback func - :param kwargs: Args to pass in callback func + :param chat_id: The chat for which we want to handle new message. + :type chat_id: :obj:`int` or :obj:`str` + + :param callback: The callback function which next new message arrives. + :type callback: :obj:`Callable[[telebot.types.Message], None]` + + :param args: Args to pass in callback func + + :param kwargs: Args to pass in callback func + + :return: None """ self.next_step_backend.register_handler(chat_id, Handler(callback, *args, **kwargs)) @@ -3699,7 +4547,10 @@ def clear_step_handler(self, message: types.Message) -> None: """ Clears all callback functions registered by register_next_step_handler(). - :param message: The message for which we want to handle new message after that in same chat. + :param message: The message for which we want to handle new message after that in same chat. + :type message: :class:`telebot.types.Message` + + :return: None """ chat_id = message.chat.id self.clear_step_handler_by_chat_id(chat_id) @@ -3709,6 +4560,9 @@ def clear_step_handler_by_chat_id(self, chat_id: Union[int, str]) -> None: Clears all callback functions registered by register_next_step_handler(). :param chat_id: The chat for which we want to clear next step handlers + :type chat_id: :obj:`int` or :obj:`str` + + :return: None """ self.next_step_backend.clear_handlers(chat_id) @@ -3717,6 +4571,9 @@ def clear_reply_handlers(self, message: types.Message) -> None: Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). :param message: The message for which we want to clear reply handlers + :type message: :class:`telebot.types.Message` + + :return: None """ message_id = message.message_id self.clear_reply_handlers_by_message_id(message_id) @@ -3726,6 +4583,9 @@ def clear_reply_handlers_by_message_id(self, message_id: int) -> None: Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). :param message_id: The message id for which we want to clear reply handlers + :type message_id: :obj:`int` + + :return: None """ self.reply_backend.clear_handlers(message_id) @@ -3764,7 +4624,7 @@ def _build_handler_dict(handler, pass_bot=False, **filters): #'filters': filters } - def middleware_handler(self, update_types=None): + def middleware_handler(self, update_types: Optional[List[str]]=None): """ Function-based middleware handler decorator. @@ -3789,6 +4649,9 @@ def print_channel_post_text(bot_instance, update): print(update.update_id) :param update_types: Optional list of update types that can be passed into the middleware handler. + :type update_types: :obj:`list` of :obj:`str` + + :return: function """ def decorator(handler): self.add_middleware_handler(handler, update_types) @@ -3800,6 +4663,8 @@ def add_middleware_handler(self, handler, update_types=None): """ Add middleware handler. + :meta private: + :param handler: :param update_types: :return: @@ -3833,23 +4698,35 @@ def register_middleware_handler(self, callback, update_types=None): bot = TeleBot('TOKEN') bot.register_middleware_handler(print_channel_post_text, update_types=['channel_post', 'edited_channel_post']) + + :param callback: Function that will be used as a middleware handler. + :type callback: :obj:`function` - :param callback: :param update_types: Optional list of update types that can be passed into the middleware handler. + :type update_types: :obj:`list` of :obj:`str` + + :return: None """ self.add_middleware_handler(callback, update_types) @staticmethod def check_commands_input(commands, method_name): + """ + :meta private: + """ if not isinstance(commands, list) or not all(isinstance(item, str) for item in commands): logger.error(f"{method_name}: Commands filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use the supplied type.") @staticmethod def check_regexp_input(regexp, method_name): + """ + :meta private: + """ if not isinstance(regexp, str): logger.error(f"{method_name}: Regexp filter should be string. Not able to use the supplied type.") - def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): + def message_handler(self, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, + content_types: Optional[List[str]]=None, chat_types: Optional[List[str]]=None, **kwargs): """ Message handler decorator. This decorator can be used to decorate functions that must handle certain types of messages. @@ -3884,7 +4761,11 @@ def default_command(message): bot.send_message(message.chat.id, "This is the default command handler.") :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param content_types: Supported message content types. Must be a list. Defaults to ['text']. @@ -3925,23 +4806,42 @@ def add_message_handler(self, handler_dict): Adds a message handler Note that you should use register_message_handler to add message_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): + def register_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + regexp: Optional[str]=None, func: Optional[Callable]=None, chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message handler. :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands + :type commands: :obj:`list` of :obj:`str` + :param regexp: - :param func: - :param chat_types: True for private chat - :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param chat_types: List of chat types + :type chat_types: :obj:`list` of :obj:`str` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ method_name = "register_message_handler" @@ -3973,13 +4873,23 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ """ Edit message handler decorator - :param commands: - :param regexp: - :param func: - :param content_types: + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param chat_types: list of chat types - :param kwargs: - :return: + :type chat_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ if content_types is None: content_types = ["text"] @@ -4015,24 +4925,41 @@ def add_edited_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_message_handler to add edited_message_handler to the bot. - + :meta private: + :param handler_dict: :return: """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): + def register_edited_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, + commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, + chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers edited message handler. :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands - :param regexp: - :param func: + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param chat_types: True for private chat + :type chat_types: :obj:`bool` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ method_name = "register_edited_message_handler" @@ -4060,14 +4987,22 @@ def register_edited_message_handler(self, callback, content_types=None, commands def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ - Channel post handler decorator + Channel post handler decorator. - :param commands: - :param regexp: - :param func: - :param content_types: - :param kwargs: - :return: + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ if content_types is None: content_types = ["text"] @@ -4102,23 +5037,38 @@ def add_channel_post_handler(self, handler_dict): """ Adds channel post handler Note that you should use register_channel_post_handler to add channel_post_handler to the bot. + + :meta private: :param handler_dict: :return: """ self.channel_post_handlers.append(handler_dict) - def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): + def register_channel_post_handler(self, callback: Callable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers channel post message handler. :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands - :param regexp: - :param func: + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ method_name = "register_channel_post_handler" @@ -4147,11 +5097,20 @@ def edited_channel_post_handler(self, commands=None, regexp=None, func=None, con """ Edit channel post handler decorator - :param commands: - :param regexp: - :param func: - :param content_types: - :param kwargs: + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + :return: """ if content_types is None: @@ -4188,21 +5147,36 @@ def add_edited_channel_post_handler(self, handler_dict): Adds the edit channel post handler Note that you should use register_edited_channel_post_handler to add edited_channel_post_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.edited_channel_post_handlers.append(handler_dict) - def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): + def register_edited_channel_post_handler(self, callback: Callable, content_types: Optional[List[str]]=None, + commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers edited channel post message handler. :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands - :param regexp: - :param func: + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. + :type pass_bot: :obj:`bool` + :return: decorated function """ method_name = "register_edited_channel_post_handler" @@ -4232,9 +5206,12 @@ def inline_handler(self, func, **kwargs): """ Inline call handler decorator - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4248,18 +5225,26 @@ def add_inline_handler(self, handler_dict): Adds inline call handler Note that you should use register_inline_handler to add inline_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.inline_handlers.append(handler_dict) - def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): + def register_inline_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers inline handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. + :type pass_bot: :obj:`bool` + :return: decorated function """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -4267,11 +5252,14 @@ def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): def chosen_inline_handler(self, func, **kwargs): """ - Description: TBD + Description: The result of an inline query that was chosen by a user and sent to their chat partner. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4285,19 +5273,27 @@ def add_chosen_inline_handler(self, handler_dict): Description: TBD Note that you should use register_chosen_inline_handler to add chosen_inline_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.chosen_inline_handlers.append(handler_dict) - def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwargs): + def register_chosen_inline_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers chosen inline handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chosen_inline_handler(handler_dict) @@ -4306,9 +5302,12 @@ def callback_query_handler(self, func, **kwargs): """ Callback request handler decorator - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4322,19 +5321,27 @@ def add_callback_query_handler(self, handler_dict): Adds a callback request handler Note that you should use register_callback_query_handler to add callback_query_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.callback_query_handlers.append(handler_dict) - def register_callback_query_handler(self, callback, func, pass_bot=False, **kwargs): + def register_callback_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers callback query handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_callback_query_handler(handler_dict) @@ -4343,9 +5350,12 @@ def shipping_query_handler(self, func, **kwargs): """ Shipping request handler - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4359,19 +5369,27 @@ def add_shipping_query_handler(self, handler_dict): Adds a shipping request handler. Note that you should use register_shipping_query_handler to add shipping_query_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.shipping_query_handlers.append(handler_dict) - def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwargs): + def register_shipping_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers shipping query handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_shipping_query_handler(handler_dict) @@ -4380,9 +5398,12 @@ def pre_checkout_query_handler(self, func, **kwargs): """ Pre-checkout request handler - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4396,18 +5417,25 @@ def add_pre_checkout_query_handler(self, handler_dict): Adds a pre-checkout request handler Note that you should use register_pre_checkout_query_handler to add pre_checkout_query_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.pre_checkout_query_handlers.append(handler_dict) - def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, **kwargs): + def register_pre_checkout_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers pre-checkout request handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :param pass_bot: Pass TeleBot to handler. + :type pass_bot: :obj:`bool` + :return: decorated function """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -4417,9 +5445,11 @@ def poll_handler(self, func, **kwargs): """ Poll request handler - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4433,19 +5463,27 @@ def add_poll_handler(self, handler_dict): Adds a poll request handler Note that you should use register_poll_handler to add poll_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.poll_handlers.append(handler_dict) - def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): + def register_poll_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_handler(handler_dict) @@ -4454,9 +5492,12 @@ def poll_answer_handler(self, func=None, **kwargs): """ Poll_answer request handler - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4475,25 +5516,35 @@ def add_poll_answer_handler(self, handler_dict): """ self.poll_answer_handlers.append(handler_dict) - def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs): + def register_poll_answer_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll answer handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_answer_handler(handler_dict) def my_chat_member_handler(self, func=None, **kwargs): """ - my_chat_member handler. + The bot's chat member status was updated in a chat. For private chats, + this update is received only when the bot is blocked or unblocked by the user. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4507,30 +5558,42 @@ def add_my_chat_member_handler(self, handler_dict): Adds a my_chat_member handler. Note that you should use register_my_chat_member_handler to add my_chat_member_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.my_chat_member_handlers.append(handler_dict) - def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): + def register_my_chat_member_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[Callable]=False, **kwargs): """ Registers my chat member handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_my_chat_member_handler(handler_dict) def chat_member_handler(self, func=None, **kwargs): """ - chat_member handler. + A chat member's status was updated in a chat. The bot must be an administrator + in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4544,30 +5607,43 @@ def add_chat_member_handler(self, handler_dict): Adds a chat_member handler. Note that you should use register_chat_member_handler to add chat_member_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.chat_member_handlers.append(handler_dict) - def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): + def register_chat_member_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers chat member handler. :param callback: function to be called - :param func: + :type callback: :obj:`function`` + + :param func: Function executed as a filter + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return:None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_member_handler(handler_dict) def chat_join_request_handler(self, func=None, **kwargs): """ - chat_join_request handler + A request to join the chat has been sent. The bot must have the can_invite_users + administrator right in the chat to receive these updates. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) @@ -4581,19 +5657,27 @@ def add_chat_join_request_handler(self, handler_dict): Adds a chat_join_request handler. Note that you should use register_chat_join_request_handler to add chat_join_request_handler to the bot. + :meta private: + :param handler_dict: :return: """ self.chat_join_request_handlers.append(handler_dict) - def register_chat_join_request_handler(self, callback, func=None, pass_bot=False, **kwargs): + def register_chat_join_request_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): """ Registers chat join request handler. :param callback: function to be called - :param func: + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param pass_bot: Pass TeleBot to handler. - :return: decorated function + :type pass_bot: :obj:`bool` + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_join_request_handler(handler_dict) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 598cf594f..9f4a3a952 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -180,6 +180,7 @@ class BaseMiddleware: so on. Same applies to post_process. .. code-block:: python + class MyMiddleware(BaseMiddleware): def __init__(self): self.update_sensitive = True From f0feb45e871c2b0011a02f435170b77b134361f4 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 19 Jul 2022 23:51:25 +0500 Subject: [PATCH 1066/1808] Completed all docstrings / typehints up to 100%(visual) --- telebot/__init__.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a43aaee93..21a05ca1e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2777,7 +2777,7 @@ def promote_chat_member( :param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only :type can_pin_messages: :obj:`bool` - :param can_promote_members:Pass True, if the administrator can add new administrators with a subset + :param can_promote_members: Pass True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) :type can_promote_members: :obj:`bool` @@ -4925,6 +4925,7 @@ def add_edited_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_message_handler to add edited_message_handler to the bot. + :meta private: :param handler_dict: @@ -4956,7 +4957,7 @@ def register_edited_message_handler(self, callback: Callable, content_types: Opt :param chat_types: True for private chat :type chat_types: :obj:`bool` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5065,7 +5066,7 @@ def register_channel_post_handler(self, callback: Callable, content_types: Optio :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5174,7 +5175,7 @@ def register_edited_channel_post_handler(self, callback: Callable, content_types :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: decorated function @@ -5242,7 +5243,7 @@ def register_inline_handler(self, callback: Callable, func: Callable, pass_bot: :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: decorated function @@ -5290,7 +5291,7 @@ def register_chosen_inline_handler(self, callback: Callable, func: Callable, pas :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5338,7 +5339,7 @@ def register_callback_query_handler(self, callback: Callable, func: Callable, pa :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5386,7 +5387,7 @@ def register_shipping_query_handler(self, callback: Callable, func: Callable, pa :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5433,7 +5434,7 @@ def register_pre_checkout_query_handler(self, callback: Callable, func: Callable :param func: Function executed as a filter - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: decorated function @@ -5480,7 +5481,7 @@ def register_poll_handler(self, callback: Callable, func: Callable, pass_bot: Op :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5511,6 +5512,8 @@ def add_poll_answer_handler(self, handler_dict): Adds a poll_answer request handler. Note that you should use register_poll_answer_handler to add poll_answer_handler to the bot. + :meta private: + :param handler_dict: :return: """ @@ -5526,7 +5529,7 @@ def register_poll_answer_handler(self, callback: Callable, func: Callable, pass_ :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5575,7 +5578,7 @@ def register_my_chat_member_handler(self, callback: Callable, func: Optional[Cal :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None @@ -5622,8 +5625,9 @@ def register_chat_member_handler(self, callback: Callable, func: Optional[Callab :type callback: :obj:`function`` :param func: Function executed as a filter + :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :param kwargs: Optional keyword arguments(custom filters) @@ -5674,7 +5678,7 @@ def register_chat_join_request_handler(self, callback: Callable, func: Optional[ :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: Pass TeleBot to handler. + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` :return: None From b0e06253ffc368877cd7c46ef0d3b7257b827fa9 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 24 Jul 2022 23:14:09 +0500 Subject: [PATCH 1067/1808] Completed docstrings for all files except types.py --- telebot/__init__.py | 173 +- telebot/async_telebot.py | 3035 +++++++++++++++++++++------ telebot/asyncio_filters.py | 165 +- telebot/asyncio_handler_backends.py | 33 +- telebot/callback_data.py | 16 +- telebot/custom_filters.py | 164 +- telebot/ext/aio/webhooks.py | 29 +- telebot/ext/sync/webhooks.py | 38 +- telebot/formatting.py | 183 +- telebot/handler_backends.py | 38 +- telebot/types.py | 61 + telebot/util.py | 211 +- 12 files changed, 3283 insertions(+), 863 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 21a05ca1e..da3bf26f8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -84,10 +84,13 @@ class TeleBot: Usage: - .. code-block:: python + .. code-block:: python3 + :caption: Creating instance of TeleBot from telebot import TeleBot bot = TeleBot('token') # get token from @BotFather + # now you can register other handlers/update listeners, + # and use bot methods. See more examples in examples/ directory: https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples @@ -243,7 +246,7 @@ def enable_saving_states(self, filename: Optional[str]="./.state-save/states.pkl Enable saving states (by default saving disabled) .. note:: - It is recommended to pass a :class:`~telebot.storage.StateMemoryStorage` instance as state_storage + It is recommended to pass a :class:`~telebot.storage.StatePickleStorage` instance as state_storage to TeleBot class. :param filename: Filename of saving file, defaults to "./.state-save/states.pkl" @@ -547,8 +550,6 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, Telegram documentation: https://core.telegram.org/bots/api#getupdates - :param allowed_updates: Array of string. List the types of updates you want your bot to receive. - :type allowed_updates: :obj:`list`, optional :param offset: Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset @@ -562,6 +563,9 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, :param timeout: Request connection timeout :type timeout: :obj:`int`, optional + :param allowed_updates: Array of string. List the types of updates you want your bot to receive. + :type allowed_updates: :obj:`list`, optional + :param long_polling_timeout: Timeout in seconds for long polling. :type long_polling_timeout: :obj:`int`, optional @@ -604,6 +608,9 @@ def process_new_updates(self, updates: List[types.Update]): Processes new updates. Just pass list of subclasses of Update to this method. :param updates: List of :class:`telebot.types.Update` objects. + :type updates: :obj:`list` of :class:`telebot.types.Update` + + :return None: """ upd_count = len(updates) logger.debug('Received {0} new updates'.format(upd_count)) @@ -885,11 +892,11 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F 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. + This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. Warning: Do not call this function more than once! - Always get updates. + Always gets updates. .. deprecated:: 4.1.1 Use :meth:`infinity_polling` instead. @@ -921,7 +928,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F 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. - :type allowed_updates: :obj:`list`] of :obj:`str` + :type allowed_updates: :obj:`list` of :obj:`str` :param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility. :type none_stop: :obj:`bool` @@ -1104,6 +1111,8 @@ def _exec_task(self, task, *args, **kwargs): def stop_polling(self): """ Stops polling. + + Does not accept any arguments. """ self.__stop_polling.set() @@ -1170,6 +1179,15 @@ def get_file_url(self, file_id: Optional[str]) -> str: def download_file(self, file_path: str) -> bytes: + """ + Downloads file. + + :param file_path: Path where the file should be downloaded. + :type file_path: str + + :return: bytes + :rtype: :obj:`bytes` + """ return apihelper.download_file(self.token, file_path) @@ -1209,7 +1227,7 @@ def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ Use this method to get a list of profile pictures for a user. - Returns a UserProfilePhotos object. + Returns a :class:`telebot.types.UserProfilePhotos` object. Telegram documentation: https://core.telegram.org/bots/api#getuserprofilephotos @@ -1240,7 +1258,7 @@ def get_chat(self, chat_id: Union[int, str]) -> types.Chat: :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - :return: :class:`telebot.types.Chat` + :return: Chat information :rtype: :class:`telebot.types.Chat` """ result = apihelper.get_chat(self.token, chat_id) @@ -1384,8 +1402,8 @@ def send_message( """ Use this method to send text messages. - Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 4000 characters, + Warning: Do not send more than about 4096 characters each message, otherwise you'll risk an HTTP 414 error. + If you must send more than 4096 characters, use the `split_string` or `smart_split` function in util.py. Telegram documentation: https://core.telegram.org/bots/api#sendmessage @@ -3552,7 +3570,12 @@ def get_game_high_scores( message_id: Optional[int]=None, inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ - Gets top points and game play. + Use this method to get data for high score tables. Will return the score of the specified user and several of + their neighbors in a game. On success, returns an Array of GameHighScore objects. + + This method will currently return scores for the target user, plus two of their closest neighbors on each side. + Will also return the top three users if the user and their neighbors are not among them. + Please note that this behavior is subject to change. Telegram documentation: https://core.telegram.org/bots/api#getgamehighscores @@ -4430,10 +4453,16 @@ def setup_middleware(self, middleware: BaseMiddleware): self.middlewares.append(middleware) - def set_state(self, user_id: int, state: Union[int, str, State], chat_id: int=None) -> None: + def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None) -> None: """ Sets a new state of a user. + .. note:: + + You should set both user id and chat id in order to set state for a user in a chat. + Otherwise, if you only set user_id, chat_id will equal to user_id, this means that + state will be set for the user in his private chat with a bot. + :param user_id: User's identifier :type user_id: :obj:`int` @@ -4449,7 +4478,7 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: int=No chat_id = user_id self.current_states.set_state(chat_id, user_id, state) - def reset_data(self, user_id: int, chat_id: int=None): + def reset_data(self, user_id: int, chat_id: Optional[int]=None): """ Reset data for a user in chat. @@ -4465,7 +4494,7 @@ def reset_data(self, user_id: int, chat_id: int=None): chat_id = user_id self.current_states.reset_data(chat_id, user_id) - def delete_state(self, user_id: int, chat_id: int=None) -> None: + def delete_state(self, user_id: int, chat_id: Optional[int]=None) -> None: """ Delete the current state of a user. @@ -4481,12 +4510,24 @@ def delete_state(self, user_id: int, chat_id: int=None) -> None: chat_id = user_id self.current_states.delete_state(chat_id, user_id) - def retrieve_data(self, user_id: int, chat_id: int=None) -> Optional[Any]: + def retrieve_data(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Any]: + """ + Returns context manager with data for a user in chat. + + :param user_id: User identifier + :type user_id: int + + :param chat_id: Chat's unique identifier, defaults to user_id + :type chat_id: int, optional + + :return: Context manager with data for a user in chat + :rtype: Optional[Any] + """ if chat_id is None: chat_id = user_id return self.current_states.get_interactive_data(chat_id, user_id) - def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str, State]]: + def get_state(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Union[int, str, State]]: """ Gets current state of a user. Not recommended to use this method. But it is ok for debugging. @@ -4504,7 +4545,7 @@ def get_state(self, user_id: int, chat_id: int=None) -> Optional[Union[int, str, chat_id = user_id return self.current_states.get_state(chat_id, user_id) - def add_data(self, user_id: int, chat_id:int=None, **kwargs): + def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): """ Add data to states. @@ -4635,6 +4676,7 @@ def middleware_handler(self, update_types: Optional[List[str]]=None): Example: .. code-block:: python3 + :caption: Usage of middleware_handler bot = TeleBot('TOKEN') @@ -4728,13 +4770,14 @@ def check_regexp_input(regexp, method_name): def message_handler(self, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, content_types: Optional[List[str]]=None, chat_types: Optional[List[str]]=None, **kwargs): """ - Message handler decorator. - This decorator can be used to decorate functions that must handle certain types of messages. + Handles New incoming message of any kind - text, photo, sticker, etc. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. All message handlers are tested in the order they were added. Example: - .. code-block:: python + .. code-block:: python3 + :caption: Usage of message_handler bot = TeleBot('TOKEN') @@ -4768,8 +4811,17 @@ def default_command(message): :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. + :type func: :obj:`lambda` + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param chat_types: list of chat types + :type chat_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: decorated function """ if content_types is None: content_types = ["text"] @@ -4871,7 +4923,8 @@ def register_message_handler(self, callback: Callable, content_types: Optional[L def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ - Edit message handler decorator + Handles new version of a message that is known to the bot and was edited. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. :param commands: Optional list of strings (commands to handle). :type commands: :obj:`list` of :obj:`str` @@ -4889,6 +4942,7 @@ def edited_message_handler(self, commands=None, regexp=None, func=None, content_ :type chat_types: :obj:`list` of :obj:`str` :param kwargs: Optional keyword arguments(custom filters) + :return: None """ if content_types is None: @@ -4960,6 +5014,8 @@ def register_edited_message_handler(self, callback: Callable, content_types: Opt :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ method_name = "register_edited_message_handler" @@ -4988,7 +5044,8 @@ def register_edited_message_handler(self, callback: Callable, content_types: Opt def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ - Channel post handler decorator. + Handles new incoming channel post of any kind - text, photo, sticker, etc. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. :param commands: Optional list of strings (commands to handle). :type commands: :obj:`list` of :obj:`str` @@ -5003,6 +5060,7 @@ def channel_post_handler(self, commands=None, regexp=None, func=None, content_ty :type content_types: :obj:`list` of :obj:`str` :param kwargs: Optional keyword arguments(custom filters) + :return: None """ if content_types is None: @@ -5069,6 +5127,8 @@ def register_channel_post_handler(self, callback: Callable, content_types: Optio :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ method_name = "register_channel_post_handler" @@ -5096,7 +5156,8 @@ def register_channel_post_handler(self, callback: Callable, content_types: Optio def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ - Edit channel post handler decorator + Handles new version of a channel post that is known to the bot and was edited. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. :param commands: Optional list of strings (commands to handle). :type commands: :obj:`list` of :obj:`str` @@ -5178,6 +5239,8 @@ def register_edited_channel_post_handler(self, callback: Callable, content_types :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: decorated function """ method_name = "register_edited_channel_post_handler" @@ -5205,7 +5268,8 @@ def register_edited_channel_post_handler(self, callback: Callable, content_types def inline_handler(self, func, **kwargs): """ - Inline call handler decorator + Handles new incoming inline query. + As a parameter to the decorator function, it passes :class:`telebot.types.InlineQuery` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5246,6 +5310,8 @@ def register_inline_handler(self, callback: Callable, func: Callable, pass_bot: :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: decorated function """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5253,7 +5319,9 @@ def register_inline_handler(self, callback: Callable, func: Callable, pass_bot: def chosen_inline_handler(self, func, **kwargs): """ - Description: The result of an inline query that was chosen by a user and sent to their chat partner. + Handles the result of an inline query that was chosen by a user and sent to their chat partner. + Please see our documentation on the feedback collecting for details on how to enable these updates for your bot. + As a parameter to the decorator function, it passes :class:`telebot.types.ChosenInlineResult` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5294,6 +5362,8 @@ def register_chosen_inline_handler(self, callback: Callable, func: Callable, pas :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5301,7 +5371,8 @@ def register_chosen_inline_handler(self, callback: Callable, func: Callable, pas def callback_query_handler(self, func, **kwargs): """ - Callback request handler decorator + Handles new incoming callback query. + As a parameter to the decorator function, it passes :class:`telebot.types.CallbackQuery` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5342,6 +5413,8 @@ def register_callback_query_handler(self, callback: Callable, func: Callable, pa :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5349,7 +5422,8 @@ def register_callback_query_handler(self, callback: Callable, func: Callable, pa def shipping_query_handler(self, func, **kwargs): """ - Shipping request handler + Handles new incoming shipping query. Only for invoices with flexible price. + As a parameter to the decorator function, it passes :class:`telebot.types.ShippingQuery` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5390,6 +5464,8 @@ def register_shipping_query_handler(self, callback: Callable, func: Callable, pa :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5397,7 +5473,8 @@ def register_shipping_query_handler(self, callback: Callable, func: Callable, pa def pre_checkout_query_handler(self, func, **kwargs): """ - Pre-checkout request handler + New incoming pre-checkout query. Contains full information about checkout. + As a parameter to the decorator function, it passes :class:`telebot.types.PreCheckoutQuery` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5437,6 +5514,8 @@ def register_pre_checkout_query_handler(self, callback: Callable, func: Callable :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: decorated function """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5444,7 +5523,8 @@ def register_pre_checkout_query_handler(self, callback: Callable, func: Callable def poll_handler(self, func, **kwargs): """ - Poll request handler + Handles new state of a poll. Bots receive only updates about stopped polls and polls, which are sent by the bot + As a parameter to the decorator function, it passes :class:`telebot.types.Poll` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5484,6 +5564,8 @@ def register_poll_handler(self, callback: Callable, func: Callable, pass_bot: Op :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5491,7 +5573,9 @@ def register_poll_handler(self, callback: Callable, func: Callable, pass_bot: Op def poll_answer_handler(self, func=None, **kwargs): """ - Poll_answer request handler + Handles change of user's answer in a non-anonymous poll(when user changes the vote). + Bots receive new votes only in polls that were sent by the bot itself. + As a parameter to the decorator function, it passes :class:`telebot.types.PollAnswer` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5532,6 +5616,8 @@ def register_poll_answer_handler(self, callback: Callable, func: Callable, pass_ :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5539,8 +5625,9 @@ def register_poll_answer_handler(self, callback: Callable, func: Callable, pass_ def my_chat_member_handler(self, func=None, **kwargs): """ - The bot's chat member status was updated in a chat. For private chats, + Handles update in a status of a bot. For private chats, this update is received only when the bot is blocked or unblocked by the user. + As a parameter to the decorator function, it passes :class:`telebot.types.ChatMemberUpdated` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5581,6 +5668,8 @@ def register_my_chat_member_handler(self, callback: Callable, func: Optional[Cal :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5588,8 +5677,10 @@ def register_my_chat_member_handler(self, callback: Callable, func: Optional[Cal def chat_member_handler(self, func=None, **kwargs): """ - A chat member's status was updated in a chat. The bot must be an administrator - in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. + Handles update in a status of a user in a chat. + The bot must be an administrator in the chat and must explicitly specify “chat_member” + in the list of allowed_updates to receive these updates. + As a parameter to the decorator function, it passes :class:`telebot.types.ChatMemberUpdated` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5639,8 +5730,9 @@ def register_chat_member_handler(self, callback: Callable, func: Optional[Callab def chat_join_request_handler(self, func=None, **kwargs): """ - A request to join the chat has been sent. The bot must have the can_invite_users + Handles a request to join the chat has been sent. The bot must have the can_invite_users administrator right in the chat to receive these updates. + As a parameter to the decorator function, it passes :class:`telebot.types.ChatJoinRequest` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -5681,6 +5773,8 @@ def register_chat_join_request_handler(self, callback: Callable, func: Optional[ :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -5707,6 +5801,15 @@ def add_custom_filter(self, custom_filter: Union[SimpleCustomFilter, AdvancedCus """ Create custom filter. + .. code-block:: python3 + :caption: Example on checking the text of a message + + class TextMatchFilter(AdvancedCustomFilter): + key = 'text' + + async def check(self, message, text): + return text == message.text + :param custom_filter: Class with check(message) method. :param custom_filter: Custom filter class with key. """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2241316ae..569c1d96f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5,7 +5,7 @@ import re import time import traceback -from typing import Any, List, Optional, Union +from typing import Any, Awaitable, Callable, List, Optional, Union # this imports are used to avoid circular import error import telebot.util @@ -13,7 +13,7 @@ # storages -from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage +from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage, StateStorageBase from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate, SkipHandler, State from inspect import signature @@ -71,18 +71,38 @@ class AsyncTeleBot: Usage: - .. code-block:: python + .. code-block:: python3 + :caption: Using asynchronous implementation of TeleBot. from telebot.async_telebot import AsyncTeleBot bot = AsyncTeleBot('token') # get token from @BotFather + # now you can register other handlers/update listeners, + # and use bot methods. + # Remember to use async/await keywords when necessary. See more examples in examples/ directory: https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples + + :param token: Token of a bot, obtained from @BotFather + :type token: :obj:`str` + + :param parse_mode: Default parse mode, defaults to None + :type parse_mode: :obj:`str`, optional + + :param offset: Offset used in get_updates, defaults to None + :type offset: :obj:`int`, optional + + :param exception_handler: Exception handler, which will handle the exception occured, defaults to None + :type exception_handler: Optional[ExceptionHandler], optional + + :param state_storage: Storage for states, defaults to StateMemoryStorage() + :type state_storage: :class:`telebot.asyncio_storage.StateMemoryStorage`, optional + """ - def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, - exception_handler=None, state_storage=StateMemoryStorage()) -> None: # TODO: ADD TYPEHINTS + def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[int]=None, + exception_handler: Optional[ExceptionHandler]=None, state_storage: Optional[StateStorageBase]=StateMemoryStorage()) -> None: self.token = token self.offset = offset @@ -118,43 +138,73 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset=None, async def close_session(self): """ Closes existing session of aiohttp. - Use this function if you stop polling. + Use this function if you stop polling/webhooks. """ await asyncio_helper.session_manager.session.close() async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, timeout: Optional[int]=20, allowed_updates: Optional[List]=None, request_timeout: Optional[int]=None) -> List[types.Update]: """ - Use this method to receive incoming updates using long polling (wiki). - An Array of Update objects is returned. + Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. + + Telegram documentation: https://core.telegram.org/bots/api#getupdates + - Telegram documentation: https://core.telegram.org/bots/api#making-requests + :param offset: Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. + By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset + higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from the end of the updates queue. + All previous updates will forgotten. + :type offset: :obj:`int`, optional + + :param limit: Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100. + :type limit: :obj:`int`, optional + + :param timeout: Request connection timeout + :type timeout: :obj:`int`, optional :param allowed_updates: Array of string. List the types of updates you want your bot to receive. - :param offset: Integer. Identifier of the first update to be returned. - :param limit: Integer. Limits the number of updates to be retrieved. - :param timeout: Integer. Request connection timeout - :param request_timeout: Timeout in seconds for a request. - :return: array of Updates + :type allowed_updates: :obj:`list`, optional + + :param long_polling_timeout: Timeout in seconds for long polling. + :type long_polling_timeout: :obj:`int`, optional + + :return: An Array of Update objects is returned. + :rtype: :obj:`list` of :class:`telebot.types.Update` """ json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, - request_timeout: int=None, allowed_updates: Optional[List[str]]=None, + request_timeout: Optional[int]=None, allowed_updates: Optional[List[str]]=None, none_stop: Optional[bool]=None): """ - This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. + Runs bot in long-polling mode in a main loop. + This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly. Warning: Do not call this function more than once! - Always get updates. + Always gets updates. + + .. note:: + + Set non_stop=True if you want your bot to continue receiving updates + if there is an error. - :param interval: Delay between two update retrivals :param non_stop: Do not stop polling when an ApiException occurs. - :param timeout: Request connection timeout + :type non_stop: :obj:`bool` + :param skip_pending: skip old updates - :param request_timeout: Timeout in seconds for a request. + :type skip_pending: :obj:`bool` + + :param interval: Delay between two update retrivals + :type interval: :obj:`int` + + :param timeout: Request connection timeout + :type timeout: :obj:`int` + + :param request_timeout: Timeout in seconds for get_updates(Defaults to None) + :type request_timeout: :obj:`int` + :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -163,7 +213,11 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= 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 + :type allowed_updates: :obj:`list` of :obj:`str` + + :param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility. + :type none_stop: :obj:`bool` + :return: """ if none_stop is not None: @@ -174,16 +228,24 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= await self.skip_updates() await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) - async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, request_timeout: int=None, logger_level=logging.ERROR, - allowed_updates: Optional[List[str]]=None, *args, **kwargs): + async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, request_timeout: Optional[int]=None, + logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. - :param timeout: Request connection timeout - :param request_timeout: Timeout in seconds for long polling (see API docs) + :param timeout: Timeout in seconds for get_updates(Defaults to None) + :type timeout: :obj:`int` + :param skip_pending: skip old updates + :type skip_pending: :obj:`bool` + + :param request_timeout: Aiohttp's request timeout. Defaults to 5 minutes(aiohttp.ClientTimeout). + :type request_timeout: :obj:`int` + :param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging + :type logger_level: :obj:`int` + :param allowed_updates: A list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See util.update_types for a complete list of available update types. @@ -192,13 +254,16 @@ async def infinity_polling(self, timeout: int=20, skip_pending: bool=False, requ 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. + :type allowed_updates: :obj:`list` of :obj:`str` + + :return: None """ if skip_pending: await self.skip_updates() self._polling = True while self._polling: try: - await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, + await self._process_polling(non_stop=False, timeout=timeout, request_timeout=request_timeout, allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: @@ -370,13 +435,16 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares, up logger.error('Middleware {} does not have post_process_{} method. post_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) else: await middleware.post_process(message, data, handler_error) # update handling - async def process_new_updates(self, updates): + async def process_new_updates(self, updates: List[types.Update]): """ Process new updates. Just pass list of updates - each update should be instance of Update object. :param updates: list of updates + :type updates: :obj:`list` of :obj:`telebot.types.Update` + + :return: None """ upd_count = len(updates) logger.info('Received {0} new updates'.format(upd_count)) @@ -471,49 +539,94 @@ async def process_new_updates(self, updates): await self.process_chat_join_request(chat_join_request) async def process_new_messages(self, new_messages): + """ + :meta private: + """ await self.__notify_update(new_messages) await self._process_updates(self.message_handlers, new_messages, 'message') async def process_new_edited_messages(self, edited_message): + """ + :meta private: + """ await self._process_updates(self.edited_message_handlers, edited_message, 'edited_message') async def process_new_channel_posts(self, channel_post): + """ + :meta private: + """ await self._process_updates(self.channel_post_handlers, channel_post , 'channel_post') async def process_new_edited_channel_posts(self, edited_channel_post): + """ + :meta private: + """ await self._process_updates(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') async def process_new_inline_query(self, new_inline_querys): + """ + :meta private: + """ await self._process_updates(self.inline_handlers, new_inline_querys, 'inline_query') async def process_new_chosen_inline_query(self, new_chosen_inline_querys): + """ + :meta private: + """ await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') async def process_new_callback_query(self, new_callback_querys): + """ + :meta private: + """ await self._process_updates(self.callback_query_handlers, new_callback_querys, 'callback_query') async def process_new_shipping_query(self, new_shipping_querys): + """ + :meta private: + """ await self._process_updates(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') async def process_new_pre_checkout_query(self, pre_checkout_querys): + """ + :meta private: + """ await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') async def process_new_poll(self, polls): + """ + :meta private: + """ await self._process_updates(self.poll_handlers, polls, 'poll') async def process_new_poll_answer(self, poll_answers): + """ + :meta private: + """ await self._process_updates(self.poll_answer_handlers, poll_answers, 'poll_answer') async def process_new_my_chat_member(self, my_chat_members): + """ + :meta private: + """ await self._process_updates(self.my_chat_member_handlers, my_chat_members, 'my_chat_member') async def process_new_chat_member(self, chat_members): + """ + :meta private: + """ await self._process_updates(self.chat_member_handlers, chat_members, 'chat_member') async def process_chat_join_request(self, chat_join_request): + """ + :meta private: + """ await self._process_updates(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') async def process_middlewares(self, update_type): + """ + :meta private: + """ if self.middlewares: middlewares = [middleware for middleware in self.middlewares if update_type in middleware.update_types] return middlewares @@ -542,19 +655,43 @@ async def _test_message_handler(self, message_handler, message): return True - def set_update_listener(self, func): + def set_update_listener(self, func: Awaitable): """ Update listener is a function that gets any update. :param func: function that should get update. + :type func: :obj:`Awaitable` + + .. code-block:: python3 + :caption: Example on asynchronous update listeners. + + async def update_listener(new_messages): + for message in new_messages: + print(message.text) # Prints message text + + bot.set_update_listener(update_listener) + + :return: None """ self.update_listener.append(func) - def add_custom_filter(self, custom_filter): + def add_custom_filter(self, custom_filter: Union[asyncio_filters.SimpleCustomFilter, asyncio_filters.AdvancedCustomFilter]): """ Create custom filter. - custom_filter: Class with check(message) method. + .. code-block:: python3 + :caption: Example on checking the text of a message + + class TextMatchFilter(AdvancedCustomFilter): + key = 'text' + + async def check(self, message, text): + return text == message.text + + :param custom_filter: Class with check(message) method. + :type custom_filter: :class:`telebot.asyncio_filters.SimpleCustomFilter` or :class:`telebot.asyncio_filters.AdvancedCustomFilter` + + :return: None """ self.custom_filters[custom_filter.key] = custom_filter @@ -613,8 +750,14 @@ def setup_middleware(self, middleware: BaseMiddleware): """ Setup middleware. + .. note:: + + Take a look at the :class:`telebot.asyncio_handler_backends.BaseMiddleware` section for more. + :param middleware: Middleware-class. - :return: + :type middleware: :class:`telebot.middlewares.BaseMiddleware` + + :return: None """ if not hasattr(middleware, 'update_types'): logger.error('Middleware has no update_types parameter. Please add list of updates to handle.') @@ -628,44 +771,58 @@ def setup_middleware(self, middleware: BaseMiddleware): def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ - Message handler decorator. - This decorator can be used to decorate functions that must handle certain types of messages. + Handles ew incoming message of any kind - text, photo, sticker, etc. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. All message handlers are tested in the order they were added. Example: - .. code-block:: python + .. code-block:: python3 + :caption: Usage of message_handler bot = TeleBot('TOKEN') # Handles all messages which text matches regexp. @bot.message_handler(regexp='someregexp') async def command_help(message): - bot.send_message(message.chat.id, 'Did someone call for help?') + await bot.send_message(message.chat.id, 'Did someone call for help?') # Handles messages in private chat @bot.message_handler(chat_types=['private']) # You can add more chat types async def command_help(message): - bot.send_message(message.chat.id, 'Private chat detected, sir!') + await bot.send_message(message.chat.id, 'Private chat detected, sir!') # Handle all sent documents of type 'text/plain'. @bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document']) async def command_handle_document(message): - bot.send_message(message.chat.id, 'Document received, sir!') + await bot.send_message(message.chat.id, 'Document received, sir!') # Handle all other messages. @bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', 'text', 'location', 'contact', 'sticker']) - async def async default_command(message): - bot.send_message(message.chat.id, "This is the async default command handler.") + async def default_command(message): + await bot.send_message(message.chat.id, "This is the default command handler.") :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. - :param content_types: Supported message content types. Must be a list. async defaults to ['text']. + + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param chat_types: list of chat types + :type chat_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: decorated function """ if content_types is None: @@ -696,24 +853,43 @@ def add_message_handler(self, handler_dict): """ Adds a message handler. Note that you should use register_message_handler to add message_handler. + + :meta private: :param handler_dict: :return: """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): + def register_message_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + regexp: Optional[str]=None, func: Optional[Callable]=None, chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message handler. :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`Awaitable` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands + :type commands: :obj:`list` of :obj:`str` + :param regexp: - :param func: - :param chat_types: True for private chat - :param pass_bot: True if you want to get TeleBot instance in your handler - :return: decorated function + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param chat_types: List of chat types + :type chat_types: :obj:`list` of :obj:`str` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ if content_types is None: content_types = ["text"] @@ -737,15 +913,28 @@ def register_message_handler(self, callback, content_types=None, commands=None, def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ - Edit message handler decorator. + Handles new version of a message that is known to the bot and was edited. + + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` - :param commands: - :param regexp: - :param func: - :param content_types: :param chat_types: list of chat types - :param kwargs: - :return: + :type chat_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ if content_types is None: @@ -777,23 +966,43 @@ def add_edited_message_handler(self, handler_dict): Adds the edit message handler. Note that you should use register_edited_message_handler to add edited_message_handler. + :meta private: + :param handler_dict: :return: """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, chat_types=None, pass_bot=False, **kwargs): + def register_edited_message_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, + commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, + chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers edited message handler. - :param pass_bot: :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`Awaitable` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands - :param regexp: - :param func: + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + :param chat_types: True for private chat - :return: decorated function + :type chat_types: :obj:`bool` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ if isinstance(commands, str): logger.warning("register_edited_message_handler: 'commands' filter should be List of strings (commands), not string.") @@ -816,14 +1025,23 @@ def register_edited_message_handler(self, callback, content_types=None, commands def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ - Channel post handler decorator. + Handles new incoming channel post of any kind - text, photo, sticker, etc. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. - :param commands: - :param regexp: - :param func: - :param content_types: - :param kwargs: - :return: + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ if content_types is None: content_types = ["text"] @@ -853,22 +1071,39 @@ def add_channel_post_handler(self, handler_dict): Adds channel post handler. Note that you should use register_channel_post_handler to add channel_post_handler. + :meta private: + :param handler_dict: :return: """ self.channel_post_handlers.append(handler_dict) - def register_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): + def register_channel_post_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers channel post message handler. - :param pass_bot: :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`Awaitable` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands - :param regexp: - :param func: - :return: decorated function + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ if isinstance(commands, str): logger.warning("register_channel_post_handler: 'commands' filter should be List of strings (commands), not string.") @@ -889,13 +1124,23 @@ def register_channel_post_handler(self, callback, content_types=None, commands=N def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ - Edit channel post handler decorator. + Handles new version of a channel post that is known to the bot and was edited. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) - :param commands: - :param regexp: - :param func: - :param content_types: - :param kwargs: :return: """ if content_types is None: @@ -926,21 +1171,38 @@ def add_edited_channel_post_handler(self, handler_dict): Adds the edit channel post handler. Note that you should use register_edited_channel_post_handler to add edited_channel_post_handler. + :meta private: + :param handler_dict: :return: """ self.edited_channel_post_handlers.append(handler_dict) - def register_edited_channel_post_handler(self, callback, content_types=None, commands=None, regexp=None, func=None, pass_bot=False, **kwargs): + def register_edited_channel_post_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, + commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers edited channel post message handler. - :param pass_bot: :param callback: function to be called - :param content_types: list of content_types + :type callback: :obj:`Awaitable` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + :param commands: list of commands - :param regexp: - :param func: + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + :return: decorated function """ if isinstance(commands, str): @@ -962,11 +1224,15 @@ def register_edited_channel_post_handler(self, callback, content_types=None, com def inline_handler(self, func, **kwargs): """ - Inline call handler decorator. + Handles new incoming inline query. + As a parameter to the decorator function, it passes :class:`telebot.types.InlineQuery` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -981,18 +1247,28 @@ def add_inline_handler(self, handler_dict): Adds inline call handler. Note that you should use register_inline_handler to add inline_handler. + :meta private: + :param handler_dict: :return: """ self.inline_handlers.append(handler_dict) - def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): + def register_inline_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers inline handler. - :param pass_bot: :param callback: function to be called - :param func: + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + :return: decorated function """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -1000,12 +1276,16 @@ def register_inline_handler(self, callback, func, pass_bot=False, **kwargs): def chosen_inline_handler(self, func, **kwargs): """ + The result of an inline query that was chosen by a user and sent to their chat partner. + Please see our documentation on the feedback collecting for details on how to enable these updates for your bot. + As a parameter to the decorator function, it passes :class:`telebot.types.ChosenInlineResult` object. - Description: TBD + :param func: Function executed as a filter + :type func: :obj:`function` - :param func: - :param kwargs: - :return: + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1020,30 +1300,44 @@ def add_chosen_inline_handler(self, handler_dict): Description: TBD Note that you should use register_chosen_inline_handler to add chosen_inline_handler. + :meta private: + :param handler_dict: :return: """ self.chosen_inline_handlers.append(handler_dict) - def register_chosen_inline_handler(self, callback, func, pass_bot=False, **kwargs): + def register_chosen_inline_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers chosen inline handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chosen_inline_handler(handler_dict) def callback_query_handler(self, func, **kwargs): """ - Callback request handler decorator. + Handles new incoming callback query. + As a parameter to the decorator function, it passes :class:`telebot.types.CallbackQuery` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1058,30 +1352,44 @@ def add_callback_query_handler(self, handler_dict): Adds a callback request handler. Note that you should use register_callback_query_handler to add callback_query_handler. + :meta private: + :param handler_dict: :return: """ self.callback_query_handlers.append(handler_dict) - def register_callback_query_handler(self, callback, func, pass_bot=False, **kwargs): + def register_callback_query_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers callback query handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_callback_query_handler(handler_dict) def shipping_query_handler(self, func, **kwargs): """ - Shipping request handler. + Handles new incoming shipping query. Only for invoices with flexible price. + As a parameter to the decorator function, it passes :class:`telebot.types.ShippingQuery` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1096,30 +1404,44 @@ def add_shipping_query_handler(self, handler_dict): Adds a shipping request handler. Note that you should use register_shipping_query_handler to add shipping_query_handler. + :meta private: + :param handler_dict: :return: """ self.shipping_query_handlers.append(handler_dict) - def register_shipping_query_handler(self, callback, func, pass_bot=False, **kwargs): + def register_shipping_query_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers shipping query handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_shipping_query_handler(handler_dict) def pre_checkout_query_handler(self, func, **kwargs): """ - Pre-checkout request handler. + New incoming pre-checkout query. Contains full information about checkout. + As a parameter to the decorator function, it passes :class:`telebot.types.PreCheckoutQuery` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1134,18 +1456,27 @@ def add_pre_checkout_query_handler(self, handler_dict): Adds a pre-checkout request handler. Note that you should use register_pre_checkout_query_handler to add pre_checkout_query_handler. + :meta private: + :param handler_dict: :return: """ self.pre_checkout_query_handlers.append(handler_dict) - def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, **kwargs): + def register_pre_checkout_query_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers pre-checkout request handler. - :param pass_bot: :param callback: function to be called - :param func: + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + :return: decorated function """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) @@ -1153,11 +1484,14 @@ def register_pre_checkout_query_handler(self, callback, func, pass_bot=False, ** def poll_handler(self, func, **kwargs): """ - Poll request handler. + Handles new state of a poll. Bots receive only updates about stopped polls and polls, which are sent by the bot + As a parameter to the decorator function, it passes :class:`telebot.types.Poll` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None """ def decorator(handler): @@ -1172,30 +1506,45 @@ def add_poll_handler(self, handler_dict): Adds a poll request handler. Note that you should use register_poll_handler to add poll_handler. + :meta private: + :param handler_dict: :return: """ self.poll_handlers.append(handler_dict) - def register_poll_handler(self, callback, func, pass_bot=False, **kwargs): + def register_poll_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_handler(handler_dict) def poll_answer_handler(self, func=None, **kwargs): """ - Poll_answer request handler. + Handles change of user's answer in a non-anonymous poll(when user changes the vote). + Bots receive new votes only in polls that were sent by the bot itself. + As a parameter to the decorator function, it passes :class:`telebot.types.PollAnswer` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1210,30 +1559,45 @@ def add_poll_answer_handler(self, handler_dict): Adds a poll_answer request handler. Note that you should use register_poll_answer_handler to add poll_answer_handler. + :meta private: + :param handler_dict: :return: """ self.poll_answer_handlers.append(handler_dict) - def register_poll_answer_handler(self, callback, func, pass_bot=False, **kwargs): + def register_poll_answer_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll answer handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_answer_handler(handler_dict) def my_chat_member_handler(self, func=None, **kwargs): """ - my_chat_member handler. + Handles update in a status of a bot. For private chats, + this update is received only when the bot is blocked or unblocked by the user. + As a parameter to the decorator function, it passes :class:`telebot.types.ChatMemberUpdated` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1248,30 +1612,46 @@ def add_my_chat_member_handler(self, handler_dict): Adds a my_chat_member handler. Note that you should use register_my_chat_member_handler to add my_chat_member_handler. + :meta private: + :param handler_dict: :return: """ self.my_chat_member_handlers.append(handler_dict) - def register_my_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): + def register_my_chat_member_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot: Optional[Callable]=False, **kwargs): """ Registers my chat member handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_my_chat_member_handler(handler_dict) def chat_member_handler(self, func=None, **kwargs): """ - chat_member handler. + Handles update in a status of a user in a chat. + The bot must be an administrator in the chat and must explicitly specify “chat_member” + in the list of allowed_updates to receive these updates. + As a parameter to the decorator function, it passes :class:`telebot.types.ChatMemberUpdated` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1286,30 +1666,45 @@ def add_chat_member_handler(self, handler_dict): Adds a chat_member handler. Note that you should use register_chat_member_handler to add chat_member_handler. + :meta private: + :param handler_dict: :return: """ self.chat_member_handlers.append(handler_dict) - def register_chat_member_handler(self, callback, func=None, pass_bot=False, **kwargs): + def register_chat_member_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers chat member handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable`` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return:None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_member_handler(handler_dict) def chat_join_request_handler(self, func=None, **kwargs): """ - chat_join_request handler. + Handles a request to join the chat has been sent. The bot must have the can_invite_users + administrator right in the chat to receive these updates. + As a parameter to the decorator function, it passes :class:`telebot.types.ChatJoinRequest` object. - :param func: - :param kwargs: - :return: + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ def decorator(handler): @@ -1324,19 +1719,29 @@ def add_chat_join_request_handler(self, handler_dict): Adds a chat_join_request handler. Note that you should use register_chat_join_request_handler to add chat_join_request_handler. + :meta private: + :param handler_dict: :return: """ self.chat_join_request_handlers.append(handler_dict) - def register_chat_join_request_handler(self, callback, func=None, pass_bot=False, **kwargs): + def register_chat_join_request_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): """ Registers chat join request handler. - :param pass_bot: :param callback: function to be called - :param func: - :return: decorated function + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_join_request_handler(handler_dict) @@ -1377,7 +1782,7 @@ async def get_me(self) -> types.User: result = await asyncio_helper.get_me(self.token) return types.User.de_json(result) - async def get_file(self, file_id: str) -> types.File: + async def get_file(self, file_id: Optional[str]) -> types.File: """ Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. @@ -1387,15 +1792,35 @@ async def get_file(self, file_id: str) -> types.File: Telegram documentation: https://core.telegram.org/bots/api#getfile - :param file_id: + :param file_id: File identifier + :type file_id: :obj:`str` + + :return: :class:`telebot.types.File` """ return types.File.de_json(await asyncio_helper.get_file(self.token, file_id)) - async def get_file_url(self, file_id: str) -> str: - + async def get_file_url(self, file_id: Optional[str]) -> str: + """ + Get a valid URL for downloading a file. + + :param file_id: File identifier to get download URL for. + :type file_id: :obj:`str` + + :return: URL for downloading the file. + :rtype: :obj:`str` + """ return await asyncio_helper.get_file_url(self.token, file_id) - async def download_file(self, file_path: str) -> bytes: + async def download_file(self, file_path: Optional[str]) -> bytes: + """ + Downloads file. + + :param file_path: Path where the file should be downloaded. + :type file_path: str + + :return: bytes + :rtype: :obj:`bytes` + """ return await asyncio_helper.download_file(self.token, file_path) async def log_out(self) -> bool: @@ -1408,6 +1833,9 @@ async def log_out(self) -> bool: Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#logout + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.log_out(self.token) @@ -1420,6 +1848,8 @@ async def close(self) -> bool: Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#close + + :return: :obj:`bool` """ return await asyncio_helper.close(self.token) @@ -1427,39 +1857,68 @@ def enable_saving_states(self, filename="./.state-save/states.pkl"): """ Enable saving states (by default saving disabled) - :param filename: Filename of saving file + .. note:: + It is recommended to pass a :class:`~telebot.asyncio_storage.StatePickleStorage` instance as state_storage + to TeleBot class. + + :param filename: Filename of saving file, defaults to "./.state-save/states.pkl" + :type filename: :obj:`str`, optional """ self.current_states = StatePickleStorage(file_path=filename) - async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, - drop_pending_updates = None, timeout=None, secret_token=None): + async def set_webhook(self, url: Optional[str]=None, certificate: Optional[Union[str, Any]]=None, max_connections: Optional[int]=None, + allowed_updates: Optional[List[str]]=None, ip_address: Optional[str]=None, + drop_pending_updates: Optional[bool] = None, timeout: Optional[int]=None, + secret_token: Optional[str]=None) -> bool: """ - Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an - update for the bot, we will send an HTTPS POST request to the specified url, - containing a JSON-serialized Update. - In case of an unsuccessful request, we will give up after a reasonable amount of attempts. - Returns True on success. + Use this method to specify a URL and receive incoming updates via an outgoing webhook. + Whenever there is an update for the bot, we will send an HTTPS POST request to the specified URL, + containing a JSON-serialized Update. In case of an unsuccessful request, we will give up after + a reasonable amount of attempts. Returns True on success. + + If you'd like to make sure that the webhook was set by you, you can specify secret data in the parameter secret_token. + If specified, the request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the secret token as content. + + Telegram Documentation: https://core.telegram.org/bots/api#setwebhook + + :param url: HTTPS URL to send updates to. Use an empty string to remove webhook integration, defaults to None + :type url: :obj:`str`, optional + + :param certificate: Upload your public key certificate so that the root certificate in use can be checked, defaults to None + :type certificate: :class:`str`, optional + + :param max_connections: The maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. + Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput, + defaults to None + :type max_connections: :obj:`int`, optional + + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, + specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update + for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). + If not specified, the previous setting will be used. + + Please note that this parameter doesn't affect updates created before the call to the setWebhook, so unwanted updates may be received + for a short period of time. Defaults to None + + :type allowed_updates: :obj:`list`, optional - Telegram documentation: https://core.telegram.org/bots/api#setwebhook - - :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration - :param certificate: Upload your public key certificate so that the root certificate in use can be checked. - See our self-signed guide for details. - :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook - for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, - and higher values to increase your bot's throughput. - :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates - of these types. See Update for a complete list of available update types. - Specify an empty list to receive all updates regardless of type (default). - If not specified, the previous setting will be used. :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address - resolved through DNS - :param drop_pending_updates: Pass True to drop all pending updates - :param timeout: Integer. Request connection timeout - :param secret_token: Secret token to be used to verify the webhook - :return: + resolved through DNS, defaults to None + :type ip_address: :obj:`str`, optional + + :param drop_pending_updates: Pass True to drop all pending updates, defaults to None + :type drop_pending_updates: :obj:`bool`, optional + + :param timeout: Timeout of a request, defaults to None + :type timeout: :obj:`int`, optional + + :param secret_token: A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” in every webhook request, 1-256 characters. + Only characters A-Z, a-z, 0-9, _ and - are allowed. The header is useful to ensure that the request comes from a webhook set by you. Defaults to None + :type secret_token: :obj:`str`, optional + + :return: True on success. + :rtype: :obj:`bool` if the request was successful. """ return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout, secret_token) @@ -1545,33 +2004,43 @@ async def run_webhooks(self, - async def delete_webhook(self, drop_pending_updates=None, timeout=None): + async def delete_webhook(self, drop_pending_updates: Optional[bool]=None, timeout: Optional[int]=None) -> bool: """ Use this method to remove webhook integration if you decide to switch back to getUpdates. + Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletewebhook - :param drop_pending_updates: Pass True to drop all pending updates - :param timeout: Integer. Request connection timeout - :return: bool + :param drop_pending_updates: Pass True to drop all pending updates, defaults to None + :type drop_pending_updates: :obj: `bool`, optional + + :param timeout: Request connection timeout, defaults to None + :type timeout: :obj:`int`, optional + + :return: Returns True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.delete_webhook(self.token, drop_pending_updates, timeout) - async def remove_webhook(self): + async def remove_webhook(self) -> bool: """ Alternative for delete_webhook but uses set_webhook """ - await self.set_webhook() + result = await self.set_webhook() + return result - async def get_webhook_info(self, timeout=None): + async def get_webhook_info(self, timeout: Optional[int]=None) -> types.WebhookInfo: """ Use this method to get current webhook status. Requires no parameters. - If the bot is using getUpdates, will return an object with the url field empty. + On success, returns a WebhookInfo object. If the bot is using getUpdates, will return an object with the url field empty. Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo - :param timeout: Integer. Request connection timeout + :param timeout: Request connection timeout + :type timeout: :obj:`int`, optional + :return: On success, returns a WebhookInfo object. + :rtype: :class:`telebot.types.WebhookInfo` """ result = await asyncio_helper.get_webhook_info(self.token, timeout) return types.WebhookInfo.de_json(result) @@ -1579,14 +2048,23 @@ async def get_webhook_info(self, timeout=None): async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ - Retrieves the user profile photos of the person with 'user_id' + Use this method to get a list of profile pictures for a user. + Returns a :class:`telebot.types.UserProfilePhotos` object. Telegram documentation: https://core.telegram.org/bots/api#getuserprofilephotos - :param user_id: - :param offset: - :param limit: - :return: API reply. + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param offset: Sequential number of the first photo to be returned. By default, all photos are returned. + :type offset: :obj:`int` + + :param limit: Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100. + :type limit: :obj:`int` + + :return: `UserProfilePhotos `_ + :rtype: :class:`telebot.types.UserProfilePhotos` + """ result = await asyncio_helper.get_user_profile_photos(self.token, user_id, offset, limit) return types.UserProfilePhotos.de_json(result) @@ -1598,8 +2076,11 @@ async def get_chat(self, chat_id: Union[int, str]) -> types.Chat: Telegram documentation: https://core.telegram.org/bots/api#getchat - :param chat_id: - :return: + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: Chat information + :rtype: :class:`telebot.types.Chat` """ result = await asyncio_helper.get_chat(self.token, chat_id) return types.Chat.de_json(result) @@ -1610,8 +2091,10 @@ async def leave_chat(self, chat_id: Union[int, str]) -> bool: Telegram documentation: https://core.telegram.org/bots/api#leavechat - :param chat_id: - :return: + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: :obj:`bool` """ result = await asyncio_helper.leave_chat(self.token, chat_id) return result @@ -1622,10 +2105,12 @@ async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types. On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. - Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators + Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators - :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) - :return: API reply. + :param chat_id: Unique identifier for the target chat or username + of the target supergroup or channel (in the format @channelusername) + :return: List made of ChatMember objects. + :rtype: :obj:`list` of :class:`telebot.types.ChatMember` """ result = await asyncio_helper.get_chat_administrators(self.token, chat_id) return [types.ChatMember.de_json(r) for r in result] @@ -1633,35 +2118,55 @@ async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types. @util.deprecated(deprecation_text="Use get_chat_member_count instead") async def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ - This function is deprecated. Use `get_chat_member_count` instead + This function is deprecated. Use `get_chat_member_count` instead. + + .. deprecated:: 4.0.0 + This function is deprecated. Use `get_chat_member_count` instead. + + Use this method to get the number of members in a chat. + + Telegram documentation: https://core.telegram.org/bots/api#getchatmembercount + + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: Number of members in the chat. + :rtype: :obj:`int` """ result = await asyncio_helper.get_chat_member_count(self.token, chat_id) return result async def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ - Use this method to get the number of members in a chat. Returns Int on success. + Use this method to get the number of members in a chat. Telegram documentation: https://core.telegram.org/bots/api#getchatmembercount - :param chat_id: - :return: + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: Number of members in the chat. + :rtype: :obj:`int` """ result = await asyncio_helper.get_chat_member_count(self.token, chat_id) return result async def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: """ - Use this method to set a new group sticker set for a supergroup. The bot must be an administrator - in the chat for this to work and must have the appropriate admin rights. - Use the field can_set_sticker_set optionally returned in getChat requests to check - if the bot can use this method. Returns True on success. - + Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat + for this to work and must have the appropriate administrator rights. Use the field can_set_sticker_set optionally returned + in getChat requests to check if the bot can use this method. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param sticker_set_name: Name of the sticker set to be set as the group sticker set - :return: API reply. + :type sticker_set_name: :obj:`str` + + :return: StickerSet object + :rtype: :class:`telebot.types.StickerSet` """ result = await asyncio_helper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) return result @@ -1673,9 +2178,12 @@ async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: optionally returned in getChat requests to check if the bot can use this method. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset - + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) - :return: API reply. + :type chat_id: :obj:`int` or :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` """ result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) return result @@ -1690,8 +2198,13 @@ async def answer_web_app_query(self, web_app_query_id: str, result: types.Inline Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery :param web_app_query_id: Unique identifier for the query to be answered + :type web_app_query_id: :obj:`str` + :param result: A JSON-serialized object describing the message to be sent - :return: + :type result: :class:`telebot.types.InlineQueryResultBase` + + :return: On success, a SentWebAppMessage object is returned. + :rtype: :class:`telebot.types.SentWebAppMessage` """ return await asyncio_helper.answer_web_app_query(self.token, web_app_query_id, result) @@ -1699,12 +2212,17 @@ async def answer_web_app_query(self, web_app_query_id: str, result: types.Inline async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. - + Telegram documentation: https://core.telegram.org/bots/api#getchatmember - :param chat_id: - :param user_id: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :return: Returns ChatMember object on success. + :rtype: :class:`telebot.types.ChatMember` """ result = await asyncio_helper.get_chat_member(self.token, chat_id, user_id) return types.ChatMember.de_json(result) @@ -1723,24 +2241,48 @@ async def send_message( """ Use this method to send text messages. - Warning: Do not send more than about 4000 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 4000 characters, + Warning: Do not send more than about 4096 characters each message, otherwise you'll risk an HTTP 414 error. + If you must send more than 4096 characters, use the `split_string` or `smart_split` function in util.py. Telegram documentation: https://core.telegram.org/bots/api#sendmessage - :param chat_id: - :param text: - :param disable_web_page_preview: - :param reply_to_message_id: - :param reply_markup: - :param parse_mode: - :param disable_notification: Boolean, Optional. Sends the message silently. - :param timeout: - :param entities: - :param allow_sending_without_reply: - :param protect_content: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param text: Text of the message to be sent + :type text: :obj:`str` + + :param parse_mode: Mode for parsing entities in the message text. + :type parse_mode: :obj:`str` + + :param entities: List of special entities that appear in message text, which can be specified instead of parse_mode + :type entities: Array of :class:`telebot.types.MessageEntity` + + :param disable_web_page_preview: Disables link previews for links in this message + :type disable_web_page_preview: :obj:`bool` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: If True, the message content will be hidden for all users except for the target user + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: 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. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1760,13 +2302,26 @@ async def forward_message( Telegram documentation: https://core.telegram.org/bots/api#forwardmessage - :param disable_notification: - :param chat_id: which chat to forward - :param from_chat_id: which chat message from - :param message_id: message id - :param protect_content: - :param timeout: - :return: API reply. + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + + :param message_id: Message identifier in the chat specified in from_chat_id + :type message_id: :obj:`int` + + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :type protect_content: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) @@ -1789,19 +2344,45 @@ async def copy_message( Telegram documentation: https://core.telegram.org/bots/api#copymessage - :param chat_id: which chat to forward - :param from_chat_id: which chat message from - :param message_id: message id - :param caption: - :param parse_mode: - :param caption_entities: - :param disable_notification: - :param reply_to_message_id: - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :param protect_content: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + :param message_id: Message identifier in the chat specified in from_chat_id + :type message_id: :obj:`int` + + :param caption: New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the new caption. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the new caption, which can be specified instead of parse_mode + :type caption_entities: Array of :class:`telebot.types.MessageEntity` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: 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. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1813,14 +2394,29 @@ async def copy_message( async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: """ - Use this method to delete message. Returns True on success. + Use this method to delete a message, including service messages, with the following limitations: + - A message can only be deleted if it was sent less than 48 hours ago. + - A dice message in a private chat can only be deleted if it was sent more than 24 hours ago. + - Bots can delete outgoing messages in private chats, groups, and supergroups. + - Bots can delete incoming messages in private chats. + - Bots granted can_post_messages permissions can delete outgoing messages in channels. + - If the bot is an administrator of a group, it can delete any message there. + - If the bot has can_delete_messages permission in a supergroup or a channel, it can delete any message there. + Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemessage - :param chat_id: in which chat to delete - :param message_id: which message to delete - :param timeout: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of the message to delete + :type message_id: :obj:`int` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.delete_message(self.token, chat_id, message_id, timeout) @@ -1833,19 +2429,39 @@ async def send_dice( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send dices. + Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#senddice - :param chat_id: - :param emoji: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param protect_content: - :return: Message + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, or “🎰”. + Dice can have values 1-6 for “🎲”, “🎯” and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults to “🎲” + :type emoji: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( await asyncio_helper.send_dice( @@ -1864,22 +2480,49 @@ async def send_photo( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None,) -> types.Message: """ - Use this method to send photos. + Use this method to send photos. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendphoto + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` - :param chat_id: - :param photo: - :param caption: - :param parse_mode: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :param protect_content: - :return: API reply. + :param photo: Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), + pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data. + The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20. + :type photo: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Photo caption (may also be used when resending photos by file_id), 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the photo caption. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: 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. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1904,26 +2547,66 @@ async def send_audio( protect_content: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. - Your audio must be in the .mp3 format. + Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, + this limit may be changed in the future. + + For sending voice messages, use the send_voice method instead. Telegram documentation: https://core.telegram.org/bots/api#sendaudio + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param audio: Audio file to send. Pass a file_id as String to send an audio file that exists on the Telegram servers (recommended), + pass an HTTP URL as a String for Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data. + Audio must be in the .MP3 or .M4A format. + :type audio: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Audio caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` - :param chat_id: Unique identifier for the message recipient - :param audio: Audio file to send. - :param caption: :param duration: Duration of the audio in seconds + :type duration: :obj:`int` + :param performer: Performer + :type performer: :obj:`str` + :param title: Track name + :type title: :obj:`str` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :param reply_markup: - :param parse_mode: - :param disable_notification: - :param timeout: - :param thumb: - :param caption_entities: - :param allow_sending_without_reply: - :param protect_content: - :return: Message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param parse_mode: Mode for parsing entities in the audio caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param thumb: 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 + :type thumb: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1945,24 +2628,52 @@ async def send_voice( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send audio files, if you want Telegram clients to display the file - as a playable voice 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. Telegram documentation: https://core.telegram.org/bots/api#sendvoice + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` - :param chat_id: Unique identifier for the message recipient. - :param voice: - :param caption: - :param duration: Duration of sent audio in seconds - :param reply_to_message_id: - :param reply_markup: - :param parse_mode: - :param disable_notification: - :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :param protect_content: - :return: Message + :param voice: Audio 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. + :type voice: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Voice message caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param duration: Duration of the voice message in seconds + :type duration: :obj:`int` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param parse_mode: Mode for parsing entities in the voice message caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1991,23 +2702,57 @@ async def send_document( Use this method to send general files. Telegram documentation: https://core.telegram.org/bots/api#senddocument - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :param document: (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 + :type chat_id: :obj:`int` or :obj:`str` + + :param document: (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 + :type document: :obj:`str` or :class:`telebot.types.InputFile` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :param caption: Document caption (may also be used when resending documents by file_id), 0-1024 characters after entities parsing - :param reply_markup: + :type caption: :obj:`str` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + :param parse_mode: Mode for parsing entities in the document caption + :type parse_mode: :obj:`str` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param timeout: + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :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 async define file name that will be visible in the Telegram instead of original file name + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param allow_sending_without_reply: 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 visible_file_name: allows to define file name that will be visible in the Telegram instead of original file name + :type visible_file_name: :obj:`str` + :param disable_content_type_detection: Disables automatic server-side content type detection for files uploaded using multipart/form-data - :param data: function typo compatibility: do not use it - :param protect_content: - :return: API reply. + :type disable_content_type_detection: :obj:`bool` + + :param data: function typo miss compatibility: do not use it + :type data: :obj:`str` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode if data and not(document): @@ -2032,20 +2777,43 @@ async def send_sticker( protect_content: Optional[bool]=None, data: Union[Any, str]=None) -> types.Message: """ - Use this method to send .webp stickers. + Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. + On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendsticker - :param chat_id: - :param sticker: - :param reply_to_message_id: - :param reply_markup: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param sticker: Sticker 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 .webp file from the Internet, or upload a new one using multipart/form-data. + :type sticker: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + :param disable_notification: to disable the notification - :param timeout: timeout - :param allow_sending_without_reply: - :param protect_content: - :param data: deprecated, for backward compatibility - :return: API reply. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param data: function typo miss compatibility: do not use it + :type data: :obj:`str` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ if data and not(sticker): # function typo miss compatibility @@ -2082,22 +2850,60 @@ async def send_video( Telegram documentation: https://core.telegram.org/bots/api#sendvideo :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param video: Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video file using multipart/form-data. + :type video: :obj:`str` or :class:`telebot.types.InputFile` + :param duration: Duration of sent video in seconds + :type duration: :obj:`int` + :param width: Video width + :type width: :obj:`int` + :param height: Video height + :type height: :obj:`int` + :param thumb: 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 . + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing + :type caption: :obj:`str` + :param parse_mode: Mode for parsing entities in the video caption - :param caption_entities: + :type parse_mode: :obj:`str` + + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param supports_streaming: Pass True, if the uploaded video is suitable for streaming + :type supports_streaming: :obj:`bool` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param protect_content: + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + :param reply_to_message_id: If the message is a reply, ID of the original message - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :param data: deprecated, for backward compatibility + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: 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. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param data: function typo miss compatibility: do not use it + :type data: :obj:`str` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode if data and not(video): @@ -2128,26 +2934,63 @@ async def send_animation( timeout: Optional[int]=None, ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). + On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. Telegram documentation: https://core.telegram.org/bots/api#sendanimation + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param animation: Animation to send. Pass a file_id as String to send an animation that exists on the Telegram servers (recommended), + pass an HTTP URL as a String for Telegram to get an animation from the Internet, or upload a new animation using multipart/form-data. + :type animation: :obj:`str` or :class:`telebot.types.InputFile` + + :param duration: Duration of sent animation in seconds + :type duration: :obj:`int` + + :param width: Animation width + :type width: :obj:`int` + + :param height: Animation height + :type height: :obj:`int` - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an - animation that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param width: Integer : Video width - :param height: Integer : Video height - :param thumb: InputFile or String : Thumbnail of the file sent - :param caption: String : Animation caption (may also be used when resending animation by file_id). - :param parse_mode: - :param protect_content: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param caption_entities: - :param allow_sending_without_reply: - :return: + :param thumb: 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 . + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param caption: Animation caption (may also be used when resending animation by file_id), 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the animation caption + :type parse_mode: :obj:`str` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param allow_sending_without_reply: 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` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2169,24 +3012,52 @@ async def send_video_note( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send - video messages. + As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. + Use this method to send video messages. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendvideonote + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param data: Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) + or upload a new video using multipart/form-data. Sending video notes by a URL is currently unsupported + :type data: :obj:`str` or :class:`telebot.types.InputFile` - :param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id - :param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend - a video that is already on the Telegram server - :param duration: Integer : Duration of sent video in seconds - :param length: Integer : Video width and height, Can't be None and should be in range of (0, 640) - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param thumb: InputFile or String : Thumbnail of the file sent - :param allow_sending_without_reply: - :param protect_content: - :return: + :param duration: Duration of sent video in seconds + :type duration: :obj:`int` + + :param length: Video width and height, i.e. diameter of the video message + :type length: :obj:`int` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param thumb: 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 . + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( await asyncio_helper.send_video_note( @@ -2204,18 +3075,34 @@ async def send_media_group( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: """ - send a group of photos or videos as an album. On success, an array of the sent Messages is returned. + Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files + can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup - - :param chat_id: - :param media: - :param disable_notification: - :param reply_to_message_id: - :param timeout: - :param allow_sending_without_reply: - :param protect_content: - :return: + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param media: A JSON-serialized array describing messages to be sent, must include 2-10 items + :type media: :obj:`list` of :obj:`types.InputMedia` + + :param disable_notification: Sends the messages silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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` + + :return: On success, an array of Messages that were sent is returned. + :rtype: List[types.Message] """ result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, @@ -2235,27 +3122,54 @@ async def send_location( proximity_alert_radius: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: - - """ - Use this method to send point on the map. + Use this method to send point on the map. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendlocation - :param chat_id: - :param latitude: - :param longitude: - :param live_period: - :param reply_to_message_id: - :param reply_markup: - :param disable_notification: - :param timeout: - :param horizontal_accuracy: - :param heading: - :param proximity_alert_radius: - :param allow_sending_without_reply: - :param protect_content: - :return: API reply. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param latitude: Latitude of the location + :type latitude: :obj:`float` + + :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. + :type live_period: :obj:`int` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param horizontal_accuracy: The radius of uncertainty for the location, measured in meters; 0-1500 + :type horizontal_accuracy: :obj:`float` + + :param heading: 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: 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 allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( await asyncio_helper.send_location( @@ -2275,21 +3189,45 @@ async def edit_message_live_location( heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None) -> types.Message: """ - Use this method to edit live location. + 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 + is returned, otherwise True is returned. Telegram documentation: https://core.telegram.org/bots/api#editmessagelivelocation - :param latitude: - :param longitude: - :param chat_id: - :param message_id: - :param reply_markup: - :param timeout: - :param inline_message_id: - :param horizontal_accuracy: - :param heading: - :param proximity_alert_radius: - :return: + :param latitude: Latitude of new location + :type latitude: :obj:`float` + + :param longitude: Longitude of new location + :type longitude: :obj:`float` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the message to edit + :type message_id: :obj:`int` + + :param reply_markup: A JSON-serialized object for a new inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param horizontal_accuracy: The radius of uncertainty for the location, measured in meters; 0-1500 + :type horizontal_accuracy: :obj:`float` + + :param heading: 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: 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` + + :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 """ return types.Message.de_json( await asyncio_helper.edit_message_live_location( @@ -2304,17 +3242,29 @@ async def stop_message_live_location( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None) -> types.Message: """ - Use this method to stop updating a live location message sent by the bot - or via the bot (for inline bots) before live_period expires. + Use this method to stop updating a live location message before live_period expires. + On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. Telegram documentation: https://core.telegram.org/bots/api#stopmessagelivelocation + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :param timeout: - :return: + :param message_id: Required if inline_message_id is not specified. Identifier of the message with live location to stop + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message with live location to stop + :type inline_message_id: :obj:`str` + + :param reply_markup: A JSON-serialized object for a new inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. + :rtype: :class:`telebot.types.Message` or bool """ return types.Message.de_json( await asyncio_helper.stop_message_live_location( @@ -2335,27 +3285,61 @@ async def send_venue( google_place_type: Optional[str]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send information about a venue. - + Use this method to send information about a venue. On success, the sent Message is returned. + Telegram documentation: https://core.telegram.org/bots/api#sendvenue - :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel - :param latitude: Float : Latitude of the venue - :param longitude: Float : Longitude of the venue - :param title: String : Name of the venue - :param address: String : Address of the venue - :param foursquare_id: String : Foursquare identifier of the venue - :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/async default”, + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` or :obj:`str` + + :param latitude: Latitude of the venue + :type latitude: :obj:`float` + + :param longitude: Longitude of the venue + :type longitude: :obj:`float` + + :param title: Name of the venue + :type title: :obj:`str` + + :param address: Address of the venue + :type address: :obj:`str` + + :param foursquare_id: Foursquare identifier of the venue + :type foursquare_id: :obj:`str` + + :param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param google_place_id: - :param google_place_type: - :param protect_content: - :return: + :type foursquare_type: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified + replied-to messages is not found. + :type allow_sending_without_reply: :obj:`bool` + + :param google_place_id: Google Places identifier of the venue + :type google_place_id: :obj:`str` + + :param google_place_type: Google Places type of the venue. + :type google_place_type: :obj:`str` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( await asyncio_helper.send_venue( @@ -2375,21 +3359,48 @@ async def send_contact( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Use this method to send phone contacts. + Use this method to send phone contacts. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendcontact - :param chat_id: Integer or String : Unique identifier for the target chat or username of the target channel - :param phone_number: String : Contact's phone number - :param first_name: String : Contact's first name - :param last_name: String : Contact's last name - :param vcard: String : Additional data about the contact in the form of a vCard, 0-2048 bytes - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param protect_content: + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` or :obj:`str` + + :param phone_number: Contact's phone number + :type phone_number: :obj:`str` + + :param first_name: Contact's first name + :type first_name: :obj:`str` + + :param last_name: Contact's last name + :type last_name: :obj:`str` + + :param vcard: Additional data about the contact in the form of a vCard, 0-2048 bytes + :type vcard: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` + or :class:`telebot.types.ForceReply` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified + replied-to messages is not found. + :type allow_sending_without_reply: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return types.Message.de_json( await asyncio_helper.send_contact( @@ -2402,17 +3413,28 @@ async def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. - The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear - its typing status). + The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). + Returns True on success. + + Example: The ImageBot needs some time to process a request and upload the image. Instead of sending a text message along the lines of + “Retrieving image, please wait…”, the bot may use sendChatAction with action = upload_photo. The user will see a “sending photo” status for the bot. Telegram documentation: https://core.telegram.org/bots/api#sendchataction - :param chat_id: - :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video', - 'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', - 'upload_video_note'. - :param timeout: - :return: API reply. :type: boolean + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` or :obj:`str` + + :param action: Type of action to broadcast. Choose one, depending on what the user is about + to receive: typing for text messages, upload_photo for photos, record_video or upload_video + for videos, record_voice or upload_voice for voice notes, upload_document for general files, + choose_sticker for stickers, find_location for location data, record_video_note or upload_video_note for video notes. + :type action: :obj:`str` + + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout) @@ -2438,14 +3460,24 @@ async def ban_chat_member( Telegram documentation: https://core.telegram.org/bots/api#banchatmember - :param chat_id: Int or string : Unique identifier for the target group or username of the target supergroup - :param user_id: Int : Unique identifier of the target user + :param chat_id: Unique identifier for the target group or username of the target supergroup + or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever + :type until_date: :obj:`int` or :obj:`datetime` + :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. - If False, the user will be able to see messages in the group that were sent before the user was removed. - Always True for supergroups and channels. - :return: boolean + If False, the user will be able to see messages in the group that were sent before the user was removed. + Always True for supergroups and channels. + :type revoke_messages: :obj:`bool` + + :return: Returns True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) @@ -2455,7 +3487,7 @@ async def unban_chat_member( """ Use this method to unban a previously kicked user in a supergroup or channel. The user will not return to the group or channel automatically, but will be able to join via link, etc. - The bot must be an administrator for this to work. By async default, this method guarantees that after the call + The bot must be an administrator for this to work. By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be removed from the chat. If you don't want this, use the parameter only_if_banned. @@ -2463,9 +3495,16 @@ async def unban_chat_member( :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param only_if_banned: Do nothing if the user is not banned + :type only_if_banned: :obj:`bool` + :return: True on success + :rtype: :obj:`bool` """ return await asyncio_helper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) @@ -2487,22 +3526,48 @@ async def restrict_chat_member( Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember - :param chat_id: Int or String : Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) - :param user_id: Int : Unique identifier of the target user + :param chat_id: Unique identifier for the target group or username of the target supergroup + or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param until_date: Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever + :type until_date: :obj:`int` or :obj:`datetime` + :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues + :type can_send_messages: :obj:`bool` + :param can_send_media_messages: Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages + :type can_send_media_messages: :obj:`bool` + :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages - :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and - use inline bots, implies can_send_media_messages - :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, implies can_send_media_messages - :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups - :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, implies can_invite_users + :type can_send_polls: :obj:`bool` + + :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages + :type can_send_other_messages: :obj:`bool` + + :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, + implies can_send_media_messages + :type can_add_web_page_previews: :obj:`bool` + + :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. + Ignored in public supergroups + :type can_change_info: :obj:`bool` + + :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, + implies can_invite_users + :type can_invite_users: :obj:`bool` + :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups + :type can_pin_messages: :obj:`bool` + :return: True on success + :rtype: :obj:`bool` """ return await asyncio_helper.restrict_chat_member( self.token, chat_id, user_id, until_date, @@ -2534,26 +3599,55 @@ async def promote_chat_member( :param chat_id: Unique identifier for the target chat or username of the target channel ( in the format @channelusername) - :param user_id: Int : Unique identifier of the target user - :param can_change_info: Bool: Pass True, if the administrator can change chat title, photo and other settings - :param can_post_messages: Bool : Pass True, if the administrator can create channel posts, channels only - :param can_edit_messages: Bool : Pass True, if the administrator can edit messages of other users, channels only - :param can_delete_messages: Bool : Pass True, if the administrator can delete messages of other users - :param can_invite_users: Bool : Pass True, if the administrator can invite new users to the chat - :param can_restrict_members: Bool: Pass True, if the administrator can restrict, ban or unban chat members - :param can_pin_messages: Bool: Pass True, if the administrator can pin messages, supergroups only - :param can_promote_members: Bool: Pass True, if the administrator can add new administrators with a subset + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param can_change_info: Pass True, if the administrator can change chat title, photo and other settings + :type can_change_info: :obj:`bool` + + :param can_post_messages: Pass True, if the administrator can create channel posts, channels only + :type can_post_messages: :obj:`bool` + + :param can_edit_messages: Pass True, if the administrator can edit messages of other users, channels only + :type can_edit_messages: :obj:`bool` + + :param can_delete_messages: Pass True, if the administrator can delete messages of other users + :type can_delete_messages: :obj:`bool` + + :param can_invite_users: Pass True, if the administrator can invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_restrict_members: Pass True, if the administrator can restrict, ban or unban chat members + :type can_restrict_members: :obj:`bool` + + :param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only + :type can_pin_messages: :obj:`bool` + + :param can_promote_members: Pass True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) - :param is_anonymous: Bool: Pass True, if the administrator's presence in the chat is hidden - :param can_manage_chat: Bool: Pass True, if the administrator can access the chat event log, chat statistics, + :type can_promote_members: :obj:`bool` + + :param is_anonymous: Pass True, if the administrator's presence in the chat is hidden + :type is_anonymous: :obj:`bool` + + :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege - :param can_manage_video_chats: Bool: Pass True, if the administrator can manage voice chats + :type can_manage_chat: :obj:`bool` + + :param can_manage_video_chats: Pass True, if the administrator can manage voice chats For now, bots can use this privilege only for passing to other administrators. - :param can_manage_voice_chats: Deprecated, use can_manage_video_chats + :type can_manage_video_chats: :obj:`bool` + + :param can_manage_voice_chats: Deprecated, use can_manage_video_chats. + :type can_manage_voice_chats: :obj:`bool` + :return: True on success. + :rtype: :obj:`bool` """ if can_manage_voice_chats is not None: @@ -2570,17 +3664,24 @@ async def promote_chat_member( async def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: """ - Use this method to set a custom title for an administrator - in a supergroup promoted by the bot. + Use this method to set a custom title for an administrator in a supergroup promoted by the bot. + Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setchatadministratorcustomtitle :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + :param custom_title: New custom title for the administrator; 0-16 characters, emoji are not allowed + :type custom_title: :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) @@ -2597,8 +3698,13 @@ async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: U Telegram documentation: https://core.telegram.org/bots/api#banchatsenderchat :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param sender_chat_id: Unique identifier of the target sender chat + :type sender_chat_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id) @@ -2611,17 +3717,21 @@ async def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Telegram documentation: https://core.telegram.org/bots/api#unbanchatsenderchat - :params: :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :param sender_chat_id: Unique identifier of the target sender chat + :type chat_id: :obj:`int` or :obj:`str` + + :param sender_chat_id: Unique identifier of the target sender chat. + :type sender_chat_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) async def set_chat_permissions( self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: """ - Use this method to set async default chat permissions for all members. + Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work and must have the can_restrict_members admin rights. @@ -2629,8 +3739,13 @@ async def set_chat_permissions( :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) - :param permissions: New async default chat permissions + :type chat_id: :obj:`int` or :obj:`str` + + :param permissions: New default chat permissions + :type permissions: :class:`telebot.types..ChatPermissions` + :return: True on success + :rtype: :obj:`bool` """ return await asyncio_helper.set_chat_permissions(self.token, chat_id, permissions) @@ -2641,18 +3756,31 @@ async def create_chat_invite_link( member_limit: Optional[int]=None, creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ - Use this method to create an additional invite link for a chat. - The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and + must have the appropriate administrator rights. + The link can be revoked using the method revokeChatInviteLink. + Returns the new invite link as ChatInviteLink object. Telegram documentation: https://core.telegram.org/bots/api#createchatinvitelink :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + :param expire_date: Point in time (Unix timestamp) when the link will expire + :type expire_date: :obj:`int` or :obj:`datetime` + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :type member_limit: :obj:`int` + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified - :return: + :type creates_join_request: :obj:`bool` + + :return: Returns the new invite link as ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( await asyncio_helper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request) @@ -2673,12 +3801,25 @@ async def edit_chat_invite_link( :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + :param invite_link: The invite link to edit + :type invite_link: :obj:`str` + :param expire_date: Point in time (Unix timestamp) when the link will expire + :type expire_date: :obj:`int` or :obj:`datetime` + :param member_limit: Maximum number of users that can be members of the chat simultaneously + :type member_limit: :obj:`int` + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified - :return: + :type creates_join_request: :obj:`bool` + + :return: Returns the new invite link as ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( await asyncio_helper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) @@ -2693,9 +3834,15 @@ async def revoke_chat_invite_link( Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink - :param chat_id: Id: Unique identifier for the target chat or username of the target channel(in the format @channelusername) + :param chat_id: Id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param invite_link: The invite link to revoke - :return: API reply. + :type invite_link: :obj:`str` + + :return: Returns the new invite link as ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( await asyncio_helper.revoke_chat_invite_link(self.token, chat_id, invite_link) @@ -2710,7 +3857,10 @@ async def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: :param chat_id: Id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :return: exported invite link as String on success. + :rtype: :obj:`str` """ return await asyncio_helper.export_chat_invite_link(self.token, chat_id) @@ -2725,8 +3875,13 @@ async def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Uni :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.approve_chat_join_request(self.token, chat_id, user_id) @@ -2740,8 +3895,13 @@ async def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Uni :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` or :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.decline_chat_join_request(self.token, chat_id, user_id) @@ -2750,13 +3910,19 @@ async def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. - Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ + setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#setchatphoto - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param photo: InputFile: New chat photo, uploaded using multipart/form-data - :return: + :type photo: :obj:`typing.Union[file_like, str]` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.set_chat_photo(self.token, chat_id, photo) @@ -2765,13 +3931,16 @@ async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. - Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ - setting is off in the target group. + Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting is off in the target group. Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.delete_chat_photo(self.token, chat_id) @@ -2784,10 +3953,16 @@ async def get_my_commands(self, scope: Optional[types.BotCommandScope], Telegram documentation: https://core.telegram.org/bots/api#getmycommands :param scope: The scope of users for which the commands are relevant. - async defaults to BotCommandScopeasync default. + Defaults to BotCommandScopeDefault. + :type scope: :class:`telebot.types.BotCommandScope` + :param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands + :type language_code: :obj:`str` + + :return: List of BotCommand on success. + :rtype: :obj:`list` of :class:`telebot.types.BotCommand` """ result = await asyncio_helper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] @@ -2803,8 +3978,13 @@ async def set_chat_menu_button(self, chat_id: Union[int, str]=None, :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be changed. + :type chat_id: :obj:`int` or :obj:`str` + :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault + :type menu_button: :class:`telebot.types.MenuButton` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.set_chat_menu_button(self.token, chat_id, menu_button) @@ -2819,8 +3999,10 @@ async def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.Men :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be returned. - :return: types.MenuButton + :type chat_id: :obj:`int` or :obj:`str` + :return: types.MenuButton + :rtype: :class:`telebot.types.MenuButton` """ return types.MenuButton.de_json(await asyncio_helper.get_chat_menu_button(self.token, chat_id)) @@ -2836,8 +4018,16 @@ async def set_my_default_administrator_rights(self, rights: types.ChatAdministra Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights - :param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared. - :param for_channels: Pass True to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + :param rights: A JSON-serialized object describing new default administrator rights. If not specified, + the default administrator rights will be cleared. + :type rights: :class:`telebot.types.ChatAdministratorRights` + + :param for_channels: Pass True to change the default administrator rights of the bot in channels. + Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + :type for_channels: :obj:`bool` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.set_my_default_administrator_rights(self.token, rights, for_channels) @@ -2851,7 +4041,10 @@ async def get_my_default_administrator_rights(self, for_channels: bool=None) -> Telegram documentation: https://core.telegram.org/bots/api#getmydefaultadministratorrights :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. - :return: types.ChatAdministratorRights + :type for_channels: :obj:`bool` + + :return: Returns ChatAdministratorRights on success. + :rtype: :class:`telebot.types.ChatAdministratorRights` """ return types.ChatAdministratorRights.de_json(await asyncio_helper.get_my_default_administrator_rights(self.token, for_channels)) @@ -2865,12 +4058,19 @@ async def set_my_commands(self, commands: List[types.BotCommand], Telegram documentation: https://core.telegram.org/bots/api#setmycommands :param commands: List of BotCommand. At most 100 commands can be specified. + :type commands: :obj:`list` of :class:`telebot.types.BotCommand` + :param scope: The scope of users for which the commands are relevant. - async defaults to BotCommandScopeasync default. + Defaults to BotCommandScopeDefault. + :type scope: :class:`telebot.types.BotCommandScope` + :param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands - :return: + :type language_code: :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.set_my_commands(self.token, commands, scope, language_code) @@ -2882,12 +4082,18 @@ async def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemycommands - + :param scope: The scope of users for which the commands are relevant. - async defaults to BotCommandScopeasync default. + Defaults to BotCommandScopeDefault. + :type scope: :class:`telebot.types.BotCommandScope` + :param language_code: A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given scope, for whose language there are no dedicated commands + :type language_code: :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.delete_my_commands(self.token, scope, language_code) @@ -2901,10 +4107,15 @@ async def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: Telegram documentation: https://core.telegram.org/bots/api#setchattitle - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param title: New chat title, 1-255 characters - :return: + :type title: :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.set_chat_title(self.token, chat_id, title) @@ -2915,10 +4126,15 @@ async def set_chat_description(self, chat_id: Union[int, str], description: Opti Telegram documentation: https://core.telegram.org/bots/api#setchatdescription - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param description: Str: New chat description, 0-255 characters + :type description: :obj:`str` + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.set_chat_description(self.token, chat_id, description) @@ -2932,12 +4148,19 @@ async def pin_chat_message( Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :param message_id: Int: Identifier of a message to pin - :param disable_notification: Bool: Pass True, if it is not necessary to send a notification + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of a message to pin + :type message_id: :obj:`int` + + :param disable_notification: Pass True, if it is not necessary to send a notification to all group members about the new pinned message - :return: + :type disable_notification: :obj:`bool` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.pin_chat_message(self.token, chat_id, message_id, disable_notification) @@ -2949,10 +4172,15 @@ async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optiona Telegram documentation: https://core.telegram.org/bots/api#unpinchatmessage - :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + :param message_id: Int: Identifier of a message to unpin - :return: + :type message_id: :obj:`int` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.unpin_chat_message(self.token, chat_id, message_id) @@ -2963,10 +4191,13 @@ async def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#unpinallchatmessages - + :param chat_id: Int or Str: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :return: + :type chat_id: :obj:`int` or :obj:`str` + + :return: True on success. + :rtype: :obj:`bool` """ return await asyncio_helper.unpin_all_chat_messages(self.token, chat_id) @@ -2984,15 +4215,32 @@ async def edit_message_text( Telegram documentation: https://core.telegram.org/bots/api#editmessagetext - :param text: - :param chat_id: - :param message_id: - :param inline_message_id: - :param parse_mode: - :param entities: - :param disable_web_page_preview: - :param reply_markup: - :return: + :param text: New text of the message, 1-4096 characters after entities parsing + :type text: :obj:`str` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param parse_mode: Mode for parsing entities in the message text. + :type parse_mode: :obj:`str` + + :param entities: List of special entities that appear in the message text, which can be specified instead of parse_mode + :type entities: List of :obj:`telebot.types.MessageEntity` + + :param disable_web_page_preview: Disables link previews for links in this message + :type disable_web_page_preview: :obj:`bool` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`InlineKeyboardMarkup` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -3012,15 +4260,25 @@ async def edit_message_media( If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL. - + Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia - :param media: - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :return: + :param media: A JSON-serialized object for a new media content of the message + :type media: :obj:`InputMedia` + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`telebot.types.InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` or :obj:`bool` """ result = await asyncio_helper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) if type(result) == bool: # if edit inline message return is bool not Message. @@ -3037,11 +4295,20 @@ async def edit_message_reply_markup( Telegram documentation: https://core.telegram.org/bots/api#editmessagereplymarkup - :param chat_id: - :param message_id: - :param inline_message_id: - :param reply_markup: - :return: + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` or :obj:`bool` """ result = await asyncio_helper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) if type(result) == bool: @@ -3061,15 +4328,32 @@ async def send_game( Telegram documentation: https://core.telegram.org/bots/api#sendgame - :param chat_id: - :param game_short_name: - :param disable_notification: - :param reply_to_message_id: - :param reply_markup: - :param timeout: - :param allow_sending_without_reply: - :param protect_content: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param game_short_name: Short name of the game, serves as the unique identifier for the game. Set up your games via @BotFather. + :type game_short_name: :obj:`str` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + + :param timeout: Timeout in seconds for waiting for a response from the bot. + :type timeout: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified replied-to messages is not found. + :type allow_sending_without_reply: :obj:`bool` + + :param protect_content: Pass True, if content of the message needs to be protected from being viewed by the bot. + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` """ result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, @@ -3089,14 +4373,29 @@ async def set_game_score( Telegram documentation: https://core.telegram.org/bots/api#setgamescore - :param user_id: - :param score: - :param force: - :param chat_id: - :param message_id: - :param inline_message_id: - :param disable_edit_message: - :return: + :param user_id: User identifier + :type user_id: :obj:`int` or :obj:`str` + + :param score: New score, must be non-negative + :type score: :obj:`int` + + :param force: Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters + :type force: :obj:`bool` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :param disable_edit_message: Pass True, if the game message should not be automatically edited to include the current scoreboard + :type disable_edit_message: :obj:`bool` + + :return: On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. + :rtype: :obj:`types.Message` or :obj:`bool` """ result = await asyncio_helper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, message_id, inline_message_id) @@ -3109,15 +4408,29 @@ async def get_game_high_scores( message_id: Optional[int]=None, inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ - Gets top points and game play. + Use this method to get data for high score tables. Will return the score of the specified user and several of + their neighbors in a game. On success, returns an Array of GameHighScore objects. + + This method will currently return scores for the target user, plus two of their closest neighbors on each side. + Will also return the top three users if the user and their neighbors are not among them. + Please note that this behavior is subject to change. Telegram documentation: https://core.telegram.org/bots/api#getgamehighscores - :param user_id: - :param chat_id: - :param message_id: - :param inline_message_id: - :return: + :param user_id: User identifier + :type user_id: :obj:`int` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :type message_id: :obj:`int` + + :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message + :type inline_message_id: :obj:`str` + + :return: On success, returns an Array of GameHighScore objects. + :rtype: List[types.GameHighScore] """ result = await asyncio_helper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) return [types.GameHighScore.de_json(r) for r in result] @@ -3148,43 +4461,100 @@ async def send_invoice( Telegram documentation: https://core.telegram.org/bots/api#sendinvoice :param chat_id: Unique identifier for the target private chat - :param title: Product name - :param description: Product description - :param invoice_payload: Bot-async defined invoice payload, 1-128 bytes. This will not be displayed to the user, + :type chat_id: :obj:`int` or :obj:`str` + + :param title: Product name, 1-32 characters + :type title: :obj:`str` + + :param description: Product description, 1-255 characters + :type description: :obj:`str` + + :param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :type invoice_payload: :obj:`str` + :param provider_token: Payments provider token, obtained via @Botfather + :type provider_token: :obj:`str` + :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies + :type currency: :obj:`str` + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :type prices: List[:obj:`types.LabeledPrice`] + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter + :type start_parameter: :obj:`str` + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for. - :param photo_size: Photo size - :param photo_width: Photo width + :type photo_url: :obj:`str` + + :param photo_size: Photo size in bytes + :type photo_size: :obj:`int` + + :param photo_width: Photo width + :type photo_width: :obj:`int` + :param photo_height: Photo height + :type photo_height: :obj:`int` + :param need_name: Pass True, if you require the user's full name to complete the order + :type need_name: :obj:`bool` + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :type need_phone_number: :obj:`bool` + :param need_email: Pass True, if you require the user's email to complete the order + :type need_email: :obj:`bool` + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :type need_shipping_address: :obj:`bool` + :param is_flexible: Pass True, if the final price depends on the shipping method + :type is_flexible: :obj:`bool` + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :type send_phone_number_to_provider: :obj:`bool` + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :type send_email_to_provider: :obj:`bool` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + :param reply_markup: 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 + :type reply_markup: :obj:`str` + :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 allow_sending_without_reply: + :type provider_data: :obj:`str` + + :param timeout: Timeout of a request, defaults to None + :type timeout: :obj:`int` + + :param allow_sending_without_reply: 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 max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency + :type max_tip_amount: :obj:`int` + :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: - :return: + :type suggested_tip_amounts: :obj:`list` of :obj:`int` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` """ result = await asyncio_helper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, @@ -3222,31 +4592,74 @@ async def create_invoice_link(self, https://core.telegram.org/bots/api#createinvoicelink :param title: Product name, 1-32 characters + :type title: :obj:`str` + :param description: Product description, 1-255 characters + :type description: :obj:`str` + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :type payload: :obj:`str` + :param provider_token: Payments provider token, obtained via @Botfather + :type provider_token: :obj:`str` + :param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies + :type currency: :obj:`str` + :param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :type prices: :obj:`list` of :obj:`types.LabeledPrice` + :param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency + :type max_tip_amount: :obj:`int` + :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. + :type suggested_tip_amounts: :obj:`list` of :obj:`int` + :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. + :type provider_data: :obj:`str` + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods + or a photo of the invoice. People like it better when they see a photo of what they are paying for. + :type photo_url: :obj:`str` + :param photo_size: Photo size in bytes + :type photo_size: :obj:`int` + :param photo_width: Photo width + :type photo_width: :obj:`int` + :param photo_height: Photo height + :type photo_height: :obj:`int` + :param need_name: Pass True, if you require the user's full name to complete the order + :type need_name: :obj:`bool` + :param need_phone_number: Pass True, if you require the user's phone number to complete the order + :type need_phone_number: :obj:`bool` + :param need_email: Pass True, if you require the user's email to complete the order + :type need_email: :obj:`bool` + :param need_shipping_address: Pass True, if you require the user's shipping address to complete the order + :type need_shipping_address: :obj:`bool` + :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider + :type send_phone_number_to_provider: :obj:`bool` + :param send_email_to_provider: Pass True, if user's email address should be sent to provider + :type send_email_to_provider: :obj:`bool` + :param is_flexible: Pass True, if the final price depends on the shipping method + :type is_flexible: :obj:`bool` :return: Created invoice link as String on success. + :rtype: :obj:`str` """ result = await asyncio_helper.create_invoice_link( self.token, title, description, payload, provider_token, @@ -3275,30 +4688,74 @@ async def send_poll( explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None) -> types.Message: """ - Send polls. + Use this method to send a native poll. + On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendpoll - :param chat_id: - :param question: - :param options: array of str with answers - :param is_anonymous: - :param type: - :param allows_multiple_answers: - :param correct_option_id: - :param explanation: - :param explanation_parse_mode: - :param open_period: - :param close_date: - :param is_closed: - :param disable_notification: - :param reply_to_message_id: - :param allow_sending_without_reply: - :param reply_markup: - :param timeout: - :param explanation_entities: - :param protect_content: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :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 is_anonymous: True, if the poll needs to be anonymous, defaults to True + :type is_anonymous: :obj:`bool` + + :param type: Poll type, “quiz” or “regular”, defaults to “regular” + :type type: :obj:`str` + + :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 + :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 + :type explanation: :obj:`str` + + :param explanation_parse_mode: Mode for parsing entities in the explanation. See formatting options for more details. + :type explanation_parse_mode: :obj:`str` + + :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date. + :type open_period: :obj:`int` + + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. + :type close_date: :obj:`int` | :obj:`datetime` + + :param is_closed: Pass True, if the poll needs to be immediately closed. This can be useful for poll preview. + :type is_closed: :obj:`bool` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param reply_to_message_id: If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :param allow_sending_without_reply: Pass True, if the poll allows multiple options to be voted simultaneously. + :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. + :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 + :type explanation_entities: :obj:`list` of :obj:`MessageEntity` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` """ if isinstance(question, types.Poll): @@ -3319,14 +4776,21 @@ async def stop_poll( self, chat_id: Union[int, str], message_id: int, reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: """ - Stops poll. + Use this method to stop a poll which was sent by the bot. On success, the stopped Poll is returned. Telegram documentation: https://core.telegram.org/bots/api#stoppoll - :param chat_id: - :param message_id: - :param reply_markup: - :return: + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :param message_id: Identifier of the original message with the poll + :type message_id: :obj:`int` + + :param reply_markup: A JSON-serialized object for a new message markup. + :type reply_markup: :obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply` + + :return: On success, the stopped Poll is returned. + :rtype: :obj:`types.Poll` """ return types.Poll.de_json(await asyncio_helper.stop_poll(self.token, chat_id, message_id, reply_markup)) @@ -3339,11 +4803,21 @@ async def answer_shipping_query( Telegram documentation: https://core.telegram.org/bots/api#answershippingquery - :param shipping_query_id: - :param ok: - :param shipping_options: - :param error_message: - :return: + :param shipping_query_id: Unique identifier for the query to be answered + :type shipping_query_id: :obj:`str` + + :param ok: Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible) + :type ok: :obj:`bool` + + :param shipping_options: Required if ok is True. A JSON-serialized array of available shipping options. + :type shipping_options: :obj:`list` of :obj:`ShippingOption` + + :param error_message: Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order + (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. + :type error_message: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) @@ -3351,14 +4825,28 @@ async def answer_pre_checkout_query( self, pre_checkout_query_id: int, ok: bool, error_message: Optional[str]=None) -> bool: """ - Response to a request for pre-inspection. + Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the + field pre_checkout_query. Use this method to respond to such pre-checkout queries. + On success, True is returned. + + .. note:: + The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. Telegram documentation: https://core.telegram.org/bots/api#answerprecheckoutquery - :param pre_checkout_query_id: - :param ok: - :param error_message: - :return: + :param pre_checkout_query_id: Unique identifier for the query to be answered + :type pre_checkout_query_id: :obj:`int` + + :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. + :type ok: :obj:`bool` + + :param error_message: Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout + (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different + color or garment!"). Telegram will display this message to the user. + :type error_message: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) @@ -3374,14 +4862,29 @@ async def edit_message_caption( Telegram documentation: https://core.telegram.org/bots/api#editmessagecaption - :param caption: - :param chat_id: - :param message_id: - :param inline_message_id: - :param parse_mode: - :param caption_entities: - :param reply_markup: - :return: + :param caption: New caption of the message + :type caption: :obj:`str` + + :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :param message_id: Required if inline_message_id is not specified. + :type message_id: :obj:`int` + + :param inline_message_id: Required if inline_message_id is not specified. Identifier of the inline message. + :type inline_message_id: :obj:`str` + + :param parse_mode: New caption of the message, 0-1024 characters after entities parsing + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized array of objects that describe how the caption should be parsed. + :type caption_entities: :obj:`list` of :obj:`types.MessageEntity` + + :param reply_markup: A JSON-serialized object for an inline keyboard. + :type reply_markup: :obj:`InlineKeyboardMarkup` + + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + :rtype: :obj:`types.Message` | :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -3395,10 +4898,16 @@ async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.M """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` - :param message: - :param text: - :param kwargs: - :return: + :param message: Instance of :class:`telebot.types.Message` + :type message: :obj:`types.Message` + + :param text: Text of the message. + :type text: :obj:`str` + + :param kwargs: Additional keyword arguments which are passed to :meth:`telebot.TeleBot.send_message` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` """ return await self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) @@ -3413,21 +4922,40 @@ async def answer_inline_query( """ Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. - + Telegram documentation: https://core.telegram.org/bots/api#answerinlinequery - + :param inline_query_id: Unique identifier for the answered query + :type inline_query_id: :obj:`str` + :param results: Array of results for the inline query + :type results: :obj:`list` of :obj:`types.InlineQueryResult` + :param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server. + :type cache_time: :obj:`int` + :param is_personal: Pass True, if results may be cached on the server side only for the user that sent the query. + :type is_personal: :obj:`bool` + :param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results. - :param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user - to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter - :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button - :return: True means success. + :type next_offset: :obj:`str` + + :param switch_pm_parameter: Deep-linking parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, + only A-Z, a-z, 0-9, _ and - are allowed. + Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. + To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a + private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a switch_inline + button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities. + :type switch_pm_parameter: :obj:`str` + + :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button + :type switch_pm_text: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, switch_pm_text, switch_pm_parameter) @@ -3439,15 +4967,27 @@ async def answer_callback_query( """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. - + Telegram documentation: https://core.telegram.org/bots/api#answercallbackquery - :param callback_query_id: - :param text: - :param show_alert: - :param url: - :param cache_time: - :return: + :param callback_query_id: Unique identifier for the query to be answered + :type callback_query_id: :obj:`int` + + :param text: Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters + :type text: :obj:`str` + + :param show_alert: If True, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false. + :type show_alert: :obj:`bool` + + :param url: URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @BotFather, specify the URL that opens your + game - note that this will only work if the query comes from a callback_game button. + :type url: :obj:`str` + + :param cache_time: The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching + starting in version 3.14. Defaults to 0. + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) @@ -3460,22 +5000,30 @@ async def set_sticker_set_thumb( Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb :param name: Sticker set name - :param user_id: User identifier - :param thumb: A PNG image with the thumbnail, must be up to 128 kilobytes in size and have width and height - exactly 100px, or a TGS animation with the thumbnail up to 32 kilobytes in size; - see https://core.telegram.org/animated_stickers#technical-requirements - + :type name: :obj:`str` + + :param user_id: User identifier + :type user_id: :obj:`int` + + :param thumb: + :type thumb: :obj:`filelike object` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) async def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. - + Telegram documentation: https://core.telegram.org/bots/api#getstickerset - :param name: - :return: + :param name: Sticker set name + :type name: :obj:`str` + + :return: On success, a StickerSet object is returned. + :rtype: :class:`telebot.types.StickerSet` """ result = await asyncio_helper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) @@ -3485,12 +5033,17 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. - Telegram documentation: https://core.telegram.org/bots/api#uploadstickerfile - :param user_id: - :param png_sticker: - :return: + :param user_id: User identifier of sticker set owner + :type user_id: :obj:`int` + + :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, + and either width or height must be exactly 512px. + :type png_sticker: :obj:`filelike object` + + :return: On success, the sent file is returned. + :rtype: :class:`telebot.types.File` """ result = await asyncio_helper.upload_sticker_file(self.token, user_id, png_sticker) return types.File.de_json(result) @@ -3510,16 +5063,39 @@ async def create_new_sticker_set( Telegram documentation: https://core.telegram.org/bots/api#createnewstickerset - :param user_id: - :param name: - :param title: - :param emojis: - :param png_sticker: - :param tgs_sticker: - :webm_sticker: - :param contains_masks: - :param mask_position: - :return: + :param user_id: User identifier of created sticker set owner + :type user_id: :obj:`int` + + :param name: Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). Can contain only English letters, + digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in "_by_". + is case insensitive. 1-64 characters. + :type name: :obj:`str` + + :param title: Sticker set title, 1-64 characters + :type title: :obj:`str` + + :param emojis: One or more emoji corresponding to the sticker + :type emojis: :obj:`str` + + :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width + or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. + :type png_sticker: :obj:`str` + + :param tgs_sticker: TGS animation with the sticker, uploaded using multipart/form-data. + :type tgs_sticker: :obj:`str` + + :param webm_sticker: WebM animation with the sticker, uploaded using multipart/form-data. + :type webm_sticker: :obj:`str` + + :param contains_masks: Pass True, if a set of mask stickers should be created + :type contains_masks: :obj:`bool` + + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces + :type mask_position: :class:`telebot.types.MaskPosition` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, @@ -3539,14 +5115,31 @@ async def add_sticker_to_set( Telegram documentation: https://core.telegram.org/bots/api#addstickertoset - :param user_id: - :param name: - :param emojis: - :param png_sticker: Required if `tgs_sticker` is None - :param tgs_sticker: Required if `png_sticker` is None - :webm_sticker: - :param mask_position: - :return: + :param user_id: User identifier of created sticker set owner + :type user_id: :obj:`int` + + :param name: Sticker set name + :type name: :obj:`str` + + :param emojis: One or more emoji corresponding to the sticker + :type emojis: :obj:`str` + + :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either + width or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, + 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. + :type png_sticker: :obj:`str` or :obj:`filelike object` + + :param tgs_sticker: TGS animation with the sticker, uploaded using multipart/form-data. + :type tgs_sticker: :obj:`str` or :obj:`filelike object` + + :param webm_sticker: WebM animation with the sticker, uploaded using multipart/form-data. + :type webm_sticker: :obj:`str` or :obj:`filelike object` + + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces + :type mask_position: :class:`telebot.types.MaskPosition` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.add_sticker_to_set( self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker) @@ -3555,85 +5148,137 @@ async def add_sticker_to_set( async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#setstickerpositioninset - :param sticker: - :param position: - :return: + :param sticker: File identifier of the sticker + :type sticker: :obj:`str` + + :param position: New sticker position in the set, zero-based + :type position: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.set_sticker_position_in_set(self.token, sticker, position) async def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletestickerfromset - :param sticker: - :return: + :param sticker: File identifier of the sticker + :return: On success, True is returned. + :rtype: :obj:`bool` """ return await asyncio_helper.delete_sticker_from_set(self.token, sticker) - async def set_state(self, user_id: int, state: Union[State, int, str], chat_id: int=None): + async def set_state(self, user_id: int, state: Union[State, int, str], chat_id: Optional[int]=None): """ Sets a new state of a user. - :param user_id: - :param chat_id: - :param state: new state. can be string or integer. + .. note:: + + You should set both user id and chat id in order to set state for a user in a chat. + Otherwise, if you only set user_id, chat_id will equal to user_id, this means that + state will be set for the user in his private chat with a bot. + + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param state: new state. can be string, integer, or :class:`telebot.types.State` + :type state: :obj:`int` or :obj:`str` or :class:`telebot.types.State` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :return: None """ if not chat_id: chat_id = user_id await self.current_states.set_state(chat_id, user_id, state) - async def reset_data(self, user_id: int, chat_id: int=None): + async def reset_data(self, user_id: int, chat_id: Optional[int]=None): """ Reset data for a user in chat. - :param user_id: - :param chat_id: + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :return: None """ if chat_id is None: chat_id = user_id await self.current_states.reset_data(chat_id, user_id) - async def delete_state(self, user_id: int, chat_id:int=None): + async def delete_state(self, user_id: int, chat_id: Optional[int]=None): """ Delete the current state of a user. - :param user_id: - :param chat_id: - :return: + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :return: None """ if not chat_id: chat_id = user_id await self.current_states.delete_state(chat_id, user_id) - def retrieve_data(self, user_id: int, chat_id: int=None): + def retrieve_data(self, user_id: int, chat_id: Optional[int]=None): + """ + Returns context manager with data for a user in chat. + + :param user_id: User identifier + :type user_id: int + + :param chat_id: Chat's unique identifier, defaults to user_id + :type chat_id: int, optional + + :return: Context manager with data for a user in chat + :rtype: Optional[Any] + """ if not chat_id: chat_id = user_id return self.current_states.get_interactive_data(chat_id, user_id) - async def get_state(self, user_id, chat_id: int=None): + async def get_state(self, user_id, chat_id: Optional[int]=None): """ - Get current state of a user. + Gets current state of a user. + Not recommended to use this method. But it is ok for debugging. + + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` - :param user_id: - :param chat_id: :return: state of a user + :rtype: :obj:`int` or :obj:`str` or :class:`telebot.types.State` """ if not chat_id: chat_id = user_id return await self.current_states.get_state(chat_id, user_id) - async def add_data(self, user_id: int, chat_id: int=None, **kwargs): + async def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): """ Add data to states. - :param user_id: - :param chat_id: + :param user_id: User's identifier + :type user_id: :obj:`int` + + :param chat_id: Chat's identifier + :type chat_id: :obj:`int` + + :param kwargs: Data to add + :return: None """ if not chat_id: chat_id = user_id diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 8cce1bb04..f1384557c 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -10,8 +10,19 @@ class SimpleCustomFilter(ABC): Simple Custom Filter base class. Create child class with check() method. Accepts only message, returns bool value, that is compared with given in handler. - + Child classes should have .key property. + + .. code-block:: python3 + :caption: Example on creating a simple custom filter. + + class ForwardFilter(SimpleCustomFilter): + # Check whether message was forwarded from channel or group. + key = 'is_forwarded' + + def check(self, message): + return message.forward_date is not None + """ key: str = None @@ -25,13 +36,23 @@ async def check(self, message): class AdvancedCustomFilter(ABC): """ - Simple Custom Filter base class. + Advanced Custom Filter base class. Create child class with check() method. Accepts two parameters, returns bool: True - filter passed, False - filter failed. message: Message class text: Filter value given in handler Child classes should have .key property. + + .. code-block:: python3 + :caption: Example on creating an advanced custom filter. + + class TextStartsFilter(AdvancedCustomFilter): + # Filter to check whether message starts with some text. + key = 'text_startswith' + + def check(self, message, text): + return message.text.startswith(text) """ key: str = None @@ -48,6 +69,25 @@ class TextFilter: Advanced text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) example of usage is in examples/asynchronous_telebot/custom_filters/advanced_text_filter.py + + :param equals: string, True if object's text is equal to passed string + :type equals: :obj:`str` + + :param contains: list[str] or tuple[str], True if any string element of iterable is in text + :type contains: list[str] or tuple[str] + + :param starts_with: string, True if object's text starts with passed string + :type starts_with: :obj:`str` + + :param ends_with: string, True if object's text starts with passed string + :type ends_with: :obj:`str` + + :param ignore_case: bool (default False), case insensitive + :type ignore_case: :obj:`bool` + + :raises ValueError: if incorrect value for a parameter was supplied + + :return: None """ def __init__(self, @@ -56,13 +96,25 @@ def __init__(self, starts_with: Optional[Union[str, list, tuple]] = None, ends_with: Optional[Union[str, list, tuple]] = None, ignore_case: bool = False): - """ :param equals: string, True if object's text is equal to passed string + :type equals: :obj:`str` + :param contains: list[str] or tuple[str], True if any string element of iterable is in text + :type contains: list[str] or tuple[str] + :param starts_with: string, True if object's text starts with passed string + :type starts_with: :obj:`str` + :param ends_with: string, True if object's text starts with passed string + :type ends_with: :obj:`str` + :param ignore_case: bool (default False), case insensitive + :type ignore_case: :obj:`bool` + + :raises ValueError: if incorrect value for a parameter was supplied + + :return: None """ to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with))) @@ -87,7 +139,9 @@ def _check_iterable(self, iterable, filter_name): return iterable async def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): - + """ + :meta private: + """ if isinstance(obj, types.Poll): text = obj.question elif isinstance(obj, types.Message): @@ -135,15 +189,20 @@ async def check(self, obj: Union[types.Message, types.CallbackQuery, types.Inlin class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. - key: text - Example: - @bot.message_handler(text=['account']) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(text=['account']) + # your function """ key = 'text' async def check(self, message, text): + """ + :meta private: + """ if isinstance(text, TextFilter): return await text.check(message) elif type(text) is list: @@ -157,14 +216,21 @@ class TextContainsFilter(AdvancedCustomFilter): Filter to check Text message. key: text - Example: - # Will respond if any message.text contains word 'account' - @bot.message_handler(text_contains=['account']) + + .. code-block:: python3 + :caption: Example on using this filter: + + # Will respond if any message.text contains word 'account' + @bot.message_handler(text_contains=['account']) + # your function """ key = 'text_contains' async def check(self, message, text): + """ + :meta private: + """ if not isinstance(text, str) and not isinstance(text, list) and not isinstance(text, tuple): raise ValueError("Incorrect text_contains value") elif isinstance(text, str): @@ -179,14 +245,20 @@ class TextStartsFilter(AdvancedCustomFilter): """ Filter to check whether message starts with some text. - Example: - # Will work if message.text starts with 'Sir'. - @bot.message_handler(text_startswith='Sir') + .. code-block:: python3 + :caption: Example on using this filter: + + # Will work if message.text starts with 'sir'. + @bot.message_handler(text_startswith='sir') + # your function """ key = 'text_startswith' async def check(self, message, text): + """ + :meta private: + """ return message.text.startswith(text) @@ -194,13 +266,19 @@ class ChatFilter(AdvancedCustomFilter): """ Check whether chat_id corresponds to given chat_id. - Example: - @bot.message_handler(chat_id=[99999]) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(chat_id=[99999]) + # your function """ key = 'chat_id' async def check(self, message, text): + """ + :meta private: + """ if isinstance(message, types.CallbackQuery): return message.message.chat.id in text return message.chat.id in text @@ -210,14 +288,19 @@ class ForwardFilter(SimpleCustomFilter): """ Check whether message was forwarded from channel or group. - Example: + .. code-block:: python3 + :caption: Example on using this filter: - @bot.message_handler(is_forwarded=True) + @bot.message_handler(is_forwarded=True) + # your function """ key = 'is_forwarded' async def check(self, message): + """ + :meta private: + """ return message.forward_date is not None @@ -225,14 +308,19 @@ class IsReplyFilter(SimpleCustomFilter): """ Check whether message is a reply. - Example: + .. code-block:: python3 + :caption: Example on using this filter: - @bot.message_handler(is_reply=True) + @bot.message_handler(is_reply=True) + # your function """ key = 'is_reply' async def check(self, message): + """ + :meta private: + """ if isinstance(message, types.CallbackQuery): return message.message.reply_to_message is not None return message.reply_to_message is not None @@ -242,14 +330,19 @@ class LanguageFilter(AdvancedCustomFilter): """ Check users language_code. - Example: + .. code-block:: python3 + :caption: Example on using this filter: - @bot.message_handler(language_code=['ru']) + @bot.message_handler(language_code=['ru']) + # your function """ key = 'language_code' async def check(self, message, text): + """ + :meta private: + """ if type(text) is list: return message.from_user.language_code in text else: @@ -260,8 +353,11 @@ class IsAdminFilter(SimpleCustomFilter): """ Check whether the user is administrator / owner of the chat. - Example: - @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) + # your function """ key = 'is_chat_admin' @@ -270,6 +366,9 @@ def __init__(self, bot): self._bot = bot async def check(self, message): + """ + :meta private: + """ if isinstance(message, types.CallbackQuery): result = await self._bot.get_chat_member(message.message.chat.id, message.from_user.id) return result.status ('creator', 'administrator') @@ -281,8 +380,11 @@ class StateFilter(AdvancedCustomFilter): """ Filter to check state. - Example: - @bot.message_handler(state=1) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(state=1) + # your function """ def __init__(self, bot): @@ -291,6 +393,9 @@ def __init__(self, bot): key = 'state' async def check(self, message, text): + """ + :meta private: + """ if text == '*': return True # needs to work with callbackquery @@ -334,10 +439,16 @@ class IsDigitFilter(SimpleCustomFilter): """ Filter to check whether the string is made up of only digits. - Example: - @bot.message_handler(is_digit=True) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(is_digit=True) + # your function """ key = 'is_digit' async def check(self, message): + """ + :meta private: + """ return message.text.isdigit() diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 4f0d1743f..34cc19a77 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -1,3 +1,10 @@ +""" +File with all middleware classes, states. +""" + + + + class BaseMiddleware: """ Base class for middleware. @@ -9,23 +16,25 @@ class BaseMiddleware: so on. Same applies to post_process. .. code-block:: python + :caption: Example of class-based middlewares + class MyMiddleware(BaseMiddleware): def __init__(self): self.update_sensitive = True self.update_types = ['message', 'edited_message'] - def pre_process_message(self, message, data): + async def pre_process_message(self, message, data): # only message update here pass - def post_process_message(self, message, data, exception): + async def post_process_message(self, message, data, exception): pass # only message update here for post_process - def pre_process_edited_message(self, message, data): + async def pre_process_edited_message(self, message, data): # only edited_message update here pass - def post_process_edited_message(self, message, data, exception): + async def post_process_edited_message(self, message, data, exception): pass # only edited_message update here for post_process """ @@ -42,6 +51,14 @@ async def post_process(self, message, data, exception): class State: + """ + Class representing a state. + + .. code-block:: python3 + + class MyStates(StatesGroup): + my_state = State() # returns my_state:State string. + """ def __init__(self) -> None: self.name = None @@ -50,6 +67,14 @@ def __str__(self) -> str: class StatesGroup: + """ + Class representing common states. + + .. code-block:: python3 + + class MyStates(StatesGroup): + my_state = State() # returns my_state:State string. + """ def __init_subclass__(cls) -> None: for name, value in cls.__dict__.items(): diff --git a/telebot/callback_data.py b/telebot/callback_data.py index 58fa0d511..57e4833da 100644 --- a/telebot/callback_data.py +++ b/telebot/callback_data.py @@ -1,3 +1,7 @@ +""" +Callback data factory's file. +""" + """ Copyright (c) 2017-2018 Alex Root Junior @@ -29,17 +33,23 @@ class CallbackDataFilter: + """ + Filter for CallbackData. + """ def __init__(self, factory, config: typing.Dict[str, str]): self.config = config self.factory = factory - def check(self, query): + def check(self, query) -> bool: """ Checks if query.data appropriates to specified config :param query: telebot.types.CallbackQuery - :return: bool + :type query: telebot.types.CallbackQuery + + :return: True if query.data appropriates to specified config + :rtype: bool """ try: @@ -135,7 +145,7 @@ def filter(self, **config) -> CallbackDataFilter: """ Generate filter - :param config: specified named parameters will be checked with CallbackQury.data + :param config: specified named parameters will be checked with CallbackQuery.data :return: CallbackDataFilter class """ diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 161c74ad7..dd6a27fe5 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -14,6 +14,17 @@ class SimpleCustomFilter(ABC): Accepts only message, returns bool value, that is compared with given in handler. Child classes should have .key property. + + .. code-block:: python3 + :caption: Example on creating a simple custom filter. + + class ForwardFilter(SimpleCustomFilter): + # Check whether message was forwarded from channel or group. + key = 'is_forwarded' + + def check(self, message): + return message.forward_date is not None + """ key: str = None @@ -27,13 +38,23 @@ def check(self, message): class AdvancedCustomFilter(ABC): """ - Simple Custom Filter base class. + Advanced Custom Filter base class. Create child class with check() method. Accepts two parameters, returns bool: True - filter passed, False - filter failed. message: Message class text: Filter value given in handler Child classes should have .key property. + + .. code-block:: python3 + :caption: Example on creating an advanced custom filter. + + class TextStartsFilter(AdvancedCustomFilter): + # Filter to check whether message starts with some text. + key = 'text_startswith' + + def check(self, message, text): + return message.text.startswith(text) """ key: str = None @@ -50,6 +71,25 @@ class TextFilter: Advanced text filter to check (types.Message, types.CallbackQuery, types.InlineQuery, types.Poll) example of usage is in examples/custom_filters/advanced_text_filter.py + + :param equals: string, True if object's text is equal to passed string + :type equals: :obj:`str` + + :param contains: list[str] or tuple[str], True if any string element of iterable is in text + :type contains: list[str] or tuple[str] + + :param starts_with: string, True if object's text starts with passed string + :type starts_with: :obj:`str` + + :param ends_with: string, True if object's text starts with passed string + :type ends_with: :obj:`str` + + :param ignore_case: bool (default False), case insensitive + :type ignore_case: :obj:`bool` + + :raises ValueError: if incorrect value for a parameter was supplied + + :return: None """ def __init__(self, @@ -58,15 +98,27 @@ def __init__(self, starts_with: Optional[Union[str, list, tuple]] = None, ends_with: Optional[Union[str, list, tuple]] = None, ignore_case: bool = False): - """ :param equals: string, True if object's text is equal to passed string + :type equals: :obj:`str` + :param contains: list[str] or tuple[str], True if any string element of iterable is in text + :type contains: list[str] or tuple[str] + :param starts_with: string, True if object's text starts with passed string + :type starts_with: :obj:`str` + :param ends_with: string, True if object's text starts with passed string + :type ends_with: :obj:`str` + :param ignore_case: bool (default False), case insensitive - """ + :type ignore_case: :obj:`bool` + :raises ValueError: if incorrect value for a parameter was supplied + + :return: None + """ + to_check = sum((pattern is not None for pattern in (equals, contains, starts_with, ends_with))) if to_check == 0: raise ValueError('None of the check modes was specified') @@ -89,6 +141,9 @@ def _check_iterable(self, iterable, filter_name: str): return iterable def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery, types.Poll]): + """ + :meta private: + """ if isinstance(obj, types.Poll): text = obj.question @@ -142,15 +197,20 @@ def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery class TextMatchFilter(AdvancedCustomFilter): """ Filter to check Text message. - key: text - Example: - @bot.message_handler(text=['account']) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(text=['account']) + # your function """ key = 'text' def check(self, message, text): + """ + :meta private: + """ if isinstance(text, TextFilter): return text.check(message) elif type(text) is list: @@ -164,14 +224,21 @@ class TextContainsFilter(AdvancedCustomFilter): Filter to check Text message. key: text - Example: - # Will respond if any message.text contains word 'account' - @bot.message_handler(text_contains=['account']) + + .. code-block:: python3 + :caption: Example on using this filter: + + # Will respond if any message.text contains word 'account' + @bot.message_handler(text_contains=['account']) + # your function """ key = 'text_contains' def check(self, message, text): + """ + :meta private: + """ if not isinstance(text, str) and not isinstance(text, list) and not isinstance(text, tuple): raise ValueError("Incorrect text_contains value") elif isinstance(text, str): @@ -186,14 +253,20 @@ class TextStartsFilter(AdvancedCustomFilter): """ Filter to check whether message starts with some text. - Example: - # Will work if message.text starts with 'Sir'. - @bot.message_handler(text_startswith='Sir') + .. code-block:: python3 + :caption: Example on using this filter: + + # Will work if message.text starts with 'sir'. + @bot.message_handler(text_startswith='sir') + # your function """ key = 'text_startswith' def check(self, message, text): + """ + :meta private: + """ return message.text.startswith(text) @@ -201,13 +274,19 @@ class ChatFilter(AdvancedCustomFilter): """ Check whether chat_id corresponds to given chat_id. - Example: - @bot.message_handler(chat_id=[99999]) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(chat_id=[99999]) + # your function """ key = 'chat_id' def check(self, message, text): + """ + :meta private: + """ if isinstance(message, types.CallbackQuery): return message.message.chat.id in text return message.chat.id in text @@ -217,14 +296,19 @@ class ForwardFilter(SimpleCustomFilter): """ Check whether message was forwarded from channel or group. - Example: + .. code-block:: python3 + :caption: Example on using this filter: - @bot.message_handler(is_forwarded=True) + @bot.message_handler(is_forwarded=True) + # your function """ key = 'is_forwarded' def check(self, message): + """ + :meta private: + """ return message.forward_date is not None @@ -232,14 +316,19 @@ class IsReplyFilter(SimpleCustomFilter): """ Check whether message is a reply. - Example: + .. code-block:: python3 + :caption: Example on using this filter: - @bot.message_handler(is_reply=True) + @bot.message_handler(is_reply=True) + # your function """ key = 'is_reply' def check(self, message): + """ + :meta private: + """ if isinstance(message, types.CallbackQuery): return message.message.reply_to_message is not None return message.reply_to_message is not None @@ -249,14 +338,19 @@ class LanguageFilter(AdvancedCustomFilter): """ Check users language_code. - Example: + .. code-block:: python3 + :caption: Example on using this filter: - @bot.message_handler(language_code=['ru']) + @bot.message_handler(language_code=['ru']) + # your function """ key = 'language_code' def check(self, message, text): + """ + :meta private: + """ if type(text) is list: return message.from_user.language_code in text else: @@ -267,8 +361,11 @@ class IsAdminFilter(SimpleCustomFilter): """ Check whether the user is administrator / owner of the chat. - Example: - @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(chat_types=['supergroup'], is_chat_admin=True) + # your function """ key = 'is_chat_admin' @@ -277,6 +374,9 @@ def __init__(self, bot): self._bot = bot def check(self, message): + """ + :meta private: + """ if isinstance(message, types.CallbackQuery): return self._bot.get_chat_member(message.message.chat.id, message.from_user.id).status in ['creator', 'administrator'] return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator'] @@ -286,8 +386,11 @@ class StateFilter(AdvancedCustomFilter): """ Filter to check state. - Example: - @bot.message_handler(state=1) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(state=1) + # your function """ def __init__(self, bot): @@ -296,6 +399,9 @@ def __init__(self, bot): key = 'state' def check(self, message, text): + """ + :meta private: + """ if text == '*': return True # needs to work with callbackquery @@ -341,10 +447,16 @@ class IsDigitFilter(SimpleCustomFilter): """ Filter to check whether the string is made up of only digits. - Example: - @bot.message_handler(is_digit=True) + .. code-block:: python3 + :caption: Example on using this filter: + + @bot.message_handler(is_digit=True) + # your function """ key = 'is_digit' def check(self, message): + """ + :meta private: + """ return message.text.isdigit() diff --git a/telebot/ext/aio/webhooks.py b/telebot/ext/aio/webhooks.py index 13e28aba4..03c456616 100644 --- a/telebot/ext/aio/webhooks.py +++ b/telebot/ext/aio/webhooks.py @@ -34,14 +34,34 @@ def __init__(self, bot, """ Aynchronous implementation of webhook listener for asynchronous version of telebot. + Not supposed to be used manually by user. + Use AsyncTeleBot.run_webhooks() instead. + + :param bot: AsyncTeleBot instance. + :type bot: telebot.async_telebot.AsyncTeleBot - :param bot: TeleBot instance :param secret_token: Telegram secret token + :type secret_token: str + :param host: Webhook host + :type host: str + :param port: Webhook port + :type port: int + :param ssl_context: SSL context + :type ssl_context: tuple + :param url_path: Webhook url path + :type url_path: str + :param debug: Debug mode + :type debug: bool + + :raises ImportError: If FastAPI or uvicorn is not installed. + :raises ImportError: If Starlette version is too old. + + :return: None """ self._check_dependencies() @@ -73,6 +93,8 @@ def _prepare_endpoint_urls(self): async def process_update(self, request: Request, update: dict): """ Processes updates. + + :meta private: """ # header containsX-Telegram-Bot-Api-Secret-Token if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: @@ -88,7 +110,10 @@ async def process_update(self, request: Request, update: dict): async def run_app(self): """ - Run app with the given parameters. + Run app with the given parameters to init. + Not supposed to be used manually by user. + + :return: None """ config = Config(app=self.app, diff --git a/telebot/ext/sync/webhooks.py b/telebot/ext/sync/webhooks.py index 89a3ec928..6e1714b13 100644 --- a/telebot/ext/sync/webhooks.py +++ b/telebot/ext/sync/webhooks.py @@ -1,8 +1,7 @@ """ -This file is used by TeleBot.run_webhooks() & -AsyncTeleBot.run_webhooks() functions. +This file is used by TeleBot.run_webhooks() function. -Flask/fastapi is required to run this script. +Fastapi is required to run this script. """ # modules required for running this script @@ -34,16 +33,36 @@ def __init__(self, bot, debug: Optional[bool]=False ) -> None: """ - Synchronous implementation of webhook listener - for synchronous version of telebot. + Aynchronous implementation of webhook listener + for asynchronous version of telebot. + Not supposed to be used manually by user. + Use AsyncTeleBot.run_webhooks() instead. + + :param bot: AsyncTeleBot instance. + :type bot: telebot.async_telebot.AsyncTeleBot - :param bot: TeleBot instance :param secret_token: Telegram secret token + :type secret_token: str + :param host: Webhook host + :type host: str + :param port: Webhook port + :type port: int + :param ssl_context: SSL context + :type ssl_context: tuple + :param url_path: Webhook url path + :type url_path: str + :param debug: Debug mode + :type debug: bool + + :raises ImportError: If FastAPI or uvicorn is not installed. + :raises ImportError: If Starlette version is too old. + + :return: None """ self._check_dependencies() @@ -75,6 +94,8 @@ def _prepare_endpoint_urls(self): def process_update(self, request: Request, update: dict): """ Processes updates. + + :meta private: """ # header containsX-Telegram-Bot-Api-Secret-Token if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != self._secret_token: @@ -89,7 +110,10 @@ def process_update(self, request: Request, update: dict): def run_app(self): """ - Run app with the given parameters. + Run app with the given parameters to init. + Not supposed to be used manually by user. + + :return: None """ uvicorn.run(app=self.app, diff --git a/telebot/formatting.py b/telebot/formatting.py index abec962d7..7b6a9f3f9 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -5,14 +5,17 @@ """ import html + import re +from typing import Optional + def format_text(*args, separator="\n"): """ Formats a list of strings into a single string. - .. code:: python + .. code:: python3 format_text( # just an example mbold('Hello'), @@ -20,7 +23,13 @@ def format_text(*args, separator="\n"): ) :param args: Strings to format. + :type args: :obj:`str` + :param separator: The separator to use between each string. + :type separator: :obj:`str` + + :return: The formatted string. + :rtype: :obj:`str` """ return separator.join(args) @@ -31,6 +40,10 @@ def escape_html(content: str) -> str: Escapes HTML characters in a string of HTML. :param content: The string of HTML to escape. + :type content: :obj:`str` + + :return: The escaped string. + :rtype: :obj:`str` """ return html.escape(content) @@ -39,9 +52,13 @@ def escape_markdown(content: str) -> str: """ Escapes Markdown characters in a string of Markdown. - Credits: simonsmh + Credits to: simonsmh :param content: The string of Markdown to escape. + :type content: :obj:`str` + + :return: The escaped string. + :rtype: :obj:`str` """ parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!])", r"\\\1", content) @@ -49,154 +66,249 @@ def escape_markdown(content: str) -> str: return reparse -def mbold(content: str, escape: bool=True) -> str: +def mbold(content: str, escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted bold string. :param content: The string to bold. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '*{}*'.format(escape_markdown(content) if escape else content) -def hbold(content: str, escape: bool=True) -> str: +def hbold(content: str, escape: Optional[bool]=True) -> str: """ Returns an HTML-formatted bold string. :param content: The string to bold. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '{}'.format(escape_html(content) if escape else content) -def mitalic(content: str, escape: bool=True) -> str: +def mitalic(content: str, escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted italic string. :param content: The string to italicize. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '_{}_\r'.format(escape_markdown(content) if escape else content) -def hitalic(content: str, escape: bool=True) -> str: +def hitalic(content: str, escape: Optional[bool]=True) -> str: """ Returns an HTML-formatted italic string. :param content: The string to italicize. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '{}'.format(escape_html(content) if escape else content) -def munderline(content: str, escape: bool=True) -> str: +def munderline(content: str, escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted underline string. :param content: The string to underline. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '__{}__'.format(escape_markdown(content) if escape else content) -def hunderline(content: str, escape: bool=True) -> str: +def hunderline(content: str, escape: Optional[bool]=True) -> str: """ Returns an HTML-formatted underline string. :param content: The string to underline. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` + """ return '{}'.format(escape_html(content) if escape else content) -def mstrikethrough(content: str, escape: bool=True) -> str: +def mstrikethrough(content: str, escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted strikethrough string. :param content: The string to strikethrough. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '~{}~'.format(escape_markdown(content) if escape else content) -def hstrikethrough(content: str, escape: bool=True) -> str: +def hstrikethrough(content: str, escape: Optional[bool]=True) -> str: """ Returns an HTML-formatted strikethrough string. :param content: The string to strikethrough. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '{}'.format(escape_html(content) if escape else content) -def mspoiler(content: str, escape: bool=True) -> str: +def mspoiler(content: str, escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted spoiler string. :param content: The string to spoiler. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '||{}||'.format(escape_markdown(content) if escape else content) -def hspoiler(content: str, escape: bool=True) -> str: +def hspoiler(content: str, escape: Optional[bool]=True) -> str: """ Returns an HTML-formatted spoiler string. :param content: The string to spoiler. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '{}'.format(escape_html(content) if escape else content) -def mlink(content: str, url: str, escape: bool=True) -> str: +def mlink(content: str, url: str, escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted link string. :param content: The string to link. + :type content: :obj:`str` + :param url: The URL to link to. - :param escape: True if you need to escape special characters. + :type url: str + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '[{}]({})'.format(escape_markdown(content), escape_markdown(url) if escape else content) -def hlink(content: str, url: str, escape: bool=True) -> str: +def hlink(content: str, url: str, escape: Optional[bool]=True) -> str: """ Returns an HTML-formatted link string. :param content: The string to link. + :type content: :obj:`str` + :param url: The URL to link to. - :param escape: True if you need to escape special characters. + :type url: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '{}'.format(escape_html(url), escape_html(content) if escape else content) -def mcode(content: str, language: str="", escape: bool=True) -> str: +def mcode(content: str, language: str="", escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted code string. :param content: The string to code. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '```{}\n{}```'.format(language, escape_markdown(content) if escape else content) -def hcode(content: str, escape: bool=True) -> str: +def hcode(content: str, escape: Optional[bool]=True) -> str: """ Returns an HTML-formatted code string. :param content: The string to code. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '{}'.format(escape_html(content) if escape else content) -def hpre(content: str, escape: bool=True, language: str="") -> str: +def hpre(content: str, escape: Optional[bool]=True, language: str="") -> str: """ Returns an HTML-formatted preformatted string. :param content: The string to preformatted. - :param escape: True if you need to escape special characters. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` """ return '
{}
'.format(language, escape_html(content) if escape else content) @@ -205,7 +317,10 @@ def hide_link(url: str) -> str: """ Hide url of an image. - :param url: - :return: str + :param url: The url of the image. + :type url: :obj:`str` + + :return: The hidden url. + :rtype: :obj:`str` """ return f'' \ No newline at end of file diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 9f4a3a952..42c5804de 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -12,7 +12,9 @@ class HandlerBackend(object): """ - Class for saving (next step|reply) handlers + Class for saving (next step|reply) handlers. + + :meta private: """ def __init__(self, handlers=None): if handlers is None: @@ -30,6 +32,9 @@ def get_handlers(self, handler_group_id): class MemoryHandlerBackend(HandlerBackend): + """ + :meta private: + """ def register_handler(self, handler_group_id, handler): if handler_group_id in self.handlers: self.handlers[handler_group_id].append(handler) @@ -47,6 +52,9 @@ def load_handlers(self, filename, del_file_after_loading): class FileHandlerBackend(HandlerBackend): + """ + :meta private: + """ def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120): super(FileHandlerBackend, self).__init__(handlers) self.filename = filename @@ -119,6 +127,9 @@ def return_load_handlers(filename, del_file_after_loading=True): class RedisHandlerBackend(HandlerBackend): + """ + :meta private: + """ def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot', password=None): super(RedisHandlerBackend, self).__init__(handlers) if not redis_installed: @@ -150,6 +161,14 @@ def get_handlers(self, handler_group_id): class State: + """ + Class representing a state. + + .. code-block:: python3 + + class MyStates(StatesGroup): + my_state = State() # returns my_state:State string. + """ def __init__(self) -> None: self.name = None def __str__(self) -> str: @@ -158,6 +177,14 @@ def __str__(self) -> str: class StatesGroup: + """ + Class representing common states. + + .. code-block:: python3 + + class MyStates(StatesGroup): + my_state = State() # returns my_state:State string. + """ def __init_subclass__(cls) -> None: for name, value in cls.__dict__.items(): if not name.startswith('__') and not callable(value) and isinstance(value, State): @@ -179,8 +206,13 @@ class BaseMiddleware: message update, then you will have to create pre_process_message function, and so on. Same applies to post_process. - .. code-block:: python - + .. note:: + If you want to use middleware, you have to set use_class_middlewares=True in your + TeleBot instance. + + .. code-block:: python3 + :caption: Example of class-based middlewares. + class MyMiddleware(BaseMiddleware): def __init__(self): self.update_sensitive = True diff --git a/telebot/types.py b/telebot/types.py index 5bab9c4cd..c2d932bd8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -20,6 +20,7 @@ class JsonSerializable(object): """ Subclasses of this class are guaranteed to be able to be converted to JSON format. All subclasses of this class must override to_json. + """ def to_json(self): @@ -36,6 +37,7 @@ class Dictionaryable(object): """ Subclasses of this class are guaranteed to be able to be converted to dictionary. All subclasses of this class must override to_dict. + """ def to_dict(self): @@ -90,6 +92,65 @@ def __str__(self): class Update(JsonDeserializable): + """ + This object represents an incoming update. + + https://core.telegram.org/bots/api#update + + :attribute update_id: The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. + This ID becomes especially handy if you're using webhooks, since it allows you to ignore repeated updates or to restore the correct + update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will + be chosen randomly instead of sequentially. + :type update_id: int + + :attribute message: New incoming message of any kind — text, photo, sticker, etc. + :type message: :class:`telebot.types.Message` + + :attribute edited_message: New version of a message that is known to the bot and was edited. + :type edited_message: :class:`telebot.types.Message` + + :attribute channel_post: New incoming channel post of any kind — text, photo, sticker, etc. + :type channel_post: :class:`telebot.types.Message` + + :attribute edited_channel_post: New version of a channel post that is known to the bot and was edited. + :type edited_channel_post: :class:`telebot.types.Message` + + :attribute inline_query: New incoming query from a user, which is answered by a bot and can be further processed. + :type inline_query: :class:`telebot.types.InlineQuery` + + :attribute chosen_inline_result: New incoming result of an inline query that was chosen by a user and sent to their chat partner. + :type chosen_inline_result: :class:`telebot.types.ChosenInlineResult` + + :attribute callback_query: New incoming callback query from a user. + :type callback_query: :class:`telebot.types.CallbackQuery` + + :attribute shipping_query: New incoming shipping query. Only for invoices with flexible price + :type shipping_query: :class:`telebot.types.ShippingQuery` + + :attribute pre_checkout_query: New incoming pre-checkout query. Contains full information about checkout + :type pre_checkout_query: :class:`telebot.types.PreCheckoutQuery` + + :attribute poll: New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot + :type poll: :class:`telebot.types.Poll` + + :attribute poll_answer: A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself. + :type poll_answer: :class:`telebot.types.PollAnswer` + + :attribute my_chat_member: The bot's chat member status was updated in a chat. For private chats, + this update is received only when the bot is blocked or unblocked by the user. + :type my_chat_member: :class:`telebot.types.ChatMember` + + :attribute chat_member: A chat member's status was updated in a chat. The bot must be an administrator in the chat and must + explicitly specify “chat_member” in the list of allowed_updates to receive these updates. + :type chat_member: :class:`telebot.types.ChatMember` + + :attribute chat_join_request: A request to join the chat has been sent. The bot must have the can_invite_users + administrator right in the chat to receive these updates. + :type chat_join_request: :class:`telebot.types.ChatJoinRequest` + + :return: An Update object. + :rtype: :class:`telebot.types.Update` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None diff --git a/telebot/util.py b/telebot/util.py index b9d7f7da2..8170f676f 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -35,11 +35,13 @@ thread_local = threading.local() +#: Contains all media content types. content_type_media = [ 'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll', 'venue', 'location' ] +#: Contains all service content types such as `User joined the group`. content_type_service = [ 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', @@ -47,6 +49,7 @@ 'video_chat_participants_invited', 'message_auto_delete_timer_changed' ] +#: All update types, should be used for allowed_updates parameter in polling. update_types = [ "message", "edited_message", "channel_post", "edited_channel_post", "inline_query", "chosen_inline_result", "callback_query", "shipping_query", "pre_checkout_query", "poll", "poll_answer", @@ -55,6 +58,9 @@ class WorkerThread(threading.Thread): + """ + :meta private: + """ count = 0 def __init__(self, exception_callback=None, queue=None, name=None): @@ -118,7 +124,9 @@ def stop(self): class ThreadPool: - + """ + :meta private: + """ def __init__(self, telebot, num_threads=2): self.telebot = telebot self.tasks = Queue.Queue() @@ -156,6 +164,9 @@ def close(self): class AsyncTask: + """ + :meta private: + """ def __init__(self, target, *args, **kwargs): self.target = target self.args = args @@ -182,6 +193,9 @@ def wait(self): class CustomRequestResponse(): + """ + :meta private: + """ def __init__(self, json_text, status_code = 200, reason = ""): self.status_code = status_code self.text = json_text @@ -192,6 +206,9 @@ def json(self): def async_dec(): + """ + :meta private: + """ def decorator(fn): def wrapper(*args, **kwargs): return AsyncTask(fn, *args, **kwargs) @@ -201,19 +218,49 @@ def wrapper(*args, **kwargs): return decorator -def is_string(var): +def is_string(var) -> bool: + """ + Returns True if the given object is a string. + """ return isinstance(var, str) -def is_dict(var): +def is_dict(var) -> bool: + """ + Returns True if the given object is a dictionary. + + :param var: object to be checked + :type var: :obj:`object` + + :return: True if the given object is a dictionary. + :rtype: :obj:`bool` + """ return isinstance(var, dict) -def is_bytes(var): +def is_bytes(var) -> bool: + """ + Returns True if the given object is a bytes object. + + :param var: object to be checked + :type var: :obj:`object` + + :return: True if the given object is a bytes object. + :rtype: :obj:`bool` + """ return isinstance(var, bytes) -def is_pil_image(var): +def is_pil_image(var) -> bool: + """ + Returns True if the given object is a PIL.Image.Image object. + + :param var: object to be checked + :type var: :obj:`object` + + :return: True if the given object is a PIL.Image.Image object. + :rtype: :obj:`bool` + """ return pil_imported and isinstance(var, Image.Image) @@ -233,7 +280,10 @@ def is_command(text: str) -> bool: Checks if `text` is a command. Telegram chat commands start with the '/' character. :param text: Text to check. + :type text: :obj:`str` + :return: True if `text` is a command, else False. + :rtype: :obj:`bool` """ if text is None: return False return text.startswith('/') @@ -244,30 +294,40 @@ def extract_command(text: str) -> Union[str, None]: Extracts the command from `text` (minus the '/') if `text` is a command (see is_command). If `text` is not a command, this function returns None. - Examples: - extract_command('/help'): 'help' - extract_command('/help@BotName'): 'help' - extract_command('/search black eyed peas'): 'search' - extract_command('Good day to you'): None + .. code-block:: python3 + :caption: Examples: + + extract_command('/help'): 'help' + extract_command('/help@BotName'): 'help' + extract_command('/search black eyed peas'): 'search' + extract_command('Good day to you'): None :param text: String to extract the command from + :type text: :obj:`str` + :return: the command if `text` is a command (according to is_command), else None. + :rtype: :obj:`str` or :obj:`None` """ if text is None: return None return text.split()[0].split('@')[0][1:] if is_command(text) else None -def extract_arguments(text: str) -> str: +def extract_arguments(text: str) -> str or None: """ Returns the argument after the command. - Examples: - extract_arguments("/get name"): 'name' - extract_arguments("/get"): '' - extract_arguments("/get@botName name"): 'name' + .. code-block:: python3 + :caption: Examples: + + extract_arguments("/get name"): 'name' + extract_arguments("/get"): '' + extract_arguments("/get@botName name"): 'name' :param text: String to extract the arguments from a command + :type text: :obj:`str` + :return: the arguments if `text` is a command (according to is_command), else None. + :rtype: :obj:`str` or :obj:`None` """ regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)", re.IGNORECASE) result = regexp.match(text) @@ -280,8 +340,13 @@ def split_string(text: str, chars_per_string: int) -> List[str]: This is very useful for splitting one giant message into multiples. :param text: The text to split + :type text: :obj:`str` + :param chars_per_string: The number of characters per line the text is split into. + :type chars_per_string: :obj:`int` + :return: The splitted text as a list of strings. + :rtype: :obj:`list` of :obj:`str` """ return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)] @@ -294,8 +359,13 @@ def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str Splits by '\n', '. ' or ' ' in exactly this priority. :param text: The text to split + :type text: :obj:`str` + :param chars_per_string: The number of maximum characters per part the text is split to. + :type chars_per_string: :obj:`int` + :return: The splitted text as a list of strings. + :rtype: :obj:`list` of :obj:`str` """ def _text_before_last(substr: str) -> str: @@ -336,12 +406,25 @@ def user_link(user: types.User, include_id: bool=False) -> str: Returns an HTML user link. This is useful for reports. Attention: Don't forget to set parse_mode to 'HTML'! - Example: - bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML') + + .. code-block:: python3 + :caption: Example: + + bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML') + + .. note:: + You can use formatting.* for all other formatting options(bold, italic, links, and etc.) + This method is kept for backward compatibility, and it is recommended to use formatting.* for + more options. :param user: the user (not the user_id) + :type user: :obj:`telebot.types.User` + :param include_id: include the user_id + :type include_id: :obj:`bool` + :return: HTML user link + :rtype: :obj:`str` """ name = escape(user.first_name) return (f"{name}" @@ -355,15 +438,16 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I Example: - .. code-block:: python + .. code-block:: python3 + :caption: Using quick_markup: quick_markup({ 'Twitter': {'url': 'https://twitter.com'}, 'Facebook': {'url': 'https://facebook.com'}, 'Back': {'callback_data': 'whatever'} }, row_width=2): - # returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook - # and a back button below + # returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook + # and a back button below # kwargs can be: { @@ -378,8 +462,13 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I } :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} + :type values: :obj:`dict` + :param row_width: int row width + :type row_width: :obj:`int` + :return: InlineKeyboardMarkup + :rtype: :obj:`types.InlineKeyboardMarkup` """ markup = types.InlineKeyboardMarkup(row_width=row_width) buttons = [ @@ -392,16 +481,25 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I # CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352 def or_set(self): + """ + :meta private: + """ self._set() self.changed() def or_clear(self): + """ + :meta private: + """ self._clear() self.changed() def orify(e, changed_callback): + """ + :meta private: + """ if not hasattr(e, "_set"): e._set = e.set if not hasattr(e, "_clear"): @@ -412,6 +510,9 @@ def orify(e, changed_callback): def OrEvent(*events): + """ + :meta private: + """ or_event = threading.Event() def changed(): @@ -435,6 +536,9 @@ def busy_wait(): def per_thread(key, construct_value, reset=False): + """ + :meta private: + """ if reset or not hasattr(thread_local, key): value = construct_value() setattr(thread_local, key, value) @@ -449,7 +553,13 @@ def chunks(lst, n): yield lst[i:i + n] -def generate_random_token(): +def generate_random_token() -> str: + """ + Generates a random token consisting of letters and digits, 16 characters long. + + :return: a random token + :rtype: :obj:`str` + """ return ''.join(random.sample(string.ascii_letters, 16)) @@ -457,10 +567,19 @@ def deprecated(warn: bool=True, alternative: Optional[Callable]=None, deprecatio """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. + + :meta private: :param warn: If True a warning is logged else an info + :type warn: :obj:`bool` + :param alternative: The new function to use instead + :type alternative: :obj:`Callable` + :param deprecation_text: Custom deprecation text + :type deprecation_text: :obj:`str` + + :return: The decorated function """ def decorator(function): def wrapper(*args, **kwargs): @@ -480,7 +599,17 @@ def wrapper(*args, **kwargs): # Cloud helpers def webhook_google_functions(bot, request): - """A webhook endpoint for Google Cloud Functions FaaS.""" + """ + A webhook endpoint for Google Cloud Functions FaaS. + + :param bot: The bot instance + :type bot: :obj:`telebot.TeleBot` or :obj:`telebot.async_telebot.AsyncTeleBot` + + :param request: The request object + :type request: :obj:`flask.Request` + + :return: The response object + """ if request.is_json: try: request_json = request.get_json() @@ -494,7 +623,7 @@ def webhook_google_functions(bot, request): return 'Bot ON' -def antiflood(function, *args, **kwargs): +def antiflood(function: Callable, *args, **kwargs): """ Use this function inside loops in order to avoid getting TooManyRequests error. Example: @@ -505,9 +634,15 @@ def antiflood(function, *args, **kwargs): for chat_id in chat_id_list: msg = antiflood(bot.send_message, chat_id, text) - :param function: - :param args: - :param kwargs: + :param function: The function to call + :type function: :obj:`Callable` + + :param args: The arguments to pass to the function + :type args: :obj:`tuple` + + :param kwargs: The keyword arguments to pass to the function + :type kwargs: :obj:`dict` + :return: None """ from telebot.apihelper import ApiTelegramException @@ -524,6 +659,17 @@ def antiflood(function, *args, **kwargs): def parse_web_app_data(token: str, raw_init_data: str): + """ + Parses web app data. + + :param token: The bot token + :type token: :obj:`str` + + :param raw_init_data: The raw init data + :type raw_init_data: :obj:`str` + + :return: The parsed init data + """ is_valid = validate_web_app_data(token, raw_init_data) if not is_valid: return False @@ -539,7 +685,18 @@ def parse_web_app_data(token: str, raw_init_data: str): return result -def validate_web_app_data(token, raw_init_data): +def validate_web_app_data(token: str, raw_init_data: str): + """ + Validates web app data. + + :param token: The bot token + :type token: :obj:`str` + + :param raw_init_data: The raw init data + :type raw_init_data: :obj:`str` + + :return: The parsed init data + """ try: parsed_data = dict(parse_qsl(raw_init_data)) except ValueError: From 9d9e76e724e05908cc793f0b3338f7e043fafb43 Mon Sep 17 00:00:00 2001 From: robz-tirtlib <62515024+robz-tirtlib@users.noreply.github.com> Date: Mon, 25 Jul 2022 15:49:33 +0300 Subject: [PATCH 1068/1808] small fix Fixed "ERROR - TeleBot: "message_handler: Commands filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use the supplied type." --- examples/custom_states.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/custom_states.py b/examples/custom_states.py index 72c96df9b..a02761286 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -47,7 +47,7 @@ def start_ex(message): # Any state -@bot.message_handler(state="*", commands='cancel') +@bot.message_handler(state="*", commands=['cancel']) def any_state(message): """ Cancel state From 3d7f334d794daeb8c23f41841c26a84bd79705e9 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 26 Jul 2022 16:16:35 +0500 Subject: [PATCH 1069/1808] Updated all docstrings for types(visual) --- telebot/types.py | 3777 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 3594 insertions(+), 183 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index c2d932bd8..1755ddd85 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -27,6 +27,8 @@ def to_json(self): """ Returns a JSON string representation of this class. + :meta private: + This function must be overridden by subclasses. :return: a JSON formatted string. """ @@ -44,6 +46,8 @@ def to_dict(self): """ Returns a DICT with class field values + :meta private: + This function must be overridden by subclasses. :return: a DICT """ @@ -61,6 +65,8 @@ def de_json(cls, json_string): """ Returns an instance of this class from the given json dict or string. + :meta private: + This function must be overridden by subclasses. :return: an instance of this class created from the given json dict or string. """ @@ -71,6 +77,8 @@ def check_json(json_type, dict_copy = True): """ Checks whether json_type is a dict or a string. If it is already a dict, it is returned as-is. If it is not, it is converted to a dict by means of json.loads(json_type) + + :meta private: :param json_type: input json or parsed dict :param dict_copy: if dict is passed and it is changed outside - should be True! @@ -93,63 +101,69 @@ def __str__(self): class Update(JsonDeserializable): """ - This object represents an incoming update. + This object represents an incoming update.At most one of the optional parameters can be present in any given update. - https://core.telegram.org/bots/api#update + Telegram Documentation: https://core.telegram.org/bots/api#update - :attribute update_id: The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. - This ID becomes especially handy if you're using webhooks, since it allows you to ignore repeated updates or to restore the correct - update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will - be chosen randomly instead of sequentially. - :type update_id: int + :param update_id: The update's unique identifier. Update identifiers start from a certain positive number and + increase sequentially. This ID becomes especially handy if you're using webhooks, since it allows you to ignore + repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates + for at least a week, then identifier of the next update will be chosen randomly instead of sequentially. + :type update_id: :obj:`int` - :attribute message: New incoming message of any kind — text, photo, sticker, etc. + :param message: Optional. New incoming message of any kind - text, photo, sticker, etc. :type message: :class:`telebot.types.Message` - :attribute edited_message: New version of a message that is known to the bot and was edited. + :param edited_message: Optional. New version of a message that is known to the bot and was edited :type edited_message: :class:`telebot.types.Message` - - :attribute channel_post: New incoming channel post of any kind — text, photo, sticker, etc. + + :param channel_post: Optional. New incoming channel post of any kind - text, photo, sticker, etc. :type channel_post: :class:`telebot.types.Message` - :attribute edited_channel_post: New version of a channel post that is known to the bot and was edited. + :param edited_channel_post: Optional. New version of a channel post that is known to the bot and was edited :type edited_channel_post: :class:`telebot.types.Message` - :attribute inline_query: New incoming query from a user, which is answered by a bot and can be further processed. + :param inline_query: Optional. New incoming inline query :type inline_query: :class:`telebot.types.InlineQuery` - :attribute chosen_inline_result: New incoming result of an inline query that was chosen by a user and sent to their chat partner. + :param chosen_inline_result: Optional. The result of an inline query that was chosen by a user and sent to their chat + partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your + bot. :type chosen_inline_result: :class:`telebot.types.ChosenInlineResult` - :attribute callback_query: New incoming callback query from a user. + :param callback_query: Optional. New incoming callback query :type callback_query: :class:`telebot.types.CallbackQuery` - :attribute shipping_query: New incoming shipping query. Only for invoices with flexible price + :param shipping_query: Optional. New incoming shipping query. Only for invoices with flexible price :type shipping_query: :class:`telebot.types.ShippingQuery` - :attribute pre_checkout_query: New incoming pre-checkout query. Contains full information about checkout + :param pre_checkout_query: Optional. New incoming pre-checkout query. Contains full information about + checkout :type pre_checkout_query: :class:`telebot.types.PreCheckoutQuery` - :attribute poll: New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot + :param poll: Optional. New poll state. Bots receive only updates about stopped polls and polls, which are sent by the + bot :type poll: :class:`telebot.types.Poll` - :attribute poll_answer: A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself. + :param poll_answer: Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in + polls that were sent by the bot itself. :type poll_answer: :class:`telebot.types.PollAnswer` - :attribute my_chat_member: The bot's chat member status was updated in a chat. For private chats, - this update is received only when the bot is blocked or unblocked by the user. - :type my_chat_member: :class:`telebot.types.ChatMember` + :param my_chat_member: Optional. The bot's chat member status was updated in a chat. For private chats, this update + is received only when the bot is blocked or unblocked by the user. + :type my_chat_member: :class:`telebot.types.ChatMemberUpdated` - :attribute chat_member: A chat member's status was updated in a chat. The bot must be an administrator in the chat and must - explicitly specify “chat_member” in the list of allowed_updates to receive these updates. - :type chat_member: :class:`telebot.types.ChatMember` + :param chat_member: Optional. A chat member's status was updated in a chat. The bot must be an administrator in the + chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. + :type chat_member: :class:`telebot.types.ChatMemberUpdated` - :attribute chat_join_request: A request to join the chat has been sent. The bot must have the can_invite_users - administrator right in the chat to receive these updates. + :param chat_join_request: Optional. A request to join the chat has been sent. The bot must have the + can_invite_users administrator right in the chat to receive these updates. :type chat_join_request: :class:`telebot.types.ChatJoinRequest` - :return: An Update object. + :return: Instance of the class :rtype: :class:`telebot.types.Update` + """ @classmethod def de_json(cls, json_string): @@ -195,6 +209,33 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan class ChatMemberUpdated(JsonDeserializable): + """ + This object represents changes in the status of a chat member. + + Telegram Documentation: https://core.telegram.org/bots/api#chatmemberupdated + + :param chat: Chat the user belongs to + :type chat: :class:`telebot.types.Chat` + + :param from_user: Performer of the action, which resulted in the change + :type from_user: :class:`telebot.types.User` + + :param date: Date the change was done in Unix time + :type date: :obj:`int` + + :param old_chat_member: Previous information about the chat member + :type old_chat_member: :class:`telebot.types.ChatMember` + + :param new_chat_member: New information about the chat member + :type new_chat_member: :class:`telebot.types.ChatMember` + + :param invite_link: Optional. Chat invite link, which was used by the user to join the chat; for joining by invite + link events only. + :type invite_link: :class:`telebot.types.ChatInviteLink` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatMemberUpdated` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -219,7 +260,11 @@ def difference(self) -> Dict[str, List]: """ Get the difference between `old_chat_member` and `new_chat_member` as a dict in the following format {'parameter': [old_value, new_value]} - E.g {'status': ['member', 'kicked'], 'until_date': [None, 1625055092]} + E.g {'status': ['member', 'kicked'], 'until_date': [None, 1625055092]} + + + :return: Dict of differences + :rtype: Dict[str, List] """ old: Dict = self.old_chat_member.__dict__ new: Dict = self.new_chat_member.__dict__ @@ -229,8 +274,32 @@ def difference(self) -> Dict[str, List]: if new[key] != old[key]: dif[key] = [old[key], new[key]] return dif + class ChatJoinRequest(JsonDeserializable): + """ + Represents a join request sent to a chat. + + Telegram Documentation: https://core.telegram.org/bots/api#chatjoinrequest + + :param chat: Chat to which the request was sent + :type chat: :class:`telebot.types.Chat` + + :param from: User that sent the join request + :type from_user: :class:`telebot.types.User` + + :param date: Date the request was sent in Unix time + :type date: :obj:`int` + + :param bio: Optional. Bio of the user. + :type bio: :obj:`str` + + :param invite_link: Optional. Chat invite link that was used by the user to send the join request + :type invite_link: :class:`telebot.types.ChatInviteLink` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatJoinRequest` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -248,6 +317,46 @@ def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs): self.invite_link = invite_link class WebhookInfo(JsonDeserializable): + """ + Describes the current status of a webhook. + + Telegram Documentation: https://core.telegram.org/bots/api#webhookinfo + + :param url: Webhook URL, may be empty if webhook is not set up + :type url: :obj:`str` + + :param has_custom_certificate: True, if a custom certificate was provided for webhook certificate checks + :type has_custom_certificate: :obj:`bool` + + :param pending_update_count: Number of updates awaiting delivery + :type pending_update_count: :obj:`int` + + :param ip_address: Optional. Currently used webhook IP address + :type ip_address: :obj:`str` + + :param last_error_date: Optional. Unix time for the most recent error that happened when trying to deliver an + update via webhook + :type last_error_date: :obj:`int` + + :param last_error_message: Optional. Error message in human-readable format for the most recent error that + happened when trying to deliver an update via webhook + :type last_error_message: :obj:`str` + + :param last_synchronization_error_date: Optional. Unix time of the most recent error that happened when trying + to synchronize available updates with Telegram datacenters + :type last_synchronization_error_date: :obj:`int` + + :param max_connections: Optional. The maximum allowed number of simultaneous HTTPS connections to the webhook + for update delivery + :type max_connections: :obj:`int` + + :param allowed_updates: Optional. A list of update types the bot is subscribed to. Defaults to all update types + except chat_member + :type allowed_updates: :obj:`list` of :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.WebhookInfo` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -269,6 +378,50 @@ def __init__(self, url, has_custom_certificate, pending_update_count, ip_address class User(JsonDeserializable, Dictionaryable, JsonSerializable): + """ + This object represents a Telegram user or bot. + + Telegram Documentation: https://core.telegram.org/bots/api#user + + :param id: Unique identifier for this user or bot. This number may have more than 32 significant bits and some + programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant + bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. + :type id: :obj:`int` + + :param is_bot: True, if this user is a bot + :type is_bot: :obj:`bool` + + :param first_name: User's or bot's first name + :type first_name: :obj:`str` + + :param last_name: Optional. User's or bot's last name + :type last_name: :obj:`str` + + :param username: Optional. User's or bot's username + :type username: :obj:`str` + + :param language_code: Optional. IETF language tag of the user's language + :type language_code: :obj:`str` + + :param is_premium: Optional. :obj:`bool`, if this user is a Telegram Premium user + :type is_premium: :obj:`bool` + + :param added_to_attachment_menu: Optional. :obj:`bool`, if this user added the bot to the attachment menu + :type added_to_attachment_menu: :obj:`bool` + + :param can_join_groups: Optional. True, if the bot can be invited to groups. Returned only in getMe. + :type can_join_groups: :obj:`bool` + + :param can_read_all_group_messages: Optional. True, if privacy mode is disabled for the bot. Returned only in + getMe. + :type can_read_all_group_messages: :obj:`bool` + + :param supports_inline_queries: Optional. True, if the bot supports inline queries. Returned only in getMe. + :type supports_inline_queries: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.User` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -293,6 +446,9 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua @property def full_name(self): + """ + :return: User's full name + """ full_name = self.first_name if self.last_name: full_name += ' {0}'.format(self.last_name) @@ -310,10 +466,15 @@ def to_dict(self): 'language_code': self.language_code, 'can_join_groups': self.can_join_groups, 'can_read_all_group_messages': self.can_read_all_group_messages, - 'supports_inline_queries': self.supports_inline_queries} + 'supports_inline_queries': self.supports_inline_queries, + 'is_premium': self.is_premium, + 'added_to_attachment_menu': self.added_to_attachment_menu} class GroupChat(JsonDeserializable): + """ + :meta private: + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -326,6 +487,95 @@ def __init__(self, id, title, **kwargs): class Chat(JsonDeserializable): + """ + This object represents a chat. + + Telegram Documentation: https://core.telegram.org/bots/api#chat + + :param id: Unique identifier for this chat. This number may have more than 32 significant bits and some programming + languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed + 64-bit integer or double-precision float type are safe for storing this identifier. + :type id: :obj:`int` + + :param type: Type of chat, can be either “private”, “group”, “supergroup” or “channel” + :type type: :obj:`str` + + :param title: Optional. Title, for supergroups, channels and group chats + :type title: :obj:`str` + + :param username: Optional. Username, for private chats, supergroups and channels if available + :type username: :obj:`str` + + :param first_name: Optional. First name of the other party in a private chat + :type first_name: :obj:`str` + + :param last_name: Optional. Last name of the other party in a private chat + :type last_name: :obj:`str` + + :param photo: Optional. Chat photo. Returned only in getChat. + :type photo: :class:`telebot.types.ChatPhoto` + + :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. + :type has_private_forwards: :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. + :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. + :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. + :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. + :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. + :type slow_mode_delay: :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. + :type message_auto_delete_time: :obj:`int` + + :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 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. + :type can_set_sticker_set: :obj:`bool` + + :param linked_chat_id: Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for + a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some + programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a + 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. + :type location: :class:`telebot.types.ChatLocation` + + :return: Instance of the class + :rtype: :class:`telebot.types.Chat` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -372,6 +622,17 @@ def __init__(self, id, type, title=None, username=None, first_name=None, class MessageID(JsonDeserializable): + """ + This object represents a unique message identifier. + + Telegram Documentation: https://core.telegram.org/bots/api#messageid + + :param message_id: Unique message identifier + :type message_id: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.MessageId` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -383,6 +644,22 @@ def __init__(self, message_id, **kwargs): class WebAppData(JsonDeserializable): + """ + Describes data sent from a Web App to the bot. + + Telegram Documentation: https://core.telegram.org/bots/api#webappdata + + :param data: The data. Be aware that a bad client can send arbitrary data in this field. + :type data: :obj:`str` + + :param button_text: Text of the web_app keyboard button from which the Web App was opened. Be aware that a bad client + can send arbitrary data in this field. + :type button_text: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.WebAppData` + """ + def __init__(self, data, button_text): self.data = data self.button_text = button_text @@ -397,6 +674,224 @@ def de_json(cls, json_string): class Message(JsonDeserializable): + """ + This object represents a message. + + Telegram Documentation: https://core.telegram.org/bots/api#message + + :param message_id: Unique message identifier inside this chat + :type message_id: :obj:`int` + + :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the + field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + :type from_user: :class:`telebot.types.User` + + :param sender_chat: Optional. Sender of the message, sent on behalf of a chat. For example, the channel itself for + channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for + messages automatically forwarded to the discussion group. For backward compatibility, the field from contains a + fake sender user in non-channel chats, if the message was sent on behalf of a chat. + :type sender_chat: :class:`telebot.types.Chat` + + :param date: Date the message was sent in Unix time + :type date: :obj:`int` + + :param chat: Conversation the message belongs to + :type chat: :class:`telebot.types.Chat` + + :param forward_from: Optional. For forwarded messages, sender of the original message + :type forward_from: :class:`telebot.types.User` + + :param forward_from_chat: Optional. For messages forwarded from channels or from anonymous administrators, + information about the original sender chat + :type forward_from_chat: :class:`telebot.types.Chat` + + :param forward_from_message_id: Optional. For messages forwarded from channels, identifier of the original + message in the channel + :type forward_from_message_id: :obj:`int` + + :param forward_signature: Optional. For forwarded messages that were originally sent in channels or by an + anonymous chat administrator, signature of the message sender if present + :type forward_signature: :obj:`str` + + :param forward_sender_name: Optional. Sender's name for messages forwarded from users who disallow adding a link + to their account in forwarded messages + :type forward_sender_name: :obj:`str` + + :param forward_date: Optional. For forwarded messages, date the original message was sent in Unix time + :type forward_date: :obj:`int` + + :param is_automatic_forward: Optional. :obj:`bool`, if the message is a channel post that was automatically + forwarded to the connected discussion group + :type is_automatic_forward: :obj:`bool` + + :param reply_to_message: Optional. For replies, the original message. Note that the Message object in this field + will not contain further reply_to_message fields even if it itself is a reply. + :type reply_to_message: :class:`telebot.types.Message` + + :param via_bot: Optional. Bot through which the message was sent + :type via_bot: :class:`telebot.types.User` + + :param edit_date: Optional. Date the message was last edited in Unix time + :type edit_date: :obj:`int` + + :param has_protected_content: Optional. :obj:`bool`, if the message can't be forwarded + :type has_protected_content: :obj:`bool` + + :param media_group_id: Optional. The unique identifier of a media message group this message belongs to + :type media_group_id: :obj:`str` + + :param author_signature: Optional. Signature of the post author for messages in channels, or the custom title of an + anonymous group administrator + :type author_signature: :obj:`str` + + :param text: Optional. For text messages, the actual UTF-8 text of the message + :type text: :obj:`str` + + :param entities: Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that + appear in the text + :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param animation: Optional. Message is an animation, information about the animation. For backward + compatibility, when this field is set, the document field will also be set + :type animation: :class:`telebot.types.Animation` + + :param audio: Optional. Message is an audio file, information about the file + :type audio: :class:`telebot.types.Audio` + + :param document: Optional. Message is a general file, information about the file + :type document: :class:`telebot.types.Document` + + :param photo: Optional. Message is a photo, available sizes of the photo + :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` + + :param sticker: Optional. Message is a sticker, information about the sticker + :type sticker: :class:`telebot.types.Sticker` + + :param video: Optional. Message is a video, information about the video + :type video: :class:`telebot.types.Video` + + :param video_note: Optional. Message is a video note, information about the video message + :type video_note: :class:`telebot.types.VideoNote` + + :param voice: Optional. Message is a voice message, information about the file + :type voice: :class:`telebot.types.Voice` + + :param caption: Optional. Caption for the animation, audio, document, photo, video or voice + :type caption: :obj:`str` + + :param caption_entities: Optional. For messages with a caption, special entities like usernames, URLs, bot + commands, etc. that appear in the caption + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param contact: Optional. Message is a shared contact, information about the contact + :type contact: :class:`telebot.types.Contact` + + :param dice: Optional. Message is a dice with random value + :type dice: :class:`telebot.types.Dice` + + :param game: Optional. Message is a game, information about the game. More about games » + :type game: :class:`telebot.types.Game` + + :param poll: Optional. Message is a native poll, information about the poll + :type poll: :class:`telebot.types.Poll` + + :param venue: Optional. Message is a venue, information about the venue. For backward compatibility, when this + field is set, the location field will also be set + :type venue: :class:`telebot.types.Venue` + + :param location: Optional. Message is a shared location, information about the location + :type location: :class:`telebot.types.Location` + + :param new_chat_members: Optional. New members that were added to the group or supergroup and information about + them (the bot itself may be one of these members) + :type new_chat_members: :obj:`list` of :class:`telebot.types.User` + + :param left_chat_member: Optional. A member was removed from the group, information about them (this member may be + the bot itself) + :type left_chat_member: :class:`telebot.types.User` + + :param new_chat_title: Optional. A chat title was changed to this value + :type new_chat_title: :obj:`str` + + :param new_chat_photo: Optional. A chat photo was change to this value + :type new_chat_photo: :obj:`list` of :class:`telebot.types.PhotoSize` + + :param delete_chat_photo: Optional. Service message: the chat photo was deleted + :type delete_chat_photo: :obj:`bool` + + :param group_chat_created: Optional. Service message: the group has been created + :type group_chat_created: :obj:`bool` + + :param supergroup_chat_created: Optional. Service message: the supergroup has been created. This field can't be + received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can + only be found in reply_to_message if someone replies to a very first message in a directly created supergroup. + :type supergroup_chat_created: :obj:`bool` + + :param channel_chat_created: Optional. Service message: the channel has been created. This field can't be + received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only + be found in reply_to_message if someone replies to a very first message in a channel. + :type channel_chat_created: :obj:`bool` + + :param message_auto_delete_timer_changed: Optional. Service message: auto-delete timer settings changed in + the chat + :type message_auto_delete_timer_changed: :class:`telebot.types.MessageAutoDeleteTimerChanged` + + :param migrate_to_chat_id: Optional. The group has been migrated to a supergroup with the specified identifier. + This number may have more than 32 significant bits and some programming languages may have difficulty/silent + defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision + float type are safe for storing this identifier. + :type migrate_to_chat_id: :obj:`int` + + :param migrate_from_chat_id: Optional. The supergroup has been migrated from a group with the specified + identifier. This number may have more than 32 significant bits and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this identifier. + :type migrate_from_chat_id: :obj:`int` + + :param pinned_message: Optional. Specified message was pinned. Note that the Message object in this field will not + contain further reply_to_message fields even if it is itself a reply. + :type pinned_message: :class:`telebot.types.Message` + + :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » + :type invoice: :class:`telebot.types.Invoice` + + :param successful_payment: Optional. Message is a service message about a successful payment, information about + the payment. More about payments » + :type successful_payment: :class:`telebot.types.SuccessfulPayment` + + :param connected_website: Optional. The domain name of the website on which the user has logged in. More about + Telegram Login » + :type connected_website: :obj:`str` + + :param passport_data: Optional. Telegram Passport data + :type passport_data: :class:`telebot.types.PassportData` + + :param proximity_alert_triggered: Optional. Service message. A user in the chat triggered another user's + proximity alert while sharing Live Location. + :type proximity_alert_triggered: :class:`telebot.types.ProximityAlertTriggered` + + :param video_chat_scheduled: Optional. Service message: video chat scheduled + :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` + + :param video_chat_started: Optional. Service message: video chat started + :type video_chat_started: :class:`telebot.types.VideoChatStarted` + + :param video_chat_ended: Optional. Service message: video chat ended + :type video_chat_ended: :class:`telebot.types.VideoChatEnded` + + :param video_chat_participants_invited: Optional. Service message: new participants invited to a video chat + :type video_chat_participants_invited: :class:`telebot.types.VideoChatParticipantsInvited` + + :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. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :return: Instance of the class + :rtype: :class:`telebot.types.Message` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -568,6 +1063,9 @@ def de_json(cls, json_string): @classmethod def parse_chat(cls, chat): + """ + Parses chat. + """ if 'first_name' not in chat: return GroupChat.de_json(chat) else: @@ -575,6 +1073,9 @@ def parse_chat(cls, chat): @classmethod def parse_photo(cls, photo_size_array): + """ + Parses photo array. + """ ret = [] for ps in photo_size_array: ret.append(PhotoSize.de_json(ps)) @@ -582,6 +1083,9 @@ def parse_photo(cls, photo_size_array): @classmethod def parse_entities(cls, message_entity_array): + """ + Parses message entity array. + """ ret = [] for me in message_entity_array: ret.append(MessageEntity.de_json(me)) @@ -650,13 +1154,17 @@ def __html_text(self, text, entities): Updaters: @badiboy Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" - Example: + .. code-block:: python3 + :caption: Example: + message.html_text >> "Test parse formatting, url, text_mention and mention @username" Custom subs: You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. - Example: + .. code-block:: python3 + :caption: Example: + message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} message.html_text >> "Test parse formatting, url and text_mention and mention @username" @@ -716,16 +1224,56 @@ def func(upd_text, subst_type=None, url=None, user=None): @property def html_text(self): + """ + Returns html-rendered text. + """ return self.__html_text(self.text, self.entities) @property def html_caption(self): + """ + Returns html-rendered caption. + """ return self.__html_text(self.caption, self.caption_entities) class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): + """ + This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. + + Telegram Documentation: https://core.telegram.org/bots/api#messageentity + + :param type: Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), “cashtag” + ($USD), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” + (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), + “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “code” + (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users + without usernames) + :type type: :obj:`str` + + :param offset: Offset in UTF-16 code units to the start of the entity + :type offset: :obj:`int` + + :param length: Length of the entity in UTF-16 code units + :type length: :obj:`int` + + :param url: Optional. For “text_link” only, URL that will be opened after user taps on the text + :type url: :obj:`str` + + :param user: Optional. For “text_mention” only, the mentioned user + :type user: :class:`telebot.types.User` + + :param language: Optional. For “pre” only, the programming language of the entity text + :type language: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.MessageEntity` + """ @staticmethod def to_list_of_dicts(entity_list) -> Union[List[Dict], None]: + """ + Converts a list of MessageEntity objects to a list of dictionaries. + """ res = [] for e in entity_list: res.append(MessageEntity.to_dict(e)) @@ -760,6 +1308,20 @@ def to_dict(self): class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): + """ + This object represents an animated emoji that displays a random value. + + Telegram Documentation: https://core.telegram.org/bots/api#dice + + :param emoji: Emoji on which the dice throw animation is based + :type emoji: :obj:`str` + + :param value: Value of the dice, 1-6 for “🎲”, “🎯” and “🎳” base emoji, 1-5 for “🏀” and “⚽” base emoji, 1-64 for “🎰” base emoji + :type value: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Dice` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -779,6 +1341,30 @@ def to_dict(self): class PhotoSize(JsonDeserializable): + """ + This object represents one size of a photo or a file / sticker thumbnail. + + Telegram Documentation: https://core.telegram.org/bots/api#photosize + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param width: Photo width + :type width: :obj:`int` + + :param height: Photo height + :type height: :obj:`int` + + :param file_size: Optional. File size in bytes + :type file_size: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.PhotoSize` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -794,6 +1380,44 @@ def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwa class Audio(JsonDeserializable): + """ + This object represents an audio file to be treated as music by the Telegram clients. + + Telegram Documentation: https://core.telegram.org/bots/api#audio + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param duration: Duration of the audio in seconds as defined by sender + :type duration: :obj:`int` + + :param performer: Optional. Performer of the audio as defined by sender or by audio tags + :type performer: :obj:`str` + + :param title: Optional. Title of the audio as defined by sender or by audio tags + :type title: :obj:`str` + + :param file_name: Optional. Original filename as defined by sender + :type file_name: :obj:`str` + + :param mime_type: Optional. MIME type of the file as defined by sender + :type mime_type: :obj:`str` + + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this value. + :type file_size: :obj:`int` + + :param thumb: Optional. Thumbnail of the album cover to which the music file belongs + :type thumb: :class:`telebot.types.PhotoSize` + + :return: Instance of the class + :rtype: :class:`telebot.types.Audio` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -818,6 +1442,32 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None class Voice(JsonDeserializable): + """ + This object represents a voice note. + + Telegram Documentation: https://core.telegram.org/bots/api#voice + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param duration: Duration of the audio in seconds as defined by sender + :type duration: :obj:`int` + + :param mime_type: Optional. MIME type of the file as defined by sender + :type mime_type: :obj:`str` + + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this value. + :type file_size: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Voice` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -833,6 +1483,35 @@ def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size= class Document(JsonDeserializable): + """ + This object represents a general file (as opposed to photos, voice messages and audio files). + + Telegram Documentation: https://core.telegram.org/bots/api#document + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param thumb: Optional. Document thumbnail as defined by sender + :type thumb: :class:`telebot.types.PhotoSize` + + :param file_name: Optional. Original filename as defined by sender + :type file_name: :obj:`str` + + :param mime_type: Optional. MIME type of the file as defined by sender + :type mime_type: :obj:`str` + + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this value. + :type file_size: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Document` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -853,6 +1532,44 @@ def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_typ class Video(JsonDeserializable): + """ + This object represents a video file. + + Telegram Documentation: https://core.telegram.org/bots/api#video + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param width: Video width as defined by sender + :type width: :obj:`int` + + :param height: Video height as defined by sender + :type height: :obj:`int` + + :param duration: Duration of the video in seconds as defined by sender + :type duration: :obj:`int` + + :param thumb: Optional. Video thumbnail + :type thumb: :class:`telebot.types.PhotoSize` + + :param file_name: Optional. Original filename as defined by sender + :type file_name: :obj:`str` + + :param mime_type: Optional. MIME type of the file as defined by sender + :type mime_type: :obj:`str` + + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this value. + :type file_size: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Video` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -874,15 +1591,42 @@ def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, class VideoNote(JsonDeserializable): - @classmethod - def de_json(cls, json_string): - if json_string is None: return None - obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) - return cls(**obj) + """ + This object represents a video message (available in Telegram apps as of v.4.0). - def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): + Telegram Documentation: https://core.telegram.org/bots/api#videonote + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param length: Video width and height (diameter of the video message) as defined by sender + :type length: :obj:`int` + + :param duration: Duration of the video in seconds as defined by sender + :type duration: :obj:`int` + + :param thumb: Optional. Video thumbnail + :type thumb: :class:`telebot.types.PhotoSize` + + :param file_size: Optional. File size in bytes + :type file_size: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.VideoNote` + """ + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'thumb' in obj and 'file_id' in obj['thumb']: + obj['thumb'] = PhotoSize.de_json(obj['thumb']) + return cls(**obj) + + def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.length: int = length @@ -892,6 +1636,31 @@ def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_s class Contact(JsonDeserializable): + """ + This object represents a phone contact. + + Telegram Documentation: https://core.telegram.org/bots/api#contact + + :param phone_number: Contact's phone number + :type phone_number: :obj:`str` + + :param first_name: Contact's first name + :type first_name: :obj:`str` + + :param last_name: Optional. Contact's last name + :type last_name: :obj:`str` + + :param user_id: Optional. Contact's user identifier in Telegram. This number may have more than 32 significant bits + and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 + significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. + :type user_id: :obj:`int` + + :param vcard: Optional. Additional data about the contact in the form of a vCard + :type vcard: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.Contact` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -907,6 +1676,34 @@ def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard class Location(JsonDeserializable, JsonSerializable, Dictionaryable): + """ + This object represents a point on the map. + + Telegram Documentation: https://core.telegram.org/bots/api#location + + :param longitude: Longitude as defined by sender + :type longitude: :obj:`float` + + :param latitude: Latitude as defined by sender + :type latitude: :obj:`float` + + :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. Time relative to the message sending date, during which the location can be updated; + in seconds. For active live locations only. + :type live_period: :obj:`int` + + :param heading: Optional. The direction in which user is moving, in degrees; 1-360. For active live locations only. + :type heading: :obj:`int` + + :param proximity_alert_radius: Optional. The maximum distance for proximity alerts about approaching another + chat member, in meters. For sent live locations only. + :type proximity_alert_radius: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Location` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -937,6 +1734,36 @@ def to_dict(self): class Venue(JsonDeserializable): + """ + This object represents a venue. + + Telegram Documentation: https://core.telegram.org/bots/api#venue + + :param location: Venue location. Can't be a live location + :type location: :class:`telebot.types.Location` + + :param title: Name of the venue + :type title: :obj:`str` + + :param address: Address of the venue + :type address: :obj:`str` + + :param foursquare_id: Optional. Foursquare identifier of the venue + :type foursquare_id: :obj:`str` + + :param foursquare_type: Optional. Foursquare type of the venue. (For example, “arts_entertainment/default”, + “arts_entertainment/aquarium” or “food/icecream”.) + :type foursquare_type: :obj:`str` + + :param google_place_id: Optional. Google Places identifier of the venue + :type google_place_id: :obj:`str` + + :param google_place_type: Optional. Google Places type of the venue. (See supported types.) + :type google_place_type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.Venue` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -956,6 +1783,20 @@ def __init__(self, location, title, address, foursquare_id=None, foursquare_type class UserProfilePhotos(JsonDeserializable): + """ + This object represent a user's profile pictures. + + Telegram Documentation: https://core.telegram.org/bots/api#userprofilephotos + + :param total_count: Total number of profile pictures the target user has + :type total_count: :obj:`int` + + :param photos: Requested profile pictures (in up to 4 sizes each) + :type photos: :obj:`list` of :obj:`list` of :class:`telebot.types.PhotoSize` + + :return: Instance of the class + :rtype: :class:`telebot.types.UserProfilePhotos` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -971,6 +1812,30 @@ def __init__(self, total_count, photos=None, **kwargs): class File(JsonDeserializable): + """ + This object represents a file ready to be downloaded. The file can be downloaded via the link https://api.telegram.org/file/bot/. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling getFile. + + Telegram Documentation: https://core.telegram.org/bots/api#file + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this value. + :type file_size: :obj:`int` + + :param file_path: Optional. File path. Use https://api.telegram.org/file/bot/ to get the + file. + :type file_path: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.File` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -985,6 +1850,27 @@ def __init__(self, file_id, file_unique_id, file_size=None, file_path=None, **kw class ForceReply(JsonSerializable): + """ + Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode. + + Telegram Documentation: https://core.telegram.org/bots/api#forcereply + + :param force_reply: Shows reply interface to the user, as if they manually selected the bot's message and tapped + 'Reply' + :type force_reply: :obj:`bool` + + :param input_field_placeholder: Optional. The placeholder to be shown in the input field when the reply is active; + 1-64 characters + :type input_field_placeholder: :obj:`str` + + :param selective: Optional. Use this parameter if you want to force reply from specific users only. Targets: 1) users + that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), + sender of the original message. + :type selective: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.ForceReply` + """ def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Optional[str]=None): self.selective: bool = selective self.input_field_placeholder: str = input_field_placeholder @@ -999,6 +1885,27 @@ def to_json(self): class ReplyKeyboardRemove(JsonSerializable): + """ + Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button (see ReplyKeyboardMarkup). + + Telegram Documentation: https://core.telegram.org/bots/api#replykeyboardremove + + :param remove_keyboard: Requests clients to remove the custom keyboard (user will not be able to summon this + keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in + ReplyKeyboardMarkup) + Note that this parameter is set to True by default by the library. You cannot modify it. + :type remove_keyboard: :obj:`bool` + + :param selective: Optional. Use this parameter if you want to remove the keyboard for specific users only. Targets: + 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has + reply_to_message_id), sender of the original message.Example: A user votes in a poll, bot returns confirmation + message in reply to the vote and removes the keyboard for that user, while still showing the keyboard with poll options + to users who haven't voted yet. + :type selective: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.ReplyKeyboardRemove` + """ def __init__(self, selective=None): self.selective: bool = selective @@ -1010,6 +1917,17 @@ def to_json(self): class WebAppInfo(JsonDeserializable): + """ + Describes a Web App. + + Telegram Documentation: https://core.telegram.org/bots/api#webappinfo + + :param url: An HTTPS URL of a Web App to be opened with additional data as specified in Initializing Web Apps + :type url: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.WebAppInfo` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1024,6 +1942,51 @@ def to_dict(self): class ReplyKeyboardMarkup(JsonSerializable): + """ + This object represents a custom keyboard with reply options (see Introduction to bots for details and examples). + + .. code-block:: python3 + :caption: Example on creating ReplyKeyboardMarkup object + + from telebot.types import ReplyKeyboardMarkup, KeyboardButton + + markup = ReplyKeyboardMarkup(resize_keyboard=True) + markup.add(KeyboardButton('Text')) + # or: + markup.add('Text') + + # display this markup: + bot.send_message(chat_id, 'Text', reply_markup=markup) + + Telegram Documentation: https://core.telegram.org/bots/api#replykeyboardmarkup + + :param keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of + :class:`telebot.types.KeyboardButton` objects + :type keyboard: :obj:`list` of :obj:`list` of :class:`telebot.types.KeyboardButton` + + :param resize_keyboard: Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make + the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is + always of the same height as the app's standard keyboard. + :type resize_keyboard: :obj:`bool` + + :param one_time_keyboard: Optional. Requests clients to hide the keyboard as soon as it's been used. The keyboard + will still be available, but clients will automatically display the usual letter-keyboard in the chat - the user can + press a special button in the input field to see the custom keyboard again. Defaults to false. + :type one_time_keyboard: :obj:`bool` + + :param input_field_placeholder: Optional. The placeholder to be shown in the input field when the keyboard is + active; 1-64 characters + :type input_field_placeholder: :obj:`str` + + :param selective: Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) + users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has + reply_to_message_id), sender of the original message.Example: A user requests to change the bot's language, bot + replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard. + :type selective: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.ReplyKeyboardMarkup` + """ max_row_keys = 12 def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, @@ -1050,8 +2013,13 @@ def add(self, *args, row_width=None): See https://core.telegram.org/bots/api#replykeyboardmarkup :param args: KeyboardButton to append to the keyboard + :type args: :obj:`str` or :class:`telebot.types.KeyboardButton` + :param row_width: width of row + :type row_width: :obj:`int` + :return: self, to allow function chaining. + :rtype: :class:`telebot.types.ReplyKeyboardMarkup` """ if row_width is None: row_width = self.row_width @@ -1082,7 +2050,10 @@ def row(self, *args): See https://core.telegram.org/bots/api#replykeyboardmarkup :param args: strings + :type args: :obj:`str` + :return: self, to allow function chaining. + :rtype: :class:`telebot.types.ReplyKeyboardMarkup` """ return self.add(*args, row_width=self.max_row_keys) @@ -1106,6 +2077,18 @@ def to_json(self): 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. + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.KeyboardButtonPollType` + """ def __init__(self, type=''): self.type: str = type @@ -1114,6 +2097,34 @@ def to_dict(self): class KeyboardButton(Dictionaryable, JsonSerializable): + """ + This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields web_app, request_contact, request_location, and request_poll are mutually exclusive. + + Telegram Documentation: https://core.telegram.org/bots/api#keyboardbutton + + :param text: Text of the button. If none of the optional fields are used, it will be sent as a message when the button is + pressed + :type text: :obj:`str` + + :param request_contact: Optional. If True, the user's phone number will be sent as a contact when the button is + pressed. Available in private chats only. + :type request_contact: :obj:`bool` + + :param request_location: Optional. If True, the user's current location will be sent when the button is pressed. + Available in private chats only. + :type request_location: :obj:`bool` + + :param request_poll: Optional. If specified, the user will be asked to create a poll and send it to the bot when the + button is pressed. Available in private chats only. + :type request_poll: :class:`telebot.types.KeyboardButtonPollType` + + :param web_app: Optional. If specified, the described Web App will be launched when the button is pressed. The Web App + will be able to send a “web_app_data” service message. Available in private chats only. + :type web_app: :class:`telebot.types.WebAppInfo` + + :return: Instance of the class + :rtype: :class:`telebot.types.KeyboardButton` + """ def __init__(self, text: str, request_contact: Optional[bool]=None, request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, web_app: WebAppInfo=None): @@ -1140,6 +2151,31 @@ def to_dict(self): class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable): + """ + This object represents an inline keyboard that appears right next to the message it belongs to. + + .. note:: + It is recommended to use :meth:`telebot.util.quick_markup` instead. + + .. code-block:: python3 + :caption: Example of a custom keyboard with buttons. + + from telebot.util import quick_markup + + markup = quick_markup( + {'text': 'Press me', 'callback_data': 'press'}, + {'text': 'Press me too', 'callback_data': 'press_too'} + ) + + Telegram Documentation: https://core.telegram.org/bots/api#inlinekeyboardmarkup + + :param inline_keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of + :class:`telebot.types.InlineKeyboardButton` objects + :type inline_keyboard: :obj:`list` of :obj:`list` of :class:`telebot.types.InlineKeyboardButton` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineKeyboardMarkup` + """ max_row_keys = 8 @classmethod @@ -1150,12 +2186,6 @@ def de_json(cls, json_string): return cls(keyboard = keyboard) def __init__(self, keyboard=None, row_width=3): - """ - This object represents an inline keyboard that appears - right next to the message it belongs to. - - :return: None - """ if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) @@ -1176,8 +2206,13 @@ def add(self, *args, row_width=None): See https://core.telegram.org/bots/api#inlinekeyboardmarkup :param args: Array of InlineKeyboardButton to append to the keyboard + :type args: :obj:`list` of :class:`telebot.types.InlineKeyboardButton` + :param row_width: width of row + :type row_width: :obj:`int` + :return: self, to allow function chaining. + :rtype: :class:`telebot.types.InlineKeyboardMarkup` """ if row_width is None: row_width = self.row_width @@ -1203,18 +2238,15 @@ def row(self, *args): See https://core.telegram.org/bots/api#inlinekeyboardmarkup :param args: Array of InlineKeyboardButton to append to the keyboard + :type args: :obj:`list` of :class:`telebot.types.InlineKeyboardButton` + :return: self, to allow function chaining. + :rtype: :class:`telebot.types.InlineKeyboardMarkup` """ return self.add(*args, row_width=self.max_row_keys) def to_json(self): - """ - Converts this object to its json representation - following the Telegram API guidelines described here: - https://core.telegram.org/bots/api#inlinekeyboardmarkup - :return: - """ return json.dumps(self.to_dict()) def to_dict(self): @@ -1224,6 +2256,54 @@ def to_dict(self): class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable): + """ + This object represents one button of an inline keyboard. You must use exactly one of the optional fields. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinekeyboardbutton + + :param text: Label text on the button + :type text: :obj:`str` + + :param url: Optional. HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id= can be + used to mention a user by their ID without using a username, if this is allowed by their privacy settings. + :type url: :obj:`str` + + :param callback_data: Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes + :type callback_data: :obj:`str` + + :param web_app: Optional. Description of the Web App that will be launched when the user presses the button. The Web + App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only + in private chats between a user and the bot. + :type web_app: :class:`telebot.types.WebAppInfo` + + :param login_url: Optional. An HTTPS URL used to automatically authorize the user. Can be used as a replacement for + the Telegram Login Widget. + :type login_url: :class:`telebot.types.LoginUrl` + + :param switch_inline_query: Optional. If set, pressing the button will prompt the user to select one of their chats, + open that chat and insert the bot's username and the specified inline query in the input field. May be empty, in which + case just the bot's username will be inserted.Note: This offers an easy way for users to start using your bot in inline + mode when they are currently in a private chat with it. Especially useful when combined with switch_pm… actions - in + this case the user will be automatically returned to the chat they switched from, skipping the chat selection screen. + :type switch_inline_query: :obj:`str` + + :param switch_inline_query_current_chat: Optional. If set, pressing the button will insert the bot's username + and the specified inline query in the current chat's input field. May be empty, in which case only the bot's username + will be inserted.This offers a quick way for the user to open your bot in inline mode in the same chat - good for selecting + something from multiple options. + :type switch_inline_query_current_chat: :obj:`str` + + :param callback_game: Optional. Description of the game that will be launched when the user presses the + button. NOTE: This type of button must always be the first button in the first row. + :type callback_game: :class:`telebot.types.CallbackGame` + + :param pay: Optional. Specify True, to send a Pay button. NOTE: This type of button must always be the first button in + the first row and can only be used in invoice messages. + :type pay: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineKeyboardButton` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1272,6 +2352,33 @@ def to_dict(self): class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): + """ + This object represents a parameter of the inline keyboard button used to automatically authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is coming from Telegram. All the user needs to do is tap/click a button and confirm that they want to log in: + + Telegram Documentation: https://core.telegram.org/bots/api#loginurl + + :param url: An HTTPS URL to be opened with user authorization data added to the query string when the button is pressed. + If the user refuses to provide authorization data, the original URL without information about the user will be + opened. The data added is the same as described in Receiving authorization data. NOTE: You must always check the hash + of the received data to verify the authentication and the integrity of the data as described in Checking + authorization. + :type url: :obj:`str` + + :param forward_text: Optional. New text of the button in forwarded messages. + :type forward_text: :obj:`str` + + :param bot_username: Optional. Username of a bot, which will be used for user authorization. See Setting up a bot for + more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the + domain linked with the bot. See Linking your domain to the bot for more details. + :type bot_username: :obj:`str` + + :param request_write_access: Optional. Pass True to request the permission for your bot to send messages to the + user. + :type request_write_access: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.LoginUrl` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1299,6 +2406,39 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#callbackquery + + :param id: Unique identifier for this query + :type id: :obj:`str` + + :param from_user: Sender + :type from_user: :class:`telebot.types.User` + + :param message: Optional. Message with the callback button that originated the query. Note that message content and + message date will not be available if the message is too old + :type message: :class:`telebot.types.Message` + + :param inline_message_id: Optional. Identifier of the message sent via the bot in inline mode, that originated the + query. + :type inline_message_id: :obj:`str` + + :param chat_instance: Global identifier, uniquely corresponding to the chat to which the message with the callback + button was sent. Useful for high scores in games. + :type chat_instance: :obj:`str` + + :param data: Optional. Data associated with the callback button. Be aware that the message originated the query can + contain no callback buttons with this data. + :type data: :obj:`str` + + :param game_short_name: Optional. Short name of a Game to be returned, serves as the unique identifier for the game + :type game_short_name: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.CallbackQuery` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1324,6 +2464,30 @@ def __init__(self, id, from_user, data, chat_instance, json_string, message=None class ChatPhoto(JsonDeserializable): + """ + This object represents a chat photo. + + Telegram Documentation: https://core.telegram.org/bots/api#chatphoto + + :param small_file_id: File identifier of small (160x160) chat photo. This file_id can be used only for photo + download and only for as long as the photo is not changed. + :type small_file_id: :obj:`str` + + :param small_file_unique_id: Unique file identifier of small (160x160) chat photo, which is supposed to be the same + over time and for different bots. Can't be used to download or reuse the file. + :type small_file_unique_id: :obj:`str` + + :param big_file_id: File identifier of big (640x640) chat photo. This file_id can be used only for photo download and + only for as long as the photo is not changed. + :type big_file_id: :obj:`str` + + :param big_file_unique_id: Unique file identifier of big (640x640) chat photo, which is supposed to be the same over + time and for different bots. Can't be used to download or reuse the file. + :type big_file_unique_id: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatPhoto` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1338,6 +2502,20 @@ def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_un class ChatMember(JsonDeserializable): + """ + This object contains information about one member of a chat. + Currently, the following 6 types of chat members are supported: + + * :class:`telebot.types.ChatMemberOwner` + * :class:`telebot.types.ChatMemberAdministrator` + * :class:`telebot.types.ChatMemberMember` + * :class:`telebot.types.ChatMemberRestricted` + * :class:`telebot.types.ChatMemberLeft` + * :class:`telebot.types.ChatMemberBanned` + + Telegram Documentation: https://core.telegram.org/bots/api#chatmember + """ + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1395,30 +2573,238 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed class ChatMemberOwner(ChatMember): + """ + Represents a chat member that owns the chat and has all administrator privileges. + + Telegram Documentation: https://core.telegram.org/bots/api#chatmemberowner + + :param status: The member's status in the chat, always “creator” + :type status: :obj:`str` + + :param user: Information about the user + :type user: :class:`telebot.types.User` + + :param is_anonymous: True, if the user's presence in the chat is hidden + :type is_anonymous: :obj:`bool` + + :param custom_title: Optional. Custom title for this user + :type custom_title: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatMemberOwner` + """ pass class ChatMemberAdministrator(ChatMember): + """ + Represents a chat member that has some additional privileges. + + Telegram Documentation: https://core.telegram.org/bots/api#chatmemberadministrator + + :param status: The member's status in the chat, always “administrator” + :type status: :obj:`str` + + :param user: Information about the user + :type user: :class:`telebot.types.User` + + :param can_be_edited: True, if the bot is allowed to edit administrator privileges of that user + :type can_be_edited: :obj:`bool` + + :param is_anonymous: True, if the user's presence in the chat is hidden + :type is_anonymous: :obj:`bool` + + :param can_manage_chat: True, if the administrator can access the chat event log, chat statistics, message + statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. + Implied by any other administrator privilege + :type can_manage_chat: :obj:`bool` + + :param can_delete_messages: True, if the administrator can delete messages of other users + :type can_delete_messages: :obj:`bool` + + :param can_manage_video_chats: True, if the administrator can manage video chats + :type can_manage_video_chats: :obj:`bool` + + :param can_restrict_members: True, if the administrator can restrict, ban or unban chat members + :type can_restrict_members: :obj:`bool` + + :param can_promote_members: True, if the administrator can add new administrators with a subset of their own + privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that + were appointed by the user) + :type can_promote_members: :obj:`bool` + + :param can_change_info: True, if the user is allowed to change the chat title, photo and other settings + :type can_change_info: :obj:`bool` + + :param can_invite_users: True, if the user is allowed to invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_post_messages: Optional. True, if the administrator can post in the channel; channels only + :type can_post_messages: :obj:`bool` + + :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin + messages; channels only + :type can_edit_messages: :obj:`bool` + + :param can_pin_messages: Optional. True, if the user is allowed to pin messages; groups and supergroups only + :type can_pin_messages: :obj:`bool` + + :param custom_title: Optional. Custom title for this user + :type custom_title: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatMemberAdministrator` + """ pass class ChatMemberMember(ChatMember): + """ + Represents a chat member that has no additional privileges or restrictions. + + Telegram Documentation: https://core.telegram.org/bots/api#chatmembermember + + :param status: The member's status in the chat, always “member” + :type status: :obj:`str` + + :param user: Information about the user + :type user: :class:`telebot.types.User` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatMemberMember` + """ pass class ChatMemberRestricted(ChatMember): + """ + Represents a chat member that is under certain restrictions in the chat. Supergroups only. + + Telegram Documentation: https://core.telegram.org/bots/api#chatmemberrestricted + + :param status: The member's status in the chat, always “restricted” + :type status: :obj:`str` + + :param user: Information about the user + :type user: :class:`telebot.types.User` + + :param is_member: True, if the user is a member of the chat at the moment of the request + :type is_member: :obj:`bool` + + :param can_change_info: True, if the user is allowed to change the chat title, photo and other settings + :type can_change_info: :obj:`bool` + + :param can_invite_users: True, if the user is allowed to invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_pin_messages: True, if the user is allowed to pin messages + :type can_pin_messages: :obj:`bool` + + :param can_send_messages: True, if the user is allowed to send text messages, contacts, locations and venues + :type can_send_messages: :obj:`bool` + + :param can_send_media_messages: True, if the user is allowed to send audios, documents, photos, videos, video + notes and voice notes + :type can_send_media_messages: :obj:`bool` + + :param can_send_polls: True, if the user is allowed to send polls + :type can_send_polls: :obj:`bool` + + :param can_send_other_messages: True, if the user is allowed to send animations, games, stickers and use inline + bots + :type can_send_other_messages: :obj:`bool` + + :param can_add_web_page_previews: True, if the user is allowed to add web page previews to their messages + :type can_add_web_page_previews: :obj:`bool` + + :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is restricted + forever + :type until_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatMemberRestricted` + """ pass class ChatMemberLeft(ChatMember): + """ + Represents a chat member that isn't currently a member of the chat, but may join it themselves. + + Telegram Documentation: https://core.telegram.org/bots/api#chatmemberleft + + :param status: The member's status in the chat, always “left” + :type status: :obj:`str` + + :param user: Information about the user + :type user: :class:`telebot.types.User` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatMemberLeft` + """ pass class ChatMemberBanned(ChatMember): + """ + Represents a chat member that was banned in the chat and can't return to the chat or view chat messages. + + Telegram Documentation: https://core.telegram.org/bots/api#chatmemberbanned + + :param status: The member's status in the chat, always “kicked” + :type status: :obj:`str` + + :param user: Information about the user + :type user: :class:`telebot.types.User` + + :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is banned + forever + :type until_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatMemberBanned` + """ pass class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): + """ + Describes actions that a non-administrator user is allowed to take in a chat. + + Telegram Documentation: https://core.telegram.org/bots/api#chatpermissions + + :param can_send_messages: Optional. True, if the user is allowed to send text messages, contacts, locations and + venues + :type can_send_messages: :obj:`bool` + + :param can_send_media_messages: Optional. True, if the user is allowed to send audios, documents, photos, videos, + video notes and voice notes, implies can_send_messages + :type can_send_media_messages: :obj:`bool` + + :param can_send_polls: Optional. True, if the user is allowed to send polls, implies can_send_messages + :type can_send_polls: :obj:`bool` + + :param can_send_other_messages: Optional. True, if the user is allowed to send animations, games, stickers and use + inline bots, implies can_send_media_messages + :type can_send_other_messages: :obj:`bool` + + :param can_add_web_page_previews: Optional. True, if the user is allowed to add web page previews to their + messages, implies can_send_media_messages + :type can_add_web_page_previews: :obj:`bool` + + :param can_change_info: Optional. True, if the user is allowed to change the chat title, photo and other settings. + Ignored in public supergroups + :type can_change_info: :obj:`bool` + + :param can_invite_users: Optional. True, if the user is allowed to invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_pin_messages: Optional. True, if the user is allowed to pin messages. Ignored in public supergroups + :type can_pin_messages: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatPermissions` + """ @classmethod def de_json(cls, json_string): if json_string is None: return json_string @@ -1463,6 +2849,21 @@ def to_dict(self): class BotCommand(JsonSerializable, JsonDeserializable): + """ + This object represents a bot command. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommand + + :param command: Text of the command; 1-32 characters. Can contain only lowercase English letters, digits and + underscores. + :type command: :obj:`str` + + :param description: Description of the command; 1-256 characters. + :type description: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommand` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1470,13 +2871,6 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, command, description): - """ - This object represents a bot command. - :param command: Text of the command, 1-32 characters. - Can contain only lowercase English letters, digits and underscores. - :param description: Description of the command, 3-256 characters. - :return: - """ self.command: str = command self.description: str = description @@ -1490,18 +2884,48 @@ def to_dict(self): # BotCommandScopes class BotCommandScope(ABC, JsonSerializable): + """ + This object represents the scope to which bot commands are applied. Currently, the following 7 scopes are supported: + + * :class:`BotCommandScopeDefault` + * :class:`BotCommandScopeAllPrivateChats` + * :class:`BotCommandScopeAllGroupChats` + * :class:`BotCommandScopeAllChatAdministrators` + * :class:`BotCommandScopeChat` + * :class:`BotCommandScopeChatAdministrators` + * :class:`BotCommandScopeChatMember` + + Determining list of commands + The following algorithm is used to determine the list of commands for a particular user viewing the bot menu. The first list of commands which is set is returned: + + Commands in the chat with the bot: + + * :class:`BotCommandScopeChat` + language_code + * :class:`BotCommandScopeChat` + * :class:`BotCommandScopeAllPrivateChats` + language_code + * :class:`BotCommandScopeAllPrivateChats` + * :class:`BotCommandScopeDefault` + language_code + * :class:`BotCommandScopeDefault` + + Commands in group and supergroup chats: + + * :class:`BotCommandScopeChatMember` + language_code + * :class:`BotCommandScopeChatMember` + * :class:`BotCommandScopeChatAdministrators` + language_code (administrators only) + * :class:`BotCommandScopeChatAdministrators` (administrators only) + * :class:`BotCommandScopeChat` + language_code + * :class:`BotCommandScopeChat` + * :class:`BotCommandScopeAllChatAdministrators` + language_code (administrators only) + * :class:`BotCommandScopeAllChatAdministrators` (administrators only) + * :class:`BotCommandScopeAllGroupChats` + language_code + * :class:`BotCommandScopeAllGroupChats` + * :class:`BotCommandScopeDefault` + language_code + * :class:`BotCommandScopeDefault` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScope` + """ def __init__(self, type='default', chat_id=None, user_id=None): - """ - Abstract class. - Use BotCommandScopeX classes to set a specific scope type: - BotCommandScopeDefault - BotCommandScopeAllPrivateChats - BotCommandScopeAllGroupChats - BotCommandScopeAllChatAdministrators - BotCommandScopeChat - BotCommandScopeChatAdministrators - BotCommandScopeChatMember - """ self.type: str = type self.chat_id: Optional[Union[int, str]] = chat_id self.user_id: Optional[Union[int, str]] = user_id @@ -1516,6 +2940,17 @@ def to_json(self): class BotCommandScopeDefault(BotCommandScope): + """ + Represents the default scope of bot commands. Default commands are used if no commands with a narrower scope are specified for the user. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommandscopedefault + + :param type: Scope type, must be default + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScopeDefault` + """ def __init__(self): """ Represents the default scope of bot commands. @@ -1525,6 +2960,17 @@ def __init__(self): class BotCommandScopeAllPrivateChats(BotCommandScope): + """ + Represents the scope of bot commands, covering all private chats. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommandscopeallprivatechats + + :param type: Scope type, must be all_private_chats + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScopeAllPrivateChats` + """ def __init__(self): """ Represents the scope of bot commands, covering all private chats. @@ -1533,6 +2979,17 @@ def __init__(self): class BotCommandScopeAllGroupChats(BotCommandScope): + """ + Represents the scope of bot commands, covering all group and supergroup chats. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommandscopeallgroupchats + + :param type: Scope type, must be all_group_chats + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScopeAllGroupChats` + """ def __init__(self): """ Represents the scope of bot commands, covering all group and supergroup chats. @@ -1541,6 +2998,17 @@ def __init__(self): class BotCommandScopeAllChatAdministrators(BotCommandScope): + """ + Represents the scope of bot commands, covering all group and supergroup chat administrators. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommandscopeallchatadministrators + + :param type: Scope type, must be all_chat_administrators + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScopeAllChatAdministrators` + """ def __init__(self): """ Represents the scope of bot commands, covering all group and supergroup chat administrators. @@ -1549,32 +3017,100 @@ def __init__(self): class BotCommandScopeChat(BotCommandScope): + """ + Represents the scope of bot commands, covering a specific chat. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommandscopechat + + :param type: Scope type, must be chat + :type type: :obj:`str` + + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format + @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScopeChat` + """ def __init__(self, chat_id=None): super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) class BotCommandScopeChatAdministrators(BotCommandScope): + """ + Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommandscopechatadministrators + + :param type: Scope type, must be chat_administrators + :type type: :obj:`str` + + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format + @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScopeChatAdministrators` + """ def __init__(self, chat_id=None): - """ - Represents the scope of bot commands, covering a specific chat. - @param chat_id: Unique identifier for the target chat - """ super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) class BotCommandScopeChatMember(BotCommandScope): + """ + Represents the scope of bot commands, covering a specific member of a group or supergroup chat. + + Telegram Documentation: https://core.telegram.org/bots/api#botcommandscopechatmember + + :param type: Scope type, must be chat_member + :type type: :obj:`str` + + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format + @supergroupusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotCommandScopeChatMember` + """ def __init__(self, chat_id=None, user_id=None): - """ - Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat - @param chat_id: Unique identifier for the target chat - @param user_id: Unique identifier of the target user - """ super(BotCommandScopeChatMember, self).__init__(type='chat_member', chat_id=chat_id, user_id=user_id) # InlineQuery 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequery + + :param id: Unique identifier for this query + :type id: :obj:`str` + + :param from_user: Sender + :type from_user: :class:`telebot.types.User` + + :param query: Text of the query (up to 256 characters) + :type query: :obj:`str` + + :param offset: Offset of the results to be returned, can be controlled by the bot + :type offset: :obj:`str` + + :param chat_type: Optional. Type of the chat from which the inline query was sent. Can be either “sender” for a private + chat with the inline query sender, “private”, “group”, “supergroup”, or “channel”. The chat type should be always + known for requests sent from official clients and most third-party clients, unless the request was sent from a secret + chat + :type chat_type: :obj:`str` + + :param location: Optional. Sender location, only for bots that request user location + :type location: :class:`telebot.types.Location` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQuery` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1585,20 +3121,6 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, id, from_user, query, offset, chat_type=None, location=None, **kwargs): - """ - This object represents an incoming inline query. - When the user sends an empty query, your bot could - return some default or trending results. - :param id: string Unique identifier for this query - :param from_user: User Sender - :param query: String Text of the query - :param chat_type: String Type of the chat, from which the inline query was sent. - Can be either “sender” for a private chat with the inline query sender, - “private”, “group”, “supergroup”, or “channel”. - :param offset: String Offset of the results to be returned, can be controlled by the bot - :param location: Sender location, only for bots that request user location - :return: InlineQuery Object - """ self.id: int = id self.from_user: User = from_user self.query: str = query @@ -1608,6 +3130,28 @@ def __init__(self, id, from_user, query, offset, chat_type=None, location=None, class InputTextMessageContent(Dictionaryable): + """ + Represents the content of a text message to be sent as the result of an inline query. + + Telegram Documentation: https://core.telegram.org/bots/api#inputtextmessagecontent + + :param message_text: Text of the message to be sent, 1-4096 characters + :type message_text: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the message text. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param entities: Optional. List of special entities that appear in message text, which can be specified instead of + parse_mode + :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param disable_web_page_preview: Optional. Disables link previews for links in the sent message + :type disable_web_page_preview: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputTextMessageContent` + """ def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None): self.message_text: str = message_text self.parse_mode: str = parse_mode @@ -1626,6 +3170,35 @@ def to_dict(self): class InputLocationMessageContent(Dictionaryable): + """ + Represents the content of a location message to be sent as the result of an inline query. + + Telegram Documentation: https://core.telegram.org/bots/api#inputlocationmessagecontent + + :param latitude: Latitude of the location in degrees + :type latitude: :obj:`float` + + :param longitude: Longitude of the location in degrees + :type longitude: :obj:`float` + + :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. + :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. + :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. + :type proximity_alert_radius: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputLocationMessageContent` + """ def __init__(self, latitude, longitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None): self.latitude: float = latitude self.longitude: float = longitude @@ -1648,6 +3221,39 @@ def to_dict(self): class InputVenueMessageContent(Dictionaryable): + """ + Represents the content of a venue message to be sent as the result of an inline query. + + Telegram Documentation: https://core.telegram.org/bots/api#inputvenuemessagecontent + + :param latitude: Latitude of the venue in degrees + :type latitude: :obj:`float` + + :param longitude: Longitude of the venue in degrees + :type longitude: :obj:`float` + + :param title: Name of the venue + :type title: :obj:`str` + + :param address: Address of the venue + :type address: :obj:`str` + + :param foursquare_id: Optional. Foursquare identifier of the venue, if known + :type foursquare_id: :obj:`str` + + :param foursquare_type: Optional. Foursquare type of the venue, if known. (For example, + “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) + :type foursquare_type: :obj:`str` + + :param google_place_id: Optional. Google Places identifier of the venue + :type google_place_id: :obj:`str` + + :param google_place_type: Optional. Google Places type of the venue. (See supported types.) + :type google_place_type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputVenueMessageContent` + """ def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, google_place_id=None, google_place_type=None): self.latitude: float = latitude @@ -1678,6 +3284,26 @@ def to_dict(self): class InputContactMessageContent(Dictionaryable): + """ + Represents the content of a contact message to be sent as the result of an inline query. + + Telegram Documentation: https://core.telegram.org/bots/api#inputcontactmessagecontent + + :param phone_number: Contact's phone number + :type phone_number: :obj:`str` + + :param first_name: Contact's first name + :type first_name: :obj:`str` + + :param last_name: Optional. Contact's last name + :type last_name: :obj:`str` + + :param vcard: Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes + :type vcard: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputContactMessageContent` + """ def __init__(self, phone_number, first_name, last_name=None, vcard=None): self.phone_number: str = phone_number self.first_name: str = first_name @@ -1694,6 +3320,84 @@ def to_dict(self): class InputInvoiceMessageContent(Dictionaryable): + """ + Represents the content of an invoice message to be sent as the result of an inline query. + + Telegram Documentation: https://core.telegram.org/bots/api#inputinvoicemessagecontent + + :param title: Product name, 1-32 characters + :type title: :obj:`str` + + :param description: Product description, 1-255 characters + :type description: :obj:`str` + + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your + internal processes. + :type payload: :obj:`str` + + :param provider_token: Payment provider token, obtained via @BotFather + :type provider_token: :obj:`str` + + :param currency: Three-letter ISO 4217 currency code, see more on currencies + :type currency: :obj:`str` + + :param prices: Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery + cost, delivery tax, bonus, etc.) + :type prices: :obj:`list` of :class:`telebot.types.LabeledPrice` + + :param max_tip_amount: Optional. The maximum accepted amount for tips in the smallest units of the currency + (integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_amount = 145. See the exp + parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the + majority of currencies). Defaults to 0 + :type max_tip_amount: :obj:`int` + + :param suggested_tip_amounts: Optional. A JSON-serialized array of suggested amounts of tip in the smallest units + of the currency (integer, not float/double). 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. + :type suggested_tip_amounts: :obj:`list` of :obj:`int` + + :param provider_data: Optional. A JSON-serialized object for data about the invoice, which will be shared with the + payment provider. A detailed description of the required fields should be provided by the payment provider. + :type provider_data: :obj:`str` + + :param photo_url: Optional. URL of the product photo for the invoice. Can be a photo of the goods or a marketing image + for a service. + :type photo_url: :obj:`str` + + :param photo_size: Optional. Photo size in bytes + :type photo_size: :obj:`int` + + :param photo_width: Optional. Photo width + :type photo_width: :obj:`int` + + :param photo_height: Optional. Photo height + :type photo_height: :obj:`int` + + :param need_name: Optional. Pass True, if you require the user's full name to complete the order + :type need_name: :obj:`bool` + + :param need_phone_number: Optional. Pass True, if you require the user's phone number to complete the order + :type need_phone_number: :obj:`bool` + + :param need_email: Optional. Pass True, if you require the user's email address to complete the order + :type need_email: :obj:`bool` + + :param need_shipping_address: Optional. Pass True, if you require the user's shipping address to complete the + order + :type need_shipping_address: :obj:`bool` + + :param send_phone_number_to_provider: Optional. Pass True, if the user's phone number should be sent to provider + :type send_phone_number_to_provider: :obj:`bool` + + :param send_email_to_provider: Optional. Pass True, if the user's email address should be sent to provider + :type send_email_to_provider: :obj:`bool` + + :param is_flexible: Optional. Pass True, if the final price depends on the shipping method + :type is_flexible: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputInvoiceMessageContent` + """ def __init__(self, title, description, payload, provider_token, currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, @@ -1762,6 +3466,30 @@ def to_dict(self): class ChosenInlineResult(JsonDeserializable): + """ + Represents a result of an inline query that was chosen by the user and sent to their chat partner. + + Telegram Documentation: https://core.telegram.org/bots/api#choseninlineresult + + :param result_id: The unique identifier for the result that was chosen + :type result_id: :obj:`str` + + :param from: The user that chose the result + :type from: :class:`telebot.types.User` + + :param location: Optional. Sender location, only for bots that require user location + :type location: :class:`telebot.types.Location` + + :param inline_message_id: Optional. Identifier of the sent inline message. Available only if there is an inline + keyboard attached to the message. Will be also received in callback queries and can be used to edit the message. + :type inline_message_id: :obj:`str` + + :param query: The query that was used to obtain the result + :type query: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChosenInlineResult` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1772,14 +3500,6 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, result_id, from_user, query, location=None, inline_message_id=None, **kwargs): - """ - This object represents a result of an inline query - that was chosen by the user and sent to their chat partner. - :param result_id: string The unique identifier for the result that was chosen. - :param from_user: User The user that chose the result. - :param query: String The query that was used to obtain the result. - :return: ChosenInlineResult Object. - """ self.result_id: str = result_id self.from_user: User = from_user self.location: Location = location @@ -1788,6 +3508,32 @@ def __init__(self, result_id, from_user, query, location=None, inline_message_id class InlineQueryResultBase(ABC, Dictionaryable, JsonSerializable): + """ + This object represents one result of an inline query. Telegram clients currently support results of the following 20 types: + + * :class:`InlineQueryResultCachedAudio` + * :class:`InlineQueryResultCachedDocument` + * :class:`InlineQueryResultCachedGif` + * :class:`InlineQueryResultCachedMpeg4Gif` + * :class:`InlineQueryResultCachedPhoto` + * :class:`InlineQueryResultCachedSticker` + * :class:`InlineQueryResultCachedVideo` + * :class:`InlineQueryResultCachedVoice` + * :class:`InlineQueryResultArticle` + * :class:`InlineQueryResultAudio` + * :class:`InlineQueryResultContact` + * :class:`InlineQueryResultGame` + * :class:`InlineQueryResultDocument` + * :class:`InlineQueryResultGif` + * :class:`InlineQueryResultLocation` + * :class:`InlineQueryResultMpeg4Gif` + * :class:`InlineQueryResultPhoto` + * :class:`InlineQueryResultVenue` + * :class:`InlineQueryResultVideo` + * :class:`InlineQueryResultVoice` + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresult + """ # noinspection PyShadowingBuiltins def __init__(self, type, id, title = None, caption = None, input_message_content = None, reply_markup = None, caption_entities = None, parse_mode = None): @@ -1824,6 +3570,18 @@ def to_dict(self): class SentWebAppMessage(JsonDeserializable): + """ + Describes an inline message sent by a Web App on behalf of a user. + + Telegram Documentation: https://core.telegram.org/bots/api#sentwebappmessage + + :param inline_message_id: Optional. Identifier of the sent inline message. Available only if there is an inline + keyboard attached to the message. + :type inline_message_id: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.SentWebAppMessage` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1839,27 +3597,51 @@ def to_dict(self): json_dict['inline_message_id'] = self.inline_message_id return json_dict - +class InlineQueryResultArticle(InlineQueryResultBase): + """ + Represents a link to an article or web page. + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultarticle -class InlineQueryResultArticle(InlineQueryResultBase): + :param type: Type of the result, must be article + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 Bytes + :type id: :obj:`str` + + :param title: Title of the result + :type title: :obj:`str` + + :param input_message_content: Content of the message to be sent + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param url: Optional. URL of the result + :type url: :obj:`str` + + :param hide_url: Optional. Pass True, if you don't want the URL to be shown in the message + :type hide_url: :obj:`bool` + + :param description: Optional. Short description of the result + :type description: :obj:`str` + + :param thumb_url: Optional. Url of the thumbnail for the result + :type thumb_url: :obj:`str` + + :param thumb_width: Optional. Thumbnail width + :type thumb_width: :obj:`int` + + :param thumb_height: Optional. Thumbnail height + :type thumb_height: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultArticle` + """ 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. - :param title: Title of the result. - :param input_message_content: InputMessageContent : Content of the message to be sent - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param url: URL of the result. - :param hide_url: Pass True, if you don't want the URL to be shown in the message. - :param description: Short description of the result. - :param thumb_url: Url of the thumbnail for the result. - :param thumb_width: Thumbnail width. - :param thumb_height: Thumbnail height - :return: - """ super().__init__('article', id, title = title, input_message_content = input_message_content, reply_markup = reply_markup) self.url = url self.hide_url = hide_url @@ -1886,24 +3668,57 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultphoto + + :param type: Type of the result, must be photo + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param photo_url: A valid URL of the photo. Photo must be in JPEG format. Photo size must not exceed 5MB + :type photo_url: :obj:`str` + + :param thumb_url: URL of the thumbnail for the photo + :type thumb_url: :obj:`str` + + :param photo_width: Optional. Width of the photo + :type photo_width: :obj:`int` + + :param photo_height: Optional. Height of the photo + :type photo_height: :obj:`int` + + :param title: Optional. Title for the result + :type title: :obj:`str` + + :param description: Optional. Short description of the result + :type description: :obj:`str` + + :param caption: Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the photo + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultPhoto` + """ def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=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 - :param photo_url: A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB - :param thumb_url: URL of the thumbnail for the photo - :param photo_width: Width of the photo. - :param photo_height: Height of the photo. - :param title: Title for the result. - :param description: Short description of the result. - :param caption: Caption of the photo to be sent, 0-200 characters. - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or - inline URLs in the media caption. - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ 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) @@ -1927,23 +3742,62 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultgif + + :param type: Type of the result, must be gif + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param gif_url: A valid URL for the GIF file. File size must not exceed 1MB + :type gif_url: :obj:`str` + + :param gif_width: Optional. Width of the GIF + :type gif_width: :obj:`int` + + :param gif_height: Optional. Height of the GIF + :type gif_height: :obj:`int` + + :param gif_duration: Optional. Duration of the GIF in seconds + :type gif_duration: :obj:`int` + + :param thumb_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result + :type thumb_url: :obj:`str` + + :param thumb_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or + “video/mp4”. Defaults to “image/jpeg” + :type thumb_mime_type: :obj:`str` + + :param title: Optional. Title for the result + :type title: :obj:`str` + + :param caption: Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the GIF animation + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultGif` + """ 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, thumb_mime_type=None): - """ - Represents a link to an animated GIF file. - :param id: Unique identifier for this result, 1-64 bytes. - :param gif_url: A valid URL for the GIF file. File size must not exceed 1MB - :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result. - :param gif_width: Width of the GIF. - :param gif_height: Height of the GIF. - :param title: Title for the result. - :param caption: Caption of the GIF file to be sent, 0-200 characters - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ 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) @@ -1970,25 +3824,62 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif + + :param type: Type of the result, must be mpeg4_gif + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param mpeg4_url: A valid URL for the MPEG4 file. File size must not exceed 1MB + :type mpeg4_url: :obj:`str` + + :param mpeg4_width: Optional. Video width + :type mpeg4_width: :obj:`int` + + :param mpeg4_height: Optional. Video height + :type mpeg4_height: :obj:`int` + + :param mpeg4_duration: Optional. Video duration in seconds + :type mpeg4_duration: :obj:`int` + + :param thumb_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result + :type thumb_url: :obj:`str` + + :param thumb_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or + “video/mp4”. Defaults to “image/jpeg” + :type thumb_mime_type: :obj:`str` + + :param title: Optional. Title for the result + :type title: :obj:`str` + + :param caption: Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the video animation + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultMpeg4Gif` + """ 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, thumb_mime_type=None): - """ - Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). - :param id: Unique identifier for this result, 1-64 bytes - :param mpeg4_url: A valid URL for the MP4 file. File size must not exceed 1MB - :param thumb_url: URL of the static thumbnail (jpeg or gif) for the result - :param mpeg4_width: Video width - :param mpeg4_height: Video height - :param title: Title for the result - :param caption: Caption of the MPEG-4 file to be sent, 0-200 characters - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text - or inline URLs in the media caption. - :param reply_markup: InlineKeyboardMarkup : Inline keyboard attached to the message - :param input_message_content: InputMessageContent : Content of the message to be sent instead of the photo - :return: - """ 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) @@ -2015,25 +3906,66 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultvideo + + :param type: Type of the result, must be video + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param video_url: A valid URL for the embedded video player or video file + :type video_url: :obj:`str` + + :param mime_type: MIME type of the content of the video URL, “text/html” or “video/mp4” + :type mime_type: :obj:`str` + + :param thumb_url: URL of the thumbnail (JPEG only) for the video + :type thumb_url: :obj:`str` + + :param title: Title for the result + :type title: :obj:`str` + + :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param video_width: Optional. Video width + :type video_width: :obj:`int` + + :param video_height: Optional. Video height + :type video_height: :obj:`int` + + :param video_duration: Optional. Video duration in seconds + :type video_duration: :obj:`int` + + :param description: Optional. Short description of the result + :type description: :obj:`str` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the video. This field is + required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultVideo` + """ 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. - :param id: Unique identifier for this result, 1-64 bytes - :param video_url: A valid URL for the embedded video player or video file - :param mime_type: Mime type of the content of video url, “text/html” or “video/mp4” - :param thumb_url: URL of the thumbnail (jpeg only) for the video - :param title: Title for the result - :param parse_mode: Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or - inline URLs in the media caption. - :param video_width: Video width - :param video_height: Video height - :param video_duration: Video duration in seconds - :param description: Short description of the result - :return: - """ 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) @@ -2060,6 +3992,49 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultaudio + + :param type: Type of the result, must be audio + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param audio_url: A valid URL for the audio file + :type audio_url: :obj:`str` + + :param title: Title + :type title: :obj:`str` + + :param caption: Optional. Caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param performer: Optional. Performer + :type performer: :obj:`str` + + :param audio_duration: Optional. Audio duration in seconds + :type audio_duration: :obj:`int` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the audio + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultAudio` + """ 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): @@ -2081,6 +4056,46 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultvoice + + :param type: Type of the result, must be voice + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param voice_url: A valid URL for the voice recording + :type voice_url: :obj:`str` + + :param title: Recording title + :type title: :obj:`str` + + :param caption: Optional. Caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the voice message caption. See formatting options for + more details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param voice_duration: Optional. Recording duration in seconds + :type voice_duration: :obj:`int` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the voice recording + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultVoice` + """ 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, @@ -2098,6 +4113,58 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultdocument + + :param type: Type of the result, must be document + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param title: Title for the result + :type title: :obj:`str` + + :param caption: Optional. Caption of the document to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param document_url: A valid URL for the file + :type document_url: :obj:`str` + + :param mime_type: MIME type of the content of the file, either “application/pdf” or “application/zip” + :type mime_type: :obj:`str` + + :param description: Optional. Short description of the result + :type description: :obj:`str` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the file + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :param thumb_url: Optional. URL of the thumbnail (JPEG only) for the file + :type thumb_url: :obj:`str` + + :param thumb_width: Optional. Thumbnail width + :type thumb_width: :obj:`int` + + :param thumb_height: Optional. Thumbnail height + :type thumb_height: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultDocument` + """ 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): @@ -2127,6 +4194,59 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultlocation + + :param type: Type of the result, must be location + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 Bytes + :type id: :obj:`str` + + :param latitude: Location latitude in degrees + :type latitude: :obj:`float` number + + :param longitude: Location longitude in degrees + :type longitude: :obj:`float` number + + :param title: Location title + :type title: :obj:`str` + + :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. + :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. + :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. + :type proximity_alert_radius: :obj:`int` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the location + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :param thumb_url: Optional. Url of the thumbnail for the result + :type thumb_url: :obj:`str` + + :param thumb_width: Optional. Thumbnail width + :type thumb_width: :obj:`int` + + :param thumb_height: Optional. Thumbnail height + :type thumb_height: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultLocation` + """ 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, heading=None, proximity_alert_radius = None): super().__init__('location', id, title = title, @@ -2163,6 +4283,60 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultvenue + + :param type: Type of the result, must be venue + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 Bytes + :type id: :obj:`str` + + :param latitude: Latitude of the venue location in degrees + :type latitude: :obj:`float` + + :param longitude: Longitude of the venue location in degrees + :type longitude: :obj:`float` + + :param title: Title of the venue + :type title: :obj:`str` + + :param address: Address of the venue + :type address: :obj:`str` + + :param foursquare_id: Optional. Foursquare identifier of the venue if known + :type foursquare_id: :obj:`str` + + :param foursquare_type: Optional. Foursquare type of the venue, if known. (For example, + “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) + :type foursquare_type: :obj:`str` + + :param google_place_id: Optional. Google Places identifier of the venue + :type google_place_id: :obj:`str` + + :param google_place_type: Optional. Google Places type of the venue. (See supported types.) + :type google_place_type: :obj:`str` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the venue + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :param thumb_url: Optional. Url of the thumbnail for the result + :type thumb_url: :obj:`str` + + :param thumb_width: Optional. Thumbnail width + :type thumb_width: :obj:`int` + + :param thumb_height: Optional. Thumbnail height + :type thumb_height: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultVenue` + """ 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): @@ -2202,6 +4376,47 @@ def to_dict(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcontact + + :param type: Type of the result, must be contact + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 Bytes + :type id: :obj:`str` + + :param phone_number: Contact's phone number + :type phone_number: :obj:`str` + + :param first_name: Contact's first name + :type first_name: :obj:`str` + + :param last_name: Optional. Contact's last name + :type last_name: :obj:`str` + + :param vcard: Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes + :type vcard: :obj:`str` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the contact + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :param thumb_url: Optional. Url of the thumbnail for the result + :type thumb_url: :obj:`str` + + :param thumb_width: Optional. Thumbnail width + :type thumb_width: :obj:`int` + + :param thumb_height: Optional. Thumbnail height + :type thumb_height: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultContact` + """ 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): @@ -2233,6 +4448,26 @@ def to_dict(self): class InlineQueryResultGame(InlineQueryResultBase): + """ + Represents a Game. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultgame + + :param type: Type of the result, must be game + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param game_short_name: Short name of the game + :type game_short_name: :obj:`str` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultGame` + """ 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 @@ -2244,6 +4479,9 @@ def to_dict(self): class InlineQueryResultCachedBase(ABC, JsonSerializable): + """ + Base class of all InlineQueryResultCached* classes. + """ def __init__(self): self.type = None self.id = None @@ -2278,6 +4516,46 @@ def to_json(self): 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcachedphoto + + :param type: Type of the result, must be photo + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param photo_file_id: A valid file identifier of the photo + :type photo_file_id: :obj:`str` + + :param title: Optional. Title for the result + :type title: :obj:`str` + + :param description: Optional. Short description of the result + :type description: :obj:`str` + + :param caption: Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the photo + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedPhoto` + """ 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): @@ -2296,6 +4574,42 @@ def __init__(self, id, photo_file_id, title=None, description=None, 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcachedgif + + :param type: Type of the result, must be gif + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param gif_file_id: A valid file identifier for the GIF file + :type gif_file_id: :obj:`str` + + :param title: Optional. Title for the result + :type title: :obj:`str` + + :param caption: Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the GIF animation + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedGif` + """ 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): @@ -2314,6 +4628,42 @@ def __init__(self, id, gif_file_id, title=None, description=None, 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif + + :param type: Type of the result, must be mpeg4_gif + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param mpeg4_file_id: A valid file identifier for the MPEG4 file + :type mpeg4_file_id: :obj:`str` + + :param title: Optional. Title for the result + :type title: :obj:`str` + + :param caption: Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the video animation + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedMpeg4Gif` + """ 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): @@ -2332,6 +4682,29 @@ def __init__(self, id, mpeg4_file_id, title=None, description=None, 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcachedsticker + + :param type: Type of the result, must be sticker + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param sticker_file_id: A valid file identifier of the sticker + :type sticker_file_id: :obj:`str` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the sticker + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedSticker` + """ def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content=None): InlineQueryResultCachedBase.__init__(self) self.type = 'sticker' @@ -2343,6 +4716,46 @@ def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcacheddocument + + :param type: Type of the result, must be document + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param title: Title for the result + :type title: :obj:`str` + + :param document_file_id: A valid file identifier for the file + :type document_file_id: :obj:`str` + + :param description: Optional. Short description of the result + :type description: :obj:`str` + + :param caption: Optional. Caption of the document to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the file + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedDocument` + """ 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): @@ -2361,6 +4774,46 @@ def __init__(self, id, document_file_id, title, description=None, 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcachedvideo + + :param type: Type of the result, must be video + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param video_file_id: A valid file identifier for the video file + :type video_file_id: :obj:`str` + + :param title: Title for the result + :type title: :obj:`str` + + :param description: Optional. Short description of the result + :type description: :obj:`str` + + :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the video + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedVideo` + """ def __init__(self, id, video_file_id, title, description=None, caption=None, caption_entities = None, parse_mode=None, reply_markup=None, @@ -2380,6 +4833,43 @@ def __init__(self, id, video_file_id, title, description=None, 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcachedvoice + + :param type: Type of the result, must be voice + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param voice_file_id: A valid file identifier for the voice message + :type voice_file_id: :obj:`str` + + :param title: Voice message title + :type title: :obj:`str` + + :param caption: Optional. Caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the voice message caption. See formatting options for + more details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the voice message + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedVoice` + """ 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) @@ -2396,6 +4886,40 @@ def __init__(self, id, voice_file_id, title, caption=None, caption_entities = No 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. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresultcachedaudio + + :param type: Type of the result, must be audio + :type type: :obj:`str` + + :param id: Unique identifier for this result, 1-64 bytes + :type id: :obj:`str` + + :param audio_file_id: A valid file identifier for the audio file + :type audio_file_id: :obj:`str` + + :param caption: Optional. Caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param reply_markup: Optional. Inline keyboard attached to the message + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :param input_message_content: Optional. Content of the message to be sent instead of the audio + :type input_message_content: :class:`telebot.types.InputMessageContent` + + :return: Instance of the class + :rtype: :class:`telebot.types.InlineQueryResultCachedAudio` + """ 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) @@ -2413,6 +4937,34 @@ def __init__(self, id, audio_file_id, caption=None, caption_entities = None, # Games class Game(JsonDeserializable): + """ + This object represents a game. Use BotFather to create and edit games, their short names will act as unique identifiers. + + Telegram Documentation: https://core.telegram.org/bots/api#game + + :param title: Title of the game + :type title: :obj:`str` + + :param description: Description of the game + :type description: :obj:`str` + + :param photo: Photo that will be displayed in the game message in chats. + :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` + + :param text: Optional. Brief description of the game or high scores included in the game message. Can be + automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited + using editMessageText. 0-4096 characters. + :type text: :obj:`str` + + :param text_entities: Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc. + :type text_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param animation: Optional. Animation that will be displayed in the game message in chats. Upload via BotFather + :type animation: :class:`telebot.types.Animation` + + :return: Instance of the class + :rtype: :class:`telebot.types.Game` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2426,6 +4978,9 @@ def de_json(cls, json_string): @classmethod def parse_photo(cls, photo_size_array): + """ + Parse the photo array into a list of PhotoSize objects + """ ret = [] for ps in photo_size_array: ret.append(PhotoSize.de_json(ps)) @@ -2433,6 +4988,9 @@ def parse_photo(cls, photo_size_array): @classmethod def parse_entities(cls, message_entity_array): + """ + Parse the message entity array into a list of MessageEntity objects + """ ret = [] for me in message_entity_array: ret.append(MessageEntity.de_json(me)) @@ -2448,6 +5006,44 @@ def __init__(self, title, description, photo, text=None, text_entities=None, ani class Animation(JsonDeserializable): + """ + This object represents an animation file (GIF or H.264/MPEG-4 AVC video without sound). + + Telegram Documentation: https://core.telegram.org/bots/api#animation + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param width: Video width as defined by sender + :type width: :obj:`int` + + :param height: Video height as defined by sender + :type height: :obj:`int` + + :param duration: Duration of the video in seconds as defined by sender + :type duration: :obj:`int` + + :param thumb: Optional. Animation thumbnail as defined by sender + :type thumb: :class:`telebot.types.PhotoSize` + + :param file_name: Optional. Original animation filename as defined by sender + :type file_name: :obj:`str` + + :param mime_type: Optional. MIME type of the file as defined by sender + :type mime_type: :obj:`str` + + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this value. + :type file_size: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Animation` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2472,6 +5068,23 @@ def __init__(self, file_id, file_unique_id, width=None, height=None, duration=No class GameHighScore(JsonDeserializable): + """ + This object represents one row of the high scores table for a game. + + Telegram Documentation: https://core.telegram.org/bots/api#gamehighscore + + :param position: Position in high score table for the game + :type position: :obj:`int` + + :param user: User + :type user: :class:`telebot.types.User` + + :param score: Score + :type score: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.GameHighScore` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2488,6 +5101,22 @@ def __init__(self, position, user, score, **kwargs): # Payments class LabeledPrice(JsonSerializable): + """ + This object represents a portion of the price for goods or services. + + Telegram Documentation: https://core.telegram.org/bots/api#labeledprice + + :param label: Portion label + :type label: :obj:`str` + + :param amount: Price of the product in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + the decimal point for each currency (2 for the majority of currencies). + :type amount: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.LabeledPrice` + """ def __init__(self, label, amount): self.label: str = label self.amount: int = amount @@ -2502,6 +5131,31 @@ def to_json(self): class Invoice(JsonDeserializable): + """ + This object contains basic information about an invoice. + + Telegram Documentation: https://core.telegram.org/bots/api#invoice + + :param title: Product name + :type title: :obj:`str` + + :param description: Product description + :type description: :obj:`str` + + :param start_parameter: Unique bot deep-linking parameter that can be used to generate this invoice + :type start_parameter: :obj:`str` + + :param currency: Three-letter ISO 4217 currency code + :type currency: :obj:`str` + + :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + the decimal point for each currency (2 for the majority of currencies). + :type total_amount: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Invoice` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2517,6 +5171,32 @@ def __init__(self, title, description, start_parameter, currency, total_amount, class ShippingAddress(JsonDeserializable): + """ + This object represents a shipping address. + + Telegram Documentation: https://core.telegram.org/bots/api#shippingaddress + + :param country_code: Two-letter ISO 3166-1 alpha-2 country code + :type country_code: :obj:`str` + + :param state: State, if applicable + :type state: :obj:`str` + + :param city: City + :type city: :obj:`str` + + :param street_line1: First line for the address + :type street_line1: :obj:`str` + + :param street_line2: Second line for the address + :type street_line2: :obj:`str` + + :param post_code: Address post code + :type post_code: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ShippingAddress` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2533,6 +5213,26 @@ def __init__(self, country_code, state, city, street_line1, street_line2, post_c class OrderInfo(JsonDeserializable): + """ + This object represents information about an order. + + Telegram Documentation: https://core.telegram.org/bots/api#orderinfo + + :param name: Optional. User name + :type name: :obj:`str` + + :param phone_number: Optional. User's phone number + :type phone_number: :obj:`str` + + :param email: Optional. User email + :type email: :obj:`str` + + :param shipping_address: Optional. User shipping address + :type shipping_address: :class:`telebot.types.ShippingAddress` + + :return: Instance of the class + :rtype: :class:`telebot.types.OrderInfo` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2548,6 +5248,23 @@ def __init__(self, name=None, phone_number=None, email=None, shipping_address=No class ShippingOption(JsonSerializable): + """ + This object represents one shipping option. + + Telegram Documentation: https://core.telegram.org/bots/api#shippingoption + + :param id: Shipping option identifier + :type id: :obj:`str` + + :param title: Option title + :type title: :obj:`str` + + :param prices: List of price portions + :type prices: :obj:`list` of :class:`telebot.types.LabeledPrice` + + :return: Instance of the class + :rtype: :class:`telebot.types.ShippingOption` + """ def __init__(self, id, title): self.id: str = id self.title: str = title @@ -2558,6 +5275,9 @@ def add_price(self, *args): Add LabeledPrice to ShippingOption :param args: LabeledPrices + :type args: :obj:`LabeledPrice` + + :return: None """ for price in args: self.prices.append(price) @@ -2572,6 +5292,37 @@ def to_json(self): class SuccessfulPayment(JsonDeserializable): + """ + This object contains basic information about a successful payment. + + Telegram Documentation: https://core.telegram.org/bots/api#successfulpayment + + :param currency: Three-letter ISO 4217 currency code + :type currency: :obj:`str` + + :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + the decimal point for each currency (2 for the majority of currencies). + :type total_amount: :obj:`int` + + :param invoice_payload: Bot specified invoice payload + :type invoice_payload: :obj:`str` + + :param shipping_option_id: Optional. Identifier of the shipping option chosen by the user + :type shipping_option_id: :obj:`str` + + :param order_info: Optional. Order information provided by the user + :type order_info: :class:`telebot.types.OrderInfo` + + :param telegram_payment_charge_id: Telegram payment identifier + :type telegram_payment_charge_id: :obj:`str` + + :param provider_payment_charge_id: Provider payment identifier + :type provider_payment_charge_id: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.SuccessfulPayment` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2591,6 +5342,26 @@ def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=N class ShippingQuery(JsonDeserializable): + """ + This object contains information about an incoming shipping query. + + Telegram Documentation: https://core.telegram.org/bots/api#shippingquery + + :param id: Unique query identifier + :type id: :obj:`str` + + :param from: User who sent the query + :type from: :class:`telebot.types.User` + + :param invoice_payload: Bot specified invoice payload + :type invoice_payload: :obj:`str` + + :param shipping_address: User specified shipping address + :type shipping_address: :class:`telebot.types.ShippingAddress` + + :return: Instance of the class + :rtype: :class:`telebot.types.ShippingQuery` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2607,6 +5378,37 @@ def __init__(self, id, from_user, invoice_payload, shipping_address, **kwargs): class PreCheckoutQuery(JsonDeserializable): + """ + This object contains information about an incoming pre-checkout query. + + Telegram Documentation: https://core.telegram.org/bots/api#precheckoutquery + + :param id: Unique query identifier + :type id: :obj:`str` + + :param from: User who sent the query + :type from: :class:`telebot.types.User` + + :param currency: Three-letter ISO 4217 currency code + :type currency: :obj:`str` + + :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + the decimal point for each currency (2 for the majority of currencies). + :type total_amount: :obj:`int` + + :param invoice_payload: Bot specified invoice payload + :type invoice_payload: :obj:`str` + + :param shipping_option_id: Optional. Identifier of the shipping option chosen by the user + :type shipping_option_id: :obj:`str` + + :param order_info: Optional. Order information provided by the user + :type order_info: :class:`telebot.types.OrderInfo` + + :return: Instance of the class + :rtype: :class:`telebot.types.PreCheckoutQuery` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2628,6 +5430,35 @@ def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipp # Stickers class StickerSet(JsonDeserializable): + """ + This object represents a sticker set. + + Telegram Documentation: https://core.telegram.org/bots/api#stickerset + + :param name: Sticker set name + :type name: :obj:`str` + + :param title: Sticker set title + :type title: :obj:`str` + + :param is_animated: True, if the sticker set contains animated stickers + :type is_animated: :obj:`bool` + + :param is_video: True, if the sticker set contains video stickers + :type is_video: :obj:`bool` + + :param contains_masks: True, if the sticker set contains masks + :type contains_masks: :obj:`bool` + + :param stickers: List of all set stickers + :type stickers: :obj:`list` of :class:`telebot.types.Sticker` + + :param thumb: Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format + :type thumb: :class:`telebot.types.PhotoSize` + + :return: Instance of the class + :rtype: :class:`telebot.types.StickerSet` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2653,6 +5484,52 @@ def __init__(self, name, title, is_animated, is_video, contains_masks, stickers, class Sticker(JsonDeserializable): + """ + This object represents a sticker. + + Telegram Documentation: https://core.telegram.org/bots/api#sticker + + :param file_id: Identifier for this file, which can be used to download or reuse the file + :type file_id: :obj:`str` + + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + bots. Can't be used to download or reuse the file. + :type file_unique_id: :obj:`str` + + :param width: Sticker width + :type width: :obj:`int` + + :param height: Sticker height + :type height: :obj:`int` + + :param is_animated: True, if the sticker is animated + :type is_animated: :obj:`bool` + + :param is_video: True, if the sticker is a video sticker + :type is_video: :obj:`bool` + + :param thumb: Optional. Sticker thumbnail in the .WEBP or .JPG format + :type thumb: :class:`telebot.types.PhotoSize` + + :param emoji: Optional. Emoji associated with the sticker + :type emoji: :obj:`str` + + :param set_name: Optional. Name of the sticker set to which the sticker belongs + :type set_name: :obj:`str` + + :param premium_animation: Optional. Premium animation for the sticker, if the sticker is premium + :type premium_animation: :class:`telebot.types.File` + + :param mask_position: Optional. For mask stickers, the position where the mask should be placed + :type mask_position: :class:`telebot.types.MaskPosition` + + :param file_size: Optional. File size in bytes + :type file_size: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Sticker` + """ + @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2686,6 +5563,30 @@ def __init__(self, file_id, file_unique_id, width, height, is_animated, class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): + """ + This object describes the position on faces where a mask should be placed by default. + + Telegram Documentation: https://core.telegram.org/bots/api#maskposition + + :param point: The part of the face relative to which the mask should be placed. One of “forehead”, “eyes”, “mouth”, or + “chin”. + :type point: :obj:`str` + + :param x_shift: Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, + choosing -1.0 will place mask just to the left of the default mask position. + :type x_shift: :obj:`float` number + + :param y_shift: Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For + example, 1.0 will place the mask just below the default mask position. + :type y_shift: :obj:`float` number + + :param scale: Mask scaling coefficient. For example, 2.0 means double size. + :type scale: :obj:`float` number + + :return: Instance of the class + :rtype: :class:`telebot.types.MaskPosition` + """ + @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2708,6 +5609,15 @@ def to_dict(self): # InputMedia class InputMedia(Dictionaryable, JsonSerializable): + """ + This object represents the content of a media message to be sent. It should be one of + + * :class:`InputMediaAnimation` + * :class:`InputMediaDocument` + * :class:`InputMediaAudio` + * :class:`InputMediaPhoto` + * :class:`InputMediaVideo` + """ def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None): self.type: str = type self.media: str = media @@ -2736,6 +5646,9 @@ def to_dict(self): return json_dict def convert_input_media(self): + """ + :meta private: + """ if util.is_string(self.media): return self.to_json(), None @@ -2743,6 +5656,33 @@ def convert_input_media(self): class InputMediaPhoto(InputMedia): + """ + Represents a photo to be sent. + + Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto + + :param type: Type of the result, must be photo + :type type: :obj:`str` + + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + multipart/form-data under name. More information on Sending Files » + :type media: :obj:`str` + + :param caption: Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputMediaPhoto` + """ def __init__(self, media, caption=None, parse_mode=None): if util.is_pil_image(media): media = util.pil_image_to_file(media) @@ -2754,6 +5694,52 @@ def to_dict(self): class InputMediaVideo(InputMedia): + """ + Represents a video to be sent. + + Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo + + :param type: Type of the result, must be video + :type type: :obj:`str` + + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + multipart/form-data under name. More information on Sending Files » + :type media: :obj:`str` + + :param thumb: Optional. 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 . More information on Sending Files » + :type thumb: InputFile or :obj:`str` + + :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param width: Optional. Video width + :type width: :obj:`int` + + :param height: Optional. Video height + :type height: :obj:`int` + + :param duration: Optional. Video duration in seconds + :type duration: :obj:`int` + + :param supports_streaming: Optional. Pass True, if the uploaded video is suitable for streaming + :type supports_streaming: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputMediaVideo` + """ def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, supports_streaming=None): super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) @@ -2779,6 +5765,49 @@ def to_dict(self): class InputMediaAnimation(InputMedia): + """ + Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. + + Telegram Documentation: https://core.telegram.org/bots/api#inputmediaanimation + + :param type: Type of the result, must be animation + :type type: :obj:`str` + + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + multipart/form-data under name. More information on Sending Files » + :type media: :obj:`str` + + :param thumb: Optional. 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 . More information on Sending Files » + :type thumb: InputFile or :obj:`str` + + :param caption: Optional. Caption of the animation to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the animation caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param width: Optional. Animation width + :type width: :obj:`int` + + :param height: Optional. Animation height + :type height: :obj:`int` + + :param duration: Optional. Animation duration in seconds + :type duration: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputMediaAnimation` + """ def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb @@ -2800,6 +5829,49 @@ def to_dict(self): class InputMediaAudio(InputMedia): + """ + Represents an audio file to be treated as music to be sent. + + Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio + + :param type: Type of the result, must be audio + :type type: :obj:`str` + + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + multipart/form-data under name. More information on Sending Files » + :type media: :obj:`str` + + :param thumb: Optional. 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 . More information on Sending Files » + :type thumb: InputFile or :obj:`str` + + :param caption: Optional. Caption of the audio to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param duration: Optional. Duration of the audio in seconds + :type duration: :obj:`int` + + :param performer: Optional. Performer of the audio + :type performer: :obj:`str` + + :param title: Optional. Title of the audio + :type title: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputMediaAudio` + """ def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb @@ -2821,6 +5893,44 @@ def to_dict(self): class InputMediaDocument(InputMedia): + """ + Represents a general file to be sent. + + Telegram Documentation: https://core.telegram.org/bots/api#inputmediadocument + + :param type: Type of the result, must be document + :type type: :obj:`str` + + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + multipart/form-data under name. More information on Sending Files » + :type media: :obj:`str` + + :param thumb: Optional. 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 . More information on Sending Files » + :type thumb: InputFile or :obj:`str` + + :param caption: Optional. Caption of the document to be sent, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more + details. + :type parse_mode: :obj:`str` + + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param disable_content_type_detection: Optional. Disables automatic server-side content type detection for + files uploaded using multipart/form-data. Always True, if the document is sent as part of an album. + :type disable_content_type_detection: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputMediaDocument` + """ def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None): super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) self.thumb = thumb @@ -2836,6 +5946,20 @@ def to_dict(self): class PollOption(JsonDeserializable): + """ + This object contains information about one answer option in a poll. + + Telegram Documentation: https://core.telegram.org/bots/api#polloption + + :param text: Option text, 1-100 characters + :type text: :obj:`str` + + :param voter_count: Number of users that voted for this option + :type voter_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.PollOption` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2852,6 +5976,56 @@ def __init__(self, text, voter_count = 0, **kwargs): class Poll(JsonDeserializable): + """ + This object contains information about a poll. + + Telegram Documentation: https://core.telegram.org/bots/api#poll + + :param id: Unique poll identifier + :type id: :obj:`str` + + :param question: Poll question, 1-300 characters + :type question: :obj:`str` + + :param options: List of poll options + :type options: :obj:`list` of :class:`telebot.types.PollOption` + + :param total_voter_count: Total number of users that voted in the poll + :type total_voter_count: :obj:`int` + + :param is_closed: True, if the poll is closed + :type is_closed: :obj:`bool` + + :param is_anonymous: True, if the poll is anonymous + :type is_anonymous: :obj:`bool` + + :param type: Poll type, currently can be “regular” or “quiz” + :type type: :obj:`str` + + :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. + :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 + :type explanation: :obj:`str` + + :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 + :type open_period: :obj:`int` + + :param close_date: Optional. Point in time (Unix timestamp) when the poll will be automatically closed + :type close_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.Poll` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2892,6 +6066,12 @@ def __init__( self.close_date: int = close_date def add(self, option): + """ + Add an option to the poll. + + :param option: Option to add + :type option: :class:`telebot.types.PollOption` or :obj:`str` + """ if type(option) is PollOption: self.options.append(option) else: @@ -2899,6 +6079,24 @@ def add(self, option): class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): + """ + This object represents an answer of a user in a non-anonymous poll. + + Telegram Documentation: https://core.telegram.org/bots/api#pollanswer + + :param poll_id: Unique poll identifier + :type poll_id: :obj:`str` + + :param user: The user, who changed the answer to the poll + :type user: :class:`telebot.types.User` + + :param option_ids: 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted + their vote. + :type option_ids: :obj:`list` of :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.PollAnswer` + """ @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2921,6 +6119,20 @@ def to_dict(self): class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): + """ + Represents a location to which a chat is connected. + + Telegram Documentation: https://core.telegram.org/bots/api#chatlocation + + :param location: The location to which the supergroup is connected. Can't be a live location. + :type location: :class:`telebot.types.Location` + + :param address: Location address; 1-64 characters, as defined by the chat owner + :type address: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatLocation` + """ @classmethod def de_json(cls, json_string): if json_string is None: return json_string @@ -2943,6 +6155,43 @@ def to_dict(self): class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): + """ + Represents an invite link for a chat. + + Telegram Documentation: https://core.telegram.org/bots/api#chatinvitelink + + :param invite_link: The invite link. If the link was created by another chat administrator, then the second part of + the link will be replaced with “…”. + :type invite_link: :obj:`str` + + :param creator: Creator of the link + :type creator: :class:`telebot.types.User` + + :param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators + :type creates_join_request: :obj:`bool` + + :param is_primary: True, if the link is primary + :type is_primary: :obj:`bool` + + :param is_revoked: True, if the link is revoked + :type is_revoked: :obj:`bool` + + :param name: Optional. Invite link name + :type name: :obj:`str` + + :param expire_date: Optional. Point in time (Unix timestamp) when the link will expire or has been expired + :type expire_date: :obj:`int` + + :param member_limit: Optional. The maximum number of users that can be members of the chat simultaneously after + joining the chat via this invite link; 1-99999 + :type member_limit: :obj:`int` + + :param pending_join_request_count: Optional. Number of pending join requests created using this link + :type pending_join_request_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatInviteLink` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -2985,6 +6234,23 @@ def to_dict(self): class ProximityAlertTriggered(JsonDeserializable): + """ + This object represents the content of a service message, sent whenever a user in the chat triggers a proximity alert set by another user. + + Telegram Documentation: https://core.telegram.org/bots/api#proximityalerttriggered + + :param traveler: User that triggered the alert + :type traveler: :class:`telebot.types.User` + + :param watcher: User that set the alert + :type watcher: :class:`telebot.types.User` + + :param distance: The distance between the users + :type distance: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.ProximityAlertTriggered` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -2998,24 +6264,39 @@ def __init__(self, traveler, watcher, distance, **kwargs): class VideoChatStarted(JsonDeserializable): + """ + This object represents a service message about a video chat started in the chat. Currently holds no information. + """ @classmethod def de_json(cls, json_string): return cls() def __init__(self): - """ - This object represents a service message about a voice chat started in the chat. - Currently holds no information. - """ pass class VoiceChatStarted(VideoChatStarted): + """ + Deprecated, use :class:`VideoChatStarted` instead. + """ + def __init__(self): logger.warning('VoiceChatStarted is deprecated. Use VideoChatStarted instead.') super().__init__() class VideoChatScheduled(JsonDeserializable): + """ + This object represents a service message about a video chat scheduled in the chat. + + Telegram Documentation: https://core.telegram.org/bots/api#videochatscheduled + + :param start_date: Point in time (Unix timestamp) when the video chat is supposed to be started by a chat + administrator + :type start_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.VideoChatScheduled` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -3026,12 +6307,26 @@ def __init__(self, start_date, **kwargs): self.start_date: int = start_date class VoiceChatScheduled(VideoChatScheduled): + """ + Deprecated, use :class:`VideoChatScheduled` instead. + """ def __init__(self, *args, **kwargs): logger.warning('VoiceChatScheduled is deprecated. Use VideoChatScheduled instead.') super().__init__(*args, **kwargs) class VideoChatEnded(JsonDeserializable): + """ + This object represents a service message about a video chat ended in the chat. + + Telegram Documentation: https://core.telegram.org/bots/api#videochatended + + :param duration: Video chat duration in seconds + :type duration: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.VideoChatEnded` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -3042,6 +6337,9 @@ def __init__(self, duration, **kwargs): self.duration: int = duration class VoiceChatEnded(VideoChatEnded): + """ + Deprecated, use :class:`VideoChatEnded` instead. + """ def __init__(self, *args, **kwargs): logger.warning('VoiceChatEnded is deprecated. Use VideoChatEnded instead.') super().__init__(*args, **kwargs) @@ -3049,6 +6347,17 @@ def __init__(self, *args, **kwargs): class VideoChatParticipantsInvited(JsonDeserializable): + """ + This object represents a service message about new members invited to a video chat. + + Telegram Documentation: https://core.telegram.org/bots/api#videochatparticipantsinvited + + :param users: New members that were invited to the video chat + :type users: :obj:`list` of :class:`telebot.types.User` + + :return: Instance of the class + :rtype: :class:`telebot.types.VideoChatParticipantsInvited` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -3061,12 +6370,26 @@ def __init__(self, users=None, **kwargs): self.users: List[User] = users class VoiceChatParticipantsInvited(VideoChatParticipantsInvited): + """ + Deprecated, use :class:`VideoChatParticipantsInvited` instead. + """ def __init__(self, *args, **kwargs): logger.warning('VoiceChatParticipantsInvited is deprecated. Use VideoChatParticipantsInvited instead.') super().__init__(*args, **kwargs) class MessageAutoDeleteTimerChanged(JsonDeserializable): + """ + This object represents a service message about a change in auto-delete timer settings. + + Telegram Documentation: https://core.telegram.org/bots/api#messageautodeletetimerchanged + + :param message_auto_delete_time: New auto-delete time for messages in the chat; in seconds + :type message_auto_delete_time: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.MessageAutoDeleteTimerChanged` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -3079,7 +6402,14 @@ def __init__(self, message_auto_delete_time, **kwargs): class MenuButton(JsonDeserializable, JsonSerializable): """ - Base class for MenuButtons. + This object describes the bot's menu button in a private chat. It should be one of + + * :class:`MenuButtonCommands` + * :class:`MenuButtonWebApp` + * :class:`MenuButtonDefault` + + If a menu button other than MenuButtonDefault is set for a private chat, then it is applied + in the chat. Otherwise the default menu button is applied. By default, the menu button opens the list of bot commands. """ @classmethod def de_json(cls, json_string): @@ -3097,6 +6427,17 @@ def to_json(self): class MenuButtonCommands(MenuButton): + """ + Represents a menu button, which opens the bot's list of commands. + + Telegram Documentation: https://core.telegram.org/bots/api#menubuttoncommands + + :param type: Type of the button, must be commands + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.MenuButtonCommands` + """ def __init__(self, type): self.type = type @@ -3108,6 +6449,24 @@ def to_json(self): return json.dumps(self.to_dict()) class MenuButtonWebApp(MenuButton): + """ + Represents a menu button, which launches a Web App. + + Telegram Documentation: https://core.telegram.org/bots/api#menubuttonwebapp + + :param type: Type of the button, must be web_app + :type type: :obj:`str` + + :param text: Text on the button + :type text: :obj:`str` + + :param web_app: Description of the Web App that will be launched when the user presses the button. The Web App will be + able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. + :type web_app: :class:`telebot.types.WebAppInfo` + + :return: Instance of the class + :rtype: :class:`telebot.types.MenuButtonWebApp` + """ def __init__(self, type, text, web_app): self.type: str = type @@ -3121,7 +6480,17 @@ def to_json(self): return json.dumps(self.to_dict()) class MenuButtonDefault(MenuButton): + """ + Describes that no specific value for the menu button was set. + Telegram Documentation: https://core.telegram.org/bots/api#menubuttondefault + + :param type: Type of the button, must be default + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.MenuButtonDefault` + """ def __init__(self, type): self.type: str = type @@ -3134,8 +6503,50 @@ def to_json(self): class ChatAdministratorRights(JsonDeserializable, JsonSerializable): """ - Class representation of: - https://core.telegram.org/bots/api#chatadministratorrights + Represents the rights of an administrator in a chat. + + Telegram Documentation: https://core.telegram.org/bots/api#chatadministratorrights + + :param is_anonymous: True, if the user's presence in the chat is hidden + :type is_anonymous: :obj:`bool` + + :param can_manage_chat: True, if the administrator can access the chat event log, chat statistics, message + statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. + Implied by any other administrator privilege + :type can_manage_chat: :obj:`bool` + + :param can_delete_messages: True, if the administrator can delete messages of other users + :type can_delete_messages: :obj:`bool` + + :param can_manage_video_chats: True, if the administrator can manage video chats + :type can_manage_video_chats: :obj:`bool` + + :param can_restrict_members: True, if the administrator can restrict, ban or unban chat members + :type can_restrict_members: :obj:`bool` + + :param can_promote_members: True, if the administrator can add new administrators with a subset of their own + privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that + were appointed by the user) + :type can_promote_members: :obj:`bool` + + :param can_change_info: True, if the user is allowed to change the chat title, photo and other settings + :type can_change_info: :obj:`bool` + + :param can_invite_users: True, if the user is allowed to invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_post_messages: Optional. True, if the administrator can post in the channel; channels only + :type can_post_messages: :obj:`bool` + + :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin + messages; channels only + :type can_edit_messages: :obj:`bool` + + :param can_pin_messages: Optional. True, if the user is allowed to pin messages; groups and supergroups only + :type can_pin_messages: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatAdministratorRights` """ @classmethod From 51b2ec701d22ba94bb3fab37fb6286cdf6c5cdc1 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 26 Jul 2022 16:27:15 +0500 Subject: [PATCH 1070/1808] Removed unecessary methods from documentation(to_json, de_json, to_dict) --- telebot/types.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 1755ddd85..6c5a1f085 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -643,7 +643,7 @@ def __init__(self, message_id, **kwargs): self.message_id = message_id -class WebAppData(JsonDeserializable): +class WebAppData(JsonDeserializable, Dictionaryable): """ Describes data sent from a Web App to the bot. @@ -1916,7 +1916,7 @@ def to_json(self): return json.dumps(json_dict) -class WebAppInfo(JsonDeserializable): +class WebAppInfo(JsonDeserializable, Dictionaryable): """ Describes a Web App. @@ -2059,11 +2059,6 @@ def row(self, *args): return self.add(*args, row_width=self.max_row_keys) def to_json(self): - """ - Converts this object to its json representation following the Telegram API guidelines described here: - https://core.telegram.org/bots/api#replykeyboardmarkup - :return: - """ json_dict = {'keyboard': self.keyboard} if self.one_time_keyboard is not None: json_dict['one_time_keyboard'] = self.one_time_keyboard @@ -2848,7 +2843,7 @@ def to_dict(self): return json_dict -class BotCommand(JsonSerializable, JsonDeserializable): +class BotCommand(JsonSerializable, JsonDeserializable, Dictionaryable): """ This object represents a bot command. @@ -3569,7 +3564,7 @@ def to_dict(self): return json_dict -class SentWebAppMessage(JsonDeserializable): +class SentWebAppMessage(JsonDeserializable, Dictionaryable): """ Describes an inline message sent by a Web App on behalf of a user. @@ -5100,7 +5095,7 @@ def __init__(self, position, user, score, **kwargs): # Payments -class LabeledPrice(JsonSerializable): +class LabeledPrice(JsonSerializable, Dictionaryable): """ This object represents a portion of the price for goods or services. @@ -6400,7 +6395,7 @@ def __init__(self, message_auto_delete_time, **kwargs): self.message_auto_delete_time = message_auto_delete_time -class MenuButton(JsonDeserializable, JsonSerializable): +class MenuButton(JsonDeserializable, JsonSerializable, Dictionaryable): """ This object describes the bot's menu button in a private chat. It should be one of @@ -6423,6 +6418,15 @@ def de_json(cls, json_string): return map[obj['type']](**obj) def to_json(self): + """ + :meta private: + """ + raise NotImplementedError + + def to_dict(self): + """ + :meta private: + """ raise NotImplementedError @@ -6501,7 +6505,7 @@ def to_json(self): return json.dumps(self.to_dict()) -class ChatAdministratorRights(JsonDeserializable, JsonSerializable): +class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryable): """ Represents the rights of an administrator in a chat. From fc01ec50fc72f56ab4aab38694e9eb418976f3c3 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 26 Jul 2022 16:30:51 +0500 Subject: [PATCH 1071/1808] Added .ext file to documentation --- docs/source/async_version/index.rst | 8 ++++++++ docs/source/sync_version/index.rst | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/docs/source/async_version/index.rst b/docs/source/async_version/index.rst index 88b382417..2d0130155 100644 --- a/docs/source/async_version/index.rst +++ b/docs/source/async_version/index.rst @@ -46,4 +46,12 @@ Asyncio handler backends :show-inheritance: +Extensions +------------------------ + + +.. automodule:: telebot.ext.aio.webhooks + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/sync_version/index.rst b/docs/source/sync_version/index.rst index 8b805dc52..9bcd3b26d 100644 --- a/docs/source/sync_version/index.rst +++ b/docs/source/sync_version/index.rst @@ -37,3 +37,13 @@ handler_backends file :undoc-members: :show-inheritance: + +Extensions +------------------------ + + + +.. automodule:: telebot.ext.aio.webhooks + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file From 6303ecc7c64d2955f9b78e0609c2e7d42c126a72 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 26 Jul 2022 20:45:32 +0500 Subject: [PATCH 1072/1808] Little fix --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 569c1d96f..1ae0de574 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -755,7 +755,7 @@ def setup_middleware(self, middleware: BaseMiddleware): Take a look at the :class:`telebot.asyncio_handler_backends.BaseMiddleware` section for more. :param middleware: Middleware-class. - :type middleware: :class:`telebot.middlewares.BaseMiddleware` + :type middleware: :class:`telebot.asyncio_handler_backends.BaseMiddleware` :return: None """ From 5beb51f9075b1f2927ea6347fdb107e1150266fa Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 27 Jul 2022 13:48:20 +0500 Subject: [PATCH 1073/1808] Removed state storages and fixed a typo --- docs/source/async_version/index.rst | 9 --------- docs/source/sync_version/index.rst | 10 +--------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/docs/source/async_version/index.rst b/docs/source/async_version/index.rst index 2d0130155..032e50233 100644 --- a/docs/source/async_version/index.rst +++ b/docs/source/async_version/index.rst @@ -26,15 +26,6 @@ Asyncio filters :undoc-members: :show-inheritance: -Asynchronous storage for states -------------------------------- - -.. automodule:: telebot.asyncio_storage - :members: - :undoc-members: - :show-inheritance: - - Asyncio handler backends ------------------------ diff --git a/docs/source/sync_version/index.rst b/docs/source/sync_version/index.rst index 9bcd3b26d..df8ce1a17 100644 --- a/docs/source/sync_version/index.rst +++ b/docs/source/sync_version/index.rst @@ -21,14 +21,6 @@ custom_filters file :undoc-members: :show-inheritance: -Synchronous storage for states -------------------------------- - -.. automodule:: telebot.storage - :members: - :undoc-members: - :show-inheritance: - handler_backends file -------------------------------- @@ -43,7 +35,7 @@ Extensions -.. automodule:: telebot.ext.aio.webhooks +.. automodule:: telebot.ext.sync.webhooks :members: :undoc-members: :show-inheritance: \ No newline at end of file From 8aa3d052cc258db9870fb402a676d1fb1f1c6974 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 27 Jul 2022 17:18:54 +0300 Subject: [PATCH 1074/1808] Bump version to 4.6.1 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 20ef458f2..2cd5719bb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.6.0' +release = '4.6.1' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index ff913fb13..a95e6234d 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.6.0' +__version__ = '4.6.1' From 65b353ffae64e738bd437f26f27c4eb6a6c49adc Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Aug 2022 12:40:43 +0500 Subject: [PATCH 1075/1808] Added InputFile --- telebot/apihelper.py | 9 +++++++++ telebot/types.py | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 07f2f57bc..bf9e23a22 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import time from datetime import datetime +import os try: import ujson as json @@ -87,6 +88,14 @@ def _make_request(token, method_name, method='get', params=None, files=None): logger.debug("Request: method={0} url={1} params={2} files={3}".format(method, request_url, params, files).replace(token, token.split(':')[0] + ":{TOKEN}")) read_timeout = READ_TIMEOUT connect_timeout = CONNECT_TIMEOUT + + if files: + # process types.InputFile + for key, value in files.items(): + if isinstance(value, types.InputFile): + files[key] = value.file + + if files and format_header_param: fields.format_header_param = _no_encode(format_header_param) if params: diff --git a/telebot/types.py b/telebot/types.py index 6c5a1f085..f331073bc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- +from io import IOBase import logging +import os +from pathlib import Path from typing import Dict, List, Optional, Union from abc import ABC @@ -6601,3 +6604,23 @@ def to_json(self): +class InputFile: + def __init__(self, file) -> None: + self._file, self.file_name = self._resolve_file(file) + + def _resolve_file(self, file): + if isinstance(file, str): + _file = open(file, 'rb') + return _file, os.path.basename(file.name) + elif isinstance(file, IOBase): + return file, os.path.basename(file.name) + elif isinstance(file, Path): + _file = open(file, 'rb') + return _file, os.path.basename(file.name) + else: + raise TypeError("File must be a string or a file-like object(pathlib.Path, io.IOBase).") + + + @property + def file(self): + return self._file From d03f3b2c52a1577f374e7a74d0407b937e234706 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Aug 2022 14:09:44 +0500 Subject: [PATCH 1076/1808] Updated asyncio_helper to support InputFile and fixed unecessary methods --- telebot/asyncio_helper.py | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 843ee0b45..18a0a194b 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -57,7 +57,7 @@ async def get_session(self): session_manager = SessionManager() async def _process_request(token, url, method='get', params=None, files=None, request_timeout=None): - params = prepare_data(params, files) + params = _prepare_data(params, files) if request_timeout is None: request_timeout = REQUEST_TIMEOUT timeout = aiohttp.ClientTimeout(total=request_timeout) @@ -85,20 +85,7 @@ async def _process_request(token, url, method='get', params=None, files=None, re - -def prepare_file(obj): - """ - returns os.path.basename for a given file - - :param obj: - :return: - """ - name = getattr(obj, 'name', None) - if name and isinstance(name, str) and name[0] != '<' and name[-1] != '>': - return os.path.basename(name) - - -def prepare_data(params=None, files=None): +def _prepare_data(params=None, files=None): """ prepare data for request. @@ -114,15 +101,10 @@ def prepare_data(params=None, files=None): if files: for key, f in files.items(): - if isinstance(f, tuple): - if len(f) == 2: - filename, fileobj = f - else: - raise ValueError('Tuple must have exactly 2 elements: filename, fileobj') - else: - filename, fileobj = prepare_file(f) or key, f + if isinstance(f, types.InputFile): + f = f.file - data.add_field(key, fileobj, filename=filename) + data.add_field(key, f, filename=key) return data From 047777fada22bd5bd611f9766594188951bc3d55 Mon Sep 17 00:00:00 2001 From: "v.korobov" Date: Mon, 8 Aug 2022 23:04:36 +0300 Subject: [PATCH 1077/1808] Fixed TOKEN visibility in fastapi swagger --- examples/webhook_examples/webhook_fastapi_echo_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/webhook_examples/webhook_fastapi_echo_bot.py b/examples/webhook_examples/webhook_fastapi_echo_bot.py index 433b9a1f6..7eb97ab74 100644 --- a/examples/webhook_examples/webhook_fastapi_echo_bot.py +++ b/examples/webhook_examples/webhook_fastapi_echo_bot.py @@ -33,7 +33,7 @@ bot = telebot.TeleBot(API_TOKEN) -app = fastapi.FastAPI() +app = fastapi.FastAPI(docs=None, redoc_url=None) # Process webhook calls From c0ed659f3011f1125659bdd7758db8bebc5b6892 Mon Sep 17 00:00:00 2001 From: "v.korobov" Date: Mon, 8 Aug 2022 23:07:04 +0300 Subject: [PATCH 1078/1808] Minor code style fixes --- .../webhook_fastapi_echo_bot.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/webhook_examples/webhook_fastapi_echo_bot.py b/examples/webhook_examples/webhook_fastapi_echo_bot.py index 7eb97ab74..015c35644 100644 --- a/examples/webhook_examples/webhook_fastapi_echo_bot.py +++ b/examples/webhook_examples/webhook_fastapi_echo_bot.py @@ -6,6 +6,7 @@ import logging import fastapi +import uvicorn import telebot API_TOKEN = 'TOKEN' @@ -36,9 +37,11 @@ app = fastapi.FastAPI(docs=None, redoc_url=None) -# Process webhook calls @app.post(f'/{API_TOKEN}/') def process_webhook(update: dict): + """ + Process webhook calls + """ if update: update = telebot.types.Update.de_json(update) bot.process_new_updates([update]) @@ -46,18 +49,21 @@ def process_webhook(update: dict): return - -# Handle '/start' and '/help' @bot.message_handler(commands=['help', 'start']) def send_welcome(message): + """ + Handle '/start' and '/help' + """ bot.reply_to(message, ("Hi there, I am EchoBot.\n" "I am here to echo your kind words back to you.")) -# Handle all other messages @bot.message_handler(func=lambda message: True, content_types=['text']) def echo_message(message): + """ + Handle all other messages + """ bot.reply_to(message, message.text) @@ -65,11 +71,12 @@ def echo_message(message): bot.remove_webhook() # Set webhook -bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, - certificate=open(WEBHOOK_SSL_CERT, 'r')) +bot.set_webhook( + url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, + certificate=open(WEBHOOK_SSL_CERT, 'r') +) -import uvicorn uvicorn.run( app, host=WEBHOOK_LISTEN, From 1667b51034dfecab611aebd1a5daa6efeddf4a29 Mon Sep 17 00:00:00 2001 From: Mahakam20000 Date: Wed, 10 Aug 2022 19:57:30 +0200 Subject: [PATCH 1079/1808] Example of async bot using webhook and aiohttp An async echo bot using aiohttp and webhoook --- .../async_webhook_aiohttp_echo_bot.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 examples/asynchronous_telebot/webhooks/async_webhook_aiohttp_echo_bot.py diff --git a/examples/asynchronous_telebot/webhooks/async_webhook_aiohttp_echo_bot.py b/examples/asynchronous_telebot/webhooks/async_webhook_aiohttp_echo_bot.py new file mode 100644 index 000000000..457a2abf3 --- /dev/null +++ b/examples/asynchronous_telebot/webhooks/async_webhook_aiohttp_echo_bot.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This is an async echo bot using decorators and webhook with aiohttp +# It echoes any incoming text messages and does not use the polling method. + +import logging +import ssl +import asyncio +from aiohttp import web +import telebot +from telebot.async_telebot import AsyncTeleBot + +API_TOKEN = '' +WEBHOOK_HOST = '' +WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open') +WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr +WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate +WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key +WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT) +WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN) + +# Quick'n'dirty SSL certificate generation: +# +# openssl genrsa -out webhook_pkey.pem 2048 +# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem +# +# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply +# with the same value in you put in WEBHOOK_HOST + +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) +bot = AsyncTeleBot(API_TOKEN) + + +# Process webhook calls +async def handle(request): + if request.match_info.get('token') == bot.token: + request_body_dict = await request.json() + update = telebot.types.Update.de_json(request_body_dict) + asyncio.ensure_future(bot.process_new_updates([update])) + return web.Response() + else: + return web.Response(status=403) + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +async def send_welcome(message): + await bot.reply_to(message, + ("Hi there, I am EchoBot.\n" + "I am here to echo your kind words back to you.")) + + +# Handle all other messages +@bot.message_handler(func=lambda message: True, content_types=['text']) +async def echo_message(message): + await bot.reply_to(message, message.text) + + +# Remove webhook and closing session before exiting +async def shutdown(app): + logger.info('Shutting down: removing webhook') + await bot.remove_webhook() + logger.info('Shutting down: closing session') + await bot.close_session() + + +async def setup(): + # Remove webhook, it fails sometimes the set if there is a previous webhook + logger.info('Starting up: removing old webhook') + await bot.remove_webhook() + # Set webhook + logger.info('Starting up: setting webhook') + await bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH, + certificate=open(WEBHOOK_SSL_CERT, 'r')) + app = web.Application() + app.router.add_post('/{token}/', handle) + app.on_cleanup.append(shutdown) + return app + + +if __name__ == '__main__': + # Build ssl context + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + context.load_cert_chain(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV) + # Start aiohttp server + web.run_app( + setup(), + host=WEBHOOK_LISTEN, + port=WEBHOOK_PORT, + ssl_context=context, + ) From 93e18131391e4ac8be840b76e0a26d2505a4ec39 Mon Sep 17 00:00:00 2001 From: Aaron Blakely Date: Thu, 11 Aug 2022 18:17:06 -0500 Subject: [PATCH 1080/1808] Update README.md Added TeleServ --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5a0ad888b..ed2e65bc5 100644 --- a/README.md +++ b/README.md @@ -901,5 +901,6 @@ Here are some examples of template: * [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...). * [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) GuXiaoJiang is a multi-functional robot, such as OSU game information query, IP test, animation screenshot search and other functions. * [Feedback-bot](https://github.com/coder2020official/feedbackbot) A feedback bot for user-admin communication. Made on AsyncTeleBot, using [template](https://github.com/coder2020official/asynctelebot_template). +* [TeleServ](https://github.com/ablakely/TeleServ) by [ablakely](https://github.com/ablakely) This is a Telegram to IRC bridge which links as an IRC server and makes Telegram users appear as native IRC users. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 41b151978670b96814d47bb64cbde6d6b32f30b5 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Rodriguez Date: Fri, 12 Aug 2022 01:53:35 +0200 Subject: [PATCH 1081/1808] Add 'web_app_data' content type to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a0ad888b..b25cacdc8 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)). The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings: -`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`. +`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`, `web_app_data`. You can use some types in one function. Example: From c5a69944be575acf5ccd16a85682488002723170 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Aug 2022 14:33:24 +0500 Subject: [PATCH 1082/1808] Documentation improvements and file name fix --- telebot/types.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f331073bc..0315a3673 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6605,22 +6605,61 @@ def to_json(self): class InputFile: + """ + A class to send files through Telegram Bot API. + + You need to pass a file, which should be an instance of :class:`io.IOBase` or + :class:`pathlib.Path`, or :obj:`str`. + + If you pass an :obj:`str` as a file, it will be opened and closed by the class. + + :param file: A file to send. + :type file: :class:`io.IOBase` or :class:`pathlib.Path` or :obj:`str` + + .. code-block:: python3 + :caption: Example on sending a file using this class + + from telebot.types import InputFile + + # Sending a file from disk + bot.send_document( + chat_id, + InputFile('/path/to/file/file.txt') + ) + + + # Sending a file from an io.IOBase object + with open('/path/to/file/file.txt', 'rb') as f: + bot.send_document( + chat_id, + InputFile(f) + ) + + # Sending a file using pathlib.Path: + bot.send_document( + chat_id, + InputFile(pathlib.Path('/path/to/file/file.txt')) + ) + """ def __init__(self, file) -> None: self._file, self.file_name = self._resolve_file(file) def _resolve_file(self, file): if isinstance(file, str): _file = open(file, 'rb') - return _file, os.path.basename(file.name) + return _file, os.path.basename(_file.name) elif isinstance(file, IOBase): return file, os.path.basename(file.name) elif isinstance(file, Path): _file = open(file, 'rb') - return _file, os.path.basename(file.name) + return _file, os.path.basename(_file.name) else: raise TypeError("File must be a string or a file-like object(pathlib.Path, io.IOBase).") @property def file(self): + """ + File object. + """ return self._file From e860f114c60e954490119e88261ac98412c93936 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Aug 2022 14:37:00 +0500 Subject: [PATCH 1083/1808] Forgot to remove unnecessary import --- telebot/apihelper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index bf9e23a22..7ef5ffc21 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import time from datetime import datetime -import os try: import ujson as json From 26db76f207bb39ca70cc803a32702348f66156ef Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Aug 2022 15:25:51 +0500 Subject: [PATCH 1084/1808] Fix backward comptability --- telebot/apihelper.py | 4 +++- telebot/asyncio_helper.py | 25 +++++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 7ef5ffc21..a3572c699 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import copy import time from datetime import datetime @@ -89,8 +90,9 @@ def _make_request(token, method_name, method='get', params=None, files=None): connect_timeout = CONNECT_TIMEOUT if files: + files_copy = copy.deepcopy(files) # process types.InputFile - for key, value in files.items(): + for key, value in files_copy.items(): if isinstance(value, types.InputFile): files[key] = value.file diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 18a0a194b..81416575b 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -83,11 +83,17 @@ async def _process_request(token, url, method='get', params=None, files=None, re if not got_result: raise RequestTimeout("Request timeout. Request: method={0} url={1} params={2} files={3} request_timeout={4}".format(method, url, params, files, request_timeout, current_try)) - +def _prepare_file(obj): + """ + Prepares file for upload. + """ + name = getattr(obj, 'name', None) + if name and isinstance(name, str) and name[0] != '<' and name[-1] != '>': + return os.path.basename(name) def _prepare_data(params=None, files=None): """ - prepare data for request. + Adds the parameters and files to the request. :param params: :param files: @@ -98,13 +104,20 @@ def _prepare_data(params=None, files=None): if params: for key, value in params.items(): data.add_field(key, str(value)) - if files: for key, f in files.items(): - if isinstance(f, types.InputFile): - f = f.file + if isinstance(f, tuple): + if len(f) == 2: + file_name, file = f + else: + raise ValueError('Tuple must have exactly 2 elements: filename, fileobj') + elif isinstance(f, types.InputFile): + file_name = f.file_name + file = f.file + else: + file_name, file = _prepare_file(f) or key, f - data.add_field(key, f, filename=key) + data.add_field(key, file, filename=file_name) return data From 5471b88da6ad698a376d0356a2ec70cd6db44538 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Aug 2022 19:25:46 +0500 Subject: [PATCH 1085/1808] Update apihelper.py --- telebot/apihelper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a3572c699..b4e89f7bf 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import copy import time from datetime import datetime @@ -90,7 +89,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): connect_timeout = CONNECT_TIMEOUT if files: - files_copy = copy.deepcopy(files) + files_copy = dict(files) # process types.InputFile for key, value in files_copy.items(): if isinstance(value, types.InputFile): From ffa1c372049da30144b520612755fcc3037c06af Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Aug 2022 20:12:44 +0500 Subject: [PATCH 1086/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2aa073df0..9ef44c8bd 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.1! +##

Supported Bot API version: 6.2!

Official documentation

From 40698408c9d97f12dd63b01553245082c0fcd207 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Aug 2022 22:10:08 +0500 Subject: [PATCH 1087/1808] Bot API 6.2 --- telebot/__init__.py | 24 +++++++++++++++++++++-- telebot/apihelper.py | 9 +++++++-- telebot/async_telebot.py | 24 +++++++++++++++++++++-- telebot/asyncio_helper.py | 7 ++++++- telebot/types.py | 41 +++++++++++++++++++++++++++++++-------- 5 files changed, 90 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index da3bf26f8..4d5a352e2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4190,6 +4190,20 @@ def get_sticker_set(self, name: str) -> types.StickerSet: result = apihelper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) + def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.Sticker]: + """ + Use this method to get information about custom emoji stickers by their identifiers. + Returns an Array of Sticker objects. + + :param custom_emoji_ids: List of custom emoji identifiers. At most 200 custom emoji identifiers can be specified. + :type custom_emoji_ids: :obj:`list` of :obj:`str` + + :return: Returns an Array of Sticker objects. + :rtype: :obj:`list` of :class:`telebot.types.Sticker` + """ + result = apihelper.get_custom_emoji_stickers(self.token, custom_emoji_ids) + return [types.Sticker.de_json(sticker) for sticker in result] + def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet @@ -4217,6 +4231,7 @@ def create_new_sticker_set( tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, + sticker_type: Optional[str]=None, mask_position: Optional[types.MaskPosition]=None) -> bool: """ Use this method to create new sticker set owned by a user. @@ -4250,9 +4265,14 @@ def create_new_sticker_set( :param webm_sticker: WebM animation with the sticker, uploaded using multipart/form-data. :type webm_sticker: :obj:`str` - :param contains_masks: Pass True, if a set of mask stickers should be created + :param contains_masks: Pass True, if a set of mask stickers should be created. Deprecated since Bot API 6.2, + use sticker_type instead. :type contains_masks: :obj:`bool` + :param sticker_type: Optional, Type of stickers in the set, pass “regular” or “mask”. Custom emoji sticker sets can't be created + via the Bot API at the moment. By default, a regular sticker set is created. + :type sticker_type: :obj:`str` + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` @@ -4261,7 +4281,7 @@ def create_new_sticker_set( """ return apihelper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks, mask_position, webm_sticker) + contains_masks, mask_position, webm_sticker, sticker_type) def add_sticker_to_set( self, user_id: int, name: str, emojis: str, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b4e89f7bf..fadb6f979 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1561,7 +1561,10 @@ def get_sticker_set(token, name): method_url = 'getStickerSet' return _make_request(token, method_url, params={'name': name}) - +def get_custom_emoji_stickers(token, custom_emoji_ids): + method_url = r'getCustomEmojiStickers' + return _make_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) + def upload_sticker_file(token, user_id, png_sticker): method_url = 'uploadStickerFile' payload = {'user_id': user_id} @@ -1571,7 +1574,7 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks=None, mask_position=None, webm_sticker=None): + contains_masks=None, mask_position=None, webm_sticker=None, sticker_type=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} if png_sticker: @@ -1592,6 +1595,8 @@ def create_new_sticker_set( payload['mask_position'] = mask_position.to_json() if webm_sticker: payload['webm_sticker'] = webm_sticker + if sticker_type: + payload['sticker_type'] = sticker_type return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1ae0de574..4305b0d37 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5028,6 +5028,20 @@ async def get_sticker_set(self, name: str) -> types.StickerSet: result = await asyncio_helper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) + async def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.Sticker]: + """ + Use this method to get information about custom emoji stickers by their identifiers. + Returns an Array of Sticker objects. + + :param custom_emoji_ids: List of custom emoji identifiers. At most 200 custom emoji identifiers can be specified. + :type custom_emoji_ids: :obj:`list` of :obj:`str` + + :return: Returns an Array of Sticker objects. + :rtype: :obj:`list` of :class:`telebot.types.Sticker` + """ + result = asyncio_helper.get_custom_emoji_stickers(self.token, custom_emoji_ids) + return [types.Sticker.de_json(sticker) for sticker in result] + async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet @@ -5055,6 +5069,7 @@ async def create_new_sticker_set( tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, + sticker_type: Optional[str]=None, mask_position: Optional[types.MaskPosition]=None) -> bool: """ Use this method to create new sticker set owned by a user. @@ -5088,9 +5103,14 @@ async def create_new_sticker_set( :param webm_sticker: WebM animation with the sticker, uploaded using multipart/form-data. :type webm_sticker: :obj:`str` - :param contains_masks: Pass True, if a set of mask stickers should be created + :param contains_masks: Pass True, if a set of mask stickers should be created. Deprecated since Bot API 6.2, + use sticker_type instead. :type contains_masks: :obj:`bool` + :param sticker_type: Optional, Type of stickers in the set, pass “regular” or “mask”. Custom emoji sticker sets can't be created + via the Bot API at the moment. By default, a regular sticker set is created. + :type sticker_type: :obj:`str` + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` @@ -5099,7 +5119,7 @@ async def create_new_sticker_set( """ return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks, mask_position, webm_sticker) + contains_masks, mask_position, webm_sticker, sticker_type) async def add_sticker_to_set( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 81416575b..992e047b6 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1532,6 +1532,9 @@ async def get_sticker_set(token, name): method_url = 'getStickerSet' return await _process_request(token, method_url, params={'name': name}) +async def get_custom_emoji_stickers(token, custom_emoji_ids): + method_url = r'getCustomEmojiStickers' + return _process_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) async def upload_sticker_file(token, user_id, png_sticker): method_url = 'uploadStickerFile' @@ -1542,7 +1545,7 @@ async def upload_sticker_file(token, user_id, png_sticker): async def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks=None, mask_position=None, webm_sticker=None): + contains_masks=None, mask_position=None, webm_sticker=None, sticker_type=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} if png_sticker: @@ -1563,6 +1566,8 @@ async def create_new_sticker_set( payload['mask_position'] = mask_position.to_json() if webm_sticker: payload['webm_sticker'] = webm_sticker + if sticker_type: + payload['sticker_type'] = sticker_type return await _process_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/types.py b/telebot/types.py index 0315a3673..69be7e6fc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -525,6 +525,10 @@ class Chat(JsonDeserializable): 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. + :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. :type join_to_send_messages: :obj:`bool` @@ -599,7 +603,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, permissions=None, slow_mode_delay=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, **kwargs): + join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -611,6 +615,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.join_to_send_messages: bool = join_to_send_messages self.join_by_request: bool = join_by_request self.has_private_forwards: bool = has_private_forwards + self.has_restricted_voice_and_video_messages: bool = has_restricted_voice_and_video_messages self.description: str = description self.invite_link: str = invite_link self.pinned_message: Message = pinned_message @@ -1251,7 +1256,7 @@ class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users - without usernames) + without usernames), “custom_emoji” (for inline custom emoji stickers) :type type: :obj:`str` :param offset: Offset in UTF-16 code units to the start of the entity @@ -1269,6 +1274,10 @@ class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): :param language: Optional. For “pre” only, the programming language of the entity text :type language: :obj:`str` + :param custom_emoji_id: Optional. For “custom_emoji” only, unique identifier of the custom emoji. + Use getCustomEmojiStickers to get full information about the sticker. + :type custom_emoji_id: :obj:`str` + :return: Instance of the class :rtype: :class:`telebot.types.MessageEntity` """ @@ -1290,13 +1299,14 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) return cls(**obj) - def __init__(self, type, offset, length, url=None, user=None, language=None, **kwargs): + def __init__(self, type, offset, length, url=None, user=None, language=None, custom_emoji_id=None, **kwargs): self.type: str = type self.offset: int = offset self.length: int = length self.url: str = url self.user: User = user self.language: str = language + self.custom_emoji_id: str = custom_emoji_id def to_json(self): return json.dumps(self.to_dict()) @@ -1307,7 +1317,8 @@ def to_dict(self): "length": self.length, "url": self.url, "user": self.user, - "language": self.language} + "language": self.language, + "custom_emoji_id": self.custom_emoji_id} class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): @@ -5439,13 +5450,17 @@ class StickerSet(JsonDeserializable): :param title: Sticker set title :type title: :obj:`str` + :param sticker_type: Type of stickers in the set, currently one of “regular”, “mask”, “custom_emoji” + :type sticker_type: :obj:`str` + :param is_animated: True, if the sticker set contains animated stickers :type is_animated: :obj:`bool` :param is_video: True, if the sticker set contains video stickers :type is_video: :obj:`bool` - :param contains_masks: True, if the sticker set contains masks + :param contains_masks: True, if the sticker set contains masks. Deprecated since Bot API 6.2, + use sticker_type instead. :type contains_masks: :obj:`bool` :param stickers: List of all set stickers @@ -5471,9 +5486,10 @@ def de_json(cls, json_string): obj['thumb'] = None return cls(**obj) - def __init__(self, name, title, is_animated, is_video, contains_masks, stickers, thumb=None, **kwargs): + def __init__(self, name, title, sticker_type, is_animated, is_video, contains_masks, stickers, thumb=None, **kwargs): self.name: str = name self.title: str = title + self.sticker_type: str = sticker_type self.is_animated: bool = is_animated self.is_video: bool = is_video self.contains_masks: bool = contains_masks @@ -5494,6 +5510,10 @@ class Sticker(JsonDeserializable): bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` + :param type: Type of the sticker, currently one of “regular”, “mask”, “custom_emoji”. The type of the sticker is + independent from its format, which is determined by the fields is_animated and is_video. + :type type: :obj:`str` + :param width: Sticker width :type width: :obj:`int` @@ -5521,6 +5541,9 @@ class Sticker(JsonDeserializable): :param mask_position: Optional. For mask stickers, the position where the mask should be placed :type mask_position: :class:`telebot.types.MaskPosition` + :param custom_emoji_id: Optional. For custom emoji stickers, unique identifier of the custom emoji + :type custom_emoji_id: :obj:`str` + :param file_size: Optional. File size in bytes :type file_size: :obj:`int` @@ -5542,11 +5565,12 @@ def de_json(cls, json_string): obj['premium_animation'] = File.de_json(obj['premium_animation']) return cls(**obj) - def __init__(self, file_id, file_unique_id, width, height, is_animated, + def __init__(self, file_id, file_unique_id, type, width, height, is_animated, is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, - premium_animation=None, **kwargs): + premium_animation=None, custom_emoji_id=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id + self.type: str = type self.width: int = width self.height: int = height self.is_animated: bool = is_animated @@ -5557,6 +5581,7 @@ def __init__(self, file_id, file_unique_id, width, height, is_animated, self.mask_position: MaskPosition = mask_position self.file_size: int = file_size self.premium_animation: File = premium_animation + self.custom_emoji_id: int = custom_emoji_id From 737c3a439d0dbc634d5bc61ba0881790a4e13142 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Aug 2022 22:13:54 +0500 Subject: [PATCH 1088/1808] Fix tests(1st attempt) --- tests/test_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index 29a50752c..4587a160e 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -83,7 +83,7 @@ def test_json_Message_Audio(): def test_json_Message_Sticker(): - json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 60, "height": 60}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"type": "regular", "width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 60, "height": 60}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb.height == 60 @@ -91,7 +91,7 @@ def test_json_Message_Sticker(): def test_json_Message_Sticker_without_thumb(): - json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"type": "regular", "width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 assert msg.sticker.thumb is None From dd4073fd74fd5f0a5cca01aed6c7a62e57e14d90 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 13 Aug 2022 13:22:25 +0500 Subject: [PATCH 1089/1808] Fixes regarding contains_masks --- telebot/__init__.py | 7 ++++++- telebot/apihelper.py | 4 +--- telebot/async_telebot.py | 7 ++++++- telebot/asyncio_helper.py | 4 +--- telebot/types.py | 13 ++++++++++--- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4d5a352e2..13a68d644 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4279,9 +4279,14 @@ def create_new_sticker_set( :return: On success, True is returned. :rtype: :obj:`bool` """ + if contains_masks is not None: + logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') + if sticker_type is None: + sticker_type = 'mask' + return apihelper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks, mask_position, webm_sticker, sticker_type) + mask_position, webm_sticker, sticker_type) def add_sticker_to_set( self, user_id: int, name: str, emojis: str, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index fadb6f979..b4f2b8ce2 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1574,7 +1574,7 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks=None, mask_position=None, webm_sticker=None, sticker_type=None): + mask_position=None, webm_sticker=None, sticker_type=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} if png_sticker: @@ -1589,8 +1589,6 @@ def create_new_sticker_set( files = {stype: sticker} else: payload[stype] = sticker - if contains_masks is not None: - payload['contains_masks'] = contains_masks if mask_position: payload['mask_position'] = mask_position.to_json() if webm_sticker: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4305b0d37..99a96e8bb 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5117,9 +5117,14 @@ async def create_new_sticker_set( :return: On success, True is returned. :rtype: :obj:`bool` """ + if contains_masks is not None: + logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') + if sticker_type is None: + sticker_type = 'mask' + return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks, mask_position, webm_sticker, sticker_type) + mask_position, webm_sticker, sticker_type) async def add_sticker_to_set( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 992e047b6..af537ca43 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1545,7 +1545,7 @@ async def upload_sticker_file(token, user_id, png_sticker): async def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - contains_masks=None, mask_position=None, webm_sticker=None, sticker_type=None): + mask_position=None, webm_sticker=None, sticker_type=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} if png_sticker: @@ -1560,8 +1560,6 @@ async def create_new_sticker_set( files = {stype: sticker} else: payload[stype] = sticker - if contains_masks is not None: - payload['contains_masks'] = contains_masks if mask_position: payload['mask_position'] = mask_position.to_json() if webm_sticker: diff --git a/telebot/types.py b/telebot/types.py index 69be7e6fc..27c8ed873 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1275,7 +1275,7 @@ class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): :type language: :obj:`str` :param custom_emoji_id: Optional. For “custom_emoji” only, unique identifier of the custom emoji. - Use getCustomEmojiStickers to get full information about the sticker. + Use get_custom_emoji_stickers to get full information about the sticker. :type custom_emoji_id: :obj:`str` :return: Instance of the class @@ -5486,16 +5486,23 @@ def de_json(cls, json_string): obj['thumb'] = None return cls(**obj) - def __init__(self, name, title, sticker_type, is_animated, is_video, contains_masks, stickers, thumb=None, **kwargs): + def __init__(self, name, title, sticker_type, is_animated, is_video, stickers, thumb=None, **kwargs): self.name: str = name self.title: str = title self.sticker_type: str = sticker_type self.is_animated: bool = is_animated self.is_video: bool = is_video - self.contains_masks: bool = contains_masks self.stickers: List[Sticker] = stickers self.thumb: PhotoSize = thumb + @property + def contains_masks(self): + """ + Deprecated since Bot API 6.2, use sticker_type instead. + """ + logger.warning("contains_masks is deprecated, use sticker_type instead") + return self.sticker_type == 'mask' + class Sticker(JsonDeserializable): """ From 2647a02fc68cbd20a94482b59292a2e10a4505ea Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 13 Aug 2022 14:36:48 +0500 Subject: [PATCH 1090/1808] Contains_mask --- telebot/__init__.py | 4 +++- telebot/async_telebot.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 13a68d644..7719fa010 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4281,8 +4281,10 @@ def create_new_sticker_set( """ if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') - if sticker_type is None: + if contains_masks: sticker_type = 'mask' + else: + sticker_type = 'regular' return apihelper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 99a96e8bb..72561fa40 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5119,8 +5119,10 @@ async def create_new_sticker_set( """ if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') - if sticker_type is None: + if contains_masks: sticker_type = 'mask' + else: + sticker_type = 'regular' return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, From ffb34da6102d06d94693801266ae36058cf300a5 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 13 Aug 2022 14:40:20 +0500 Subject: [PATCH 1091/1808] Fix --- telebot/__init__.py | 6 ++---- telebot/async_telebot.py | 8 +++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7719fa010..ee1cc556e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4281,10 +4281,8 @@ def create_new_sticker_set( """ if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') - if contains_masks: - sticker_type = 'mask' - else: - sticker_type = 'regular' + if sticker_type is None: + sticker_type = 'mask' if contains_masks else 'regular' return apihelper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 72561fa40..f8be96806 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5119,11 +5119,9 @@ async def create_new_sticker_set( """ if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') - if contains_masks: - sticker_type = 'mask' - else: - sticker_type = 'regular' - + if sticker_type is None: + sticker_type = 'mask' if contains_masks else 'regular' + return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker, sticker_type) From 9f9821bbe859a0b90da0555b94d9bf456a35e4b1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 13 Aug 2022 12:52:38 +0300 Subject: [PATCH 1092/1808] Bump version to 4.7.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 2cd5719bb..ae52f756d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.6.1' +release = '4.7.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index a95e6234d..a79bcc2c5 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.6.1' +__version__ = '4.7.0' From 20bdb54e94ed9cec38703f617a2d1465d7fce904 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 13 Aug 2022 14:57:39 +0500 Subject: [PATCH 1093/1808] Update async_telebot.py --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f8be96806..b7168c33e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5039,7 +5039,7 @@ async def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[t :return: Returns an Array of Sticker objects. :rtype: :obj:`list` of :class:`telebot.types.Sticker` """ - result = asyncio_helper.get_custom_emoji_stickers(self.token, custom_emoji_ids) + result = await asyncio_helper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: From 345fa3433cec7a4edc7e94b89a4a29f8a34555d8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 13 Aug 2022 12:58:20 +0300 Subject: [PATCH 1094/1808] Readme update --- README.md | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 9ef44c8bd..6a53601ac 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ * [Logging](#logging) * [Proxy](#proxy) * [Testing](#testing) - * [API conformance](#api-conformance) + * [API conformance limitations](#api-conformance) * [AsyncTeleBot](#asynctelebot) * [F.A.Q.](#faq) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) @@ -726,27 +726,10 @@ Result will be: -## API conformance -* ✔ [Bot API 6.1](https://core.telegram.org/bots/api#june-20-2022) -* ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022) -* ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022) -* ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021) -* ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021) -* ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021) -* ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMember* classes are full copies of ChatMember -* ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021) -* ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021) -* ✔ [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) +## API conformance limitations * ➕ [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) -* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support -* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support +* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support +* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support ## AsyncTeleBot From d6ef67073e896e44b227fe70617aed2e86083d97 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 13 Aug 2022 12:59:13 +0300 Subject: [PATCH 1095/1808] Readme fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a53601ac..919d5a3be 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ * [Logging](#logging) * [Proxy](#proxy) * [Testing](#testing) - * [API conformance limitations](#api-conformance) + * [API conformance limitations](#api-conformance-limitations) * [AsyncTeleBot](#asynctelebot) * [F.A.Q.](#faq) * [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat) From d3cab9cdba60b9a2fd7eb8f4eb25289af780879a Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 13 Aug 2022 14:59:57 +0500 Subject: [PATCH 1096/1808] Update asyncio_helper.py --- 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 af537ca43..06575db65 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1534,7 +1534,7 @@ async def get_sticker_set(token, name): async def get_custom_emoji_stickers(token, custom_emoji_ids): method_url = r'getCustomEmojiStickers' - return _process_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) + return await _process_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) async def upload_sticker_file(token, user_id, png_sticker): method_url = 'uploadStickerFile' From 426f9f37873cb71810995ea01d10c7a8662df433 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 16 Aug 2022 17:12:50 +0300 Subject: [PATCH 1097/1808] Typo fix and minor code opt --- telebot/__init__.py | 42 +++++++++++++++++++--------------------- telebot/async_telebot.py | 20 +++++++++---------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ee1cc556e..4617788ec 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -744,35 +744,35 @@ def process_new_edited_channel_posts(self, edited_channel_post): """ self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') - def process_new_inline_query(self, new_inline_querys): + def process_new_inline_query(self, new_inline_queries): """ :meta private: """ - self._notify_command_handlers(self.inline_handlers, new_inline_querys, 'inline_query') + self._notify_command_handlers(self.inline_handlers, new_inline_queries, 'inline_query') - def process_new_chosen_inline_query(self, new_chosen_inline_querys): + def process_new_chosen_inline_query(self, new_chosen_inline_queries): """ :meta private: """ - self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') + self._notify_command_handlers(self.chosen_inline_handlers, new_chosen_inline_queries, 'chosen_inline_query') - def process_new_callback_query(self, new_callback_querys): + def process_new_callback_query(self, new_callback_queries): """ :meta private: """ - self._notify_command_handlers(self.callback_query_handlers, new_callback_querys, 'callback_query') + self._notify_command_handlers(self.callback_query_handlers, new_callback_queries, 'callback_query') - def process_new_shipping_query(self, new_shipping_querys): + def process_new_shipping_query(self, new_shipping_queries): """ :meta private: """ - self._notify_command_handlers(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') + self._notify_command_handlers(self.shipping_query_handlers, new_shipping_queries, 'shipping_query') - def process_new_pre_checkout_query(self, pre_checkout_querys): + def process_new_pre_checkout_query(self, pre_checkout_queries): """ :meta private: """ - self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') + self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_queries, 'pre_checkout_query') def process_new_poll(self, polls): """ @@ -1087,12 +1087,10 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ def _exec_task(self, task, *args, **kwargs): - if kwargs and kwargs.get('task_type') == 'handler': - pass_bot = kwargs.get('pass_bot') - kwargs.pop('pass_bot') - kwargs.pop('task_type') - if pass_bot: - kwargs['bot'] = self + if kwargs: + if kwargs.pop('task_type', "") == 'handler': + if kwargs.pop('pass_bot', False): + kwargs['bot'] = self if self.threaded: self.worker_pool.put(task, *args, **kwargs) @@ -5977,16 +5975,16 @@ def _notify_command_handlers(self, handlers, new_messages, update_type): :param new_messages: :return: """ - if len(handlers) == 0 and not self.use_class_middlewares: + if not(handlers) and not(self.use_class_middlewares): return for message in new_messages: - if self.use_class_middlewares: - middleware = self._check_middleware(update_type) - self._exec_task(self._run_middlewares_and_handler, message, handlers=handlers, middlewares=middleware, update_type=update_type) - return - else: + if not self.use_class_middlewares: for message_handler in handlers: if self._test_message_handler(message_handler, message): self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') break + else: + middleware = self._check_middleware(update_type) + self._exec_task(self._run_middlewares_and_handler, message, handlers=handlers, middlewares=middleware, update_type=update_type) + return diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index b7168c33e..4ede1b4ce 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -563,35 +563,35 @@ async def process_new_edited_channel_posts(self, edited_channel_post): """ await self._process_updates(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') - async def process_new_inline_query(self, new_inline_querys): + async def process_new_inline_query(self, new_inline_queries): """ :meta private: """ - await self._process_updates(self.inline_handlers, new_inline_querys, 'inline_query') + await self._process_updates(self.inline_handlers, new_inline_queries, 'inline_query') - async def process_new_chosen_inline_query(self, new_chosen_inline_querys): + async def process_new_chosen_inline_query(self, new_chosen_inline_queries): """ :meta private: """ - await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_querys, 'chosen_inline_query') + await self._process_updates(self.chosen_inline_handlers, new_chosen_inline_queries, 'chosen_inline_query') - async def process_new_callback_query(self, new_callback_querys): + async def process_new_callback_query(self, new_callback_queries): """ :meta private: """ - await self._process_updates(self.callback_query_handlers, new_callback_querys, 'callback_query') + await self._process_updates(self.callback_query_handlers, new_callback_queries, 'callback_query') - async def process_new_shipping_query(self, new_shipping_querys): + async def process_new_shipping_query(self, new_shipping_queries): """ :meta private: """ - await self._process_updates(self.shipping_query_handlers, new_shipping_querys, 'shipping_query') + await self._process_updates(self.shipping_query_handlers, new_shipping_queries, 'shipping_query') - async def process_new_pre_checkout_query(self, pre_checkout_querys): + async def process_new_pre_checkout_query(self, pre_checkout_queries): """ :meta private: """ - await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_querys, 'pre_checkout_query') + await self._process_updates(self.pre_checkout_query_handlers, pre_checkout_queries, 'pre_checkout_query') async def process_new_poll(self, polls): """ From 01be1fb583bbe1a8df5da3bc646cb59552c81c72 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 16 Aug 2022 21:39:20 +0500 Subject: [PATCH 1098/1808] Fixes #1650 --- telebot/types.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 27c8ed873..0f7f53f67 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1223,11 +1223,19 @@ def func(upd_text, subst_type=None, url=None, user=None): html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) offset += entity.length else: - # TODO: process nested entities from Bot API 4.5 - # Now ignoring them - pass + # Here we are processing nested entities. + # We shouldn't update offset, because they are the same as entity before. + # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, + # And we don't change it). + entity_string = utf16_text[entity.offset * 2 : (entity.offset + entity.length) * 2] + formatted_string = func(entity_string, entity.type, entity.url, entity.user) + entity_string_decoded = entity_string.decode("utf-16-le") + last_occurence = html_text.rfind(entity_string_decoded) + string_length = len(entity_string_decoded) + html_text = html_text.replace(html_text[last_occurence:last_occurence+string_length], formatted_string) if offset * 2 < len(utf16_text): html_text += func(utf16_text[offset * 2:]) + return html_text @property From 3d2576ca24be065757cfd69f0da9651024f961e8 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 21 Aug 2022 20:42:55 +0500 Subject: [PATCH 1099/1808] Fixed bug with searching a new handler after the execution of handler --- telebot/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4617788ec..1eb02fe67 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5950,6 +5950,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty return handler["function"](message, **data_copy) + break except Exception as e: handler_error = e if self.exception_handler: From b1a413660315eea57290f01e2584d50604cd3023 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Aug 2022 20:32:37 +0300 Subject: [PATCH 1100/1808] Code simplify and sync/async unificatiion --- telebot/__init__.py | 81 ++++++++++++++++++-------------------- telebot/async_telebot.py | 85 ++++++++++++++++++---------------------- 2 files changed, 76 insertions(+), 90 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1eb02fe67..133220849 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5678,7 +5678,7 @@ def add_my_chat_member_handler(self, handler_dict): """ self.my_chat_member_handlers.append(handler_dict) - def register_my_chat_member_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[Callable]=False, **kwargs): + def register_my_chat_member_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers my chat member handler. @@ -5898,7 +5898,8 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty data = {} params =[] handler_error = None - skip_handler = False + skip_handlers = False + if middlewares: for middleware in middlewares: if middleware.update_sensitive: @@ -5913,60 +5914,54 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty # Also, we will not run other middlewares if isinstance(result, CancelUpdate): return - elif isinstance(result, SkipHandler) and skip_handler is False: - skip_handler = True + elif isinstance(result, SkipHandler): + skip_handlers = True - try: - if handlers and not skip_handler: + if handlers and not(skip_handlers): + try: for handler in handlers: process_handler = self._test_message_handler(handler, message) if not process_handler: continue - else: - for i in inspect.signature(handler['function']).parameters: - params.append(i) - if len(params) == 1: - handler['function'](message) + for i in inspect.signature(handler['function']).parameters: + params.append(i) + if len(params) == 1: + handler['function'](message) + elif "data" in params: + if len(params) == 2: + handler['function'](message, data) + elif len(params) == 3: + handler['function'](message, data=data, bot=self) else: - if "data" in params: - if len(params) == 2: - handler['function'](message, data) - elif len(params) == 3: - handler['function'](message, data=data, bot=self) - else: - logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) - return - - else: - - data_copy = data.copy() - - for key in list(data_copy): - # remove data from data_copy if handler does not accept it - if key not in params: - del data_copy[key] - if handler.get('pass_bot'): data_copy["bot"] = self - if len(data_copy) > len(params) - 1: # remove the message parameter - logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) - return - - handler["function"](message, **data_copy) - break - except Exception as e: - handler_error = e - if self.exception_handler: - self.exception_handler.handle(e) - else: logging.error(str(e)) - + logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) + return + else: + data_copy = data.copy() + for key in list(data_copy): + # remove data from data_copy if handler does not accept it + if key not in params: + del data_copy[key] + if handler.get('pass_bot'): + data_copy["bot"] = self + if len(data_copy) > len(params) - 1: # remove the message parameter + logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) + return + handler["function"](message, **data_copy) + break + except Exception as e: + handler_error = e + if self.exception_handler: + self.exception_handler.handle(e) + else: logging.error(str(e)) if middlewares: for middleware in middlewares: if middleware.update_sensitive: if hasattr(middleware, f'post_process_{update_type}'): - result = getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) + getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) else: logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) else: - result = middleware.post_process(message, data, handler_error) + middleware.post_process(message, data, handler_error) def _notify_command_handlers(self, handlers, new_messages, update_type): """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4ede1b4ce..4397def3f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -359,8 +359,9 @@ async def _process_updates(self, handlers, messages, update_type): async def _run_middlewares_and_handlers(self, handlers, message, middlewares, update_type): handler_error = None data = {} - process_handler = True + skip_handlers = False params = [] + if middlewares: for middleware in middlewares: if middleware.update_sensitive: @@ -371,60 +372,50 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares, up middleware_result = None else: middleware_result = await middleware.pre_process(message, data) - if isinstance(middleware_result, SkipHandler): - await middleware.post_process(message, data, handler_error) - process_handler = False if isinstance(middleware_result, CancelUpdate): return - for handler in handlers: - if not process_handler: - break + elif isinstance(middleware_result, SkipHandler): + await middleware.post_process(message, data, handler_error) + skip_handlers = True - process_update = await self._test_message_handler(handler, message) - if not process_update: - continue - elif process_update: - try: + if handlers and not(skip_handlers): + try: + for handler in handlers: + process_update = await self._test_message_handler(handler, message) + if not process_update: continue for i in signature(handler['function']).parameters: params.append(i) if len(params) == 1: await handler['function'](message) break - else: - if "data" in params: - if len(params) == 2: - await handler['function'](message, data) - break - elif len(params) == 3: - await handler['function'](message, data=data, bot=self) - break - else: - logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) - return - - else: - - data_copy = data.copy() - - for key in list(data_copy): - # remove data from data_copy if handler does not accept it - if key not in params: - del data_copy[key] - if handler.get('pass_bot'): data_copy["bot"] = self - if len(data_copy) > len(params) - 1: # remove the message parameter - logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) - return - - await handler["function"](message, **data_copy) + elif "data" in params: + if len(params) == 2: + await handler['function'](message, data) break - except Exception as e: - handler_error = e - - if self.exception_handler: - self.exception_handler.handle(e) - else: logger.error(str(e)) - break - + elif len(params) == 3: + await handler['function'](message, data=data, bot=self) + break + else: + logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) + return + else: + data_copy = data.copy() + for key in list(data_copy): + # remove data from data_copy if handler does not accept it + if key not in params: + del data_copy[key] + if handler.get('pass_bot'): + data_copy["bot"] = self + if len(data_copy) > len(params) - 1: # remove the message parameter + logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) + return + await handler["function"](message, **data_copy) + break + except Exception as e: + if self.exception_handler: + self.exception_handler.handle(e) + else: + logger.error(str(e)) if middlewares: for middleware in middlewares: @@ -1619,7 +1610,7 @@ def add_my_chat_member_handler(self, handler_dict): """ self.my_chat_member_handlers.append(handler_dict) - def register_my_chat_member_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot: Optional[Callable]=False, **kwargs): + def register_my_chat_member_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers my chat member handler. From f4e66f68074843d265adfea7848ea75c661fc3aa Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Aug 2022 22:27:02 +0300 Subject: [PATCH 1101/1808] Added traceback for handlers exception --- telebot/__init__.py | 4 +++- telebot/async_telebot.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 133220849..026d32eca 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5951,7 +5951,9 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty handler_error = e if self.exception_handler: self.exception_handler.handle(e) - else: logging.error(str(e)) + else: + logging.error(str(e)) + logger.debug("Exception traceback:\n%s", traceback.format_exc()) if middlewares: for middleware in middlewares: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4397def3f..68e1768bc 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -416,6 +416,7 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares, up self.exception_handler.handle(e) else: logger.error(str(e)) + logger.debug("Exception traceback:\n%s", traceback.format_exc()) if middlewares: for middleware in middlewares: From b86c38367a1d36fe039c7b596c0daf66b61093ce Mon Sep 17 00:00:00 2001 From: Ananth Bhaskararaman Date: Sun, 28 Aug 2022 17:32:56 +0530 Subject: [PATCH 1102/1808] Import aioredis from redis module too aioredis is available in redis-py as of version 4.2.0rc1: https://github.com/aio-libs/aioredis-py#-aioredis-is-now-in-redis-py-420rc1- Try importing from the new package as well. --- telebot/asyncio_storage/redis_storage.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index e31ddb0a1..bc9ba54a7 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -6,7 +6,10 @@ try: import aioredis except: - redis_installed = False + try: + from redis import asyncio as aioredis + except: + redis_installed = False class StateRedisStorage(StateStorageBase): From 85bd174fdcc6e0982046602d5a2394ddacad7a71 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 30 Aug 2022 12:26:34 +0400 Subject: [PATCH 1103/1808] Update redis_storage.py --- telebot/asyncio_storage/redis_storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index bc9ba54a7..92842abb1 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -5,10 +5,10 @@ redis_installed = True try: import aioredis -except: +except ImportError: try: from redis import asyncio as aioredis - except: + except ImportError: redis_installed = False From d7770bf6708e002c67cf461c73074bd2cde00489 Mon Sep 17 00:00:00 2001 From: Ananth Bhaskararaman Date: Tue, 30 Aug 2022 16:11:58 +0530 Subject: [PATCH 1104/1808] Starlette ASGI example --- .../webhooks/webhook_starlette_echo_bot.py | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py diff --git a/examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py b/examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py new file mode 100644 index 000000000..dadaa90fc --- /dev/null +++ b/examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" +Asynchronous Telegram Echo Bot example. + +This is a simple bot that echoes each message that is received onto the chat. +It uses the Starlette ASGI framework to receive updates via webhook requests. +""" + +import uvicorn +from starlette.applications import Starlette +from starlette.requests import Request +from starlette.responses import PlainTextResponse, Response +from starlette.routing import Route +from telebot.async_telebot import AsyncTeleBot +from telebot.types import Message, Update + +API_TOKEN = "TOKEN" + +WEBHOOK_HOST = "" +WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open') +WEBHOOK_LISTEN = "0.0.0.0" +WEBHOOK_URL = f"https://{WEBHOOK_HOST}:{WEBHOOK_PORT}/telegram" +WEBHOOK_SECRET_TOKEN = "SECRET_TOKEN" + +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) + +bot = AsyncTeleBot(token=API_TOKEN) + +# BOT HANDLERS +@bot.message_handler(commands=["help", "start"]) +async def send_welcome(message: Message): + """ + Handle '/start' and '/help' + """ + await bot.reply_to( + message, + ("Hi there, I am EchoBot.\n" "I am here to echo your kind words back to you."), + ) + + +@bot.message_handler(func=lambda _: True, content_types=["text"]) +async def echo_message(message: Message): + """ + Handle all other messages + """ + await bot.reply_to(message, message.text) + + +# WEBSERVER HANDLERS +async def telegram(request: Request) -> Response: + """Handle incoming Telegram updates.""" + token_header_name = "X-Telegram-Bot-Api-Secret-Token" + if request.headers.get(token_header_name) != WEBHOOK_SECRET_TOKEN: + return PlainTextResponse("Forbidden", status_code=403) + await bot.process_new_updates([Update.de_json(await request.json())]) + return Response() + + +async def startup() -> None: + """Register webhook for telegram updates.""" + webhook_info = await bot.get_webhook_info(30) + if WEBHOOK_URL != webhook_info.url: + logger.debug( + f"updating webhook url, old: {webhook_info.url}, new: {WEBHOOK_URL}" + ) + if not await bot.set_webhook( + url=WEBHOOK_URL, secret_token=WEBHOOK_SECRET_TOKEN + ): + raise RuntimeError("unable to set webhook") + + +app = Starlette( + routes=[ + Route("/telegram", telegram, methods=["POST"]), + ], + on_startup=[startup], +) + + +uvicorn.run( + app, + host=WEBHOOK_HOST, + port=WEBHOOK_LISTEN, +) From e4179ea65f0a2ef92bcab17152e065d3985460c7 Mon Sep 17 00:00:00 2001 From: Ananth Bhaskararaman Date: Tue, 30 Aug 2022 17:27:43 +0530 Subject: [PATCH 1105/1808] Add SSL cert --- .../webhooks/webhook_starlette_echo_bot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py b/examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py index dadaa90fc..bcb440cf8 100644 --- a/examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py +++ b/examples/asynchronous_telebot/webhooks/webhook_starlette_echo_bot.py @@ -19,6 +19,8 @@ WEBHOOK_HOST = "" WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open') WEBHOOK_LISTEN = "0.0.0.0" +WEBHOOK_SSL_CERT = "./webhook_cert.pem" # Path to the ssl certificate +WEBHOOK_SSL_PRIV = "./webhook_pkey.pem" # Path to the ssl private key WEBHOOK_URL = f"https://{WEBHOOK_HOST}:{WEBHOOK_PORT}/telegram" WEBHOOK_SECRET_TOKEN = "SECRET_TOKEN" @@ -82,4 +84,6 @@ async def startup() -> None: app, host=WEBHOOK_HOST, port=WEBHOOK_LISTEN, + ssl_certfile=WEBHOOK_SSL_CERT, + ssl_keyfile=WEBHOOK_SSL_PRIV, ) From e0ffe0b4f5ce6dce8bddde9c22d2107770327580 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 30 Aug 2022 21:24:54 +0400 Subject: [PATCH 1106/1808] Added reloader to ext --- telebot/ext/reloader.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 telebot/ext/reloader.py diff --git a/telebot/ext/reloader.py b/telebot/ext/reloader.py new file mode 100644 index 000000000..6ec08a5bf --- /dev/null +++ b/telebot/ext/reloader.py @@ -0,0 +1,27 @@ + +from watchdog.events import FileSystemEventHandler +from watchdog.events import FileSystemEvent +import psutil +import os +import sys +import logging + +logger = logging.getLogger('TeleBot') + + + +class EventHandler(FileSystemEventHandler): + def on_any_event(self, event: FileSystemEvent): + logger.info('* Detected changes in: %s , reloading', (event.src_path)) + restart_file() + +def restart_file(): + try: + p = psutil.Process(os.getpid()) + for handler in p.open_files() + p.connections(): + os.close(handler.fd) + except Exception as e: + logger.error(e) + + python = sys.executable + os.execl(python, python, *sys.argv) From 0f7ab0d05f7023e6d54b5883d3541a30cd999ad7 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 30 Aug 2022 21:25:41 +0400 Subject: [PATCH 1107/1808] Added colorful logs, file restarts on changes to sync --- telebot/__init__.py | 47 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 026d32eca..de1b7127e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -131,6 +131,9 @@ class TeleBot: :param use_class_middlewares: Use class middlewares, defaults to False :type use_class_middlewares: :obj:`bool`, optional + + :param colorful_logs: Outputs colorful logs + :type colorful_logs: :obj:`bool`, optional """ def __init__( @@ -139,7 +142,8 @@ def __init__( next_step_backend: Optional[HandlerBackend]=None, reply_backend: Optional[HandlerBackend]=None, exception_handler: Optional[ExceptionHandler]=None, last_update_id: Optional[int]=0, suppress_middleware_excepions: Optional[bool]=False, state_storage: Optional[StateStorageBase]=StateMemoryStorage(), - use_class_middlewares: Optional[bool]=False + use_class_middlewares: Optional[bool]=False, + colorful_logs: Optional[bool]=False ): self.token = token self.parse_mode = parse_mode @@ -147,6 +151,16 @@ def __init__( self.skip_pending = skip_pending self.suppress_middleware_excepions = suppress_middleware_excepions + if colorful_logs: + try: + import coloredlogs + coloredlogs.install(logger=logger, level=logger.level) + except ImportError: + raise ImportError( + 'Install colorredlogs module to use colorful_logs option.' + ) + + self.__stop_polling = threading.Event() self.last_update_id = last_update_id self.exc_info = None @@ -838,7 +852,8 @@ def __notify_update(self, new_messages): def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, long_polling_timeout: Optional[int]=20, - logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): + logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, + restart_on_change: Optional[bool]=False, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -864,6 +879,9 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo so unwanted updates may be received for a short period of time. :type allowed_updates: :obj:`list` of :obj:`str` + :param restart_on_change: Restart a file on file(s) change. Defaults to False + :type restart_on_change: :obj:`bool` + :return: """ if skip_pending: @@ -872,7 +890,8 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo while not self.__stop_polling.is_set(): try: self.polling(non_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, - logger_level=logger_level, allowed_updates=allowed_updates, *args, **kwargs) + logger_level=logger_level, allowed_updates=allowed_updates, restart_on_change=restart_on_change, + *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) @@ -889,7 +908,7 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=False, interval: Optional[int]=0, timeout: Optional[int]=20, long_polling_timeout: Optional[int]=20, logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, - none_stop: Optional[bool]=None): + none_stop: Optional[bool]=None, restart_on_change: Optional[bool]=False): """ This function creates a new Thread that calls an internal __retrieve_updates function. This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. @@ -932,6 +951,9 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility. :type none_stop: :obj:`bool` + + :param restart_on_change: Restart a file on file(s) change. Defaults to False + :type restart_on_change: :obj:`bool` :return: """ @@ -941,6 +963,23 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F if skip_pending: self.__skip_updates() + + if restart_on_change: + try: + from watchdog.observers import Observer + from telebot.ext.reloader import EventHandler + except ImportError: + raise ImportError( + 'Please install watchdog and psutil before using restart_on_change option.' + ) + + event_handler = EventHandler() + path = sys.argv[1] if len(sys.argv) > 1 else '.' + observer = Observer() + observer.schedule(event_handler, path, recursive=True) + observer.start() + + logger.info('Starting your bot with username: [@%s]', self.user.username) if self.threaded: self.__threaded_polling(non_stop=non_stop, interval=interval, timeout=timeout, long_polling_timeout=long_polling_timeout, From 9216f15c16d56033e7dcbbdc414580ab1c5b43b0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 30 Aug 2022 21:26:41 +0400 Subject: [PATCH 1108/1808] Logs, file restarts, loggers to async Added colorful logs, file restarts on changes, improved logger, added cached version of user-bot to async --- telebot/async_telebot.py | 67 +++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 68e1768bc..9cf5e9fb7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3,6 +3,7 @@ import logging import re +import sys import time import traceback from typing import Any, Awaitable, Callable, List, Optional, Union @@ -18,12 +19,11 @@ from inspect import signature -from telebot import logger - from telebot import util, types, asyncio_helper import asyncio from telebot import asyncio_filters +logger = logging.getLogger('TeleBot') REPLY_MARKUP_TYPES = Union[ types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, @@ -99,10 +99,14 @@ class AsyncTeleBot: :param state_storage: Storage for states, defaults to StateMemoryStorage() :type state_storage: :class:`telebot.asyncio_storage.StateMemoryStorage`, optional + :param colorful_logs: Outputs colorful logs + :type colorful_logs: :obj:`bool`, optional + """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[int]=None, - exception_handler: Optional[ExceptionHandler]=None, state_storage: Optional[StateStorageBase]=StateMemoryStorage()) -> None: + exception_handler: Optional[ExceptionHandler]=None, state_storage: Optional[StateStorageBase]=StateMemoryStorage(), + colorful_logs: Optional[bool]=False) -> None: self.token = token self.offset = offset @@ -110,6 +114,15 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.parse_mode = parse_mode self.update_listener = [] + if colorful_logs: + try: + import coloredlogs + coloredlogs.install(logger=logger, level=logger.level) + except ImportError: + raise ImportError( + 'Install colorredlogs module to use colorful_logs option.' + ) + self.exception_handler = exception_handler @@ -135,6 +148,12 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.middlewares = [] + self._user = None # set during polling + + @property + def user(self): + return self._user + async def close_session(self): """ Closes existing session of aiohttp. @@ -176,7 +195,7 @@ async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=Non async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, request_timeout: Optional[int]=None, allowed_updates: Optional[List[str]]=None, - none_stop: Optional[bool]=None): + none_stop: Optional[bool]=None, restart_on_change: Optional[bool]=False): """ Runs bot in long-polling mode in a main loop. This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly. @@ -217,6 +236,9 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= :param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility. :type none_stop: :obj:`bool` + + :param restart_on_change: Restart a file on file(s) change. Defaults to False + :type restart_on_change: :obj:`bool` :return: """ @@ -226,10 +248,11 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= if skip_pending: await self.skip_updates() - await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) + await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates, restart_on_change) async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, request_timeout: Optional[int]=None, - logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, *args, **kwargs): + logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, + restart_on_change: Optional[bool]=False, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -256,6 +279,9 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option so unwanted updates may be received for a short period of time. :type allowed_updates: :obj:`list` of :obj:`str` + :param restart_on_change: Restart a file on file(s) change. Defaults to False + :type restart_on_change: :obj:`bool` + :return: None """ if skip_pending: @@ -264,7 +290,7 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option while self._polling: try: await self._process_polling(non_stop=False, timeout=timeout, request_timeout=request_timeout, - allowed_updates=allowed_updates, *args, **kwargs) + allowed_updates=allowed_updates, restart_on_change=restart_on_change, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) @@ -278,7 +304,7 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option logger.error("Break infinity polling") async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, - request_timeout: int=None, allowed_updates: Optional[List[str]]=None): + request_timeout: int=None, allowed_updates: Optional[List[str]]=None, restart_on_change: Optional[bool]=False): """ Function to process polling. @@ -294,9 +320,34 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: 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 restart_on_change: Restart a file on file(s) change. Defaults to False + :type restart_on_change: :obj:`bool` + :return: """ + + self._user = await self.get_me() + + + if restart_on_change: + try: + from watchdog.observers import Observer + from telebot.ext.reloader import EventHandler + except ImportError: + raise ImportError( + 'Please install watchdog and psutil before using restart_on_change option.' + ) + + event_handler = EventHandler() + path = sys.argv[1] if len(sys.argv) > 1 else '.' + observer = Observer() + observer.schedule(event_handler, path, recursive=True) + observer.start() + + logger.info('Starting your bot with username: [@%s]', self.user.username) + self._polling = True try: From 1b1d6c8239cb8d83508000baa098990cdcb724a7 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 30 Aug 2022 21:26:56 +0400 Subject: [PATCH 1109/1808] Improved asyncio helper's logger --- telebot/asyncio_helper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 06575db65..f3e58bd1e 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -13,8 +13,10 @@ from datetime import datetime -from telebot import util, logger +from telebot import util +import logging +logger = logging.getLogger('TeleBot') proxy = None session = None From 71d3ec8b4207a08b2fcf1d6e8bca5e7c67bab9a6 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 6 Sep 2022 18:11:40 +0400 Subject: [PATCH 1110/1808] Changed user id and chat id --- telebot/custom_filters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index dd6a27fe5..e924f134c 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -428,7 +428,7 @@ def check(self, message, text): text = text.name if message.chat.type in ['group', 'supergroup']: - group_state = self.bot.current_states.get_state(user_id, chat_id) + group_state = self.bot.current_states.get_state(chat_id, user_id) if group_state == text: return True elif type(text) is list and group_state in text: @@ -436,7 +436,7 @@ def check(self, message, text): else: - user_state = self.bot.current_states.get_state(user_id, chat_id) + user_state = self.bot.current_states.get_state(chat_id, user_id) if user_state == text: return True elif type(text) is list and user_state in text: From e3a4fdff9a9d19db30840e7bd4ef1c3d10a3b623 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 6 Sep 2022 18:12:11 +0400 Subject: [PATCH 1111/1808] Update asyncio_filters.py --- telebot/asyncio_filters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index f1384557c..72695e90b 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -420,7 +420,7 @@ async def check(self, message, text): text = text.name if message.chat.type in ['group', 'supergroup']: - group_state = await self.bot.current_states.get_state(user_id, chat_id) + group_state = await self.bot.current_states.get_state(chat_id, user_id) if group_state == text: return True elif type(text) is list and group_state in text: @@ -428,7 +428,7 @@ async def check(self, message, text): else: - user_state = await self.bot.current_states.get_state(user_id, chat_id) + user_state = await self.bot.current_states.get_state(chat_id, user_id) if user_state == text: return True elif type(text) is list and user_state in text: From b4c28de104d790c990f3945c4964e33748c431df Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 7 Sep 2022 20:44:39 +0400 Subject: [PATCH 1112/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 06575db65..00d4aa3c7 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -159,15 +159,13 @@ async def get_file_url(token, file_id): async def download_file(token, file_path): if FILE_URL is None: url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path) - else: - # noinspection PyUnresolvedReferences - url = FILE_URL.format(token, file_path) - async with await session_manager.get_session() as session: - async with session.get(url, proxy=proxy) as response: - result = await response.read() - if response.status != 200: - raise ApiHTTPException('Download file', result) - + else: url = FILE_URL.format(token, file_path) + session = await session_manager.get_session() + async with session.get(url, proxy=proxy) as response: + if response.status != 200: + raise ApiHTTPException('Download file', result) + result = await response.read() + return result From 783beb165b9b172d5ea1e6913f96f1e413f35d68 Mon Sep 17 00:00:00 2001 From: orocane Date: Sat, 10 Sep 2022 15:59:40 +0800 Subject: [PATCH 1113/1808] raise other exceptions in antiflood --- telebot/util.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index 8170f676f..0211f5215 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -654,6 +654,10 @@ def antiflood(function: Callable, *args, **kwargs): if ex.error_code == 429: sleep(ex.result_json['parameters']['retry_after']) msg = function(*args, **kwargs) + else: + raise + except: + raise finally: return msg From 2f8d878f063019f1268a4fed990ddbdab4da6e8b Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 10 Sep 2022 14:34:56 +0400 Subject: [PATCH 1114/1808] Fixed difference between request_timeout and timeout. getUpdates parameter may contain 2 parameters: request_timeout & timeout. other methods may contain timeout parameter that should be applied to ClientTimeout. --- telebot/asyncio_helper.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 00d4aa3c7..bf97033bb 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -56,10 +56,29 @@ async def get_session(self): session_manager = SessionManager() -async def _process_request(token, url, method='get', params=None, files=None, request_timeout=None): +async def _process_request(token, url, method='get', params=None, files=None, **kwargs): + # Let's resolve all timeout parameters. + # getUpdates parameter may contain 2 parameters: request_timeout & timeout. + # other methods may contain timeout parameter that should be applied to + # ClientTimeout only. + # timeout should be added to params for getUpdates. All other timeout's should be used + # for request timeout. + try: + request_timeout = kwargs.pop('request_timeout') + # if exception wasn't raised, then we have request_timeout in kwargs(this is getUpdates method) + # so we will simply apply that request_timeout to ClientTimeout + except KeyError: + # exception was raised, so we know that this method is not getUpdates. + # let's check for timeout in params + request_timeout = kwargs.pop('timeout', None) + # we will apply default request_timeout if there is no timeout in params + # otherwise, we will use timeout parameter applied for payload. + request_timeout = REQUEST_TIMEOUT if request_timeout is None else request_timeout + + + # Preparing data by adding all parameters and files to FormData params = _prepare_data(params, files) - if request_timeout is None: - request_timeout = REQUEST_TIMEOUT + timeout = aiohttp.ClientTimeout(total=request_timeout) got_result = False current_try=0 From da5084f53c937cf96d569d4d4c11c78aa01607e5 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 10 Sep 2022 14:36:56 +0400 Subject: [PATCH 1115/1808] Update asyncio_helper.py --- 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 bf97033bb..47095a2af 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -70,7 +70,7 @@ async def _process_request(token, url, method='get', params=None, files=None, ** except KeyError: # exception was raised, so we know that this method is not getUpdates. # let's check for timeout in params - request_timeout = kwargs.pop('timeout', None) + request_timeout = params.pop('timeout', None) # we will apply default request_timeout if there is no timeout in params # otherwise, we will use timeout parameter applied for payload. request_timeout = REQUEST_TIMEOUT if request_timeout is None else request_timeout From a06b4a1e9c221b5294886743cbf00b8b5d066824 Mon Sep 17 00:00:00 2001 From: orocane Date: Sat, 10 Sep 2022 21:46:16 +0800 Subject: [PATCH 1116/1808] raise other exceptions in antiflood --- telebot/util.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 0211f5215..6d29e9493 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -647,19 +647,15 @@ def antiflood(function: Callable, *args, **kwargs): """ from telebot.apihelper import ApiTelegramException from time import sleep - msg = None + try: - msg = function(*args, **kwargs) + return function(*args, **kwargs) except ApiTelegramException as ex: if ex.error_code == 429: sleep(ex.result_json['parameters']['retry_after']) - msg = function(*args, **kwargs) + return function(*args, **kwargs) else: raise - except: - raise - finally: - return msg def parse_web_app_data(token: str, raw_init_data: str): From 0028feb4c5888ce65fec69a3064275e21b1bcd11 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 10 Sep 2022 20:14:48 +0400 Subject: [PATCH 1117/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 47095a2af..8b20d91c3 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -21,7 +21,7 @@ FILE_URL = None -REQUEST_TIMEOUT = None +REQUEST_TIMEOUT = 300 MAX_RETRIES = 3 REQUEST_LIMIT = 50 @@ -63,12 +63,11 @@ async def _process_request(token, url, method='get', params=None, files=None, ** # ClientTimeout only. # timeout should be added to params for getUpdates. All other timeout's should be used # for request timeout. - try: - request_timeout = kwargs.pop('request_timeout') - # if exception wasn't raised, then we have request_timeout in kwargs(this is getUpdates method) - # so we will simply apply that request_timeout to ClientTimeout - except KeyError: - # exception was raised, so we know that this method is not getUpdates. + request_timeout = kwargs.pop('request_timeout', False) + # if we got some value, then we have request_timeout in kwargs(this is getUpdates method) + # so we will simply apply that request_timeout to ClientTimeout + # and if we got False, then we don't have request_timeout in kwargs(this is not getUpdates method) + if request_timeout is False: # let's check for timeout in params request_timeout = params.pop('timeout', None) # we will apply default request_timeout if there is no timeout in params From 4f97b26e81eb5f7fd6518357aa80e0d506d7666e Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 10 Sep 2022 20:37:13 +0400 Subject: [PATCH 1118/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 8b20d91c3..4b4453227 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -63,16 +63,17 @@ async def _process_request(token, url, method='get', params=None, files=None, ** # ClientTimeout only. # timeout should be added to params for getUpdates. All other timeout's should be used # for request timeout. - request_timeout = kwargs.pop('request_timeout', False) - # if we got some value, then we have request_timeout in kwargs(this is getUpdates method) - # so we will simply apply that request_timeout to ClientTimeout - # and if we got False, then we don't have request_timeout in kwargs(this is not getUpdates method) - if request_timeout is False: + # here we got request_timeout, so this is getUpdates method. + if 'request_timeout' in kwargs: + request_timeout = kwargs.pop('request_timeout') + + else: # let's check for timeout in params request_timeout = params.pop('timeout', None) # we will apply default request_timeout if there is no timeout in params # otherwise, we will use timeout parameter applied for payload. - request_timeout = REQUEST_TIMEOUT if request_timeout is None else request_timeout + + request_timeout = REQUEST_TIMEOUT if request_timeout is None else request_timeout # Preparing data by adding all parameters and files to FormData From 96e137f5e60c59076c721cdbb5430cf5ccd537f1 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 16 Sep 2022 22:39:40 +0400 Subject: [PATCH 1119/1808] Update setup.py --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 2e60d9142..5180f5623 100644 --- a/setup.py +++ b/setup.py @@ -31,6 +31,9 @@ def read(filename): 'aiohttp': 'aiohttp', 'fastapi': 'fastapi', 'uvicorn': 'uvicorn', + 'psutil': 'psutil', + 'coloredlogs': 'coloredlogs', + 'watchdog': 'watchdog' }, classifiers=[ 'Development Status :: 5 - Production/Stable', From da639dd1f66adf1e2c4b6fcf41f2b44418fffb92 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Sep 2022 11:57:12 +0300 Subject: [PATCH 1120/1808] Handlers and Middlewares processing union Call for handlers now union in a single function for future extension. Plus minor fixes in storages. --- telebot/__init__.py | 174 ++++++++++++++++-------------- telebot/ext/sync/webhooks.py | 18 ++-- telebot/storage/base_storage.py | 5 +- telebot/storage/memory_storage.py | 1 + telebot/storage/pickle_storage.py | 2 +- telebot/storage/redis_storage.py | 1 + 6 files changed, 107 insertions(+), 94 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 026d32eca..2e8caa924 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -469,6 +469,7 @@ def run_webhooks(self, webhook_url = "{}://{}:{}/{}".format(protocol, listen, port, url_path) if certificate and certificate_key: + # noinspection PyTypeChecker ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(certificate, certificate_key) @@ -5875,7 +5876,7 @@ def _check_filter(self, message_filter, filter_value, message): return False # middleware check-up method - def _check_middleware(self, update_type): + def _check_middlewares(self, update_type): """ Check middleware @@ -5889,100 +5890,111 @@ def _check_middleware(self, update_type): def _run_middlewares_and_handler(self, message, handlers, middlewares, update_type): """ - This class is made to run handler and middleware in queue. + This class is made to run handlers and middlewares in queue. - :param handler: handler that should be executed. - :param middleware: middleware that should be executed. + :param message: received message (update part) to process with handlers and/or middlewares + :param handlers: all created handlers (not filtered) + :param middlewares: middlewares that should be executed (already filtered) + :param update_type: handler/update type (Update field name) :return: """ - data = {} - params =[] - handler_error = None - skip_handlers = False - - if middlewares: - for middleware in middlewares: - if middleware.update_sensitive: - if hasattr(middleware, f'pre_process_{update_type}'): - result = getattr(middleware, f'pre_process_{update_type}')(message, data) - else: - logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) - result = None - else: - result = middleware.pre_process(message, data) - # We will break this loop if CancelUpdate is returned - # Also, we will not run other middlewares - if isinstance(result, CancelUpdate): - return - elif isinstance(result, SkipHandler): - skip_handlers = True - - if handlers and not(skip_handlers): - try: - for handler in handlers: - process_handler = self._test_message_handler(handler, message) - if not process_handler: continue - for i in inspect.signature(handler['function']).parameters: - params.append(i) - if len(params) == 1: - handler['function'](message) - elif "data" in params: - if len(params) == 2: - handler['function'](message, data) - elif len(params) == 3: - handler['function'](message, data=data, bot=self) + + if not self.use_class_middlewares: + for message_handler in handlers: + if self._test_message_handler(message_handler, message): + self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') + break + else: + data = {} + params =[] + handler_error = None + skip_handlers = False + + if middlewares: + for middleware in middlewares: + if middleware.update_sensitive: + if hasattr(middleware, f'pre_process_{update_type}'): + result = getattr(middleware, f'pre_process_{update_type}')(message, data) else: - logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) - return + logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) + result = None else: - data_copy = data.copy() - for key in list(data_copy): - # remove data from data_copy if handler does not accept it - if key not in params: - del data_copy[key] - if handler.get('pass_bot'): - data_copy["bot"] = self - if len(data_copy) > len(params) - 1: # remove the message parameter - logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) - return - handler["function"](message, **data_copy) - break - except Exception as e: - handler_error = e - if self.exception_handler: - self.exception_handler.handle(e) - else: - logging.error(str(e)) - logger.debug("Exception traceback:\n%s", traceback.format_exc()) - - if middlewares: - for middleware in middlewares: - if middleware.update_sensitive: - if hasattr(middleware, f'post_process_{update_type}'): - getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) + result = middleware.pre_process(message, data) + # We will break this loop if CancelUpdate is returned + # Also, we will not run other middlewares + if isinstance(result, CancelUpdate): + return + elif isinstance(result, SkipHandler): + skip_handlers = True + + if handlers and not(skip_handlers): + try: + for handler in handlers: + process_handler = self._test_message_handler(handler, message) + if not process_handler: continue + for i in inspect.signature(handler['function']).parameters: + params.append(i) + if len(params) == 1: + handler['function'](message) + elif "data" in params: + if len(params) == 2: + handler['function'](message, data) + elif len(params) == 3: + handler['function'](message, data=data, bot=self) + else: + logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) + return + else: + data_copy = data.copy() + for key in list(data_copy): + # remove data from data_copy if handler does not accept it + if key not in params: + del data_copy[key] + if handler.get('pass_bot'): + data_copy["bot"] = self + if len(data_copy) > len(params) - 1: # remove the message parameter + logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) + return + handler["function"](message, **data_copy) + break + except Exception as e: + handler_error = e + if self.exception_handler: + self.exception_handler.handle(e) else: - logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) - else: - middleware.post_process(message, data, handler_error) + logging.error(str(e)) + logger.debug("Exception traceback:\n%s", traceback.format_exc()) + + if middlewares: + for middleware in middlewares: + if middleware.update_sensitive: + if hasattr(middleware, f'post_process_{update_type}'): + getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) + else: + logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) + else: + middleware.post_process(message, data, handler_error) def _notify_command_handlers(self, handlers, new_messages, update_type): """ Notifies command handlers. - :param handlers: - :param new_messages: + :param handlers: all created handlers + :param new_messages: received messages to proceed + :param update_type: handler/update type (Update fields) :return: """ if not(handlers) and not(self.use_class_middlewares): return + if self.use_class_middlewares: + middlewares = self._check_middlewares(update_type) + else: + middlewares = None for message in new_messages: - if not self.use_class_middlewares: - for message_handler in handlers: - if self._test_message_handler(message_handler, message): - self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') - break - else: - middleware = self._check_middleware(update_type) - self._exec_task(self._run_middlewares_and_handler, message, handlers=handlers, middlewares=middleware, update_type=update_type) - return + self._exec_task( + self._run_middlewares_and_handler, + message, + handlers=handlers, + middlewares=middlewares, + update_type=update_type) diff --git a/telebot/ext/sync/webhooks.py b/telebot/ext/sync/webhooks.py index 6e1714b13..a73c16c4c 100644 --- a/telebot/ext/sync/webhooks.py +++ b/telebot/ext/sync/webhooks.py @@ -1,6 +1,5 @@ """ This file is used by TeleBot.run_webhooks() function. - Fastapi is required to run this script. """ @@ -15,15 +14,11 @@ except ImportError: fastapi_installed = False - from telebot.types import Update - from typing import Optional - - class SyncWebhookListener: def __init__(self, bot, secret_token: str, host: Optional[str]="127.0.0.1", @@ -33,13 +28,13 @@ def __init__(self, bot, debug: Optional[bool]=False ) -> None: """ - Aynchronous implementation of webhook listener - for asynchronous version of telebot. + Synchronous implementation of webhook listener + for synchronous version of telebot. Not supposed to be used manually by user. - Use AsyncTeleBot.run_webhooks() instead. + Use TeleBot.run_webhooks() instead. - :param bot: AsyncTeleBot instance. - :type bot: telebot.async_telebot.AsyncTeleBot + :param bot: TeleBot instance. + :type bot: telebot.TeleBot :param secret_token: Telegram secret token :type secret_token: str @@ -77,7 +72,8 @@ def __init__(self, bot, self._prepare_endpoint_urls() - def _check_dependencies(self): + @staticmethod + def _check_dependencies(): if not fastapi_installed: raise ImportError('Fastapi or uvicorn is not installed. Please install it via pip.') diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py index bafd9a1a8..92b31ba85 100644 --- a/telebot/storage/base_storage.py +++ b/telebot/storage/base_storage.py @@ -41,7 +41,10 @@ def reset_data(self, chat_id, user_id): def get_state(self, chat_id, user_id): raise NotImplementedError - + + def get_interactive_data(self, chat_id, user_id): + raise NotImplementedError + def save(self, chat_id, user_id, data): raise NotImplementedError diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 67cd984b5..7d71c7ccd 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -3,6 +3,7 @@ class StateMemoryStorage(StateStorageBase): def __init__(self) -> None: + super().__init__() self.data = {} # # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index ff72ac3b6..dfffcf830 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -5,8 +5,8 @@ class StatePickleStorage(StateStorageBase): - # noinspection PyMissingConstructor def __init__(self, file_path="./.state-save/states.pkl") -> None: + super().__init__() self.file_path = file_path self.create_dir() self.data = self.read() diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 2a5fb4a37..a10494811 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -16,6 +16,7 @@ class StateRedisStorage(StateStorageBase): TeleBot(storage=StateRedisStorage()) """ def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_'): + super().__init__() self.redis = ConnectionPool(host=host, port=port, db=db, password=password) #self.con = Redis(connection_pool=self.redis) -> use this when necessary # From c14760d81c4623ed5b02664551fc5e44e08527e6 Mon Sep 17 00:00:00 2001 From: byehack Date: Sat, 17 Sep 2022 13:58:28 +0430 Subject: [PATCH 1121/1808] don't block loop --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 68e1768bc..389e20009 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -270,7 +270,7 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option logger.error("Infinity polling exception: %s", str(e)) if logger_level and logger_level >= logging.DEBUG: logger.error("Exception traceback:\n%s", traceback.format_exc()) - time.sleep(3) + await asyncio.sleep(3) continue if logger_level and logger_level >= logging.INFO: logger.error("Infinity polling: polling exited") From 598de25b6dded3b71423242663c993951e5c13cb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Sep 2022 12:55:55 +0300 Subject: [PATCH 1122/1808] Rename _check_middlewares to _get_middlewares --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2e8caa924..7a5776696 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5876,7 +5876,7 @@ def _check_filter(self, message_filter, filter_value, message): return False # middleware check-up method - def _check_middlewares(self, update_type): + def _get_middlewares(self, update_type): """ Check middleware @@ -5988,7 +5988,7 @@ def _notify_command_handlers(self, handlers, new_messages, update_type): return if self.use_class_middlewares: - middlewares = self._check_middlewares(update_type) + middlewares = self._get_middlewares(update_type) else: middlewares = None for message in new_messages: From e7a96ec2edd41409e7ebe9036fc2931da97fbac4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Sep 2022 14:09:05 +0300 Subject: [PATCH 1123/1808] Rename also in Async --- telebot/async_telebot.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 68e1768bc..784479d48 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -339,7 +339,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: await self.close_session() logger.warning('Polling is stopped.') - def _loop_create_task(self, coro): + @staticmethod + def _loop_create_task(coro): return asyncio.create_task(coro) async def _process_updates(self, handlers, messages, update_type): @@ -351,12 +352,12 @@ async def _process_updates(self, handlers, messages, update_type): :return: """ tasks = [] + middlewares = await self._get_middlewares(update_type) for message in messages: - middleware = await self.process_middlewares(update_type) - tasks.append(self._run_middlewares_and_handlers(handlers, message, middleware, update_type)) + tasks.append(self._run_middlewares_and_handlers(message, handlers, middlewares, update_type)) await asyncio.gather(*tasks) - async def _run_middlewares_and_handlers(self, handlers, message, middlewares, update_type): + async def _run_middlewares_and_handlers(self, message, handlers, middlewares, update_type): handler_error = None data = {} skip_handlers = False @@ -426,7 +427,7 @@ async def _run_middlewares_and_handlers(self, handlers, message, middlewares, up else: logger.error('Middleware {} does not have post_process_{} method. post_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) else: await middleware.post_process(message, data, handler_error) - # update handling + async def process_new_updates(self, updates: List[types.Update]): """ Process new updates. @@ -615,7 +616,7 @@ async def process_chat_join_request(self, chat_join_request): """ await self._process_updates(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') - async def process_middlewares(self, update_type): + async def _get_middlewares(self, update_type): """ :meta private: """ From 52e09637c2f360d7f99d5599be1c92e8b515b21b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Sep 2022 23:17:07 +0300 Subject: [PATCH 1124/1808] Fix: do not call handler in one more task --- telebot/__init__.py | 31 ++++++++++++++++++------------- telebot/async_telebot.py | 10 ++++++++++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7a5776696..539bf7ef5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1088,11 +1088,6 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ def _exec_task(self, task, *args, **kwargs): - if kwargs: - if kwargs.pop('task_type', "") == 'handler': - if kwargs.pop('pass_bot', False): - kwargs['bot'] = self - if self.threaded: self.worker_pool.put(task, *args, **kwargs) else: @@ -4791,8 +4786,14 @@ def check_regexp_input(regexp, method_name): if not isinstance(regexp, str): logger.error(f"{method_name}: Regexp filter should be string. Not able to use the supplied type.") - def message_handler(self, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, - content_types: Optional[List[str]]=None, chat_types: Optional[List[str]]=None, **kwargs): + def message_handler( + self, + commands: Optional[List[str]]=None, + regexp: Optional[str]=None, + func: Optional[Callable]=None, + content_types: Optional[List[str]]=None, + chat_types: Optional[List[str]]=None, + **kwargs): """ Handles New incoming message of any kind - text, photo, sticker, etc. As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. @@ -5890,7 +5891,7 @@ def _get_middlewares(self, update_type): def _run_middlewares_and_handler(self, message, handlers, middlewares, update_type): """ - This class is made to run handlers and middlewares in queue. + This method is made to run handlers and middlewares in queue. :param message: received message (update part) to process with handlers and/or middlewares :param handlers: all created handlers (not filtered) @@ -5900,10 +5901,14 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty """ if not self.use_class_middlewares: - for message_handler in handlers: - if self._test_message_handler(message_handler, message): - self._exec_task(message_handler['function'], message, pass_bot=message_handler['pass_bot'], task_type='handler') - break + if handlers: + for handler in handlers: + if self._test_message_handler(handler, message): + if handler.get('pass_bot', False): + handler['function'](message, bot = self) + else: + handler['function'](message) + break else: data = {} params =[] @@ -5962,7 +5967,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty if self.exception_handler: self.exception_handler.handle(e) else: - logging.error(str(e)) + logger.error(str(e)) logger.debug("Exception traceback:\n%s", traceback.format_exc()) if middlewares: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 784479d48..abdaa4561 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -358,6 +358,16 @@ async def _process_updates(self, handlers, messages, update_type): await asyncio.gather(*tasks) async def _run_middlewares_and_handlers(self, message, handlers, middlewares, update_type): + """ + This method is made to run handlers and middlewares in queue. + + :param message: received message (update part) to process with handlers and/or middlewares + :param handlers: all created handlers (not filtered) + :param middlewares: middlewares that should be executed (already filtered) + :param update_type: handler/update type (Update field name) + :return: + """ + handler_error = None data = {} skip_handlers = False From e002484a9bc89ffdbfad7c3d85c6781c3d8c6e64 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 23 Sep 2022 21:52:40 +0400 Subject: [PATCH 1125/1808] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Added=20some=20f?= =?UTF-8?q?requent=20parameters=20to=20classes(see=20full=20list)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added: - disable_web_page_preview - disable_notification - protect_content - allow_sending_without_reply --- telebot/__init__.py | 120 ++++++++++++++++++++++++++++++++++++--- telebot/async_telebot.py | 115 +++++++++++++++++++++++++++++++++---- 2 files changed, 215 insertions(+), 20 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 026d32eca..b0c546f88 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -131,6 +131,18 @@ class TeleBot: :param use_class_middlewares: Use class middlewares, defaults to False :type use_class_middlewares: :obj:`bool`, optional + + :param disable_web_page_preview: Default value for disable_web_page_preview, defaults to None + :type disable_web_page_preview: :obj:`bool`, optional + + :param disable_notification: Default value for disable_notification, defaults to None + :type disable_notification: :obj:`bool`, optional + + :param protect_content: Default value for protect_content, defaults to None + :type protect_content: :obj:`bool`, optional + + :param allow_sending_without_reply: Default value for allow_sending_without_reply, defaults to None + :type allow_sending_without_reply: :obj:`bool`, optional """ def __init__( @@ -139,18 +151,32 @@ def __init__( next_step_backend: Optional[HandlerBackend]=None, reply_backend: Optional[HandlerBackend]=None, exception_handler: Optional[ExceptionHandler]=None, last_update_id: Optional[int]=0, suppress_middleware_excepions: Optional[bool]=False, state_storage: Optional[StateStorageBase]=StateMemoryStorage(), - use_class_middlewares: Optional[bool]=False + use_class_middlewares: Optional[bool]=False, + disable_web_page_preview: Optional[bool]=None, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + allow_sending_without_reply: Optional[bool]=None ): + + # update-related self.token = token - self.parse_mode = parse_mode - self.update_listener = [] - self.skip_pending = skip_pending + self.skip_pending = skip_pending # backward compatibility + self.last_update_id = last_update_id + + # propertys self.suppress_middleware_excepions = suppress_middleware_excepions + self.parse_mode = parse_mode + self.disable_web_page_preview = disable_web_page_preview + self.disable_notification = disable_notification + self.protect_content = protect_content + self.allow_sending_without_reply = allow_sending_without_reply + # threading-related self.__stop_polling = threading.Event() - self.last_update_id = last_update_id self.exc_info = None + # states & register_next_step_handler + self.current_states = state_storage self.next_step_backend = next_step_backend if not self.next_step_backend: self.next_step_backend = MemoryHandlerBackend() @@ -159,8 +185,9 @@ def __init__( if not self.reply_backend: self.reply_backend = MemoryHandlerBackend() + # handlers self.exception_handler = exception_handler - + self.update_listener = [] self.message_handlers = [] self.edited_message_handlers = [] self.channel_post_handlers = [] @@ -178,8 +205,7 @@ def __init__( self.custom_filters = {} self.state_handlers = [] - self.current_states = state_storage - + # middlewares self.use_class_middlewares = use_class_middlewares if apihelper.ENABLE_MIDDLEWARE and not use_class_middlewares: self.typed_middleware_handlers = { @@ -205,6 +231,8 @@ def __init__( 'You are using class based middlewares while having ENABLE_MIDDLEWARE set to True. This is not recommended.' ) self.middlewares = [] if use_class_middlewares else None + + # threads self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(self, num_threads=num_threads) @@ -1444,6 +1472,10 @@ def send_message( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( apihelper.send_message( @@ -1482,6 +1514,9 @@ def forward_message( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + return types.Message.de_json( apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) @@ -1544,6 +1579,11 @@ def copy_message( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, @@ -1621,6 +1661,10 @@ def send_dice( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( apihelper.send_dice( self.token, chat_id, emoji, disable_notification, reply_to_message_id, @@ -1684,6 +1728,9 @@ def send_photo( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( apihelper.send_photo( @@ -1769,6 +1816,9 @@ def send_audio( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( apihelper.send_audio( @@ -1837,6 +1887,9 @@ def send_voice( :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( apihelper.send_voice( @@ -1917,6 +1970,10 @@ def send_document( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(document): # function typo miss compatibility document = data @@ -1981,9 +2038,14 @@ def send_sticker( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(sticker): # function typo miss compatibility sticker = data + return types.Message.de_json( apihelper.send_data( self.token, chat_id, sticker, 'sticker', @@ -2071,6 +2133,10 @@ def send_video( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(video): # function typo miss compatibility video = data @@ -2157,6 +2223,9 @@ def send_animation( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( apihelper.send_animation( @@ -2224,6 +2293,10 @@ def send_video_note( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( apihelper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, @@ -2270,6 +2343,10 @@ def send_media_group( :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + result = apihelper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, allow_sending_without_reply, protect_content) @@ -2338,6 +2415,10 @@ def send_location( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( apihelper.send_location( self.token, chat_id, latitude, longitude, live_period, @@ -2509,6 +2590,10 @@ def send_venue( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( apihelper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, @@ -2571,6 +2656,10 @@ def send_contact( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, @@ -3322,6 +3411,8 @@ def pin_chat_message( :return: True on success. :rtype: :obj:`bool` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: @@ -3403,6 +3494,7 @@ def edit_message_text( :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, entities, disable_web_page_preview, reply_markup) @@ -3515,6 +3607,10 @@ def send_game( :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + result = apihelper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, reply_markup, timeout, @@ -3717,6 +3813,10 @@ def send_invoice( :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + result = apihelper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, @@ -3918,6 +4018,10 @@ def send_poll( :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if isinstance(question, types.Poll): raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 389e20009..ccbc88a6b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3,7 +3,6 @@ import logging import re -import time import traceback from typing import Any, Awaitable, Callable, List, Optional, Union @@ -99,21 +98,45 @@ class AsyncTeleBot: :param state_storage: Storage for states, defaults to StateMemoryStorage() :type state_storage: :class:`telebot.asyncio_storage.StateMemoryStorage`, optional + :param disable_web_page_preview: Default value for disable_web_page_preview, defaults to None + :type disable_web_page_preview: :obj:`bool`, optional + + :param disable_notification: Default value for disable_notification, defaults to None + :type disable_notification: :obj:`bool`, optional + + :param protect_content: Default value for protect_content, defaults to None + :type protect_content: :obj:`bool`, optional + + :param allow_sending_without_reply: Default value for allow_sending_without_reply, defaults to None + :type allow_sending_without_reply: :obj:`bool`, optional + """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[int]=None, - exception_handler: Optional[ExceptionHandler]=None, state_storage: Optional[StateStorageBase]=StateMemoryStorage()) -> None: + exception_handler: Optional[ExceptionHandler]=None, + state_storage: Optional[StateStorageBase]=StateMemoryStorage(), + disable_web_page_preview: Optional[bool]=None, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + allow_sending_without_reply: Optional[bool]=None) -> None: + + # update-related self.token = token - self.offset = offset - self.token = token - self.parse_mode = parse_mode - self.update_listener = [] + # properties + self.parse_mode = parse_mode + self.disable_web_page_preview = disable_web_page_preview + self.disable_notification = disable_notification + self.protect_content = protect_content + self.allow_sending_without_reply = allow_sending_without_reply + # states + self.current_states = state_storage + # handlers + self.update_listener = [] self.exception_handler = exception_handler - self.message_handlers = [] self.edited_message_handlers = [] self.channel_post_handlers = [] @@ -130,9 +153,6 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.chat_join_request_handlers = [] self.custom_filters = {} self.state_handlers = [] - - self.current_states = state_storage - self.middlewares = [] async def close_session(self): @@ -2277,6 +2297,10 @@ async def send_message( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_message( @@ -2315,6 +2339,9 @@ async def forward_message( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + return types.Message.de_json( await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) @@ -2377,6 +2404,9 @@ async def copy_message( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, @@ -2455,6 +2485,10 @@ async def send_dice( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, reply_to_message_id, @@ -2517,6 +2551,9 @@ async def send_photo( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_photo( @@ -2601,6 +2638,9 @@ async def send_audio( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_audio( @@ -2668,6 +2708,9 @@ async def send_voice( :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_voice( @@ -2747,6 +2790,10 @@ async def send_document( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(document): # function typo miss compatibility document = data @@ -2807,6 +2854,10 @@ async def send_sticker( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(sticker): # function typo miss compatibility logger.warning("send_sticker: data parameter is deprecated. Use sticker instead.") @@ -2898,6 +2949,10 @@ async def send_video( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(video): # function typo miss compatibility logger.warning("send_sticker: data parameter is deprecated. Use video instead.") @@ -2985,6 +3040,9 @@ async def send_animation( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_animation( @@ -3051,6 +3109,10 @@ async def send_video_note( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, @@ -3096,6 +3158,10 @@ async def send_media_group( :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, allow_sending_without_reply, protect_content) @@ -3163,6 +3229,10 @@ async def send_location( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( await asyncio_helper.send_location( self.token, chat_id, latitude, longitude, live_period, @@ -3333,6 +3403,10 @@ async def send_venue( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( await asyncio_helper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, @@ -3394,6 +3468,10 @@ async def send_contact( :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + return types.Message.de_json( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, @@ -4154,6 +4232,8 @@ async def pin_chat_message( :return: True on success. :rtype: :obj:`bool` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + return await asyncio_helper.pin_chat_message(self.token, chat_id, message_id, disable_notification) async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: @@ -4235,6 +4315,7 @@ async def edit_message_text( :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode + disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, entities, disable_web_page_preview, reply_markup) @@ -4347,6 +4428,10 @@ async def send_game( :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, reply_markup, timeout, @@ -4548,6 +4633,10 @@ async def send_invoice( :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + result = await asyncio_helper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, @@ -4749,12 +4838,14 @@ async def send_poll( :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + explanation_parse_mode = self.parse_mode if (explanation_parse_mode is None) else explanation_parse_mode if isinstance(question, types.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 - return types.Message.de_json( await asyncio_helper.send_poll( self.token, chat_id, From d943f4064398dfd963de951770b883a0fc83f027 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Sep 2022 15:33:11 +0300 Subject: [PATCH 1126/1808] Added a new bot to the list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 919d5a3be..903718e2c 100644 --- a/README.md +++ b/README.md @@ -885,5 +885,6 @@ Here are some examples of template: * [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) GuXiaoJiang is a multi-functional robot, such as OSU game information query, IP test, animation screenshot search and other functions. * [Feedback-bot](https://github.com/coder2020official/feedbackbot) A feedback bot for user-admin communication. Made on AsyncTeleBot, using [template](https://github.com/coder2020official/asynctelebot_template). * [TeleServ](https://github.com/ablakely/TeleServ) by [ablakely](https://github.com/ablakely) This is a Telegram to IRC bridge which links as an IRC server and makes Telegram users appear as native IRC users. +* [Simple Store Bot](https://github.com/AntonGlyzin/myshopbot) by [Anton Glyzin](https://github.com/AntonGlyzin) This is a simple telegram-store with an admin panel. Designed according to a template. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From eb576d83fb6d5b6c19d51ad12eb0cc61605ff478 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Sep 2022 23:22:21 +0400 Subject: [PATCH 1127/1808] Fixed a bug, made improvements in reload system Didn't test that much, there is still some stuff to do --- telebot/__init__.py | 45 ++++++++++++++++++--------- telebot/async_telebot.py | 64 ++++++++++++++++++++++++--------------- telebot/asyncio_helper.py | 2 +- telebot/ext/reloader.py | 4 +-- 4 files changed, 72 insertions(+), 43 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 63c10a8e7..cfe61042e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -880,10 +880,27 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) + def _setup_change_detector(self): + try: + from watchdog.observers import Observer + from telebot.ext.reloader import EventHandler + except ImportError: + raise ImportError( + 'Please install watchdog and psutil before using restart_on_change option.' + ) + + self.event_handler = EventHandler() + path = path_to_watch if path_to_watch else None + if path is None: + path = sys.argv[1] if len(sys.argv) > 1 else '.' # current directory + + self.observer = Observer() + self.observer.schedule(self.event_handler, path, recursive=True) + self.observer.start() def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, long_polling_timeout: Optional[int]=20, logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, - restart_on_change: Optional[bool]=False, *args, **kwargs): + restart_on_change: Optional[bool]=False, path_to_watch: Optional[str]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -912,11 +929,18 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo :param restart_on_change: Restart a file on file(s) change. Defaults to False :type restart_on_change: :obj:`bool` + :param path_to_watch: Path to watch for changes. Defaults to current directory + :type path_to_watch: :obj:`str` + :return: """ if skip_pending: self.__skip_updates() + if restart_on_change: + self._setup_change_detector(path_to_watch) + restart_on_change = False + while not self.__stop_polling.is_set(): try: self.polling(non_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, @@ -938,7 +962,7 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=False, interval: Optional[int]=0, timeout: Optional[int]=20, long_polling_timeout: Optional[int]=20, logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, - none_stop: Optional[bool]=None, restart_on_change: Optional[bool]=False): + none_stop: Optional[bool]=None, restart_on_change: Optional[bool]=False, path_to_watch: Optional[str]=None): """ This function creates a new Thread that calls an internal __retrieve_updates function. This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. @@ -984,6 +1008,9 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :param restart_on_change: Restart a file on file(s) change. Defaults to False :type restart_on_change: :obj:`bool` + + :param path_to_watch: Path to watch for changes. Defaults to None + :type path_to_watch: :obj:`str` :return: """ @@ -995,19 +1022,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F self.__skip_updates() if restart_on_change: - try: - from watchdog.observers import Observer - from telebot.ext.reloader import EventHandler - except ImportError: - raise ImportError( - 'Please install watchdog and psutil before using restart_on_change option.' - ) - - event_handler = EventHandler() - path = sys.argv[1] if len(sys.argv) > 1 else '.' - observer = Observer() - observer.schedule(event_handler, path, recursive=True) - observer.start() + self._setup_change_detector(path_to_watch) logger.info('Starting your bot with username: [@%s]', self.user.username) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index b705a365c..9e99f93bb 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5,6 +5,7 @@ import re import traceback from typing import Any, Awaitable, Callable, List, Optional, Union +import sys # this imports are used to avoid circular import error import telebot.util @@ -213,9 +214,27 @@ async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=Non json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] + def _setup_change_detector(self, path_to_watch: str) -> None: + try: + from watchdog.observers import Observer + from telebot.ext.reloader import EventHandler + except ImportError: + raise ImportError( + 'Please install watchdog and psutil before using restart_on_change option.' + ) + + self.event_handler = EventHandler() + path = path_to_watch if path_to_watch else None + if path is None: + path = sys.argv[1] if len(sys.argv) > 1 else '.' # current directory + + self.observer = Observer() + self.observer.schedule(self.event_handler, path, recursive=True) + self.observer.start() + async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, request_timeout: Optional[int]=None, allowed_updates: Optional[List[str]]=None, - none_stop: Optional[bool]=None, restart_on_change: Optional[bool]=False): + none_stop: Optional[bool]=None, restart_on_change: Optional[bool]=False, path_to_watch: Optional[str]=None): """ Runs bot in long-polling mode in a main loop. This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly. @@ -259,6 +278,9 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= :param restart_on_change: Restart a file on file(s) change. Defaults to False :type restart_on_change: :obj:`bool` + + :param path_to_watch: Path to watch for changes. Defaults to current directory + :type path_to_watch: :obj:`str` :return: """ @@ -268,11 +290,15 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= if skip_pending: await self.skip_updates() - await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates, restart_on_change) + + if restart_on_change: + self._setup_change_detector(path_to_watch) + + await self._process_polling(non_stop, interval, timeout, request_timeout, allowed_updates) async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, request_timeout: Optional[int]=None, logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, - restart_on_change: Optional[bool]=False, *args, **kwargs): + restart_on_change: Optional[bool]=False, path_to_watch: Optional[str]=None, *args, **kwargs): """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. @@ -302,15 +328,24 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option :param restart_on_change: Restart a file on file(s) change. Defaults to False :type restart_on_change: :obj:`bool` + :param path_to_watch: Path to watch for changes. Defaults to current directory + :type path_to_watch: :obj:`str` + :return: None """ if skip_pending: await self.skip_updates() self._polling = True + + if restart_on_change: + restart_on_change = False + + self._setup_change_detector(path_to_watch) + while self._polling: try: await self._process_polling(non_stop=False, timeout=timeout, request_timeout=request_timeout, - allowed_updates=allowed_updates, restart_on_change=restart_on_change, *args, **kwargs) + allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: logger.error("Infinity polling exception: %s", str(e)) @@ -324,7 +359,7 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option logger.error("Break infinity polling") async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, - request_timeout: int=None, allowed_updates: Optional[List[str]]=None, restart_on_change: Optional[bool]=False): + request_timeout: int=None, allowed_updates: Optional[List[str]]=None): """ Function to process polling. @@ -341,30 +376,11 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: 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 restart_on_change: Restart a file on file(s) change. Defaults to False - :type restart_on_change: :obj:`bool` - :return: """ self._user = await self.get_me() - - - if restart_on_change: - try: - from watchdog.observers import Observer - from telebot.ext.reloader import EventHandler - except ImportError: - raise ImportError( - 'Please install watchdog and psutil before using restart_on_change option.' - ) - - event_handler = EventHandler() - path = sys.argv[1] if len(sys.argv) > 1 else '.' - observer = Observer() - observer.schedule(event_handler, path, recursive=True) - observer.start() logger.info('Starting your bot with username: [@%s]', self.user.username) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 03288e303..7e8958a4b 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -71,7 +71,7 @@ async def _process_request(token, url, method='get', params=None, files=None, ** else: # let's check for timeout in params - request_timeout = params.pop('timeout', None) + request_timeout = params.pop('timeout', None) if params else None # we will apply default request_timeout if there is no timeout in params # otherwise, we will use timeout parameter applied for payload. diff --git a/telebot/ext/reloader.py b/telebot/ext/reloader.py index 6ec08a5bf..d57453fc5 100644 --- a/telebot/ext/reloader.py +++ b/telebot/ext/reloader.py @@ -8,11 +8,9 @@ logger = logging.getLogger('TeleBot') - - class EventHandler(FileSystemEventHandler): def on_any_event(self, event: FileSystemEvent): - logger.info('* Detected changes in: %s , reloading', (event.src_path)) + logger.info('* Detected changes in: %s, reloading', (event.src_path)) restart_file() def restart_file(): From 04ff428bbab2b33941acad3c71dd1de103a2a2e4 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 1 Oct 2022 21:02:40 +0400 Subject: [PATCH 1128/1808] Added option to specify path to watch --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index cfe61042e..e5aad037d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -880,7 +880,7 @@ def __notify_update(self, new_messages): for listener in self.update_listener: self._exec_task(listener, new_messages) - def _setup_change_detector(self): + def _setup_change_detector(self, path_to_watch: str): try: from watchdog.observers import Observer from telebot.ext.reloader import EventHandler From ea69b8093d350ca21a9e25d1aa17787865075a29 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 1 Oct 2022 21:15:24 +0400 Subject: [PATCH 1129/1808] Added some notes --- telebot/__init__.py | 12 ++++++++++++ telebot/async_telebot.py | 13 ++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e5aad037d..98f2acafb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -95,6 +95,10 @@ class TeleBot: See more examples in examples/ directory: https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples + .. note:: + + Install coloredlogs module to specify colorful_logs=True + :param token: Token of a bot, should be obtained from @BotFather :type token: :obj:`str` @@ -904,6 +908,10 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. + .. note:: + + Install watchdog and psutil before using restart_on_change option. + :param timeout: Request connection timeout. :type timeout: :obj:`int` @@ -974,6 +982,10 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F .. deprecated:: 4.1.1 Use :meth:`infinity_polling` instead. + .. note:: + + Install watchdog and psutil before using restart_on_change option. + :param interval: Delay between two update retrivals :type interval: :obj:`int` diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 9e99f93bb..4279fbde4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -82,6 +82,10 @@ class AsyncTeleBot: See more examples in examples/ directory: https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples + .. note:: + + Install coloredlogs module to specify colorful_logs=True + :param token: Token of a bot, obtained from @BotFather :type token: :obj:`str` @@ -248,6 +252,10 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= Set non_stop=True if you want your bot to continue receiving updates if there is an error. + .. note:: + + Install watchdog and psutil before using restart_on_change option. + :param non_stop: Do not stop polling when an ApiException occurs. :type non_stop: :obj:`bool` @@ -276,7 +284,7 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= :param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility. :type none_stop: :obj:`bool` - :param restart_on_change: Restart a file on file(s) change. Defaults to False + :param restart_on_change: Restart a file on file(s) change. Defaults to False. :type restart_on_change: :obj:`bool` :param path_to_watch: Path to watch for changes. Defaults to current directory @@ -302,6 +310,9 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option """ Wrap polling with infinite loop and exception handling to avoid bot stops polling. + .. note:: + Install watchdog and psutil before using restart_on_change option. + :param timeout: Timeout in seconds for get_updates(Defaults to None) :type timeout: :obj:`int` From 27e0197855f2b7ddff48d38b4c0949df20f971af Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 1 Oct 2022 21:28:53 +0400 Subject: [PATCH 1130/1808] Added examples and made it possible to specify --path path for path --- .../asynchronous_telebot/detect_changes.py | 28 +++++++++++++++++++ examples/detect_changes.py | 28 +++++++++++++++++++ telebot/__init__.py | 4 ++- telebot/async_telebot.py | 3 +- 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 examples/asynchronous_telebot/detect_changes.py create mode 100644 examples/detect_changes.py diff --git a/examples/asynchronous_telebot/detect_changes.py b/examples/asynchronous_telebot/detect_changes.py new file mode 100644 index 000000000..0a0e14252 --- /dev/null +++ b/examples/asynchronous_telebot/detect_changes.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +# This is a simple echo bot using the decorator mechanism. +# It echoes any incoming text messages. + +from telebot.async_telebot import AsyncTeleBot +bot = AsyncTeleBot('TOKEN') + + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +async def send_welcome(message): + await bot.reply_to(message, """\ +Hi there, I am EchoBot. +I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\ +""") + + +# Handle all other messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +async def echo_message(message): + await bot.reply_to(message, message.text) + + +import asyncio +# only new versions(4.7.0+) +asyncio.run(bot.polling(restart_on_change=True)) diff --git a/examples/detect_changes.py b/examples/detect_changes.py new file mode 100644 index 000000000..da2a2c842 --- /dev/null +++ b/examples/detect_changes.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +# This is a simple echo bot using the decorator mechanism. +# It echoes any incoming text messages. + +import telebot + +API_TOKEN = '' + +bot = telebot.TeleBot(API_TOKEN) + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +def send_welcome(message): + bot.reply_to(message, """\ +Hi there, I am EchoBot. +I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\ +""") + + +# Handle all other messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +def echo_message(message): + bot.reply_to(message, message.text) + +# only versions greater than 4.7.0 +bot.infinity_polling(restart_on_change=True) diff --git a/telebot/__init__.py b/telebot/__init__.py index 98f2acafb..9d4c02156 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -896,7 +896,9 @@ def _setup_change_detector(self, path_to_watch: str): self.event_handler = EventHandler() path = path_to_watch if path_to_watch else None if path is None: - path = sys.argv[1] if len(sys.argv) > 1 else '.' # current directory + # Make it possible to specify --path argument to the script + path = sys.argv[sys.argv.index('--path') + 1] if '--path' in sys.argv else '.' + self.observer = Observer() self.observer.schedule(self.event_handler, path, recursive=True) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4279fbde4..148bb4069 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -230,7 +230,8 @@ def _setup_change_detector(self, path_to_watch: str) -> None: self.event_handler = EventHandler() path = path_to_watch if path_to_watch else None if path is None: - path = sys.argv[1] if len(sys.argv) > 1 else '.' # current directory + # Make it possible to specify --path argument to the script + path = sys.argv[sys.argv.index('--path') + 1] if '--path' in sys.argv else '.' self.observer = Observer() self.observer.schedule(self.event_handler, path, recursive=True) From b523cec22f0367c1e8abb2c86cba6a50d7f088cd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 1 Oct 2022 22:32:41 +0400 Subject: [PATCH 1131/1808] Improved code readability --- telebot/__init__.py | 3 +-- telebot/async_telebot.py | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9d4c02156..1b78cee42 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -949,12 +949,11 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo if restart_on_change: self._setup_change_detector(path_to_watch) - restart_on_change = False while not self.__stop_polling.is_set(): try: self.polling(non_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, - logger_level=logger_level, allowed_updates=allowed_updates, restart_on_change=restart_on_change, + logger_level=logger_level, allowed_updates=allowed_updates, restart_on_change=False, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 148bb4069..a2efc41cf 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -350,8 +350,6 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option self._polling = True if restart_on_change: - restart_on_change = False - self._setup_change_detector(path_to_watch) while self._polling: From 2d1f39085d4f5ff800595c65b75571eb121dfc13 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 1 Oct 2022 22:34:49 +0400 Subject: [PATCH 1132/1808] Improved code readability x 2 --- telebot/__init__.py | 6 +++--- telebot/async_telebot.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1b78cee42..12ecf7356 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -900,9 +900,9 @@ def _setup_change_detector(self, path_to_watch: str): path = sys.argv[sys.argv.index('--path') + 1] if '--path' in sys.argv else '.' - self.observer = Observer() - self.observer.schedule(self.event_handler, path, recursive=True) - self.observer.start() + self.event_observer = Observer() + self.event_observer.schedule(self.event_handler, path, recursive=True) + self.event_observer.start() def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, long_polling_timeout: Optional[int]=20, logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a2efc41cf..0aa10974b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -233,9 +233,9 @@ def _setup_change_detector(self, path_to_watch: str) -> None: # Make it possible to specify --path argument to the script path = sys.argv[sys.argv.index('--path') + 1] if '--path' in sys.argv else '.' - self.observer = Observer() - self.observer.schedule(self.event_handler, path, recursive=True) - self.observer.start() + self.event_observer = Observer() + self.event_observer.schedule(self.event_handler, path, recursive=True) + self.event_observer.start() async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, request_timeout: Optional[int]=None, allowed_updates: Optional[List[str]]=None, From 30aaf8d0f10377978dce33fa6e050dc717121eb2 Mon Sep 17 00:00:00 2001 From: byehack Date: Sun, 2 Oct 2022 03:27:06 +0330 Subject: [PATCH 1133/1808] Support ContinueHandling --- telebot/__init__.py | 17 +++++++++++------ telebot/async_telebot.py | 15 +++++++-------- telebot/handler_backends.py | 14 ++++++-------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 12ecf7356..72d244cb2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -37,7 +37,10 @@ logger.setLevel(logging.ERROR) from telebot import apihelper, util, types -from telebot.handler_backends import HandlerBackend, MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, CancelUpdate, SkipHandler, State +from telebot.handler_backends import ( + HandlerBackend, MemoryHandlerBackend, FileHandlerBackend, BaseMiddleware, + CancelUpdate, SkipHandler, State, ContinueHandling +) from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter @@ -6111,13 +6114,14 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty if not process_handler: continue for i in inspect.signature(handler['function']).parameters: params.append(i) + result = None if len(params) == 1: - handler['function'](message) + result = handler['function'](message) elif "data" in params: if len(params) == 2: - handler['function'](message, data) + result = handler['function'](message, data) elif len(params) == 3: - handler['function'](message, data=data, bot=self) + result = handler['function'](message, data=data, bot=self) else: logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return @@ -6132,8 +6136,9 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty if len(data_copy) > len(params) - 1: # remove the message parameter logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) return - handler["function"](message, **data_copy) - break + result = handler["function"](message, **data_copy) + if not isinstance(result, ContinueHandling): + break except Exception as e: handler_error = e if self.exception_handler: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0aa10974b..d2552b78d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -14,7 +14,7 @@ # storages from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage, StateStorageBase -from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate, SkipHandler, State +from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate, SkipHandler, State, ContinueHandling from inspect import signature @@ -493,16 +493,14 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up if not process_update: continue for i in signature(handler['function']).parameters: params.append(i) + result = None if len(params) == 1: - await handler['function'](message) - break + result = await handler['function'](message) elif "data" in params: if len(params) == 2: - await handler['function'](message, data) - break + result = await handler['function'](message, data) elif len(params) == 3: - await handler['function'](message, data=data, bot=self) - break + result = await handler['function'](message, data=data, bot=self) else: logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return @@ -517,7 +515,8 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up if len(data_copy) > len(params) - 1: # remove the message parameter logger.error("You are passing more data than the handler needs. Check your handler: {}".format(handler['function'])) return - await handler["function"](message, **data_copy) + result = await handler["function"](message, **data_copy) + if not isinstance(result, ContinueHandling): break except Exception as e: if self.exception_handler: diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 42c5804de..70ab67db2 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -174,7 +174,6 @@ def __init__(self) -> None: def __str__(self) -> str: return self.name - class StatesGroup: """ @@ -192,9 +191,6 @@ def __init_subclass__(cls) -> None: value.name = ':'.join((cls.__name__, name)) value.group = cls - - - class BaseMiddleware: """ @@ -254,8 +250,6 @@ class SkipHandler: but will skip execution of handler. """ - def __init__(self) -> None: - pass class CancelUpdate: """ @@ -266,5 +260,9 @@ class CancelUpdate: of post_process in middlewares. """ - def __init__(self) -> None: - pass \ No newline at end of file +class ContinueHandling: + """ + Class for continue updates in handlers. + Just return instance of this class + in handlers to continue process. + """ From 4798c26188a7f3c6f4609c0384b1afb1e6cac30b Mon Sep 17 00:00:00 2001 From: byehack Date: Sun, 2 Oct 2022 12:05:20 +0330 Subject: [PATCH 1134/1808] improve code quality --- telebot/__init__.py | 140 ++++++++++++++++++------------------ telebot/handler_backends.py | 8 +++ 2 files changed, 79 insertions(+), 69 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 72d244cb2..30c371690 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6080,82 +6080,84 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty for handler in handlers: if self._test_message_handler(handler, message): if handler.get('pass_bot', False): - handler['function'](message, bot = self) + result = handler['function'](message, bot=self) else: - handler['function'](message) - break - else: - data = {} - params =[] - handler_error = None - skip_handlers = False - - if middlewares: - for middleware in middlewares: - if middleware.update_sensitive: - if hasattr(middleware, f'pre_process_{update_type}'): - result = getattr(middleware, f'pre_process_{update_type}')(message, data) - else: - logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) - result = None - else: - result = middleware.pre_process(message, data) - # We will break this loop if CancelUpdate is returned - # Also, we will not run other middlewares - if isinstance(result, CancelUpdate): - return - elif isinstance(result, SkipHandler): - skip_handlers = True - - if handlers and not(skip_handlers): - try: - for handler in handlers: - process_handler = self._test_message_handler(handler, message) - if not process_handler: continue - for i in inspect.signature(handler['function']).parameters: - params.append(i) - result = None - if len(params) == 1: result = handler['function'](message) - elif "data" in params: - if len(params) == 2: - result = handler['function'](message, data) - elif len(params) == 3: - result = handler['function'](message, data=data, bot=self) - else: - logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) - return - else: - data_copy = data.copy() - for key in list(data_copy): - # remove data from data_copy if handler does not accept it - if key not in params: - del data_copy[key] - if handler.get('pass_bot'): - data_copy["bot"] = self - if len(data_copy) > len(params) - 1: # remove the message parameter - logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) - return - result = handler["function"](message, **data_copy) if not isinstance(result, ContinueHandling): break - except Exception as e: - handler_error = e - if self.exception_handler: - self.exception_handler.handle(e) + return + + data = {} + params =[] + handler_error = None + skip_handlers = False + + if middlewares: + for middleware in middlewares: + if middleware.update_sensitive: + if hasattr(middleware, f'pre_process_{update_type}'): + result = getattr(middleware, f'pre_process_{update_type}')(message, data) else: - logger.error(str(e)) - logger.debug("Exception traceback:\n%s", traceback.format_exc()) - - if middlewares: - for middleware in middlewares: - if middleware.update_sensitive: - if hasattr(middleware, f'post_process_{update_type}'): - getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) + logger.error('Middleware {} does not have pre_process_{} method. pre_process function execution was skipped.'.format(middleware.__class__.__name__, update_type)) + result = None + else: + result = middleware.pre_process(message, data) + # We will break this loop if CancelUpdate is returned + # Also, we will not run other middlewares + if isinstance(result, CancelUpdate): + return + elif isinstance(result, SkipHandler): + skip_handlers = True + + if handlers and not skip_handlers: + try: + for handler in handlers: + process_handler = self._test_message_handler(handler, message) + if not process_handler: continue + for i in inspect.signature(handler['function']).parameters: + params.append(i) + result = None + if len(params) == 1: + result = handler['function'](message) + elif "data" in params: + if len(params) == 2: + result = handler['function'](message, data) + elif len(params) == 3: + result = handler['function'](message, data=data, bot=self) else: - logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) + logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) + return else: - middleware.post_process(message, data, handler_error) + data_copy = data.copy() + for key in list(data_copy): + # remove data from data_copy if handler does not accept it + if key not in params: + del data_copy[key] + if handler.get('pass_bot'): + data_copy["bot"] = self + if len(data_copy) > len(params) - 1: # remove the message parameter + logger.error("You are passing more parameters than the handler needs. Check your handler: {}".format(handler['function'])) + return + result = handler["function"](message, **data_copy) + if not isinstance(result, ContinueHandling): + break + except Exception as e: + handler_error = e + if self.exception_handler: + self.exception_handler.handle(e) + else: + logger.error(str(e)) + logger.debug("Exception traceback:\n%s", traceback.format_exc()) + + if middlewares: + for middleware in middlewares: + if middleware.update_sensitive: + if hasattr(middleware, f'post_process_{update_type}'): + getattr(middleware, f'post_process_{update_type}')(message, data, handler_error) + else: + logger.error("Middleware: {} does not have post_process_{} method. Post process function was not executed.".format(middleware.__class__.__name__, update_type)) + else: + middleware.post_process(message, data, handler_error) def _notify_command_handlers(self, handlers, new_messages, update_type): """ diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 70ab67db2..b4acc86c7 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -249,6 +249,8 @@ class SkipHandler: Update will go to post_process, but will skip execution of handler. """ + def __init__(self) -> None: + pass class CancelUpdate: @@ -259,6 +261,9 @@ class CancelUpdate: Update will skip handler and execution of post_process in middlewares. """ + def __init__(self) -> None: + pass + class ContinueHandling: """ @@ -266,3 +271,6 @@ class ContinueHandling: Just return instance of this class in handlers to continue process. """ + def __init__(self) -> None: + pass + From c541533762eaa8831fee71cebe11c951b8fecd94 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 7 Oct 2022 21:50:51 +0400 Subject: [PATCH 1135/1808] Update __init__.py --- telebot/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index de1b7127e..51b8d9f2f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -12,6 +12,7 @@ # these imports are used to avoid circular import error import telebot.util import telebot.types +import telebot.formatting # storage from telebot.storage import StatePickleStorage, StateMemoryStorage, StateStorageBase From 2d7170feee580807fdc1d6c008a5374b179b4e84 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 8 Oct 2022 22:35:22 +0400 Subject: [PATCH 1136/1808] Added warning for non_stop=False --- telebot/async_telebot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0aa10974b..c8242607d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -390,6 +390,10 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: """ + if not non_stop: + # show warning + logger.warning("Important: Set non_stop=True for production so that polling won't crash during errors.") + self._user = await self.get_me() logger.info('Starting your bot with username: [@%s]', self.user.username) From b3953d6249ac320254bf489110194da718879e29 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 8 Oct 2022 23:03:04 +0400 Subject: [PATCH 1137/1808] =?UTF-8?q?=E2=84=B9=EF=B8=8F=20Better=20descrip?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index c8242607d..6a37ff892 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -392,7 +392,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if not non_stop: # show warning - logger.warning("Important: Set non_stop=True for production so that polling won't crash during errors.") + logger.warning("Setting non_stop to False will stop polling on API and system exceptions.") self._user = await self.get_me() From 620b1364a6778744f4b58ad929b5d2de0a137888 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 8 Oct 2022 23:41:41 +0300 Subject: [PATCH 1138/1808] Check CUSTOM_REQUEST_SENDER before RETRY_xxx. --- telebot/apihelper.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b4f2b8ce2..5f8d62eb2 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -111,7 +111,13 @@ def _make_request(token, method_name, method='get', params=None, files=None): params = params or None # Set params to None if empty result = None - if RETRY_ON_ERROR and RETRY_ENGINE == 1: + + if CUSTOM_REQUEST_SENDER: + # noinspection PyCallingNonCallable + result = CUSTOM_REQUEST_SENDER( + method, request_url, params=params, files=files, + timeout=(connect_timeout, read_timeout), proxies=proxy) + elif RETRY_ON_ERROR and RETRY_ENGINE == 1: got_result = False current_try = 0 while not got_result and current_try Date: Sun, 9 Oct 2022 02:28:05 +0330 Subject: [PATCH 1139/1808] ContinueHandling on asyncio_handler_backends --- telebot/asyncio_handler_backends.py | 15 ++++++++++++--- telebot/handler_backends.py | 1 - 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 34cc19a77..a67a2a476 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -3,8 +3,6 @@ """ - - class BaseMiddleware: """ Base class for middleware. @@ -96,6 +94,7 @@ class SkipHandler: def __init__(self) -> None: pass + class CancelUpdate: """ Class for canceling updates. @@ -106,4 +105,14 @@ class CancelUpdate: """ def __init__(self) -> None: - pass \ No newline at end of file + pass + + +class ContinueHandling: + """ + Class for continue updates in handlers. + Just return instance of this class + in handlers to continue process. + """ + def __init__(self) -> None: + pass diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index b4acc86c7..ca4cc3574 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -273,4 +273,3 @@ class ContinueHandling: """ def __init__(self) -> None: pass - From 982e642c738f78fef1094ae22460f6a9e12f5adb Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 11 Oct 2022 19:05:55 +0400 Subject: [PATCH 1140/1808] Update telebot/handler_backends.py --- telebot/handler_backends.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index ca4cc3574..be2714c5c 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -270,6 +270,19 @@ class ContinueHandling: Class for continue updates in handlers. Just return instance of this class in handlers to continue process. + + .. code-block:: python3 + :caption: Example of using ContinueHandling + + @bot.message_handler(commands=['start']) + def start(message): + bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + + @bot.message_handler(commands=['start']) + def start2(message): + bot.send_message(message.chat.id, 'Hello World2!') + """ def __init__(self) -> None: pass From 0fecf4620194208ca3ce488b290e184af8be40ab Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 11 Oct 2022 19:09:59 +0400 Subject: [PATCH 1141/1808] Create continue_handling.py --- examples/continue_handling.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 examples/continue_handling.py diff --git a/examples/continue_handling.py b/examples/continue_handling.py new file mode 100644 index 000000000..68e26f137 --- /dev/null +++ b/examples/continue_handling.py @@ -0,0 +1,23 @@ +from telebot import TeleBot +from telebot.handler_backends import ContinueHandling + + +bot = TeleBot('TOKEN') + +@bot.message_handler(commands=['start']) +def start(message): + bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + +@bot.message_handler(commands=['start']) +def start2(message): + """ + This handler comes after the first one, but it will never be called. + But you can call it by returning ContinueHandling() in the first handler. + + If you return ContinueHandling() in the first handler, the next + registered handler with appropriate filters will be called. + """ + bot.send_message(message.chat.id, 'Hello World2!') + +bot.infinity_polling() From 5d16b8bd4a80b83998a2ad9371853dad149b4355 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 11 Oct 2022 19:13:10 +0400 Subject: [PATCH 1142/1808] Create continue_handling.py --- .../asynchronous_telebot/continue_handling.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/asynchronous_telebot/continue_handling.py diff --git a/examples/asynchronous_telebot/continue_handling.py b/examples/asynchronous_telebot/continue_handling.py new file mode 100644 index 000000000..4781cc45f --- /dev/null +++ b/examples/asynchronous_telebot/continue_handling.py @@ -0,0 +1,27 @@ +from telebot.async_telebot import AsyncTeleBot +from telebot.asyncio_handler_backends import ContinueHandling + + +bot = AsyncTeleBot('TOKEN') + +@bot.message_handler(commands=['start']) +async def start(message): + await bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + +@bot.message_handler(commands=['start']) +async def start2(message): + """ + This handler comes after the first one, but it will never be called. + But you can call it by returning ContinueHandling() in the first handler. + + If you return ContinueHandling() in the first handler, the next + registered handler with appropriate filters will be called. + """ + await bot.send_message(message.chat.id, 'Hello World2!') + +import asyncio +asyncio.run(bot.polling()) # just a reminder that infinity polling +# wraps polling into try/except block just as sync version, +# but you can use any of them because neither of them stops if you +# pass non_stop=True From 81f090cce6e163c69e53a70a7eab84d87fb6374a Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 11 Oct 2022 19:15:01 +0400 Subject: [PATCH 1143/1808] Update asyncio_handler_backends.py --- telebot/asyncio_handler_backends.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index a67a2a476..6a1f2cacb 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -113,6 +113,19 @@ class ContinueHandling: Class for continue updates in handlers. Just return instance of this class in handlers to continue process. + + .. code-block:: python3 + :caption: Example of using ContinueHandling + + @bot.message_handler(commands=['start']) + def start(message): + bot.send_message(message.chat.id, 'Hello World!') + return ContinueHandling() + + @bot.message_handler(commands=['start']) + def start2(message): + bot.send_message(message.chat.id, 'Hello World2!') + """ def __init__(self) -> None: pass From c45af810f98f9454ee5c03692ddd9c9c4f77890a Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 11 Oct 2022 19:15:38 +0400 Subject: [PATCH 1144/1808] Updated docstrings for ContinueHandling --- telebot/asyncio_handler_backends.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 6a1f2cacb..96e13ee53 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -118,13 +118,13 @@ class ContinueHandling: :caption: Example of using ContinueHandling @bot.message_handler(commands=['start']) - def start(message): - bot.send_message(message.chat.id, 'Hello World!') + async def start(message): + await bot.send_message(message.chat.id, 'Hello World!') return ContinueHandling() @bot.message_handler(commands=['start']) - def start2(message): - bot.send_message(message.chat.id, 'Hello World2!') + async def start2(message): + await bot.send_message(message.chat.id, 'Hello World2!') """ def __init__(self) -> None: From 623d8b27ec41ab812099e5459be792979e336c90 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 21 Oct 2022 10:18:47 +0300 Subject: [PATCH 1145/1808] Bump version to 4.7.1 --- docs/source/conf.py | 2 +- examples/asynchronous_telebot/detect_changes.py | 2 +- examples/detect_changes.py | 2 +- telebot/version.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ae52f756d..6a389091e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.7.0' +release = '4.7.1' # -- General configuration --------------------------------------------------- diff --git a/examples/asynchronous_telebot/detect_changes.py b/examples/asynchronous_telebot/detect_changes.py index 0a0e14252..7fb202bd4 100644 --- a/examples/asynchronous_telebot/detect_changes.py +++ b/examples/asynchronous_telebot/detect_changes.py @@ -24,5 +24,5 @@ async def echo_message(message): import asyncio -# only new versions(4.7.0+) +# only for versions 4.7.0+ asyncio.run(bot.polling(restart_on_change=True)) diff --git a/examples/detect_changes.py b/examples/detect_changes.py index da2a2c842..58eeb8f2d 100644 --- a/examples/detect_changes.py +++ b/examples/detect_changes.py @@ -24,5 +24,5 @@ def send_welcome(message): def echo_message(message): bot.reply_to(message, message.text) -# only versions greater than 4.7.0 +# only for versions 4.7.0+ bot.infinity_polling(restart_on_change=True) diff --git a/telebot/version.py b/telebot/version.py index a79bcc2c5..794c97f8d 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.7.0' +__version__ = '4.7.1' From 572f103db7c8f3a7e1408379e37814ba4620d2b0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 22 Oct 2022 21:48:29 +0400 Subject: [PATCH 1146/1808] Extended exception handler behaviour for async --- telebot/async_telebot.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3a9e142ab..d18bce9e8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -421,17 +421,30 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: continue else: return - except asyncio_helper.ApiTelegramException as e: - logger.error(str(e)) - if non_stop: + except asyncio_helper.ApiException as e: + handled = False + if self.exception_handler: + self.exception_handler.handle(e) + handled = True + + if not handled: + logger.debug(traceback.format_exc()) + + if non_stop or handled: continue else: - break + raise e except Exception as e: - logger.error('Cause exception while getting updates.') - if non_stop: - logger.error(str(e)) - await asyncio.sleep(3) + handled = False + if self.exception_handler: + self.exception_handler.handle(e) + handled = True + + if not handled: + logger.error('Cause exception while getting updates. Enable debug logging for more info.') + logger.debug(traceback.format_exc()) + + if non_stop or handled: continue else: raise e From d11b9802da0db44d3a00a41b21eaa88d57778c57 Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Mon, 24 Oct 2022 16:02:59 +0200 Subject: [PATCH 1147/1808] Fixed InlineQueryResultVideo params in example. Fixed InlineQueryResultVideo params in example. String "Video" was passed in stead of the thumbnail url, making the example not work, This has been tested working. --- examples/inline_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/inline_example.py b/examples/inline_example.py index b0dc10671..be927c82a 100644 --- a/examples/inline_example.py +++ b/examples/inline_example.py @@ -42,7 +42,7 @@ def query_video(inline_query): try: r = types.InlineQueryResultVideo('1', 'https://github.com/eternnoir/pyTelegramBotAPI/blob/master/tests/test_data/test_video.mp4?raw=true', - 'video/mp4', 'Video', + 'video/mp4', 'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg', 'Title' ) From 1d8dc78c872fac401d5a39ea00cbacfa8c298603 Mon Sep 17 00:00:00 2001 From: Yan Khachko Date: Sat, 29 Oct 2022 01:18:52 +0300 Subject: [PATCH 1148/1808] Added example of running serverless on AWS Lambda Example of using of PyTelegramBotAPI in Amazon AWS Lambda --- examples/serverless/lambda_function.py | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 examples/serverless/lambda_function.py diff --git a/examples/serverless/lambda_function.py b/examples/serverless/lambda_function.py new file mode 100644 index 000000000..e9679846c --- /dev/null +++ b/examples/serverless/lambda_function.py @@ -0,0 +1,50 @@ +""" +Example of running PyTelegramBotAPI serverless in Amazon AWS Lambdaю +You have to set your lambda's url as telegram webhook manually https://core.telegram.org/bots/api#setwebhook +""" + +import logging + +import telebot +import json +import os + +API_TOKEN = os.environ['TELEGRAM_TOKEN'] + + +logger = telebot.logger +telebot.logger.setLevel(logging.INFO) + +bot = telebot.TeleBot(API_TOKEN, threaded=False) + + +def process_event(event): + # Get telegram webhook json from event + request_body_dict = json.loads(event['body']) + # Parse updates from json + update = telebot.types.Update.de_json(request_body_dict) + # Run handlers and etc for updates + bot.process_new_updates([update]) + + +def lambda_handler(event, context): + # Process event from aws and respond + process_event(event) + return { + 'statusCode': 200 + } + + +# Handle '/start' and '/help' +@bot.message_handler(commands=['help', 'start']) +def send_welcome(message): + bot.reply_to(message, + ("Hi there, I am EchoBot.\n" + "I am here to echo your kind words back to you.")) + + +# Handle all other messages +@bot.message_handler(func=lambda message: True, content_types=['text']) +def echo_message(message): + bot.reply_to(message, message.text) + From 507d53efbde257d5b50bec69d69c34c03f3b0c75 Mon Sep 17 00:00:00 2001 From: Yan Khachko Date: Sat, 29 Oct 2022 11:57:53 +0300 Subject: [PATCH 1149/1808] Rename lambda_function.py to aws_lambda_function.py --- .../serverless/{lambda_function.py => aws_lambda_function.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/serverless/{lambda_function.py => aws_lambda_function.py} (100%) diff --git a/examples/serverless/lambda_function.py b/examples/serverless/aws_lambda_function.py similarity index 100% rename from examples/serverless/lambda_function.py rename to examples/serverless/aws_lambda_function.py From 92ecfdec48900c80be7c8b5de5b9c8e78b713c8a Mon Sep 17 00:00:00 2001 From: batmanscode <29989939+batmanscode@users.noreply.github.com> Date: Sun, 30 Oct 2022 17:21:53 +0000 Subject: [PATCH 1150/1808] fixes: `debug` removed from uvicorn #1767 --- telebot/__init__.py | 8 ++------ telebot/ext/aio/webhooks.py | 6 ------ telebot/ext/sync/webhooks.py | 6 ------ 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5b697b007..46e086c10 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -451,8 +451,7 @@ def run_webhooks(self, drop_pending_updates: Optional[bool] = None, timeout: Optional[int]=None, secret_token: Optional[str]=None, - secret_token_length: Optional[int]=20, - debug: Optional[bool]=False): + secret_token_length: Optional[int]=20,): """ This class sets webhooks and listens to a given url and port. @@ -501,9 +500,6 @@ def run_webhooks(self, :param secret_token_length: Length of a secret token, defaults to 20 :type secret_token_length: :obj:`int`, optional - :param debug: set True if you want to set debugging on for webserver, defaults to False - :type debug: :obj:`bool`, optional - :raises ImportError: If necessary libraries were not installed. """ @@ -544,7 +540,7 @@ def run_webhooks(self, from telebot.ext.sync import SyncWebhookListener except (NameError, ImportError): raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") - self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) + self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path) self.webhook_listener.run_app() diff --git a/telebot/ext/aio/webhooks.py b/telebot/ext/aio/webhooks.py index 03c456616..56af59276 100644 --- a/telebot/ext/aio/webhooks.py +++ b/telebot/ext/aio/webhooks.py @@ -29,7 +29,6 @@ def __init__(self, bot, port: Optional[int]=443, ssl_context: Optional[tuple]=None, url_path: Optional[str]=None, - debug: Optional[bool]=False ) -> None: """ Aynchronous implementation of webhook listener @@ -55,9 +54,6 @@ def __init__(self, bot, :param url_path: Webhook url path :type url_path: str - :param debug: Debug mode - :type debug: bool - :raises ImportError: If FastAPI or uvicorn is not installed. :raises ImportError: If Starlette version is too old. @@ -72,7 +68,6 @@ def __init__(self, bot, self._host = host self._ssl_context = ssl_context self._url_path = url_path - self._debug = debug self._prepare_endpoint_urls() @@ -119,7 +114,6 @@ async def run_app(self): config = Config(app=self.app, host=self._host, port=self._port, - debug=self._debug, ssl_certfile=self._ssl_context[0], ssl_keyfile=self._ssl_context[1] ) diff --git a/telebot/ext/sync/webhooks.py b/telebot/ext/sync/webhooks.py index a73c16c4c..e15e8c8a8 100644 --- a/telebot/ext/sync/webhooks.py +++ b/telebot/ext/sync/webhooks.py @@ -25,7 +25,6 @@ def __init__(self, bot, port: Optional[int]=443, ssl_context: Optional[tuple]=None, url_path: Optional[str]=None, - debug: Optional[bool]=False ) -> None: """ Synchronous implementation of webhook listener @@ -51,9 +50,6 @@ def __init__(self, bot, :param url_path: Webhook url path :type url_path: str - :param debug: Debug mode - :type debug: bool - :raises ImportError: If FastAPI or uvicorn is not installed. :raises ImportError: If Starlette version is too old. @@ -68,7 +64,6 @@ def __init__(self, bot, self._host = host self._ssl_context = ssl_context self._url_path = url_path - self._debug = debug self._prepare_endpoint_urls() @@ -115,7 +110,6 @@ def run_app(self): uvicorn.run(app=self.app, host=self._host, port=self._port, - debug=self._debug, ssl_certfile=self._ssl_context[0], ssl_keyfile=self._ssl_context[1] ) From 81c8ee5820a81a91b48364f8f3c96011987c4931 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 5 Nov 2022 22:20:11 +0400 Subject: [PATCH 1151/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 903718e2c..5fbd99ac5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.2! +##

Supported Bot API version: 6.3!

Official documentation

From f1f18c6df2cbbac9f45b6ae0bf59f92da05aa21c Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 5 Nov 2022 22:49:07 +0400 Subject: [PATCH 1152/1808] Fix error description --- telebot/async_telebot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d18bce9e8..f93a556f0 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -428,12 +428,13 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: handled = True if not handled: + logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) logger.debug(traceback.format_exc()) if non_stop or handled: continue else: - raise e + break except Exception as e: handled = False if self.exception_handler: @@ -441,13 +442,13 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: handled = True if not handled: - logger.error('Cause exception while getting updates. Enable debug logging for more info.') + logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) logger.debug(traceback.format_exc()) if non_stop or handled: continue else: - raise e + break finally: self._polling = False await self.close_session() From 070479f7af2e91ecb256216e214845dcf7fa9237 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 5 Nov 2022 23:06:28 +0400 Subject: [PATCH 1153/1808] Update async_telebot.py --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f93a556f0..a44155c27 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -354,7 +354,7 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option while self._polling: try: - await self._process_polling(non_stop=False, timeout=timeout, request_timeout=request_timeout, + await self._process_polling(non_stop=True, timeout=timeout, request_timeout=request_timeout, allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: From 77738b2537cfa99e86fa7d65f0f42d761771f1d7 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 5 Nov 2022 23:14:37 +0400 Subject: [PATCH 1154/1808] Added is_forum --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 0f7f53f67..badc3c876 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -515,6 +515,9 @@ class Chat(JsonDeserializable): :param last_name: Optional. Last name of the other party in a private chat :type last_name: :obj:`str` + :param is_forum: Optional. True, if the supergroup chat is a forum (has topics enabled) + :type is_forum: :obj:`bool` + :param photo: Optional. Chat photo. Returned only in getChat. :type photo: :class:`telebot.types.ChatPhoto` @@ -603,13 +606,15 @@ def __init__(self, id, type, title=None, username=None, first_name=None, permissions=None, slow_mode_delay=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, **kwargs): + join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None, + is_forum=None, ,**kwargs): self.id: int = id self.type: str = type self.title: str = title self.username: str = username self.first_name: str = first_name self.last_name: str = last_name + self.is_forum: bool = is_forum self.photo: ChatPhoto = photo self.bio: str = bio self.join_to_send_messages: bool = join_to_send_messages From 76f06cacfe642d856ca6c52129eabbf8e45f4bb9 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 5 Nov 2022 23:15:10 +0400 Subject: [PATCH 1155/1808] Fix typo --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index badc3c876..fbe8855ef 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -607,7 +607,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, ,**kwargs): + is_forum=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title From 475394d2410145c8b55e743aeada8c1c3cf61ed9 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 5 Nov 2022 23:23:00 +0400 Subject: [PATCH 1156/1808] Added message_thread_id & is_topic_message --- telebot/types.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index fbe8855ef..f9f4f4978 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -695,6 +695,9 @@ class Message(JsonDeserializable): :param message_id: Unique message identifier inside this chat :type message_id: :obj:`int` + :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups only + :type message_thread_id: :obj:`int` + :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. :type from_user: :class:`telebot.types.User` @@ -733,6 +736,9 @@ class Message(JsonDeserializable): :param forward_date: Optional. For forwarded messages, date the original message was sent in Unix time :type forward_date: :obj:`int` + :param is_topic_message: Optional. True, if the message is sent to a forum topic + :type is_topic_message: :obj:`bool` + :param is_automatic_forward: Optional. :obj:`bool`, if the message is a channel post that was automatically forwarded to the connected discussion group :type is_automatic_forward: :obj:`bool` @@ -1157,6 +1163,8 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.successful_payment: Optional[SuccessfulPayment] = None self.connected_website: Optional[str] = None self.reply_markup: Optional[InlineKeyboardMarkup] = None + self.message_thread_id: Optional[int] = None + self.is_topic_message: Optional[bool] = None for key in options: setattr(self, key, options[key]) self.json = json_string From f288470b43ea5106b4afcc1b2beefd88e2ea9563 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 01:04:33 +0400 Subject: [PATCH 1157/1808] Added the classes ForumTopicCreated, ForumTopicClosed, and ForumTopicReopened and the fields forum_topic_created, forum_topic_closed, and forum_topic_reopened to the class Message. Note that service messages about forum topic creation can't be deleted with the deleteMessage method. Added the classes ForumTopicCreated, ForumTopicClosed, and ForumTopicReopened and the fields forum_topic_created, forum_topic_closed, and forum_topic_reopened to the class Message. Note that service messages about forum topic creation can't be deleted with the deleteMessage method. --- telebot/types.py | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index f9f4f4978..3e4fd42ba 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -889,6 +889,15 @@ class Message(JsonDeserializable): proximity alert while sharing Live Location. :type proximity_alert_triggered: :class:`telebot.types.ProximityAlertTriggered` + :param forum_topic_created: Optional. Service message: forum topic created + :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` + + :param forum_topic_closed: Optional. Service message: forum topic closed + :type forum_topic_closed: :class:`telebot.types.ForumTopicClosed` + + :param forum_topic_reopened: Optional. Service message: forum topic reopened + :type forum_topic_reopened: :class:`telebot.types.ForumTopicReopened` + :param video_chat_scheduled: Optional. Service message: video chat scheduled :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` @@ -1078,6 +1087,15 @@ 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 'forum_topic_created' in obj: + opts['forum_topic_created'] = ForumTopicCreated.de_json(obj['forum_topic_created']) + content_type = 'forum_topic_created' + if 'forum_topic_closed' in obj: + opts['forum_topic_closed'] = ForumTopicClosed.de_json(obj['forum_topic_closed']) + content_type = 'forum_topic_closed' + if 'forum_topic_reopened' in obj: + opts['forum_topic_reopened'] = ForumTopicReopened.de_json(obj['forum_topic_reopened']) + content_type = 'forum_topic_reopened' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1165,6 +1183,9 @@ 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.forum_topic_created: Optional[ForumTopicCreated] = None + self.forum_topic_closed: Optional[ForumTopicClosed] = None + self.forum_topic_reopened: Optional[ForumTopicReopened] = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -6716,3 +6737,56 @@ def file(self): File object. """ return self._file + + +class ForumTopicCreated(JsonDeserializable): + """ + This object represents a service message about a new forum topic created in the chat. + + + :param name: Name of the topic + :type name: :obj:`str` + + :param icon_color: Color of the topic icon in RGB format + :type icon_color: :obj:`int` + + :param icon_custom_emoji_id: Optional. Unique identifier of the custom emoji shown as the topic icon + :type icon_custom_emoji_id: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ForumTopicCreated` + """ + def de_json(self, json_string): + if json_string is None: return None + obj = self.check_json(json_string) + return self(**obj) + + def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]) -> None: + self.name: str = name + self.icon_color: int = icon_color + self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id + + +class ForumTopicClosed(JsonDeserializable): + """This object represents a service message about a forum topic closed in the chat. Currently holds no information.""" + # for future use + def de_json(self, json_string): + return self() + + def __init__(self) -> None: + pass + + +class ForumTopicReopened(JsonDeserializable): + """This object represents a service message about a forum topic reopened in the chat. Currently holds no information.""" + # for future use + def de_json(self, json_string): + return self() + + def __init__(self) -> None: + pass + + + + + From 566aef1679328b93edd00ca685b5b2d38c5bfbbd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 14:49:13 +0400 Subject: [PATCH 1158/1808] Fix wrong typehint for previous commit --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 3e4fd42ba..8bbcaf0ae 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6761,7 +6761,7 @@ def de_json(self, json_string): obj = self.check_json(json_string) return self(**obj) - def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]) -> None: + def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None) -> None: self.name: str = name self.icon_color: int = icon_color self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id From 4e2ea90db394825be18c79689e949caef73808be Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 15:00:20 +0400 Subject: [PATCH 1159/1808] Added field can_manage_topics Added the field can_manage_topics to the classes ChatAdministratorRights, ChatPermissions, ChatMemberAdministrator, and ChatMemberRestricted. --- telebot/types.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 8bbcaf0ae..96154d6e6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2597,7 +2597,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, can_manage_chat=None, can_manage_video_chats=None, - until_date=None, **kwargs): + until_date=None, can_manage_topics=None, **kwargs): self.user: User = user self.status: str = status self.custom_title: str = custom_title @@ -2621,6 +2621,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_manage_video_chats: bool = can_manage_video_chats self.can_manage_voice_chats: bool = self.can_manage_video_chats # deprecated, for backward compatibility self.until_date: int = until_date + self.can_manage_topics: bool = can_manage_topics class ChatMemberOwner(ChatMember): @@ -2700,6 +2701,10 @@ class ChatMemberAdministrator(ChatMember): :param can_pin_messages: Optional. True, if the user is allowed to pin messages; groups and supergroups only :type can_pin_messages: :obj:`bool` + :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; + supergroups only + :type can_manage_topics: :obj:`bool` + :param custom_title: Optional. Custom title for this user :type custom_title: :obj:`str` @@ -2751,6 +2756,9 @@ class ChatMemberRestricted(ChatMember): :param can_pin_messages: True, if the user is allowed to pin messages :type can_pin_messages: :obj:`bool` + :param can_manage_topics: True, if the user is allowed to create forum topics + :type can_manage_topics: :obj:`bool` + :param can_send_messages: True, if the user is allowed to send text messages, contacts, locations and venues :type can_send_messages: :obj:`bool` @@ -2853,6 +2861,10 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): :param can_pin_messages: Optional. True, if the user is allowed to pin messages. Ignored in public supergroups :type can_pin_messages: :obj:`bool` + :param can_manage_topics: Optional. True, if the user is allowed to create forum topics. If omitted defaults to the + value of can_pin_messages + :type can_manage_topics: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ChatPermissions` """ @@ -2865,7 +2877,8 @@ def de_json(cls, json_string): def __init__(self, can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, **kwargs): + can_invite_users=None, can_pin_messages=None, + can_manage_topics=None, **kwargs): self.can_send_messages: bool = can_send_messages self.can_send_media_messages: bool = can_send_media_messages self.can_send_polls: bool = can_send_polls @@ -2874,6 +2887,7 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None, self.can_change_info: bool = can_change_info self.can_invite_users: bool = can_invite_users self.can_pin_messages: bool = can_pin_messages + self.can_manage_topics: bool = can_manage_topics def to_json(self): return json.dumps(self.to_dict()) @@ -2896,6 +2910,9 @@ def to_dict(self): json_dict['can_invite_users'] = self.can_invite_users if self.can_pin_messages is not None: json_dict['can_pin_messages'] = self.can_pin_messages + if self.can_manage_topics is not None: + json_dict['can_manage_topics'] = self.can_manage_topics + return json_dict @@ -6626,6 +6643,9 @@ class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryab :param can_pin_messages: Optional. True, if the user is allowed to pin messages; groups and supergroups only :type can_pin_messages: :obj:`bool` + :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only + :type can_manage_topics: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ChatAdministratorRights` """ @@ -6640,7 +6660,7 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, can_delete_messages: bool, can_manage_video_chats: bool, can_restrict_members: bool, can_promote_members: bool, can_change_info: bool, can_invite_users: bool, can_post_messages: bool=None, can_edit_messages: bool=None, - can_pin_messages: bool=None) -> None: + can_pin_messages: bool=None, can_manage_topics: bool=None) -> None: self.is_anonymous: bool = is_anonymous self.can_manage_chat: bool = can_manage_chat @@ -6653,6 +6673,7 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, self.can_post_messages: bool = can_post_messages self.can_edit_messages: bool = can_edit_messages self.can_pin_messages: bool = can_pin_messages + self.can_manage_topics: bool = can_manage_topics def to_dict(self): json_dict = { @@ -6671,6 +6692,8 @@ def to_dict(self): json_dict['can_edit_messages'] = self.can_edit_messages if self.can_pin_messages is not None: json_dict['can_pin_messages'] = self.can_pin_messages + if self.can_manage_topics is not None: + json_dict['can_manage_topics'] = self.can_manage_topics return json_dict def to_json(self): From 7958d0dca74894234066d1afc3cb7424883422de Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 15:04:41 +0400 Subject: [PATCH 1160/1808] Added can_manage_topics for promotechatmember Added the parameter can_manage_topics to the method promoteChatMember. --- telebot/__init__.py | 9 +++++++-- telebot/apihelper.py | 5 ++++- telebot/async_telebot.py | 9 +++++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 46e086c10..e28f56321 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2910,7 +2910,8 @@ def promote_chat_member( is_anonymous: Optional[bool]=None, can_manage_chat: Optional[bool]=None, can_manage_video_chats: Optional[bool]=None, - can_manage_voice_chats: Optional[bool]=None) -> bool: + can_manage_voice_chats: Optional[bool]=None, + can_manage_topics: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -2967,6 +2968,10 @@ def promote_chat_member( :param can_manage_voice_chats: Deprecated, use can_manage_video_chats. :type can_manage_voice_chats: :obj:`bool` + :param can_manage_topics: Pass True if the user is allowed to create, rename, close, + and reopen forum topics, supergroups only + :type can_manage_topics: :obj:`bool` + :return: True on success. :rtype: :obj:`bool` """ @@ -2979,7 +2984,7 @@ def promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_video_chats) + is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics) def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5f8d62eb2..b5009de15 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -977,7 +977,8 @@ def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, - is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None): + is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None, + can_manage_topics=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -1002,6 +1003,8 @@ def promote_chat_member( payload['can_manage_chat'] = can_manage_chat if can_manage_video_chats is not None: payload['can_manage_video_chats'] = can_manage_video_chats + if can_manage_topics is not None: + payload['can_manage_topics'] = can_manage_topics return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3a9e142ab..40e3a88a6 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3750,7 +3750,8 @@ async def promote_chat_member( is_anonymous: Optional[bool]=None, can_manage_chat: Optional[bool]=None, can_manage_video_chats: Optional[bool]=None, - can_manage_voice_chats: Optional[bool]=None) -> bool: + can_manage_voice_chats: Optional[bool]=None, + can_manage_topics: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -3807,6 +3808,10 @@ async def promote_chat_member( :param can_manage_voice_chats: Deprecated, use can_manage_video_chats. :type can_manage_voice_chats: :obj:`bool` + :param can_manage_topics: Pass True if the user is allowed to create, rename, close, + and reopen forum topics, supergroups only + :type can_manage_topics: :obj:`bool` + :return: True on success. :rtype: :obj:`bool` """ @@ -3820,7 +3825,7 @@ async def promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_video_chats) + is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics) async def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 7e8958a4b..3389b334b 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -968,7 +968,7 @@ async def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, - is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None): + is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None, can_manage_topics=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -993,6 +993,8 @@ async def promote_chat_member( payload['can_manage_chat'] = can_manage_chat if can_manage_video_chats is not None: payload['can_manage_video_chats'] = can_manage_video_chats + if can_manage_topics is not None: + payload['can_manage_topics'] = can_manage_topics return await _process_request(token, method_url, params=payload, method='post') From 876d679765812f8f315bd64dbbe299c361c747e7 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 15:14:19 +0400 Subject: [PATCH 1161/1808] Added ForumTopic class and fixed previous classes by fixing de_json method. --- telebot/types.py | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 96154d6e6..98fae8d9b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6779,10 +6779,11 @@ class ForumTopicCreated(JsonDeserializable): :return: Instance of the class :rtype: :class:`telebot.types.ForumTopicCreated` """ - def de_json(self, json_string): + @classmethod + def de_json(cls, json_string): if json_string is None: return None - obj = self.check_json(json_string) - return self(**obj) + obj = cls.check_json(json_string) + return cls(**obj) def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None) -> None: self.name: str = name @@ -6793,8 +6794,9 @@ def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[st class ForumTopicClosed(JsonDeserializable): """This object represents a service message about a forum topic closed in the chat. Currently holds no information.""" # for future use - def de_json(self, json_string): - return self() + @classmethod + def de_json(cls, json_string): + return cls() def __init__(self) -> None: pass @@ -6803,13 +6805,45 @@ def __init__(self) -> None: class ForumTopicReopened(JsonDeserializable): """This object represents a service message about a forum topic reopened in the chat. Currently holds no information.""" # for future use - def de_json(self, json_string): - return self() + @classmethod + def de_json(cls, json_string): + return cls() def __init__(self) -> None: pass +class ForumTopic(JsonDeserializable): + """ + This object represents a forum topic. + + :param message_thread_id: Unique identifier of the forum topic + :type message_thread_id: :obj:`int` + + :param name: Name of the topic + :type name: :obj:`str` + + :param icon_color: Color of the topic icon in RGB format + :type icon_color: :obj:`int` + + :param icon_custom_emoji_id: Optional. Unique identifier of the custom emoji shown as the topic icon + :type icon_custom_emoji_id: :obj:`str` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + def __init__(self, message_thread_id: int, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None) -> None: + self.message_thread_id: int = message_thread_id + self.name: str = name + self.icon_color: int = icon_color + self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id + + + From 4825624d48f64892c43e1028172cbca1690c6a67 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 15:43:16 +0400 Subject: [PATCH 1162/1808] Added docs to types, added new methods Added the methods createForumTopic, editForumTopic, closeForumTopic, reopenForumTopic, deleteForumTopic, unpinAllForumTopicMessages, and getForumTopicIconStickers for forum topic management. --- telebot/__init__.py | 146 +++++++++++++++++++++++++++++++++++++- telebot/apihelper.py | 37 ++++++++++ telebot/async_telebot.py | 143 +++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 39 ++++++++++ telebot/types.py | 18 ++++- 5 files changed, 380 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e28f56321..e1ec4a3cc 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4530,6 +4530,150 @@ def delete_sticker_from_set(self, sticker: str) -> bool: """ return apihelper.delete_sticker_from_set(self.token, sticker) + def create_forum_topic(self, + chat_id: int, name: str, icon_color: Optional[int]=None, + icon_custom_emoji_id: Optional[str]=None) -> types.ForumTopic: + """ + Use this method to create a topic in a forum supergroup chat. The bot must be an administrator + in the chat for this to work and must have the can_manage_topics administrator rights. + Returns information about the created topic as a ForumTopic object. + + Telegram documentation: https://core.telegram.org/bots/api#createforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param name: Name of the topic, 1-128 characters + :type name: :obj:`str` + + :param icon_color: Color of the topic icon in RGB format. Currently, must be one of 0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F + :type icon_color: :obj:`int` + + :param icon_custom_emoji_id: Custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long + :type icon_custom_emoji_id: :obj:`str` + + :return: On success, information about the created topic is returned as a ForumTopic object. + :rtype: :class:`telebot.types.ForumTopic` + """ + return apihelper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) + + def edit_forum_topic( + self, chat_id: Union[int, str], + message_thread_id: int, name: str, + icon_custom_emoji_id: str, + ) -> bool: + """ + Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an + administrator in the chat for this to work and must have can_manage_topics administrator rights, + unless it is the creator of the topic. Returns True on success. + + Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to edit + :type message_thread_id: :obj:`int` + + :param name: New name of the topic, 1-128 characters + :type name: :obj:`str` + + :param icon_custom_emoji_id: New custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long + :type icon_custom_emoji_id: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.edit_forum_topic(self.token, chat_id, message_thread_id, name, icon_custom_emoji_id) + + def close_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to close an open topic in a forum supergroup chat. The bot must be an administrator + in the chat for this to work and must have the can_manage_topics administrator rights, unless it is + the creator of the topic. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to close + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.close_forum_topic(self.token, chat_id, message_thread_id) + + def reopen_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to reopen a closed topic in a forum supergroup chat. The bot must be an administrator in the chat + for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#reopenforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to reopen + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.reopen_forum_topic(self.token, chat_id, message_thread_id) + + def delete_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to delete a topic in a forum supergroup chat. The bot must be an administrator in the chat for this + to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. Returns True + on success. + + Telegram documentation: https://core.telegram.org/bots/api#deleteforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to delete + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.delete_forum_topic(self.token, chat_id, message_thread_id) + + def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to clear the list of pinned messages in a forum topic. The bot must be an administrator in the + chat for this to work and must have the can_pin_messages administrator right in the supergroup. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unpinallforumtopicmessages + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.unpin_all_forum_topic_messages(self.token, chat_id, message_thread_id) + + def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: + """ + Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. + Requires no parameters. Returns an Array of Sticker objects. + + Telegram documentation: https://core.telegram.org/bots/api#getforumtopiciconstickers + + :return: On success, a list of StickerSet objects is returned. + :rtype: List[:class:`telebot.types.StickerSet`] + """ + return apihelper.get_forum_topic_icon_stickers(self.token) + def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryResultBase) -> types.SentWebAppMessage: """ Use this method to set the result of an interaction with a Web App and @@ -6009,7 +6153,7 @@ def add_custom_filter(self, custom_filter: Union[SimpleCustomFilter, AdvancedCus class TextMatchFilter(AdvancedCustomFilter): key = 'text' - async def check(self, message, text): + def check(self, message, text): return text == message.text :param custom_filter: Class with check(message) method. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b5009de15..6458f0411 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1732,6 +1732,43 @@ def send_poll( payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload) +def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_emoji_id=None): + method_url = r'createForumTopic' + payload = {'chat_id': chat_id, 'name': name} + if icon_color: + payload['icon_color'] = icon_color + if icon_custom_emoji_id: + payload['icon_custom_emoji_id'] = icon_custom_emoji_id + return _make_request(token, method_url, params=payload) + +def edit_forum_topic(token, chat_id, message_thread_id, name, icon_custom_emoji_id): + method_url = r'editForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id, 'name': name, 'icon_custom_emoji_id': icon_custom_emoji_id} + return _make_request(token, method_url, params=payload) + +def close_forum_topic(token, chat_id, message_thread_id): + method_url = r'closeForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return _make_request(token, method_url, params=payload) + +def reopen_forum_topic(token, chat_id, message_thread_id): + method_url = r'reopenForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return _make_request(token, method_url, params=payload) + +def delete_forum_topic(token, chat_id, message_thread_id): + method_url = r'deleteForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return _make_request(token, method_url, params=payload) + +def unpin_all_forum_topic_messages(token, chat_id, message_thread_id): + method_url = r'unpinAllForumTopicMessages' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return _make_request(token, method_url, params=payload) + +def get_forum_topic_icon_stickers(token): + method_url = r'getForumTopicIconStickers' + return _make_request(token, method_url) def stop_poll(token, chat_id, message_id, reply_markup=None): method_url = r'stopPoll' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 40e3a88a6..1851b8ee5 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5378,6 +5378,149 @@ async def delete_sticker_from_set(self, sticker: str) -> bool: """ return await asyncio_helper.delete_sticker_from_set(self.token, sticker) + async def create_forum_topic(self, + chat_id: int, name: str, icon_color: Optional[int]=None, + icon_custom_emoji_id: Optional[str]=None) -> types.ForumTopic: + """ + Use this method to create a topic in a forum supergroup chat. The bot must be an administrator + in the chat for this to work and must have the can_manage_topics administrator rights. + Returns information about the created topic as a ForumTopic object. + + Telegram documentation: https://core.telegram.org/bots/api#createforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param name: Name of the topic, 1-128 characters + :type name: :obj:`str` + + :param icon_color: Color of the topic icon in RGB format. Currently, must be one of 0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F + :type icon_color: :obj:`int` + + :param icon_custom_emoji_id: Custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long + :type icon_custom_emoji_id: :obj:`str` + + :return: On success, information about the created topic is returned as a ForumTopic object. + :rtype: :class:`telebot.types.ForumTopic` + """ + return await asyncio_helper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) + + async def edit_forum_topic( + self, chat_id: Union[int, str], + message_thread_id: int, name: str, + icon_custom_emoji_id: str, + ) -> bool: + """ + Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an + administrator in the chat for this to work and must have can_manage_topics administrator rights, + unless it is the creator of the topic. Returns True on success. + + Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to edit + :type message_thread_id: :obj:`int` + + :param name: New name of the topic, 1-128 characters + :type name: :obj:`str` + + :param icon_custom_emoji_id: New custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long + :type icon_custom_emoji_id: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.edit_forum_topic(self.token, chat_id, message_thread_id, name, icon_custom_emoji_id) + + async def close_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to close an open topic in a forum supergroup chat. The bot must be an administrator + in the chat for this to work and must have the can_manage_topics administrator rights, unless it is + the creator of the topic. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to close + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.close_forum_topic(self.token, chat_id, message_thread_id) + + async def reopen_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to reopen a closed topic in a forum supergroup chat. The bot must be an administrator in the chat + for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#reopenforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to reopen + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.reopen_forum_topic(self.token, chat_id, message_thread_id) + + async def delete_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to delete a topic in a forum supergroup chat. The bot must be an administrator in the chat for this + to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. Returns True + on success. + + Telegram documentation: https://core.telegram.org/bots/api#deleteforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic to delete + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.delete_forum_topic(self.token, chat_id, message_thread_id) + + async def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message_thread_id: int) -> bool: + """ + Use this method to clear the list of pinned messages in a forum topic. The bot must be an administrator in the + chat for this to work and must have the can_pin_messages administrator right in the supergroup. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unpinallforumtopicmessages + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_thread_id: Identifier of the topic + :type message_thread_id: :obj:`int` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.unpin_all_forum_topic_messages(self.token, chat_id, message_thread_id) + + async def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: + """ + Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. + Requires no parameters. Returns an Array of Sticker objects. + + Telegram documentation: https://core.telegram.org/bots/api#getforumtopiciconstickers + + :return: On success, a list of StickerSet objects is returned. + :rtype: List[:class:`telebot.types.StickerSet`] + """ + return await asyncio_helper.get_forum_topic_icon_stickers(self.token) async def set_state(self, user_id: int, state: Union[State, int, str], chat_id: Optional[int]=None): """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3389b334b..ed819b45e 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1720,6 +1720,45 @@ async def send_poll( payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload) + +async def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_emoji_id=None): + method_url = r'createForumTopic' + payload = {'chat_id': chat_id, 'name': name} + if icon_color: + payload['icon_color'] = icon_color + if icon_custom_emoji_id: + payload['icon_custom_emoji_id'] = icon_custom_emoji_id + return await _process_request(token, method_url, params=payload) + +async def edit_forum_topic(token, chat_id, message_thread_id, name, icon_custom_emoji_id): + method_url = r'editForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id, 'name': name, 'icon_custom_emoji_id': icon_custom_emoji_id} + return await _process_request(token, method_url, params=payload) + +async def close_forum_topic(token, chat_id, message_thread_id): + method_url = r'closeForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return await _process_request(token, method_url, params=payload) + +async def reopen_forum_topic(token, chat_id, message_thread_id): + method_url = r'reopenForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return await _process_request(token, method_url, params=payload) + +async def delete_forum_topic(token, chat_id, message_thread_id): + method_url = r'deleteForumTopic' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return await _process_request(token, method_url, params=payload) + +async def unpin_all_forum_topic_messages(token, chat_id, message_thread_id): + method_url = r'unpinAllForumTopicMessages' + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + return await _process_request(token, method_url, params=payload) + +async def get_forum_topic_icon_stickers(token): + method_url = r'getForumTopicIconStickers' + return await _process_request(token, method_url) + async def _convert_list_json_serializable(results): ret = '' for r in results: diff --git a/telebot/types.py b/telebot/types.py index 98fae8d9b..c9e6f4fa6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6766,6 +6766,7 @@ class ForumTopicCreated(JsonDeserializable): """ This object represents a service message about a new forum topic created in the chat. + Telegram documentation: https://core.telegram.org/bots/api#forumtopiccreated :param name: Name of the topic :type name: :obj:`str` @@ -6792,7 +6793,11 @@ def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[st class ForumTopicClosed(JsonDeserializable): - """This object represents a service message about a forum topic closed in the chat. Currently holds no information.""" + """ + This object represents a service message about a forum topic closed in the chat. Currently holds no information. + + Telegram documentation: https://core.telegram.org/bots/api#forumtopicclosed + """ # for future use @classmethod def de_json(cls, json_string): @@ -6803,7 +6808,11 @@ def __init__(self) -> None: class ForumTopicReopened(JsonDeserializable): - """This object represents a service message about a forum topic reopened in the chat. Currently holds no information.""" + """ + This object represents a service message about a forum topic reopened in the chat. Currently holds no information. + + Telegram documentation: https://core.telegram.org/bots/api#forumtopicreopened + """ # for future use @classmethod def de_json(cls, json_string): @@ -6817,6 +6826,8 @@ class ForumTopic(JsonDeserializable): """ This object represents a forum topic. + Telegram documentation: https://core.telegram.org/bots/api#forumtopic + :param message_thread_id: Unique identifier of the forum topic :type message_thread_id: :obj:`int` @@ -6828,6 +6839,9 @@ class ForumTopic(JsonDeserializable): :param icon_custom_emoji_id: Optional. Unique identifier of the custom emoji shown as the topic icon :type icon_custom_emoji_id: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.ForumTopic` """ @classmethod From 0846852ea1ec9b90ed13368f6d8e932f7b55990d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 17:31:49 +0400 Subject: [PATCH 1163/1808] Added all necessary parameters Added the parameter message_thread_id to the methods sendMessage, sendPhoto, sendVideo, sendAnimation, sendAudio, sendDocument, sendSticker, sendVideoNote, sendVoice, sendLocation, sendVenue, sendContact, sendPoll, sendDice, sendInvoice, sendGame, sendMediaGroup, copyMessage, forwardMessage to support sending of messages to a forum topic. --- telebot/__init__.py | 155 +++++++++++++++++++++++++++---------- telebot/apihelper.py | 93 ++++++++++++++-------- telebot/async_telebot.py | 159 ++++++++++++++++++++++++++++---------- telebot/asyncio_helper.py | 95 +++++++++++++++-------- 4 files changed, 356 insertions(+), 146 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e1ec4a3cc..1cca14908 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1488,7 +1488,8 @@ def send_message( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send text messages. @@ -1532,6 +1533,9 @@ def send_message( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1545,13 +1549,14 @@ def send_message( apihelper.send_message( self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, - entities, allow_sending_without_reply, protect_content=protect_content)) + entities, allow_sending_without_reply, protect_content=protect_content, message_thread_id=message_thread_id)) def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - timeout: Optional[int]=None) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -1575,6 +1580,9 @@ def forward_message( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1582,7 +1590,8 @@ def forward_message( protect_content = self.protect_content if (protect_content is None) else protect_content return types.Message.de_json( - apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) + apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content, + message_thread_id)) def copy_message( @@ -1597,7 +1606,8 @@ def copy_message( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> types.MessageID: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.MessageID: """ Use this method to copy messages of any kind. @@ -1639,6 +1649,9 @@ def copy_message( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -1651,7 +1664,7 @@ def copy_message( return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, - timeout, protect_content)) + timeout, protect_content, message_thread_id)) def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -1689,7 +1702,8 @@ def send_dice( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -1722,6 +1736,9 @@ def send_dice( :param protect_content: Protects the contents of the sent message from forwarding :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1732,7 +1749,7 @@ def send_dice( return types.Message.de_json( apihelper.send_dice( self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout, allow_sending_without_reply, protect_content) + reply_markup, timeout, allow_sending_without_reply, protect_content, message_thread_id) ) @@ -1745,7 +1762,8 @@ def send_photo( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None,) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -1787,6 +1805,9 @@ def send_photo( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -1800,7 +1821,7 @@ def send_photo( apihelper.send_photo( self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content)) + allow_sending_without_reply, protect_content, message_thread_id)) # TODO: Rewrite this method like in API. def send_audio( @@ -1815,7 +1836,8 @@ def send_audio( thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -1876,6 +1898,9 @@ def send_audio( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1888,7 +1913,7 @@ def send_audio( apihelper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply, protect_content)) + caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) # TODO: Rewrite this method like in API. def send_voice( @@ -1901,7 +1926,8 @@ def send_voice( timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=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). @@ -1948,6 +1974,9 @@ def send_voice( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -1959,7 +1988,7 @@ def send_voice( apihelper.send_voice( self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content)) + allow_sending_without_reply, protect_content, message_thread_id)) # TODO: Rewrite this method like in API. def send_document( @@ -1976,7 +2005,7 @@ def send_document( visible_file_name: Optional[str]=None, disable_content_type_detection: Optional[bool]=None, data: Optional[Union[Any, str]]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send general files. @@ -2030,6 +2059,9 @@ def send_document( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The thread to which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2049,7 +2081,7 @@ def send_document( 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, - protect_content = protect_content)) + protect_content = protect_content, message_thread_id = message_thread_id)) # TODO: Rewrite this method like in API. @@ -2062,7 +2094,8 @@ def send_sticker( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content:Optional[bool]=None, - data: Union[Any, str]=None) -> types.Message: + data: Union[Any, str]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2099,6 +2132,9 @@ def send_sticker( :param data: function typo miss compatibility: do not use it :type data: :obj:`str` + :param message_thread_id: The thread to which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2116,7 +2152,7 @@ def send_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, - protect_content=protect_content)) + protect_content=protect_content, message_thread_id=message_thread_id)) def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -2134,7 +2170,8 @@ def send_video( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - data: Optional[Union[Any, str]]=None) -> types.Message: + data: Optional[Union[Any, str]]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2193,6 +2230,9 @@ def send_video( :param data: function typo miss compatibility: do not use it :type data: :obj:`str` + :param message_thread_id: Identifier of a message thread, in which the video will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2209,7 +2249,7 @@ def send_video( apihelper.send_video( self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, - caption_entities, allow_sending_without_reply, protect_content)) + caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -2225,7 +2265,8 @@ def send_animation( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None, ) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2283,6 +2324,9 @@ def send_animation( :param allow_sending_without_reply: 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 message_thread_id: Identifier of a message thread, in which the video will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2295,7 +2339,7 @@ def send_animation( apihelper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply, protect_content, width, height)) + caption_entities, allow_sending_without_reply, protect_content, width, height, message_thread_id)) # TODO: Rewrite this method like in API. def send_video_note( @@ -2308,7 +2352,8 @@ def send_video_note( timeout: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -2354,6 +2399,9 @@ def send_video_note( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the video note will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2364,7 +2412,7 @@ def send_video_note( return types.Message.de_json( apihelper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb, allow_sending_without_reply, protect_content)) + disable_notification, timeout, thumb, allow_sending_without_reply, protect_content, message_thread_id)) def send_media_group( @@ -2376,7 +2424,8 @@ def send_media_group( protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: + allow_sending_without_reply: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -2404,6 +2453,9 @@ def send_media_group( :param allow_sending_without_reply: 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 message_thread_id: Identifier of a message thread, in which the media group will be sent + :type message_thread_id: :obj:`int` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -2413,7 +2465,7 @@ def send_media_group( result = apihelper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, - allow_sending_without_reply, protect_content) + allow_sending_without_reply, protect_content, message_thread_id) return [types.Message.de_json(msg) for msg in result] # TODO: Rewrite this method like in API. @@ -2429,7 +2481,8 @@ def send_location( heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -2476,6 +2529,9 @@ def send_location( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2488,7 +2544,7 @@ def send_location( self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - allow_sending_without_reply, protect_content)) + allow_sending_without_reply, protect_content, message_thread_id)) def edit_message_live_location( self, latitude: float, longitude: float, @@ -2596,7 +2652,8 @@ def send_venue( allow_sending_without_reply: Optional[bool]=None, google_place_id: Optional[str]=None, google_place_type: Optional[str]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -2651,6 +2708,9 @@ def send_venue( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The thread identifier of a message from which the reply will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2662,7 +2722,7 @@ def send_venue( apihelper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, google_place_id, google_place_type, protect_content)) + allow_sending_without_reply, google_place_id, google_place_type, protect_content, message_thread_id)) # TODO: Rewrite this method like in API. @@ -2675,7 +2735,7 @@ def send_contact( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -2717,6 +2777,9 @@ def send_contact( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The thread identifier of a message from which the reply will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2728,7 +2791,7 @@ def send_contact( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content)) + allow_sending_without_reply, protect_content, message_thread_id)) def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: @@ -3643,7 +3706,8 @@ def send_game( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Used to send the game. @@ -3673,6 +3737,9 @@ def send_game( :param protect_content: Pass True, if content of the message needs to be protected from being viewed by the bot. :type protect_content: :obj:`bool` + :param message_thread_id: The identifier of a message thread, in which the game message will be sent. + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -3683,7 +3750,7 @@ def send_game( result = apihelper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content) + allow_sending_without_reply, protect_content, message_thread_id) return types.Message.de_json(result) def set_game_score( @@ -3780,7 +3847,8 @@ def send_invoice( allow_sending_without_reply: Optional[bool]=None, max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Sends invoice. @@ -3879,6 +3947,9 @@ def send_invoice( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The identifier of a message thread, in which the invoice message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -3892,7 +3963,7 @@ def send_invoice( photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, - max_tip_amount, suggested_tip_amounts, protect_content) + max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id) return types.Message.de_json(result) def create_invoice_link(self, @@ -4016,7 +4087,8 @@ def send_poll( allow_sending_without_reply: Optional[bool]=None, timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -4084,6 +4156,9 @@ def send_poll( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The identifier of a message thread, in which the poll will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4103,7 +4178,7 @@ 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_to_message_id, allow_sending_without_reply, - reply_markup, timeout, explanation_entities, protect_content)) + reply_markup, timeout, explanation_entities, protect_content, message_thread_id)) def stop_poll( self, chat_id: Union[int, str], message_id: int, @@ -4555,7 +4630,9 @@ def create_forum_topic(self, :return: On success, information about the created topic is returned as a ForumTopic object. :rtype: :class:`telebot.types.ForumTopic` """ - return apihelper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) + return types.ForumTopic( + apihelper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) + ) def edit_forum_topic( self, chat_id: Union[int, str], diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6458f0411..b25e0365e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -237,23 +237,8 @@ def send_message( token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - entities=None, allow_sending_without_reply=None, protect_content=None): - """ - Use this method to send text messages. On success, the sent Message is returned. - :param token: - :param chat_id: - :param text: - :param disable_web_page_preview: - :param reply_to_message_id: - :param reply_markup: - :param parse_mode: - :param disable_notification: - :param timeout: - :param entities: - :param allow_sending_without_reply: - :param protect_content: - :return: - """ + entities=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if disable_web_page_preview is not None: @@ -274,6 +259,8 @@ def send_message( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, method='post') @@ -400,7 +387,7 @@ def get_chat_member(token, chat_id, user_id): def forward_message( token, chat_id, from_chat_id, message_id, - disable_notification=None, timeout=None, protect_content=None): + disable_notification=None, timeout=None, protect_content=None, message_thread_id=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -409,12 +396,14 @@ def forward_message( payload['timeout'] = timeout if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, protect_content=None): + reply_markup=None, timeout=None, protect_content=None, message_thread_id=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -435,13 +424,15 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['timeout'] = timeout if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None): + reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -458,6 +449,8 @@ def send_dice( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) @@ -465,7 +458,8 @@ def send_photo( token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -493,13 +487,15 @@ def send_photo( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_media_group( token, chat_id, media, disable_notification=None, reply_to_message_id=None, - timeout=None, allow_sending_without_reply=None, protect_content=None): + timeout=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -513,6 +509,8 @@ def send_media_group( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -524,7 +522,8 @@ def send_location( live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None): + proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -547,6 +546,8 @@ def send_location( payload['timeout'] = timeout if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) @@ -598,7 +599,7 @@ def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, allow_sending_without_reply=None, google_place_id=None, - google_place_type=None, protect_content=None): + google_place_type=None, protect_content=None, message_thread_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -621,13 +622,15 @@ def send_venue( payload['google_place_type'] = google_place_type if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None): + allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -646,6 +649,8 @@ def send_contact( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) @@ -660,7 +665,8 @@ def send_chat_action(token, chat_id, action, timeout=None): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, - thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None): + thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -702,13 +708,15 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, protect_content=None, width=None, height=None): + allow_sending_without_reply=None, protect_content=None, width=None, height=None, message_thread_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -748,12 +756,14 @@ def send_animation( payload['width'] = width if height: payload['height'] = height + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - allow_sending_without_reply=None, protect_content=None): + allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -781,11 +791,14 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None): + disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -819,12 +832,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -864,13 +879,15 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, - protect_content = None): + protect_content = None, message_thread_id=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -909,6 +926,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: payload['disable_content_type_detection'] = disable_content_type_detection + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1317,7 +1336,7 @@ def delete_message(token, chat_id, message_id, timeout=None): def send_game( token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None): + allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1332,6 +1351,8 @@ def send_game( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) @@ -1397,7 +1418,7 @@ def send_invoice( send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None): + protect_content=None, message_thread_id=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1478,6 +1499,8 @@ def send_invoice( payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) @@ -1686,7 +1709,7 @@ 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_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, explanation_entities=None, protect_content=None): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1730,6 +1753,8 @@ def send_poll( 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 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 1851b8ee5..4c1610dd4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2340,7 +2340,8 @@ async def send_message( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send text messages. @@ -2384,6 +2385,9 @@ async def send_message( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for forum supergroups only + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2397,13 +2401,14 @@ async def send_message( await asyncio_helper.send_message( self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, - entities, allow_sending_without_reply, protect_content)) + entities, allow_sending_without_reply, protect_content, message_thread_id)) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - timeout: Optional[int]=None) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -2427,6 +2432,9 @@ async def forward_message( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for forum supergroups only + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2434,7 +2442,8 @@ async def forward_message( protect_content = self.protect_content if (protect_content is None) else protect_content return types.Message.de_json( - await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content)) + await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content, + message_thread_id)) async def copy_message( self, chat_id: Union[int, str], @@ -2448,7 +2457,8 @@ async def copy_message( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None) -> types.MessageID: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.MessageID: """ Use this method to copy messages of any kind. @@ -2490,6 +2500,9 @@ async def copy_message( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2502,7 +2515,7 @@ async def copy_message( return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, - timeout, protect_content)) + timeout, protect_content, message_thread_id)) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -2540,7 +2553,8 @@ async def send_dice( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -2573,6 +2587,9 @@ async def send_dice( :param protect_content: Protects the contents of the sent message from forwarding :type protect_content: :obj:`bool` + :param message_thread_id: The identifier of a message thread, unique within the chat to which the message with the thread identifier belongs + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2583,7 +2600,7 @@ async def send_dice( return types.Message.de_json( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout, allow_sending_without_reply, protect_content) + reply_markup, timeout, allow_sending_without_reply, protect_content, message_thread_id) ) async def send_photo( @@ -2595,7 +2612,8 @@ async def send_photo( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None,) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2637,6 +2655,9 @@ async def send_photo( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2650,7 +2671,7 @@ async def send_photo( await asyncio_helper.send_photo( self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content)) + allow_sending_without_reply, protect_content, message_thread_id)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -2664,7 +2685,8 @@ async def send_audio( thumb: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2725,6 +2747,9 @@ async def send_audio( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2737,7 +2762,7 @@ async def send_audio( await asyncio_helper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply, protect_content)) + caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) async def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -2749,7 +2774,8 @@ async def send_voice( timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=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). @@ -2796,6 +2822,9 @@ async def send_voice( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2807,7 +2836,7 @@ async def send_voice( await asyncio_helper.send_voice( self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content)) + allow_sending_without_reply, protect_content, message_thread_id)) async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], @@ -2823,7 +2852,8 @@ async def send_document( visible_file_name: Optional[str]=None, disable_content_type_detection: Optional[bool]=None, data: Optional[Union[Any, str]]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send general files. @@ -2877,6 +2907,9 @@ async def send_document( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2895,7 +2928,8 @@ async def send_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, protect_content = protect_content)) + disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content, + message_thread_id = message_thread_id)) async def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], @@ -2905,7 +2939,8 @@ async def send_sticker( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - data: Union[Any, str]=None) -> types.Message: + data: Union[Any, str]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2942,6 +2977,9 @@ async def send_sticker( :param data: function typo miss compatibility: do not use it :type data: :obj:`str` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2959,7 +2997,8 @@ async def send_sticker( self.token, chat_id, sticker, '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, protect_content=protect_content)) + allow_sending_without_reply=allow_sending_without_reply, protect_content=protect_content, + message_thread_id=message_thread_id)) async def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -2977,7 +3016,8 @@ async def send_video( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - data: Optional[Union[Any, str]]=None) -> types.Message: + data: Optional[Union[Any, str]]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -3036,6 +3076,9 @@ async def send_video( :param data: function typo miss compatibility: do not use it :type data: :obj:`str` + :param message_thread_id: Identifier of a message thread, in which the video will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3053,7 +3096,7 @@ async def send_video( await asyncio_helper.send_video( self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, - caption_entities, allow_sending_without_reply, protect_content)) + caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -3069,7 +3112,8 @@ async def send_animation( reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - timeout: Optional[int]=None, ) -> types.Message: + timeout: Optional[int]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -3127,6 +3171,9 @@ async def send_animation( :param allow_sending_without_reply: 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 message_thread_id: Identifier of a message thread, in which the video will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3139,7 +3186,7 @@ async def send_animation( await asyncio_helper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply, width, height, protect_content)) + caption_entities, allow_sending_without_reply, width, height, protect_content, message_thread_id)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -3151,7 +3198,8 @@ async def send_video_note( timeout: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -3197,6 +3245,9 @@ async def send_video_note( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the video note will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3207,7 +3258,7 @@ async def send_video_note( return types.Message.de_json( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb, allow_sending_without_reply, protect_content)) + disable_notification, timeout, thumb, allow_sending_without_reply, protect_content, message_thread_id)) async def send_media_group( self, chat_id: Union[int, str], @@ -3218,7 +3269,8 @@ async def send_media_group( protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None) -> List[types.Message]: + allow_sending_without_reply: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -3246,6 +3298,9 @@ async def send_media_group( :param allow_sending_without_reply: 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 message_thread_id: Identifier of a message thread, in which the messages will be sent + :type message_thread_id: :obj:`int` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -3255,7 +3310,7 @@ async def send_media_group( result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, - allow_sending_without_reply, protect_content) + allow_sending_without_reply, protect_content, message_thread_id) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -3270,7 +3325,8 @@ async def send_location( heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -3317,6 +3373,9 @@ async def send_location( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of a message thread, in which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3329,7 +3388,7 @@ async def send_location( self.token, chat_id, latitude, longitude, live_period, reply_to_message_id, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - allow_sending_without_reply, protect_content)) + allow_sending_without_reply, protect_content, message_thread_id)) async def edit_message_live_location( self, latitude: float, longitude: float, @@ -3436,7 +3495,8 @@ async def send_venue( allow_sending_without_reply: Optional[bool]=None, google_place_id: Optional[str]=None, google_place_type: Optional[str]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -3491,6 +3551,9 @@ async def send_venue( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The thread to which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3502,7 +3565,7 @@ async def send_venue( await asyncio_helper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, google_place_id, google_place_type, protect_content) + allow_sending_without_reply, google_place_id, google_place_type, protect_content, message_thread_id) ) async def send_contact( @@ -3514,7 +3577,8 @@ async def send_contact( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -3556,6 +3620,9 @@ async def send_contact( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The thread to which the message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3567,7 +3634,7 @@ async def send_contact( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content) + allow_sending_without_reply, protect_content, message_thread_id) ) async def send_chat_action( @@ -4491,7 +4558,8 @@ async def send_game( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Used to send the game. @@ -4521,6 +4589,9 @@ async def send_game( :param protect_content: Pass True, if content of the message needs to be protected from being viewed by the bot. :type protect_content: :obj:`bool` + :param message_thread_id: Identifier of the thread to which the message will be sent. + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4531,7 +4602,7 @@ async def send_game( result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content) + allow_sending_without_reply, protect_content, message_thread_id) return types.Message.de_json(result) async def set_game_score( @@ -4627,7 +4698,8 @@ async def send_invoice( allow_sending_without_reply: Optional[bool]=None, max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Sends invoice. @@ -4726,6 +4798,9 @@ async def send_invoice( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The identifier of a message thread, in which the invoice message will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4739,7 +4814,7 @@ async def send_invoice( photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, - max_tip_amount, suggested_tip_amounts, protect_content) + max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id) return types.Message.de_json(result) @@ -4863,7 +4938,8 @@ async def send_poll( allow_sending_without_reply: Optional[bool]=None, timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, - protect_content: Optional[bool]=None) -> types.Message: + protect_content: Optional[bool]=None, + message_thread_id: Optional[int]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -4931,6 +5007,9 @@ async def send_poll( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` + :param message_thread_id: The identifier of a message thread, in which the poll will be sent + :type message_thread_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4949,7 +5028,7 @@ 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_to_message_id, allow_sending_without_reply, - reply_markup, timeout, explanation_entities, protect_content)) + reply_markup, timeout, explanation_entities, protect_content, message_thread_id)) async def stop_poll( self, chat_id: Union[int, str], message_id: int, @@ -5403,7 +5482,9 @@ async def create_forum_topic(self, :return: On success, information about the created topic is returned as a ForumTopic object. :rtype: :class:`telebot.types.ForumTopic` """ - return await asyncio_helper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) + return types.ForumTopic.de_json( + await asyncio_helper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) + ) async def edit_forum_topic( self, chat_id: Union[int, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index ed819b45e..6118d8446 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -278,23 +278,8 @@ async def send_message( token, chat_id, text, disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - entities=None, allow_sending_without_reply=None, protect_content=None): - """ - Use this method to send text messages. On success, the sent Message is returned. - :param token: - :param chat_id: - :param text: - :param disable_web_page_preview: - :param reply_to_message_id: - :param reply_markup: - :param parse_mode: - :param disable_notification: - :param timeout: - :param entities: - :param allow_sending_without_reply: - :param protect_content: - :return: - """ + entities=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} if disable_web_page_preview is not None: @@ -315,6 +300,8 @@ async def send_message( params['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: params['protect_content'] = protect_content + if message_thread_id: + params['message_thread_id'] = message_thread_id return await _process_request(token, method_name, params=params) @@ -392,7 +379,8 @@ async def get_chat_member(token, chat_id, user_id): async def forward_message( token, chat_id, from_chat_id, message_id, - disable_notification=None, timeout=None, protect_content=None): + disable_notification=None, timeout=None, protect_content=None, + message_thread_id=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -401,12 +389,14 @@ async def forward_message( payload['timeout'] = timeout if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, protect_content=None): + reply_markup=None, timeout=None, protect_content=None, message_thread_id=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -427,13 +417,16 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['timeout'] = timeout if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) async def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None): + reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -450,6 +443,8 @@ async def send_dice( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) @@ -457,7 +452,8 @@ async def send_photo( token, chat_id, photo, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -485,13 +481,15 @@ async def send_photo( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_media_group( token, chat_id, media, disable_notification=None, reply_to_message_id=None, - timeout=None, allow_sending_without_reply=None, protect_content=None): + timeout=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendMediaGroup' media_json, files = await convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -505,6 +503,8 @@ async def send_media_group( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request( token, method_url, params=payload, method='post' if files else 'get', @@ -516,7 +516,7 @@ async def send_location( live_period=None, reply_to_message_id=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None): + proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -539,6 +539,8 @@ async def send_location( payload['timeout'] = timeout if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) @@ -590,7 +592,7 @@ async def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, allow_sending_without_reply=None, google_place_id=None, - google_place_type=None, protect_content=None): + google_place_type=None, protect_content=None, message_thread_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -613,13 +615,15 @@ async def send_venue( payload['google_place_type'] = google_place_type if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None): + allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -638,6 +642,8 @@ async def send_contact( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) @@ -652,7 +658,7 @@ async def send_chat_action(token, chat_id, action, timeout=None): async def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, - protect_content=None): + protect_content=None, message_thread_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -694,13 +700,15 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, width=None, height=None, protect_content=None): + allow_sending_without_reply=None, width=None, height=None, protect_content=None, message_thread_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -740,12 +748,14 @@ async def send_animation( payload['height'] = height if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - allow_sending_without_reply=None, protect_content=None): + allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -773,11 +783,14 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_t payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None): + disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -811,12 +824,14 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None): + caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -856,12 +871,15 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None): + allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, + message_thread_id=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -900,6 +918,8 @@ async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, r payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: payload['disable_content_type_detection'] = disable_content_type_detection + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1306,7 +1326,7 @@ async def delete_message(token, chat_id, message_id, timeout=None): async def send_game( token, chat_id, game_short_name, disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None): + allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1321,6 +1341,8 @@ async def send_game( payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) @@ -1385,7 +1407,8 @@ async def send_invoice( need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, protect_content=None): + timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, protect_content=None, + message_thread_id=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1466,6 +1489,8 @@ async def send_invoice( payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts) if protect_content is not None: payload['protect_content'] = protect_content + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) @@ -1674,7 +1699,7 @@ 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_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, explanation_entities=None, protect_content=None): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1718,6 +1743,8 @@ async def send_poll( 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 return await _process_request(token, method_url, params=payload) From ae1845f285277debeab28cdb8fcab7c985ad7712 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 6 Nov 2022 17:38:01 +0400 Subject: [PATCH 1164/1808] Added active_usernames and emoji_status_custom_emoji_id --- telebot/types.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index c9e6f4fa6..d22307e0a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -521,6 +521,14 @@ class Chat(JsonDeserializable): :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. + :type active_usernames: :obj:`list` of :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. + :type emoji_status_custom_emoji_id: :obj:`str` + :param bio: Optional. Bio of the other party in a private chat. Returned only in getChat. :type bio: :obj:`str` @@ -607,7 +615,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, **kwargs): + is_forum=None, active_usernames=None, emoji_status_custom_emoji_id=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -632,6 +640,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.can_set_sticker_set: bool = can_set_sticker_set self.linked_chat_id: int = linked_chat_id self.location: ChatLocation = location + self.active_usernames: List[str] = active_usernames + self.emoji_status_custom_emoji_id: str = emoji_status_custom_emoji_id class MessageID(JsonDeserializable): From 8b735aa114ce256a9c7de9f6c370ebabd9f68c7c Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 13 Nov 2022 13:34:54 +0400 Subject: [PATCH 1165/1808] Update __init__.py --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1cca14908..f34bee791 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4630,7 +4630,7 @@ def create_forum_topic(self, :return: On success, information about the created topic is returned as a ForumTopic object. :rtype: :class:`telebot.types.ForumTopic` """ - return types.ForumTopic( + return types.ForumTopic.de_json( apihelper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) ) From 74732f2edadad40629f761938860b2df2aef8776 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 13 Nov 2022 13:20:26 +0300 Subject: [PATCH 1166/1808] content_type_media and content_type_service reviewed --- telebot/util.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 6d29e9493..485d8e6b0 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -37,8 +37,9 @@ #: Contains all media content types. content_type_media = [ - 'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll', - 'venue', 'location' + 'text', 'audio', 'document', 'animation', 'game', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', + 'location', 'venue', 'dice', 'invoice', 'successful_payment', 'connected_website', 'poll', 'passport_data', + 'web_app_data', ] #: Contains all service content types such as `User joined the group`. @@ -46,14 +47,15 @@ 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', 'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', - 'video_chat_participants_invited', 'message_auto_delete_timer_changed' + 'video_chat_participants_invited', 'message_auto_delete_timer_changed', 'forum_topic_created', 'forum_topic_closed', + 'forum_topic_reopened', ] #: All update types, should be used for allowed_updates parameter in polling. update_types = [ - "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", "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", ] From 25571b581c25abb2503f86271ecbdc9cbc476d00 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 18 Nov 2022 23:12:03 +0400 Subject: [PATCH 1167/1808] Update types.py --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index d22307e0a..06761fb91 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -956,6 +956,8 @@ def de_json(cls, json_string): opts['forward_date'] = obj.get('forward_date') if 'is_automatic_forward' in obj: opts['is_automatic_forward'] = obj.get('is_automatic_forward') + if 'is_topic_message' in obj: + opts['is_topic_message'] = obj.get('is_topic_message') if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) if 'via_bot' in obj: From 15bd5f991ac07433cf9cfae45e7d35268f9441c0 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 18 Nov 2022 23:21:06 +0400 Subject: [PATCH 1168/1808] Update types.py --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 06761fb91..f1f9f97c9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -958,6 +958,8 @@ def de_json(cls, json_string): opts['is_automatic_forward'] = obj.get('is_automatic_forward') if 'is_topic_message' in obj: opts['is_topic_message'] = obj.get('is_topic_message') + if 'message_thread_id' in obj: + opts['message_thread_id'] = obj.get('message_thread_id') if 'reply_to_message' in obj: opts['reply_to_message'] = Message.de_json(obj['reply_to_message']) if 'via_bot' in obj: From deb2099396745a12c4aceccb64e2256176f863d2 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 22 Nov 2022 18:36:15 +0400 Subject: [PATCH 1169/1808] Update types.py --- telebot/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index f1f9f97c9..f6a03c401 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1280,7 +1280,8 @@ def func(upd_text, subst_type=None, url=None, user=None): entity_string_decoded = entity_string.decode("utf-16-le") last_occurence = html_text.rfind(entity_string_decoded) string_length = len(entity_string_decoded) - html_text = html_text.replace(html_text[last_occurence:last_occurence+string_length], formatted_string) + #html_text = html_text.replace(html_text[last_occurence:last_occurence+string_length], formatted_string) + html_text = html_text[:last_occurence] + formatted_string + html_text[last_occurence+string_length:] if offset * 2 < len(utf16_text): html_text += func(utf16_text[offset * 2:]) From 0759c8e08186b8b6ad81f738ae690b047c680312 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 26 Nov 2022 20:07:08 +0400 Subject: [PATCH 1170/1808] Update __init__.py --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f34bee791..6c0a73db6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6311,7 +6311,6 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty return data = {} - params =[] handler_error = None skip_handlers = False @@ -6335,6 +6334,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty if handlers and not skip_handlers: try: for handler in handlers: + params = [] process_handler = self._test_message_handler(handler, message) if not process_handler: continue for i in inspect.signature(handler['function']).parameters: From 8d82b3d56b850e22cd8db023d5f9691134edac1f Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 26 Nov 2022 20:08:00 +0400 Subject: [PATCH 1171/1808] Update async_telebot.py --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2a0acc713..1083a2802 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -486,7 +486,6 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up handler_error = None data = {} skip_handlers = False - params = [] if middlewares: for middleware in middlewares: @@ -507,6 +506,7 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up if handlers and not(skip_handlers): try: for handler in handlers: + params = [] process_update = await self._test_message_handler(handler, message) if not process_update: continue for i in signature(handler['function']).parameters: From a169404a7c6649925adc96f9b8dbe7282e25378e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 28 Nov 2022 19:14:07 +0300 Subject: [PATCH 1172/1808] Bump version to 4.8.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 6a389091e..d55b51a6f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.7.1' +release = '4.8.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 794c97f8d..c757e00e6 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.7.1' +__version__ = '4.8.0' From 8d723bdcb3066459fbf82547d052c9cf843b9508 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 28 Nov 2022 19:17:23 +0300 Subject: [PATCH 1173/1808] Python 3.6 removed, Python 3.11 added --- .github/workflows/setup_python.yml | 2 +- .travis.yml | 1 + README.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index d1ef9640b..ac436e848 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.6','3.7','3.8','3.9', '3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9'] + python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 diff --git a/.travis.yml b/.travis.yml index c4bd8afe9..d316070b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "3.8" - "3.9" - "3.10" + - "3.11" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/README.md b/README.md index 5fbd99ac5..e4e70e6c6 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ ## Getting started -This API is tested with Python 3.6-3.10 and Pypy 3. +This API is tested with Python 3.7-3.11 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager): From 7502d26b1ae655d83a361341928a807dec1eeec0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 29 Nov 2022 14:45:51 +0400 Subject: [PATCH 1174/1808] Added locales --- .../locales/en/LC_MESSAGES/async_version.po | 4805 ++++++++++++++ .../source/locales/en/LC_MESSAGES/calldata.po | 127 + .../locales/en/LC_MESSAGES/formatting.po | 251 + docs/source/locales/en/LC_MESSAGES/index.po | 120 + docs/source/locales/en/LC_MESSAGES/install.po | 58 + .../locales/en/LC_MESSAGES/quick_start.po | 40 + .../locales/en/LC_MESSAGES/sync_version.po | 4805 ++++++++++++++ docs/source/locales/en/LC_MESSAGES/types.po | 5507 +++++++++++++++++ docs/source/locales/en/LC_MESSAGES/util.po | 345 ++ .../locales/ru/LC_MESSAGES/async_version.po | 4805 ++++++++++++++ .../source/locales/ru/LC_MESSAGES/calldata.po | 127 + .../locales/ru/LC_MESSAGES/formatting.po | 251 + docs/source/locales/ru/LC_MESSAGES/index.po | 120 + docs/source/locales/ru/LC_MESSAGES/install.po | 58 + .../locales/ru/LC_MESSAGES/quick_start.po | 40 + .../locales/ru/LC_MESSAGES/sync_version.po | 4805 ++++++++++++++ docs/source/locales/ru/LC_MESSAGES/types.po | 5507 +++++++++++++++++ docs/source/locales/ru/LC_MESSAGES/util.po | 345 ++ 18 files changed, 32116 insertions(+) create mode 100644 docs/source/locales/en/LC_MESSAGES/async_version.po create mode 100644 docs/source/locales/en/LC_MESSAGES/calldata.po create mode 100644 docs/source/locales/en/LC_MESSAGES/formatting.po create mode 100644 docs/source/locales/en/LC_MESSAGES/index.po create mode 100644 docs/source/locales/en/LC_MESSAGES/install.po create mode 100644 docs/source/locales/en/LC_MESSAGES/quick_start.po create mode 100644 docs/source/locales/en/LC_MESSAGES/sync_version.po create mode 100644 docs/source/locales/en/LC_MESSAGES/types.po create mode 100644 docs/source/locales/en/LC_MESSAGES/util.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/async_version.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/calldata.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/formatting.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/index.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/install.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/quick_start.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/sync_version.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/types.po create mode 100644 docs/source/locales/ru/LC_MESSAGES/util.po diff --git a/docs/source/locales/en/LC_MESSAGES/async_version.po b/docs/source/locales/en/LC_MESSAGES/async_version.po new file mode 100644 index 000000000..f731d317e --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/async_version.po @@ -0,0 +1,4805 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../async_version/index.rst:3 +msgid "AsyncTeleBot" +msgstr "" + +#: ../../async_version/index.rst:6 +msgid "Asynchronous pyTelegramBotAPI" +msgstr "" + +#: ../../async_version/index.rst:6 +msgid "ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation" +msgstr "" + +#: ../../async_version/index.rst:12 +msgid "AsyncTeleBot methods" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:1 +#: telebot.async_telebot.ExceptionHandler:1 telebot.async_telebot.Handler:1 +#: telebot.asyncio_filters.TextFilter:1 +#: telebot.asyncio_handler_backends.BaseMiddleware:1 +#: telebot.asyncio_handler_backends.CancelUpdate:1 +#: telebot.asyncio_handler_backends.ContinueHandling:1 +#: telebot.asyncio_handler_backends.SkipHandler:1 +#: telebot.asyncio_handler_backends.State:1 +#: telebot.asyncio_handler_backends.StatesGroup:1 +#: telebot.ext.aio.webhooks.AsyncWebhookListener:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:1 +msgid "This is the main asynchronous class for Bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:3 +msgid "It allows you to add handlers for different kind of updates." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:5 +msgid "Usage:" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:7 +msgid "Using asynchronous implementation of TeleBot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:16 +msgid "" +"See more examples in examples/ directory: " +"https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:21 +msgid "Install coloredlogs module to specify colorful_logs=True" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot +#: telebot.async_telebot.AsyncTeleBot.add_custom_filter +#: telebot.async_telebot.AsyncTeleBot.add_data +#: telebot.async_telebot.AsyncTeleBot.add_sticker_to_set +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic +#: telebot.async_telebot.AsyncTeleBot.create_invoice_link +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic +#: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands +#: telebot.async_telebot.AsyncTeleBot.delete_state +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_webhook +#: telebot.async_telebot.AsyncTeleBot.download_file +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_message_caption +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location +#: telebot.async_telebot.AsyncTeleBot.edit_message_media +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup +#: telebot.async_telebot.AsyncTeleBot.edit_message_text +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.enable_saving_states +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.get_chat +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators +#: telebot.async_telebot.AsyncTeleBot.get_chat_member +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count +#: telebot.async_telebot.AsyncTeleBot.get_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers +#: telebot.async_telebot.AsyncTeleBot.get_file +#: telebot.async_telebot.AsyncTeleBot.get_file_url +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores +#: telebot.async_telebot.AsyncTeleBot.get_my_commands +#: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_state +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set +#: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.infinity_polling +#: telebot.async_telebot.AsyncTeleBot.inline_handler +#: telebot.async_telebot.AsyncTeleBot.leave_chat +#: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.poll_handler +#: telebot.async_telebot.AsyncTeleBot.polling +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.process_new_updates +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reply_to +#: telebot.async_telebot.AsyncTeleBot.reset_data +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member +#: telebot.async_telebot.AsyncTeleBot.retrieve_data +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.run_webhooks +#: telebot.async_telebot.AsyncTeleBot.send_animation +#: telebot.async_telebot.AsyncTeleBot.send_audio +#: telebot.async_telebot.AsyncTeleBot.send_chat_action +#: telebot.async_telebot.AsyncTeleBot.send_contact +#: telebot.async_telebot.AsyncTeleBot.send_dice +#: telebot.async_telebot.AsyncTeleBot.send_document +#: telebot.async_telebot.AsyncTeleBot.send_game +#: telebot.async_telebot.AsyncTeleBot.send_invoice +#: telebot.async_telebot.AsyncTeleBot.send_location +#: telebot.async_telebot.AsyncTeleBot.send_media_group +#: telebot.async_telebot.AsyncTeleBot.send_message +#: telebot.async_telebot.AsyncTeleBot.send_photo +#: telebot.async_telebot.AsyncTeleBot.send_poll +#: telebot.async_telebot.AsyncTeleBot.send_sticker +#: telebot.async_telebot.AsyncTeleBot.send_venue +#: telebot.async_telebot.AsyncTeleBot.send_video +#: telebot.async_telebot.AsyncTeleBot.send_video_note +#: telebot.async_telebot.AsyncTeleBot.send_voice +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title +#: telebot.async_telebot.AsyncTeleBot.set_chat_description +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_my_commands +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_update_listener +#: telebot.async_telebot.AsyncTeleBot.set_webhook +#: telebot.async_telebot.AsyncTeleBot.setup_middleware +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location +#: telebot.async_telebot.AsyncTeleBot.stop_poll +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file +#: telebot.asyncio_filters.TextFilter +msgid "Parameters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:24 +msgid "Token of a bot, obtained from @BotFather" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:27 +msgid "Default parse mode, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:30 +msgid "Offset used in get_updates, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:33 +msgid "" +"Exception handler, which will handle the exception occured, defaults to " +"None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:36 +msgid "Storage for states, defaults to StateMemoryStorage()" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:39 +msgid "Default value for disable_web_page_preview, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:42 +msgid "Default value for disable_notification, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:45 +msgid "Default value for protect_content, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:48 +msgid "Default value for allow_sending_without_reply, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:51 +msgid "Outputs colorful logs" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:1 +msgid "Create custom filter." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:3 +msgid "Example on checking the text of a message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:12 +msgid "Class with check(message) method." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter +#: telebot.async_telebot.AsyncTeleBot.add_data +#: telebot.async_telebot.AsyncTeleBot.add_sticker_to_set +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.close +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic +#: telebot.async_telebot.AsyncTeleBot.create_invoice_link +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic +#: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands +#: telebot.async_telebot.AsyncTeleBot.delete_state +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_webhook +#: telebot.async_telebot.AsyncTeleBot.download_file +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_message_caption +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location +#: telebot.async_telebot.AsyncTeleBot.edit_message_media +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup +#: telebot.async_telebot.AsyncTeleBot.edit_message_text +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.get_chat +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators +#: telebot.async_telebot.AsyncTeleBot.get_chat_member +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count +#: telebot.async_telebot.AsyncTeleBot.get_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers +#: telebot.async_telebot.AsyncTeleBot.get_file +#: telebot.async_telebot.AsyncTeleBot.get_file_url +#: telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores +#: telebot.async_telebot.AsyncTeleBot.get_my_commands +#: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_state +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set +#: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.infinity_polling +#: telebot.async_telebot.AsyncTeleBot.inline_handler +#: telebot.async_telebot.AsyncTeleBot.leave_chat +#: telebot.async_telebot.AsyncTeleBot.log_out +#: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.poll_handler +#: telebot.async_telebot.AsyncTeleBot.polling +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.process_new_updates +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reply_to +#: telebot.async_telebot.AsyncTeleBot.reset_data +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member +#: telebot.async_telebot.AsyncTeleBot.retrieve_data +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.run_webhooks +#: telebot.async_telebot.AsyncTeleBot.send_animation +#: telebot.async_telebot.AsyncTeleBot.send_audio +#: telebot.async_telebot.AsyncTeleBot.send_chat_action +#: telebot.async_telebot.AsyncTeleBot.send_contact +#: telebot.async_telebot.AsyncTeleBot.send_dice +#: telebot.async_telebot.AsyncTeleBot.send_document +#: telebot.async_telebot.AsyncTeleBot.send_game +#: telebot.async_telebot.AsyncTeleBot.send_invoice +#: telebot.async_telebot.AsyncTeleBot.send_location +#: telebot.async_telebot.AsyncTeleBot.send_media_group +#: telebot.async_telebot.AsyncTeleBot.send_message +#: telebot.async_telebot.AsyncTeleBot.send_photo +#: telebot.async_telebot.AsyncTeleBot.send_poll +#: telebot.async_telebot.AsyncTeleBot.send_sticker +#: telebot.async_telebot.AsyncTeleBot.send_venue +#: telebot.async_telebot.AsyncTeleBot.send_video +#: telebot.async_telebot.AsyncTeleBot.send_video_note +#: telebot.async_telebot.AsyncTeleBot.send_voice +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title +#: telebot.async_telebot.AsyncTeleBot.set_chat_description +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_my_commands +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_update_listener +#: telebot.async_telebot.AsyncTeleBot.set_webhook +#: telebot.async_telebot.AsyncTeleBot.setup_middleware +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location +#: telebot.async_telebot.AsyncTeleBot.stop_poll +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file +#: telebot.asyncio_filters.TextFilter +#: telebot.ext.aio.webhooks.AsyncWebhookListener.run_app +msgid "Returns" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:15 +#: telebot.async_telebot.AsyncTeleBot.add_data:10 +#: telebot.async_telebot.AsyncTeleBot.callback_query_handler:9 +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler:17 +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:10 +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler:11 +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:10 +#: telebot.async_telebot.AsyncTeleBot.delete_state:9 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:22 +#: telebot.async_telebot.AsyncTeleBot.infinity_polling:35 +#: telebot.async_telebot.AsyncTeleBot.inline_handler:9 +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:10 +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:10 +#: telebot.async_telebot.AsyncTeleBot.poll_handler:8 +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:9 +#: telebot.async_telebot.AsyncTeleBot.process_new_updates:8 +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:23 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:26 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:26 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:14 +#: telebot.async_telebot.AsyncTeleBot.reset_data:9 +#: telebot.async_telebot.AsyncTeleBot.set_state:18 +#: telebot.async_telebot.AsyncTeleBot.set_update_listener:15 +#: telebot.async_telebot.AsyncTeleBot.setup_middleware:10 +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:9 +#: telebot.asyncio_filters.TextFilter:22 +#: telebot.ext.aio.webhooks.AsyncWebhookListener.run_app:4 +msgid "None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:1 +msgid "Add data to states." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:3 +#: telebot.async_telebot.AsyncTeleBot.delete_state:3 +#: telebot.async_telebot.AsyncTeleBot.get_state:4 +#: telebot.async_telebot.AsyncTeleBot.reset_data:3 +#: telebot.async_telebot.AsyncTeleBot.set_state:9 +msgid "User's identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:6 +#: telebot.async_telebot.AsyncTeleBot.delete_state:6 +#: telebot.async_telebot.AsyncTeleBot.get_state:7 +#: telebot.async_telebot.AsyncTeleBot.reset_data:6 +#: telebot.async_telebot.AsyncTeleBot.set_state:15 +msgid "Chat's identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:9 +msgid "Data to add" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:1 +msgid "" +"Use this method to add a new sticker to a set created by the bot. It's " +"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:7 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:7 +msgid "User identifier of created sticker set owner" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:10 +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:6 +msgid "Sticker set name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:13 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:18 +msgid "One or more emoji corresponding to the sticker" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:16 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:21 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px. Pass a file_id as a String to send a file that already " +"exists on the Telegram servers, 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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:26 +msgid "TGS animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:24 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 +msgid "WebM animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:27 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:40 +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:30 +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query:22 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:35 +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:21 +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:18 +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:43 +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:6 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:19 +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:15 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:13 +msgid "On success, True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic +#: telebot.async_telebot.AsyncTeleBot.create_invoice_link +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic +#: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_webhook +#: telebot.async_telebot.AsyncTeleBot.download_file +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_message_caption +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location +#: telebot.async_telebot.AsyncTeleBot.edit_message_media +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup +#: telebot.async_telebot.AsyncTeleBot.edit_message_text +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.get_chat +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators +#: telebot.async_telebot.AsyncTeleBot.get_chat_member +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count +#: telebot.async_telebot.AsyncTeleBot.get_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers +#: telebot.async_telebot.AsyncTeleBot.get_file_url +#: telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores +#: telebot.async_telebot.AsyncTeleBot.get_my_commands +#: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_state +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set +#: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.log_out +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reply_to +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member +#: telebot.async_telebot.AsyncTeleBot.retrieve_data +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.send_animation +#: telebot.async_telebot.AsyncTeleBot.send_audio +#: telebot.async_telebot.AsyncTeleBot.send_chat_action +#: telebot.async_telebot.AsyncTeleBot.send_contact +#: telebot.async_telebot.AsyncTeleBot.send_dice +#: telebot.async_telebot.AsyncTeleBot.send_document +#: telebot.async_telebot.AsyncTeleBot.send_game +#: telebot.async_telebot.AsyncTeleBot.send_invoice +#: telebot.async_telebot.AsyncTeleBot.send_location +#: telebot.async_telebot.AsyncTeleBot.send_media_group +#: telebot.async_telebot.AsyncTeleBot.send_message +#: telebot.async_telebot.AsyncTeleBot.send_photo +#: telebot.async_telebot.AsyncTeleBot.send_poll +#: telebot.async_telebot.AsyncTeleBot.send_sticker +#: telebot.async_telebot.AsyncTeleBot.send_venue +#: telebot.async_telebot.AsyncTeleBot.send_video +#: telebot.async_telebot.AsyncTeleBot.send_video_note +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title +#: telebot.async_telebot.AsyncTeleBot.set_chat_description +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_my_commands +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_webhook +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location +#: telebot.async_telebot.AsyncTeleBot.stop_poll +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file +msgid "Return type" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:31 +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query:23 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:36 +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:22 +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:19 +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:15 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member:25 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:17 +#: telebot.async_telebot.AsyncTeleBot.close:9 +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic:14 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:44 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:15 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo:13 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:11 +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:14 +#: telebot.async_telebot.AsyncTeleBot.delete_message:23 +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands:17 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:7 +#: telebot.async_telebot.AsyncTeleBot.delete_webhook:13 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:20 +#: telebot.async_telebot.AsyncTeleBot.leave_chat:8 +#: telebot.async_telebot.AsyncTeleBot.log_out:11 +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:48 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:23 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:18 +#: telebot.async_telebot.AsyncTeleBot.set_chat_description:14 +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo:16 +#: telebot.async_telebot.AsyncTeleBot.set_chat_title:17 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:18 +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:18 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:12 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:16 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member:20 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:15 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:12 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:14 +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:15 +msgid ":obj:`bool`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:1 +msgid "" +"Use this method to send answers to callback queries sent from inline " +"keyboards. The answer will be displayed to the user as a notification at " +"the top of the chat screen or as an alert." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answercallbackquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:6 +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:10 +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:5 +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query:8 +msgid "Unique identifier for the query to be answered" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:9 +msgid "" +"Text of the notification. If not specified, nothing will be shown to the " +"user, 0-200 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:12 +msgid "" +"If True, an alert will be shown by the client instead of a notification " +"at the top of the chat screen. Defaults to false." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:15 +msgid "" +"URL that will be opened by the user's client. If you have created a Game " +"and accepted the conditions via @BotFather, specify the URL that opens " +"your game - note that this will only work if the query comes from a " +"callback_game button." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:19 +msgid "" +"The maximum amount of time in seconds that the result of the callback " +"query may be cached client-side. Telegram apps will support caching " +"starting in version 3.14. Defaults to 0." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:1 +msgid "" +"Use this method to send answers to an inline query. On success, True is " +"returned. No more than 50 results per query are allowed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerinlinequery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:6 +msgid "Unique identifier for the answered query" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:9 +msgid "Array of results for the inline query" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:12 +msgid "" +"The maximum amount of time in seconds that the result of the inline query" +" may be cached on the server." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:16 +msgid "" +"Pass True, if results may be cached on the server side only for the user " +"that sent the query." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:20 +msgid "" +"Pass the offset that a client should send in the next query with the same" +" text to receive more results." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:24 +msgid "" +"Deep-linking parameter for the /start message sent to the bot when user " +"presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - " +"are allowed. Example: An inline bot that sends YouTube videos can ask the" +" user to connect the bot to their YouTube account to adapt search results" +" accordingly. To do this, it displays a 'Connect your YouTube account' " +"button above the results, or even before showing any. The user presses " +"the button, switches to a private chat with the bot and, in doing so, " +"passes a start parameter that instructs the bot to return an OAuth link. " +"Once done, the bot can offer a switch_inline button so that the user can " +"easily return to the chat where they wanted to use the bot's inline " +"capabilities." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:32 +msgid "" +"Parameter for the start message sent to the bot when user presses the " +"switch button" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:1 +msgid "" +"Once the user has confirmed their payment and shipping details, the Bot " +"API sends the final confirmation in the form of an Update with the field " +"pre_checkout_query. Use this method to respond to such pre-checkout " +"queries. On success, True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:6 +msgid "" +"The Bot API must receive an answer within 10 seconds after the pre-" +"checkout query was sent." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerprecheckoutquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:13 +msgid "" +"Specify True if everything is alright (goods are available, etc.) and the" +" bot is ready to proceed with the order. Use False if there are any " +"problems." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:16 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains the reason for failure to proceed with the checkout (e.g. " +"\"Sorry, somebody just bought the last of our amazing black T-shirts " +"while you were busy filling out your payment details. Please choose a " +"different color or garment!\"). Telegram will display this message to the" +" user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:1 +msgid "Asks for an answer to a shipping question." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answershippingquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:8 +msgid "" +"Specify True if delivery to the specified address is possible and False " +"if there are any problems (for example, if delivery to the specified " +"address is not possible)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:11 +msgid "" +"Required if ok is True. A JSON-serialized array of available shipping " +"options." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:14 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains why it is impossible to complete the order (e.g. \"Sorry, " +"delivery to your desired address is unavailable'). Telegram will display " +"this message to the user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:1 +msgid "" +"Use this method to set the result of an interaction with a Web App and " +"send a corresponding message on behalf of the user to the chat from which" +" the query originated. On success, a SentWebAppMessage object is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:6 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#answerwebappquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:11 +msgid "A JSON-serialized object describing the message to be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:14 +msgid "On success, a SentWebAppMessage object is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:15 +msgid ":class:`telebot.types.SentWebAppMessage`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:1 +msgid "" +"Use this method to approve a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#approvechatjoinrequest" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:7 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:7 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:7 +#: telebot.async_telebot.AsyncTeleBot.get_chat_member:5 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:6 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:7 +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:7 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup (in the format @supergroupusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:11 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member:12 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:11 +#: telebot.async_telebot.AsyncTeleBot.get_chat_member:8 +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:6 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:11 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:11 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:10 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member:13 +msgid "Unique identifier of the target user" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:14 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:16 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:14 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo:12 +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands:16 +#: telebot.async_telebot.AsyncTeleBot.log_out:10 +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message:18 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:60 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:17 +#: telebot.async_telebot.AsyncTeleBot.set_chat_description:13 +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:14 +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_title:16 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:17 +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:17 +#: telebot.async_telebot.AsyncTeleBot.set_webhook:46 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:14 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:11 +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:14 +msgid "True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:1 +msgid "" +"Use this method to ban a user in a group, a supergroup or a channel. In " +"the case of supergroups and channels, the user will not be able to return" +" to the chat on their own using invite links, etc., unless unbanned " +"first. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:8 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:7 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:15 +msgid "" +"Date when the user will be unbanned, unix time. If user is banned for " +"more than 366 days or less than 30 seconds from the current time they are" +" considered to be banned forever" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:19 +msgid "" +"Bool: Pass True to delete all messages from the chat for the user that is" +" being removed. If False, the user will be able to see messages in the " +"group that were sent before the user was removed. Always True for " +"supergroups and channels." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 +#: telebot.async_telebot.AsyncTeleBot.delete_message:22 +#: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:22 +msgid "Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:1 +msgid "" +"Use this method to ban a channel chat in a supergroup or a channel. The " +"owner of the chat will not be able to send messages and join live streams" +" on behalf of the chat, unless it is unbanned first. The bot must be an " +"administrator in the supergroup or channel for this to work and must have" +" the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#banchatsenderchat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:10 +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.copy_message:5 +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.delete_message:13 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:13 +#: telebot.async_telebot.AsyncTeleBot.forward_message:8 +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message:7 +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.send_animation:6 +#: telebot.async_telebot.AsyncTeleBot.send_audio:9 +#: telebot.async_telebot.AsyncTeleBot.send_dice:5 +#: telebot.async_telebot.AsyncTeleBot.send_document:5 +#: telebot.async_telebot.AsyncTeleBot.send_game:5 +#: telebot.async_telebot.AsyncTeleBot.send_location:5 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:6 +#: telebot.async_telebot.AsyncTeleBot.send_message:9 +#: telebot.async_telebot.AsyncTeleBot.send_photo:5 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:6 +#: telebot.async_telebot.AsyncTeleBot.send_video:5 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:6 +#: telebot.async_telebot.AsyncTeleBot.send_voice:7 +#: telebot.async_telebot.AsyncTeleBot.set_chat_description:6 +#: telebot.async_telebot.AsyncTeleBot.set_chat_title:9 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:6 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:8 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:7 +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:7 +msgid "" +"Unique identifier for the target chat or username of the target channel " +"(in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:13 +msgid "Unique identifier of the target sender chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:1 +msgid "" +"Handles new incoming callback query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.CallbackQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:4 +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler:10 +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:5 +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler:6 +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:5 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:10 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:11 +#: telebot.async_telebot.AsyncTeleBot.inline_handler:4 +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:5 +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:5 +#: telebot.async_telebot.AsyncTeleBot.poll_handler:4 +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:4 +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:4 +msgid "Function executed as a filter" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:7 +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler:16 +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:8 +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler:9 +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:8 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:16 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:20 +#: telebot.async_telebot.AsyncTeleBot.inline_handler:7 +#: telebot.async_telebot.AsyncTeleBot.message_handler:50 +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:8 +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:8 +#: telebot.async_telebot.AsyncTeleBot.poll_handler:7 +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:7 +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:24 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:24 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:11 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:12 +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:7 +msgid "Optional keyword arguments(custom filters)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:1 +msgid "" +"Handles new incoming channel post of any kind - text, photo, sticker, " +"etc. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:4 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:4 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:5 +#: telebot.async_telebot.AsyncTeleBot.message_handler:34 +msgid "Optional list of strings (commands to handle)." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:7 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:7 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:8 +#: telebot.async_telebot.AsyncTeleBot.message_handler:37 +msgid "Optional regular expression." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:13 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:13 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:14 +#: telebot.async_telebot.AsyncTeleBot.message_handler:44 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:6 +msgid "Supported message content types. Must be a list. Defaults to ['text']." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:1 +msgid "" +"Handles a request to join the chat has been sent. The bot must have the " +"can_invite_users administrator right in the chat to receive these " +"updates. As a parameter to the decorator function, it passes " +":class:`telebot.types.ChatJoinRequest` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.chat_member_handler:1 +msgid "" +"Handles update in a status of a user in a chat. The bot must be an " +"administrator in the chat and must explicitly specify “chat_member” in " +"the list of allowed_updates to receive these updates. As a parameter to " +"the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:1 +msgid "" +"The result of an inline query that was chosen by a user and sent to their" +" chat partner. Please see our documentation on the feedback collecting " +"for details on how to enable these updates for your bot. As a parameter " +"to the decorator function, it passes " +":class:`telebot.types.ChosenInlineResult` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close:1 +msgid "" +"Use this method to close the bot instance before moving it from one local" +" server to another. You need to delete the webhook before calling this " +"method to ensure that the bot isn't launched again after server restart. " +"The method will return error 429 in the first 10 minutes after the bot is" +" launched. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#close" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:1 +msgid "" +"Use this method to close an open topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:10 +msgid "Identifier of the topic to close" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_session:1 +msgid "" +"Closes existing session of aiohttp. Use this function if you stop " +"polling/webhooks." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:1 +msgid "Use this method to copy messages of any kind." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:8 +#: telebot.async_telebot.AsyncTeleBot.forward_message:11 +msgid "" +"Unique identifier for the chat where the original message was sent (or " +"channel username in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:10 +#: telebot.async_telebot.AsyncTeleBot.forward_message:14 +msgid "Message identifier in the chat specified in from_chat_id" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:13 +msgid "" +"New caption for media, 0-1024 characters after entities parsing. If not " +"specified, the original caption is kept" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:16 +msgid "Mode for parsing entities in the new caption." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:19 +msgid "" +"A JSON-serialized list of special entities that appear in the new " +"caption, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:22 +#: telebot.async_telebot.AsyncTeleBot.send_animation:45 +#: telebot.async_telebot.AsyncTeleBot.send_audio:39 +#: telebot.async_telebot.AsyncTeleBot.send_contact:20 +#: telebot.async_telebot.AsyncTeleBot.send_dice:12 +#: telebot.async_telebot.AsyncTeleBot.send_document:26 +#: telebot.async_telebot.AsyncTeleBot.send_game:11 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:67 +#: telebot.async_telebot.AsyncTeleBot.send_location:25 +#: telebot.async_telebot.AsyncTeleBot.send_message:24 +#: telebot.async_telebot.AsyncTeleBot.send_photo:22 +#: telebot.async_telebot.AsyncTeleBot.send_poll:44 +#: telebot.async_telebot.AsyncTeleBot.send_venue:27 +#: telebot.async_telebot.AsyncTeleBot.send_video:35 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:27 +#: telebot.async_telebot.AsyncTeleBot.send_voice:31 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:25 +#: telebot.async_telebot.AsyncTeleBot.send_animation:34 +#: telebot.async_telebot.AsyncTeleBot.send_audio:57 +#: telebot.async_telebot.AsyncTeleBot.send_contact:38 +#: telebot.async_telebot.AsyncTeleBot.send_document:50 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:95 +#: telebot.async_telebot.AsyncTeleBot.send_location:43 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:15 +#: telebot.async_telebot.AsyncTeleBot.send_photo:25 +#: telebot.async_telebot.AsyncTeleBot.send_poll:64 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:30 +#: telebot.async_telebot.AsyncTeleBot.send_venue:51 +#: telebot.async_telebot.AsyncTeleBot.send_video:38 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:42 +#: telebot.async_telebot.AsyncTeleBot.send_voice:43 +msgid "Protects the contents of the sent message from forwarding and saving" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:28 +#: telebot.async_telebot.AsyncTeleBot.send_animation:37 +#: telebot.async_telebot.AsyncTeleBot.send_audio:29 +#: telebot.async_telebot.AsyncTeleBot.send_contact:23 +#: telebot.async_telebot.AsyncTeleBot.send_dice:15 +#: telebot.async_telebot.AsyncTeleBot.send_document:12 +#: telebot.async_telebot.AsyncTeleBot.send_game:14 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:70 +#: telebot.async_telebot.AsyncTeleBot.send_location:17 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:18 +#: telebot.async_telebot.AsyncTeleBot.send_message:30 +#: telebot.async_telebot.AsyncTeleBot.send_photo:28 +#: telebot.async_telebot.AsyncTeleBot.send_poll:47 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:13 +#: telebot.async_telebot.AsyncTeleBot.send_venue:30 +#: telebot.async_telebot.AsyncTeleBot.send_video:41 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:19 +#: telebot.async_telebot.AsyncTeleBot.send_voice:20 +msgid "If the message is a reply, ID of the original message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:31 +#: telebot.async_telebot.AsyncTeleBot.send_animation:54 +#: telebot.async_telebot.AsyncTeleBot.send_audio:54 +#: telebot.async_telebot.AsyncTeleBot.send_dice:26 +#: telebot.async_telebot.AsyncTeleBot.send_document:38 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:84 +#: telebot.async_telebot.AsyncTeleBot.send_location:40 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:24 +#: telebot.async_telebot.AsyncTeleBot.send_message:33 +#: telebot.async_telebot.AsyncTeleBot.send_photo:31 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:27 +#: telebot.async_telebot.AsyncTeleBot.send_video:44 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:39 +#: telebot.async_telebot.AsyncTeleBot.send_voice:40 +msgid "" +"Pass True, if the message should be sent even if the specified replied-to" +" message is not found" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:34 +#: telebot.async_telebot.AsyncTeleBot.send_animation:40 +#: telebot.async_telebot.AsyncTeleBot.send_contact:26 +#: telebot.async_telebot.AsyncTeleBot.send_dice:18 +#: telebot.async_telebot.AsyncTeleBot.send_document:18 +#: telebot.async_telebot.AsyncTeleBot.send_game:17 +#: telebot.async_telebot.AsyncTeleBot.send_location:20 +#: telebot.async_telebot.AsyncTeleBot.send_message:36 +#: telebot.async_telebot.AsyncTeleBot.send_photo:34 +#: telebot.async_telebot.AsyncTeleBot.send_poll:53 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:16 +#: telebot.async_telebot.AsyncTeleBot.send_venue:33 +#: telebot.async_telebot.AsyncTeleBot.send_video:47 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:22 +#: telebot.async_telebot.AsyncTeleBot.send_voice:23 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:39 +#: telebot.async_telebot.AsyncTeleBot.delete_message:19 +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:23 +#: telebot.async_telebot.AsyncTeleBot.forward_message:20 +#: telebot.async_telebot.AsyncTeleBot.send_animation:48 +#: telebot.async_telebot.AsyncTeleBot.send_audio:42 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:19 +#: telebot.async_telebot.AsyncTeleBot.send_contact:31 +#: telebot.async_telebot.AsyncTeleBot.send_dice:23 +#: telebot.async_telebot.AsyncTeleBot.send_document:29 +#: telebot.async_telebot.AsyncTeleBot.send_location:28 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:21 +#: telebot.async_telebot.AsyncTeleBot.send_message:40 +#: telebot.async_telebot.AsyncTeleBot.send_photo:39 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:24 +#: telebot.async_telebot.AsyncTeleBot.send_venue:38 +#: telebot.async_telebot.AsyncTeleBot.send_video:52 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:30 +#: telebot.async_telebot.AsyncTeleBot.send_voice:34 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:19 +msgid "Timeout in seconds for the request." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:42 +#: telebot.async_telebot.AsyncTeleBot.send_audio:60 +#: telebot.async_telebot.AsyncTeleBot.send_document:53 +#: telebot.async_telebot.AsyncTeleBot.send_location:46 +#: telebot.async_telebot.AsyncTeleBot.send_photo:42 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:36 +#: telebot.async_telebot.AsyncTeleBot.send_voice:46 +msgid "Identifier of a message thread, in which the message will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:45 +#: telebot.async_telebot.AsyncTeleBot.forward_message:26 +#: telebot.async_telebot.AsyncTeleBot.reply_to:11 +#: telebot.async_telebot.AsyncTeleBot.send_animation:60 +#: telebot.async_telebot.AsyncTeleBot.send_audio:63 +#: telebot.async_telebot.AsyncTeleBot.send_contact:44 +#: telebot.async_telebot.AsyncTeleBot.send_dice:35 +#: telebot.async_telebot.AsyncTeleBot.send_document:56 +#: telebot.async_telebot.AsyncTeleBot.send_game:32 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:101 +#: telebot.async_telebot.AsyncTeleBot.send_location:49 +#: telebot.async_telebot.AsyncTeleBot.send_message:46 +#: telebot.async_telebot.AsyncTeleBot.send_photo:45 +#: telebot.async_telebot.AsyncTeleBot.send_poll:70 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:39 +#: telebot.async_telebot.AsyncTeleBot.send_venue:57 +#: telebot.async_telebot.AsyncTeleBot.send_video:61 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:48 +#: telebot.async_telebot.AsyncTeleBot.send_voice:49 +msgid "On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:46 +#: telebot.async_telebot.AsyncTeleBot.forward_message:27 +#: telebot.async_telebot.AsyncTeleBot.reply_to:12 +#: telebot.async_telebot.AsyncTeleBot.send_animation:61 +#: telebot.async_telebot.AsyncTeleBot.send_audio:64 +#: telebot.async_telebot.AsyncTeleBot.send_contact:45 +#: telebot.async_telebot.AsyncTeleBot.send_dice:36 +#: telebot.async_telebot.AsyncTeleBot.send_document:57 +#: telebot.async_telebot.AsyncTeleBot.send_location:50 +#: telebot.async_telebot.AsyncTeleBot.send_message:47 +#: telebot.async_telebot.AsyncTeleBot.send_photo:46 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:40 +#: telebot.async_telebot.AsyncTeleBot.send_venue:58 +#: telebot.async_telebot.AsyncTeleBot.send_video:62 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:49 +msgid ":class:`telebot.types.Message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:1 +msgid "" +"Use this method to create an additional invite link for a chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. The link can be revoked using the " +"method revokeChatInviteLink. Returns the new invite link as " +"ChatInviteLink object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createchatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:8 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:6 +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:6 +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:7 +msgid "" +"Id: Unique identifier for the target chat or username of the target " +"channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:12 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:10 +msgid "Invite link name; 0-32 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:15 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:16 +msgid "Point in time (Unix timestamp) when the link will expire" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:18 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:19 +msgid "Maximum number of users that can be members of the chat simultaneously" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:21 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:22 +msgid "" +"True, if users joining the chat via the link need to be approved by chat " +"administrators. If True, member_limit can't be specified" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:24 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:25 +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:14 +msgid "Returns the new invite link as ChatInviteLink object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:25 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:26 +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:15 +msgid ":class:`telebot.types.ChatInviteLink`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:1 +msgid "" +"Use this method to create a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights. Returns information about the " +"created topic as a ForumTopic object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:10 +msgid "Name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:13 +msgid "" +"Color of the topic icon in RGB format. Currently, must be one of " +"0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:16 +msgid "" +"Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " +"be exactly 1 character long" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:19 +msgid "" +"On success, information about the created topic is returned as a " +"ForumTopic object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:20 +msgid ":class:`telebot.types.ForumTopic`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:1 +msgid "" +"Use this method to create a link for an invoice. Returns the created " +"invoice link as String on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createinvoicelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:7 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:8 +msgid "Product name, 1-32 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:10 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:11 +msgid "Product description, 1-255 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:13 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:14 +msgid "" +"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " +"the user, use for your internal processes." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:17 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:18 +msgid "Payments provider token, obtained via @Botfather" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:20 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:21 +msgid "" +"Three-letter ISO 4217 currency code, see " +"https://core.telegram.org/bots/payments#supported-currencies" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:24 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:25 +msgid "" +"Price breakdown, a list of components (e.g. product price, tax, discount," +" delivery cost, delivery tax, bonus, etc.)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:28 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:87 +msgid "The maximum accepted amount for tips in the smallest units of the currency" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:31 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:90 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:36 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:77 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:40 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a photo of the invoice. People like it better when they see a photo of " +"what they are paying for." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:44 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:37 +msgid "Photo size in bytes" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:47 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:40 +msgid "Photo width" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:50 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:43 +msgid "Photo height" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:53 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:46 +msgid "Pass True, if you require the user's full name to complete the order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:56 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:49 +msgid "Pass True, if you require the user's phone number to complete the order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:59 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:52 +msgid "Pass True, if you require the user's email to complete the order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:62 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:55 +msgid "" +"Pass True, if you require the user's shipping address to complete the " +"order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:65 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:61 +msgid "Pass True, if user's phone number should be sent to provider" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:68 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:64 +msgid "Pass True, if user's email address should be sent to provider" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:71 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:58 +msgid "Pass True, if the final price depends on the shipping method" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:74 +msgid "Created invoice link as String on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:75 +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:11 +#: telebot.async_telebot.AsyncTeleBot.get_file_url:7 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:1 +msgid "" +"Use this method to create new sticker set owned by a user. The bot will " +"be able to edit the created sticker set. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createnewstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:10 +msgid "" +"Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " +"animals). Can contain only English letters, digits and underscores. Must " +"begin with a letter, can't contain consecutive underscores and must end " +"in \"_by_\". is case insensitive. 1-64 " +"characters." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:15 +msgid "Sticker set title, 1-64 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:32 +msgid "" +"Pass True, if a set of mask stickers should be created. Deprecated since " +"Bot API 6.2, use sticker_type instead." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:36 +msgid "" +"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " +"emoji sticker sets can't be created via the Bot API at the moment. By " +"default, a regular sticker set is created." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:1 +msgid "" +"Use this method to decline a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#declinechatjoinrequest" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:1 +msgid "" +"Use this method to delete a chat photo. Photos can't be changed for " +"private chats. The bot must be an administrator in the chat for this to " +"work and must have the appropriate admin rights. Returns True on success." +" Note: In regular groups (non-supergroups), this method will only work if" +" the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:8 +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo:9 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:7 +msgid "" +"Int or Str: Unique identifier for the target chat or username of the " +"target channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:1 +msgid "" +"Use this method to delete a group sticker set from a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights. Use the field can_set_sticker_set optionally " +"returned in getChat requests to check if the bot can use this method. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletechatstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:1 +msgid "" +"Use this method to delete a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights, unless it is the creator of the " +"topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:10 +msgid "Identifier of the topic to delete" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_message:1 +msgid "" +"Use this method to delete a message, including service messages, with the" +" following limitations: - A message can only be deleted if it was sent " +"less than 48 hours ago. - A dice message in a private chat can only be " +"deleted if it was sent more than 24 hours ago. - Bots can delete outgoing" +" messages in private chats, groups, and supergroups. - Bots can delete " +"incoming messages in private chats. - Bots granted can_post_messages " +"permissions can delete outgoing messages in channels. - If the bot is an " +"administrator of a group, it can delete any message there. - If the bot " +"has can_delete_messages permission in a supergroup or a channel, it can " +"delete any message there. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_message:11 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_message:16 +msgid "Identifier of the message to delete" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:1 +msgid "" +"Use this method to delete the list of the bot's commands for the given " +"scope and user language. After deletion, higher level commands will be " +"shown to affected users. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletemycommands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:7 +#: telebot.async_telebot.AsyncTeleBot.get_my_commands:6 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:8 +msgid "" +"The scope of users for which the commands are relevant. Defaults to " +"BotCommandScopeDefault." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:11 +#: telebot.async_telebot.AsyncTeleBot.get_my_commands:10 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:12 +msgid "" +"A two-letter ISO 639-1 language code. If empty, commands will be applied " +"to all users from the given scope, for whose language there are no " +"dedicated commands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_state:1 +msgid "Delete the current state of a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:1 +msgid "" +"Use this method to delete a sticker from a set created by the bot. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletestickerfromset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:5 +msgid "File identifier of the sticker" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:1 +msgid "" +"Use this method to remove webhook integration if you decide to switch " +"back to getUpdates. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:6 +#: telebot.async_telebot.AsyncTeleBot.set_webhook:36 +msgid "Pass True to drop all pending updates, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:9 +msgid "Request connection timeout, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:1 +msgid "Downloads file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:3 +msgid "Path where the file should be downloaded." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:6 +msgid "bytes" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:7 +msgid ":obj:`bytes`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:1 +msgid "" +"Use this method to edit a non-primary invite link created by the bot. The" +" bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editchatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:13 +msgid "The invite link to edit" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:1 +msgid "" +"Use this method to edit name and icon of a topic in a forum supergroup " +"chat. The bot must be an administrator in the chat for this to work and " +"must have can_manage_topics administrator rights, unless it is the " +"creator of the topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:5 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:10 +msgid "Identifier of the topic to edit" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:13 +msgid "New name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:16 +msgid "" +"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " +"must be exactly 1 character long" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:1 +msgid "Use this method to edit captions of messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagecaption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:5 +msgid "New caption of the message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:8 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:11 +msgid "Required if inline_message_id is not specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:14 +msgid "" +"Required if inline_message_id is not specified. Identifier of the inline " +"message." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:17 +msgid "New caption of the message, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:20 +msgid "" +"A JSON-serialized array of objects that describe how the caption should " +"be parsed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:23 +#: telebot.async_telebot.AsyncTeleBot.edit_message_media:19 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:14 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:26 +msgid "A JSON-serialized object for an inline keyboard." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:26 +#: telebot.async_telebot.AsyncTeleBot.edit_message_media:22 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:17 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:29 +msgid "" +"On success, if edited message is sent by the bot, the edited Message is " +"returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:27 +msgid ":obj:`types.Message` | :obj:`bool`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:3 +msgid "" +"Use this method to edit live location messages. A location can be edited " +"until its live_period expires or editing is explicitly" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:2 +msgid "" +"disabled by a call to stopMessageLiveLocation. On success, if the edited " +"message is not an inline message, the edited Message is returned, " +"otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagelivelocation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:7 +msgid "Latitude of new location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:10 +msgid "Longitude of new location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:16 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" to edit" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:19 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:15 +msgid "A JSON-serialized object for a new inline keyboard." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:26 +#: telebot.async_telebot.AsyncTeleBot.edit_message_media:16 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:11 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:14 +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:19 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:20 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:29 +#: telebot.async_telebot.AsyncTeleBot.send_location:31 +msgid "The radius of uncertainty for the location, measured in meters; 0-1500" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:32 +msgid "" +"Direction in which the user is moving, in degrees. Must be between 1 and " +"360 if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:35 +msgid "" +"The maximum distance for proximity alerts about approaching another chat " +"member, in meters. Must be between 1 and 100000 if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:38 +msgid "" +"On success, if the edited message is not an inline message, the edited " +"Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:39 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:23 +msgid ":class:`telebot.types.Message` or bool" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:1 +msgid "" +"Use this method to edit animation, audio, document, photo, or video " +"messages. If a message is a part of a message album, then it can be " +"edited only to a photo or a video. Otherwise, message type can be changed" +" arbitrarily. When inline message is edited, new file can't be uploaded. " +"Use previously uploaded file via its file_id or specify a URL." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagemedia" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:8 +msgid "A JSON-serialized object for a new media content of the message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:10 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:5 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:8 +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:13 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:14 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel (in the format " +"@channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:13 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:8 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:11 +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:16 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:17 +msgid "" +"Required if inline_message_id is not specified. Identifier of the sent " +"message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:23 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:18 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:30 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:27 +msgid ":obj:`types.Message` or :obj:`bool`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:1 +msgid "Use this method to edit only the reply markup of messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagereplymarkup" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:1 +msgid "Use this method to edit text and game messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#editmessagetext" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:5 +msgid "New text of the message, 1-4096 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:17 +#: telebot.async_telebot.AsyncTeleBot.send_message:15 +msgid "Mode for parsing entities in the message text." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:20 +msgid "" +"List of special entities that appear in the message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:23 +#: telebot.async_telebot.AsyncTeleBot.send_message:21 +msgid "Disables link previews for links in this message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:1 +msgid "" +"Handles new version of a channel post that is known to the bot and was " +"edited. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:1 +msgid "Handles new version of a message that is known to the bot and was edited." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:3 +msgid "" +"As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:17 +#: telebot.async_telebot.AsyncTeleBot.message_handler:47 +msgid "list of chat types" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:1 +msgid "Enable saving states (by default saving disabled)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:4 +msgid "" +"It is recommended to pass a " +":class:`~telebot.asyncio_storage.StatePickleStorage` instance as " +"state_storage to TeleBot class." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:7 +msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:1 +msgid "" +"Use this method to export an invite link to a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#exportchatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:10 +msgid "exported invite link as String on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:1 +msgid "Use this method to forward messages of any kind." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:17 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:23 +#: telebot.async_telebot.AsyncTeleBot.send_message:43 +msgid "" +"Unique identifier for the target message thread (topic) of the forum; for" +" forum supergroups only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:1 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:6 +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators:7 +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count:5 +#: telebot.async_telebot.AsyncTeleBot.leave_chat:5 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:9 +msgid "Chat information" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:10 +msgid ":class:`telebot.types.Chat`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:1 +msgid "" +"Use this method to get a list of administrators in a chat. On success, " +"returns an Array of ChatMember objects that contains information about " +"all chat administrators except other bots." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatadministrators" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:9 +msgid "List made of ChatMember objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:10 +msgid ":obj:`list` of :class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:1 +msgid "" +"Use this method to get information about a member of a chat. Returns a " +"ChatMember object on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:11 +msgid "Returns ChatMember object on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:12 +msgid ":class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:1 +msgid "Use this method to get the number of members in a chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatmembercount" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:8 +msgid "Number of members in the chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:9 +msgid ":obj:`int`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:1 +msgid "" +"Use this method to get the current value of the bot's menu button in a " +"private chat, or the default menu button. Returns MenuButton on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:5 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#getchatmenubutton" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:11 +msgid "types.MenuButton" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:12 +msgid ":class:`telebot.types.MenuButton`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:1 +msgid "" +"Use this method to get information about custom emoji stickers by their " +"identifiers. Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:4 +msgid "" +"List of custom emoji identifiers. At most 200 custom emoji identifiers " +"can be specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:7 +msgid "Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:8 +msgid ":obj:`list` of :class:`telebot.types.Sticker`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:1 +msgid "" +"Use this method to get basic info about a file and prepare it for " +"downloading. For the moment, bots can download files of up to 20MB in " +"size. On success, a File object is returned. It is guaranteed that the " +"link will be valid for at least 1 hour. When the link expires, a new one " +"can be requested by calling get_file again." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:9 +msgid "File identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:12 +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file:14 +msgid ":class:`telebot.types.File`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file_url:1 +msgid "Get a valid URL for downloading a file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file_url:3 +msgid "File identifier to get download URL for." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file_url:6 +msgid "URL for downloading the file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:1 +msgid "" +"Use this method to get custom emoji stickers, which can be used as a " +"forum topic icon by any user. Requires no parameters. Returns an Array of" +" Sticker objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getforumtopiciconstickers" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:6 +msgid "On success, a list of StickerSet objects is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:7 +msgid "List[:class:`telebot.types.StickerSet`]" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:1 +msgid "" +"Use this method to get data for high score tables. Will return the score " +"of the specified user and several of their neighbors in a game. On " +"success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:4 +msgid "" +"This method will currently return scores for the target user, plus two of" +" their closest neighbors on each side. Will also return the top three " +"users if the user and their neighbors are not among them. Please note " +"that this behavior is subject to change." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getgamehighscores" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:10 +#: telebot.async_telebot.AsyncTeleBot.retrieve_data:3 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:9 +msgid "User identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:22 +msgid "On success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_me:1 +msgid "Returns basic information about the bot in form of a User object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_me:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getme" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:1 +msgid "" +"Use this method to get the current list of the bot's commands. Returns " +"List of BotCommand on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:15 +msgid "List of BotCommand on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:16 +msgid ":obj:`list` of :class:`telebot.types.BotCommand`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:1 +msgid "" +"Use this method to get the current default administrator rights of the " +"bot. Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmydefaultadministratorrights" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:6 +msgid "" +"Pass True to get the default administrator rights of the bot in channels." +" Otherwise, the default administrator rights of the bot for groups and " +"supergroups will be returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:9 +msgid "Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:10 +msgid ":class:`telebot.types.ChatAdministratorRights`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_state:1 +msgid "" +"Gets current state of a user. Not recommended to use this method. But it " +"is ok for debugging." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_state:10 +msgid "state of a user" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_state:11 +msgid ":obj:`int` or :obj:`str` or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:1 +msgid "" +"Use this method to get a sticker set. On success, a StickerSet object is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:8 +msgid "On success, a StickerSet object is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:9 +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:14 +msgid ":class:`telebot.types.StickerSet`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:1 +msgid "" +"Use this method to receive incoming updates using long polling (wiki). An" +" Array of Update objects is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:6 +msgid "" +"Identifier of the first update to be returned. Must be greater by one " +"than the highest among the identifiers of previously received updates. By" +" default, updates starting with the earliest unconfirmed update are " +"returned. An update is considered confirmed as soon as getUpdates is " +"called with an offset higher than its update_id. The negative offset can " +"be specified to retrieve updates starting from -offset update from the " +"end of the updates queue. All previous updates will forgotten." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:12 +msgid "" +"Limits the number of updates to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:15 +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info:6 +#: telebot.async_telebot.AsyncTeleBot.polling:26 +msgid "Request connection timeout" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:18 +msgid "Array of string. List the types of updates you want your bot to receive." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:21 +msgid "Timeout in seconds for long polling." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:24 +msgid "An Array of Update objects is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:25 +msgid ":obj:`list` of :class:`telebot.types.Update`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:1 +msgid "" +"Use this method to get a list of profile pictures for a user. Returns a " +":class:`telebot.types.UserProfilePhotos` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserprofilephotos" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:9 +msgid "" +"Sequential number of the first photo to be returned. By default, all " +"photos are returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:12 +msgid "" +"Limits the number of photos to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:15 +msgid "" +"`UserProfilePhotos " +"`_" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:16 +msgid ":class:`telebot.types.UserProfilePhotos`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:1 +msgid "" +"Use this method to get current webhook status. Requires no parameters. On" +" success, returns a WebhookInfo object. If the bot is using getUpdates, " +"will return an object with the url field empty." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:9 +msgid "On success, returns a WebhookInfo object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:10 +msgid ":class:`telebot.types.WebhookInfo`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:1 +msgid "" +"Wrap polling with infinite loop and exception handling to avoid bot stops" +" polling." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:4 +#: telebot.async_telebot.AsyncTeleBot.polling:15 +msgid "Install watchdog and psutil before using restart_on_change option." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:6 +#: telebot.async_telebot.AsyncTeleBot.polling:29 +msgid "Timeout in seconds for get_updates(Defaults to None)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:9 +#: telebot.async_telebot.AsyncTeleBot.polling:20 +msgid "skip old updates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:12 +msgid "Aiohttp's request timeout. Defaults to 5 minutes(aiohttp.ClientTimeout)." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:15 +msgid "" +"Custom logging level for infinity_polling logging. Use logger levels from" +" logging as a value. None/NOTSET = no error logging" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:19 +#: telebot.async_telebot.AsyncTeleBot.polling:32 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. 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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:19 +#: telebot.async_telebot.AsyncTeleBot.polling:32 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:25 +#: telebot.async_telebot.AsyncTeleBot.polling:38 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:29 +msgid "Restart a file on file(s) change. Defaults to False" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:32 +#: telebot.async_telebot.AsyncTeleBot.polling:48 +msgid "Path to watch for changes. Defaults to current directory" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.inline_handler:1 +msgid "" +"Handles new incoming inline query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.InlineQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.kick_chat_member:1 +msgid "This function is deprecated. Use `ban_chat_member` instead" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.leave_chat:1 +msgid "" +"Use this method for your bot to leave a group, supergroup or channel. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.leave_chat:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#leavechat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.log_out:1 +msgid "" +"Use this method to log out from the cloud Bot API server before launching" +" the bot locally. You MUST log out the bot before running it locally, " +"otherwise there is no guarantee that the bot will receive updates. After " +"a successful call, you can immediately log in on a local server, but will" +" not be able to log in back to the cloud Bot API server for 10 minutes. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.log_out:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:1 +msgid "" +"Handles ew incoming message of any kind - text, photo, sticker, etc. As a" +" parameter to the decorator function, it passes " +":class:`telebot.types.Message` object. All message handlers are tested in" +" the order they were added." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:5 +msgid "Example:" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:7 +msgid "Usage of message_handler" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:40 +msgid "" +"Optional lambda function. The lambda receives the message to test as the " +"first parameter. It must return True if the command should handle the " +"message." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:52 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:23 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:13 +msgid "decorated function" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:1 +msgid "" +"Handles update in a status of a bot. For private chats, this update is " +"received only when the bot is blocked or unblocked by the user. As a " +"parameter to the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:1 +msgid "" +"Use this method to pin a message in a supergroup. The bot must be an " +"administrator in the chat for this to work and must have the appropriate " +"admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:11 +msgid "Identifier of a message to pin" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:14 +msgid "" +"Pass True, if it is not necessary to send a notification to all group " +"members about the new pinned message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.poll_answer_handler:1 +msgid "" +"Handles change of user's answer in a non-anonymous poll(when user changes" +" the vote). Bots receive new votes only in polls that were sent by the " +"bot itself. As a parameter to the decorator function, it passes " +":class:`telebot.types.PollAnswer` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.poll_handler:1 +msgid "" +"Handles new state of a poll. Bots receive only updates about stopped " +"polls and polls, which are sent by the bot As a parameter to the " +"decorator function, it passes :class:`telebot.types.Poll` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:1 +msgid "" +"Runs bot in long-polling mode in a main loop. This allows the bot to " +"retrieve Updates automagically and notify listeners and message handlers " +"accordingly." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:4 +msgid "Warning: Do not call this function more than once!" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:6 +msgid "Always gets updates." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:10 +msgid "" +"Set non_stop=True if you want your bot to continue receiving updates if " +"there is an error." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:17 +msgid "Do not stop polling when an ApiException occurs." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:23 +msgid "Delay between two update retrivals" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:42 +msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:45 +msgid "Restart a file on file(s) change. Defaults to False." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:1 +msgid "" +"New incoming pre-checkout query. Contains full information about " +"checkout. As a parameter to the decorator function, it passes " +":class:`telebot.types.PreCheckoutQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.process_new_updates:1 +msgid "" +"Process new updates. Just pass list of updates - each update should be " +"instance of Update object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.process_new_updates:5 +msgid "list of updates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:1 +msgid "" +"Use this method to promote or demote a user in a supergroup or a channel." +" The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Pass False for all boolean parameters " +"to demote a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#promotechatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:7 +msgid "" +"Unique identifier for the target chat or username of the target channel (" +" in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:14 +msgid "" +"Pass True, if the administrator can change chat title, photo and other " +"settings" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:17 +msgid "Pass True, if the administrator can create channel posts, channels only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:20 +msgid "" +"Pass True, if the administrator can edit messages of other users, " +"channels only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:23 +msgid "Pass True, if the administrator can delete messages of other users" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:26 +msgid "Pass True, if the administrator can invite new users to the chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:29 +msgid "Pass True, if the administrator can restrict, ban or unban chat members" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:32 +msgid "Pass True, if the administrator can pin messages, supergroups only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:35 +msgid "" +"Pass True, if the administrator can add new administrators with a subset " +"of his own privileges or demote administrators that he has promoted, " +"directly or indirectly (promoted by administrators that were appointed by" +" him)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:40 +msgid "Pass True, if the administrator's presence in the chat is hidden" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:43 +msgid "" +"Pass True, if the administrator can access the chat event log, chat " +"statistics, message statistics in channels, see channel members, see " +"anonymous administrators in supergroups and ignore slow mode. Implied by " +"any other administrator privilege" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:49 +msgid "" +"Pass True, if the administrator can manage voice chats For now, bots can " +"use this privilege only for passing to other administrators." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:53 +msgid "Deprecated, use can_manage_video_chats." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:56 +msgid "" +"Pass True if the user is allowed to create, rename, close, and reopen " +"forum topics, supergroups only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:1 +msgid "Registers callback query handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:3 +msgid "function to be called" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:18 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:18 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:8 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:9 +msgid "" +"True if you need to pass TeleBot instance to handler(useful for " +"separating handlers into different files)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:1 +msgid "Registers channel post message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:9 +msgid "list of commands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:12 +msgid "Regular expression" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:1 +msgid "Registers chat join request handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:1 +msgid "Registers chat member handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:14 +msgid ":return:None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:1 +msgid "Registers chosen inline handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:1 +msgid "Registers edited channel post message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:1 +msgid "Registers edited message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:18 +msgid "True for private chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_inline_handler:1 +msgid "Registers inline handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_message_handler:1 +msgid "Registers message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_message_handler:18 +msgid "List of chat types" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:1 +msgid "Registers my chat member handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:1 +msgid "Registers poll answer handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_poll_handler:1 +msgid "Registers poll handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:1 +msgid "Registers pre-checkout request handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:1 +msgid "Registers shipping query handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.remove_webhook:1 +msgid "Alternative for delete_webhook but uses set_webhook" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:1 +msgid "" +"Use this method to reopen a closed topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopenforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:10 +msgid "Identifier of the topic to reopen" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:1 +msgid "" +"Convenience function for `send_message(message.chat.id, text, " +"reply_to_message_id=message.message_id, **kwargs)`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:3 +msgid "Instance of :class:`telebot.types.Message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:6 +msgid "Text of the message." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:9 +msgid "" +"Additional keyword arguments which are passed to " +":meth:`telebot.TeleBot.send_message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reset_data:1 +msgid "Reset data for a user in chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:1 +msgid "" +"Use this method to restrict a user in a supergroup. The bot must be an " +"administrator in the supergroup for this to work and must have the " +"appropriate admin rights. Pass True for all boolean parameters to lift " +"restrictions from a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#restrictchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 +msgid "" +"Date when restrictions will be lifted for the user, unix time. If user is" +" restricted for more than 366 days or less than 30 seconds from the " +"current time, they are considered to be restricted forever" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:19 +msgid "" +"Pass True, if the user can send text messages, contacts, locations and " +"venues" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:22 +msgid "" +"Pass True, if the user can send audios, documents, photos, videos, video " +"notes and voice notes, implies can_send_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:26 +msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:29 +msgid "" +"Pass True, if the user can send animations, games, stickers and use " +"inline bots, implies can_send_media_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 +msgid "" +"Pass True, if the user may add web page previews to their messages, " +"implies can_send_media_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:36 +msgid "" +"Pass True, if the user is allowed to change the chat title, photo and " +"other settings. Ignored in public supergroups" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:40 +msgid "" +"Pass True, if the user is allowed to invite new users to the chat, " +"implies can_invite_users" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:44 +msgid "" +"Pass True, if the user is allowed to pin messages. Ignored in public " +"supergroups" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:47 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:14 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member:19 +msgid "True on success" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.retrieve_data:1 +msgid "Returns context manager with data for a user in chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.retrieve_data:6 +msgid "Chat's unique identifier, defaults to user_id" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.retrieve_data:9 +msgid "Context manager with data for a user in chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:1 +msgid "" +"Use this method to revoke an invite link created by the bot. Note: If the" +" primary link is revoked, a new link is automatically generated The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#revokechatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:11 +msgid "The invite link to revoke" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:1 +msgid "This class sets webhooks and listens to a given url and port." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:3 +msgid "IP address to listen to. Defaults to 0.0.0.0" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:5 +msgid "A port which will be used to listen to webhooks." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:6 +msgid "Path to the webhook. Defaults to /token" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:7 +msgid "Path to the certificate file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:8 +msgid "Path to the certificate key file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:9 +msgid "Webhook URL." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:10 +msgid "" +"Maximum allowed number of simultaneous HTTPS connections to the webhook " +"for update delivery, 1-100. Defaults to 40. Use lower values to limit the" +" load on your bot's server, and higher values to increase your bot's " +"throughput." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:11 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all updates " +"regardless of type (default). If not specified, the previous setting will" +" be used." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:12 +msgid "" +"The fixed IP address which will be used to send webhook requests instead " +"of the IP address resolved through DNS" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:13 +msgid "Pass True to drop all pending updates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:14 +msgid "Integer. Request connection timeout" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:15 +msgid "Secret token to be used to verify the webhook request." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:1 +msgid "" +"Use this method to send animation files (GIF or H.264/MPEG-4 AVC video " +"without sound). On success, the sent Message is returned. Bots can " +"currently send animation files of up to 50 MB in size, this limit may be " +"changed in the future." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:9 +msgid "" +"Animation to send. Pass a file_id as String to send an animation that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an animation from the Internet, or upload a " +"new animation using multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:13 +msgid "Duration of sent animation in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:16 +msgid "Animation width" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:19 +msgid "Animation height" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:22 +#: telebot.async_telebot.AsyncTeleBot.send_video:20 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:33 +msgid "" +"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 ." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:28 +msgid "" +"Animation caption (may also be used when resending animation by file_id)," +" 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:31 +msgid "Mode for parsing entities in the animation caption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:51 +#: telebot.async_telebot.AsyncTeleBot.send_video:29 +msgid "" +"List of special entities that appear in the caption, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:57 +#: telebot.async_telebot.AsyncTeleBot.send_video:58 +msgid "Identifier of a message thread, in which the video will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:1 +msgid "" +"Use this method to send audio files, if you want Telegram clients to " +"display them in the music player. Your audio must be in the .MP3 or .M4A " +"format. On success, the sent Message is returned. Bots can currently send" +" audio files of up to 50 MB in size, this limit may be changed in the " +"future." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:5 +msgid "For sending voice messages, use the send_voice method instead." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendaudio" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:12 +msgid "" +"Audio file to send. Pass a file_id as String to send an audio file that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an audio file from the Internet, or upload a " +"new one using multipart/form-data. Audio must be in the .MP3 or .M4A " +"format." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:17 +msgid "Audio caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:20 +msgid "Duration of the audio in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:23 +msgid "Performer" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:26 +msgid "Track name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:36 +msgid "" +"Mode for parsing entities in the audio caption. See formatting options " +"for more details." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:45 +msgid "" +"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 " +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:51 +#: telebot.async_telebot.AsyncTeleBot.send_document:35 +#: telebot.async_telebot.AsyncTeleBot.send_photo:19 +#: telebot.async_telebot.AsyncTeleBot.send_voice:37 +msgid "" +"A JSON-serialized list of special entities that appear in the caption, " +"which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:1 +msgid "" +"Use this method when you need to tell the user that something is " +"happening on the bot's side. The status is set for 5 seconds or less " +"(when a message arrives from your bot, Telegram clients clear its typing " +"status). Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:5 +msgid "" +"Example: The ImageBot needs some time to process a request and upload the" +" image. Instead of sending a text message along the lines of “Retrieving " +"image, please wait…”, the bot may use sendChatAction with action = " +"upload_photo. The user will see a “sending photo” status for the bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:10 +#: telebot.async_telebot.AsyncTeleBot.send_contact:5 +#: telebot.async_telebot.AsyncTeleBot.send_poll:6 +#: telebot.async_telebot.AsyncTeleBot.send_venue:5 +#: telebot.async_telebot.AsyncTeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:13 +msgid "" +"Type of action to broadcast. Choose one, depending on what the user is " +"about to receive: typing for text messages, upload_photo for photos, " +"record_video or upload_video for videos, record_voice or upload_voice for" +" voice notes, upload_document for general files, choose_sticker for " +"stickers, find_location for location data, record_video_note or " +"upload_video_note for video notes." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:1 +msgid "" +"Use this method to send phone contacts. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:8 +msgid "Contact's phone number" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:11 +msgid "Contact's first name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:14 +msgid "Contact's last name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:17 +msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:34 +#: telebot.async_telebot.AsyncTeleBot.send_game:23 +#: telebot.async_telebot.AsyncTeleBot.send_venue:41 +msgid "" +"Pass True, if the message should be sent even if one of the specified " +"replied-to messages is not found." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:41 +#: telebot.async_telebot.AsyncTeleBot.send_venue:54 +msgid "The thread to which the message will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:1 +msgid "" +"Use this method to send an animated emoji that will display a random " +"value. On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:8 +msgid "" +"Emoji on which the dice throw animation is based. Currently, must be one " +"of “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, or “🎰”. Dice can have values 1-6 for “🎲”, “🎯”" +" and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " +"to “🎲”" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:29 +msgid "Protects the contents of the sent message from forwarding" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:32 +msgid "" +"The identifier of a message thread, unique within the chat to which the " +"message with the thread identifier belongs" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:1 +msgid "Use this method to send general files." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddocument" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:8 +msgid "" +"(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" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:15 +msgid "" +"Document caption (may also be used when resending documents by file_id), " +"0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:23 +msgid "Mode for parsing entities in the document caption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:32 +msgid "" +"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 " +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:41 +msgid "" +"allows to define file name that will be visible in the Telegram instead " +"of original file name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:44 +msgid "" +"Disables automatic server-side content type detection for files uploaded " +"using multipart/form-data" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:47 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:33 +#: telebot.async_telebot.AsyncTeleBot.send_video:55 +msgid "function typo miss compatibility: do not use it" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:1 +msgid "Used to send the game." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendgame" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:8 +msgid "" +"Short name of the game, serves as the unique identifier for the game. Set" +" up your games via @BotFather." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:20 +msgid "Timeout in seconds for waiting for a response from the bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:26 +msgid "" +"Pass True, if content of the message needs to be protected from being " +"viewed by the bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:29 +msgid "Identifier of the thread to which the message will be sent." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:33 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:102 +#: telebot.async_telebot.AsyncTeleBot.send_poll:71 +msgid ":obj:`types.Message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:1 +msgid "Sends invoice." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendinvoice" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:5 +msgid "Unique identifier for the target private chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:29 +msgid "" +"Unique deep-linking parameter that can be used to generate this invoice " +"when used as a start parameter" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:33 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a marketing image for a service. People like it better when they see what" +" they are paying for." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:73 +msgid "" +"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" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:81 +#: telebot.async_telebot.AsyncTeleBot.set_webhook:39 +msgid "Timeout of a request, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:98 +msgid "" +"The identifier of a message thread, in which the invoice message will be " +"sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:1 +msgid "" +"Use this method to send point on the map. On success, the sent Message is" +" returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendlocation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:8 +msgid "Latitude of the location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:11 +msgid "Longitude of the location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:14 +msgid "" +"Period in seconds for which the location will be updated (see Live " +"Locations, should be between 60 and 86400." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:34 +msgid "" +"For live locations, a direction in which the user is moving, in degrees. " +"Must be between 1 and 360 if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:37 +msgid "" +"For live locations, a maximum distance for proximity alerts about " +"approaching another chat member, in meters. Must be between 1 and 100000 " +"if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:1 +msgid "" +"Use this method to send a group of photos, videos, documents or audios as" +" an album. Documents and audio files can be only grouped in an album with" +" messages of the same type. On success, an array of Messages that were " +"sent is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:9 +msgid "" +"A JSON-serialized array describing messages to be sent, must include 2-10" +" items" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:12 +msgid "" +"Sends the messages silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:27 +msgid "Identifier of a message thread, in which the messages will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:30 +msgid "On success, an array of Messages that were sent is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:1 +msgid "Use this method to send text messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:3 +msgid "" +"Warning: Do not send more than about 4096 characters each message, " +"otherwise you'll risk an HTTP 414 error. If you must send more than 4096 " +"characters, use the `split_string` or `smart_split` function in util.py." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:12 +msgid "Text of the message to be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:18 +msgid "" +"List of special entities that appear in message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:27 +msgid "" +"If True, the message content will be hidden for all users except for the " +"target user" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:1 +msgid "Use this method to send photos. On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:8 +msgid "" +"Photo to send. Pass a file_id as String to send a photo that exists on " +"the Telegram servers (recommended), pass an HTTP URL as a String for " +"Telegram to get a photo from the Internet, or upload a new photo using " +"multipart/form-data. The photo must be at most 10 MB in size. The photo's" +" width and height must not exceed 10000 in total. Width and height ratio " +"must be at most 20." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:13 +msgid "" +"Photo caption (may also be used when resending photos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:16 +msgid "Mode for parsing entities in the photo caption." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:1 +msgid "" +"Use this method to send a native poll. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:9 +msgid "Poll question, 1-300 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:12 +msgid "" +"A JSON-serialized list of answer options, 2-10 strings 1-100 characters " +"each" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:15 +msgid "True, if the poll needs to be anonymous, defaults to True" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:18 +msgid "Poll type, “quiz” or “regular”, defaults to “regular”" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:21 +msgid "" +"True, if the poll allows multiple answers, ignored for polls in quiz " +"mode, defaults to False" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:24 +msgid "" +"0-based identifier of the correct answer option. Available only for polls" +" in quiz mode, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:28 +msgid "" +"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" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:32 +msgid "" +"Mode for parsing entities in the explanation. See formatting options for " +"more details." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:35 +msgid "" +"Amount of time in seconds the poll will be active after creation, 5-600. " +"Can't be used together with close_date." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:38 +msgid "Point in time (Unix timestamp) when the poll will be automatically closed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:41 +msgid "" +"Pass True, if the poll needs to be immediately closed. This can be useful" +" for poll preview." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:50 +msgid "Pass True, if the poll allows multiple options to be voted simultaneously." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:57 +msgid "Timeout in seconds for waiting for a response from the user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:60 +msgid "" +"A JSON-serialized list of special entities that appear in the " +"explanation, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:67 +msgid "The identifier of a message thread, in which the poll will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:1 +msgid "" +"Use this method to send static .WEBP, animated .TGS, or video .WEBM " +"stickers. On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendsticker" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:9 +msgid "" +"Sticker 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 .webp file from the Internet, or upload a new one using" +" multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:21 +msgid "to disable the notification" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:1 +msgid "" +"Use this method to send information about a venue. On success, the sent " +"Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:8 +msgid "Latitude of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:11 +msgid "Longitude of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:14 +msgid "Name of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:17 +msgid "Address of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:20 +msgid "Foursquare identifier of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:23 +msgid "" +"Foursquare type of the venue, if known. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:45 +msgid "Google Places identifier of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:48 +msgid "Google Places type of the venue." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:1 +msgid "" +"Use this method to send video files, Telegram clients support mp4 videos " +"(other formats may be sent as Document)." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:8 +msgid "" +"Video to send. You can either pass a file_id as String to resend a video " +"that is already on the Telegram servers, or upload a new video file using" +" multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:11 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:13 +msgid "Duration of sent video in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:14 +msgid "Video width" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:17 +msgid "Video height" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:23 +msgid "" +"Video caption (may also be used when resending videos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:26 +msgid "Mode for parsing entities in the video caption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:32 +msgid "Pass True, if the uploaded video is suitable for streaming" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:1 +msgid "" +"As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " +"to 1 minute long. Use this method to send video messages. On success, the" +" sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:9 +msgid "" +"Video note to send. Pass a file_id as String to send a video note that " +"exists on the Telegram servers (recommended) or upload a new video using " +"multipart/form-data. Sending video notes by a URL is currently " +"unsupported" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:16 +msgid "Video width and height, i.e. diameter of the video message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:45 +msgid "Identifier of a message thread, in which the video note will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:10 +msgid "" +"Audio 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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:14 +msgid "Voice message caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:17 +msgid "Duration of the voice message in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:28 +msgid "" +"Mode for parsing entities in the voice message caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:1 +msgid "" +"Use this method to set a custom title for an administrator in a " +"supergroup promoted by the bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatadministratorcustomtitle" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:13 +msgid "" +"New custom title for the administrator; 0-16 characters, emoji are not " +"allowed" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_description:1 +msgid "" +"Use this method to change the description of a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_description:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatdescription" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_description:10 +msgid "Str: New chat description, 0-255 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:1 +msgid "" +"Use this method to change the bot's menu button in a private chat, or the" +" default menu button. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatmenubutton" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be changed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:11 +msgid "" +"A JSON-serialized object for the new bot's menu button. Defaults to " +"MenuButtonDefault" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:1 +msgid "" +"Use this method to set default chat permissions for all members. The bot " +"must be an administrator in the group or a supergroup for this to work " +"and must have the can_restrict_members admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatpermissions" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:11 +msgid "New default chat permissions" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:1 +msgid "" +"Use this method to set a new profile photo for the chat. Photos can't be " +"changed for private chats. The bot must be an administrator in the chat " +"for this to work and must have the appropriate admin rights. Returns True" +" on success. Note: In regular groups (non-supergroups), this method will " +"only work if the ‘All Members Are Admins’ setting is off in the target " +"group." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:13 +msgid "InputFile: New chat photo, uploaded using multipart/form-data" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:1 +msgid "" +"Use this method to set a new group sticker set for a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. Use the field can_set_sticker_set " +"optionally returned in getChat requests to check if the bot can use this " +"method. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:10 +msgid "Name of the sticker set to be set as the group sticker set" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:13 +msgid "StickerSet object" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_title:1 +msgid "" +"Use this method to change the title of a chat. Titles can't be changed " +"for private chats. The bot must be an administrator in the chat for this " +"to work and must have the appropriate admin rights. Returns True on " +"success. Note: In regular groups (non-supergroups), this method will only" +" work if the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_title:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_title:13 +msgid "New chat title, 1-255 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:1 +msgid "Sets the value of points in the game to a specific user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setgamescore" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:8 +msgid "New score, must be non-negative" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:11 +msgid "" +"Pass True, if the high score is allowed to decrease. This can be useful " +"when fixing mistakes or banning cheaters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:23 +msgid "" +"Pass True, if the game message should not be automatically edited to " +"include the current scoreboard" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:26 +msgid "" +"On success, if the message was sent by the bot, returns the edited " +"Message, otherwise returns True." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_commands:1 +msgid "Use this method to change the list of the bot's commands." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_commands:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmycommands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_commands:5 +msgid "List of BotCommand. At most 100 commands can be specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:1 +msgid "" +"Use this method to change the default administrator rights requested by " +"the bot when it's added as an administrator to groups or channels. These " +"rights will be suggested to users, but they are are free to modify the " +"list before adding the bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:7 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmydefaultadministratorrights" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:9 +msgid "" +"A JSON-serialized object describing new default administrator rights. If " +"not specified, the default administrator rights will be cleared." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:13 +msgid "" +"Pass True to change the default administrator rights of the bot in " +"channels. Otherwise, the default administrator rights of the bot for " +"groups and supergroups will be changed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_state:1 +msgid "Sets a new state of a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_state:5 +msgid "" +"You should set both user id and chat id in order to set state for a user " +"in a chat. Otherwise, if you only set user_id, chat_id will equal to " +"user_id, this means that state will be set for the user in his private " +"chat with a bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_state:12 +msgid "new state. can be string, integer, or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:1 +msgid "" +"Use this method to move a sticker in a set created by the bot to a " +"specific position . Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickerpositioninset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:8 +msgid "New sticker position in the set, zero-based" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:1 +msgid "" +"Use this method to set the thumbnail of a sticker set. Animated " +"thumbnails can be set for animated sticker sets only. Returns True on " +"success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickersetthumb" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_update_listener:1 +msgid "Update listener is a function that gets any update." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_update_listener:3 +msgid "function that should get update." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_update_listener:6 +msgid "Example on asynchronous update listeners." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:1 +msgid "" +"Use this method to specify a URL and receive incoming updates via an " +"outgoing webhook. Whenever there is an update for the bot, we will send " +"an HTTPS POST request to the specified URL, containing a JSON-serialized " +"Update. In case of an unsuccessful request, we will give up after a " +"reasonable amount of attempts. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:6 +msgid "" +"If you'd like to make sure that the webhook was set by you, you can " +"specify secret data in the parameter secret_token. If specified, the " +"request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the " +"secret token as content." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:9 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#setwebhook" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:11 +msgid "" +"HTTPS URL to send updates to. Use an empty string to remove webhook " +"integration, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:14 +msgid "" +"Upload your public key certificate so that the root certificate in use " +"can be checked, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:17 +msgid "" +"The maximum allowed number of simultaneous HTTPS connections to the " +"webhook for update delivery, 1-100. Defaults to 40. Use lower values to " +"limit the load on your bot's server, and higher values to increase your " +"bot's throughput, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. Please note that this parameter doesn't affect " +"updates created before the call to the setWebhook, so unwanted updates " +"may be received for a short period of time. Defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:27 +msgid "" +"Please note that this parameter doesn't affect updates created before the" +" call to the setWebhook, so unwanted updates may be received for a short " +"period of time. Defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:32 +msgid "" +"The fixed IP address which will be used to send webhook requests instead " +"of the IP address resolved through DNS, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:42 +msgid "" +"A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” " +"in every webhook request, 1-256 characters. Only characters A-Z, a-z, " +"0-9, _ and - are allowed. The header is useful to ensure that the request" +" comes from a webhook set by you. Defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:47 +msgid ":obj:`bool` if the request was successful." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.setup_middleware:1 +msgid "Setup middleware." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.setup_middleware:5 +msgid "" +"Take a look at the " +":class:`telebot.asyncio_handler_backends.BaseMiddleware` section for " +"more." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.setup_middleware:7 +msgid "Middleware-class." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.shipping_query_handler:1 +msgid "" +"Handles new incoming shipping query. Only for invoices with flexible " +"price. As a parameter to the decorator function, it passes " +":class:`telebot.types.ShippingQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.skip_updates:1 +msgid "Skip existing updates. Only last update will remain on server." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:1 +msgid "" +"Use this method to stop updating a live location message before " +"live_period expires. On success, if the message is not an inline message," +" the edited Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#stopmessagelivelocation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:9 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" with live location to stop" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:12 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message with live location to stop" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:22 +msgid "" +"On success, if the message is not an inline message, the edited Message " +"is returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:1 +msgid "" +"Use this method to stop a poll which was sent by the bot. On success, the" +" stopped Poll is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:8 +msgid "Identifier of the original message with the poll" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:11 +msgid "A JSON-serialized object for a new message markup." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:14 +msgid "On success, the stopped Poll is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:15 +msgid ":obj:`types.Poll`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:1 +msgid "" +"Use this method to unban a previously kicked user in a supergroup or " +"channel. The user will not return to the group or channel automatically, " +"but will be able to join via link, etc. The bot must be an administrator " +"for this to work. By default, this method guarantees that after the call " +"the user is not a member of the chat, but will be able to join it. So if " +"the user is a member of the chat they will also be removed from the chat." +" If you don't want this, use the parameter only_if_banned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:9 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @username)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:16 +msgid "Do nothing if the user is not banned" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:1 +msgid "" +"Use this method to unban a previously banned channel chat in a supergroup" +" or channel. The bot must be an administrator for this to work and must " +"have the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unbanchatsenderchat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:11 +msgid "Unique identifier of the target sender chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:1 +msgid "" +"Use this method to unpin a all pinned messages in a supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallchatmessages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:1 +msgid "" +"Use this method to clear the list of pinned messages in a forum topic. " +"The bot must be an administrator in the chat for this to work and must " +"have the can_pin_messages administrator right in the supergroup. Returns " +"True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:10 +msgid "Identifier of the topic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:1 +msgid "" +"Use this method to unpin specific pinned message in a supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinchatmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:11 +msgid "Int: Identifier of a message to unpin" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:1 +msgid "" +"Use this method to upload a .png file with a sticker for later use in " +"createNewStickerSet and addStickerToSet methods (can be used multiple " +"times). Returns the uploaded File on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#uploadstickerfile" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:6 +msgid "User identifier of sticker set owner" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:9 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:13 +msgid "On success, the sent file is returned." +msgstr "" + +#: of telebot.async_telebot.ExceptionHandler:1 +msgid "Class for handling exceptions while Polling" +msgstr "" + +#: of telebot.async_telebot.Handler:1 +msgid "Class for (next step|reply) handlers" +msgstr "" + +#: ../../async_version/index.rst:22 +msgid "Asyncio filters" +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:1 +#: telebot.asyncio_filters.SimpleCustomFilter:1 +msgid "Bases: :py:class:`abc.ABC`" +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:1 +msgid "" +"Advanced Custom Filter base class. Create child class with check() " +"method. Accepts two parameters, returns bool: True - filter passed, False" +" - filter failed. message: Message class text: Filter value given in " +"handler" +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:7 +#: telebot.asyncio_filters.SimpleCustomFilter:5 +msgid "Child classes should have .key property." +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:9 +msgid "Example on creating an advanced custom filter." +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter.check:1 +#: telebot.asyncio_filters.SimpleCustomFilter.check:1 +msgid "Perform a check." +msgstr "" + +#: of telebot.asyncio_filters.ChatFilter:1 +#: telebot.asyncio_filters.LanguageFilter:1 +#: telebot.asyncio_filters.StateFilter:1 +#: telebot.asyncio_filters.TextContainsFilter:1 +#: telebot.asyncio_filters.TextMatchFilter:1 +#: telebot.asyncio_filters.TextStartsFilter:1 +msgid "Bases: :py:class:`telebot.asyncio_filters.AdvancedCustomFilter`" +msgstr "" + +#: of telebot.asyncio_filters.ChatFilter:1 +msgid "Check whether chat_id corresponds to given chat_id." +msgstr "" + +#: of telebot.asyncio_filters.ChatFilter:3 +#: telebot.asyncio_filters.ForwardFilter:3 +#: telebot.asyncio_filters.IsAdminFilter:3 +#: telebot.asyncio_filters.IsDigitFilter:3 +#: telebot.asyncio_filters.IsReplyFilter:3 +#: telebot.asyncio_filters.LanguageFilter:3 +#: telebot.asyncio_filters.StateFilter:3 +#: telebot.asyncio_filters.TextContainsFilter:5 +#: telebot.asyncio_filters.TextMatchFilter:3 +#: telebot.asyncio_filters.TextStartsFilter:3 +msgid "Example on using this filter:" +msgstr "" + +#: of telebot.asyncio_filters.ForwardFilter:1 +#: telebot.asyncio_filters.IsAdminFilter:1 +#: telebot.asyncio_filters.IsDigitFilter:1 +#: telebot.asyncio_filters.IsReplyFilter:1 +msgid "Bases: :py:class:`telebot.asyncio_filters.SimpleCustomFilter`" +msgstr "" + +#: of telebot.asyncio_filters.ForwardFilter:1 +msgid "Check whether message was forwarded from channel or group." +msgstr "" + +#: of telebot.asyncio_filters.IsAdminFilter:1 +msgid "Check whether the user is administrator / owner of the chat." +msgstr "" + +#: of telebot.asyncio_filters.IsDigitFilter:1 +msgid "Filter to check whether the string is made up of only digits." +msgstr "" + +#: of telebot.asyncio_filters.IsReplyFilter:1 +msgid "Check whether message is a reply." +msgstr "" + +#: of telebot.asyncio_filters.LanguageFilter:1 +msgid "Check users language_code." +msgstr "" + +#: of telebot.asyncio_filters.SimpleCustomFilter:1 +msgid "" +"Simple Custom Filter base class. Create child class with check() method. " +"Accepts only message, returns bool value, that is compared with given in " +"handler." +msgstr "" + +#: of telebot.asyncio_filters.SimpleCustomFilter:7 +msgid "Example on creating a simple custom filter." +msgstr "" + +#: of telebot.asyncio_filters.StateFilter:1 +msgid "Filter to check state." +msgstr "" + +#: of telebot.asyncio_filters.TextContainsFilter:1 +msgid "Filter to check Text message. key: text" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:1 +msgid "" +"Advanced text filter to check (types.Message, types.CallbackQuery, " +"types.InlineQuery, types.Poll)" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:3 +msgid "" +"example of usage is in " +"examples/asynchronous_telebot/custom_filters/advanced_text_filter.py" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:5 +msgid "string, True if object's text is equal to passed string" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:8 +msgid "list[str] or tuple[str], True if any string element of iterable is in text" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:11 +#: telebot.asyncio_filters.TextFilter:14 +msgid "string, True if object's text starts with passed string" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:17 +msgid "bool (default False), case insensitive" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter +msgid "Raises" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:20 +msgid "if incorrect value for a parameter was supplied" +msgstr "" + +#: of telebot.asyncio_filters.TextMatchFilter:1 +msgid "Filter to check Text message." +msgstr "" + +#: of telebot.asyncio_filters.TextStartsFilter:1 +msgid "Filter to check whether message starts with some text." +msgstr "" + +#: ../../async_version/index.rst:30 +msgid "Asyncio handler backends" +msgstr "" + +#: of telebot.asyncio_handler_backends:1 +msgid "File with all middleware classes, states." +msgstr "" + +#: of telebot.asyncio_handler_backends.BaseMiddleware:1 +msgid "" +"Base class for middleware. Your middlewares should be inherited from this" +" class." +msgstr "" + +#: of telebot.asyncio_handler_backends.BaseMiddleware:4 +msgid "" +"Set update_sensitive=True if you want to get different updates on " +"different functions. For example, if you want to handle pre_process for " +"message update, then you will have to create pre_process_message " +"function, and so on. Same applies to post_process." +msgstr "" + +#: of telebot.asyncio_handler_backends.BaseMiddleware:9 +msgid "Example of class-based middlewares" +msgstr "" + +#: of telebot.asyncio_handler_backends.CancelUpdate:1 +msgid "" +"Class for canceling updates. Just return instance of this class in " +"middleware to skip update. Update will skip handler and execution of " +"post_process in middlewares." +msgstr "" + +#: of telebot.asyncio_handler_backends.ContinueHandling:1 +msgid "" +"Class for continue updates in handlers. Just return instance of this " +"class in handlers to continue process." +msgstr "" + +#: of telebot.asyncio_handler_backends.ContinueHandling:5 +msgid "Example of using ContinueHandling" +msgstr "" + +#: of telebot.asyncio_handler_backends.SkipHandler:1 +msgid "" +"Class for skipping handlers. Just return instance of this class in " +"middleware to skip handler. Update will go to post_process, but will skip" +" execution of handler." +msgstr "" + +#: of telebot.asyncio_handler_backends.State:1 +msgid "Class representing a state." +msgstr "" + +#: of telebot.asyncio_handler_backends.StatesGroup:1 +msgid "Class representing common states." +msgstr "" + +#: ../../async_version/index.rst:41 +msgid "Extensions" +msgstr "" + +#: of telebot.ext.aio.webhooks:1 +msgid "This file is used by AsyncTeleBot.run_webhooks() function." +msgstr "" + +#: of telebot.ext.aio.webhooks:3 +msgid "Fastapi and starlette(0.20.2+) libraries are required to run this script." +msgstr "" + +#: of telebot.ext.aio.webhooks.AsyncWebhookListener.run_app:1 +msgid "" +"Run app with the given parameters to init. Not supposed to be used " +"manually by user." +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/calldata.po b/docs/source/locales/en/LC_MESSAGES/calldata.po new file mode 100644 index 000000000..03484e874 --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/calldata.po @@ -0,0 +1,127 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../calldata.rst:4 +msgid "Callback data factory" +msgstr "" + +#: ../../calldata.rst:6 +msgid "Callback data factory in pyTelegramBotAPI" +msgstr "" + +#: ../../calldata.rst:6 +msgid "" +"ptba, pytba, pyTelegramBotAPI, callbackdatafactory, guide, callbackdata, " +"factory" +msgstr "" + +#: ../../calldata.rst:12 +msgid "callback\\_data file" +msgstr "" + +#: of telebot.callback_data:1 +msgid "Callback data factory's file." +msgstr "" + +#: of telebot.callback_data.CallbackData:1 +#: telebot.callback_data.CallbackDataFilter:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.callback_data.CallbackData:1 +msgid "Callback data factory This class will help you to work with CallbackQuery" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter:1 +msgid "Generate filter" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter +#: telebot.callback_data.CallbackData.new +#: telebot.callback_data.CallbackData.parse +#: telebot.callback_data.CallbackDataFilter.check +msgid "Parameters" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter:3 +msgid "specified named parameters will be checked with CallbackQuery.data" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter +#: telebot.callback_data.CallbackData.new +#: telebot.callback_data.CallbackData.parse +#: telebot.callback_data.CallbackDataFilter.check +msgid "Returns" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter:4 +msgid "CallbackDataFilter class" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:1 +msgid "Generate callback data" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:3 +msgid "positional parameters of CallbackData instance parts" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:4 +msgid "named parameters" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:5 +msgid "str" +msgstr "" + +#: of telebot.callback_data.CallbackData.parse:1 +msgid "Parse data from the callback data" +msgstr "" + +#: of telebot.callback_data.CallbackData.parse:3 +msgid "" +"string, use to telebot.types.CallbackQuery to parse it from string to a " +"dict" +msgstr "" + +#: of telebot.callback_data.CallbackData.parse:4 +msgid "dict parsed from callback data" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter:1 +msgid "Filter for CallbackData." +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check:1 +msgid "Checks if query.data appropriates to specified config" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check:3 +msgid "telebot.types.CallbackQuery" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check:6 +msgid "True if query.data appropriates to specified config" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check +msgid "Return type" +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/formatting.po b/docs/source/locales/en/LC_MESSAGES/formatting.po new file mode 100644 index 000000000..0f6eff001 --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/formatting.po @@ -0,0 +1,251 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../formatting.rst:3 +msgid "Formatting options" +msgstr "" + +#: ../../formatting.rst:5 +msgid "Formatting options in pyTelegramBotAPI" +msgstr "" + +#: ../../formatting.rst:5 +msgid "html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI" +msgstr "" + +#: of telebot.formatting:1 +msgid "Markdown & HTML formatting functions." +msgstr "" + +#: of telebot.formatting.escape_html:1 +msgid "Escapes HTML characters in a string of HTML." +msgstr "" + +#: of telebot.formatting.escape_html telebot.formatting.escape_markdown +#: telebot.formatting.format_text telebot.formatting.hbold +#: telebot.formatting.hcode telebot.formatting.hide_link +#: telebot.formatting.hitalic telebot.formatting.hlink telebot.formatting.hpre +#: telebot.formatting.hspoiler telebot.formatting.hstrikethrough +#: telebot.formatting.hunderline telebot.formatting.mbold +#: telebot.formatting.mcode telebot.formatting.mitalic telebot.formatting.mlink +#: telebot.formatting.mspoiler telebot.formatting.mstrikethrough +#: telebot.formatting.munderline +msgid "Parameters" +msgstr "" + +#: of telebot.formatting.escape_html:3 +msgid "The string of HTML to escape." +msgstr "" + +#: of telebot.formatting.escape_html telebot.formatting.escape_markdown +#: telebot.formatting.format_text telebot.formatting.hbold +#: telebot.formatting.hcode telebot.formatting.hide_link +#: telebot.formatting.hitalic telebot.formatting.hlink telebot.formatting.hpre +#: telebot.formatting.hspoiler telebot.formatting.hstrikethrough +#: telebot.formatting.hunderline telebot.formatting.mbold +#: telebot.formatting.mcode telebot.formatting.mitalic telebot.formatting.mlink +#: telebot.formatting.mspoiler telebot.formatting.mstrikethrough +#: telebot.formatting.munderline +msgid "Returns" +msgstr "" + +#: of telebot.formatting.escape_html:6 telebot.formatting.escape_markdown:8 +msgid "The escaped string." +msgstr "" + +#: of telebot.formatting.escape_html telebot.formatting.escape_markdown +#: telebot.formatting.format_text telebot.formatting.hbold +#: telebot.formatting.hcode telebot.formatting.hide_link +#: telebot.formatting.hitalic telebot.formatting.hlink telebot.formatting.hpre +#: telebot.formatting.hspoiler telebot.formatting.hstrikethrough +#: telebot.formatting.hunderline telebot.formatting.mbold +#: telebot.formatting.mcode telebot.formatting.mitalic telebot.formatting.mlink +#: telebot.formatting.mspoiler telebot.formatting.mstrikethrough +#: telebot.formatting.munderline +msgid "Return type" +msgstr "" + +#: of telebot.formatting.escape_html:7 telebot.formatting.escape_markdown:9 +#: telebot.formatting.format_text:17 telebot.formatting.hbold:10 +#: telebot.formatting.hcode:10 telebot.formatting.hide_link:7 +#: telebot.formatting.hitalic:10 telebot.formatting.hlink:13 +#: telebot.formatting.hpre:10 telebot.formatting.hspoiler:10 +#: telebot.formatting.hstrikethrough:10 telebot.formatting.hunderline:10 +#: telebot.formatting.mbold:10 telebot.formatting.mcode:10 +#: telebot.formatting.mitalic:10 telebot.formatting.mlink:13 +#: telebot.formatting.mspoiler:10 telebot.formatting.mstrikethrough:10 +#: telebot.formatting.munderline:10 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.formatting.escape_markdown:1 +msgid "Escapes Markdown characters in a string of Markdown." +msgstr "" + +#: of telebot.formatting.escape_markdown:3 +msgid "Credits to: simonsmh" +msgstr "" + +#: of telebot.formatting.escape_markdown:5 +msgid "The string of Markdown to escape." +msgstr "" + +#: of telebot.formatting.format_text:1 +msgid "Formats a list of strings into a single string." +msgstr "" + +#: of telebot.formatting.format_text:10 +msgid "Strings to format." +msgstr "" + +#: of telebot.formatting.format_text:13 +msgid "The separator to use between each string." +msgstr "" + +#: of telebot.formatting.format_text:16 telebot.formatting.hbold:9 +#: telebot.formatting.hcode:9 telebot.formatting.hitalic:9 +#: telebot.formatting.hlink:12 telebot.formatting.hpre:9 +#: telebot.formatting.hspoiler:9 telebot.formatting.hstrikethrough:9 +#: telebot.formatting.hunderline:9 telebot.formatting.mbold:9 +#: telebot.formatting.mcode:9 telebot.formatting.mitalic:9 +#: telebot.formatting.mlink:12 telebot.formatting.mspoiler:9 +#: telebot.formatting.mstrikethrough:9 telebot.formatting.munderline:9 +msgid "The formatted string." +msgstr "" + +#: of telebot.formatting.hbold:1 +msgid "Returns an HTML-formatted bold string." +msgstr "" + +#: of telebot.formatting.hbold:3 telebot.formatting.mbold:3 +msgid "The string to bold." +msgstr "" + +#: of telebot.formatting.hbold:6 telebot.formatting.hcode:6 +#: telebot.formatting.hitalic:6 telebot.formatting.hlink:9 +#: telebot.formatting.hpre:6 telebot.formatting.hspoiler:6 +#: telebot.formatting.hstrikethrough:6 telebot.formatting.hunderline:6 +#: telebot.formatting.mbold:6 telebot.formatting.mcode:6 +#: telebot.formatting.mitalic:6 telebot.formatting.mlink:9 +#: telebot.formatting.mspoiler:6 telebot.formatting.mstrikethrough:6 +#: telebot.formatting.munderline:6 +msgid "True if you need to escape special characters. Defaults to True." +msgstr "" + +#: of telebot.formatting.hcode:1 +msgid "Returns an HTML-formatted code string." +msgstr "" + +#: of telebot.formatting.hcode:3 telebot.formatting.mcode:3 +msgid "The string to code." +msgstr "" + +#: of telebot.formatting.hide_link:1 +msgid "Hide url of an image." +msgstr "" + +#: of telebot.formatting.hide_link:3 +msgid "The url of the image." +msgstr "" + +#: of telebot.formatting.hide_link:6 +msgid "The hidden url." +msgstr "" + +#: of telebot.formatting.hitalic:1 +msgid "Returns an HTML-formatted italic string." +msgstr "" + +#: of telebot.formatting.hitalic:3 telebot.formatting.mitalic:3 +msgid "The string to italicize." +msgstr "" + +#: of telebot.formatting.hlink:1 +msgid "Returns an HTML-formatted link string." +msgstr "" + +#: of telebot.formatting.hlink:3 telebot.formatting.mlink:3 +msgid "The string to link." +msgstr "" + +#: of telebot.formatting.hlink:6 telebot.formatting.mlink:6 +msgid "The URL to link to." +msgstr "" + +#: of telebot.formatting.hpre:1 +msgid "Returns an HTML-formatted preformatted string." +msgstr "" + +#: of telebot.formatting.hpre:3 +msgid "The string to preformatted." +msgstr "" + +#: of telebot.formatting.hspoiler:1 +msgid "Returns an HTML-formatted spoiler string." +msgstr "" + +#: of telebot.formatting.hspoiler:3 telebot.formatting.mspoiler:3 +msgid "The string to spoiler." +msgstr "" + +#: of telebot.formatting.hstrikethrough:1 +msgid "Returns an HTML-formatted strikethrough string." +msgstr "" + +#: of telebot.formatting.hstrikethrough:3 telebot.formatting.mstrikethrough:3 +msgid "The string to strikethrough." +msgstr "" + +#: of telebot.formatting.hunderline:1 +msgid "Returns an HTML-formatted underline string." +msgstr "" + +#: of telebot.formatting.hunderline:3 telebot.formatting.munderline:3 +msgid "The string to underline." +msgstr "" + +#: of telebot.formatting.mbold:1 +msgid "Returns a Markdown-formatted bold string." +msgstr "" + +#: of telebot.formatting.mcode:1 +msgid "Returns a Markdown-formatted code string." +msgstr "" + +#: of telebot.formatting.mitalic:1 +msgid "Returns a Markdown-formatted italic string." +msgstr "" + +#: of telebot.formatting.mlink:1 +msgid "Returns a Markdown-formatted link string." +msgstr "" + +#: of telebot.formatting.mspoiler:1 +msgid "Returns a Markdown-formatted spoiler string." +msgstr "" + +#: of telebot.formatting.mstrikethrough:1 +msgid "Returns a Markdown-formatted strikethrough string." +msgstr "" + +#: of telebot.formatting.munderline:1 +msgid "Returns a Markdown-formatted underline string." +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/index.po b/docs/source/locales/en/LC_MESSAGES/index.po new file mode 100644 index 000000000..38a49544f --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/index.po @@ -0,0 +1,120 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../index.rst:8 +msgid "Welcome to pyTelegramBotAPI's documentation!" +msgstr "" + +#: ../../index.rst:10 +msgid "Official documentation of pyTelegramBotAPI" +msgstr "" + +#: ../../index.rst:10 +msgid "ptba, pytba, pyTelegramBotAPI, documentation, guide" +msgstr "" + +#: ../../index.rst:17 +msgid "TeleBot" +msgstr "" + +#: ../../index.rst:18 +msgid "" +"TeleBot is synchronous and asynchronous implementation of `Telegram Bot " +"API `_." +msgstr "" + +#: ../../index.rst:21 +msgid "Chats" +msgstr "" + +#: ../../index.rst:22 +msgid "" +"English chat: `Private chat " +"`__" +msgstr "" + +#: ../../index.rst:24 +msgid "" +"Russian chat: `@pytelegrambotapi_talks_ru " +"`__" +msgstr "" + +#: ../../index.rst:26 +msgid "News: `@pyTelegramBotAPI `__" +msgstr "" + +#: ../../index.rst:28 +msgid "Pypi: `Pypi `__" +msgstr "" + +#: ../../index.rst:30 +msgid "" +"Source: `Github repository " +"`__" +msgstr "" + +#: ../../index.rst:33 +msgid "Some features:" +msgstr "" + +#: ../../index.rst:34 +msgid "Easy to learn and use." +msgstr "" + +#: ../../index.rst:36 +msgid "Easy to understand." +msgstr "" + +#: ../../index.rst:38 +msgid "Both sync and async." +msgstr "" + +#: ../../index.rst:40 +msgid "Examples on features." +msgstr "" + +#: ../../index.rst:42 +msgid "States" +msgstr "" + +#: ../../index.rst:44 +msgid "And more..." +msgstr "" + +#: ../../index.rst:47 +msgid "Content" +msgstr "" + +#: ../../index.rst:63 +msgid "Indices and tables" +msgstr "" + +#: ../../index.rst:65 +msgid ":ref:`genindex`" +msgstr "" + +#: ../../index.rst:66 +msgid ":ref:`modindex`" +msgstr "" + +#: ../../index.rst:67 +msgid ":ref:`search`" +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/install.po b/docs/source/locales/en/LC_MESSAGES/install.po new file mode 100644 index 000000000..0f4a3b386 --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/install.po @@ -0,0 +1,58 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../install.rst:3 +msgid "Installation Guide" +msgstr "" + +#: ../../install.rst:5 +msgid "Installation of pyTelegramBotAPI" +msgstr "" + +#: ../../install.rst:5 +msgid "ptba, pytba, pyTelegramBotAPI, installation, guide" +msgstr "" + +#: ../../install.rst:11 +msgid "Using PIP" +msgstr "" + +#: ../../install.rst:17 +msgid "Using pipenv" +msgstr "" + +#: ../../install.rst:23 +msgid "By cloning repository" +msgstr "" + +#: ../../install.rst:31 +msgid "Directly using pip" +msgstr "" + +#: ../../install.rst:37 +msgid "It is generally recommended to use the first option." +msgstr "" + +#: ../../install.rst:39 +msgid "" +"While the API is production-ready, it is still under development and it " +"has regular updates, do not forget to update it regularly by calling:" +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/quick_start.po b/docs/source/locales/en/LC_MESSAGES/quick_start.po new file mode 100644 index 000000000..9e7c22fbb --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/quick_start.po @@ -0,0 +1,40 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../quick_start.rst:4 +msgid "Quick start" +msgstr "" + +#: ../../quick_start.rst:6 +msgid "Quickstart guide" +msgstr "" + +#: ../../quick_start.rst:6 +msgid "ptba, pytba, pyTelegramBotAPI, quickstart, guide" +msgstr "" + +#: ../../quick_start.rst:11 +msgid "Synchronous TeleBot" +msgstr "" + +#: ../../quick_start.rst:16 +msgid "Asynchronous TeleBot" +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/sync_version.po b/docs/source/locales/en/LC_MESSAGES/sync_version.po new file mode 100644 index 000000000..1c91ece19 --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/sync_version.po @@ -0,0 +1,4805 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../sync_version/index.rst:3 +msgid "TeleBot version" +msgstr "" + +#: ../../sync_version/index.rst:5 +msgid "Synchronous pyTelegramBotAPI documentation" +msgstr "" + +#: ../../sync_version/index.rst:5 +msgid "ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync" +msgstr "" + +#: ../../sync_version/index.rst:10 +msgid "TeleBot methods" +msgstr "" + +#: of telebot.ExceptionHandler:1 telebot.Handler:1 telebot.TeleBot:1 +#: telebot.custom_filters.TextFilter:1 +#: telebot.ext.sync.webhooks.SyncWebhookListener:1 +#: telebot.handler_backends.BaseMiddleware:1 +#: telebot.handler_backends.CancelUpdate:1 +#: telebot.handler_backends.ContinueHandling:1 +#: telebot.handler_backends.SkipHandler:1 telebot.handler_backends.State:1 +#: telebot.handler_backends.StatesGroup:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.ExceptionHandler:1 +msgid "Class for handling exceptions while Polling" +msgstr "" + +#: of telebot.Handler:1 +msgid "Class for (next step|reply) handlers." +msgstr "" + +#: ../../docstring of telebot.REPLY_MARKUP_TYPES:1 +msgid "telebot" +msgstr "" + +#: ../../docstring of telebot.REPLY_MARKUP_TYPES +msgid "type" +msgstr "" + +#: ../../docstring of telebot.REPLY_MARKUP_TYPES:3 +msgid "Module" +msgstr "" + +#: of telebot.TeleBot:1 +msgid "This is the main synchronous class for Bot." +msgstr "" + +#: of telebot.TeleBot:3 +msgid "It allows you to add handlers for different kind of updates." +msgstr "" + +#: of telebot.TeleBot:5 +msgid "Usage:" +msgstr "" + +#: of telebot.TeleBot:7 +msgid "Creating instance of TeleBot" +msgstr "" + +#: of telebot.TeleBot:15 +msgid "" +"See more examples in examples/ directory: " +"https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" +msgstr "" + +#: of telebot.TeleBot:20 +msgid "Install coloredlogs module to specify colorful_logs=True" +msgstr "" + +#: of telebot.TeleBot telebot.TeleBot.add_custom_filter +#: telebot.TeleBot.add_data telebot.TeleBot.add_sticker_to_set +#: telebot.TeleBot.answer_callback_query telebot.TeleBot.answer_inline_query +#: telebot.TeleBot.answer_pre_checkout_query +#: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query +#: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member +#: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler +#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.chat_join_request_handler +#: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler +#: telebot.TeleBot.clear_reply_handlers +#: telebot.TeleBot.clear_reply_handlers_by_message_id +#: telebot.TeleBot.clear_step_handler +#: telebot.TeleBot.clear_step_handler_by_chat_id +#: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo +#: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands +#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.edit_message_live_location +#: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup +#: telebot.TeleBot.edit_message_text +#: telebot.TeleBot.edited_channel_post_handler +#: telebot.TeleBot.edited_message_handler +#: telebot.TeleBot.enable_save_next_step_handlers +#: telebot.TeleBot.enable_save_reply_handlers +#: telebot.TeleBot.enable_saving_states telebot.TeleBot.export_chat_invite_link +#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat +#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member +#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file +#: telebot.TeleBot.get_file_url telebot.TeleBot.get_game_high_scores +#: telebot.TeleBot.get_my_commands +#: telebot.TeleBot.get_my_default_administrator_rights +#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers +#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler +#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member +#: telebot.TeleBot.register_callback_query_handler +#: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_join_request_handler +#: telebot.TeleBot.register_chat_member_handler +#: telebot.TeleBot.register_chosen_inline_handler +#: telebot.TeleBot.register_edited_channel_post_handler +#: telebot.TeleBot.register_edited_message_handler +#: telebot.TeleBot.register_for_reply +#: telebot.TeleBot.register_for_reply_by_message_id +#: telebot.TeleBot.register_inline_handler +#: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_middleware_handler +#: telebot.TeleBot.register_my_chat_member_handler +#: telebot.TeleBot.register_next_step_handler +#: telebot.TeleBot.register_next_step_handler_by_chat_id +#: telebot.TeleBot.register_poll_answer_handler +#: telebot.TeleBot.register_poll_handler +#: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_shipping_query_handler +#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member +#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link +#: telebot.TeleBot.run_webhooks telebot.TeleBot.send_animation +#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action +#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice +#: telebot.TeleBot.send_document telebot.TeleBot.send_game +#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location +#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message +#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll +#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note +#: telebot.TeleBot.send_voice +#: telebot.TeleBot.set_chat_administrator_custom_title +#: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button +#: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo +#: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_update_listener +#: telebot.TeleBot.set_webhook telebot.TeleBot.setup_middleware +#: telebot.TeleBot.shipping_query_handler +#: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll +#: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unpin_all_chat_messages +#: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file +#: telebot.custom_filters.TextFilter +msgid "Parameters" +msgstr "" + +#: of telebot.TeleBot:23 +msgid "Token of a bot, should be obtained from @BotFather" +msgstr "" + +#: of telebot.TeleBot:26 +msgid "Default parse mode, defaults to None" +msgstr "" + +#: of telebot.TeleBot:29 +msgid "Threaded or not, defaults to True" +msgstr "" + +#: of telebot.TeleBot:32 +msgid "Skips pending updates, defaults to False" +msgstr "" + +#: of telebot.TeleBot:35 +msgid "Number of maximum parallel threads, defaults to 2" +msgstr "" + +#: of telebot.TeleBot:38 +msgid "Next step backend class, defaults to None" +msgstr "" + +#: of telebot.TeleBot:41 +msgid "Reply step handler class, defaults to None" +msgstr "" + +#: of telebot.TeleBot:44 +msgid "Exception handler to handle errors, defaults to None" +msgstr "" + +#: of telebot.TeleBot:47 +msgid "Last update's id, defaults to 0" +msgstr "" + +#: of telebot.TeleBot:50 +msgid "Supress middleware exceptions, defaults to False" +msgstr "" + +#: of telebot.TeleBot:53 +msgid "Storage for states, defaults to StateMemoryStorage()" +msgstr "" + +#: of telebot.TeleBot:56 +msgid "Use class middlewares, defaults to False" +msgstr "" + +#: of telebot.TeleBot:59 +msgid "Default value for disable_web_page_preview, defaults to None" +msgstr "" + +#: of telebot.TeleBot:62 +msgid "Default value for disable_notification, defaults to None" +msgstr "" + +#: of telebot.TeleBot:65 +msgid "Default value for protect_content, defaults to None" +msgstr "" + +#: of telebot.TeleBot:68 +msgid "Default value for allow_sending_without_reply, defaults to None" +msgstr "" + +#: of telebot.TeleBot:72 +msgid "Outputs colorful logs" +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:1 +msgid "Create custom filter." +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:3 +msgid "Example on checking the text of a message" +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:12 +msgid "Class with check(message) method." +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:13 +msgid "Custom filter class with key." +msgstr "" + +#: of telebot.TeleBot.add_data:1 +msgid "Add data to states." +msgstr "" + +#: of telebot.TeleBot.add_data:3 telebot.TeleBot.delete_state:3 +#: telebot.TeleBot.get_state:4 telebot.TeleBot.reset_data:3 +#: telebot.TeleBot.set_state:9 +msgid "User's identifier" +msgstr "" + +#: of telebot.TeleBot.add_data:6 telebot.TeleBot.delete_state:6 +#: telebot.TeleBot.get_state:7 telebot.TeleBot.reset_data:6 +#: telebot.TeleBot.set_state:15 +msgid "Chat's identifier" +msgstr "" + +#: of telebot.TeleBot.add_data:9 +msgid "Data to add" +msgstr "" + +#: of telebot.TeleBot.add_data telebot.TeleBot.add_sticker_to_set +#: telebot.TeleBot.answer_callback_query telebot.TeleBot.answer_inline_query +#: telebot.TeleBot.answer_pre_checkout_query +#: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query +#: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member +#: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler +#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.chat_join_request_handler +#: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler +#: telebot.TeleBot.clear_reply_handlers +#: telebot.TeleBot.clear_reply_handlers_by_message_id +#: telebot.TeleBot.clear_step_handler +#: telebot.TeleBot.clear_step_handler_by_chat_id telebot.TeleBot.close +#: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo +#: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands +#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.edit_message_live_location +#: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup +#: telebot.TeleBot.edit_message_text +#: telebot.TeleBot.edited_channel_post_handler +#: telebot.TeleBot.edited_message_handler +#: telebot.TeleBot.enable_save_next_step_handlers +#: telebot.TeleBot.export_chat_invite_link telebot.TeleBot.forward_message +#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators +#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count +#: telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file +#: telebot.TeleBot.get_file_url telebot.TeleBot.get_forum_topic_icon_stickers +#: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands +#: telebot.TeleBot.get_my_default_administrator_rights +#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.log_out telebot.TeleBot.message_handler +#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler +#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler +#: telebot.TeleBot.poll_handler telebot.TeleBot.polling +#: telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.promote_chat_member +#: telebot.TeleBot.register_callback_query_handler +#: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_join_request_handler +#: telebot.TeleBot.register_chosen_inline_handler +#: telebot.TeleBot.register_edited_channel_post_handler +#: telebot.TeleBot.register_edited_message_handler +#: telebot.TeleBot.register_for_reply +#: telebot.TeleBot.register_for_reply_by_message_id +#: telebot.TeleBot.register_inline_handler +#: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_middleware_handler +#: telebot.TeleBot.register_my_chat_member_handler +#: telebot.TeleBot.register_next_step_handler +#: telebot.TeleBot.register_next_step_handler_by_chat_id +#: telebot.TeleBot.register_poll_answer_handler +#: telebot.TeleBot.register_poll_handler +#: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_shipping_query_handler +#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic +#: telebot.TeleBot.reply_to telebot.TeleBot.reset_data +#: telebot.TeleBot.restrict_chat_member telebot.TeleBot.retrieve_data +#: telebot.TeleBot.revoke_chat_invite_link telebot.TeleBot.send_animation +#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action +#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice +#: telebot.TeleBot.send_document telebot.TeleBot.send_game +#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location +#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message +#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll +#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note +#: telebot.TeleBot.send_voice +#: telebot.TeleBot.set_chat_administrator_custom_title +#: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button +#: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo +#: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.setup_middleware telebot.TeleBot.shipping_query_handler +#: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll +#: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unpin_all_chat_messages +#: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file +#: telebot.TeleBot.user telebot.custom_filters.TextFilter +#: telebot.ext.sync.webhooks.SyncWebhookListener.run_app +msgid "Returns" +msgstr "" + +#: of telebot.TeleBot.add_data:10 telebot.TeleBot.callback_query_handler:9 +#: telebot.TeleBot.channel_post_handler:18 +#: telebot.TeleBot.chat_join_request_handler:10 +#: telebot.TeleBot.chat_member_handler:11 +#: telebot.TeleBot.chosen_inline_handler:10 +#: telebot.TeleBot.clear_reply_handlers:6 +#: telebot.TeleBot.clear_reply_handlers_by_message_id:6 +#: telebot.TeleBot.clear_step_handler:6 +#: telebot.TeleBot.clear_step_handler_by_chat_id:6 +#: telebot.TeleBot.delete_state:9 telebot.TeleBot.edited_message_handler:21 +#: telebot.TeleBot.enable_save_next_step_handlers:13 +#: telebot.TeleBot.inline_handler:9 telebot.TeleBot.my_chat_member_handler:10 +#: telebot.TeleBot.poll_answer_handler:10 telebot.TeleBot.poll_handler:8 +#: telebot.TeleBot.pre_checkout_query_handler:9 +#: telebot.TeleBot.register_callback_query_handler:14 +#: telebot.TeleBot.register_channel_post_handler:23 +#: telebot.TeleBot.register_chat_join_request_handler:14 +#: telebot.TeleBot.register_chosen_inline_handler:14 +#: telebot.TeleBot.register_edited_message_handler:26 +#: telebot.TeleBot.register_for_reply:15 +#: telebot.TeleBot.register_for_reply_by_message_id:15 +#: telebot.TeleBot.register_message_handler:26 +#: telebot.TeleBot.register_middleware_handler:18 +#: telebot.TeleBot.register_my_chat_member_handler:14 +#: telebot.TeleBot.register_next_step_handler:15 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:15 +#: telebot.TeleBot.register_poll_answer_handler:14 +#: telebot.TeleBot.register_poll_handler:14 +#: telebot.TeleBot.register_shipping_query_handler:14 +#: telebot.TeleBot.reset_data:9 telebot.TeleBot.set_state:18 +#: telebot.TeleBot.setup_middleware:5 telebot.TeleBot.shipping_query_handler:9 +#: telebot.custom_filters.TextFilter:22 +#: telebot.ext.sync.webhooks.SyncWebhookListener.run_app:4 +msgid "None" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:1 +msgid "" +"Use this method to add a new sticker to a set created by the bot. It's " +"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:7 +#: telebot.TeleBot.create_new_sticker_set:7 +msgid "User identifier of created sticker set owner" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:10 telebot.TeleBot.get_sticker_set:5 +#: telebot.TeleBot.set_sticker_set_thumb:6 +msgid "Sticker set name" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:13 +#: telebot.TeleBot.create_new_sticker_set:18 +msgid "One or more emoji corresponding to the sticker" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:16 +#: telebot.TeleBot.create_new_sticker_set:21 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px. Pass a file_id as a String to send a file that already " +"exists on the Telegram servers, 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." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:21 +#: telebot.TeleBot.create_new_sticker_set:26 +msgid "TGS animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:24 +#: telebot.TeleBot.create_new_sticker_set:29 +msgid "WebM animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:27 +#: telebot.TeleBot.create_new_sticker_set:40 +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:30 +#: telebot.TeleBot.answer_callback_query:22 +#: telebot.TeleBot.answer_inline_query:35 +#: telebot.TeleBot.answer_pre_checkout_query:21 +#: telebot.TeleBot.answer_shipping_query:18 +#: telebot.TeleBot.close_forum_topic:13 +#: telebot.TeleBot.create_new_sticker_set:43 +#: telebot.TeleBot.delete_forum_topic:13 +#: telebot.TeleBot.delete_sticker_from_set:6 +#: telebot.TeleBot.edit_forum_topic:19 telebot.TeleBot.reopen_forum_topic:13 +#: telebot.TeleBot.set_sticker_position_in_set:11 +#: telebot.TeleBot.set_sticker_set_thumb:15 +#: telebot.TeleBot.unpin_all_forum_topic_messages:13 +msgid "On success, True is returned." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set telebot.TeleBot.answer_callback_query +#: telebot.TeleBot.answer_inline_query +#: telebot.TeleBot.answer_pre_checkout_query +#: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query +#: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member +#: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.close_forum_topic +#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link +#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link +#: telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo +#: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_webhook +#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link +#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.edit_message_live_location +#: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup +#: telebot.TeleBot.edit_message_text telebot.TeleBot.export_chat_invite_link +#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat +#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member +#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file_url +#: telebot.TeleBot.get_forum_topic_icon_stickers +#: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands +#: telebot.TeleBot.get_my_default_administrator_rights +#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.log_out +#: telebot.TeleBot.pin_chat_message telebot.TeleBot.promote_chat_member +#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic +#: telebot.TeleBot.reply_to telebot.TeleBot.restrict_chat_member +#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link +#: telebot.TeleBot.send_animation telebot.TeleBot.send_audio +#: telebot.TeleBot.send_chat_action telebot.TeleBot.send_contact +#: telebot.TeleBot.send_dice telebot.TeleBot.send_document +#: telebot.TeleBot.send_game telebot.TeleBot.send_invoice +#: telebot.TeleBot.send_location telebot.TeleBot.send_media_group +#: telebot.TeleBot.send_message telebot.TeleBot.send_photo +#: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker +#: telebot.TeleBot.send_venue telebot.TeleBot.send_video +#: telebot.TeleBot.send_video_note +#: telebot.TeleBot.set_chat_administrator_custom_title +#: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button +#: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo +#: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll +#: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unpin_all_chat_messages +#: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file +#: telebot.TeleBot.user +msgid "Return type" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:31 +#: telebot.TeleBot.answer_callback_query:23 +#: telebot.TeleBot.answer_inline_query:36 +#: telebot.TeleBot.answer_pre_checkout_query:22 +#: telebot.TeleBot.answer_shipping_query:19 +#: telebot.TeleBot.approve_chat_join_request:15 +#: telebot.TeleBot.ban_chat_member:25 telebot.TeleBot.ban_chat_sender_chat:17 +#: telebot.TeleBot.close:9 telebot.TeleBot.close_forum_topic:14 +#: telebot.TeleBot.create_new_sticker_set:44 +#: telebot.TeleBot.decline_chat_join_request:15 +#: telebot.TeleBot.delete_chat_photo:13 +#: telebot.TeleBot.delete_chat_sticker_set:11 +#: telebot.TeleBot.delete_forum_topic:14 telebot.TeleBot.delete_message:23 +#: telebot.TeleBot.delete_my_commands:17 +#: telebot.TeleBot.delete_sticker_from_set:7 telebot.TeleBot.delete_webhook:13 +#: telebot.TeleBot.edit_forum_topic:20 telebot.TeleBot.leave_chat:8 +#: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 +#: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 +#: telebot.TeleBot.reopen_forum_topic:14 +#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:23 +#: telebot.TeleBot.set_chat_administrator_custom_title:18 +#: telebot.TeleBot.set_chat_description:14 +#: telebot.TeleBot.set_chat_menu_button:15 +#: telebot.TeleBot.set_chat_permissions:15 telebot.TeleBot.set_chat_photo:16 +#: telebot.TeleBot.set_chat_title:17 telebot.TeleBot.set_my_commands:18 +#: telebot.TeleBot.set_my_default_administrator_rights:18 +#: telebot.TeleBot.set_sticker_position_in_set:12 +#: telebot.TeleBot.set_sticker_set_thumb:16 +#: telebot.TeleBot.unban_chat_member:20 +#: telebot.TeleBot.unban_chat_sender_chat:15 +#: telebot.TeleBot.unpin_all_chat_messages:12 +#: telebot.TeleBot.unpin_all_forum_topic_messages:14 +#: telebot.TeleBot.unpin_chat_message:15 +msgid ":obj:`bool`" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:1 +msgid "" +"Use this method to send answers to callback queries sent from inline " +"keyboards. The answer will be displayed to the user as a notification at " +"the top of the chat screen or as an alert." +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answercallbackquery" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:6 +#: telebot.TeleBot.answer_pre_checkout_query:10 +#: telebot.TeleBot.answer_shipping_query:5 +#: telebot.TeleBot.answer_web_app_query:8 +msgid "Unique identifier for the query to be answered" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:9 +msgid "" +"Text of the notification. If not specified, nothing will be shown to the " +"user, 0-200 characters" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:12 +msgid "" +"If True, an alert will be shown by the client instead of a notification " +"at the top of the chat screen. Defaults to false." +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:15 +msgid "" +"URL that will be opened by the user's client. If you have created a Game " +"and accepted the conditions via @BotFather, specify the URL that opens " +"your game - note that this will only work if the query comes from a " +"callback_game button." +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:19 +msgid "" +"The maximum amount of time in seconds that the result of the callback " +"query may be cached client-side. Telegram apps will support caching " +"starting in version 3.14. Defaults to 0." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:1 +msgid "" +"Use this method to send answers to an inline query. On success, True is " +"returned. No more than 50 results per query are allowed." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerinlinequery" +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:6 +msgid "Unique identifier for the answered query" +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:9 +msgid "Array of results for the inline query" +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:12 +msgid "" +"The maximum amount of time in seconds that the result of the inline query" +" may be cached on the server." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:16 +msgid "" +"Pass True, if results may be cached on the server side only for the user " +"that sent the query." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:20 +msgid "" +"Pass the offset that a client should send in the next query with the same" +" text to receive more results." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:24 +msgid "" +"Deep-linking parameter for the /start message sent to the bot when user " +"presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - " +"are allowed. Example: An inline bot that sends YouTube videos can ask the" +" user to connect the bot to their YouTube account to adapt search results" +" accordingly. To do this, it displays a 'Connect your YouTube account' " +"button above the results, or even before showing any. The user presses " +"the button, switches to a private chat with the bot and, in doing so, " +"passes a start parameter that instructs the bot to return an OAuth link. " +"Once done, the bot can offer a switch_inline button so that the user can " +"easily return to the chat where they wanted to use the bot's inline " +"capabilities." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:32 +msgid "" +"Parameter for the start message sent to the bot when user presses the " +"switch button" +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:1 +msgid "" +"Once the user has confirmed their payment and shipping details, the Bot " +"API sends the final confirmation in the form of an Update with the field " +"pre_checkout_query. Use this method to respond to such pre-checkout " +"queries. On success, True is returned." +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:6 +msgid "" +"The Bot API must receive an answer within 10 seconds after the pre-" +"checkout query was sent." +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerprecheckoutquery" +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:13 +msgid "" +"Specify True if everything is alright (goods are available, etc.) and the" +" bot is ready to proceed with the order. Use False if there are any " +"problems." +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:16 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains the reason for failure to proceed with the checkout (e.g. " +"\"Sorry, somebody just bought the last of our amazing black T-shirts " +"while you were busy filling out your payment details. Please choose a " +"different color or garment!\"). Telegram will display this message to the" +" user." +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:1 +msgid "Asks for an answer to a shipping question." +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answershippingquery" +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:8 +msgid "" +"Specify True if delivery to the specified address is possible and False " +"if there are any problems (for example, if delivery to the specified " +"address is not possible)" +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:11 +msgid "" +"Required if ok is True. A JSON-serialized array of available shipping " +"options." +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:14 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains why it is impossible to complete the order (e.g. \"Sorry, " +"delivery to your desired address is unavailable'). Telegram will display " +"this message to the user." +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:1 +msgid "" +"Use this method to set the result of an interaction with a Web App and " +"send a corresponding message on behalf of the user to the chat from which" +" the query originated. On success, a SentWebAppMessage object is " +"returned." +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:6 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#answerwebappquery" +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:11 +msgid "A JSON-serialized object describing the message to be sent" +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:14 +msgid "On success, a SentWebAppMessage object is returned." +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:15 +msgid ":class:`telebot.types.SentWebAppMessage`" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:1 +msgid "" +"Use this method to approve a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#approvechatjoinrequest" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:7 +#: telebot.TeleBot.decline_chat_join_request:7 +#: telebot.TeleBot.delete_chat_sticker_set:7 telebot.TeleBot.get_chat_member:5 +#: telebot.TeleBot.set_chat_administrator_custom_title:6 +#: telebot.TeleBot.set_chat_permissions:7 +#: telebot.TeleBot.set_chat_sticker_set:7 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup (in the format @supergroupusername)" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:11 +#: telebot.TeleBot.ban_chat_member:12 +#: telebot.TeleBot.decline_chat_join_request:11 +#: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_profile_photos:6 +#: telebot.TeleBot.promote_chat_member:11 +#: telebot.TeleBot.restrict_chat_member:11 +#: telebot.TeleBot.set_chat_administrator_custom_title:10 +#: telebot.TeleBot.unban_chat_member:13 +msgid "Unique identifier of the target user" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:14 +#: telebot.TeleBot.ban_chat_sender_chat:16 +#: telebot.TeleBot.decline_chat_join_request:14 +#: telebot.TeleBot.delete_chat_photo:12 telebot.TeleBot.delete_my_commands:16 +#: telebot.TeleBot.log_out:10 telebot.TeleBot.pin_chat_message:18 +#: telebot.TeleBot.promote_chat_member:60 telebot.TeleBot.remove_webhook:3 +#: telebot.TeleBot.set_chat_administrator_custom_title:17 +#: telebot.TeleBot.set_chat_description:13 +#: telebot.TeleBot.set_chat_menu_button:14 telebot.TeleBot.set_chat_photo:15 +#: telebot.TeleBot.set_chat_title:16 telebot.TeleBot.set_my_commands:17 +#: telebot.TeleBot.set_my_default_administrator_rights:17 +#: telebot.TeleBot.set_webhook:46 telebot.TeleBot.unban_chat_sender_chat:14 +#: telebot.TeleBot.unpin_all_chat_messages:11 +#: telebot.TeleBot.unpin_chat_message:14 +msgid "True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:1 +msgid "" +"Use this method to ban a user in a group, a supergroup or a channel. In " +"the case of supergroups and channels, the user will not be able to return" +" to the chat on their own using invite links, etc., unless unbanned " +"first. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:8 telebot.TeleBot.restrict_chat_member:7 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:15 +msgid "" +"Date when the user will be unbanned, unix time. If user is banned for " +"more than 366 days or less than 30 seconds from the current time they are" +" considered to be banned forever" +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:19 +msgid "" +"Bool: Pass True to delete all messages from the chat for the user that is" +" being removed. If False, the user will be able to see messages in the " +"group that were sent before the user was removed. Always True for " +"supergroups and channels." +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:24 +#: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 +#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:22 +msgid "Returns True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:1 +msgid "" +"Use this method to ban a channel chat in a supergroup or a channel. The " +"owner of the chat will not be able to send messages and join live streams" +" on behalf of the chat, unless it is unbanned first. The bot must be an " +"administrator in the supergroup or channel for this to work and must have" +" the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#banchatsenderchat" +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:10 +#: telebot.TeleBot.close_forum_topic:7 telebot.TeleBot.copy_message:5 +#: telebot.TeleBot.create_forum_topic:7 telebot.TeleBot.delete_forum_topic:7 +#: telebot.TeleBot.delete_message:13 telebot.TeleBot.edit_forum_topic:7 +#: telebot.TeleBot.edit_message_live_location:13 +#: telebot.TeleBot.forward_message:8 telebot.TeleBot.pin_chat_message:7 +#: telebot.TeleBot.reopen_forum_topic:7 telebot.TeleBot.send_animation:6 +#: telebot.TeleBot.send_audio:9 telebot.TeleBot.send_dice:5 +#: telebot.TeleBot.send_document:5 telebot.TeleBot.send_game:5 +#: telebot.TeleBot.send_location:5 telebot.TeleBot.send_media_group:6 +#: telebot.TeleBot.send_message:9 telebot.TeleBot.send_photo:5 +#: telebot.TeleBot.send_sticker:6 telebot.TeleBot.send_video:5 +#: telebot.TeleBot.send_video_note:6 telebot.TeleBot.send_voice:7 +#: telebot.TeleBot.set_chat_description:6 telebot.TeleBot.set_chat_title:9 +#: telebot.TeleBot.stop_message_live_location:6 +#: telebot.TeleBot.unban_chat_sender_chat:8 +#: telebot.TeleBot.unpin_all_forum_topic_messages:7 +#: telebot.TeleBot.unpin_chat_message:7 +msgid "" +"Unique identifier for the target chat or username of the target channel " +"(in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:13 +msgid "Unique identifier of the target sender chat" +msgstr "" + +#: of telebot.TeleBot.callback_query_handler:1 +msgid "" +"Handles new incoming callback query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.CallbackQuery` object." +msgstr "" + +#: of telebot.TeleBot.callback_query_handler:4 +#: telebot.TeleBot.channel_post_handler:10 +#: telebot.TeleBot.chat_join_request_handler:5 +#: telebot.TeleBot.chat_member_handler:6 +#: telebot.TeleBot.chosen_inline_handler:5 +#: telebot.TeleBot.edited_channel_post_handler:10 +#: telebot.TeleBot.edited_message_handler:10 telebot.TeleBot.inline_handler:4 +#: telebot.TeleBot.my_chat_member_handler:5 +#: telebot.TeleBot.poll_answer_handler:5 telebot.TeleBot.poll_handler:4 +#: telebot.TeleBot.pre_checkout_query_handler:4 +#: telebot.TeleBot.register_callback_query_handler:6 +#: telebot.TeleBot.register_channel_post_handler:15 +#: telebot.TeleBot.register_chat_join_request_handler:6 +#: telebot.TeleBot.register_chat_member_handler:6 +#: telebot.TeleBot.register_chosen_inline_handler:6 +#: telebot.TeleBot.register_edited_channel_post_handler:15 +#: telebot.TeleBot.register_edited_message_handler:15 +#: telebot.TeleBot.register_inline_handler:6 +#: telebot.TeleBot.register_message_handler:15 +#: telebot.TeleBot.register_my_chat_member_handler:6 +#: telebot.TeleBot.register_poll_answer_handler:6 +#: telebot.TeleBot.register_poll_handler:6 +#: telebot.TeleBot.register_pre_checkout_query_handler:6 +#: telebot.TeleBot.register_shipping_query_handler:6 +#: telebot.TeleBot.shipping_query_handler:4 +msgid "Function executed as a filter" +msgstr "" + +#: of telebot.TeleBot.callback_query_handler:7 +#: telebot.TeleBot.channel_post_handler:16 +#: telebot.TeleBot.chat_join_request_handler:8 +#: telebot.TeleBot.chat_member_handler:9 +#: telebot.TeleBot.chosen_inline_handler:8 +#: telebot.TeleBot.edited_channel_post_handler:16 +#: telebot.TeleBot.edited_message_handler:19 telebot.TeleBot.inline_handler:7 +#: telebot.TeleBot.message_handler:50 telebot.TeleBot.my_chat_member_handler:8 +#: telebot.TeleBot.poll_answer_handler:8 telebot.TeleBot.poll_handler:7 +#: telebot.TeleBot.pre_checkout_query_handler:7 +#: telebot.TeleBot.register_callback_query_handler:12 +#: telebot.TeleBot.register_channel_post_handler:21 +#: telebot.TeleBot.register_chat_join_request_handler:12 +#: telebot.TeleBot.register_chat_member_handler:12 +#: telebot.TeleBot.register_chosen_inline_handler:12 +#: telebot.TeleBot.register_edited_channel_post_handler:21 +#: telebot.TeleBot.register_edited_message_handler:24 +#: telebot.TeleBot.register_inline_handler:12 +#: telebot.TeleBot.register_message_handler:24 +#: telebot.TeleBot.register_my_chat_member_handler:12 +#: telebot.TeleBot.register_poll_answer_handler:12 +#: telebot.TeleBot.register_poll_handler:12 +#: telebot.TeleBot.register_pre_checkout_query_handler:11 +#: telebot.TeleBot.register_shipping_query_handler:12 +#: telebot.TeleBot.shipping_query_handler:7 +msgid "Optional keyword arguments(custom filters)" +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:1 +msgid "" +"Handles new incoming channel post of any kind - text, photo, sticker, " +"etc. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:4 +#: telebot.TeleBot.edited_channel_post_handler:4 +#: telebot.TeleBot.edited_message_handler:4 telebot.TeleBot.message_handler:34 +msgid "Optional list of strings (commands to handle)." +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:7 +#: telebot.TeleBot.edited_channel_post_handler:7 +#: telebot.TeleBot.edited_message_handler:7 telebot.TeleBot.message_handler:37 +msgid "Optional regular expression." +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:13 +#: telebot.TeleBot.edited_channel_post_handler:13 +#: telebot.TeleBot.edited_message_handler:13 telebot.TeleBot.message_handler:44 +#: telebot.TeleBot.register_channel_post_handler:6 +#: telebot.TeleBot.register_edited_channel_post_handler:6 +#: telebot.TeleBot.register_edited_message_handler:6 +#: telebot.TeleBot.register_message_handler:6 +msgid "Supported message content types. Must be a list. Defaults to ['text']." +msgstr "" + +#: of telebot.TeleBot.chat_join_request_handler:1 +msgid "" +"Handles a request to join the chat has been sent. The bot must have the " +"can_invite_users administrator right in the chat to receive these " +"updates. As a parameter to the decorator function, it passes " +":class:`telebot.types.ChatJoinRequest` object." +msgstr "" + +#: of telebot.TeleBot.chat_member_handler:1 +msgid "" +"Handles update in a status of a user in a chat. The bot must be an " +"administrator in the chat and must explicitly specify “chat_member” in " +"the list of allowed_updates to receive these updates. As a parameter to " +"the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.TeleBot.chosen_inline_handler:1 +msgid "" +"Handles the result of an inline query that was chosen by a user and sent " +"to their chat partner. Please see our documentation on the feedback " +"collecting for details on how to enable these updates for your bot. As a " +"parameter to the decorator function, it passes " +":class:`telebot.types.ChosenInlineResult` object." +msgstr "" + +#: of telebot.TeleBot.clear_reply_handlers:1 +#: telebot.TeleBot.clear_reply_handlers_by_message_id:1 +msgid "" +"Clears all callback functions registered by register_for_reply() and " +"register_for_reply_by_message_id()." +msgstr "" + +#: of telebot.TeleBot.clear_reply_handlers:3 +msgid "The message for which we want to clear reply handlers" +msgstr "" + +#: of telebot.TeleBot.clear_reply_handlers_by_message_id:3 +msgid "The message id for which we want to clear reply handlers" +msgstr "" + +#: of telebot.TeleBot.clear_step_handler:1 +#: telebot.TeleBot.clear_step_handler_by_chat_id:1 +msgid "Clears all callback functions registered by register_next_step_handler()." +msgstr "" + +#: of telebot.TeleBot.clear_step_handler:3 +msgid "" +"The message for which we want to handle new message after that in same " +"chat." +msgstr "" + +#: of telebot.TeleBot.clear_step_handler_by_chat_id:3 +msgid "The chat for which we want to clear next step handlers" +msgstr "" + +#: of telebot.TeleBot.close:1 +msgid "" +"Use this method to close the bot instance before moving it from one local" +" server to another. You need to delete the webhook before calling this " +"method to ensure that the bot isn't launched again after server restart. " +"The method will return error 429 in the first 10 minutes after the bot is" +" launched. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.close:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#close" +msgstr "" + +#: of telebot.TeleBot.close_forum_topic:1 +msgid "" +"Use this method to close an open topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.close_forum_topic:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" +msgstr "" + +#: of telebot.TeleBot.close_forum_topic:10 +msgid "Identifier of the topic to close" +msgstr "" + +#: of telebot.TeleBot.copy_message:1 +msgid "Use this method to copy messages of any kind." +msgstr "" + +#: of telebot.TeleBot.copy_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" +msgstr "" + +#: of telebot.TeleBot.copy_message:8 telebot.TeleBot.forward_message:11 +msgid "" +"Unique identifier for the chat where the original message was sent (or " +"channel username in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.copy_message:10 telebot.TeleBot.forward_message:14 +msgid "Message identifier in the chat specified in from_chat_id" +msgstr "" + +#: of telebot.TeleBot.copy_message:13 +msgid "" +"New caption for media, 0-1024 characters after entities parsing. If not " +"specified, the original caption is kept" +msgstr "" + +#: of telebot.TeleBot.copy_message:16 +msgid "Mode for parsing entities in the new caption." +msgstr "" + +#: of telebot.TeleBot.copy_message:19 +msgid "" +"A JSON-serialized list of special entities that appear in the new " +"caption, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 +#: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 +#: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:26 +#: telebot.TeleBot.send_game:11 telebot.TeleBot.send_invoice:67 +#: telebot.TeleBot.send_location:25 telebot.TeleBot.send_message:24 +#: telebot.TeleBot.send_photo:22 telebot.TeleBot.send_poll:44 +#: telebot.TeleBot.send_venue:27 telebot.TeleBot.send_video:35 +#: telebot.TeleBot.send_video_note:27 telebot.TeleBot.send_voice:31 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.TeleBot.copy_message:25 telebot.TeleBot.send_animation:34 +#: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:38 +#: telebot.TeleBot.send_document:50 telebot.TeleBot.send_invoice:95 +#: telebot.TeleBot.send_location:43 telebot.TeleBot.send_media_group:15 +#: telebot.TeleBot.send_photo:25 telebot.TeleBot.send_poll:64 +#: telebot.TeleBot.send_sticker:30 telebot.TeleBot.send_venue:51 +#: telebot.TeleBot.send_video:38 telebot.TeleBot.send_video_note:42 +#: telebot.TeleBot.send_voice:43 +msgid "Protects the contents of the sent message from forwarding and saving" +msgstr "" + +#: of telebot.TeleBot.copy_message:28 telebot.TeleBot.send_animation:37 +#: telebot.TeleBot.send_audio:29 telebot.TeleBot.send_contact:23 +#: telebot.TeleBot.send_dice:15 telebot.TeleBot.send_document:12 +#: telebot.TeleBot.send_game:14 telebot.TeleBot.send_invoice:70 +#: telebot.TeleBot.send_location:17 telebot.TeleBot.send_media_group:18 +#: telebot.TeleBot.send_message:30 telebot.TeleBot.send_photo:28 +#: telebot.TeleBot.send_poll:47 telebot.TeleBot.send_sticker:13 +#: telebot.TeleBot.send_venue:30 telebot.TeleBot.send_video:41 +#: telebot.TeleBot.send_video_note:19 telebot.TeleBot.send_voice:20 +msgid "If the message is a reply, ID of the original message" +msgstr "" + +#: of telebot.TeleBot.copy_message:31 telebot.TeleBot.send_animation:54 +#: telebot.TeleBot.send_audio:54 telebot.TeleBot.send_dice:26 +#: telebot.TeleBot.send_document:38 telebot.TeleBot.send_invoice:84 +#: telebot.TeleBot.send_location:40 telebot.TeleBot.send_media_group:24 +#: telebot.TeleBot.send_message:33 telebot.TeleBot.send_photo:31 +#: telebot.TeleBot.send_sticker:27 telebot.TeleBot.send_video:44 +#: telebot.TeleBot.send_video_note:39 telebot.TeleBot.send_voice:40 +msgid "" +"Pass True, if the message should be sent even if the specified replied-to" +" message is not found" +msgstr "" + +#: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:40 +#: telebot.TeleBot.send_contact:26 telebot.TeleBot.send_dice:18 +#: telebot.TeleBot.send_document:18 telebot.TeleBot.send_game:17 +#: telebot.TeleBot.send_location:20 telebot.TeleBot.send_message:36 +#: telebot.TeleBot.send_photo:34 telebot.TeleBot.send_poll:53 +#: telebot.TeleBot.send_sticker:16 telebot.TeleBot.send_venue:33 +#: telebot.TeleBot.send_video:47 telebot.TeleBot.send_video_note:22 +#: telebot.TeleBot.send_voice:23 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.copy_message:39 telebot.TeleBot.delete_message:19 +#: telebot.TeleBot.edit_message_live_location:23 +#: telebot.TeleBot.forward_message:20 telebot.TeleBot.send_animation:48 +#: telebot.TeleBot.send_audio:42 telebot.TeleBot.send_chat_action:19 +#: telebot.TeleBot.send_contact:31 telebot.TeleBot.send_dice:23 +#: telebot.TeleBot.send_document:29 telebot.TeleBot.send_location:28 +#: telebot.TeleBot.send_media_group:21 telebot.TeleBot.send_message:40 +#: telebot.TeleBot.send_photo:39 telebot.TeleBot.send_sticker:24 +#: telebot.TeleBot.send_venue:38 telebot.TeleBot.send_video:52 +#: telebot.TeleBot.send_video_note:30 telebot.TeleBot.send_voice:34 +#: telebot.TeleBot.stop_message_live_location:19 +msgid "Timeout in seconds for the request." +msgstr "" + +#: of telebot.TeleBot.copy_message:42 telebot.TeleBot.forward_message:23 +#: telebot.TeleBot.send_audio:60 telebot.TeleBot.send_dice:32 +#: telebot.TeleBot.send_location:46 telebot.TeleBot.send_message:43 +#: telebot.TeleBot.send_photo:42 telebot.TeleBot.send_voice:46 +msgid "Identifier of a message thread, in which the message will be sent" +msgstr "" + +#: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:60 +#: telebot.TeleBot.send_audio:63 telebot.TeleBot.send_contact:44 +#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:56 +#: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 +#: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 +#: telebot.TeleBot.send_photo:45 telebot.TeleBot.send_poll:70 +#: telebot.TeleBot.send_sticker:39 telebot.TeleBot.send_venue:57 +#: telebot.TeleBot.send_video:61 telebot.TeleBot.send_video_note:48 +#: telebot.TeleBot.send_voice:49 +msgid "On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 +#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:61 +#: telebot.TeleBot.send_audio:64 telebot.TeleBot.send_contact:45 +#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:57 +#: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 +#: telebot.TeleBot.send_photo:46 telebot.TeleBot.send_sticker:40 +#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:62 +#: telebot.TeleBot.send_video_note:49 +msgid ":class:`telebot.types.Message`" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:1 +msgid "" +"Use this method to create an additional invite link for a chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. The link can be revoked using the " +"method revokeChatInviteLink. Returns the new invite link as " +"ChatInviteLink object." +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createchatinvitelink" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:8 +#: telebot.TeleBot.edit_chat_invite_link:6 +#: telebot.TeleBot.export_chat_invite_link:6 +#: telebot.TeleBot.revoke_chat_invite_link:7 +msgid "" +"Id: Unique identifier for the target chat or username of the target " +"channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:12 +#: telebot.TeleBot.edit_chat_invite_link:10 +msgid "Invite link name; 0-32 characters" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:15 +#: telebot.TeleBot.edit_chat_invite_link:16 +msgid "Point in time (Unix timestamp) when the link will expire" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:18 +#: telebot.TeleBot.edit_chat_invite_link:19 +msgid "Maximum number of users that can be members of the chat simultaneously" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:21 +#: telebot.TeleBot.edit_chat_invite_link:22 +msgid "" +"True, if users joining the chat via the link need to be approved by chat " +"administrators. If True, member_limit can't be specified" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:24 +#: telebot.TeleBot.edit_chat_invite_link:25 +#: telebot.TeleBot.revoke_chat_invite_link:14 +msgid "Returns the new invite link as ChatInviteLink object." +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:25 +#: telebot.TeleBot.edit_chat_invite_link:26 +#: telebot.TeleBot.revoke_chat_invite_link:15 +msgid ":class:`telebot.types.ChatInviteLink`" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:1 +msgid "" +"Use this method to create a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights. Returns information about the " +"created topic as a ForumTopic object." +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createforumtopic" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:10 +msgid "Name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:13 +msgid "" +"Color of the topic icon in RGB format. Currently, must be one of " +"0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:16 +msgid "" +"Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " +"be exactly 1 character long" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:19 +msgid "" +"On success, information about the created topic is returned as a " +"ForumTopic object." +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:20 +msgid ":class:`telebot.types.ForumTopic`" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:1 +msgid "" +"Use this method to create a link for an invoice. Returns the created " +"invoice link as String on success." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createinvoicelink" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:7 telebot.TeleBot.send_invoice:8 +msgid "Product name, 1-32 characters" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:10 telebot.TeleBot.send_invoice:11 +msgid "Product description, 1-255 characters" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:13 telebot.TeleBot.send_invoice:14 +msgid "" +"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " +"the user, use for your internal processes." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:17 telebot.TeleBot.send_invoice:18 +msgid "Payments provider token, obtained via @Botfather" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:20 telebot.TeleBot.send_invoice:21 +msgid "" +"Three-letter ISO 4217 currency code, see " +"https://core.telegram.org/bots/payments#supported-currencies" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:24 telebot.TeleBot.send_invoice:25 +msgid "" +"Price breakdown, a list of components (e.g. product price, tax, discount," +" delivery cost, delivery tax, bonus, etc.)" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:28 telebot.TeleBot.send_invoice:87 +msgid "The maximum accepted amount for tips in the smallest units of the currency" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:31 telebot.TeleBot.send_invoice:90 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:77 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:40 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a photo of the invoice. People like it better when they see a photo of " +"what they are paying for." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:44 telebot.TeleBot.send_invoice:37 +msgid "Photo size in bytes" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:47 telebot.TeleBot.send_invoice:40 +msgid "Photo width" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:50 telebot.TeleBot.send_invoice:43 +msgid "Photo height" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:53 telebot.TeleBot.send_invoice:46 +msgid "Pass True, if you require the user's full name to complete the order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:56 telebot.TeleBot.send_invoice:49 +msgid "Pass True, if you require the user's phone number to complete the order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:59 telebot.TeleBot.send_invoice:52 +msgid "Pass True, if you require the user's email to complete the order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:62 telebot.TeleBot.send_invoice:55 +msgid "" +"Pass True, if you require the user's shipping address to complete the " +"order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:65 telebot.TeleBot.send_invoice:61 +msgid "Pass True, if user's phone number should be sent to provider" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:68 telebot.TeleBot.send_invoice:64 +msgid "Pass True, if user's email address should be sent to provider" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:71 telebot.TeleBot.send_invoice:58 +msgid "Pass True, if the final price depends on the shipping method" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:74 +msgid "Created invoice link as String on success." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:75 +#: telebot.TeleBot.export_chat_invite_link:11 telebot.TeleBot.get_file_url:7 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:1 +msgid "" +"Use this method to create new sticker set owned by a user. The bot will " +"be able to edit the created sticker set. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createnewstickerset" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:10 +msgid "" +"Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " +"animals). Can contain only English letters, digits and underscores. Must " +"begin with a letter, can't contain consecutive underscores and must end " +"in \"_by_\". is case insensitive. 1-64 " +"characters." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:15 +msgid "Sticker set title, 1-64 characters" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:32 +msgid "" +"Pass True, if a set of mask stickers should be created. Deprecated since " +"Bot API 6.2, use sticker_type instead." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:36 +msgid "" +"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " +"emoji sticker sets can't be created via the Bot API at the moment. By " +"default, a regular sticker set is created." +msgstr "" + +#: of telebot.TeleBot.decline_chat_join_request:1 +msgid "" +"Use this method to decline a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.decline_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#declinechatjoinrequest" +msgstr "" + +#: of telebot.TeleBot.delete_chat_photo:1 +msgid "" +"Use this method to delete a chat photo. Photos can't be changed for " +"private chats. The bot must be an administrator in the chat for this to " +"work and must have the appropriate admin rights. Returns True on success." +" Note: In regular groups (non-supergroups), this method will only work if" +" the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.TeleBot.delete_chat_photo:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" +msgstr "" + +#: of telebot.TeleBot.delete_chat_photo:8 telebot.TeleBot.set_chat_photo:9 +#: telebot.TeleBot.unpin_all_chat_messages:7 +msgid "" +"Int or Str: Unique identifier for the target chat or username of the " +"target channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.delete_chat_sticker_set:1 +msgid "" +"Use this method to delete a group sticker set from a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights. Use the field can_set_sticker_set optionally " +"returned in getChat requests to check if the bot can use this method. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletechatstickerset" +msgstr "" + +#: of telebot.TeleBot.delete_forum_topic:1 +msgid "" +"Use this method to delete a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights, unless it is the creator of the " +"topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "" + +#: of telebot.TeleBot.delete_forum_topic:10 +msgid "Identifier of the topic to delete" +msgstr "" + +#: of telebot.TeleBot.delete_message:1 +msgid "" +"Use this method to delete a message, including service messages, with the" +" following limitations: - A message can only be deleted if it was sent " +"less than 48 hours ago. - A dice message in a private chat can only be " +"deleted if it was sent more than 24 hours ago. - Bots can delete outgoing" +" messages in private chats, groups, and supergroups. - Bots can delete " +"incoming messages in private chats. - Bots granted can_post_messages " +"permissions can delete outgoing messages in channels. - If the bot is an " +"administrator of a group, it can delete any message there. - If the bot " +"has can_delete_messages permission in a supergroup or a channel, it can " +"delete any message there. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_message:11 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" +msgstr "" + +#: of telebot.TeleBot.delete_message:16 +msgid "Identifier of the message to delete" +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:1 +msgid "" +"Use this method to delete the list of the bot's commands for the given " +"scope and user language. After deletion, higher level commands will be " +"shown to affected users. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletemycommands" +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:7 telebot.TeleBot.get_my_commands:6 +#: telebot.TeleBot.set_my_commands:8 +msgid "" +"The scope of users for which the commands are relevant. Defaults to " +"BotCommandScopeDefault." +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:11 telebot.TeleBot.get_my_commands:10 +#: telebot.TeleBot.set_my_commands:12 +msgid "" +"A two-letter ISO 639-1 language code. If empty, commands will be applied " +"to all users from the given scope, for whose language there are no " +"dedicated commands" +msgstr "" + +#: of telebot.TeleBot.delete_state:1 +msgid "Delete the current state of a user." +msgstr "" + +#: of telebot.TeleBot.delete_sticker_from_set:1 +msgid "" +"Use this method to delete a sticker from a set created by the bot. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_sticker_from_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletestickerfromset" +msgstr "" + +#: of telebot.TeleBot.delete_sticker_from_set:5 +#: telebot.TeleBot.set_sticker_position_in_set:5 +msgid "File identifier of the sticker" +msgstr "" + +#: of telebot.TeleBot.delete_webhook:1 +msgid "" +"Use this method to remove webhook integration if you decide to switch " +"back to getUpdates. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_webhook:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" +msgstr "" + +#: of telebot.TeleBot.delete_webhook:6 telebot.TeleBot.run_webhooks:36 +#: telebot.TeleBot.set_webhook:36 +msgid "Pass True to drop all pending updates, defaults to None" +msgstr "" + +#: of telebot.TeleBot.delete_webhook:9 telebot.TeleBot.run_webhooks:39 +msgid "Request connection timeout, defaults to None" +msgstr "" + +#: of telebot.TeleBot.disable_save_next_step_handlers:1 +#: telebot.TeleBot.disable_save_reply_handlers:1 +msgid "Disable saving next step handlers (by default saving disable)" +msgstr "" + +#: of telebot.TeleBot.disable_save_next_step_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" disable file saving capability for handlers. For the same purpose, " +"MemoryHandlerBackend is reassigned as a new next_step_backend backend " +"instead of FileHandlerBackend." +msgstr "" + +#: of telebot.TeleBot.disable_save_reply_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" disable file saving capability for handlers. For the same purpose, " +"MemoryHandlerBackend is reassigned as a new reply_backend backend instead" +" of FileHandlerBackend." +msgstr "" + +#: of telebot.TeleBot.download_file:1 +msgid "Downloads file." +msgstr "" + +#: of telebot.TeleBot.download_file:3 +msgid "Path where the file should be downloaded." +msgstr "" + +#: of telebot.TeleBot.download_file:6 +msgid "bytes" +msgstr "" + +#: of telebot.TeleBot.download_file:7 +msgid ":obj:`bytes`" +msgstr "" + +#: of telebot.TeleBot.edit_chat_invite_link:1 +msgid "" +"Use this method to edit a non-primary invite link created by the bot. The" +" bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.edit_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editchatinvitelink" +msgstr "" + +#: of telebot.TeleBot.edit_chat_invite_link:13 +msgid "The invite link to edit" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:1 +msgid "" +"Use this method to edit name and icon of a topic in a forum supergroup " +"chat. The bot must be an administrator in the chat for this to work and " +"must have can_manage_topics administrator rights, unless it is the " +"creator of the topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:5 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:10 +msgid "Identifier of the topic to edit" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:13 +msgid "New name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:16 +msgid "" +"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " +"must be exactly 1 character long" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:1 +msgid "Use this method to edit captions of messages." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagecaption" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:5 +msgid "New caption of the message" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:8 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:11 +msgid "Required if inline_message_id is not specified." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:14 +msgid "" +"Required if inline_message_id is not specified. Identifier of the inline " +"message." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:17 +msgid "New caption of the message, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:20 +msgid "" +"A JSON-serialized array of objects that describe how the caption should " +"be parsed." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:23 +#: telebot.TeleBot.edit_message_media:19 +#: telebot.TeleBot.edit_message_reply_markup:14 +#: telebot.TeleBot.edit_message_text:26 +msgid "A JSON-serialized object for an inline keyboard." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:26 +#: telebot.TeleBot.edit_message_media:22 +#: telebot.TeleBot.edit_message_reply_markup:17 +#: telebot.TeleBot.edit_message_text:29 +msgid "" +"On success, if edited message is sent by the bot, the edited Message is " +"returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:27 +msgid ":obj:`types.Message` | :obj:`bool`" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:3 +msgid "" +"Use this method to edit live location messages. A location can be edited " +"until its live_period expires or editing is explicitly" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:2 +msgid "" +"disabled by a call to stopMessageLiveLocation. On success, if the edited " +"message is not an inline message, the edited Message is returned, " +"otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagelivelocation" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:7 +msgid "Latitude of new location" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:10 +msgid "Longitude of new location" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:16 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" to edit" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:19 +#: telebot.TeleBot.stop_message_live_location:15 +msgid "A JSON-serialized object for a new inline keyboard." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:26 +#: telebot.TeleBot.edit_message_media:16 +#: telebot.TeleBot.edit_message_reply_markup:11 +#: telebot.TeleBot.edit_message_text:14 telebot.TeleBot.get_game_high_scores:19 +#: telebot.TeleBot.set_game_score:20 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:29 +#: telebot.TeleBot.send_location:31 +msgid "The radius of uncertainty for the location, measured in meters; 0-1500" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:32 +msgid "" +"Direction in which the user is moving, in degrees. Must be between 1 and " +"360 if specified." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:35 +msgid "" +"The maximum distance for proximity alerts about approaching another chat " +"member, in meters. Must be between 1 and 100000 if specified." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:38 +msgid "" +"On success, if the edited message is not an inline message, the edited " +"Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:39 +#: telebot.TeleBot.stop_message_live_location:23 +msgid ":class:`telebot.types.Message` or bool" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:1 +msgid "" +"Use this method to edit animation, audio, document, photo, or video " +"messages. If a message is a part of a message album, then it can be " +"edited only to a photo or a video. Otherwise, message type can be changed" +" arbitrarily. When inline message is edited, new file can't be uploaded. " +"Use previously uploaded file via its file_id or specify a URL." +msgstr "" + +#: of telebot.TeleBot.edit_message_media:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagemedia" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:8 +msgid "A JSON-serialized object for a new media content of the message" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:10 +#: telebot.TeleBot.edit_message_reply_markup:5 +#: telebot.TeleBot.edit_message_text:8 telebot.TeleBot.get_game_high_scores:13 +#: telebot.TeleBot.set_game_score:14 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel (in the format " +"@channelusername)" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:13 +#: telebot.TeleBot.edit_message_reply_markup:8 +#: telebot.TeleBot.edit_message_text:11 telebot.TeleBot.get_game_high_scores:16 +#: telebot.TeleBot.set_game_score:17 +msgid "" +"Required if inline_message_id is not specified. Identifier of the sent " +"message" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:23 +#: telebot.TeleBot.edit_message_reply_markup:18 +#: telebot.TeleBot.edit_message_text:30 telebot.TeleBot.set_game_score:27 +msgid ":obj:`types.Message` or :obj:`bool`" +msgstr "" + +#: of telebot.TeleBot.edit_message_reply_markup:1 +msgid "Use this method to edit only the reply markup of messages." +msgstr "" + +#: of telebot.TeleBot.edit_message_reply_markup:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagereplymarkup" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:1 +msgid "Use this method to edit text and game messages." +msgstr "" + +#: of telebot.TeleBot.edit_message_text:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#editmessagetext" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:5 +msgid "New text of the message, 1-4096 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:17 telebot.TeleBot.send_message:15 +msgid "Mode for parsing entities in the message text." +msgstr "" + +#: of telebot.TeleBot.edit_message_text:20 +msgid "" +"List of special entities that appear in the message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:23 telebot.TeleBot.send_message:21 +msgid "Disables link previews for links in this message" +msgstr "" + +#: of telebot.TeleBot.edited_channel_post_handler:1 +msgid "" +"Handles new version of a channel post that is known to the bot and was " +"edited. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.TeleBot.edited_message_handler:1 +msgid "" +"Handles new version of a message that is known to the bot and was edited." +" As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.TeleBot.edited_message_handler:16 +#: telebot.TeleBot.message_handler:47 +msgid "list of chat types" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:1 +msgid "Enable saving next step handlers (by default saving disabled)" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:3 +#: telebot.TeleBot.enable_save_reply_handlers:3 +msgid "" +"This function explicitly assigns FileHandlerBackend (instead of Saver) " +"just to keep backward compatibility whose purpose was to enable file " +"saving capability for handlers. And the same implementation is now " +"available with FileHandlerBackend" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:7 +#: telebot.TeleBot.enable_save_reply_handlers:7 +msgid "Delay between changes in handlers and saving, defaults to 120" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:10 +msgid "Filename of save file, defaults to \"./.handler-saves/step.save\"" +msgstr "" + +#: of telebot.TeleBot.enable_save_reply_handlers:1 +msgid "Enable saving reply handlers (by default saving disable)" +msgstr "" + +#: of telebot.TeleBot.enable_save_reply_handlers:10 +msgid "Filename of save file, defaults to \"./.handler-saves/reply.save\"" +msgstr "" + +#: of telebot.TeleBot.enable_saving_states:1 +msgid "Enable saving states (by default saving disabled)" +msgstr "" + +#: of telebot.TeleBot.enable_saving_states:4 +msgid "" +"It is recommended to pass a :class:`~telebot.storage.StatePickleStorage` " +"instance as state_storage to TeleBot class." +msgstr "" + +#: of telebot.TeleBot.enable_saving_states:7 +msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" +msgstr "" + +#: of telebot.TeleBot.export_chat_invite_link:1 +msgid "" +"Use this method to export an invite link to a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.export_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#exportchatinvitelink" +msgstr "" + +#: of telebot.TeleBot.export_chat_invite_link:10 +msgid "exported invite link as String on success." +msgstr "" + +#: of telebot.TeleBot.forward_message:1 +msgid "Use this method to forward messages of any kind." +msgstr "" + +#: of telebot.TeleBot.forward_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" +msgstr "" + +#: of telebot.TeleBot.forward_message:5 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" + +#: of telebot.TeleBot.forward_message:17 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "" + +#: of telebot.TeleBot.get_chat:1 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.get_chat:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" +msgstr "" + +#: of telebot.TeleBot.get_chat:6 telebot.TeleBot.get_chat_administrators:7 +#: telebot.TeleBot.get_chat_member_count:5 telebot.TeleBot.leave_chat:5 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.get_chat:9 +msgid "Chat information" +msgstr "" + +#: of telebot.TeleBot.get_chat:10 +msgid ":class:`telebot.types.Chat`" +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:1 +msgid "" +"Use this method to get a list of administrators in a chat. On success, " +"returns an Array of ChatMember objects that contains information about " +"all chat administrators except other bots." +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatadministrators" +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:9 +msgid "List made of ChatMember objects." +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:10 +msgid ":obj:`list` of :class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.TeleBot.get_chat_member:1 +msgid "" +"Use this method to get information about a member of a chat. Returns a " +"ChatMember object on success." +msgstr "" + +#: of telebot.TeleBot.get_chat_member:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" +msgstr "" + +#: of telebot.TeleBot.get_chat_member:11 +msgid "Returns ChatMember object on success." +msgstr "" + +#: of telebot.TeleBot.get_chat_member:12 +msgid ":class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:1 +msgid "Use this method to get the number of members in a chat." +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatmembercount" +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:8 +msgid "Number of members in the chat." +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:9 +msgid ":obj:`int`" +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:1 +msgid "" +"Use this method to get the current value of the bot's menu button in a " +"private chat, or the default menu button. Returns MenuButton on success." +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:5 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#getchatmenubutton" +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be returned." +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:11 +msgid "types.MenuButton" +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:12 +msgid ":class:`telebot.types.MenuButton`" +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:1 +msgid "" +"Use this method to get information about custom emoji stickers by their " +"identifiers. Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:4 +msgid "" +"List of custom emoji identifiers. At most 200 custom emoji identifiers " +"can be specified." +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:7 +msgid "Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:8 +msgid ":obj:`list` of :class:`telebot.types.Sticker`" +msgstr "" + +#: of telebot.TeleBot.get_file:1 +msgid "" +"Use this method to get basic info about a file and prepare it for " +"downloading. For the moment, bots can download files of up to 20MB in " +"size. On success, a File object is returned. It is guaranteed that the " +"link will be valid for at least 1 hour. When the link expires, a new one " +"can be requested by calling get_file again." +msgstr "" + +#: of telebot.TeleBot.get_file:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" +msgstr "" + +#: of telebot.TeleBot.get_file:9 +msgid "File identifier" +msgstr "" + +#: of telebot.TeleBot.get_file:12 telebot.TeleBot.upload_sticker_file:14 +msgid ":class:`telebot.types.File`" +msgstr "" + +#: of telebot.TeleBot.get_file_url:1 +msgid "Get a valid URL for downloading a file." +msgstr "" + +#: of telebot.TeleBot.get_file_url:3 +msgid "File identifier to get download URL for." +msgstr "" + +#: of telebot.TeleBot.get_file_url:6 +msgid "URL for downloading the file." +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:1 +msgid "" +"Use this method to get custom emoji stickers, which can be used as a " +"forum topic icon by any user. Requires no parameters. Returns an Array of" +" Sticker objects." +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getforumtopiciconstickers" +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:6 +msgid "On success, a list of StickerSet objects is returned." +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:7 +msgid "List[:class:`telebot.types.StickerSet`]" +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:1 +msgid "" +"Use this method to get data for high score tables. Will return the score " +"of the specified user and several of their neighbors in a game. On " +"success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:4 +msgid "" +"This method will currently return scores for the target user, plus two of" +" their closest neighbors on each side. Will also return the top three " +"users if the user and their neighbors are not among them. Please note " +"that this behavior is subject to change." +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getgamehighscores" +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:10 telebot.TeleBot.retrieve_data:3 +#: telebot.TeleBot.set_game_score:5 telebot.TeleBot.set_sticker_set_thumb:9 +msgid "User identifier" +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:22 +msgid "On success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.TeleBot.get_me:1 +msgid "" +"A simple method for testing your bot's authentication token. Requires no " +"parameters. Returns basic information about the bot in form of a User " +"object." +msgstr "" + +#: of telebot.TeleBot.get_me:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getme" +msgstr "" + +#: of telebot.TeleBot.get_my_commands:1 +msgid "" +"Use this method to get the current list of the bot's commands. Returns " +"List of BotCommand on success." +msgstr "" + +#: of telebot.TeleBot.get_my_commands:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" +msgstr "" + +#: of telebot.TeleBot.get_my_commands:15 +msgid "List of BotCommand on success." +msgstr "" + +#: of telebot.TeleBot.get_my_commands:16 +msgid ":obj:`list` of :class:`telebot.types.BotCommand`" +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:1 +msgid "" +"Use this method to get the current default administrator rights of the " +"bot. Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmydefaultadministratorrights" +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:6 +msgid "" +"Pass True to get the default administrator rights of the bot in channels." +" Otherwise, the default administrator rights of the bot for groups and " +"supergroups will be returned." +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:9 +msgid "Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:10 +msgid ":class:`telebot.types.ChatAdministratorRights`" +msgstr "" + +#: of telebot.TeleBot.get_state:1 +msgid "" +"Gets current state of a user. Not recommended to use this method. But it " +"is ok for debugging." +msgstr "" + +#: of telebot.TeleBot.get_state:10 +msgid "state of a user" +msgstr "" + +#: of telebot.TeleBot.get_state:11 +msgid ":obj:`int` or :obj:`str` or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:1 +msgid "" +"Use this method to get a sticker set. On success, a StickerSet object is " +"returned." +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:8 +msgid "On success, a StickerSet object is returned." +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:9 telebot.TeleBot.set_chat_sticker_set:14 +msgid ":class:`telebot.types.StickerSet`" +msgstr "" + +#: of telebot.TeleBot.get_updates:1 +msgid "" +"Use this method to receive incoming updates using long polling (wiki). An" +" Array of Update objects is returned." +msgstr "" + +#: of telebot.TeleBot.get_updates:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" +msgstr "" + +#: of telebot.TeleBot.get_updates:6 +msgid "" +"Identifier of the first update to be returned. Must be greater by one " +"than the highest among the identifiers of previously received updates. By" +" default, updates starting with the earliest unconfirmed update are " +"returned. An update is considered confirmed as soon as getUpdates is " +"called with an offset higher than its update_id. The negative offset can " +"be specified to retrieve updates starting from -offset update from the " +"end of the updates queue. All previous updates will forgotten." +msgstr "" + +#: of telebot.TeleBot.get_updates:12 +msgid "" +"Limits the number of updates to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.TeleBot.get_updates:15 telebot.TeleBot.get_webhook_info:6 +#: telebot.TeleBot.polling:21 +msgid "Request connection timeout" +msgstr "" + +#: of telebot.TeleBot.get_updates:18 +msgid "Array of string. List the types of updates you want your bot to receive." +msgstr "" + +#: of telebot.TeleBot.get_updates:21 +msgid "Timeout in seconds for long polling." +msgstr "" + +#: of telebot.TeleBot.get_updates:24 +msgid "An Array of Update objects is returned." +msgstr "" + +#: of telebot.TeleBot.get_updates:25 +msgid ":obj:`list` of :class:`telebot.types.Update`" +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:1 +msgid "" +"Use this method to get a list of profile pictures for a user. Returns a " +":class:`telebot.types.UserProfilePhotos` object." +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserprofilephotos" +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:9 +msgid "" +"Sequential number of the first photo to be returned. By default, all " +"photos are returned." +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:12 +msgid "" +"Limits the number of photos to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:15 +msgid "" +"`UserProfilePhotos " +"`_" +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:16 +msgid ":class:`telebot.types.UserProfilePhotos`" +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:1 +msgid "" +"Use this method to get current webhook status. Requires no parameters. On" +" success, returns a WebhookInfo object. If the bot is using getUpdates, " +"will return an object with the url field empty." +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:9 +msgid "On success, returns a WebhookInfo object." +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:10 +msgid ":class:`telebot.types.WebhookInfo`" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:1 +msgid "" +"Wrap polling with infinite loop and exception handling to avoid bot stops" +" polling." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:5 telebot.TeleBot.polling:13 +msgid "Install watchdog and psutil before using restart_on_change option." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:7 +msgid "Request connection timeout." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:10 telebot.TeleBot.polling:27 +msgid "Timeout in seconds for long polling (see API docs)" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:13 telebot.TeleBot.polling:24 +msgid "skip old updates" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:16 telebot.TeleBot.polling:30 +msgid "" +"Custom (different from logger itself) logging level for infinity_polling " +"logging. Use logger levels from logging as a value. None/NOTSET = no " +"error logging" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:20 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. 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." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:29 telebot.TeleBot.polling:47 +msgid "Restart a file on file(s) change. Defaults to False" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:32 +msgid "Path to watch for changes. Defaults to current directory" +msgstr "" + +#: of telebot.TeleBot.inline_handler:1 +msgid "" +"Handles new incoming inline query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.InlineQuery` object." +msgstr "" + +#: of telebot.TeleBot.leave_chat:1 +msgid "" +"Use this method for your bot to leave a group, supergroup or channel. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.leave_chat:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#leavechat" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:1 +msgid "Load next step handlers from save file" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" load handlers from file with the help of FileHandlerBackend and is only " +"recommended to use if next_step_backend was assigned as " +"FileHandlerBackend before entering this function" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:8 +msgid "" +"Filename of the file where handlers was saved, defaults to \"./.handler-" +"saves/step.save\"" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:11 +msgid "" +"If True is passed, after the loading file will be deleted, defaults to " +"True" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:1 +msgid "Load reply handlers from save file" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" load handlers from file with the help of FileHandlerBackend and is only " +"recommended to use if reply_backend was assigned as FileHandlerBackend " +"before entering this function" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:7 +msgid "" +"Filename of the file where handlers was saved, defaults to \"./.handler-" +"saves/reply.save\"" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:10 +msgid "" +"If True is passed, after the loading file will be deleted, defaults to " +"True, defaults to True" +msgstr "" + +#: of telebot.TeleBot.log_out:1 +msgid "" +"Use this method to log out from the cloud Bot API server before launching" +" the bot locally. You MUST log out the bot before running it locally, " +"otherwise there is no guarantee that the bot will receive updates. After " +"a successful call, you can immediately log in on a local server, but will" +" not be able to log in back to the cloud Bot API server for 10 minutes. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.log_out:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" +msgstr "" + +#: of telebot.TeleBot.message_handler:1 +msgid "" +"Handles New incoming message of any kind - text, photo, sticker, etc. As " +"a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object. All message handlers are tested in" +" the order they were added." +msgstr "" + +#: of telebot.TeleBot.message_handler:5 telebot.TeleBot.middleware_handler:7 +#: telebot.TeleBot.register_middleware_handler:6 +msgid "Example:" +msgstr "" + +#: of telebot.TeleBot.message_handler:7 +msgid "Usage of message_handler" +msgstr "" + +#: of telebot.TeleBot.message_handler:40 +msgid "" +"Optional lambda function. The lambda receives the message to test as the " +"first parameter. It must return True if the command should handle the " +"message." +msgstr "" + +#: of telebot.TeleBot.message_handler:52 +#: telebot.TeleBot.register_edited_channel_post_handler:23 +#: telebot.TeleBot.register_inline_handler:14 +#: telebot.TeleBot.register_pre_checkout_query_handler:13 +msgid "decorated function" +msgstr "" + +#: of telebot.TeleBot.middleware_handler:1 +msgid "Function-based middleware handler decorator." +msgstr "" + +#: of telebot.TeleBot.middleware_handler:3 +msgid "" +"This decorator can be used to decorate functions that must be handled as " +"middlewares before entering any other message handlers But, be careful " +"and check type of the update inside the handler if more than one " +"update_type is given" +msgstr "" + +#: of telebot.TeleBot.middleware_handler:9 +msgid "Usage of middleware_handler" +msgstr "" + +#: of telebot.TeleBot.middleware_handler:24 +#: telebot.TeleBot.register_middleware_handler:15 +msgid "" +"Optional list of update types that can be passed into the middleware " +"handler." +msgstr "" + +#: of telebot.TeleBot.middleware_handler:27 +msgid "function" +msgstr "" + +#: of telebot.TeleBot.my_chat_member_handler:1 +msgid "" +"Handles update in a status of a bot. For private chats, this update is " +"received only when the bot is blocked or unblocked by the user. As a " +"parameter to the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:1 +msgid "" +"Use this method to pin a message in a supergroup. The bot must be an " +"administrator in the chat for this to work and must have the appropriate " +"admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:11 +msgid "Identifier of a message to pin" +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:14 +msgid "" +"Pass True, if it is not necessary to send a notification to all group " +"members about the new pinned message" +msgstr "" + +#: of telebot.TeleBot.poll_answer_handler:1 +msgid "" +"Handles change of user's answer in a non-anonymous poll(when user changes" +" the vote). Bots receive new votes only in polls that were sent by the " +"bot itself. As a parameter to the decorator function, it passes " +":class:`telebot.types.PollAnswer` object." +msgstr "" + +#: of telebot.TeleBot.poll_handler:1 +msgid "" +"Handles new state of a poll. Bots receive only updates about stopped " +"polls and polls, which are sent by the bot As a parameter to the " +"decorator function, it passes :class:`telebot.types.Poll` object." +msgstr "" + +#: of telebot.TeleBot.polling:1 +msgid "" +"This function creates a new Thread that calls an internal " +"__retrieve_updates function. This allows the bot to retrieve Updates " +"automatically and notify listeners and message handlers accordingly." +msgstr "" + +#: of telebot.TeleBot.polling:4 +msgid "Warning: Do not call this function more than once!" +msgstr "" + +#: of telebot.TeleBot.polling:6 +msgid "Always gets updates." +msgstr "" + +#: of telebot.TeleBot.polling:8 +msgid "Use :meth:`infinity_polling` instead." +msgstr "" + +#: of telebot.TeleBot.polling:15 +msgid "Delay between two update retrivals" +msgstr "" + +#: of telebot.TeleBot.polling:18 +msgid "Do not stop polling when an ApiException occurs." +msgstr "" + +#: of telebot.TeleBot.polling:34 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. 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." +msgstr "" + +#: of telebot.TeleBot.polling:34 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.TeleBot.polling:40 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.polling:44 +msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." +msgstr "" + +#: of telebot.TeleBot.polling:50 +msgid "Path to watch for changes. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.pre_checkout_query_handler:1 +msgid "" +"New incoming pre-checkout query. Contains full information about " +"checkout. As a parameter to the decorator function, it passes " +":class:`telebot.types.PreCheckoutQuery` object." +msgstr "" + +#: of telebot.TeleBot.process_new_updates:1 +msgid "" +"Processes new updates. Just pass list of subclasses of Update to this " +"method." +msgstr "" + +#: of telebot.TeleBot.process_new_updates:3 +msgid "List of :class:`telebot.types.Update` objects." +msgstr "" + +#: of telebot.TeleBot.process_new_updates +msgid "return None" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:1 +msgid "" +"Use this method to promote or demote a user in a supergroup or a channel." +" The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Pass False for all boolean parameters " +"to demote a user." +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#promotechatmember" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:7 +msgid "" +"Unique identifier for the target chat or username of the target channel (" +" in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:14 +msgid "" +"Pass True, if the administrator can change chat title, photo and other " +"settings" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:17 +msgid "Pass True, if the administrator can create channel posts, channels only" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:20 +msgid "" +"Pass True, if the administrator can edit messages of other users, " +"channels only" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:23 +msgid "Pass True, if the administrator can delete messages of other users" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:26 +msgid "Pass True, if the administrator can invite new users to the chat" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:29 +msgid "Pass True, if the administrator can restrict, ban or unban chat members" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:32 +msgid "Pass True, if the administrator can pin messages, supergroups only" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:35 +msgid "" +"Pass True, if the administrator can add new administrators with a subset " +"of his own privileges or demote administrators that he has promoted, " +"directly or indirectly (promoted by administrators that were appointed by" +" him)" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:40 +msgid "Pass True, if the administrator's presence in the chat is hidden" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:43 +msgid "" +"Pass True, if the administrator can access the chat event log, chat " +"statistics, message statistics in channels, see channel members, see " +"anonymous administrators in supergroups and ignore slow mode. Implied by " +"any other administrator privilege" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:49 +msgid "" +"Pass True, if the administrator can manage voice chats For now, bots can " +"use this privilege only for passing to other administrators." +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:53 +msgid "Deprecated, use can_manage_video_chats." +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:56 +msgid "" +"Pass True if the user is allowed to create, rename, close, and reopen " +"forum topics, supergroups only" +msgstr "" + +#: of telebot.TeleBot.register_callback_query_handler:1 +msgid "Registers callback query handler." +msgstr "" + +#: of telebot.TeleBot.register_callback_query_handler:3 +#: telebot.TeleBot.register_channel_post_handler:3 +#: telebot.TeleBot.register_chat_join_request_handler:3 +#: telebot.TeleBot.register_chat_member_handler:3 +#: telebot.TeleBot.register_chosen_inline_handler:3 +#: telebot.TeleBot.register_edited_channel_post_handler:3 +#: telebot.TeleBot.register_edited_message_handler:3 +#: telebot.TeleBot.register_inline_handler:3 +#: telebot.TeleBot.register_message_handler:3 +#: telebot.TeleBot.register_my_chat_member_handler:3 +#: telebot.TeleBot.register_poll_answer_handler:3 +#: telebot.TeleBot.register_poll_handler:3 +#: telebot.TeleBot.register_pre_checkout_query_handler:3 +#: telebot.TeleBot.register_shipping_query_handler:3 +msgid "function to be called" +msgstr "" + +#: of telebot.TeleBot.register_callback_query_handler:9 +#: telebot.TeleBot.register_channel_post_handler:18 +#: telebot.TeleBot.register_chat_join_request_handler:9 +#: telebot.TeleBot.register_chat_member_handler:9 +#: telebot.TeleBot.register_chosen_inline_handler:9 +#: telebot.TeleBot.register_edited_channel_post_handler:18 +#: telebot.TeleBot.register_edited_message_handler:21 +#: telebot.TeleBot.register_inline_handler:9 +#: telebot.TeleBot.register_message_handler:21 +#: telebot.TeleBot.register_my_chat_member_handler:9 +#: telebot.TeleBot.register_poll_answer_handler:9 +#: telebot.TeleBot.register_poll_handler:9 +#: telebot.TeleBot.register_pre_checkout_query_handler:8 +#: telebot.TeleBot.register_shipping_query_handler:9 +msgid "" +"True if you need to pass TeleBot instance to handler(useful for " +"separating handlers into different files)" +msgstr "" + +#: of telebot.TeleBot.register_channel_post_handler:1 +msgid "Registers channel post message handler." +msgstr "" + +#: of telebot.TeleBot.register_channel_post_handler:9 +#: telebot.TeleBot.register_edited_channel_post_handler:9 +#: telebot.TeleBot.register_edited_message_handler:9 +#: telebot.TeleBot.register_message_handler:9 +msgid "list of commands" +msgstr "" + +#: of telebot.TeleBot.register_channel_post_handler:12 +#: telebot.TeleBot.register_edited_channel_post_handler:12 +#: telebot.TeleBot.register_edited_message_handler:12 +msgid "Regular expression" +msgstr "" + +#: of telebot.TeleBot.register_chat_join_request_handler:1 +msgid "Registers chat join request handler." +msgstr "" + +#: of telebot.TeleBot.register_chat_member_handler:1 +msgid "Registers chat member handler." +msgstr "" + +#: of telebot.TeleBot.register_chat_member_handler:14 +msgid ":return:None" +msgstr "" + +#: of telebot.TeleBot.register_chosen_inline_handler:1 +msgid "Registers chosen inline handler." +msgstr "" + +#: of telebot.TeleBot.register_edited_channel_post_handler:1 +msgid "Registers edited channel post message handler." +msgstr "" + +#: of telebot.TeleBot.register_edited_message_handler:1 +msgid "Registers edited message handler." +msgstr "" + +#: of telebot.TeleBot.register_edited_message_handler:18 +msgid "True for private chat" +msgstr "" + +#: of telebot.TeleBot.register_for_reply:1 +#: telebot.TeleBot.register_for_reply_by_message_id:1 +msgid "" +"Registers a callback function to be notified when a reply to `message` " +"arrives." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:3 +#: telebot.TeleBot.register_for_reply_by_message_id:3 +msgid "" +"Warning: In case `callback` as lambda function, saving reply handlers " +"will not work." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:5 +msgid "The message for which we are awaiting a reply." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:8 +#: telebot.TeleBot.register_for_reply_by_message_id:8 +msgid "" +"The callback function to be called when a reply arrives. Must accept one " +"`message` parameter, which will contain the replied message." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:12 +#: telebot.TeleBot.register_for_reply_by_message_id:12 +msgid "Optional arguments for the callback function." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:13 +#: telebot.TeleBot.register_for_reply_by_message_id:13 +msgid "Optional keyword arguments for the callback function." +msgstr "" + +#: of telebot.TeleBot.register_for_reply_by_message_id:5 +msgid "The id of the message for which we are awaiting a reply." +msgstr "" + +#: of telebot.TeleBot.register_inline_handler:1 +msgid "Registers inline handler." +msgstr "" + +#: of telebot.TeleBot.register_message_handler:1 +msgid "Registers message handler." +msgstr "" + +#: of telebot.TeleBot.register_message_handler:18 +msgid "List of chat types" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:1 +msgid "Adds function-based middleware handler." +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:3 +msgid "" +"This function will register your decorator function. Function-based " +"middlewares are executed before handlers. But, be careful and check type " +"of the update inside the handler if more than one update_type is given" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:8 +msgid "bot = TeleBot('TOKEN')" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:10 +msgid "" +"bot.register_middleware_handler(print_channel_post_text, " +"update_types=['channel_post', 'edited_channel_post'])" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:12 +msgid "Function that will be used as a middleware handler." +msgstr "" + +#: of telebot.TeleBot.register_my_chat_member_handler:1 +msgid "Registers my chat member handler." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:1 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:1 +msgid "" +"Registers a callback function to be notified when new message arrives " +"after `message`." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:3 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:3 +msgid "" +"Warning: In case `callback` as lambda function, saving next step handlers" +" will not work." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:5 +msgid "The message for which we want to handle new message in the same chat." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:8 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:8 +msgid "The callback function which next new message arrives." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:11 +#: telebot.TeleBot.register_next_step_handler:13 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:11 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:13 +msgid "Args to pass in callback func" +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 +msgid "The chat for which we want to handle new message." +msgstr "" + +#: of telebot.TeleBot.register_poll_answer_handler:1 +msgid "Registers poll answer handler." +msgstr "" + +#: of telebot.TeleBot.register_poll_handler:1 +msgid "Registers poll handler." +msgstr "" + +#: of telebot.TeleBot.register_pre_checkout_query_handler:1 +msgid "Registers pre-checkout request handler." +msgstr "" + +#: of telebot.TeleBot.register_shipping_query_handler:1 +msgid "Registers shipping query handler." +msgstr "" + +#: of telebot.TeleBot.remove_webhook:1 +msgid "Deletes webhooks using set_webhook() function." +msgstr "" + +#: of telebot.TeleBot.reopen_forum_topic:1 +msgid "" +"Use this method to reopen a closed topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.reopen_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopenforumtopic" +msgstr "" + +#: of telebot.TeleBot.reopen_forum_topic:10 +msgid "Identifier of the topic to reopen" +msgstr "" + +#: of telebot.TeleBot.reply_to:1 +msgid "" +"Convenience function for `send_message(message.chat.id, text, " +"reply_to_message_id=message.message_id, **kwargs)`" +msgstr "" + +#: of telebot.TeleBot.reply_to:3 +msgid "Instance of :class:`telebot.types.Message`" +msgstr "" + +#: of telebot.TeleBot.reply_to:6 +msgid "Text of the message." +msgstr "" + +#: of telebot.TeleBot.reply_to:9 +msgid "" +"Additional keyword arguments which are passed to " +":meth:`telebot.TeleBot.send_message`" +msgstr "" + +#: of telebot.TeleBot.reset_data:1 +msgid "Reset data for a user in chat." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:1 +msgid "" +"Use this method to restrict a user in a supergroup. The bot must be an " +"administrator in the supergroup for this to work and must have the " +"appropriate admin rights. Pass True for all boolean parameters to lift " +"restrictions from a user." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#restrictchatmember" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:14 +msgid "" +"Date when restrictions will be lifted for the user, unix time. If user is" +" restricted for more than 366 days or less than 30 seconds from the " +"current time, they are considered to be restricted forever" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:19 +msgid "" +"Pass True, if the user can send text messages, contacts, locations and " +"venues" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:22 +msgid "" +"Pass True, if the user can send audios, documents, photos, videos, video " +"notes and voice notes, implies can_send_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:26 +msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:29 +msgid "" +"Pass True, if the user can send animations, games, stickers and use " +"inline bots, implies can_send_media_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:32 +msgid "" +"Pass True, if the user may add web page previews to their messages, " +"implies can_send_media_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:36 +msgid "" +"Pass True, if the user is allowed to change the chat title, photo and " +"other settings. Ignored in public supergroups" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:40 +msgid "" +"Pass True, if the user is allowed to invite new users to the chat, " +"implies can_invite_users" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:44 +msgid "" +"Pass True, if the user is allowed to pin messages. Ignored in public " +"supergroups" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:47 +#: telebot.TeleBot.set_chat_permissions:14 telebot.TeleBot.unban_chat_member:19 +msgid "True on success" +msgstr "" + +#: of telebot.TeleBot.retrieve_data:1 +msgid "Returns context manager with data for a user in chat." +msgstr "" + +#: of telebot.TeleBot.retrieve_data:6 +msgid "Chat's unique identifier, defaults to user_id" +msgstr "" + +#: of telebot.TeleBot.retrieve_data:9 +msgid "Context manager with data for a user in chat" +msgstr "" + +#: of telebot.TeleBot.revoke_chat_invite_link:1 +msgid "" +"Use this method to revoke an invite link created by the bot. Note: If the" +" primary link is revoked, a new link is automatically generated The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.revoke_chat_invite_link:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#revokechatinvitelink" +msgstr "" + +#: of telebot.TeleBot.revoke_chat_invite_link:11 +msgid "The invite link to revoke" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:1 +msgid "This class sets webhooks and listens to a given url and port." +msgstr "" + +#: of telebot.TeleBot.run_webhooks:3 +msgid "Requires fastapi, uvicorn, and latest version of starlette." +msgstr "" + +#: of telebot.TeleBot.run_webhooks:5 +msgid "IP address to listen to, defaults to \"127.0.0.1\"" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:8 +msgid "A port which will be used to listen to webhooks., defaults to 443" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:11 +msgid "Path to the webhook. Defaults to /token, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:14 +msgid "Path to the certificate file, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:17 +msgid "Path to the certificate key file, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:20 +msgid "Webhook URL to be set, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:23 +msgid "" +"Maximum allowed number of simultaneous HTTPS connections to the webhook " +"for update delivery, 1-100. Defaults to 40. Use lower values to limit the" +" load on your bot's server, and higher values to increase your bot's " +"throughput., defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:28 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all updates " +"regardless of type (default). If not specified, the previous setting will" +" be used. defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:33 telebot.TeleBot.set_webhook:32 +msgid "" +"The fixed IP address which will be used to send webhook requests instead " +"of the IP address resolved through DNS, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:42 +msgid "Secret token to be used to verify the webhook request, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:45 +msgid "Length of a secret token, defaults to 20" +msgstr "" + +#: of telebot.TeleBot.run_webhooks telebot.custom_filters.TextFilter +msgid "Raises" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:48 +msgid "If necessary libraries were not installed." +msgstr "" + +#: of telebot.TeleBot.send_animation:1 +msgid "" +"Use this method to send animation files (GIF or H.264/MPEG-4 AVC video " +"without sound). On success, the sent Message is returned. Bots can " +"currently send animation files of up to 50 MB in size, this limit may be " +"changed in the future." +msgstr "" + +#: of telebot.TeleBot.send_animation:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" +msgstr "" + +#: of telebot.TeleBot.send_animation:9 +msgid "" +"Animation to send. Pass a file_id as String to send an animation that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an animation from the Internet, or upload a " +"new animation using multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.send_animation:13 +msgid "Duration of sent animation in seconds" +msgstr "" + +#: of telebot.TeleBot.send_animation:16 +msgid "Animation width" +msgstr "" + +#: of telebot.TeleBot.send_animation:19 +msgid "Animation height" +msgstr "" + +#: of telebot.TeleBot.send_animation:22 telebot.TeleBot.send_video:20 +#: telebot.TeleBot.send_video_note:33 +msgid "" +"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 ." +msgstr "" + +#: of telebot.TeleBot.send_animation:28 +msgid "" +"Animation caption (may also be used when resending animation by file_id)," +" 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_animation:31 +msgid "Mode for parsing entities in the animation caption" +msgstr "" + +#: of telebot.TeleBot.send_animation:51 telebot.TeleBot.send_video:29 +msgid "" +"List of special entities that appear in the caption, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_animation:57 telebot.TeleBot.send_video:58 +msgid "Identifier of a message thread, in which the video will be sent" +msgstr "" + +#: of telebot.TeleBot.send_audio:1 +msgid "" +"Use this method to send audio files, if you want Telegram clients to " +"display them in the music player. Your audio must be in the .MP3 or .M4A " +"format. On success, the sent Message is returned. Bots can currently send" +" audio files of up to 50 MB in size, this limit may be changed in the " +"future." +msgstr "" + +#: of telebot.TeleBot.send_audio:5 +msgid "For sending voice messages, use the send_voice method instead." +msgstr "" + +#: of telebot.TeleBot.send_audio:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendaudio" +msgstr "" + +#: of telebot.TeleBot.send_audio:12 +msgid "" +"Audio file to send. Pass a file_id as String to send an audio file that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an audio file from the Internet, or upload a " +"new one using multipart/form-data. Audio must be in the .MP3 or .M4A " +"format." +msgstr "" + +#: of telebot.TeleBot.send_audio:17 +msgid "Audio caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_audio:20 +msgid "Duration of the audio in seconds" +msgstr "" + +#: of telebot.TeleBot.send_audio:23 +msgid "Performer" +msgstr "" + +#: of telebot.TeleBot.send_audio:26 +msgid "Track name" +msgstr "" + +#: of telebot.TeleBot.send_audio:36 +msgid "" +"Mode for parsing entities in the audio caption. See formatting options " +"for more details." +msgstr "" + +#: of telebot.TeleBot.send_audio:45 +msgid "" +"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 " +msgstr "" + +#: of telebot.TeleBot.send_audio:51 telebot.TeleBot.send_document:35 +#: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:37 +msgid "" +"A JSON-serialized list of special entities that appear in the caption, " +"which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_chat_action:1 +msgid "" +"Use this method when you need to tell the user that something is " +"happening on the bot's side. The status is set for 5 seconds or less " +"(when a message arrives from your bot, Telegram clients clear its typing " +"status). Returns True on success." +msgstr "" + +#: of telebot.TeleBot.send_chat_action:5 +msgid "" +"Example: The ImageBot needs some time to process a request and upload the" +" image. Instead of sending a text message along the lines of “Retrieving " +"image, please wait…”, the bot may use sendChatAction with action = " +"upload_photo. The user will see a “sending photo” status for the bot." +msgstr "" + +#: of telebot.TeleBot.send_chat_action:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" +msgstr "" + +#: of telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 +#: telebot.TeleBot.send_poll:6 telebot.TeleBot.send_venue:5 +#: telebot.TeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "" + +#: of telebot.TeleBot.send_chat_action:13 +msgid "" +"Type of action to broadcast. Choose one, depending on what the user is " +"about to receive: typing for text messages, upload_photo for photos, " +"record_video or upload_video for videos, record_voice or upload_voice for" +" voice notes, upload_document for general files, choose_sticker for " +"stickers, find_location for location data, record_video_note or " +"upload_video_note for video notes." +msgstr "" + +#: of telebot.TeleBot.send_contact:1 +msgid "" +"Use this method to send phone contacts. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.TeleBot.send_contact:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" +msgstr "" + +#: of telebot.TeleBot.send_contact:8 +msgid "Contact's phone number" +msgstr "" + +#: of telebot.TeleBot.send_contact:11 +msgid "Contact's first name" +msgstr "" + +#: of telebot.TeleBot.send_contact:14 +msgid "Contact's last name" +msgstr "" + +#: of telebot.TeleBot.send_contact:17 +msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" +msgstr "" + +#: of telebot.TeleBot.send_contact:34 telebot.TeleBot.send_game:23 +#: telebot.TeleBot.send_venue:41 +msgid "" +"Pass True, if the message should be sent even if one of the specified " +"replied-to messages is not found." +msgstr "" + +#: of telebot.TeleBot.send_contact:41 telebot.TeleBot.send_venue:54 +msgid "The thread identifier of a message from which the reply will be sent" +msgstr "" + +#: of telebot.TeleBot.send_dice:1 +msgid "" +"Use this method to send an animated emoji that will display a random " +"value. On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_dice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" +msgstr "" + +#: of telebot.TeleBot.send_dice:8 +msgid "" +"Emoji on which the dice throw animation is based. Currently, must be one " +"of “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, or “🎰”. Dice can have values 1-6 for “🎲”, “🎯”" +" and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " +"to “🎲”" +msgstr "" + +#: of telebot.TeleBot.send_dice:29 +msgid "Protects the contents of the sent message from forwarding" +msgstr "" + +#: of telebot.TeleBot.send_document:1 +msgid "Use this method to send general files." +msgstr "" + +#: of telebot.TeleBot.send_document:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddocument" +msgstr "" + +#: of telebot.TeleBot.send_document:8 +msgid "" +"(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" +msgstr "" + +#: of telebot.TeleBot.send_document:15 +msgid "" +"Document caption (may also be used when resending documents by file_id), " +"0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_document:23 +msgid "Mode for parsing entities in the document caption" +msgstr "" + +#: of telebot.TeleBot.send_document:32 +msgid "" +"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 " +msgstr "" + +#: of telebot.TeleBot.send_document:41 +msgid "" +"allows to define file name that will be visible in the Telegram instead " +"of original file name" +msgstr "" + +#: of telebot.TeleBot.send_document:44 +msgid "" +"Disables automatic server-side content type detection for files uploaded " +"using multipart/form-data" +msgstr "" + +#: of telebot.TeleBot.send_document:47 telebot.TeleBot.send_sticker:33 +#: telebot.TeleBot.send_video:55 +msgid "function typo miss compatibility: do not use it" +msgstr "" + +#: of telebot.TeleBot.send_document:53 telebot.TeleBot.send_sticker:36 +msgid "The thread to which the message will be sent" +msgstr "" + +#: of telebot.TeleBot.send_game:1 +msgid "Used to send the game." +msgstr "" + +#: of telebot.TeleBot.send_game:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendgame" +msgstr "" + +#: of telebot.TeleBot.send_game:8 +msgid "" +"Short name of the game, serves as the unique identifier for the game. Set" +" up your games via @BotFather." +msgstr "" + +#: of telebot.TeleBot.send_game:20 +msgid "Timeout in seconds for waiting for a response from the bot." +msgstr "" + +#: of telebot.TeleBot.send_game:26 +msgid "" +"Pass True, if content of the message needs to be protected from being " +"viewed by the bot." +msgstr "" + +#: of telebot.TeleBot.send_game:29 +msgid "" +"The identifier of a message thread, in which the game message will be " +"sent." +msgstr "" + +#: of telebot.TeleBot.send_game:33 telebot.TeleBot.send_invoice:102 +#: telebot.TeleBot.send_poll:71 +msgid ":obj:`types.Message`" +msgstr "" + +#: of telebot.TeleBot.send_invoice:1 +msgid "Sends invoice." +msgstr "" + +#: of telebot.TeleBot.send_invoice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendinvoice" +msgstr "" + +#: of telebot.TeleBot.send_invoice:5 +msgid "Unique identifier for the target private chat" +msgstr "" + +#: of telebot.TeleBot.send_invoice:29 +msgid "" +"Unique deep-linking parameter that can be used to generate this invoice " +"when used as a start parameter" +msgstr "" + +#: of telebot.TeleBot.send_invoice:33 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a marketing image for a service. People like it better when they see what" +" they are paying for." +msgstr "" + +#: of telebot.TeleBot.send_invoice:73 +msgid "" +"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" +msgstr "" + +#: of telebot.TeleBot.send_invoice:81 telebot.TeleBot.set_webhook:39 +msgid "Timeout of a request, defaults to None" +msgstr "" + +#: of telebot.TeleBot.send_invoice:98 +msgid "" +"The identifier of a message thread, in which the invoice message will be " +"sent" +msgstr "" + +#: of telebot.TeleBot.send_location:1 +msgid "" +"Use this method to send point on the map. On success, the sent Message is" +" returned." +msgstr "" + +#: of telebot.TeleBot.send_location:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendlocation" +msgstr "" + +#: of telebot.TeleBot.send_location:8 +msgid "Latitude of the location" +msgstr "" + +#: of telebot.TeleBot.send_location:11 +msgid "Longitude of the location" +msgstr "" + +#: of telebot.TeleBot.send_location:14 +msgid "" +"Period in seconds for which the location will be updated (see Live " +"Locations, should be between 60 and 86400." +msgstr "" + +#: of telebot.TeleBot.send_location:34 +msgid "" +"For live locations, a direction in which the user is moving, in degrees. " +"Must be between 1 and 360 if specified." +msgstr "" + +#: of telebot.TeleBot.send_location:37 +msgid "" +"For live locations, a maximum distance for proximity alerts about " +"approaching another chat member, in meters. Must be between 1 and 100000 " +"if specified." +msgstr "" + +#: of telebot.TeleBot.send_media_group:1 +msgid "" +"Use this method to send a group of photos, videos, documents or audios as" +" an album. Documents and audio files can be only grouped in an album with" +" messages of the same type. On success, an array of Messages that were " +"sent is returned." +msgstr "" + +#: of telebot.TeleBot.send_media_group:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" +msgstr "" + +#: of telebot.TeleBot.send_media_group:9 +msgid "" +"A JSON-serialized array describing messages to be sent, must include 2-10" +" items" +msgstr "" + +#: of telebot.TeleBot.send_media_group:12 +msgid "" +"Sends the messages silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.TeleBot.send_media_group:27 +msgid "Identifier of a message thread, in which the media group will be sent" +msgstr "" + +#: of telebot.TeleBot.send_media_group:30 +msgid "On success, an array of Messages that were sent is returned." +msgstr "" + +#: of telebot.TeleBot.send_message:1 +msgid "Use this method to send text messages." +msgstr "" + +#: of telebot.TeleBot.send_message:3 +msgid "" +"Warning: Do not send more than about 4096 characters each message, " +"otherwise you'll risk an HTTP 414 error. If you must send more than 4096 " +"characters, use the `split_string` or `smart_split` function in util.py." +msgstr "" + +#: of telebot.TeleBot.send_message:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" +msgstr "" + +#: of telebot.TeleBot.send_message:12 +msgid "Text of the message to be sent" +msgstr "" + +#: of telebot.TeleBot.send_message:18 +msgid "" +"List of special entities that appear in message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_message:27 +msgid "" +"If True, the message content will be hidden for all users except for the " +"target user" +msgstr "" + +#: of telebot.TeleBot.send_photo:1 +msgid "Use this method to send photos. On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_photo:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" +msgstr "" + +#: of telebot.TeleBot.send_photo:8 +msgid "" +"Photo to send. Pass a file_id as String to send a photo that exists on " +"the Telegram servers (recommended), pass an HTTP URL as a String for " +"Telegram to get a photo from the Internet, or upload a new photo using " +"multipart/form-data. The photo must be at most 10 MB in size. The photo's" +" width and height must not exceed 10000 in total. Width and height ratio " +"must be at most 20." +msgstr "" + +#: of telebot.TeleBot.send_photo:13 +msgid "" +"Photo caption (may also be used when resending photos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_photo:16 +msgid "Mode for parsing entities in the photo caption." +msgstr "" + +#: of telebot.TeleBot.send_poll:1 +msgid "" +"Use this method to send a native poll. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.TeleBot.send_poll:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" +msgstr "" + +#: of telebot.TeleBot.send_poll:9 +msgid "Poll question, 1-300 characters" +msgstr "" + +#: of telebot.TeleBot.send_poll:12 +msgid "" +"A JSON-serialized list of answer options, 2-10 strings 1-100 characters " +"each" +msgstr "" + +#: of telebot.TeleBot.send_poll:15 +msgid "True, if the poll needs to be anonymous, defaults to True" +msgstr "" + +#: of telebot.TeleBot.send_poll:18 +msgid "Poll type, “quiz” or “regular”, defaults to “regular”" +msgstr "" + +#: of telebot.TeleBot.send_poll:21 +msgid "" +"True, if the poll allows multiple answers, ignored for polls in quiz " +"mode, defaults to False" +msgstr "" + +#: of telebot.TeleBot.send_poll:24 +msgid "" +"0-based identifier of the correct answer option. Available only for polls" +" in quiz mode, defaults to None" +msgstr "" + +#: of telebot.TeleBot.send_poll:28 +msgid "" +"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" +msgstr "" + +#: of telebot.TeleBot.send_poll:32 +msgid "" +"Mode for parsing entities in the explanation. See formatting options for " +"more details." +msgstr "" + +#: of telebot.TeleBot.send_poll:35 +msgid "" +"Amount of time in seconds the poll will be active after creation, 5-600. " +"Can't be used together with close_date." +msgstr "" + +#: of telebot.TeleBot.send_poll:38 +msgid "Point in time (Unix timestamp) when the poll will be automatically closed." +msgstr "" + +#: of telebot.TeleBot.send_poll:41 +msgid "" +"Pass True, if the poll needs to be immediately closed. This can be useful" +" for poll preview." +msgstr "" + +#: of telebot.TeleBot.send_poll:50 +msgid "Pass True, if the poll allows multiple options to be voted simultaneously." +msgstr "" + +#: of telebot.TeleBot.send_poll:57 +msgid "Timeout in seconds for waiting for a response from the user." +msgstr "" + +#: of telebot.TeleBot.send_poll:60 +msgid "" +"A JSON-serialized list of special entities that appear in the " +"explanation, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_poll:67 +msgid "The identifier of a message thread, in which the poll will be sent" +msgstr "" + +#: of telebot.TeleBot.send_sticker:1 +msgid "" +"Use this method to send static .WEBP, animated .TGS, or video .WEBM " +"stickers. On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_sticker:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendsticker" +msgstr "" + +#: of telebot.TeleBot.send_sticker:9 +msgid "" +"Sticker 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 .webp file from the Internet, or upload a new one using" +" multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.send_sticker:21 +msgid "to disable the notification" +msgstr "" + +#: of telebot.TeleBot.send_venue:1 +msgid "" +"Use this method to send information about a venue. On success, the sent " +"Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_venue:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" +msgstr "" + +#: of telebot.TeleBot.send_venue:8 +msgid "Latitude of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:11 +msgid "Longitude of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:14 +msgid "Name of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:17 +msgid "Address of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:20 +msgid "Foursquare identifier of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:23 +msgid "" +"Foursquare type of the venue, if known. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.TeleBot.send_venue:45 +msgid "Google Places identifier of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:48 +msgid "Google Places type of the venue." +msgstr "" + +#: of telebot.TeleBot.send_video:1 +msgid "" +"Use this method to send video files, Telegram clients support mp4 videos " +"(other formats may be sent as Document)." +msgstr "" + +#: of telebot.TeleBot.send_video:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" +msgstr "" + +#: of telebot.TeleBot.send_video:8 +msgid "" +"Video to send. You can either pass a file_id as String to resend a video " +"that is already on the Telegram servers, or upload a new video file using" +" multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.send_video:11 telebot.TeleBot.send_video_note:13 +msgid "Duration of sent video in seconds" +msgstr "" + +#: of telebot.TeleBot.send_video:14 +msgid "Video width" +msgstr "" + +#: of telebot.TeleBot.send_video:17 +msgid "Video height" +msgstr "" + +#: of telebot.TeleBot.send_video:23 +msgid "" +"Video caption (may also be used when resending videos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_video:26 +msgid "Mode for parsing entities in the video caption" +msgstr "" + +#: of telebot.TeleBot.send_video:32 +msgid "Pass True, if the uploaded video is suitable for streaming" +msgstr "" + +#: of telebot.TeleBot.send_video_note:1 +msgid "" +"As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " +"to 1 minute long. Use this method to send video messages. On success, the" +" sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_video_note:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" +msgstr "" + +#: of telebot.TeleBot.send_video_note:9 +msgid "" +"Video note to send. Pass a file_id as String to send a video note that " +"exists on the Telegram servers (recommended) or upload a new video using " +"multipart/form-data. Sending video notes by a URL is currently " +"unsupported" +msgstr "" + +#: of telebot.TeleBot.send_video_note:16 +msgid "Video width and height, i.e. diameter of the video message" +msgstr "" + +#: of telebot.TeleBot.send_video_note:45 +msgid "Identifier of a message thread, in which the video note will be sent" +msgstr "" + +#: of telebot.TeleBot.send_voice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.send_voice:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" +msgstr "" + +#: of telebot.TeleBot.send_voice:10 +msgid "" +"Audio 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." +msgstr "" + +#: of telebot.TeleBot.send_voice:14 +msgid "Voice message caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_voice:17 +msgid "Duration of the voice message in seconds" +msgstr "" + +#: of telebot.TeleBot.send_voice:28 +msgid "" +"Mode for parsing entities in the voice message caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.TeleBot.set_chat_administrator_custom_title:1 +msgid "" +"Use this method to set a custom title for an administrator in a " +"supergroup promoted by the bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_chat_administrator_custom_title:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatadministratorcustomtitle" +msgstr "" + +#: of telebot.TeleBot.set_chat_administrator_custom_title:13 +msgid "" +"New custom title for the administrator; 0-16 characters, emoji are not " +"allowed" +msgstr "" + +#: of telebot.TeleBot.set_chat_description:1 +msgid "" +"Use this method to change the description of a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.set_chat_description:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatdescription" +msgstr "" + +#: of telebot.TeleBot.set_chat_description:10 +msgid "Str: New chat description, 0-255 characters" +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:1 +msgid "" +"Use this method to change the bot's menu button in a private chat, or the" +" default menu button. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatmenubutton" +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be changed." +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:11 +msgid "" +"A JSON-serialized object for the new bot's menu button. Defaults to " +"MenuButtonDefault" +msgstr "" + +#: of telebot.TeleBot.set_chat_permissions:1 +msgid "" +"Use this method to set default chat permissions for all members. The bot " +"must be an administrator in the group or a supergroup for this to work " +"and must have the can_restrict_members admin rights." +msgstr "" + +#: of telebot.TeleBot.set_chat_permissions:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatpermissions" +msgstr "" + +#: of telebot.TeleBot.set_chat_permissions:11 +msgid "New default chat permissions" +msgstr "" + +#: of telebot.TeleBot.set_chat_photo:1 +msgid "" +"Use this method to set a new profile photo for the chat. Photos can't be " +"changed for private chats. The bot must be an administrator in the chat " +"for this to work and must have the appropriate admin rights. Returns True" +" on success. Note: In regular groups (non-supergroups), this method will " +"only work if the ‘All Members Are Admins’ setting is off in the target " +"group." +msgstr "" + +#: of telebot.TeleBot.set_chat_photo:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" +msgstr "" + +#: of telebot.TeleBot.set_chat_photo:13 +msgid "InputFile: New chat photo, uploaded using multipart/form-data" +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:1 +msgid "" +"Use this method to set a new group sticker set for a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. Use the field can_set_sticker_set " +"optionally returned in getChat requests to check if the bot can use this " +"method. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatstickerset" +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:10 +msgid "Name of the sticker set to be set as the group sticker set" +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:13 +msgid "StickerSet object" +msgstr "" + +#: of telebot.TeleBot.set_chat_title:1 +msgid "" +"Use this method to change the title of a chat. Titles can't be changed " +"for private chats. The bot must be an administrator in the chat for this " +"to work and must have the appropriate admin rights. Returns True on " +"success. Note: In regular groups (non-supergroups), this method will only" +" work if the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.TeleBot.set_chat_title:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" +msgstr "" + +#: of telebot.TeleBot.set_chat_title:13 +msgid "New chat title, 1-255 characters" +msgstr "" + +#: of telebot.TeleBot.set_game_score:1 +msgid "Sets the value of points in the game to a specific user." +msgstr "" + +#: of telebot.TeleBot.set_game_score:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setgamescore" +msgstr "" + +#: of telebot.TeleBot.set_game_score:8 +msgid "New score, must be non-negative" +msgstr "" + +#: of telebot.TeleBot.set_game_score:11 +msgid "" +"Pass True, if the high score is allowed to decrease. This can be useful " +"when fixing mistakes or banning cheaters" +msgstr "" + +#: of telebot.TeleBot.set_game_score:23 +msgid "" +"Pass True, if the game message should not be automatically edited to " +"include the current scoreboard" +msgstr "" + +#: of telebot.TeleBot.set_game_score:26 +msgid "" +"On success, if the message was sent by the bot, returns the edited " +"Message, otherwise returns True." +msgstr "" + +#: of telebot.TeleBot.set_my_commands:1 +msgid "Use this method to change the list of the bot's commands." +msgstr "" + +#: of telebot.TeleBot.set_my_commands:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmycommands" +msgstr "" + +#: of telebot.TeleBot.set_my_commands:5 +msgid "List of BotCommand. At most 100 commands can be specified." +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:1 +msgid "" +"Use this method to change the default administrator rights requested by " +"the bot when it's added as an administrator to groups or channels. These " +"rights will be suggested to users, but they are are free to modify the " +"list before adding the bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:7 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmydefaultadministratorrights" +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:9 +msgid "" +"A JSON-serialized object describing new default administrator rights. If " +"not specified, the default administrator rights will be cleared." +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:13 +msgid "" +"Pass True to change the default administrator rights of the bot in " +"channels. Otherwise, the default administrator rights of the bot for " +"groups and supergroups will be changed." +msgstr "" + +#: of telebot.TeleBot.set_state:1 +msgid "Sets a new state of a user." +msgstr "" + +#: of telebot.TeleBot.set_state:5 +msgid "" +"You should set both user id and chat id in order to set state for a user " +"in a chat. Otherwise, if you only set user_id, chat_id will equal to " +"user_id, this means that state will be set for the user in his private " +"chat with a bot." +msgstr "" + +#: of telebot.TeleBot.set_state:12 +msgid "new state. can be string, integer, or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.TeleBot.set_sticker_position_in_set:1 +msgid "" +"Use this method to move a sticker in a set created by the bot to a " +"specific position . Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_position_in_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickerpositioninset" +msgstr "" + +#: of telebot.TeleBot.set_sticker_position_in_set:8 +msgid "New sticker position in the set, zero-based" +msgstr "" + +#: of telebot.TeleBot.set_sticker_set_thumb:1 +msgid "" +"Use this method to set the thumbnail of a sticker set. Animated " +"thumbnails can be set for animated sticker sets only. Returns True on " +"success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_set_thumb:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickersetthumb" +msgstr "" + +#: of telebot.TeleBot.set_update_listener:1 +msgid "Sets a listener function to be called when a new update is received." +msgstr "" + +#: of telebot.TeleBot.set_update_listener:3 +msgid "Listener function." +msgstr "" + +#: of telebot.TeleBot.set_webhook:1 +msgid "" +"Use this method to specify a URL and receive incoming updates via an " +"outgoing webhook. Whenever there is an update for the bot, we will send " +"an HTTPS POST request to the specified URL, containing a JSON-serialized " +"Update. In case of an unsuccessful request, we will give up after a " +"reasonable amount of attempts. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_webhook:6 +msgid "" +"If you'd like to make sure that the webhook was set by you, you can " +"specify secret data in the parameter secret_token. If specified, the " +"request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the " +"secret token as content." +msgstr "" + +#: of telebot.TeleBot.set_webhook:9 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#setwebhook" +msgstr "" + +#: of telebot.TeleBot.set_webhook:11 +msgid "" +"HTTPS URL to send updates to. Use an empty string to remove webhook " +"integration, defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:14 +msgid "" +"Upload your public key certificate so that the root certificate in use " +"can be checked, defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:17 +msgid "" +"The maximum allowed number of simultaneous HTTPS connections to the " +"webhook for update delivery, 1-100. Defaults to 40. Use lower values to " +"limit the load on your bot's server, and higher values to increase your " +"bot's throughput, defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. Please note that this parameter doesn't affect " +"updates created before the call to the setWebhook, so unwanted updates " +"may be received for a short period of time. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.TeleBot.set_webhook:27 +msgid "" +"Please note that this parameter doesn't affect updates created before the" +" call to the setWebhook, so unwanted updates may be received for a short " +"period of time. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:42 +msgid "" +"A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” " +"in every webhook request, 1-256 characters. Only characters A-Z, a-z, " +"0-9, _ and - are allowed. The header is useful to ensure that the request" +" comes from a webhook set by you. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:47 +msgid ":obj:`bool` if the request was successful." +msgstr "" + +#: of telebot.TeleBot.setup_middleware:1 +msgid "Registers class-based middleware." +msgstr "" + +#: of telebot.TeleBot.setup_middleware:3 +msgid "Subclass of :class:`telebot.handler_backends.BaseMiddleware`" +msgstr "" + +#: of telebot.TeleBot.shipping_query_handler:1 +msgid "" +"Handles new incoming shipping query. Only for invoices with flexible " +"price. As a parameter to the decorator function, it passes " +":class:`telebot.types.ShippingQuery` object." +msgstr "" + +#: of telebot.TeleBot.stop_bot:1 +msgid "Stops bot by stopping polling and closing the worker pool." +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:1 +msgid "" +"Use this method to stop updating a live location message before " +"live_period expires. On success, if the message is not an inline message," +" the edited Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#stopmessagelivelocation" +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:9 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" with live location to stop" +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:12 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message with live location to stop" +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:22 +msgid "" +"On success, if the message is not an inline message, the edited Message " +"is returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.stop_poll:1 +msgid "" +"Use this method to stop a poll which was sent by the bot. On success, the" +" stopped Poll is returned." +msgstr "" + +#: of telebot.TeleBot.stop_poll:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" +msgstr "" + +#: of telebot.TeleBot.stop_poll:8 +msgid "Identifier of the original message with the poll" +msgstr "" + +#: of telebot.TeleBot.stop_poll:11 +msgid "A JSON-serialized object for a new message markup." +msgstr "" + +#: of telebot.TeleBot.stop_poll:14 +msgid "On success, the stopped Poll is returned." +msgstr "" + +#: of telebot.TeleBot.stop_poll:15 +msgid ":obj:`types.Poll`" +msgstr "" + +#: of telebot.TeleBot.stop_polling:1 +msgid "Stops polling." +msgstr "" + +#: of telebot.TeleBot.stop_polling:3 +msgid "Does not accept any arguments." +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:1 +msgid "" +"Use this method to unban a previously kicked user in a supergroup or " +"channel. The user will not return to the group or channel automatically, " +"but will be able to join via link, etc. The bot must be an administrator " +"for this to work. By default, this method guarantees that after the call " +"the user is not a member of the chat, but will be able to join it. So if " +"the user is a member of the chat they will also be removed from the chat." +" If you don't want this, use the parameter only_if_banned." +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember" +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:9 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @username)" +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:16 +msgid "Do nothing if the user is not banned" +msgstr "" + +#: of telebot.TeleBot.unban_chat_sender_chat:1 +msgid "" +"Use this method to unban a previously banned channel chat in a supergroup" +" or channel. The bot must be an administrator for this to work and must " +"have the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unban_chat_sender_chat:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unbanchatsenderchat" +msgstr "" + +#: of telebot.TeleBot.unban_chat_sender_chat:11 +msgid "Unique identifier of the target sender chat." +msgstr "" + +#: of telebot.TeleBot.unpin_all_chat_messages:1 +msgid "" +"Use this method to unpin a all pinned messages in a supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unpin_all_chat_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallchatmessages" +msgstr "" + +#: of telebot.TeleBot.unpin_all_forum_topic_messages:1 +msgid "" +"Use this method to clear the list of pinned messages in a forum topic. " +"The bot must be an administrator in the chat for this to work and must " +"have the can_pin_messages administrator right in the supergroup. Returns " +"True on success." +msgstr "" + +#: of telebot.TeleBot.unpin_all_forum_topic_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" +msgstr "" + +#: of telebot.TeleBot.unpin_all_forum_topic_messages:10 +msgid "Identifier of the topic" +msgstr "" + +#: of telebot.TeleBot.unpin_chat_message:1 +msgid "" +"Use this method to unpin specific pinned message in a supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unpin_chat_message:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinchatmessage" +msgstr "" + +#: of telebot.TeleBot.unpin_chat_message:11 +msgid "Int: Identifier of a message to unpin" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:1 +msgid "" +"Use this method to upload a .png file with a sticker for later use in " +"createNewStickerSet and addStickerToSet methods (can be used multiple " +"times). Returns the uploaded File on success." +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#uploadstickerfile" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:6 +msgid "User identifier of sticker set owner" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:9 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px." +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:13 +msgid "On success, the sent file is returned." +msgstr "" + +#: of telebot.TeleBot.user:1 +msgid "" +"The User object representing this bot. Equivalent to bot.get_me() but the" +" result is cached so only one API call is needed." +msgstr "" + +#: of telebot.TeleBot.user:4 +msgid "Bot's info." +msgstr "" + +#: of telebot.TeleBot.user:5 +msgid ":class:`telebot.types.User`" +msgstr "" + +#: ../../sync_version/index.rst:17 +msgid "custom_filters file" +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:1 +#: telebot.custom_filters.SimpleCustomFilter:1 +msgid "Bases: :py:class:`abc.ABC`" +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:1 +msgid "" +"Advanced Custom Filter base class. Create child class with check() " +"method. Accepts two parameters, returns bool: True - filter passed, False" +" - filter failed. message: Message class text: Filter value given in " +"handler" +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:7 +#: telebot.custom_filters.SimpleCustomFilter:5 +msgid "Child classes should have .key property." +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:9 +msgid "Example on creating an advanced custom filter." +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter.check:1 +#: telebot.custom_filters.SimpleCustomFilter.check:1 +msgid "Perform a check." +msgstr "" + +#: of telebot.custom_filters.ChatFilter:1 +#: telebot.custom_filters.LanguageFilter:1 telebot.custom_filters.StateFilter:1 +#: telebot.custom_filters.TextContainsFilter:1 +#: telebot.custom_filters.TextMatchFilter:1 +#: telebot.custom_filters.TextStartsFilter:1 +msgid "Bases: :py:class:`telebot.custom_filters.AdvancedCustomFilter`" +msgstr "" + +#: of telebot.custom_filters.ChatFilter:1 +msgid "Check whether chat_id corresponds to given chat_id." +msgstr "" + +#: of telebot.custom_filters.ChatFilter:3 +#: telebot.custom_filters.ForwardFilter:3 +#: telebot.custom_filters.IsAdminFilter:3 +#: telebot.custom_filters.IsDigitFilter:3 +#: telebot.custom_filters.IsReplyFilter:3 +#: telebot.custom_filters.LanguageFilter:3 telebot.custom_filters.StateFilter:3 +#: telebot.custom_filters.TextContainsFilter:5 +#: telebot.custom_filters.TextMatchFilter:3 +#: telebot.custom_filters.TextStartsFilter:3 +msgid "Example on using this filter:" +msgstr "" + +#: of telebot.custom_filters.ForwardFilter:1 +#: telebot.custom_filters.IsAdminFilter:1 +#: telebot.custom_filters.IsDigitFilter:1 +#: telebot.custom_filters.IsReplyFilter:1 +msgid "Bases: :py:class:`telebot.custom_filters.SimpleCustomFilter`" +msgstr "" + +#: of telebot.custom_filters.ForwardFilter:1 +msgid "Check whether message was forwarded from channel or group." +msgstr "" + +#: of telebot.custom_filters.IsAdminFilter:1 +msgid "Check whether the user is administrator / owner of the chat." +msgstr "" + +#: of telebot.custom_filters.IsDigitFilter:1 +msgid "Filter to check whether the string is made up of only digits." +msgstr "" + +#: of telebot.custom_filters.IsReplyFilter:1 +msgid "Check whether message is a reply." +msgstr "" + +#: of telebot.custom_filters.LanguageFilter:1 +msgid "Check users language_code." +msgstr "" + +#: of telebot.custom_filters.SimpleCustomFilter:1 +msgid "" +"Simple Custom Filter base class. Create child class with check() method. " +"Accepts only message, returns bool value, that is compared with given in " +"handler." +msgstr "" + +#: of telebot.custom_filters.SimpleCustomFilter:7 +msgid "Example on creating a simple custom filter." +msgstr "" + +#: of telebot.custom_filters.StateFilter:1 +msgid "Filter to check state." +msgstr "" + +#: of telebot.custom_filters.TextContainsFilter:1 +msgid "Filter to check Text message. key: text" +msgstr "" + +#: of telebot.custom_filters.TextFilter:1 +msgid "" +"Advanced text filter to check (types.Message, types.CallbackQuery, " +"types.InlineQuery, types.Poll)" +msgstr "" + +#: of telebot.custom_filters.TextFilter:3 +msgid "example of usage is in examples/custom_filters/advanced_text_filter.py" +msgstr "" + +#: of telebot.custom_filters.TextFilter:5 +msgid "string, True if object's text is equal to passed string" +msgstr "" + +#: of telebot.custom_filters.TextFilter:8 +msgid "list[str] or tuple[str], True if any string element of iterable is in text" +msgstr "" + +#: of telebot.custom_filters.TextFilter:11 telebot.custom_filters.TextFilter:14 +msgid "string, True if object's text starts with passed string" +msgstr "" + +#: of telebot.custom_filters.TextFilter:17 +msgid "bool (default False), case insensitive" +msgstr "" + +#: of telebot.custom_filters.TextFilter:20 +msgid "if incorrect value for a parameter was supplied" +msgstr "" + +#: of telebot.custom_filters.TextMatchFilter:1 +msgid "Filter to check Text message." +msgstr "" + +#: of telebot.custom_filters.TextStartsFilter:1 +msgid "Filter to check whether message starts with some text." +msgstr "" + +#: ../../sync_version/index.rst:25 +msgid "handler_backends file" +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:1 +msgid "" +"Base class for middleware. Your middlewares should be inherited from this" +" class." +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:4 +msgid "" +"Set update_sensitive=True if you want to get different updates on " +"different functions. For example, if you want to handle pre_process for " +"message update, then you will have to create pre_process_message " +"function, and so on. Same applies to post_process." +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:10 +msgid "" +"If you want to use middleware, you have to set use_class_middlewares=True" +" in your TeleBot instance." +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:13 +msgid "Example of class-based middlewares." +msgstr "" + +#: of telebot.handler_backends.CancelUpdate:1 +msgid "" +"Class for canceling updates. Just return instance of this class in " +"middleware to skip update. Update will skip handler and execution of " +"post_process in middlewares." +msgstr "" + +#: of telebot.handler_backends.ContinueHandling:1 +msgid "" +"Class for continue updates in handlers. Just return instance of this " +"class in handlers to continue process." +msgstr "" + +#: of telebot.handler_backends.ContinueHandling:5 +msgid "Example of using ContinueHandling" +msgstr "" + +#: of telebot.handler_backends.SkipHandler:1 +msgid "" +"Class for skipping handlers. Just return instance of this class in " +"middleware to skip handler. Update will go to post_process, but will skip" +" execution of handler." +msgstr "" + +#: of telebot.handler_backends.State:1 +msgid "Class representing a state." +msgstr "" + +#: of telebot.handler_backends.StatesGroup:1 +msgid "Class representing common states." +msgstr "" + +#: ../../sync_version/index.rst:34 +msgid "Extensions" +msgstr "" + +#: of telebot.ext.sync.webhooks:1 +msgid "" +"This file is used by TeleBot.run_webhooks() function. Fastapi is required" +" to run this script." +msgstr "" + +#: of telebot.ext.sync.webhooks.SyncWebhookListener.run_app:1 +msgid "" +"Run app with the given parameters to init. Not supposed to be used " +"manually by user." +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/types.po b/docs/source/locales/en/LC_MESSAGES/types.po new file mode 100644 index 000000000..e9417f378 --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/types.po @@ -0,0 +1,5507 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../types.rst:3 +msgid "Types of API" +msgstr "" + +#: of telebot.types.Animation:1 telebot.types.Audio:1 +#: telebot.types.CallbackQuery:1 telebot.types.Chat:1 +#: telebot.types.ChatJoinRequest:1 telebot.types.ChatMember:1 +#: telebot.types.ChatMemberUpdated:1 telebot.types.ChatPhoto:1 +#: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 +#: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 +#: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 +#: telebot.types.ForumTopicReopened:1 telebot.types.Game:1 +#: telebot.types.GameHighScore:1 telebot.types.InlineQuery:1 +#: telebot.types.Invoice:1 telebot.types.Message:1 +#: telebot.types.MessageAutoDeleteTimerChanged:1 telebot.types.MessageID:1 +#: telebot.types.OrderInfo:1 telebot.types.PhotoSize:1 telebot.types.Poll:1 +#: telebot.types.PollOption:1 telebot.types.PreCheckoutQuery:1 +#: telebot.types.ProximityAlertTriggered:1 telebot.types.ShippingAddress:1 +#: telebot.types.ShippingQuery:1 telebot.types.Sticker:1 +#: telebot.types.StickerSet:1 telebot.types.SuccessfulPayment:1 +#: telebot.types.Update:1 telebot.types.UserProfilePhotos:1 +#: telebot.types.Venue:1 telebot.types.Video:1 telebot.types.VideoChatEnded:1 +#: telebot.types.VideoChatParticipantsInvited:1 +#: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 +#: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 +msgid "Bases: :py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.Animation:1 +msgid "" +"This object represents an animation file (GIF or H.264/MPEG-4 AVC video " +"without sound)." +msgstr "" + +#: of telebot.types.Animation:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#animation" +msgstr "" + +#: of telebot.types.Animation telebot.types.Audio telebot.types.BotCommand +#: telebot.types.BotCommandScopeAllChatAdministrators +#: telebot.types.BotCommandScopeAllGroupChats +#: telebot.types.BotCommandScopeAllPrivateChats +#: telebot.types.BotCommandScopeChat +#: telebot.types.BotCommandScopeChatAdministrators +#: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault +#: telebot.types.CallbackQuery telebot.types.Chat +#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChosenInlineResult +#: telebot.types.Contact telebot.types.Dice telebot.types.Document +#: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic +#: telebot.types.ForumTopicCreated telebot.types.Game +#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery +#: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio +#: telebot.types.InlineQueryResultCachedAudio +#: telebot.types.InlineQueryResultCachedDocument +#: telebot.types.InlineQueryResultCachedGif +#: telebot.types.InlineQueryResultCachedMpeg4Gif +#: telebot.types.InlineQueryResultCachedPhoto +#: telebot.types.InlineQueryResultCachedSticker +#: telebot.types.InlineQueryResultCachedVideo +#: telebot.types.InlineQueryResultCachedVoice +#: telebot.types.InlineQueryResultContact +#: telebot.types.InlineQueryResultDocument telebot.types.InlineQueryResultGame +#: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation +#: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto +#: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo +#: telebot.types.InlineQueryResultVoice +#: telebot.types.InputContactMessageContent telebot.types.InputFile +#: telebot.types.InputInvoiceMessageContent +#: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation +#: telebot.types.InputMediaAudio telebot.types.InputMediaDocument +#: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo +#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent +#: telebot.types.Invoice telebot.types.KeyboardButton +#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault +#: telebot.types.MenuButtonWebApp telebot.types.Message +#: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity +#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize +#: telebot.types.Poll telebot.types.Poll.add telebot.types.PollAnswer +#: telebot.types.PollOption telebot.types.PreCheckoutQuery +#: telebot.types.ProximityAlertTriggered telebot.types.ReplyKeyboardMarkup +#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row +#: telebot.types.ReplyKeyboardRemove telebot.types.SentWebAppMessage +#: telebot.types.ShippingAddress telebot.types.ShippingOption +#: telebot.types.ShippingOption.add_price telebot.types.ShippingQuery +#: telebot.types.Sticker telebot.types.StickerSet +#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User +#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited +#: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice +#: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +msgid "Parameters" +msgstr "" + +#: of telebot.types.Animation:5 telebot.types.Audio:5 telebot.types.Document:5 +#: telebot.types.File:5 telebot.types.PhotoSize:5 telebot.types.Sticker:5 +#: telebot.types.Video:5 telebot.types.VideoNote:5 telebot.types.Voice:5 +msgid "Identifier for this file, which can be used to download or reuse the file" +msgstr "" + +#: of telebot.types.Animation:8 telebot.types.Audio:8 telebot.types.Document:8 +#: telebot.types.File:8 telebot.types.PhotoSize:8 telebot.types.Sticker:8 +#: telebot.types.Video:8 telebot.types.VideoNote:8 telebot.types.Voice:8 +msgid "" +"Unique identifier for this file, which is supposed to be the same over " +"time and for different bots. Can't be used to download or reuse the file." +msgstr "" + +#: of telebot.types.Animation:12 telebot.types.Video:12 +msgid "Video width as defined by sender" +msgstr "" + +#: of telebot.types.Animation:15 telebot.types.Video:15 +msgid "Video height as defined by sender" +msgstr "" + +#: of telebot.types.Animation:18 telebot.types.Video:18 +#: telebot.types.VideoNote:15 +msgid "Duration of the video in seconds as defined by sender" +msgstr "" + +#: of telebot.types.Animation:21 +msgid "Optional. Animation thumbnail as defined by sender" +msgstr "" + +#: of telebot.types.Animation:24 +msgid "Optional. Original animation filename as defined by sender" +msgstr "" + +#: of telebot.types.Animation:27 telebot.types.Audio:24 +#: telebot.types.Document:18 telebot.types.Video:27 telebot.types.Voice:15 +msgid "Optional. MIME type of the file as defined by sender" +msgstr "" + +#: of telebot.types.Animation:30 telebot.types.Audio:27 +#: telebot.types.Document:21 telebot.types.File:12 telebot.types.Video:30 +#: telebot.types.Voice:18 +msgid "" +"Optional. File size in bytes. It can be bigger than 2^31 and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it has at most 52 significant bits, so a signed 64-bit integer or" +" double-precision float type are safe for storing this value." +msgstr "" + +#: of telebot.types.Animation telebot.types.Audio telebot.types.BotCommand +#: telebot.types.BotCommandScope +#: telebot.types.BotCommandScopeAllChatAdministrators +#: telebot.types.BotCommandScopeAllGroupChats +#: telebot.types.BotCommandScopeAllPrivateChats +#: telebot.types.BotCommandScopeChat +#: telebot.types.BotCommandScopeChatAdministrators +#: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault +#: telebot.types.CallbackQuery telebot.types.Chat +#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice +#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game +#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery +#: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio +#: telebot.types.InlineQueryResultCachedAudio +#: telebot.types.InlineQueryResultCachedDocument +#: telebot.types.InlineQueryResultCachedGif +#: telebot.types.InlineQueryResultCachedMpeg4Gif +#: telebot.types.InlineQueryResultCachedPhoto +#: telebot.types.InlineQueryResultCachedSticker +#: telebot.types.InlineQueryResultCachedVideo +#: telebot.types.InlineQueryResultCachedVoice +#: telebot.types.InlineQueryResultContact +#: telebot.types.InlineQueryResultDocument telebot.types.InlineQueryResultGame +#: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation +#: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto +#: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo +#: telebot.types.InlineQueryResultVoice +#: telebot.types.InputContactMessageContent +#: telebot.types.InputInvoiceMessageContent +#: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation +#: telebot.types.InputMediaAudio telebot.types.InputMediaDocument +#: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo +#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent +#: telebot.types.Invoice telebot.types.KeyboardButton +#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault +#: telebot.types.MenuButtonWebApp telebot.types.Message +#: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity +#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize +#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption +#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered +#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add +#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.SentWebAppMessage telebot.types.ShippingAddress +#: telebot.types.ShippingOption telebot.types.ShippingOption.add_price +#: telebot.types.ShippingQuery telebot.types.Sticker telebot.types.StickerSet +#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User +#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited +#: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice +#: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +msgid "Returns" +msgstr "" + +#: of telebot.types.Animation:35 telebot.types.Audio:35 +#: telebot.types.BotCommand:12 telebot.types.BotCommandScope:38 +#: telebot.types.BotCommandScopeAllChatAdministrators:8 +#: telebot.types.BotCommandScopeAllGroupChats:8 +#: telebot.types.BotCommandScopeAllPrivateChats:8 +#: telebot.types.BotCommandScopeChat:12 +#: telebot.types.BotCommandScopeChatAdministrators:12 +#: telebot.types.BotCommandScopeChatMember:15 +#: telebot.types.BotCommandScopeDefault:8 telebot.types.CallbackQuery:30 +#: telebot.types.Chat:101 telebot.types.ChatAdministratorRights:46 +#: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:20 +#: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:59 +#: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 +#: telebot.types.ChatMemberMember:11 telebot.types.ChatMemberOwner:17 +#: telebot.types.ChatMemberRestricted:47 telebot.types.ChatMemberUpdated:24 +#: telebot.types.ChatPermissions:38 telebot.types.ChatPhoto:21 +#: telebot.types.ChosenInlineResult:21 telebot.types.Contact:22 +#: telebot.types.Dice:11 telebot.types.Document:26 telebot.types.File:21 +#: telebot.types.ForceReply:18 telebot.types.ForumTopic:17 +#: telebot.types.ForumTopicCreated:14 telebot.types.Game:25 +#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:45 +#: telebot.types.InlineKeyboardMarkup:22 telebot.types.InlineQuery:26 +#: telebot.types.InlineQueryResultArticle:38 +#: telebot.types.InlineQueryResultAudio:40 +#: telebot.types.InlineQueryResultCachedAudio:31 +#: telebot.types.InlineQueryResultCachedDocument:37 +#: telebot.types.InlineQueryResultCachedGif:33 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:33 +#: telebot.types.InlineQueryResultCachedPhoto:37 +#: telebot.types.InlineQueryResultCachedSticker:20 +#: telebot.types.InlineQueryResultCachedVideo:37 +#: telebot.types.InlineQueryResultCachedVoice:34 +#: telebot.types.InlineQueryResultContact:38 +#: telebot.types.InlineQueryResultDocument:49 +#: telebot.types.InlineQueryResultGame:17 telebot.types.InlineQueryResultGif:49 +#: telebot.types.InlineQueryResultLocation:50 +#: telebot.types.InlineQueryResultMpeg4Gif:49 +#: telebot.types.InlineQueryResultPhoto:46 +#: telebot.types.InlineQueryResultVenue:51 +#: telebot.types.InlineQueryResultVideo:53 +#: telebot.types.InlineQueryResultVoice:37 +#: telebot.types.InputContactMessageContent:17 +#: telebot.types.InputInvoiceMessageContent:75 +#: telebot.types.InputLocationMessageContent:26 +#: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:40 +#: telebot.types.InputMediaDocument:35 telebot.types.InputMediaPhoto:24 +#: telebot.types.InputMediaVideo:43 telebot.types.InputTextMessageContent:19 +#: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 +#: telebot.types.KeyboardButton:25 telebot.types.KeyboardButtonPollType:9 +#: telebot.types.LabeledPrice:13 telebot.types.Location:25 +#: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 +#: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 +#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:230 +#: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 +#: telebot.types.MessageID:8 telebot.types.OrderInfo:17 +#: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:15 +#: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 +#: telebot.types.ProximityAlertTriggered:14 +#: telebot.types.ReplyKeyboardMarkup:42 telebot.types.ReplyKeyboardRemove:18 +#: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 +#: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 +#: telebot.types.Sticker:49 telebot.types.StickerSet:30 +#: telebot.types.SuccessfulPayment:28 telebot.types.Update:61 +#: telebot.types.User:41 telebot.types.UserProfilePhotos:11 +#: telebot.types.Venue:27 telebot.types.Video:35 telebot.types.VideoChatEnded:8 +#: telebot.types.VideoChatParticipantsInvited:8 +#: telebot.types.VideoChatScheduled:9 telebot.types.VideoNote:24 +#: telebot.types.Voice:23 telebot.types.WebAppData:12 +#: telebot.types.WebAppInfo:8 telebot.types.WebhookInfo:37 +msgid "Instance of the class" +msgstr "" + +#: of telebot.types.Animation telebot.types.Audio telebot.types.BotCommand +#: telebot.types.BotCommandScope +#: telebot.types.BotCommandScopeAllChatAdministrators +#: telebot.types.BotCommandScopeAllGroupChats +#: telebot.types.BotCommandScopeAllPrivateChats +#: telebot.types.BotCommandScopeChat +#: telebot.types.BotCommandScopeChatAdministrators +#: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault +#: telebot.types.CallbackQuery telebot.types.Chat +#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice +#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game +#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery +#: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio +#: telebot.types.InlineQueryResultCachedAudio +#: telebot.types.InlineQueryResultCachedDocument +#: telebot.types.InlineQueryResultCachedGif +#: telebot.types.InlineQueryResultCachedMpeg4Gif +#: telebot.types.InlineQueryResultCachedPhoto +#: telebot.types.InlineQueryResultCachedSticker +#: telebot.types.InlineQueryResultCachedVideo +#: telebot.types.InlineQueryResultCachedVoice +#: telebot.types.InlineQueryResultContact +#: telebot.types.InlineQueryResultDocument telebot.types.InlineQueryResultGame +#: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation +#: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto +#: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo +#: telebot.types.InlineQueryResultVoice +#: telebot.types.InputContactMessageContent +#: telebot.types.InputInvoiceMessageContent +#: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation +#: telebot.types.InputMediaAudio telebot.types.InputMediaDocument +#: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo +#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent +#: telebot.types.Invoice telebot.types.KeyboardButton +#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault +#: telebot.types.MenuButtonWebApp telebot.types.Message +#: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity +#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize +#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption +#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered +#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add +#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.SentWebAppMessage telebot.types.ShippingAddress +#: telebot.types.ShippingOption telebot.types.ShippingQuery +#: telebot.types.Sticker telebot.types.StickerSet +#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User +#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited +#: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice +#: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +msgid "Return type" +msgstr "" + +#: of telebot.types.Animation:36 +msgid ":class:`telebot.types.Animation`" +msgstr "" + +#: of telebot.types.Audio:1 +msgid "" +"This object represents an audio file to be treated as music by the " +"Telegram clients." +msgstr "" + +#: of telebot.types.Audio:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#audio" +msgstr "" + +#: of telebot.types.Audio:12 telebot.types.Voice:12 +msgid "Duration of the audio in seconds as defined by sender" +msgstr "" + +#: of telebot.types.Audio:15 +msgid "Optional. Performer of the audio as defined by sender or by audio tags" +msgstr "" + +#: of telebot.types.Audio:18 +msgid "Optional. Title of the audio as defined by sender or by audio tags" +msgstr "" + +#: of telebot.types.Audio:21 telebot.types.Document:15 telebot.types.Video:24 +msgid "Optional. Original filename as defined by sender" +msgstr "" + +#: of telebot.types.Audio:32 +msgid "Optional. Thumbnail of the album cover to which the music file belongs" +msgstr "" + +#: of telebot.types.Audio:36 +msgid ":class:`telebot.types.Audio`" +msgstr "" + +#: of telebot.types.BotCommand:1 telebot.types.ChatInviteLink:1 +#: telebot.types.ChatLocation:1 telebot.types.PollAnswer:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.BotCommand:1 +msgid "This object represents a bot command." +msgstr "" + +#: of telebot.types.BotCommand:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#botcommand" +msgstr "" + +#: of telebot.types.BotCommand:5 +msgid "" +"Text of the command; 1-32 characters. Can contain only lowercase English " +"letters, digits and underscores." +msgstr "" + +#: of telebot.types.BotCommand:9 +msgid "Description of the command; 1-256 characters." +msgstr "" + +#: of telebot.types.BotCommand:13 +msgid ":class:`telebot.types.BotCommand`" +msgstr "" + +#: of telebot.types.BotCommandScope:1 +#: telebot.types.InlineQueryResultCachedBase:1 +msgid "Bases: :py:class:`abc.ABC`, :py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.BotCommandScope:1 +msgid "" +"This object represents the scope to which bot commands are applied. " +"Currently, the following 7 scopes are supported:" +msgstr "" + +#: of telebot.types.BotCommandScope:3 telebot.types.BotCommandScope:21 +#: telebot.types.BotCommandScope:36 +msgid ":class:`BotCommandScopeDefault`" +msgstr "" + +#: of telebot.types.BotCommandScope:4 telebot.types.BotCommandScope:19 +msgid ":class:`BotCommandScopeAllPrivateChats`" +msgstr "" + +#: of telebot.types.BotCommandScope:5 telebot.types.BotCommandScope:34 +msgid ":class:`BotCommandScopeAllGroupChats`" +msgstr "" + +#: of telebot.types.BotCommandScope:6 +msgid ":class:`BotCommandScopeAllChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScope:7 telebot.types.BotCommandScope:17 +#: telebot.types.BotCommandScope:30 +msgid ":class:`BotCommandScopeChat`" +msgstr "" + +#: of telebot.types.BotCommandScope:8 +msgid ":class:`BotCommandScopeChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScope:9 telebot.types.BotCommandScope:26 +msgid ":class:`BotCommandScopeChatMember`" +msgstr "" + +#: of telebot.types.BotCommandScope:11 +msgid "" +"Determining list of commands The following algorithm is used to determine" +" the list of commands for a particular user viewing the bot menu. The " +"first list of commands which is set is returned:" +msgstr "" + +#: of telebot.types.BotCommandScope:14 +msgid "Commands in the chat with the bot:" +msgstr "" + +#: of telebot.types.BotCommandScope:16 telebot.types.BotCommandScope:29 +msgid ":class:`BotCommandScopeChat` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:18 +msgid ":class:`BotCommandScopeAllPrivateChats` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:20 telebot.types.BotCommandScope:35 +msgid ":class:`BotCommandScopeDefault` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:23 +msgid "Commands in group and supergroup chats:" +msgstr "" + +#: of telebot.types.BotCommandScope:25 +msgid ":class:`BotCommandScopeChatMember` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:27 +msgid "" +":class:`BotCommandScopeChatAdministrators` + language_code " +"(administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:28 +msgid ":class:`BotCommandScopeChatAdministrators` (administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:31 +msgid "" +":class:`BotCommandScopeAllChatAdministrators` + language_code " +"(administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:32 +msgid ":class:`BotCommandScopeAllChatAdministrators` (administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:33 +msgid ":class:`BotCommandScopeAllGroupChats` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:39 +msgid ":class:`telebot.types.BotCommandScope`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:1 +#: telebot.types.BotCommandScopeAllGroupChats:1 +#: telebot.types.BotCommandScopeAllPrivateChats:1 +#: telebot.types.BotCommandScopeChat:1 +#: telebot.types.BotCommandScopeChatAdministrators:1 +#: telebot.types.BotCommandScopeChatMember:1 +#: telebot.types.BotCommandScopeDefault:1 +msgid "Bases: :py:class:`telebot.types.BotCommandScope`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:1 +msgid "" +"Represents the scope of bot commands, covering all group and supergroup " +"chat administrators." +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopeallchatadministrators" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:5 +msgid "Scope type, must be all_chat_administrators" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:9 +msgid ":class:`telebot.types.BotCommandScopeAllChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:1 +msgid "" +"Represents the scope of bot commands, covering all group and supergroup " +"chats." +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopeallgroupchats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:5 +msgid "Scope type, must be all_group_chats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:9 +msgid ":class:`telebot.types.BotCommandScopeAllGroupChats`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:1 +msgid "Represents the scope of bot commands, covering all private chats." +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopeallprivatechats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:5 +msgid "Scope type, must be all_private_chats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:9 +msgid ":class:`telebot.types.BotCommandScopeAllPrivateChats`" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:1 +msgid "Represents the scope of bot commands, covering a specific chat." +msgstr "" + +#: of telebot.types.BotCommandScopeChat:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopechat" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:5 +msgid "Scope type, must be chat" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:8 +#: telebot.types.BotCommandScopeChatAdministrators:8 +#: telebot.types.BotCommandScopeChatMember:8 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup (in the format @supergroupusername)" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:13 +msgid ":class:`telebot.types.BotCommandScopeChat`" +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:1 +msgid "" +"Represents the scope of bot commands, covering all administrators of a " +"specific group or supergroup chat." +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopechatadministrators" +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:5 +msgid "Scope type, must be chat_administrators" +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:13 +msgid ":class:`telebot.types.BotCommandScopeChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:1 +msgid "" +"Represents the scope of bot commands, covering a specific member of a " +"group or supergroup chat." +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopechatmember" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:5 +msgid "Scope type, must be chat_member" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:12 +msgid "Unique identifier of the target user" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:16 +msgid ":class:`telebot.types.BotCommandScopeChatMember`" +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:1 +msgid "" +"Represents the default scope of bot commands. Default commands are used " +"if no commands with a narrower scope are specified for the user." +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopedefault" +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:5 +msgid "Scope type, must be default" +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:9 +msgid ":class:`telebot.types.BotCommandScopeDefault`" +msgstr "" + +#: of telebot.types.CallbackQuery:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.CallbackQuery:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#callbackquery" +msgstr "" + +#: of telebot.types.CallbackQuery:5 telebot.types.InlineQuery:5 +msgid "Unique identifier for this query" +msgstr "" + +#: of telebot.types.CallbackQuery:8 telebot.types.InlineQuery:8 +msgid "Sender" +msgstr "" + +#: of telebot.types.CallbackQuery:11 +msgid "" +"Optional. Message with the callback button that originated the query. " +"Note that message content and message date will not be available if the " +"message is too old" +msgstr "" + +#: of telebot.types.CallbackQuery:15 +msgid "" +"Optional. Identifier of the message sent via the bot in inline mode, that" +" originated the query." +msgstr "" + +#: of telebot.types.CallbackQuery:19 +msgid "" +"Global identifier, uniquely corresponding to the chat to which the " +"message with the callback button was sent. Useful for high scores in " +"games." +msgstr "" + +#: of telebot.types.CallbackQuery:23 +msgid "" +"Optional. Data associated with the callback button. Be aware that the " +"message originated the query can contain no callback buttons with this " +"data." +msgstr "" + +#: of telebot.types.CallbackQuery:27 +msgid "" +"Optional. Short name of a Game to be returned, serves as the unique " +"identifier for the game" +msgstr "" + +#: of telebot.types.CallbackQuery:31 +msgid ":class:`telebot.types.CallbackQuery`" +msgstr "" + +#: of telebot.types.Chat:1 +msgid "This object represents a chat." +msgstr "" + +#: of telebot.types.Chat:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chat" +msgstr "" + +#: of telebot.types.Chat:5 +msgid "" +"Unique identifier for this chat. This number may have more than 32 " +"significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a signed 64-bit integer or double-precision float " +"type are safe for storing this identifier." +msgstr "" + +#: of telebot.types.Chat:10 +msgid "Type of chat, can be either “private”, “group”, “supergroup” or “channel”" +msgstr "" + +#: of telebot.types.Chat:13 +msgid "Optional. Title, for supergroups, channels and group chats" +msgstr "" + +#: of telebot.types.Chat:16 +msgid "" +"Optional. Username, for private chats, supergroups and channels if " +"available" +msgstr "" + +#: of telebot.types.Chat:19 +msgid "Optional. First name of the other party in a private chat" +msgstr "" + +#: of telebot.types.Chat:22 +msgid "Optional. Last name of the other party in a private chat" +msgstr "" + +#: of telebot.types.Chat:25 +msgid "Optional. True, if the supergroup chat is a forum (has topics enabled)" +msgstr "" + +#: of telebot.types.Chat:28 +msgid "Optional. Chat photo. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:31 +msgid "" +"Optional. If non-empty, the list of all active chat usernames; for " +"private chats, supergroups and channels. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:35 +msgid "" +"Optional. Custom emoji identifier of emoji status of the other party in a" +" private chat. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:39 +msgid "" +"Optional. Bio of the other party in a private chat. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:42 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Chat:46 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Chat:48 +msgid ":type :obj:`bool`" +msgstr "" + +#: of telebot.types.Chat:50 +msgid "" +"Optional. :obj:`bool`, if users need to join the supergroup before they " +"can send messages. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:54 +msgid "" +"Optional. :obj:`bool`, if all users directly joining the supergroup need " +"to be approved by supergroup administrators. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:58 +msgid "" +"Optional. Description, for groups, supergroups and channel chats. " +"Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:61 +msgid "" +"Optional. Primary invite link, for groups, supergroups and channel chats." +" Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:65 +msgid "" +"Optional. The most recent pinned message (by sending date). Returned only" +" in getChat." +msgstr "" + +#: of telebot.types.Chat:68 +msgid "" +"Optional. Default chat member permissions, for groups and supergroups. " +"Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:72 +msgid "" +"Optional. For supergroups, the minimum allowed delay between consecutive " +"messages sent by each unpriviledged user; in seconds. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:76 +msgid "" +"Optional. The time after which all messages sent to the chat will be " +"automatically deleted; in seconds. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:80 +msgid "" +"Optional. :obj:`bool`, if messages from the chat can't be forwarded to " +"other chats. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:84 +msgid "" +"Optional. For supergroups, name of group sticker set. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:87 +msgid "" +"Optional. :obj:`bool`, if the bot can change the group sticker set. " +"Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:91 +msgid "" +"Optional. Unique identifier for the linked chat, i.e. the discussion " +"group identifier for a channel and vice versa; for supergroups and " +"channel chats. This identifier may be greater than 32 bits and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it is smaller than 52 bits, so a signed 64 bit integer or double-" +"precision float type are safe for storing this identifier. Returned only " +"in getChat." +msgstr "" + +#: of telebot.types.Chat:97 +msgid "" +"Optional. For supergroups, the location to which the supergroup is " +"connected. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:102 +msgid ":class:`telebot.types.Chat`" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:1 telebot.types.ChatPermissions:1 +#: telebot.types.Location:1 telebot.types.MenuButton:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:1 +msgid "Represents the rights of an administrator in a chat." +msgstr "" + +#: of telebot.types.ChatAdministratorRights:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatadministratorrights" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:5 +#: telebot.types.ChatMemberAdministrator:14 telebot.types.ChatMemberOwner:11 +msgid "True, if the user's presence in the chat is hidden" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:8 +#: telebot.types.ChatMemberAdministrator:17 +msgid "" +"True, if the administrator can access the chat event log, chat " +"statistics, message statistics in channels, see channel members, see " +"anonymous administrators in supergroups and ignore slow mode. Implied by " +"any other administrator privilege" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:13 +#: telebot.types.ChatMemberAdministrator:22 +msgid "True, if the administrator can delete messages of other users" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:16 +#: telebot.types.ChatMemberAdministrator:25 +msgid "True, if the administrator can manage video chats" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:19 +#: telebot.types.ChatMemberAdministrator:28 +msgid "True, if the administrator can restrict, ban or unban chat members" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:22 +#: telebot.types.ChatMemberAdministrator:31 +msgid "" +"True, if the administrator can add new administrators with a subset of " +"their own privileges or demote administrators that he has promoted, " +"directly or indirectly (promoted by administrators that were appointed by" +" the user)" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:27 +#: telebot.types.ChatMemberAdministrator:36 +#: telebot.types.ChatMemberRestricted:14 +msgid "" +"True, if the user is allowed to change the chat title, photo and other " +"settings" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:30 +#: telebot.types.ChatMemberAdministrator:39 +#: telebot.types.ChatMemberRestricted:17 +msgid "True, if the user is allowed to invite new users to the chat" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:33 +#: telebot.types.ChatMemberAdministrator:42 +msgid "" +"Optional. True, if the administrator can post in the channel; channels " +"only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:36 +#: telebot.types.ChatMemberAdministrator:45 +msgid "" +"Optional. True, if the administrator can edit messages of other users and" +" can pin messages; channels only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:40 +#: telebot.types.ChatMemberAdministrator:49 +msgid "" +"Optional. True, if the user is allowed to pin messages; groups and " +"supergroups only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:43 +#: telebot.types.ChatMemberAdministrator:52 +msgid "" +"Optional. True, if the user is allowed to create, rename, close, and " +"reopen forum topics; supergroups only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:47 +msgid ":class:`telebot.types.ChatAdministratorRights`" +msgstr "" + +#: of telebot.types.ChatInviteLink:1 +msgid "Represents an invite link for a chat." +msgstr "" + +#: of telebot.types.ChatInviteLink:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatinvitelink" +msgstr "" + +#: of telebot.types.ChatInviteLink:5 +msgid "" +"The invite link. If the link was created by another chat administrator, " +"then the second part of the link will be replaced with “…”." +msgstr "" + +#: of telebot.types.ChatInviteLink:9 +msgid "Creator of the link" +msgstr "" + +#: of telebot.types.ChatInviteLink:12 +msgid "" +"True, if users joining the chat via the link need to be approved by chat " +"administrators" +msgstr "" + +#: of telebot.types.ChatInviteLink:15 +msgid "True, if the link is primary" +msgstr "" + +#: of telebot.types.ChatInviteLink:18 +msgid "True, if the link is revoked" +msgstr "" + +#: of telebot.types.ChatInviteLink:21 +msgid "Optional. Invite link name" +msgstr "" + +#: of telebot.types.ChatInviteLink:24 +msgid "" +"Optional. Point in time (Unix timestamp) when the link will expire or has" +" been expired" +msgstr "" + +#: of telebot.types.ChatInviteLink:27 +msgid "" +"Optional. The maximum number of users that can be members of the chat " +"simultaneously after joining the chat via this invite link; 1-99999" +msgstr "" + +#: of telebot.types.ChatInviteLink:31 +msgid "Optional. Number of pending join requests created using this link" +msgstr "" + +#: of telebot.types.ChatInviteLink:35 +msgid ":class:`telebot.types.ChatInviteLink`" +msgstr "" + +#: of telebot.types.ChatJoinRequest:1 +msgid "Represents a join request sent to a chat." +msgstr "" + +#: of telebot.types.ChatJoinRequest:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatjoinrequest" +msgstr "" + +#: of telebot.types.ChatJoinRequest:5 +msgid "Chat to which the request was sent" +msgstr "" + +#: of telebot.types.ChatJoinRequest:8 +msgid "User that sent the join request" +msgstr "" + +#: of telebot.types.ChatJoinRequest:11 +msgid "Date the request was sent in Unix time" +msgstr "" + +#: of telebot.types.ChatJoinRequest:14 +msgid "Optional. Bio of the user." +msgstr "" + +#: of telebot.types.ChatJoinRequest:17 +msgid "" +"Optional. Chat invite link that was used by the user to send the join " +"request" +msgstr "" + +#: of telebot.types.ChatJoinRequest:21 +msgid ":class:`telebot.types.ChatJoinRequest`" +msgstr "" + +#: of telebot.types.ChatLocation:1 +msgid "Represents a location to which a chat is connected." +msgstr "" + +#: of telebot.types.ChatLocation:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatlocation" +msgstr "" + +#: of telebot.types.ChatLocation:5 +msgid "" +"The location to which the supergroup is connected. Can't be a live " +"location." +msgstr "" + +#: of telebot.types.ChatLocation:8 +msgid "Location address; 1-64 characters, as defined by the chat owner" +msgstr "" + +#: of telebot.types.ChatLocation:12 +msgid ":class:`telebot.types.ChatLocation`" +msgstr "" + +#: of telebot.types.ChatMember:1 +msgid "" +"This object contains information about one member of a chat. Currently, " +"the following 6 types of chat members are supported:" +msgstr "" + +#: of telebot.types.ChatMember:4 telebot.types.ChatMemberOwner:18 +msgid ":class:`telebot.types.ChatMemberOwner`" +msgstr "" + +#: of telebot.types.ChatMember:5 telebot.types.ChatMemberAdministrator:60 +msgid ":class:`telebot.types.ChatMemberAdministrator`" +msgstr "" + +#: of telebot.types.ChatMember:6 telebot.types.ChatMemberMember:12 +msgid ":class:`telebot.types.ChatMemberMember`" +msgstr "" + +#: of telebot.types.ChatMember:7 telebot.types.ChatMemberRestricted:48 +msgid ":class:`telebot.types.ChatMemberRestricted`" +msgstr "" + +#: of telebot.types.ChatMember:8 telebot.types.ChatMemberLeft:12 +msgid ":class:`telebot.types.ChatMemberLeft`" +msgstr "" + +#: of telebot.types.ChatMember:9 telebot.types.ChatMemberBanned:16 +msgid ":class:`telebot.types.ChatMemberBanned`" +msgstr "" + +#: of telebot.types.ChatMember:11 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatmember" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:1 telebot.types.ChatMemberBanned:1 +#: telebot.types.ChatMemberLeft:1 telebot.types.ChatMemberMember:1 +#: telebot.types.ChatMemberOwner:1 telebot.types.ChatMemberRestricted:1 +msgid "Bases: :py:class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:1 +msgid "Represents a chat member that has some additional privileges." +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberadministrator" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:5 +msgid "The member's status in the chat, always “administrator”" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:8 telebot.types.ChatMemberBanned:8 +#: telebot.types.ChatMemberLeft:8 telebot.types.ChatMemberMember:8 +#: telebot.types.ChatMemberOwner:8 telebot.types.ChatMemberRestricted:8 +msgid "Information about the user" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:11 +msgid "True, if the bot is allowed to edit administrator privileges of that user" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:56 telebot.types.ChatMemberOwner:14 +msgid "Optional. Custom title for this user" +msgstr "" + +#: of telebot.types.ChatMemberBanned:1 +msgid "" +"Represents a chat member that was banned in the chat and can't return to " +"the chat or view chat messages." +msgstr "" + +#: of telebot.types.ChatMemberBanned:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberbanned" +msgstr "" + +#: of telebot.types.ChatMemberBanned:5 +msgid "The member's status in the chat, always “kicked”" +msgstr "" + +#: of telebot.types.ChatMemberBanned:11 +msgid "" +"Date when restrictions will be lifted for this user; unix time. If 0, " +"then the user is banned forever" +msgstr "" + +#: of telebot.types.ChatMemberLeft:1 +msgid "" +"Represents a chat member that isn't currently a member of the chat, but " +"may join it themselves." +msgstr "" + +#: of telebot.types.ChatMemberLeft:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatmemberleft" +msgstr "" + +#: of telebot.types.ChatMemberLeft:5 +msgid "The member's status in the chat, always “left”" +msgstr "" + +#: of telebot.types.ChatMemberMember:1 +msgid "" +"Represents a chat member that has no additional privileges or " +"restrictions." +msgstr "" + +#: of telebot.types.ChatMemberMember:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmembermember" +msgstr "" + +#: of telebot.types.ChatMemberMember:5 +msgid "The member's status in the chat, always “member”" +msgstr "" + +#: of telebot.types.ChatMemberOwner:1 +msgid "" +"Represents a chat member that owns the chat and has all administrator " +"privileges." +msgstr "" + +#: of telebot.types.ChatMemberOwner:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatmemberowner" +msgstr "" + +#: of telebot.types.ChatMemberOwner:5 +msgid "The member's status in the chat, always “creator”" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:1 +msgid "" +"Represents a chat member that is under certain restrictions in the chat. " +"Supergroups only." +msgstr "" + +#: of telebot.types.ChatMemberRestricted:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberrestricted" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:5 +msgid "The member's status in the chat, always “restricted”" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:11 +msgid "True, if the user is a member of the chat at the moment of the request" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:20 +msgid "True, if the user is allowed to pin messages" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:23 +msgid "True, if the user is allowed to create forum topics" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:26 +msgid "" +"True, if the user is allowed to send text messages, contacts, locations " +"and venues" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:29 +msgid "" +"True, if the user is allowed to send audios, documents, photos, videos, " +"video notes and voice notes" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:33 +msgid "True, if the user is allowed to send polls" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:36 +msgid "" +"True, if the user is allowed to send animations, games, stickers and use " +"inline bots" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:40 +msgid "True, if the user is allowed to add web page previews to their messages" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:43 +msgid "" +"Date when restrictions will be lifted for this user; unix time. If 0, " +"then the user is restricted forever" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:1 +msgid "This object represents changes in the status of a chat member." +msgstr "" + +#: of telebot.types.ChatMemberUpdated:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberupdated" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:5 +msgid "Chat the user belongs to" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:8 +msgid "Performer of the action, which resulted in the change" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:11 +msgid "Date the change was done in Unix time" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:14 +msgid "Previous information about the chat member" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:17 +msgid "New information about the chat member" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:20 +msgid "" +"Optional. Chat invite link, which was used by the user to join the chat; " +"for joining by invite link events only." +msgstr "" + +#: of telebot.types.ChatMemberUpdated:25 +msgid ":class:`telebot.types.ChatMemberUpdated`" +msgstr "" + +#: of telebot.types.ChatMemberUpdated.difference:1 +msgid "" +"Get the difference between `old_chat_member` and `new_chat_member` as a " +"dict in the following format {'parameter': [old_value, new_value]} E.g " +"{'status': ['member', 'kicked'], 'until_date': [None, 1625055092]}" +msgstr "" + +#: of telebot.types.ChatMemberUpdated.difference:6 +msgid "Dict of differences" +msgstr "" + +#: of telebot.types.ChatPermissions:1 +msgid "" +"Describes actions that a non-administrator user is allowed to take in a " +"chat." +msgstr "" + +#: of telebot.types.ChatPermissions:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatpermissions" +msgstr "" + +#: of telebot.types.ChatPermissions:5 +msgid "" +"Optional. True, if the user is allowed to send text messages, contacts, " +"locations and venues" +msgstr "" + +#: of telebot.types.ChatPermissions:9 +msgid "" +"Optional. True, if the user is allowed to send audios, documents, photos," +" videos, video notes and voice notes, implies can_send_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:13 +msgid "" +"Optional. True, if the user is allowed to send polls, implies " +"can_send_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:16 +msgid "" +"Optional. True, if the user is allowed to send animations, games, " +"stickers and use inline bots, implies can_send_media_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:20 +msgid "" +"Optional. True, if the user is allowed to add web page previews to their " +"messages, implies can_send_media_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:24 +msgid "" +"Optional. True, if the user is allowed to change the chat title, photo " +"and other settings. Ignored in public supergroups" +msgstr "" + +#: of telebot.types.ChatPermissions:28 +msgid "Optional. True, if the user is allowed to invite new users to the chat" +msgstr "" + +#: of telebot.types.ChatPermissions:31 +msgid "" +"Optional. True, if the user is allowed to pin messages. Ignored in public" +" supergroups" +msgstr "" + +#: of telebot.types.ChatPermissions:34 +msgid "" +"Optional. True, if the user is allowed to create forum topics. If omitted" +" defaults to the value of can_pin_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:39 +msgid ":class:`telebot.types.ChatPermissions`" +msgstr "" + +#: of telebot.types.ChatPhoto:1 +msgid "This object represents a chat photo." +msgstr "" + +#: of telebot.types.ChatPhoto:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatphoto" +msgstr "" + +#: of telebot.types.ChatPhoto:5 +msgid "" +"File identifier of small (160x160) chat photo. This file_id can be used " +"only for photo download and only for as long as the photo is not changed." +msgstr "" + +#: of telebot.types.ChatPhoto:9 +msgid "" +"Unique file identifier of small (160x160) chat photo, which is supposed " +"to be the same over time and for different bots. Can't be used to " +"download or reuse the file." +msgstr "" + +#: of telebot.types.ChatPhoto:13 +msgid "" +"File identifier of big (640x640) chat photo. This file_id can be used " +"only for photo download and only for as long as the photo is not changed." +msgstr "" + +#: of telebot.types.ChatPhoto:17 +msgid "" +"Unique file identifier of big (640x640) chat photo, which is supposed to " +"be the same over time and for different bots. Can't be used to download " +"or reuse the file." +msgstr "" + +#: of telebot.types.ChatPhoto:22 +msgid ":class:`telebot.types.ChatPhoto`" +msgstr "" + +#: of telebot.types.ChosenInlineResult:1 +msgid "" +"Represents a result of an inline query that was chosen by the user and " +"sent to their chat partner." +msgstr "" + +#: of telebot.types.ChosenInlineResult:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#choseninlineresult" +msgstr "" + +#: of telebot.types.ChosenInlineResult:5 +msgid "The unique identifier for the result that was chosen" +msgstr "" + +#: of telebot.types.ChosenInlineResult:8 +msgid "The user that chose the result" +msgstr "" + +#: of telebot.types.ChosenInlineResult:11 +msgid "Optional. Sender location, only for bots that require user location" +msgstr "" + +#: of telebot.types.ChosenInlineResult:14 +msgid "" +"Optional. Identifier of the sent inline message. Available only if there " +"is an inline keyboard attached to the message. Will be also received in " +"callback queries and can be used to edit the message." +msgstr "" + +#: of telebot.types.ChosenInlineResult:18 +msgid "The query that was used to obtain the result" +msgstr "" + +#: of telebot.types.ChosenInlineResult:22 +msgid ":class:`telebot.types.ChosenInlineResult`" +msgstr "" + +#: of telebot.types.Contact:1 +msgid "This object represents a phone contact." +msgstr "" + +#: of telebot.types.Contact:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#contact" +msgstr "" + +#: of telebot.types.Contact:5 telebot.types.InlineQueryResultContact:11 +#: telebot.types.InputContactMessageContent:5 +msgid "Contact's phone number" +msgstr "" + +#: of telebot.types.Contact:8 telebot.types.InlineQueryResultContact:14 +#: telebot.types.InputContactMessageContent:8 +msgid "Contact's first name" +msgstr "" + +#: of telebot.types.Contact:11 telebot.types.InlineQueryResultContact:17 +#: telebot.types.InputContactMessageContent:11 +msgid "Optional. Contact's last name" +msgstr "" + +#: of telebot.types.Contact:14 +msgid "" +"Optional. Contact's user identifier in Telegram. This number may have " +"more than 32 significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier." +msgstr "" + +#: of telebot.types.Contact:19 +msgid "Optional. Additional data about the contact in the form of a vCard" +msgstr "" + +#: of telebot.types.Contact:23 +msgid ":class:`telebot.types.Contact`" +msgstr "" + +#: of telebot.types.Dice:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.Dice:1 +msgid "This object represents an animated emoji that displays a random value." +msgstr "" + +#: of telebot.types.Dice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#dice" +msgstr "" + +#: of telebot.types.Dice:5 +msgid "Emoji on which the dice throw animation is based" +msgstr "" + +#: of telebot.types.Dice:8 +msgid "" +"Value of the dice, 1-6 for “🎲”, “🎯” and “🎳” base emoji, 1-5 for “🏀” and " +"“⚽” base emoji, 1-64 for “🎰” base emoji" +msgstr "" + +#: of telebot.types.Dice:12 +msgid ":class:`telebot.types.Dice`" +msgstr "" + +#: of telebot.types.Dictionaryable:1 telebot.types.InputFile:1 +#: telebot.types.JsonDeserializable:1 telebot.types.JsonSerializable:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.types.Dictionaryable:1 +msgid "" +"Subclasses of this class are guaranteed to be able to be converted to " +"dictionary. All subclasses of this class must override to_dict." +msgstr "" + +#: of telebot.types.Document:1 +msgid "" +"This object represents a general file (as opposed to photos, voice " +"messages and audio files)." +msgstr "" + +#: of telebot.types.Document:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#document" +msgstr "" + +#: of telebot.types.Document:12 +msgid "Optional. Document thumbnail as defined by sender" +msgstr "" + +#: of telebot.types.Document:27 +msgid ":class:`telebot.types.Document`" +msgstr "" + +#: of telebot.types.File:1 +msgid "" +"This object represents a file ready to be downloaded. The file can be " +"downloaded via the link " +"https://api.telegram.org/file/bot/. It is guaranteed " +"that the link will be valid for at least 1 hour. When the link expires, a" +" new one can be requested by calling getFile." +msgstr "" + +#: of telebot.types.File:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#file" +msgstr "" + +#: of telebot.types.File:17 +msgid "" +"Optional. File path. Use " +"https://api.telegram.org/file/bot/ to get the file." +msgstr "" + +#: of telebot.types.File:22 +msgid ":class:`telebot.types.File`" +msgstr "" + +#: of telebot.types.ForceReply:1 telebot.types.ReplyKeyboardMarkup:1 +#: telebot.types.ReplyKeyboardRemove:1 telebot.types.ShippingOption:1 +msgid "Bases: :py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.ForceReply:1 +msgid "" +"Upon receiving a message with this object, Telegram clients will display " +"a reply interface to the user (act as if the user has selected the bot's " +"message and tapped 'Reply'). This can be extremely useful if you want to " +"create user-friendly step-by-step interfaces without having to sacrifice " +"privacy mode." +msgstr "" + +#: of telebot.types.ForceReply:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#forcereply" +msgstr "" + +#: of telebot.types.ForceReply:5 +msgid "" +"Shows reply interface to the user, as if they manually selected the bot's" +" message and tapped 'Reply'" +msgstr "" + +#: of telebot.types.ForceReply:9 +msgid "" +"Optional. The placeholder to be shown in the input field when the reply " +"is active; 1-64 characters" +msgstr "" + +#: of telebot.types.ForceReply:13 +msgid "" +"Optional. Use this parameter if you want to force reply from specific " +"users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message." +msgstr "" + +#: of telebot.types.ForceReply:19 +msgid ":class:`telebot.types.ForceReply`" +msgstr "" + +#: of telebot.types.ForumTopic:1 +msgid "This object represents a forum topic." +msgstr "" + +#: of telebot.types.ForumTopic:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#forumtopic" +msgstr "" + +#: of telebot.types.ForumTopic:5 +msgid "Unique identifier of the forum topic" +msgstr "" + +#: of telebot.types.ForumTopic:8 telebot.types.ForumTopicCreated:5 +msgid "Name of the topic" +msgstr "" + +#: of telebot.types.ForumTopic:11 telebot.types.ForumTopicCreated:8 +msgid "Color of the topic icon in RGB format" +msgstr "" + +#: of telebot.types.ForumTopic:14 telebot.types.ForumTopicCreated:11 +msgid "Optional. Unique identifier of the custom emoji shown as the topic icon" +msgstr "" + +#: of telebot.types.ForumTopic:18 +msgid ":class:`telebot.types.ForumTopic`" +msgstr "" + +#: of telebot.types.ForumTopicClosed:1 +msgid "" +"This object represents a service message about a forum topic closed in " +"the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.ForumTopicClosed:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopicclosed" +msgstr "" + +#: of telebot.types.ForumTopicCreated:1 +msgid "" +"This object represents a service message about a new forum topic created " +"in the chat." +msgstr "" + +#: of telebot.types.ForumTopicCreated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopiccreated" +msgstr "" + +#: of telebot.types.ForumTopicCreated:15 +msgid ":class:`telebot.types.ForumTopicCreated`" +msgstr "" + +#: of telebot.types.ForumTopicReopened:1 +msgid "" +"This object represents a service message about a forum topic reopened in " +"the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.ForumTopicReopened:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopicreopened" +msgstr "" + +#: of telebot.types.Game:1 +msgid "" +"This object represents a game. Use BotFather to create and edit games, " +"their short names will act as unique identifiers." +msgstr "" + +#: of telebot.types.Game:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#game" +msgstr "" + +#: of telebot.types.Game:5 +msgid "Title of the game" +msgstr "" + +#: of telebot.types.Game:8 +msgid "Description of the game" +msgstr "" + +#: of telebot.types.Game:11 +msgid "Photo that will be displayed in the game message in chats." +msgstr "" + +#: of telebot.types.Game:14 +msgid "" +"Optional. Brief description of the game or high scores included in the " +"game message. Can be automatically edited to include current high scores " +"for the game when the bot calls setGameScore, or manually edited using " +"editMessageText. 0-4096 characters." +msgstr "" + +#: of telebot.types.Game:19 +msgid "" +"Optional. Special entities that appear in text, such as usernames, URLs, " +"bot commands, etc." +msgstr "" + +#: of telebot.types.Game:22 +msgid "" +"Optional. Animation that will be displayed in the game message in chats. " +"Upload via BotFather" +msgstr "" + +#: of telebot.types.Game:26 +msgid ":class:`telebot.types.Game`" +msgstr "" + +#: of telebot.types.Game.parse_entities:1 +msgid "Parse the message entity array into a list of MessageEntity objects" +msgstr "" + +#: of telebot.types.Game.parse_photo:1 +msgid "Parse the photo array into a list of PhotoSize objects" +msgstr "" + +#: of telebot.types.GameHighScore:1 +msgid "This object represents one row of the high scores table for a game." +msgstr "" + +#: of telebot.types.GameHighScore:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#gamehighscore" +msgstr "" + +#: of telebot.types.GameHighScore:5 +msgid "Position in high score table for the game" +msgstr "" + +#: of telebot.types.GameHighScore:8 +msgid "User" +msgstr "" + +#: of telebot.types.GameHighScore:11 +msgid "Score" +msgstr "" + +#: of telebot.types.GameHighScore:15 +msgid ":class:`telebot.types.GameHighScore`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 +#: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 +msgid "" +"This object represents one button of an inline keyboard. You must use " +"exactly one of the optional fields." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinekeyboardbutton" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:5 +msgid "Label text on the button" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:8 +msgid "" +"Optional. HTTP or tg:// URL to be opened when the button is pressed. " +"Links tg://user?id= can be used to mention a user by their ID " +"without using a username, if this is allowed by their privacy settings." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:12 +msgid "" +"Optional. Data to be sent in a callback query to the bot when button is " +"pressed, 1-64 bytes" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:15 +msgid "" +"Optional. Description of the Web App that will be launched when the user " +"presses the button. The Web App will be able to send an arbitrary message" +" on behalf of the user using the method answerWebAppQuery. Available only" +" in private chats between a user and the bot." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:20 +msgid "" +"Optional. An HTTPS URL used to automatically authorize the user. Can be " +"used as a replacement for the Telegram Login Widget." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:24 +msgid "" +"Optional. If set, pressing the button will prompt the user to select one " +"of their chats, open that chat and insert the bot's username and the " +"specified inline query in the input field. May be empty, in which case " +"just the bot's username will be inserted.Note: This offers an easy way " +"for users to start using your bot in inline mode when they are currently " +"in a private chat with it. Especially useful when combined with " +"switch_pm… actions - in this case the user will be automatically returned" +" to the chat they switched from, skipping the chat selection screen." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:31 +msgid "" +"Optional. If set, pressing the button will insert the bot's username and " +"the specified inline query in the current chat's input field. May be " +"empty, in which case only the bot's username will be inserted.This offers" +" a quick way for the user to open your bot in inline mode in the same " +"chat - good for selecting something from multiple options." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:37 +msgid "" +"Optional. Description of the game that will be launched when the user " +"presses the button. NOTE: This type of button must always be the first " +"button in the first row." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:41 +msgid "" +"Optional. Specify True, to send a Pay button. NOTE: This type of button " +"must always be the first button in the first row and can only be used in " +"invoice messages." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:46 +msgid ":class:`telebot.types.InlineKeyboardButton`" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:1 +msgid "" +"This object represents an inline keyboard that appears right next to the " +"message it belongs to." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:4 +msgid "It is recommended to use :meth:`telebot.util.quick_markup` instead." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:6 +msgid "Example of a custom keyboard with buttons." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:16 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinekeyboardmarkup" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:18 +msgid "" +":obj:`list` of button rows, each represented by an :obj:`list` of " +":class:`telebot.types.InlineKeyboardButton` objects" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:23 +#: telebot.types.InlineKeyboardMarkup.add:17 +#: telebot.types.InlineKeyboardMarkup.row:12 +msgid ":class:`telebot.types.InlineKeyboardMarkup`" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:1 +msgid "This method adds buttons to the keyboard without exceeding row_width." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:3 +msgid "" +"E.g. InlineKeyboardMarkup.add(\"A\", \"B\", \"C\") yields the json " +"result: {keyboard: [[\"A\"], [\"B\"], [\"C\"]]} when row_width is set to " +"1. When row_width is set to 2, the result: {keyboard: [[\"A\", \"B\"], " +"[\"C\"]]} See https://core.telegram.org/bots/api#inlinekeyboardmarkup" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:10 +#: telebot.types.InlineKeyboardMarkup.row:8 +msgid "Array of InlineKeyboardButton to append to the keyboard" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:13 +#: telebot.types.ReplyKeyboardMarkup.add:10 +msgid "width of row" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:16 +#: telebot.types.InlineKeyboardMarkup.row:11 +#: telebot.types.ReplyKeyboardMarkup.add:13 +#: telebot.types.ReplyKeyboardMarkup.row:8 +msgid "self, to allow function chaining." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.row:1 +msgid "" +"Adds a list of InlineKeyboardButton to the keyboard. This method does not" +" consider row_width." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.row:4 +msgid "" +"InlineKeyboardMarkup.row(\"A\").row(\"B\", \"C\").to_json() outputs: " +"'{keyboard: [[\"A\"], [\"B\", \"C\"]]}' See " +"https://core.telegram.org/bots/api#inlinekeyboardmarkup" +msgstr "" + +#: of telebot.types.InlineQuery:1 +msgid "" +"This object represents an incoming inline query. When the user sends an " +"empty query, your bot could return some default or trending results." +msgstr "" + +#: of telebot.types.InlineQuery:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inlinequery" +msgstr "" + +#: of telebot.types.InlineQuery:11 +msgid "Text of the query (up to 256 characters)" +msgstr "" + +#: of telebot.types.InlineQuery:14 +msgid "Offset of the results to be returned, can be controlled by the bot" +msgstr "" + +#: of telebot.types.InlineQuery:17 +msgid "" +"Optional. Type of the chat from which the inline query was sent. Can be " +"either “sender” for a private chat with the inline query sender, " +"“private”, “group”, “supergroup”, or “channel”. The chat type should be " +"always known for requests sent from official clients and most third-party" +" clients, unless the request was sent from a secret chat" +msgstr "" + +#: of telebot.types.InlineQuery:23 +msgid "Optional. Sender location, only for bots that request user location" +msgstr "" + +#: of telebot.types.InlineQuery:27 +msgid ":class:`telebot.types.InlineQuery`" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:1 +#: telebot.types.InlineQueryResultAudio:1 +#: telebot.types.InlineQueryResultContact:1 +#: telebot.types.InlineQueryResultDocument:1 +#: telebot.types.InlineQueryResultGame:1 telebot.types.InlineQueryResultGif:1 +#: telebot.types.InlineQueryResultLocation:1 +#: telebot.types.InlineQueryResultMpeg4Gif:1 +#: telebot.types.InlineQueryResultPhoto:1 +#: telebot.types.InlineQueryResultVenue:1 +#: telebot.types.InlineQueryResultVideo:1 +#: telebot.types.InlineQueryResultVoice:1 +msgid "Bases: :py:class:`telebot.types.InlineQueryResultBase`" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:1 +msgid "Represents a link to an article or web page." +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultarticle" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:5 +msgid "Type of the result, must be article" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:8 +#: telebot.types.InlineQueryResultContact:8 +#: telebot.types.InlineQueryResultLocation:8 +#: telebot.types.InlineQueryResultVenue:8 +msgid "Unique identifier for this result, 1-64 Bytes" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:11 +msgid "Title of the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:14 +msgid "Content of the message to be sent" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:17 +#: telebot.types.InlineQueryResultAudio:34 +#: telebot.types.InlineQueryResultCachedAudio:25 +#: telebot.types.InlineQueryResultCachedDocument:31 +#: telebot.types.InlineQueryResultCachedGif:27 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:27 +#: telebot.types.InlineQueryResultCachedPhoto:31 +#: telebot.types.InlineQueryResultCachedSticker:14 +#: telebot.types.InlineQueryResultCachedVideo:31 +#: telebot.types.InlineQueryResultCachedVoice:28 +#: telebot.types.InlineQueryResultContact:23 +#: telebot.types.InlineQueryResultDocument:34 +#: telebot.types.InlineQueryResultGame:14 telebot.types.InlineQueryResultGif:43 +#: telebot.types.InlineQueryResultLocation:35 +#: telebot.types.InlineQueryResultMpeg4Gif:43 +#: telebot.types.InlineQueryResultPhoto:40 +#: telebot.types.InlineQueryResultVenue:36 +#: telebot.types.InlineQueryResultVideo:46 +#: telebot.types.InlineQueryResultVoice:31 +msgid "Optional. Inline keyboard attached to the message" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:20 +msgid "Optional. URL of the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:23 +msgid "Optional. Pass True, if you don't want the URL to be shown in the message" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:26 +#: telebot.types.InlineQueryResultCachedDocument:17 +#: telebot.types.InlineQueryResultCachedPhoto:17 +#: telebot.types.InlineQueryResultCachedVideo:17 +#: telebot.types.InlineQueryResultDocument:31 +#: telebot.types.InlineQueryResultPhoto:26 +#: telebot.types.InlineQueryResultVideo:43 +msgid "Optional. Short description of the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:29 +#: telebot.types.InlineQueryResultContact:29 +#: telebot.types.InlineQueryResultLocation:41 +#: telebot.types.InlineQueryResultVenue:42 +msgid "Optional. Url of the thumbnail for the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:32 +#: telebot.types.InlineQueryResultContact:32 +#: telebot.types.InlineQueryResultDocument:43 +#: telebot.types.InlineQueryResultLocation:44 +#: telebot.types.InlineQueryResultVenue:45 +msgid "Optional. Thumbnail width" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:35 +#: telebot.types.InlineQueryResultContact:35 +#: telebot.types.InlineQueryResultDocument:46 +#: telebot.types.InlineQueryResultLocation:47 +#: telebot.types.InlineQueryResultVenue:48 +msgid "Optional. Thumbnail height" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:39 +msgid ":class:`telebot.types.InlineQueryResultArticle`" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultaudio" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:5 +#: telebot.types.InlineQueryResultCachedAudio:5 telebot.types.InputMediaAudio:5 +msgid "Type of the result, must be audio" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:8 +#: telebot.types.InlineQueryResultCachedAudio:8 +#: telebot.types.InlineQueryResultCachedDocument:8 +#: telebot.types.InlineQueryResultCachedGif:8 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:8 +#: telebot.types.InlineQueryResultCachedPhoto:8 +#: telebot.types.InlineQueryResultCachedSticker:8 +#: telebot.types.InlineQueryResultCachedVideo:8 +#: telebot.types.InlineQueryResultCachedVoice:8 +#: telebot.types.InlineQueryResultDocument:8 +#: telebot.types.InlineQueryResultGame:8 telebot.types.InlineQueryResultGif:8 +#: telebot.types.InlineQueryResultMpeg4Gif:8 +#: telebot.types.InlineQueryResultPhoto:8 +#: telebot.types.InlineQueryResultVideo:8 +#: telebot.types.InlineQueryResultVoice:8 +msgid "Unique identifier for this result, 1-64 bytes" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:11 +msgid "A valid URL for the audio file" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:14 +msgid "Title" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:17 +#: telebot.types.InlineQueryResultCachedAudio:14 +#: telebot.types.InlineQueryResultCachedVoice:17 +#: telebot.types.InlineQueryResultVoice:17 +msgid "Optional. Caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:20 +#: telebot.types.InlineQueryResultCachedAudio:17 +#: telebot.types.InputMediaAudio:23 +msgid "" +"Optional. Mode for parsing entities in the audio caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:24 +#: telebot.types.InlineQueryResultCachedAudio:21 +#: telebot.types.InlineQueryResultCachedDocument:27 +#: telebot.types.InlineQueryResultCachedGif:23 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:23 +#: telebot.types.InlineQueryResultCachedPhoto:27 +#: telebot.types.InlineQueryResultCachedVideo:27 +#: telebot.types.InlineQueryResultCachedVoice:24 +#: telebot.types.InlineQueryResultDocument:21 +#: telebot.types.InlineQueryResultGif:39 +#: telebot.types.InlineQueryResultMpeg4Gif:39 +#: telebot.types.InlineQueryResultPhoto:36 +#: telebot.types.InlineQueryResultVideo:30 +#: telebot.types.InlineQueryResultVoice:24 telebot.types.InputMediaAnimation:27 +#: telebot.types.InputMediaAudio:27 telebot.types.InputMediaDocument:27 +#: telebot.types.InputMediaPhoto:20 telebot.types.InputMediaVideo:27 +msgid "" +"Optional. List of special entities that appear in the caption, which can " +"be specified instead of parse_mode" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:28 +msgid "Optional. Performer" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:31 +msgid "Optional. Audio duration in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:37 +#: telebot.types.InlineQueryResultCachedAudio:28 +msgid "Optional. Content of the message to be sent instead of the audio" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:41 +msgid ":class:`telebot.types.InlineQueryResultAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:1 +msgid "" +"Bases: :py:class:`abc.ABC`, :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:1 +msgid "" +"This object represents one result of an inline query. Telegram clients " +"currently support results of the following 20 types:" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:3 +msgid ":class:`InlineQueryResultCachedAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:4 +msgid ":class:`InlineQueryResultCachedDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:5 +msgid ":class:`InlineQueryResultCachedGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:6 +msgid ":class:`InlineQueryResultCachedMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:7 +msgid ":class:`InlineQueryResultCachedPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:8 +msgid ":class:`InlineQueryResultCachedSticker`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:9 +msgid ":class:`InlineQueryResultCachedVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:10 +msgid ":class:`InlineQueryResultCachedVoice`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:11 +msgid ":class:`InlineQueryResultArticle`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:12 +msgid ":class:`InlineQueryResultAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:13 +msgid ":class:`InlineQueryResultContact`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:14 +msgid ":class:`InlineQueryResultGame`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:15 +msgid ":class:`InlineQueryResultDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:16 +msgid ":class:`InlineQueryResultGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:17 +msgid ":class:`InlineQueryResultLocation`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:18 +msgid ":class:`InlineQueryResultMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:19 +msgid ":class:`InlineQueryResultPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:20 +msgid ":class:`InlineQueryResultVenue`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:21 +msgid ":class:`InlineQueryResultVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:22 +msgid ":class:`InlineQueryResultVoice`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:24 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresult" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:1 +#: telebot.types.InlineQueryResultCachedDocument:1 +#: telebot.types.InlineQueryResultCachedGif:1 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:1 +#: telebot.types.InlineQueryResultCachedPhoto:1 +#: telebot.types.InlineQueryResultCachedSticker:1 +#: telebot.types.InlineQueryResultCachedVideo:1 +#: telebot.types.InlineQueryResultCachedVoice:1 +msgid "Bases: :py:class:`telebot.types.InlineQueryResultCachedBase`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedaudio" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:11 +msgid "A valid file identifier for the audio file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:32 +msgid ":class:`telebot.types.InlineQueryResultCachedAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedBase:1 +msgid "Base class of all InlineQueryResultCached* classes." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcacheddocument" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:5 +#: telebot.types.InlineQueryResultDocument:5 telebot.types.InputMediaDocument:5 +msgid "Type of the result, must be document" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:11 +#: telebot.types.InlineQueryResultCachedVideo:14 +#: telebot.types.InlineQueryResultDocument:11 +#: telebot.types.InlineQueryResultVideo:20 +msgid "Title for the result" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:14 +msgid "A valid file identifier for the file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:20 +#: telebot.types.InlineQueryResultDocument:14 +#: telebot.types.InputMediaDocument:20 +msgid "" +"Optional. Caption of the document to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:23 +#: telebot.types.InlineQueryResultDocument:17 +#: telebot.types.InputMediaDocument:23 +msgid "" +"Optional. Mode for parsing entities in the document caption. See " +"formatting options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:34 +#: telebot.types.InlineQueryResultDocument:37 +msgid "Optional. Content of the message to be sent instead of the file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:38 +msgid ":class:`telebot.types.InlineQueryResultCachedDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedgif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:5 +#: telebot.types.InlineQueryResultGif:5 +msgid "Type of the result, must be gif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:11 +msgid "A valid file identifier for the GIF file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:14 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:14 +#: telebot.types.InlineQueryResultCachedPhoto:14 +#: telebot.types.InlineQueryResultGif:30 +#: telebot.types.InlineQueryResultMpeg4Gif:30 +#: telebot.types.InlineQueryResultPhoto:23 +msgid "Optional. Title for the result" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:17 +#: telebot.types.InlineQueryResultGif:33 +msgid "" +"Optional. Caption of the GIF file to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:20 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:20 +#: telebot.types.InlineQueryResultGif:36 +#: telebot.types.InlineQueryResultMpeg4Gif:36 +msgid "" +"Optional. Mode for parsing entities in the caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:30 +#: telebot.types.InlineQueryResultGif:46 +msgid "Optional. Content of the message to be sent instead of the GIF animation" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:34 +msgid ":class:`telebot.types.InlineQueryResultCachedGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:5 +#: telebot.types.InlineQueryResultMpeg4Gif:5 +msgid "Type of the result, must be mpeg4_gif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:11 +msgid "A valid file identifier for the MPEG4 file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:17 +#: telebot.types.InlineQueryResultMpeg4Gif:33 +msgid "" +"Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:30 +#: telebot.types.InlineQueryResultMpeg4Gif:46 +msgid "Optional. Content of the message to be sent instead of the video animation" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:34 +msgid ":class:`telebot.types.InlineQueryResultCachedMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedphoto" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:5 +#: telebot.types.InlineQueryResultPhoto:5 telebot.types.InputMediaPhoto:5 +msgid "Type of the result, must be photo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:11 +msgid "A valid file identifier of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:20 +#: telebot.types.InlineQueryResultPhoto:29 telebot.types.InputMediaPhoto:13 +msgid "" +"Optional. Caption of the photo to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:23 +#: telebot.types.InlineQueryResultPhoto:32 telebot.types.InputMediaPhoto:16 +msgid "" +"Optional. Mode for parsing entities in the photo caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:34 +#: telebot.types.InlineQueryResultPhoto:43 +msgid "Optional. Content of the message to be sent instead of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:38 +msgid ":class:`telebot.types.InlineQueryResultCachedPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedsticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:5 +msgid "Type of the result, must be sticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:11 +msgid "A valid file identifier of the sticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:17 +msgid "Optional. Content of the message to be sent instead of the sticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:21 +msgid ":class:`telebot.types.InlineQueryResultCachedSticker`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedvideo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:5 +#: telebot.types.InlineQueryResultVideo:5 telebot.types.InputMediaVideo:5 +msgid "Type of the result, must be video" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:11 +msgid "A valid file identifier for the video file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:20 +#: telebot.types.InlineQueryResultVideo:23 telebot.types.InputMediaVideo:20 +msgid "" +"Optional. Caption of the video to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:23 +#: telebot.types.InlineQueryResultVideo:26 telebot.types.InputMediaVideo:23 +msgid "" +"Optional. Mode for parsing entities in the video caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:34 +msgid "Optional. Content of the message to be sent instead of the video" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:38 +msgid ":class:`telebot.types.InlineQueryResultCachedVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedvoice" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:5 +#: telebot.types.InlineQueryResultVoice:5 +msgid "Type of the result, must be voice" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:11 +msgid "A valid file identifier for the voice message" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:14 +msgid "Voice message title" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:20 +#: telebot.types.InlineQueryResultVoice:20 +msgid "" +"Optional. Mode for parsing entities in the voice message caption. See " +"formatting options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:31 +msgid "Optional. Content of the message to be sent instead of the voice message" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:35 +msgid ":class:`telebot.types.InlineQueryResultCachedVoice`" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultContact:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcontact" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:5 +msgid "Type of the result, must be contact" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:20 +#: telebot.types.InputContactMessageContent:14 +msgid "" +"Optional. Additional data about the contact in the form of a vCard, " +"0-2048 bytes" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:26 +msgid "Optional. Content of the message to be sent instead of the contact" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:39 +msgid ":class:`telebot.types.InlineQueryResultContact`" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultdocument" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:25 +msgid "A valid URL for the file" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:28 +msgid "" +"MIME type of the content of the file, either “application/pdf” or " +"“application/zip”" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:40 +msgid "Optional. URL of the thumbnail (JPEG only) for the file" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:50 +msgid ":class:`telebot.types.InlineQueryResultDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:1 +msgid "Represents a Game." +msgstr "" + +#: of telebot.types.InlineQueryResultGame:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultgame" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:5 +msgid "Type of the result, must be game" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:11 +msgid "Short name of the game" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:18 +msgid ":class:`telebot.types.InlineQueryResultGame`" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultGif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultgif" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:11 +msgid "A valid URL for the GIF file. File size must not exceed 1MB" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:14 +msgid "Optional. Width of the GIF" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:17 +msgid "Optional. Height of the GIF" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:20 +msgid "Optional. Duration of the GIF in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:23 +#: telebot.types.InlineQueryResultMpeg4Gif:23 +msgid "" +"URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the " +"result" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:26 +#: telebot.types.InlineQueryResultMpeg4Gif:26 +msgid "" +"Optional. MIME type of the thumbnail, must be one of “image/jpeg”, " +"“image/gif”, or “video/mp4”. Defaults to “image/jpeg”" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:50 +msgid ":class:`telebot.types.InlineQueryResultGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultlocation" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:5 +msgid "Type of the result, must be location" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:11 +msgid "Location latitude in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:14 +msgid "Location longitude in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:17 +msgid "Location title" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:20 +#: telebot.types.InputLocationMessageContent:11 telebot.types.Location:11 +msgid "" +"Optional. The radius of uncertainty for the location, measured in meters;" +" 0-1500" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:23 +#: telebot.types.InputLocationMessageContent:14 +msgid "" +"Optional. Period in seconds for which the location can be updated, should" +" be between 60 and 86400." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:27 +#: telebot.types.InputLocationMessageContent:18 +msgid "" +"Optional. For live locations, a direction in which the user is moving, in" +" degrees. Must be between 1 and 360 if specified." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:31 +#: telebot.types.InputLocationMessageContent:22 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:38 +msgid "Optional. Content of the message to be sent instead of the location" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:51 +msgid ":class:`telebot.types.InlineQueryResultLocation`" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:11 +msgid "A valid URL for the MPEG4 file. File size must not exceed 1MB" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:14 +#: telebot.types.InlineQueryResultVideo:34 telebot.types.InputMediaVideo:31 +msgid "Optional. Video width" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:17 +#: telebot.types.InlineQueryResultVideo:37 telebot.types.InputMediaVideo:34 +msgid "Optional. Video height" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:20 +#: telebot.types.InlineQueryResultVideo:40 telebot.types.InputMediaVideo:37 +msgid "Optional. Video duration in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:50 +msgid ":class:`telebot.types.InlineQueryResultMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultphoto" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:11 +msgid "" +"A valid URL of the photo. Photo must be in JPEG format. Photo size must " +"not exceed 5MB" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:14 +msgid "URL of the thumbnail for the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:17 +msgid "Optional. Width of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:20 +msgid "Optional. Height of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:47 +msgid ":class:`telebot.types.InlineQueryResultPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultvenue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:5 +msgid "Type of the result, must be venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:11 +msgid "Latitude of the venue location in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:14 +msgid "Longitude of the venue location in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:17 +msgid "Title of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:20 +#: telebot.types.InputVenueMessageContent:14 telebot.types.Venue:11 +msgid "Address of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:23 +msgid "Optional. Foursquare identifier of the venue if known" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:26 +#: telebot.types.InputVenueMessageContent:20 +msgid "" +"Optional. Foursquare type of the venue, if known. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:30 +#: telebot.types.InputVenueMessageContent:24 telebot.types.Venue:21 +msgid "Optional. Google Places identifier of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:33 +#: telebot.types.InputVenueMessageContent:27 telebot.types.Venue:24 +msgid "Optional. Google Places type of the venue. (See supported types.)" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:39 +msgid "Optional. Content of the message to be sent instead of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:52 +msgid ":class:`telebot.types.InlineQueryResultVenue`" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultvideo" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:11 +msgid "A valid URL for the embedded video player or video file" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:14 +msgid "MIME type of the content of the video URL, “text/html” or “video/mp4”" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:17 +msgid "URL of the thumbnail (JPEG only) for the video" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:49 +msgid "" +"Optional. Content of the message to be sent instead of the video. This " +"field is required if InlineQueryResultVideo is used to send an HTML-page " +"as a result (e.g., a YouTube video)." +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:54 +msgid ":class:`telebot.types.InlineQueryResultVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultvoice" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:11 +msgid "A valid URL for the voice recording" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:14 +msgid "Recording title" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:28 +msgid "Optional. Recording duration in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:34 +msgid "Optional. Content of the message to be sent instead of the voice recording" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:38 +msgid ":class:`telebot.types.InlineQueryResultVoice`" +msgstr "" + +#: of telebot.types.InputContactMessageContent:1 +#: telebot.types.InputInvoiceMessageContent:1 +#: telebot.types.InputLocationMessageContent:1 +#: telebot.types.InputTextMessageContent:1 +#: telebot.types.InputVenueMessageContent:1 +#: telebot.types.KeyboardButtonPollType:1 +msgid "Bases: :py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.InputContactMessageContent:1 +msgid "" +"Represents the content of a contact message to be sent as the result of " +"an inline query." +msgstr "" + +#: of telebot.types.InputContactMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputcontactmessagecontent" +msgstr "" + +#: of telebot.types.InputContactMessageContent:18 +msgid ":class:`telebot.types.InputContactMessageContent`" +msgstr "" + +#: of telebot.types.InputFile:1 +msgid "A class to send files through Telegram Bot API." +msgstr "" + +#: of telebot.types.InputFile:3 +msgid "" +"You need to pass a file, which should be an instance of " +":class:`io.IOBase` or :class:`pathlib.Path`, or :obj:`str`." +msgstr "" + +#: of telebot.types.InputFile:6 +msgid "" +"If you pass an :obj:`str` as a file, it will be opened and closed by the " +"class." +msgstr "" + +#: of telebot.types.InputFile:8 +msgid "A file to send." +msgstr "" + +#: of telebot.types.InputFile:11 +msgid "Example on sending a file using this class" +msgstr "" + +#: of telebot.types.InputFile.file:1 +msgid "File object." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:1 +msgid "" +"Represents the content of an invoice message to be sent as the result of " +"an inline query." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputinvoicemessagecontent" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:5 +msgid "Product name, 1-32 characters" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:8 +msgid "Product description, 1-255 characters" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:11 +msgid "" +"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " +"the user, use for your internal processes." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:15 +msgid "Payment provider token, obtained via @BotFather" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:18 +msgid "Three-letter ISO 4217 currency code, see more on currencies" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:21 +msgid "" +"Price breakdown, a JSON-serialized list of components (e.g. product " +"price, tax, discount, delivery cost, delivery tax, bonus, etc.)" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:25 +msgid "" +"Optional. The maximum accepted amount for tips in the smallest units of " +"the currency (integer, not float/double). For example, for a maximum tip " +"of US$ 1.45 pass max_tip_amount = 145. See the exp parameter in " +"currencies.json, it shows the number of digits past the decimal point for" +" each currency (2 for the majority of currencies). Defaults to 0" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:31 +msgid "" +"Optional. A JSON-serialized array of suggested amounts of tip in the " +"smallest units of the currency (integer, not float/double). 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." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:36 +msgid "" +"Optional. A JSON-serialized object for data about the invoice, which will" +" be shared with the payment provider. A detailed description of the " +"required fields should be provided by the payment provider." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:40 +msgid "" +"Optional. URL of the product photo for the invoice. Can be a photo of the" +" goods or a marketing image for a service." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:44 +msgid "Optional. Photo size in bytes" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:47 +msgid "Optional. Photo width" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:50 +msgid "Optional. Photo height" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:53 +msgid "" +"Optional. Pass True, if you require the user's full name to complete the " +"order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:56 +msgid "" +"Optional. Pass True, if you require the user's phone number to complete " +"the order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:59 +msgid "" +"Optional. Pass True, if you require the user's email address to complete " +"the order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:62 +msgid "" +"Optional. Pass True, if you require the user's shipping address to " +"complete the order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:66 +msgid "Optional. Pass True, if the user's phone number should be sent to provider" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:69 +msgid "" +"Optional. Pass True, if the user's email address should be sent to " +"provider" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:72 +msgid "Optional. Pass True, if the final price depends on the shipping method" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:76 +msgid ":class:`telebot.types.InputInvoiceMessageContent`" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:1 +msgid "" +"Represents the content of a location message to be sent as the result of " +"an inline query." +msgstr "" + +#: of telebot.types.InputLocationMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputlocationmessagecontent" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:5 +msgid "Latitude of the location in degrees" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:8 +msgid "Longitude of the location in degrees" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:27 +msgid ":class:`telebot.types.InputLocationMessageContent`" +msgstr "" + +#: of telebot.types.InputMedia:1 telebot.types.KeyboardButton:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.InputMedia:1 +msgid "" +"This object represents the content of a media message to be sent. It " +"should be one of" +msgstr "" + +#: of telebot.types.InputMedia:3 +msgid ":class:`InputMediaAnimation`" +msgstr "" + +#: of telebot.types.InputMedia:4 +msgid ":class:`InputMediaDocument`" +msgstr "" + +#: of telebot.types.InputMedia:5 +msgid ":class:`InputMediaAudio`" +msgstr "" + +#: of telebot.types.InputMedia:6 +msgid ":class:`InputMediaPhoto`" +msgstr "" + +#: of telebot.types.InputMedia:7 +msgid ":class:`InputMediaVideo`" +msgstr "" + +#: of telebot.types.InputMediaAnimation:1 telebot.types.InputMediaAudio:1 +#: telebot.types.InputMediaDocument:1 telebot.types.InputMediaPhoto:1 +#: telebot.types.InputMediaVideo:1 +msgid "Bases: :py:class:`telebot.types.InputMedia`" +msgstr "" + +#: of telebot.types.InputMediaAnimation:1 +msgid "" +"Represents an animation file (GIF or H.264/MPEG-4 AVC video without " +"sound) to be sent." +msgstr "" + +#: of telebot.types.InputMediaAnimation:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputmediaanimation" +msgstr "" + +#: of telebot.types.InputMediaAnimation:5 +msgid "Type of the result, must be animation" +msgstr "" + +#: of telebot.types.InputMediaAnimation:8 telebot.types.InputMediaAudio:8 +#: telebot.types.InputMediaDocument:8 telebot.types.InputMediaPhoto:8 +#: telebot.types.InputMediaVideo:8 +msgid "" +"File to send. Pass a file_id to send a file that exists on the Telegram " +"servers (recommended), pass an HTTP URL for Telegram to get a file from " +"the Internet, or pass “attach://” to upload a new one " +"using multipart/form-data under name. More information" +" on Sending Files »" +msgstr "" + +#: of telebot.types.InputMediaAnimation:13 telebot.types.InputMediaAudio:13 +#: telebot.types.InputMediaDocument:13 telebot.types.InputMediaVideo:13 +msgid "" +"Optional. 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 " +". More information on Sending Files »" +msgstr "" + +#: of telebot.types.InputMediaAnimation:20 +msgid "" +"Optional. Caption of the animation to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InputMediaAnimation:23 +msgid "" +"Optional. Mode for parsing entities in the animation caption. See " +"formatting options for more details." +msgstr "" + +#: of telebot.types.InputMediaAnimation:31 +msgid "Optional. Animation width" +msgstr "" + +#: of telebot.types.InputMediaAnimation:34 +msgid "Optional. Animation height" +msgstr "" + +#: of telebot.types.InputMediaAnimation:37 +msgid "Optional. Animation duration in seconds" +msgstr "" + +#: of telebot.types.InputMediaAnimation:41 +msgid ":class:`telebot.types.InputMediaAnimation`" +msgstr "" + +#: of telebot.types.InputMediaAudio:1 +msgid "Represents an audio file to be treated as music to be sent." +msgstr "" + +#: of telebot.types.InputMediaAudio:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio" +msgstr "" + +#: of telebot.types.InputMediaAudio:20 +msgid "" +"Optional. Caption of the audio to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InputMediaAudio:31 +msgid "Optional. Duration of the audio in seconds" +msgstr "" + +#: of telebot.types.InputMediaAudio:34 +msgid "Optional. Performer of the audio" +msgstr "" + +#: of telebot.types.InputMediaAudio:37 +msgid "Optional. Title of the audio" +msgstr "" + +#: of telebot.types.InputMediaAudio:41 +msgid ":class:`telebot.types.InputMediaAudio`" +msgstr "" + +#: of telebot.types.InputMediaDocument:1 +msgid "Represents a general file to be sent." +msgstr "" + +#: of telebot.types.InputMediaDocument:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputmediadocument" +msgstr "" + +#: of telebot.types.InputMediaDocument:31 +msgid "" +"Optional. Disables automatic server-side content type detection for files" +" uploaded using multipart/form-data. Always True, if the document is sent" +" as part of an album." +msgstr "" + +#: of telebot.types.InputMediaDocument:36 +msgid ":class:`telebot.types.InputMediaDocument`" +msgstr "" + +#: of telebot.types.InputMediaPhoto:1 +msgid "Represents a photo to be sent." +msgstr "" + +#: of telebot.types.InputMediaPhoto:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto" +msgstr "" + +#: of telebot.types.InputMediaPhoto:25 +msgid ":class:`telebot.types.InputMediaPhoto`" +msgstr "" + +#: of telebot.types.InputMediaVideo:1 +msgid "Represents a video to be sent." +msgstr "" + +#: of telebot.types.InputMediaVideo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo" +msgstr "" + +#: of telebot.types.InputMediaVideo:40 +msgid "Optional. Pass True, if the uploaded video is suitable for streaming" +msgstr "" + +#: of telebot.types.InputMediaVideo:44 +msgid ":class:`telebot.types.InputMediaVideo`" +msgstr "" + +#: of telebot.types.InputTextMessageContent:1 +msgid "" +"Represents the content of a text message to be sent as the result of an " +"inline query." +msgstr "" + +#: of telebot.types.InputTextMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputtextmessagecontent" +msgstr "" + +#: of telebot.types.InputTextMessageContent:5 +msgid "Text of the message to be sent, 1-4096 characters" +msgstr "" + +#: of telebot.types.InputTextMessageContent:8 +msgid "" +"Optional. Mode for parsing entities in the message text. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InputTextMessageContent:12 +msgid "" +"Optional. List of special entities that appear in message text, which can" +" be specified instead of parse_mode" +msgstr "" + +#: of telebot.types.InputTextMessageContent:16 +msgid "Optional. Disables link previews for links in the sent message" +msgstr "" + +#: of telebot.types.InputTextMessageContent:20 +msgid ":class:`telebot.types.InputTextMessageContent`" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:1 +msgid "" +"Represents the content of a venue message to be sent as the result of an " +"inline query." +msgstr "" + +#: of telebot.types.InputVenueMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputvenuemessagecontent" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:5 +msgid "Latitude of the venue in degrees" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:8 +msgid "Longitude of the venue in degrees" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:11 telebot.types.Venue:8 +msgid "Name of the venue" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:17 +msgid "Optional. Foursquare identifier of the venue, if known" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:31 +msgid ":class:`telebot.types.InputVenueMessageContent`" +msgstr "" + +#: of telebot.types.Invoice:1 +msgid "This object contains basic information about an invoice." +msgstr "" + +#: of telebot.types.Invoice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#invoice" +msgstr "" + +#: of telebot.types.Invoice:5 +msgid "Product name" +msgstr "" + +#: of telebot.types.Invoice:8 +msgid "Product description" +msgstr "" + +#: of telebot.types.Invoice:11 +msgid "" +"Unique bot deep-linking parameter that can be used to generate this " +"invoice" +msgstr "" + +#: of telebot.types.Invoice:14 telebot.types.PreCheckoutQuery:11 +#: telebot.types.SuccessfulPayment:5 +msgid "Three-letter ISO 4217 currency code" +msgstr "" + +#: of telebot.types.Invoice:17 telebot.types.PreCheckoutQuery:14 +#: telebot.types.SuccessfulPayment:8 +msgid "" +"Total price in the smallest units of the currency (integer, not " +"float/double). For example, for a price of US$ 1.45 pass amount = 145. " +"See the exp parameter in currencies.json, it shows the number of digits " +"past the decimal point for each currency (2 for the majority of " +"currencies)." +msgstr "" + +#: of telebot.types.Invoice:23 +msgid ":class:`telebot.types.Invoice`" +msgstr "" + +#: of telebot.types.JsonDeserializable:1 +msgid "" +"Subclasses of this class are guaranteed to be able to be created from a " +"json-style dict or json formatted string. All subclasses of this class " +"must override de_json." +msgstr "" + +#: of telebot.types.JsonSerializable:1 +msgid "" +"Subclasses of this class are guaranteed to be able to be converted to " +"JSON format. All subclasses of this class must override to_json." +msgstr "" + +#: of telebot.types.KeyboardButton:1 +msgid "" +"This object represents one button of the reply keyboard. For simple text " +"buttons String can be used instead of this object to specify text of the " +"button. Optional fields web_app, request_contact, request_location, and " +"request_poll are mutually exclusive." +msgstr "" + +#: of telebot.types.KeyboardButton:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#keyboardbutton" +msgstr "" + +#: of telebot.types.KeyboardButton:5 +msgid "" +"Text of the button. If none of the optional fields are used, it will be " +"sent as a message when the button is pressed" +msgstr "" + +#: of telebot.types.KeyboardButton:9 +msgid "" +"Optional. If True, the user's phone number will be sent as a contact when" +" the button is pressed. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:13 +msgid "" +"Optional. If True, the user's current location will be sent when the " +"button is pressed. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:17 +msgid "" +"Optional. If specified, the user will be asked to create a poll and send " +"it to the bot when the button is pressed. Available in private chats " +"only." +msgstr "" + +#: of telebot.types.KeyboardButton:21 +msgid "" +"Optional. If specified, the described Web App will be launched when the " +"button is pressed. The Web App will be able to send a “web_app_data” " +"service message. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:26 +msgid ":class:`telebot.types.KeyboardButton`" +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:1 +msgid "" +"This object represents type of a poll, which is allowed to be created and" +" sent when the corresponding button is pressed." +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#keyboardbuttonpolltype" +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:5 +msgid "" +"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." +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:10 +msgid ":class:`telebot.types.KeyboardButtonPollType`" +msgstr "" + +#: of telebot.types.LabeledPrice:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.LabeledPrice:1 +msgid "This object represents a portion of the price for goods or services." +msgstr "" + +#: of telebot.types.LabeledPrice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#labeledprice" +msgstr "" + +#: of telebot.types.LabeledPrice:5 +msgid "Portion label" +msgstr "" + +#: of telebot.types.LabeledPrice:8 +msgid "" +"Price of the product in the smallest units of the currency (integer, not " +"float/double). For example, for a price of US$ 1.45 pass amount = 145. " +"See the exp parameter in currencies.json, it shows the number of digits " +"past the decimal point for each currency (2 for the majority of " +"currencies)." +msgstr "" + +#: of telebot.types.LabeledPrice:14 +msgid ":class:`telebot.types.LabeledPrice`" +msgstr "" + +#: of telebot.types.Location:1 +msgid "This object represents a point on the map." +msgstr "" + +#: of telebot.types.Location:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#location" +msgstr "" + +#: of telebot.types.Location:5 +msgid "Longitude as defined by sender" +msgstr "" + +#: of telebot.types.Location:8 +msgid "Latitude as defined by sender" +msgstr "" + +#: of telebot.types.Location:14 +msgid "" +"Optional. Time relative to the message sending date, during which the " +"location can be updated; in seconds. For active live locations only." +msgstr "" + +#: of telebot.types.Location:18 +msgid "" +"Optional. The direction in which user is moving, in degrees; 1-360. For " +"active live locations only." +msgstr "" + +#: of telebot.types.Location:21 +msgid "" +"Optional. The maximum distance for proximity alerts about approaching " +"another chat member, in meters. For sent live locations only." +msgstr "" + +#: of telebot.types.Location:26 +msgid ":class:`telebot.types.Location`" +msgstr "" + +#: of telebot.types.LoginUrl:1 +msgid "" +"This object represents a parameter of the inline keyboard button used to " +"automatically authorize a user. Serves as a great replacement for the " +"Telegram Login Widget when the user is coming from Telegram. All the user" +" needs to do is tap/click a button and confirm that they want to log in:" +msgstr "" + +#: of telebot.types.LoginUrl:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#loginurl" +msgstr "" + +#: of telebot.types.LoginUrl:5 +msgid "" +"An HTTPS URL to be opened with user authorization data added to the query" +" string when the button is pressed. If the user refuses to provide " +"authorization data, the original URL without information about the user " +"will be opened. The data added is the same as described in Receiving " +"authorization data. NOTE: You must always check the hash of the received " +"data to verify the authentication and the integrity of the data as " +"described in Checking authorization." +msgstr "" + +#: of telebot.types.LoginUrl:12 +msgid "Optional. New text of the button in forwarded messages." +msgstr "" + +#: of telebot.types.LoginUrl:15 +msgid "" +"Optional. Username of a bot, which will be used for user authorization. " +"See Setting up a bot for more details. If not specified, the current " +"bot's username will be assumed. The url's domain must be the same as the " +"domain linked with the bot. See Linking your domain to the bot for more " +"details." +msgstr "" + +#: of telebot.types.LoginUrl:20 +msgid "" +"Optional. Pass True to request the permission for your bot to send " +"messages to the user." +msgstr "" + +#: of telebot.types.LoginUrl:25 +msgid ":class:`telebot.types.LoginUrl`" +msgstr "" + +#: of telebot.types.MaskPosition:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.MaskPosition:1 +msgid "" +"This object describes the position on faces where a mask should be placed" +" by default." +msgstr "" + +#: of telebot.types.MaskPosition:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#maskposition" +msgstr "" + +#: of telebot.types.MaskPosition:5 +msgid "" +"The part of the face relative to which the mask should be placed. One of " +"“forehead”, “eyes”, “mouth”, or “chin”." +msgstr "" + +#: of telebot.types.MaskPosition:9 +msgid "" +"Shift by X-axis measured in widths of the mask scaled to the face size, " +"from left to right. For example, choosing -1.0 will place mask just to " +"the left of the default mask position." +msgstr "" + +#: of telebot.types.MaskPosition:13 +msgid "" +"Shift by Y-axis measured in heights of the mask scaled to the face size, " +"from top to bottom. For example, 1.0 will place the mask just below the " +"default mask position." +msgstr "" + +#: of telebot.types.MaskPosition:17 +msgid "Mask scaling coefficient. For example, 2.0 means double size." +msgstr "" + +#: of telebot.types.MaskPosition:21 +msgid ":class:`telebot.types.MaskPosition`" +msgstr "" + +#: of telebot.types.MenuButton:1 +msgid "" +"This object describes the bot's menu button in a private chat. It should " +"be one of" +msgstr "" + +#: of telebot.types.MenuButton:3 +msgid ":class:`MenuButtonCommands`" +msgstr "" + +#: of telebot.types.MenuButton:4 +msgid ":class:`MenuButtonWebApp`" +msgstr "" + +#: of telebot.types.MenuButton:5 +msgid ":class:`MenuButtonDefault`" +msgstr "" + +#: of telebot.types.MenuButton:7 +msgid "" +"If a menu button other than MenuButtonDefault is set for a private chat, " +"then it is applied in the chat. Otherwise the default menu button is " +"applied. By default, the menu button opens the list of bot commands." +msgstr "" + +#: of telebot.types.MenuButtonCommands:1 telebot.types.MenuButtonDefault:1 +#: telebot.types.MenuButtonWebApp:1 +msgid "Bases: :py:class:`telebot.types.MenuButton`" +msgstr "" + +#: of telebot.types.MenuButtonCommands:1 +msgid "Represents a menu button, which opens the bot's list of commands." +msgstr "" + +#: of telebot.types.MenuButtonCommands:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#menubuttoncommands" +msgstr "" + +#: of telebot.types.MenuButtonCommands:5 +msgid "Type of the button, must be commands" +msgstr "" + +#: of telebot.types.MenuButtonCommands:9 +msgid ":class:`telebot.types.MenuButtonCommands`" +msgstr "" + +#: of telebot.types.MenuButtonDefault:1 +msgid "Describes that no specific value for the menu button was set." +msgstr "" + +#: of telebot.types.MenuButtonDefault:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#menubuttondefault" +msgstr "" + +#: of telebot.types.MenuButtonDefault:5 +msgid "Type of the button, must be default" +msgstr "" + +#: of telebot.types.MenuButtonDefault:9 +msgid ":class:`telebot.types.MenuButtonDefault`" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:1 +msgid "Represents a menu button, which launches a Web App." +msgstr "" + +#: of telebot.types.MenuButtonWebApp:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#menubuttonwebapp" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:5 +msgid "Type of the button, must be web_app" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:8 +msgid "Text on the button" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:11 +msgid "" +"Description of the Web App that will be launched when the user presses " +"the button. The Web App will be able to send an arbitrary message on " +"behalf of the user using the method answerWebAppQuery." +msgstr "" + +#: of telebot.types.MenuButtonWebApp:16 +msgid ":class:`telebot.types.MenuButtonWebApp`" +msgstr "" + +#: of telebot.types.Message:1 +msgid "This object represents a message." +msgstr "" + +#: of telebot.types.Message:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#message" +msgstr "" + +#: of telebot.types.Message:5 +msgid "Unique message identifier inside this chat" +msgstr "" + +#: of telebot.types.Message:8 +msgid "" +"Optional. Unique identifier of a message thread to which the message " +"belongs; for supergroups only" +msgstr "" + +#: of telebot.types.Message:11 +msgid "" +"Optional. Sender of the message; empty for messages sent to channels. For" +" backward compatibility, the field contains a fake sender user in non-" +"channel chats, if the message was sent on behalf of a chat." +msgstr "" + +#: of telebot.types.Message:15 +msgid "" +"Optional. Sender of the message, sent on behalf of a chat. For example, " +"the channel itself for channel posts, the supergroup itself for messages " +"from anonymous group administrators, the linked channel for messages " +"automatically forwarded to the discussion group. For backward " +"compatibility, the field from contains a fake sender user in non-channel " +"chats, if the message was sent on behalf of a chat." +msgstr "" + +#: of telebot.types.Message:21 +msgid "Date the message was sent in Unix time" +msgstr "" + +#: of telebot.types.Message:24 +msgid "Conversation the message belongs to" +msgstr "" + +#: of telebot.types.Message:27 +msgid "Optional. For forwarded messages, sender of the original message" +msgstr "" + +#: of telebot.types.Message:30 +msgid "" +"Optional. For messages forwarded from channels or from anonymous " +"administrators, information about the original sender chat" +msgstr "" + +#: of telebot.types.Message:34 +msgid "" +"Optional. For messages forwarded from channels, identifier of the " +"original message in the channel" +msgstr "" + +#: of telebot.types.Message:38 +msgid "" +"Optional. For forwarded messages that were originally sent in channels or" +" by an anonymous chat administrator, signature of the message sender if " +"present" +msgstr "" + +#: of telebot.types.Message:42 +msgid "" +"Optional. Sender's name for messages forwarded from users who disallow " +"adding a link to their account in forwarded messages" +msgstr "" + +#: of telebot.types.Message:46 +msgid "" +"Optional. For forwarded messages, date the original message was sent in " +"Unix time" +msgstr "" + +#: of telebot.types.Message:49 +msgid "Optional. True, if the message is sent to a forum topic" +msgstr "" + +#: of telebot.types.Message:52 +msgid "" +"Optional. :obj:`bool`, if the message is a channel post that was " +"automatically forwarded to the connected discussion group" +msgstr "" + +#: of telebot.types.Message:56 +msgid "" +"Optional. For replies, the original message. Note that the Message object" +" in this field will not contain further reply_to_message fields even if " +"it itself is a reply." +msgstr "" + +#: of telebot.types.Message:60 +msgid "Optional. Bot through which the message was sent" +msgstr "" + +#: of telebot.types.Message:63 +msgid "Optional. Date the message was last edited in Unix time" +msgstr "" + +#: of telebot.types.Message:66 +msgid "Optional. :obj:`bool`, if the message can't be forwarded" +msgstr "" + +#: of telebot.types.Message:69 +msgid "" +"Optional. The unique identifier of a media message group this message " +"belongs to" +msgstr "" + +#: of telebot.types.Message:72 +msgid "" +"Optional. Signature of the post author for messages in channels, or the " +"custom title of an anonymous group administrator" +msgstr "" + +#: of telebot.types.Message:76 +msgid "Optional. For text messages, the actual UTF-8 text of the message" +msgstr "" + +#: of telebot.types.Message:79 +msgid "" +"Optional. For text messages, special entities like usernames, URLs, bot " +"commands, etc. that appear in the text" +msgstr "" + +#: of telebot.types.Message:83 +msgid "" +"Optional. Message is an animation, information about the animation. For " +"backward compatibility, when this field is set, the document field will " +"also be set" +msgstr "" + +#: of telebot.types.Message:87 +msgid "Optional. Message is an audio file, information about the file" +msgstr "" + +#: of telebot.types.Message:90 +msgid "Optional. Message is a general file, information about the file" +msgstr "" + +#: of telebot.types.Message:93 +msgid "Optional. Message is a photo, available sizes of the photo" +msgstr "" + +#: of telebot.types.Message:96 +msgid "Optional. Message is a sticker, information about the sticker" +msgstr "" + +#: of telebot.types.Message:99 +msgid "Optional. Message is a video, information about the video" +msgstr "" + +#: of telebot.types.Message:102 +msgid "Optional. Message is a video note, information about the video message" +msgstr "" + +#: of telebot.types.Message:105 +msgid "Optional. Message is a voice message, information about the file" +msgstr "" + +#: of telebot.types.Message:108 +msgid "" +"Optional. Caption for the animation, audio, document, photo, video or " +"voice" +msgstr "" + +#: of telebot.types.Message:111 +msgid "" +"Optional. For messages with a caption, special entities like usernames, " +"URLs, bot commands, etc. that appear in the caption" +msgstr "" + +#: of telebot.types.Message:115 +msgid "Optional. Message is a shared contact, information about the contact" +msgstr "" + +#: of telebot.types.Message:118 +msgid "Optional. Message is a dice with random value" +msgstr "" + +#: of telebot.types.Message:121 +msgid "" +"Optional. Message is a game, information about the game. More about games" +" »" +msgstr "" + +#: of telebot.types.Message:124 +msgid "Optional. Message is a native poll, information about the poll" +msgstr "" + +#: of telebot.types.Message:127 +msgid "" +"Optional. Message is a venue, information about the venue. For backward " +"compatibility, when this field is set, the location field will also be " +"set" +msgstr "" + +#: of telebot.types.Message:131 +msgid "Optional. Message is a shared location, information about the location" +msgstr "" + +#: of telebot.types.Message:134 +msgid "" +"Optional. New members that were added to the group or supergroup and " +"information about them (the bot itself may be one of these members)" +msgstr "" + +#: of telebot.types.Message:138 +msgid "" +"Optional. A member was removed from the group, information about them " +"(this member may be the bot itself)" +msgstr "" + +#: of telebot.types.Message:142 +msgid "Optional. A chat title was changed to this value" +msgstr "" + +#: of telebot.types.Message:145 +msgid "Optional. A chat photo was change to this value" +msgstr "" + +#: of telebot.types.Message:148 +msgid "Optional. Service message: the chat photo was deleted" +msgstr "" + +#: of telebot.types.Message:151 +msgid "Optional. Service message: the group has been created" +msgstr "" + +#: of telebot.types.Message:154 +msgid "" +"Optional. Service message: the supergroup has been created. This field " +"can't be received in a message coming through updates, because bot can't " +"be a member of a supergroup when it is created. It can only be found in " +"reply_to_message if someone replies to a very first message in a directly" +" created supergroup." +msgstr "" + +#: of telebot.types.Message:159 +msgid "" +"Optional. Service message: the channel has been created. This field can't" +" be received in a message coming through updates, because bot can't be a " +"member of a channel when it is created. It can only be found in " +"reply_to_message if someone replies to a very first message in a channel." +msgstr "" + +#: of telebot.types.Message:164 +msgid "Optional. Service message: auto-delete timer settings changed in the chat" +msgstr "" + +#: of telebot.types.Message:168 +msgid "" +"Optional. The group has been migrated to a supergroup with the specified " +"identifier. This number may have more than 32 significant bits and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it has at most 52 significant bits, so a signed 64-bit integer or" +" double-precision float type are safe for storing this identifier." +msgstr "" + +#: of telebot.types.Message:174 +msgid "" +"Optional. The supergroup has been migrated from a group with the " +"specified identifier. This number may have more than 32 significant bits " +"and some programming languages may have difficulty/silent defects in " +"interpreting it. But it has at most 52 significant bits, so a signed " +"64-bit integer or double-precision float type are safe for storing this " +"identifier." +msgstr "" + +#: of telebot.types.Message:180 +msgid "" +"Optional. Specified message was pinned. Note that the Message object in " +"this field will not contain further reply_to_message fields even if it is" +" itself a reply." +msgstr "" + +#: of telebot.types.Message:184 +msgid "" +"Optional. Message is an invoice for a payment, information about the " +"invoice. More about payments »" +msgstr "" + +#: of telebot.types.Message:187 +msgid "" +"Optional. Message is a service message about a successful payment, " +"information about the payment. More about payments »" +msgstr "" + +#: of telebot.types.Message:191 +msgid "" +"Optional. The domain name of the website on which the user has logged in." +" More about Telegram Login »" +msgstr "" + +#: of telebot.types.Message:195 +msgid "Optional. Telegram Passport data" +msgstr "" + +#: of telebot.types.Message:198 +msgid "" +"Optional. Service message. A user in the chat triggered another user's " +"proximity alert while sharing Live Location." +msgstr "" + +#: of telebot.types.Message:202 +msgid "Optional. Service message: forum topic created" +msgstr "" + +#: of telebot.types.Message:205 +msgid "Optional. Service message: forum topic closed" +msgstr "" + +#: of telebot.types.Message:208 +msgid "Optional. Service message: forum topic reopened" +msgstr "" + +#: of telebot.types.Message:211 +msgid "Optional. Service message: video chat scheduled" +msgstr "" + +#: of telebot.types.Message:214 +msgid "Optional. Service message: video chat started" +msgstr "" + +#: of telebot.types.Message:217 +msgid "Optional. Service message: video chat ended" +msgstr "" + +#: of telebot.types.Message:220 +msgid "Optional. Service message: new participants invited to a video chat" +msgstr "" + +#: of telebot.types.Message:223 +msgid "Optional. Service message: data sent by a Web App" +msgstr "" + +#: of telebot.types.Message:226 +msgid "" +"Optional. Inline keyboard attached to the message. login_url buttons are " +"represented as ordinary url buttons." +msgstr "" + +#: of telebot.types.Message:231 +msgid ":class:`telebot.types.Message`" +msgstr "" + +#: of telebot.types.Message.html_caption:1 +msgid "Returns html-rendered caption." +msgstr "" + +#: of telebot.types.Message.html_text:1 +msgid "Returns html-rendered text." +msgstr "" + +#: of telebot.types.Message.parse_chat:1 +msgid "Parses chat." +msgstr "" + +#: of telebot.types.Message.parse_entities:1 +msgid "Parses message entity array." +msgstr "" + +#: of telebot.types.Message.parse_photo:1 +msgid "Parses photo array." +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:1 +msgid "" +"This object represents a service message about a change in auto-delete " +"timer settings." +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#messageautodeletetimerchanged" +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:5 +msgid "New auto-delete time for messages in the chat; in seconds" +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:9 +msgid ":class:`telebot.types.MessageAutoDeleteTimerChanged`" +msgstr "" + +#: of telebot.types.MessageEntity:1 +msgid "" +"This object represents one special entity in a text message. For example," +" hashtags, usernames, URLs, etc." +msgstr "" + +#: of telebot.types.MessageEntity:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#messageentity" +msgstr "" + +#: of telebot.types.MessageEntity:5 +msgid "" +"Type of the entity. Currently, can be “mention” (@username), “hashtag” " +"(#hashtag), “cashtag” ($USD), “bot_command” (/start@jobs_bot), “url” " +"(https://telegram.org), “email” (do-not-reply@telegram.org), " +"“phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic " +"text), “underline” (underlined text), “strikethrough” (strikethrough " +"text), “spoiler” (spoiler message), “code” (monowidth string), “pre” " +"(monowidth block), “text_link” (for clickable text URLs), “text_mention” " +"(for users without usernames), “custom_emoji” (for inline custom emoji " +"stickers)" +msgstr "" + +#: of telebot.types.MessageEntity:13 +msgid "Offset in UTF-16 code units to the start of the entity" +msgstr "" + +#: of telebot.types.MessageEntity:16 +msgid "Length of the entity in UTF-16 code units" +msgstr "" + +#: of telebot.types.MessageEntity:19 +msgid "" +"Optional. For “text_link” only, URL that will be opened after user taps " +"on the text" +msgstr "" + +#: of telebot.types.MessageEntity:22 +msgid "Optional. For “text_mention” only, the mentioned user" +msgstr "" + +#: of telebot.types.MessageEntity:25 +msgid "Optional. For “pre” only, the programming language of the entity text" +msgstr "" + +#: of telebot.types.MessageEntity:28 +msgid "" +"Optional. For “custom_emoji” only, unique identifier of the custom emoji." +" Use get_custom_emoji_stickers to get full information about the sticker." +msgstr "" + +#: of telebot.types.MessageEntity:33 +msgid ":class:`telebot.types.MessageEntity`" +msgstr "" + +#: of telebot.types.MessageEntity.to_list_of_dicts:1 +msgid "Converts a list of MessageEntity objects to a list of dictionaries." +msgstr "" + +#: of telebot.types.MessageID:1 +msgid "This object represents a unique message identifier." +msgstr "" + +#: of telebot.types.MessageID:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#messageid" +msgstr "" + +#: of telebot.types.MessageID:5 +msgid "Unique message identifier" +msgstr "" + +#: of telebot.types.MessageID:9 +msgid ":class:`telebot.types.MessageId`" +msgstr "" + +#: of telebot.types.OrderInfo:1 +msgid "This object represents information about an order." +msgstr "" + +#: of telebot.types.OrderInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#orderinfo" +msgstr "" + +#: of telebot.types.OrderInfo:5 +msgid "Optional. User name" +msgstr "" + +#: of telebot.types.OrderInfo:8 +msgid "Optional. User's phone number" +msgstr "" + +#: of telebot.types.OrderInfo:11 +msgid "Optional. User email" +msgstr "" + +#: of telebot.types.OrderInfo:14 +msgid "Optional. User shipping address" +msgstr "" + +#: of telebot.types.OrderInfo:18 +msgid ":class:`telebot.types.OrderInfo`" +msgstr "" + +#: of telebot.types.PhotoSize:1 +msgid "This object represents one size of a photo or a file / sticker thumbnail." +msgstr "" + +#: of telebot.types.PhotoSize:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#photosize" +msgstr "" + +#: of telebot.types.PhotoSize:12 +msgid "Photo width" +msgstr "" + +#: of telebot.types.PhotoSize:15 +msgid "Photo height" +msgstr "" + +#: of telebot.types.PhotoSize:18 telebot.types.Sticker:46 +#: telebot.types.VideoNote:21 +msgid "Optional. File size in bytes" +msgstr "" + +#: of telebot.types.PhotoSize:22 +msgid ":class:`telebot.types.PhotoSize`" +msgstr "" + +#: of telebot.types.Poll:1 +msgid "This object contains information about a poll." +msgstr "" + +#: of telebot.types.Poll:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#poll" +msgstr "" + +#: of telebot.types.Poll:5 telebot.types.PollAnswer:5 +msgid "Unique poll identifier" +msgstr "" + +#: of telebot.types.Poll:8 +msgid "Poll question, 1-300 characters" +msgstr "" + +#: of telebot.types.Poll:11 +msgid "List of poll options" +msgstr "" + +#: of telebot.types.Poll:14 +msgid "Total number of users that voted in the poll" +msgstr "" + +#: of telebot.types.Poll:17 +msgid "True, if the poll is closed" +msgstr "" + +#: of telebot.types.Poll:20 +msgid "True, if the poll is anonymous" +msgstr "" + +#: of telebot.types.Poll:23 +msgid "Poll type, currently can be “regular” or “quiz”" +msgstr "" + +#: of telebot.types.Poll:26 +msgid "True, if the poll allows multiple answers" +msgstr "" + +#: of telebot.types.Poll:29 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Poll:33 +msgid "" +"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" +msgstr "" + +#: of telebot.types.Poll:37 +msgid "" +"Optional. Special entities like usernames, URLs, bot commands, etc. that " +"appear in the explanation" +msgstr "" + +#: of telebot.types.Poll:41 +msgid "Optional. Amount of time in seconds the poll will be active after creation" +msgstr "" + +#: of telebot.types.Poll:44 +msgid "" +"Optional. Point in time (Unix timestamp) when the poll will be " +"automatically closed" +msgstr "" + +#: of telebot.types.Poll:48 +msgid ":class:`telebot.types.Poll`" +msgstr "" + +#: of telebot.types.Poll.add:1 +msgid "Add an option to the poll." +msgstr "" + +#: of telebot.types.Poll.add:3 +msgid "Option to add" +msgstr "" + +#: of telebot.types.PollAnswer:1 +msgid "This object represents an answer of a user in a non-anonymous poll." +msgstr "" + +#: of telebot.types.PollAnswer:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#pollanswer" +msgstr "" + +#: of telebot.types.PollAnswer:8 +msgid "The user, who changed the answer to the poll" +msgstr "" + +#: of telebot.types.PollAnswer:11 +msgid "" +"0-based identifiers of answer options, chosen by the user. May be empty " +"if the user retracted their vote." +msgstr "" + +#: of telebot.types.PollAnswer:16 +msgid ":class:`telebot.types.PollAnswer`" +msgstr "" + +#: of telebot.types.PollOption:1 +msgid "This object contains information about one answer option in a poll." +msgstr "" + +#: of telebot.types.PollOption:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#polloption" +msgstr "" + +#: of telebot.types.PollOption:5 +msgid "Option text, 1-100 characters" +msgstr "" + +#: of telebot.types.PollOption:8 +msgid "Number of users that voted for this option" +msgstr "" + +#: of telebot.types.PollOption:12 +msgid ":class:`telebot.types.PollOption`" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:1 +msgid "This object contains information about an incoming pre-checkout query." +msgstr "" + +#: of telebot.types.PreCheckoutQuery:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#precheckoutquery" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:5 telebot.types.ShippingQuery:5 +msgid "Unique query identifier" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:8 telebot.types.ShippingQuery:8 +msgid "User who sent the query" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:19 telebot.types.ShippingQuery:11 +#: telebot.types.SuccessfulPayment:13 +msgid "Bot specified invoice payload" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:22 telebot.types.SuccessfulPayment:16 +msgid "Optional. Identifier of the shipping option chosen by the user" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:25 telebot.types.SuccessfulPayment:19 +msgid "Optional. Order information provided by the user" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:29 +msgid ":class:`telebot.types.PreCheckoutQuery`" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:1 +msgid "" +"This object represents the content of a service message, sent whenever a " +"user in the chat triggers a proximity alert set by another user." +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#proximityalerttriggered" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:5 +msgid "User that triggered the alert" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:8 +msgid "User that set the alert" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:11 +msgid "The distance between the users" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:15 +msgid ":class:`telebot.types.ProximityAlertTriggered`" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:1 +msgid "" +"This object represents a custom keyboard with reply options (see " +"Introduction to bots for details and examples)." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:3 +msgid "Example on creating ReplyKeyboardMarkup object" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:16 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#replykeyboardmarkup" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:18 +msgid "" +":obj:`list` of button rows, each represented by an :obj:`list` of " +":class:`telebot.types.KeyboardButton` objects" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:22 +msgid "" +"Optional. Requests clients to resize the keyboard vertically for optimal " +"fit (e.g., make the keyboard smaller if there are just two rows of " +"buttons). Defaults to false, in which case the custom keyboard is always " +"of the same height as the app's standard keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:27 +msgid "" +"Optional. Requests clients to hide the keyboard as soon as it's been " +"used. The keyboard will still be available, but clients will " +"automatically display the usual letter-keyboard in the chat - the user " +"can press a special button in the input field to see the custom keyboard " +"again. Defaults to false." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:32 +msgid "" +"Optional. The placeholder to be shown in the input field when the " +"keyboard is active; 1-64 characters" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:36 +msgid "" +"Optional. Use this parameter if you want to show the keyboard to specific" +" users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message.Example: A user " +"requests to change the bot's language, bot replies to the request with a " +"keyboard to select the new language. Other users in the group don't see " +"the keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:43 +#: telebot.types.ReplyKeyboardMarkup.add:14 +#: telebot.types.ReplyKeyboardMarkup.row:9 +msgid ":class:`telebot.types.ReplyKeyboardMarkup`" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.add:1 +msgid "" +"This function adds strings to the keyboard, while not exceeding " +"row_width. E.g. ReplyKeyboardMarkup#add(\"A\", \"B\", \"C\") yields the " +"json result {keyboard: [[\"A\"], [\"B\"], [\"C\"]]} when row_width is set" +" to 1. When row_width is set to 2, the following is the result of this " +"function: {keyboard: [[\"A\", \"B\"], [\"C\"]]} See " +"https://core.telegram.org/bots/api#replykeyboardmarkup" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.add:7 +msgid "KeyboardButton to append to the keyboard" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.row:1 +msgid "" +"Adds a list of KeyboardButton to the keyboard. This function does not " +"consider row_width. ReplyKeyboardMarkup#row(\"A\")#row(\"B\", " +"\"C\")#to_json() outputs '{keyboard: [[\"A\"], [\"B\", \"C\"]]}' See " +"https://core.telegram.org/bots/api#replykeyboardmarkup" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.row:5 +msgid "strings" +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:1 +msgid "" +"Upon receiving a message with this object, Telegram clients will remove " +"the current custom keyboard and display the default letter-keyboard. By " +"default, custom keyboards are displayed until a new keyboard is sent by a" +" bot. An exception is made for one-time keyboards that are hidden " +"immediately after the user presses a button (see ReplyKeyboardMarkup)." +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#replykeyboardremove" +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:5 +msgid "" +"Requests clients to remove the custom keyboard (user will not be able to " +"summon this keyboard; if you want to hide the keyboard from sight but " +"keep it accessible, use one_time_keyboard in ReplyKeyboardMarkup) Note " +"that this parameter is set to True by default by the library. You cannot " +"modify it." +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:11 +msgid "" +"Optional. Use this parameter if you want to remove the keyboard for " +"specific users only. Targets: 1) users that are @mentioned in the text of" +" the Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message.Example: A user " +"votes in a poll, bot returns confirmation message in reply to the vote " +"and removes the keyboard for that user, while still showing the keyboard " +"with poll options to users who haven't voted yet." +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:19 +msgid ":class:`telebot.types.ReplyKeyboardRemove`" +msgstr "" + +#: of telebot.types.SentWebAppMessage:1 telebot.types.WebAppData:1 +#: telebot.types.WebAppInfo:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.SentWebAppMessage:1 +msgid "Describes an inline message sent by a Web App on behalf of a user." +msgstr "" + +#: of telebot.types.SentWebAppMessage:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#sentwebappmessage" +msgstr "" + +#: of telebot.types.SentWebAppMessage:5 +msgid "" +"Optional. Identifier of the sent inline message. Available only if there " +"is an inline keyboard attached to the message." +msgstr "" + +#: of telebot.types.SentWebAppMessage:10 +msgid ":class:`telebot.types.SentWebAppMessage`" +msgstr "" + +#: of telebot.types.ShippingAddress:1 +msgid "This object represents a shipping address." +msgstr "" + +#: of telebot.types.ShippingAddress:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#shippingaddress" +msgstr "" + +#: of telebot.types.ShippingAddress:5 +msgid "Two-letter ISO 3166-1 alpha-2 country code" +msgstr "" + +#: of telebot.types.ShippingAddress:8 +msgid "State, if applicable" +msgstr "" + +#: of telebot.types.ShippingAddress:11 +msgid "City" +msgstr "" + +#: of telebot.types.ShippingAddress:14 +msgid "First line for the address" +msgstr "" + +#: of telebot.types.ShippingAddress:17 +msgid "Second line for the address" +msgstr "" + +#: of telebot.types.ShippingAddress:20 +msgid "Address post code" +msgstr "" + +#: of telebot.types.ShippingAddress:24 +msgid ":class:`telebot.types.ShippingAddress`" +msgstr "" + +#: of telebot.types.ShippingOption:1 +msgid "This object represents one shipping option." +msgstr "" + +#: of telebot.types.ShippingOption:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#shippingoption" +msgstr "" + +#: of telebot.types.ShippingOption:5 +msgid "Shipping option identifier" +msgstr "" + +#: of telebot.types.ShippingOption:8 +msgid "Option title" +msgstr "" + +#: of telebot.types.ShippingOption:11 +msgid "List of price portions" +msgstr "" + +#: of telebot.types.ShippingOption:15 +msgid ":class:`telebot.types.ShippingOption`" +msgstr "" + +#: of telebot.types.ShippingOption.add_price:1 +msgid "Add LabeledPrice to ShippingOption" +msgstr "" + +#: of telebot.types.ShippingOption.add_price:3 +msgid "LabeledPrices" +msgstr "" + +#: of telebot.types.ShippingOption.add_price:6 +msgid "None" +msgstr "" + +#: of telebot.types.ShippingQuery:1 +msgid "This object contains information about an incoming shipping query." +msgstr "" + +#: of telebot.types.ShippingQuery:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#shippingquery" +msgstr "" + +#: of telebot.types.ShippingQuery:14 +msgid "User specified shipping address" +msgstr "" + +#: of telebot.types.ShippingQuery:18 +msgid ":class:`telebot.types.ShippingQuery`" +msgstr "" + +#: of telebot.types.Sticker:1 +msgid "This object represents a sticker." +msgstr "" + +#: of telebot.types.Sticker:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#sticker" +msgstr "" + +#: of telebot.types.Sticker:12 +msgid "" +"Type of the sticker, currently one of “regular”, “mask”, “custom_emoji”. " +"The type of the sticker is independent from its format, which is " +"determined by the fields is_animated and is_video." +msgstr "" + +#: of telebot.types.Sticker:16 +msgid "Sticker width" +msgstr "" + +#: of telebot.types.Sticker:19 +msgid "Sticker height" +msgstr "" + +#: of telebot.types.Sticker:22 +msgid "True, if the sticker is animated" +msgstr "" + +#: of telebot.types.Sticker:25 +msgid "True, if the sticker is a video sticker" +msgstr "" + +#: of telebot.types.Sticker:28 +msgid "Optional. Sticker thumbnail in the .WEBP or .JPG format" +msgstr "" + +#: of telebot.types.Sticker:31 +msgid "Optional. Emoji associated with the sticker" +msgstr "" + +#: of telebot.types.Sticker:34 +msgid "Optional. Name of the sticker set to which the sticker belongs" +msgstr "" + +#: of telebot.types.Sticker:37 +msgid "Optional. Premium animation for the sticker, if the sticker is premium" +msgstr "" + +#: of telebot.types.Sticker:40 +msgid "Optional. For mask stickers, the position where the mask should be placed" +msgstr "" + +#: of telebot.types.Sticker:43 +msgid "Optional. For custom emoji stickers, unique identifier of the custom emoji" +msgstr "" + +#: of telebot.types.Sticker:50 +msgid ":class:`telebot.types.Sticker`" +msgstr "" + +#: of telebot.types.StickerSet:1 +msgid "This object represents a sticker set." +msgstr "" + +#: of telebot.types.StickerSet:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#stickerset" +msgstr "" + +#: of telebot.types.StickerSet:5 +msgid "Sticker set name" +msgstr "" + +#: of telebot.types.StickerSet:8 +msgid "Sticker set title" +msgstr "" + +#: of telebot.types.StickerSet:11 +msgid "" +"Type of stickers in the set, currently one of “regular”, “mask”, " +"“custom_emoji”" +msgstr "" + +#: of telebot.types.StickerSet:14 +msgid "True, if the sticker set contains animated stickers" +msgstr "" + +#: of telebot.types.StickerSet:17 +msgid "True, if the sticker set contains video stickers" +msgstr "" + +#: of telebot.types.StickerSet:20 +msgid "" +"True, if the sticker set contains masks. Deprecated since Bot API 6.2, " +"use sticker_type instead." +msgstr "" + +#: of telebot.types.StickerSet:24 +msgid "List of all set stickers" +msgstr "" + +#: of telebot.types.StickerSet:27 +msgid "Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format" +msgstr "" + +#: of telebot.types.StickerSet:31 +msgid ":class:`telebot.types.StickerSet`" +msgstr "" + +#: of telebot.types.StickerSet.contains_masks:1 +msgid "Deprecated since Bot API 6.2, use sticker_type instead." +msgstr "" + +#: of telebot.types.SuccessfulPayment:1 +msgid "This object contains basic information about a successful payment." +msgstr "" + +#: of telebot.types.SuccessfulPayment:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#successfulpayment" +msgstr "" + +#: of telebot.types.SuccessfulPayment:22 +msgid "Telegram payment identifier" +msgstr "" + +#: of telebot.types.SuccessfulPayment:25 +msgid "Provider payment identifier" +msgstr "" + +#: of telebot.types.SuccessfulPayment:29 +msgid ":class:`telebot.types.SuccessfulPayment`" +msgstr "" + +#: of telebot.types.Update:1 +msgid "" +"This object represents an incoming update.At most one of the optional " +"parameters can be present in any given update." +msgstr "" + +#: of telebot.types.Update:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#update" +msgstr "" + +#: of telebot.types.Update:5 +msgid "" +"The update's unique identifier. Update identifiers start from a certain " +"positive number and increase sequentially. This ID becomes especially " +"handy if you're using webhooks, since it allows you to ignore repeated " +"updates or to restore the correct update sequence, should they get out of" +" order. If there are no new updates for at least a week, then identifier " +"of the next update will be chosen randomly instead of sequentially." +msgstr "" + +#: of telebot.types.Update:11 +msgid "Optional. New incoming message of any kind - text, photo, sticker, etc." +msgstr "" + +#: of telebot.types.Update:14 +msgid "Optional. New version of a message that is known to the bot and was edited" +msgstr "" + +#: of telebot.types.Update:17 +msgid "" +"Optional. New incoming channel post of any kind - text, photo, sticker, " +"etc." +msgstr "" + +#: of telebot.types.Update:20 +msgid "" +"Optional. New version of a channel post that is known to the bot and was " +"edited" +msgstr "" + +#: of telebot.types.Update:23 +msgid "Optional. New incoming inline query" +msgstr "" + +#: of telebot.types.Update:26 +msgid "" +"Optional. The result of an inline query that was chosen by a user and " +"sent to their chat partner. Please see our documentation on the feedback " +"collecting for details on how to enable these updates for your bot." +msgstr "" + +#: of telebot.types.Update:31 +msgid "Optional. New incoming callback query" +msgstr "" + +#: of telebot.types.Update:34 +msgid "" +"Optional. New incoming shipping query. Only for invoices with flexible " +"price" +msgstr "" + +#: of telebot.types.Update:37 +msgid "" +"Optional. New incoming pre-checkout query. Contains full information " +"about checkout" +msgstr "" + +#: of telebot.types.Update:41 +msgid "" +"Optional. New poll state. Bots receive only updates about stopped polls " +"and polls, which are sent by the bot" +msgstr "" + +#: of telebot.types.Update:45 +msgid "" +"Optional. A user changed their answer in a non-anonymous poll. Bots " +"receive new votes only in polls that were sent by the bot itself." +msgstr "" + +#: of telebot.types.Update:49 +msgid "" +"Optional. The bot's chat member status was updated in a chat. For private" +" chats, this update is received only when the bot is blocked or unblocked" +" by the user." +msgstr "" + +#: of telebot.types.Update:53 +msgid "" +"Optional. A chat member's status was updated in a chat. The bot must be " +"an administrator in the chat and must explicitly specify “chat_member” in" +" the list of allowed_updates to receive these updates." +msgstr "" + +#: of telebot.types.Update:57 +msgid "" +"Optional. A request to join the chat has been sent. The bot must have the" +" can_invite_users administrator right in the chat to receive these " +"updates." +msgstr "" + +#: of telebot.types.Update:62 +msgid ":class:`telebot.types.Update`" +msgstr "" + +#: of telebot.types.User:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.User:1 +msgid "This object represents a Telegram user or bot." +msgstr "" + +#: of telebot.types.User:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#user" +msgstr "" + +#: of telebot.types.User:5 +msgid "" +"Unique identifier for this user or bot. This number may have more than 32" +" significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier." +msgstr "" + +#: of telebot.types.User:10 +msgid "True, if this user is a bot" +msgstr "" + +#: of telebot.types.User:13 +msgid "User's or bot's first name" +msgstr "" + +#: of telebot.types.User:16 +msgid "Optional. User's or bot's last name" +msgstr "" + +#: of telebot.types.User:19 +msgid "Optional. User's or bot's username" +msgstr "" + +#: of telebot.types.User:22 +msgid "Optional. IETF language tag of the user's language" +msgstr "" + +#: of telebot.types.User:25 +msgid "Optional. :obj:`bool`, if this user is a Telegram Premium user" +msgstr "" + +#: of telebot.types.User:28 +msgid "Optional. :obj:`bool`, if this user added the bot to the attachment menu" +msgstr "" + +#: of telebot.types.User:31 +msgid "" +"Optional. True, if the bot can be invited to groups. Returned only in " +"getMe." +msgstr "" + +#: of telebot.types.User:34 +msgid "" +"Optional. True, if privacy mode is disabled for the bot. Returned only in" +" getMe." +msgstr "" + +#: of telebot.types.User:38 +msgid "" +"Optional. True, if the bot supports inline queries. Returned only in " +"getMe." +msgstr "" + +#: of telebot.types.User:42 +msgid ":class:`telebot.types.User`" +msgstr "" + +#: of telebot.types.User.full_name:1 +msgid "User's full name" +msgstr "" + +#: of telebot.types.User.full_name +msgid "type" +msgstr "" + +#: of telebot.types.User.full_name:3 +msgid "return" +msgstr "" + +#: of telebot.types.UserProfilePhotos:1 +msgid "This object represent a user's profile pictures." +msgstr "" + +#: of telebot.types.UserProfilePhotos:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#userprofilephotos" +msgstr "" + +#: of telebot.types.UserProfilePhotos:5 +msgid "Total number of profile pictures the target user has" +msgstr "" + +#: of telebot.types.UserProfilePhotos:8 +msgid "Requested profile pictures (in up to 4 sizes each)" +msgstr "" + +#: of telebot.types.UserProfilePhotos:12 +msgid ":class:`telebot.types.UserProfilePhotos`" +msgstr "" + +#: of telebot.types.Venue:1 +msgid "This object represents a venue." +msgstr "" + +#: of telebot.types.Venue:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#venue" +msgstr "" + +#: of telebot.types.Venue:5 +msgid "Venue location. Can't be a live location" +msgstr "" + +#: of telebot.types.Venue:14 +msgid "Optional. Foursquare identifier of the venue" +msgstr "" + +#: of telebot.types.Venue:17 +msgid "" +"Optional. Foursquare type of the venue. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.types.Venue:28 +msgid ":class:`telebot.types.Venue`" +msgstr "" + +#: of telebot.types.Video:1 +msgid "This object represents a video file." +msgstr "" + +#: of telebot.types.Video:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#video" +msgstr "" + +#: of telebot.types.Video:21 telebot.types.VideoNote:18 +msgid "Optional. Video thumbnail" +msgstr "" + +#: of telebot.types.Video:36 +msgid ":class:`telebot.types.Video`" +msgstr "" + +#: of telebot.types.VideoChatEnded:1 +msgid "" +"This object represents a service message about a video chat ended in the " +"chat." +msgstr "" + +#: of telebot.types.VideoChatEnded:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#videochatended" +msgstr "" + +#: of telebot.types.VideoChatEnded:5 +msgid "Video chat duration in seconds" +msgstr "" + +#: of telebot.types.VideoChatEnded:9 +msgid ":class:`telebot.types.VideoChatEnded`" +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:1 +msgid "" +"This object represents a service message about new members invited to a " +"video chat." +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#videochatparticipantsinvited" +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:5 +msgid "New members that were invited to the video chat" +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:9 +msgid ":class:`telebot.types.VideoChatParticipantsInvited`" +msgstr "" + +#: of telebot.types.VideoChatScheduled:1 +msgid "" +"This object represents a service message about a video chat scheduled in " +"the chat." +msgstr "" + +#: of telebot.types.VideoChatScheduled:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#videochatscheduled" +msgstr "" + +#: of telebot.types.VideoChatScheduled:5 +msgid "" +"Point in time (Unix timestamp) when the video chat is supposed to be " +"started by a chat administrator" +msgstr "" + +#: of telebot.types.VideoChatScheduled:10 +msgid ":class:`telebot.types.VideoChatScheduled`" +msgstr "" + +#: of telebot.types.VideoChatStarted:1 +msgid "" +"This object represents a service message about a video chat started in " +"the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.VideoNote:1 +msgid "" +"This object represents a video message (available in Telegram apps as of " +"v.4.0)." +msgstr "" + +#: of telebot.types.VideoNote:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#videonote" +msgstr "" + +#: of telebot.types.VideoNote:12 +msgid "" +"Video width and height (diameter of the video message) as defined by " +"sender" +msgstr "" + +#: of telebot.types.VideoNote:25 +msgid ":class:`telebot.types.VideoNote`" +msgstr "" + +#: of telebot.types.Voice:1 +msgid "This object represents a voice note." +msgstr "" + +#: of telebot.types.Voice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#voice" +msgstr "" + +#: of telebot.types.Voice:24 +msgid ":class:`telebot.types.Voice`" +msgstr "" + +#: of telebot.types.VoiceChatEnded:1 +msgid "Bases: :py:class:`telebot.types.VideoChatEnded`" +msgstr "" + +#: of telebot.types.VoiceChatEnded:1 +msgid "Deprecated, use :class:`VideoChatEnded` instead." +msgstr "" + +#: of telebot.types.VoiceChatParticipantsInvited:1 +msgid "Bases: :py:class:`telebot.types.VideoChatParticipantsInvited`" +msgstr "" + +#: of telebot.types.VoiceChatParticipantsInvited:1 +msgid "Deprecated, use :class:`VideoChatParticipantsInvited` instead." +msgstr "" + +#: of telebot.types.VoiceChatScheduled:1 +msgid "Bases: :py:class:`telebot.types.VideoChatScheduled`" +msgstr "" + +#: of telebot.types.VoiceChatScheduled:1 +msgid "Deprecated, use :class:`VideoChatScheduled` instead." +msgstr "" + +#: of telebot.types.VoiceChatStarted:1 +msgid "Bases: :py:class:`telebot.types.VideoChatStarted`" +msgstr "" + +#: of telebot.types.VoiceChatStarted:1 +msgid "Deprecated, use :class:`VideoChatStarted` instead." +msgstr "" + +#: of telebot.types.WebAppData:1 +msgid "Describes data sent from a Web App to the bot." +msgstr "" + +#: of telebot.types.WebAppData:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#webappdata" +msgstr "" + +#: of telebot.types.WebAppData:5 +msgid "" +"The data. Be aware that a bad client can send arbitrary data in this " +"field." +msgstr "" + +#: of telebot.types.WebAppData:8 +msgid "" +"Text of the web_app keyboard button from which the Web App was opened. Be" +" aware that a bad client can send arbitrary data in this field." +msgstr "" + +#: of telebot.types.WebAppData:13 +msgid ":class:`telebot.types.WebAppData`" +msgstr "" + +#: of telebot.types.WebAppInfo:1 +msgid "Describes a Web App." +msgstr "" + +#: of telebot.types.WebAppInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#webappinfo" +msgstr "" + +#: of telebot.types.WebAppInfo:5 +msgid "" +"An HTTPS URL of a Web App to be opened with additional data as specified " +"in Initializing Web Apps" +msgstr "" + +#: of telebot.types.WebAppInfo:9 +msgid ":class:`telebot.types.WebAppInfo`" +msgstr "" + +#: of telebot.types.WebhookInfo:1 +msgid "Describes the current status of a webhook." +msgstr "" + +#: of telebot.types.WebhookInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#webhookinfo" +msgstr "" + +#: of telebot.types.WebhookInfo:5 +msgid "Webhook URL, may be empty if webhook is not set up" +msgstr "" + +#: of telebot.types.WebhookInfo:8 +msgid "True, if a custom certificate was provided for webhook certificate checks" +msgstr "" + +#: of telebot.types.WebhookInfo:11 +msgid "Number of updates awaiting delivery" +msgstr "" + +#: of telebot.types.WebhookInfo:14 +msgid "Optional. Currently used webhook IP address" +msgstr "" + +#: of telebot.types.WebhookInfo:17 +msgid "" +"Optional. Unix time for the most recent error that happened when trying " +"to deliver an update via webhook" +msgstr "" + +#: of telebot.types.WebhookInfo:21 +msgid "" +"Optional. Error message in human-readable format for the most recent " +"error that happened when trying to deliver an update via webhook" +msgstr "" + +#: of telebot.types.WebhookInfo:25 +msgid "" +"Optional. Unix time of the most recent error that happened when trying to" +" synchronize available updates with Telegram datacenters" +msgstr "" + +#: of telebot.types.WebhookInfo:29 +msgid "" +"Optional. The maximum allowed number of simultaneous HTTPS connections to" +" the webhook for update delivery" +msgstr "" + +#: of telebot.types.WebhookInfo:33 +msgid "" +"Optional. A list of update types the bot is subscribed to. Defaults to " +"all update types except chat_member" +msgstr "" + +#: of telebot.types.WebhookInfo:38 +msgid ":class:`telebot.types.WebhookInfo`" +msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/util.po b/docs/source/locales/en/LC_MESSAGES/util.po new file mode 100644 index 000000000..afba83905 --- /dev/null +++ b/docs/source/locales/en/LC_MESSAGES/util.po @@ -0,0 +1,345 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../util.rst:3 +msgid "Utils" +msgstr "" + +#: ../../util.rst:5 +msgid "Utils in pyTelegramBotAPI" +msgstr "" + +#: ../../util.rst:5 +msgid "ptba, pytba, pyTelegramBotAPI, utils, guide" +msgstr "" + +#: ../../util.rst:11 +msgid "util file" +msgstr "" + +#: of telebot.util.antiflood:1 +msgid "" +"Use this function inside loops in order to avoid getting TooManyRequests " +"error. Example:" +msgstr "" + +#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_bytes telebot.util.is_command +#: telebot.util.is_dict telebot.util.is_pil_image +#: telebot.util.parse_web_app_data telebot.util.quick_markup +#: telebot.util.smart_split telebot.util.split_string telebot.util.user_link +#: telebot.util.validate_web_app_data telebot.util.webhook_google_functions +msgid "Parameters" +msgstr "" + +#: of telebot.util.antiflood:10 +msgid "The function to call" +msgstr "" + +#: of telebot.util.antiflood:13 +msgid "The arguments to pass to the function" +msgstr "" + +#: of telebot.util.antiflood:16 +msgid "The keyword arguments to pass to the function" +msgstr "" + +#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.generate_random_token +#: telebot.util.is_bytes telebot.util.is_command telebot.util.is_dict +#: telebot.util.is_pil_image telebot.util.parse_web_app_data +#: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string +#: telebot.util.user_link telebot.util.validate_web_app_data +#: telebot.util.webhook_google_functions +msgid "Returns" +msgstr "" + +#: of telebot.util.antiflood:19 +msgid "None" +msgstr "" + +#: of telebot.util.chunks:1 +msgid "Yield successive n-sized chunks from lst." +msgstr "" + +#: ../../docstring of telebot.util.content_type_media:1 +msgid "Contains all media content types." +msgstr "" + +#: ../../docstring of telebot.util.content_type_service:1 +msgid "Contains all service content types such as `User joined the group`." +msgstr "" + +#: of telebot.util.escape:1 +msgid "" +"Replaces the following chars in `text` ('&' with '&', '<' with '<'" +" and '>' with '>')." +msgstr "" + +#: of telebot.util.escape:3 +msgid "the text to escape" +msgstr "" + +#: of telebot.util.escape:4 +msgid "the escaped text" +msgstr "" + +#: of telebot.util.extract_arguments:1 +msgid "Returns the argument after the command." +msgstr "" + +#: of telebot.util.extract_arguments:3 telebot.util.extract_command:4 +msgid "Examples:" +msgstr "" + +#: of telebot.util.extract_arguments:10 +msgid "String to extract the arguments from a command" +msgstr "" + +#: of telebot.util.extract_arguments:13 +msgid "the arguments if `text` is a command (according to is_command), else None." +msgstr "" + +#: of telebot.util.extract_arguments telebot.util.extract_command +#: telebot.util.generate_random_token telebot.util.is_bytes +#: telebot.util.is_command telebot.util.is_dict telebot.util.is_pil_image +#: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string +#: telebot.util.user_link +msgid "Return type" +msgstr "" + +#: of telebot.util.extract_arguments:14 telebot.util.extract_command:16 +msgid ":obj:`str` or :obj:`None`" +msgstr "" + +#: of telebot.util.extract_command:1 +msgid "" +"Extracts the command from `text` (minus the '/') if `text` is a command " +"(see is_command). If `text` is not a command, this function returns None." +msgstr "" + +#: of telebot.util.extract_command:12 +msgid "String to extract the command from" +msgstr "" + +#: of telebot.util.extract_command:15 +msgid "the command if `text` is a command (according to is_command), else None." +msgstr "" + +#: of telebot.util.generate_random_token:1 +msgid "" +"Generates a random token consisting of letters and digits, 16 characters " +"long." +msgstr "" + +#: of telebot.util.generate_random_token:3 +msgid "a random token" +msgstr "" + +#: of telebot.util.generate_random_token:4 telebot.util.user_link:22 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.util.is_bytes:1 +msgid "Returns True if the given object is a bytes object." +msgstr "" + +#: of telebot.util.is_bytes:3 telebot.util.is_dict:3 +#: telebot.util.is_pil_image:3 +msgid "object to be checked" +msgstr "" + +#: of telebot.util.is_bytes:6 +msgid "True if the given object is a bytes object." +msgstr "" + +#: of telebot.util.is_bytes:7 telebot.util.is_command:7 telebot.util.is_dict:7 +#: telebot.util.is_pil_image:7 +msgid ":obj:`bool`" +msgstr "" + +#: of telebot.util.is_command:1 +msgid "" +"Checks if `text` is a command. Telegram chat commands start with the '/' " +"character." +msgstr "" + +#: of telebot.util.is_command:3 +msgid "Text to check." +msgstr "" + +#: of telebot.util.is_command:6 +msgid "True if `text` is a command, else False." +msgstr "" + +#: of telebot.util.is_dict:1 +msgid "Returns True if the given object is a dictionary." +msgstr "" + +#: of telebot.util.is_dict:6 +msgid "True if the given object is a dictionary." +msgstr "" + +#: of telebot.util.is_pil_image:1 +msgid "Returns True if the given object is a PIL.Image.Image object." +msgstr "" + +#: of telebot.util.is_pil_image:6 +msgid "True if the given object is a PIL.Image.Image object." +msgstr "" + +#: of telebot.util.is_string:1 +msgid "Returns True if the given object is a string." +msgstr "" + +#: of telebot.util.parse_web_app_data:1 +msgid "Parses web app data." +msgstr "" + +#: of telebot.util.parse_web_app_data:3 telebot.util.validate_web_app_data:3 +msgid "The bot token" +msgstr "" + +#: of telebot.util.parse_web_app_data:6 telebot.util.validate_web_app_data:6 +msgid "The raw init data" +msgstr "" + +#: of telebot.util.parse_web_app_data:9 telebot.util.validate_web_app_data:9 +msgid "The parsed init data" +msgstr "" + +#: of telebot.util.quick_markup:1 +msgid "" +"Returns a reply markup from a dict in this format: {'text': kwargs} This " +"is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2" +" = InlineKeyboardButton(...)'" +msgstr "" + +#: of telebot.util.quick_markup:4 telebot.util.user_link:5 +msgid "Example:" +msgstr "" + +#: of telebot.util.quick_markup:6 +msgid "Using quick_markup:" +msgstr "" + +#: of telebot.util.quick_markup:29 +msgid "" +"a dict containing all buttons to create in this format: {text: kwargs} " +"{str:}" +msgstr "" + +#: of telebot.util.quick_markup:32 +msgid "int row width" +msgstr "" + +#: of telebot.util.quick_markup:35 +msgid "InlineKeyboardMarkup" +msgstr "" + +#: of telebot.util.quick_markup:36 +msgid ":obj:`types.InlineKeyboardMarkup`" +msgstr "" + +#: of telebot.util.smart_split:1 +msgid "" +"Splits one string into multiple strings, with a maximum amount of " +"`chars_per_string` characters per string. This is very useful for " +"splitting one giant message into multiples. If `chars_per_string` > 4096:" +" `chars_per_string` = 4096. Splits by '\\n', '. ' or ' ' in exactly this " +"priority." +msgstr "" + +#: of telebot.util.smart_split:6 telebot.util.split_string:4 +msgid "The text to split" +msgstr "" + +#: of telebot.util.smart_split:9 +msgid "The number of maximum characters per part the text is split to." +msgstr "" + +#: of telebot.util.smart_split:12 telebot.util.split_string:10 +msgid "The splitted text as a list of strings." +msgstr "" + +#: of telebot.util.smart_split:13 telebot.util.split_string:11 +msgid ":obj:`list` of :obj:`str`" +msgstr "" + +#: of telebot.util.split_string:1 +msgid "" +"Splits one string into multiple strings, with a maximum amount of " +"`chars_per_string` characters per string. This is very useful for " +"splitting one giant message into multiples." +msgstr "" + +#: of telebot.util.split_string:7 +msgid "The number of characters per line the text is split into." +msgstr "" + +#: ../../docstring of telebot.util.update_types:1 +msgid "All update types, should be used for allowed_updates parameter in polling." +msgstr "" + +#: of telebot.util.user_link:1 +msgid "" +"Returns an HTML user link. This is useful for reports. Attention: Don't " +"forget to set parse_mode to 'HTML'!" +msgstr "" + +#: of telebot.util.user_link:11 +msgid "" +"You can use formatting.* for all other formatting options(bold, italic, " +"links, and etc.) This method is kept for backward compatibility, and it " +"is recommended to use formatting.* for more options." +msgstr "" + +#: of telebot.util.user_link:15 +msgid "the user (not the user_id)" +msgstr "" + +#: of telebot.util.user_link:18 +msgid "include the user_id" +msgstr "" + +#: of telebot.util.user_link:21 +msgid "HTML user link" +msgstr "" + +#: of telebot.util.validate_web_app_data:1 +msgid "Validates web app data." +msgstr "" + +#: of telebot.util.webhook_google_functions:1 +msgid "A webhook endpoint for Google Cloud Functions FaaS." +msgstr "" + +#: of telebot.util.webhook_google_functions:3 +msgid "The bot instance" +msgstr "" + +#: of telebot.util.webhook_google_functions:6 +msgid "The request object" +msgstr "" + +#: of telebot.util.webhook_google_functions:9 +msgid "The response object" +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po new file mode 100644 index 000000000..f731d317e --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -0,0 +1,4805 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../async_version/index.rst:3 +msgid "AsyncTeleBot" +msgstr "" + +#: ../../async_version/index.rst:6 +msgid "Asynchronous pyTelegramBotAPI" +msgstr "" + +#: ../../async_version/index.rst:6 +msgid "ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation" +msgstr "" + +#: ../../async_version/index.rst:12 +msgid "AsyncTeleBot methods" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:1 +#: telebot.async_telebot.ExceptionHandler:1 telebot.async_telebot.Handler:1 +#: telebot.asyncio_filters.TextFilter:1 +#: telebot.asyncio_handler_backends.BaseMiddleware:1 +#: telebot.asyncio_handler_backends.CancelUpdate:1 +#: telebot.asyncio_handler_backends.ContinueHandling:1 +#: telebot.asyncio_handler_backends.SkipHandler:1 +#: telebot.asyncio_handler_backends.State:1 +#: telebot.asyncio_handler_backends.StatesGroup:1 +#: telebot.ext.aio.webhooks.AsyncWebhookListener:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:1 +msgid "This is the main asynchronous class for Bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:3 +msgid "It allows you to add handlers for different kind of updates." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:5 +msgid "Usage:" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:7 +msgid "Using asynchronous implementation of TeleBot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:16 +msgid "" +"See more examples in examples/ directory: " +"https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:21 +msgid "Install coloredlogs module to specify colorful_logs=True" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot +#: telebot.async_telebot.AsyncTeleBot.add_custom_filter +#: telebot.async_telebot.AsyncTeleBot.add_data +#: telebot.async_telebot.AsyncTeleBot.add_sticker_to_set +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic +#: telebot.async_telebot.AsyncTeleBot.create_invoice_link +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic +#: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands +#: telebot.async_telebot.AsyncTeleBot.delete_state +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_webhook +#: telebot.async_telebot.AsyncTeleBot.download_file +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_message_caption +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location +#: telebot.async_telebot.AsyncTeleBot.edit_message_media +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup +#: telebot.async_telebot.AsyncTeleBot.edit_message_text +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.enable_saving_states +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.get_chat +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators +#: telebot.async_telebot.AsyncTeleBot.get_chat_member +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count +#: telebot.async_telebot.AsyncTeleBot.get_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers +#: telebot.async_telebot.AsyncTeleBot.get_file +#: telebot.async_telebot.AsyncTeleBot.get_file_url +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores +#: telebot.async_telebot.AsyncTeleBot.get_my_commands +#: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_state +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set +#: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.infinity_polling +#: telebot.async_telebot.AsyncTeleBot.inline_handler +#: telebot.async_telebot.AsyncTeleBot.leave_chat +#: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.poll_handler +#: telebot.async_telebot.AsyncTeleBot.polling +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.process_new_updates +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reply_to +#: telebot.async_telebot.AsyncTeleBot.reset_data +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member +#: telebot.async_telebot.AsyncTeleBot.retrieve_data +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.run_webhooks +#: telebot.async_telebot.AsyncTeleBot.send_animation +#: telebot.async_telebot.AsyncTeleBot.send_audio +#: telebot.async_telebot.AsyncTeleBot.send_chat_action +#: telebot.async_telebot.AsyncTeleBot.send_contact +#: telebot.async_telebot.AsyncTeleBot.send_dice +#: telebot.async_telebot.AsyncTeleBot.send_document +#: telebot.async_telebot.AsyncTeleBot.send_game +#: telebot.async_telebot.AsyncTeleBot.send_invoice +#: telebot.async_telebot.AsyncTeleBot.send_location +#: telebot.async_telebot.AsyncTeleBot.send_media_group +#: telebot.async_telebot.AsyncTeleBot.send_message +#: telebot.async_telebot.AsyncTeleBot.send_photo +#: telebot.async_telebot.AsyncTeleBot.send_poll +#: telebot.async_telebot.AsyncTeleBot.send_sticker +#: telebot.async_telebot.AsyncTeleBot.send_venue +#: telebot.async_telebot.AsyncTeleBot.send_video +#: telebot.async_telebot.AsyncTeleBot.send_video_note +#: telebot.async_telebot.AsyncTeleBot.send_voice +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title +#: telebot.async_telebot.AsyncTeleBot.set_chat_description +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_my_commands +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_update_listener +#: telebot.async_telebot.AsyncTeleBot.set_webhook +#: telebot.async_telebot.AsyncTeleBot.setup_middleware +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location +#: telebot.async_telebot.AsyncTeleBot.stop_poll +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file +#: telebot.asyncio_filters.TextFilter +msgid "Parameters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:24 +msgid "Token of a bot, obtained from @BotFather" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:27 +msgid "Default parse mode, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:30 +msgid "Offset used in get_updates, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:33 +msgid "" +"Exception handler, which will handle the exception occured, defaults to " +"None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:36 +msgid "Storage for states, defaults to StateMemoryStorage()" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:39 +msgid "Default value for disable_web_page_preview, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:42 +msgid "Default value for disable_notification, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:45 +msgid "Default value for protect_content, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:48 +msgid "Default value for allow_sending_without_reply, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot:51 +msgid "Outputs colorful logs" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:1 +msgid "Create custom filter." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:3 +msgid "Example on checking the text of a message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:12 +msgid "Class with check(message) method." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter +#: telebot.async_telebot.AsyncTeleBot.add_data +#: telebot.async_telebot.AsyncTeleBot.add_sticker_to_set +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.close +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic +#: telebot.async_telebot.AsyncTeleBot.create_invoice_link +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic +#: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands +#: telebot.async_telebot.AsyncTeleBot.delete_state +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_webhook +#: telebot.async_telebot.AsyncTeleBot.download_file +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_message_caption +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location +#: telebot.async_telebot.AsyncTeleBot.edit_message_media +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup +#: telebot.async_telebot.AsyncTeleBot.edit_message_text +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.get_chat +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators +#: telebot.async_telebot.AsyncTeleBot.get_chat_member +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count +#: telebot.async_telebot.AsyncTeleBot.get_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers +#: telebot.async_telebot.AsyncTeleBot.get_file +#: telebot.async_telebot.AsyncTeleBot.get_file_url +#: telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores +#: telebot.async_telebot.AsyncTeleBot.get_my_commands +#: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_state +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set +#: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.infinity_polling +#: telebot.async_telebot.AsyncTeleBot.inline_handler +#: telebot.async_telebot.AsyncTeleBot.leave_chat +#: telebot.async_telebot.AsyncTeleBot.log_out +#: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.poll_handler +#: telebot.async_telebot.AsyncTeleBot.polling +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.process_new_updates +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reply_to +#: telebot.async_telebot.AsyncTeleBot.reset_data +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member +#: telebot.async_telebot.AsyncTeleBot.retrieve_data +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.run_webhooks +#: telebot.async_telebot.AsyncTeleBot.send_animation +#: telebot.async_telebot.AsyncTeleBot.send_audio +#: telebot.async_telebot.AsyncTeleBot.send_chat_action +#: telebot.async_telebot.AsyncTeleBot.send_contact +#: telebot.async_telebot.AsyncTeleBot.send_dice +#: telebot.async_telebot.AsyncTeleBot.send_document +#: telebot.async_telebot.AsyncTeleBot.send_game +#: telebot.async_telebot.AsyncTeleBot.send_invoice +#: telebot.async_telebot.AsyncTeleBot.send_location +#: telebot.async_telebot.AsyncTeleBot.send_media_group +#: telebot.async_telebot.AsyncTeleBot.send_message +#: telebot.async_telebot.AsyncTeleBot.send_photo +#: telebot.async_telebot.AsyncTeleBot.send_poll +#: telebot.async_telebot.AsyncTeleBot.send_sticker +#: telebot.async_telebot.AsyncTeleBot.send_venue +#: telebot.async_telebot.AsyncTeleBot.send_video +#: telebot.async_telebot.AsyncTeleBot.send_video_note +#: telebot.async_telebot.AsyncTeleBot.send_voice +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title +#: telebot.async_telebot.AsyncTeleBot.set_chat_description +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_my_commands +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_update_listener +#: telebot.async_telebot.AsyncTeleBot.set_webhook +#: telebot.async_telebot.AsyncTeleBot.setup_middleware +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location +#: telebot.async_telebot.AsyncTeleBot.stop_poll +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file +#: telebot.asyncio_filters.TextFilter +#: telebot.ext.aio.webhooks.AsyncWebhookListener.run_app +msgid "Returns" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:15 +#: telebot.async_telebot.AsyncTeleBot.add_data:10 +#: telebot.async_telebot.AsyncTeleBot.callback_query_handler:9 +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler:17 +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:10 +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler:11 +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:10 +#: telebot.async_telebot.AsyncTeleBot.delete_state:9 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:22 +#: telebot.async_telebot.AsyncTeleBot.infinity_polling:35 +#: telebot.async_telebot.AsyncTeleBot.inline_handler:9 +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:10 +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:10 +#: telebot.async_telebot.AsyncTeleBot.poll_handler:8 +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:9 +#: telebot.async_telebot.AsyncTeleBot.process_new_updates:8 +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:23 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:26 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:26 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:14 +#: telebot.async_telebot.AsyncTeleBot.reset_data:9 +#: telebot.async_telebot.AsyncTeleBot.set_state:18 +#: telebot.async_telebot.AsyncTeleBot.set_update_listener:15 +#: telebot.async_telebot.AsyncTeleBot.setup_middleware:10 +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:9 +#: telebot.asyncio_filters.TextFilter:22 +#: telebot.ext.aio.webhooks.AsyncWebhookListener.run_app:4 +msgid "None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:1 +msgid "Add data to states." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:3 +#: telebot.async_telebot.AsyncTeleBot.delete_state:3 +#: telebot.async_telebot.AsyncTeleBot.get_state:4 +#: telebot.async_telebot.AsyncTeleBot.reset_data:3 +#: telebot.async_telebot.AsyncTeleBot.set_state:9 +msgid "User's identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:6 +#: telebot.async_telebot.AsyncTeleBot.delete_state:6 +#: telebot.async_telebot.AsyncTeleBot.get_state:7 +#: telebot.async_telebot.AsyncTeleBot.reset_data:6 +#: telebot.async_telebot.AsyncTeleBot.set_state:15 +msgid "Chat's identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_data:9 +msgid "Data to add" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:1 +msgid "" +"Use this method to add a new sticker to a set created by the bot. It's " +"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:7 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:7 +msgid "User identifier of created sticker set owner" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:10 +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:6 +msgid "Sticker set name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:13 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:18 +msgid "One or more emoji corresponding to the sticker" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:16 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:21 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px. Pass a file_id as a String to send a file that already " +"exists on the Telegram servers, 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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:26 +msgid "TGS animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:24 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 +msgid "WebM animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:27 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:40 +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:30 +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query:22 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:35 +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:21 +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:18 +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:43 +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:6 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:19 +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:15 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:13 +msgid "On success, True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic +#: telebot.async_telebot.AsyncTeleBot.create_invoice_link +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic +#: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_webhook +#: telebot.async_telebot.AsyncTeleBot.download_file +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_message_caption +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location +#: telebot.async_telebot.AsyncTeleBot.edit_message_media +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup +#: telebot.async_telebot.AsyncTeleBot.edit_message_text +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.get_chat +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators +#: telebot.async_telebot.AsyncTeleBot.get_chat_member +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count +#: telebot.async_telebot.AsyncTeleBot.get_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers +#: telebot.async_telebot.AsyncTeleBot.get_file_url +#: telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores +#: telebot.async_telebot.AsyncTeleBot.get_my_commands +#: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_state +#: telebot.async_telebot.AsyncTeleBot.get_sticker_set +#: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.log_out +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reply_to +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member +#: telebot.async_telebot.AsyncTeleBot.retrieve_data +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link +#: telebot.async_telebot.AsyncTeleBot.send_animation +#: telebot.async_telebot.AsyncTeleBot.send_audio +#: telebot.async_telebot.AsyncTeleBot.send_chat_action +#: telebot.async_telebot.AsyncTeleBot.send_contact +#: telebot.async_telebot.AsyncTeleBot.send_dice +#: telebot.async_telebot.AsyncTeleBot.send_document +#: telebot.async_telebot.AsyncTeleBot.send_game +#: telebot.async_telebot.AsyncTeleBot.send_invoice +#: telebot.async_telebot.AsyncTeleBot.send_location +#: telebot.async_telebot.AsyncTeleBot.send_media_group +#: telebot.async_telebot.AsyncTeleBot.send_message +#: telebot.async_telebot.AsyncTeleBot.send_photo +#: telebot.async_telebot.AsyncTeleBot.send_poll +#: telebot.async_telebot.AsyncTeleBot.send_sticker +#: telebot.async_telebot.AsyncTeleBot.send_venue +#: telebot.async_telebot.AsyncTeleBot.send_video +#: telebot.async_telebot.AsyncTeleBot.send_video_note +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title +#: telebot.async_telebot.AsyncTeleBot.set_chat_description +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set +#: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_my_commands +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_webhook +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location +#: telebot.async_telebot.AsyncTeleBot.stop_poll +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file +msgid "Return type" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:31 +#: telebot.async_telebot.AsyncTeleBot.answer_callback_query:23 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:36 +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:22 +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:19 +#: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:15 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member:25 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:17 +#: telebot.async_telebot.AsyncTeleBot.close:9 +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic:14 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:44 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:15 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo:13 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:11 +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:14 +#: telebot.async_telebot.AsyncTeleBot.delete_message:23 +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands:17 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:7 +#: telebot.async_telebot.AsyncTeleBot.delete_webhook:13 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:20 +#: telebot.async_telebot.AsyncTeleBot.leave_chat:8 +#: telebot.async_telebot.AsyncTeleBot.log_out:11 +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:48 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:23 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:18 +#: telebot.async_telebot.AsyncTeleBot.set_chat_description:14 +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo:16 +#: telebot.async_telebot.AsyncTeleBot.set_chat_title:17 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:18 +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:18 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:12 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:16 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member:20 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:15 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:12 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:14 +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:15 +msgid ":obj:`bool`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:1 +msgid "" +"Use this method to send answers to callback queries sent from inline " +"keyboards. The answer will be displayed to the user as a notification at " +"the top of the chat screen or as an alert." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answercallbackquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:6 +#: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:10 +#: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:5 +#: telebot.async_telebot.AsyncTeleBot.answer_web_app_query:8 +msgid "Unique identifier for the query to be answered" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:9 +msgid "" +"Text of the notification. If not specified, nothing will be shown to the " +"user, 0-200 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:12 +msgid "" +"If True, an alert will be shown by the client instead of a notification " +"at the top of the chat screen. Defaults to false." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:15 +msgid "" +"URL that will be opened by the user's client. If you have created a Game " +"and accepted the conditions via @BotFather, specify the URL that opens " +"your game - note that this will only work if the query comes from a " +"callback_game button." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:19 +msgid "" +"The maximum amount of time in seconds that the result of the callback " +"query may be cached client-side. Telegram apps will support caching " +"starting in version 3.14. Defaults to 0." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:1 +msgid "" +"Use this method to send answers to an inline query. On success, True is " +"returned. No more than 50 results per query are allowed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerinlinequery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:6 +msgid "Unique identifier for the answered query" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:9 +msgid "Array of results for the inline query" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:12 +msgid "" +"The maximum amount of time in seconds that the result of the inline query" +" may be cached on the server." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:16 +msgid "" +"Pass True, if results may be cached on the server side only for the user " +"that sent the query." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:20 +msgid "" +"Pass the offset that a client should send in the next query with the same" +" text to receive more results." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:24 +msgid "" +"Deep-linking parameter for the /start message sent to the bot when user " +"presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - " +"are allowed. Example: An inline bot that sends YouTube videos can ask the" +" user to connect the bot to their YouTube account to adapt search results" +" accordingly. To do this, it displays a 'Connect your YouTube account' " +"button above the results, or even before showing any. The user presses " +"the button, switches to a private chat with the bot and, in doing so, " +"passes a start parameter that instructs the bot to return an OAuth link. " +"Once done, the bot can offer a switch_inline button so that the user can " +"easily return to the chat where they wanted to use the bot's inline " +"capabilities." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:32 +msgid "" +"Parameter for the start message sent to the bot when user presses the " +"switch button" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:1 +msgid "" +"Once the user has confirmed their payment and shipping details, the Bot " +"API sends the final confirmation in the form of an Update with the field " +"pre_checkout_query. Use this method to respond to such pre-checkout " +"queries. On success, True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:6 +msgid "" +"The Bot API must receive an answer within 10 seconds after the pre-" +"checkout query was sent." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerprecheckoutquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:13 +msgid "" +"Specify True if everything is alright (goods are available, etc.) and the" +" bot is ready to proceed with the order. Use False if there are any " +"problems." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:16 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains the reason for failure to proceed with the checkout (e.g. " +"\"Sorry, somebody just bought the last of our amazing black T-shirts " +"while you were busy filling out your payment details. Please choose a " +"different color or garment!\"). Telegram will display this message to the" +" user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:1 +msgid "Asks for an answer to a shipping question." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answershippingquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:8 +msgid "" +"Specify True if delivery to the specified address is possible and False " +"if there are any problems (for example, if delivery to the specified " +"address is not possible)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:11 +msgid "" +"Required if ok is True. A JSON-serialized array of available shipping " +"options." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:14 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains why it is impossible to complete the order (e.g. \"Sorry, " +"delivery to your desired address is unavailable'). Telegram will display " +"this message to the user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:1 +msgid "" +"Use this method to set the result of an interaction with a Web App and " +"send a corresponding message on behalf of the user to the chat from which" +" the query originated. On success, a SentWebAppMessage object is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:6 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#answerwebappquery" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:11 +msgid "A JSON-serialized object describing the message to be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:14 +msgid "On success, a SentWebAppMessage object is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:15 +msgid ":class:`telebot.types.SentWebAppMessage`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:1 +msgid "" +"Use this method to approve a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#approvechatjoinrequest" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:7 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:7 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:7 +#: telebot.async_telebot.AsyncTeleBot.get_chat_member:5 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:6 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:7 +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:7 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup (in the format @supergroupusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:11 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_member:12 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:11 +#: telebot.async_telebot.AsyncTeleBot.get_chat_member:8 +#: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:6 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:11 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:11 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:10 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member:13 +msgid "Unique identifier of the target user" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:14 +#: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:16 +#: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:14 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_photo:12 +#: telebot.async_telebot.AsyncTeleBot.delete_my_commands:16 +#: telebot.async_telebot.AsyncTeleBot.log_out:10 +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message:18 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:60 +#: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:17 +#: telebot.async_telebot.AsyncTeleBot.set_chat_description:13 +#: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:14 +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_title:16 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:17 +#: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:17 +#: telebot.async_telebot.AsyncTeleBot.set_webhook:46 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:14 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:11 +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:14 +msgid "True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:1 +msgid "" +"Use this method to ban a user in a group, a supergroup or a channel. In " +"the case of supergroups and channels, the user will not be able to return" +" to the chat on their own using invite links, etc., unless unbanned " +"first. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:8 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:7 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:15 +msgid "" +"Date when the user will be unbanned, unix time. If user is banned for " +"more than 366 days or less than 30 seconds from the current time they are" +" considered to be banned forever" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:19 +msgid "" +"Bool: Pass True to delete all messages from the chat for the user that is" +" being removed. If False, the user will be able to see messages in the " +"group that were sent before the user was removed. Always True for " +"supergroups and channels." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 +#: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 +#: telebot.async_telebot.AsyncTeleBot.delete_message:22 +#: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:22 +msgid "Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:1 +msgid "" +"Use this method to ban a channel chat in a supergroup or a channel. The " +"owner of the chat will not be able to send messages and join live streams" +" on behalf of the chat, unless it is unbanned first. The bot must be an " +"administrator in the supergroup or channel for this to work and must have" +" the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#banchatsenderchat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:10 +#: telebot.async_telebot.AsyncTeleBot.close_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.copy_message:5 +#: telebot.async_telebot.AsyncTeleBot.create_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.delete_message:13 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:13 +#: telebot.async_telebot.AsyncTeleBot.forward_message:8 +#: telebot.async_telebot.AsyncTeleBot.pin_chat_message:7 +#: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.send_animation:6 +#: telebot.async_telebot.AsyncTeleBot.send_audio:9 +#: telebot.async_telebot.AsyncTeleBot.send_dice:5 +#: telebot.async_telebot.AsyncTeleBot.send_document:5 +#: telebot.async_telebot.AsyncTeleBot.send_game:5 +#: telebot.async_telebot.AsyncTeleBot.send_location:5 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:6 +#: telebot.async_telebot.AsyncTeleBot.send_message:9 +#: telebot.async_telebot.AsyncTeleBot.send_photo:5 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:6 +#: telebot.async_telebot.AsyncTeleBot.send_video:5 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:6 +#: telebot.async_telebot.AsyncTeleBot.send_voice:7 +#: telebot.async_telebot.AsyncTeleBot.set_chat_description:6 +#: telebot.async_telebot.AsyncTeleBot.set_chat_title:9 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:6 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:8 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:7 +#: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:7 +msgid "" +"Unique identifier for the target chat or username of the target channel " +"(in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:13 +msgid "Unique identifier of the target sender chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:1 +msgid "" +"Handles new incoming callback query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.CallbackQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:4 +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler:10 +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:5 +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler:6 +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:5 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:10 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:11 +#: telebot.async_telebot.AsyncTeleBot.inline_handler:4 +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:5 +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:5 +#: telebot.async_telebot.AsyncTeleBot.poll_handler:4 +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:4 +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:4 +msgid "Function executed as a filter" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:7 +#: telebot.async_telebot.AsyncTeleBot.channel_post_handler:16 +#: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:8 +#: telebot.async_telebot.AsyncTeleBot.chat_member_handler:9 +#: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:8 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:16 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:20 +#: telebot.async_telebot.AsyncTeleBot.inline_handler:7 +#: telebot.async_telebot.AsyncTeleBot.message_handler:50 +#: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:8 +#: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:8 +#: telebot.async_telebot.AsyncTeleBot.poll_handler:7 +#: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:7 +#: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:24 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:24 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:11 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:12 +#: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:7 +msgid "Optional keyword arguments(custom filters)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:1 +msgid "" +"Handles new incoming channel post of any kind - text, photo, sticker, " +"etc. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:4 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:4 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:5 +#: telebot.async_telebot.AsyncTeleBot.message_handler:34 +msgid "Optional list of strings (commands to handle)." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:7 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:7 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:8 +#: telebot.async_telebot.AsyncTeleBot.message_handler:37 +msgid "Optional regular expression." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:13 +#: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:13 +#: telebot.async_telebot.AsyncTeleBot.edited_message_handler:14 +#: telebot.async_telebot.AsyncTeleBot.message_handler:44 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:6 +msgid "Supported message content types. Must be a list. Defaults to ['text']." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:1 +msgid "" +"Handles a request to join the chat has been sent. The bot must have the " +"can_invite_users administrator right in the chat to receive these " +"updates. As a parameter to the decorator function, it passes " +":class:`telebot.types.ChatJoinRequest` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.chat_member_handler:1 +msgid "" +"Handles update in a status of a user in a chat. The bot must be an " +"administrator in the chat and must explicitly specify “chat_member” in " +"the list of allowed_updates to receive these updates. As a parameter to " +"the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:1 +msgid "" +"The result of an inline query that was chosen by a user and sent to their" +" chat partner. Please see our documentation on the feedback collecting " +"for details on how to enable these updates for your bot. As a parameter " +"to the decorator function, it passes " +":class:`telebot.types.ChosenInlineResult` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close:1 +msgid "" +"Use this method to close the bot instance before moving it from one local" +" server to another. You need to delete the webhook before calling this " +"method to ensure that the bot isn't launched again after server restart. " +"The method will return error 429 in the first 10 minutes after the bot is" +" launched. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#close" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:1 +msgid "" +"Use this method to close an open topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:10 +msgid "Identifier of the topic to close" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_session:1 +msgid "" +"Closes existing session of aiohttp. Use this function if you stop " +"polling/webhooks." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:1 +msgid "Use this method to copy messages of any kind." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:8 +#: telebot.async_telebot.AsyncTeleBot.forward_message:11 +msgid "" +"Unique identifier for the chat where the original message was sent (or " +"channel username in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:10 +#: telebot.async_telebot.AsyncTeleBot.forward_message:14 +msgid "Message identifier in the chat specified in from_chat_id" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:13 +msgid "" +"New caption for media, 0-1024 characters after entities parsing. If not " +"specified, the original caption is kept" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:16 +msgid "Mode for parsing entities in the new caption." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:19 +msgid "" +"A JSON-serialized list of special entities that appear in the new " +"caption, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:22 +#: telebot.async_telebot.AsyncTeleBot.send_animation:45 +#: telebot.async_telebot.AsyncTeleBot.send_audio:39 +#: telebot.async_telebot.AsyncTeleBot.send_contact:20 +#: telebot.async_telebot.AsyncTeleBot.send_dice:12 +#: telebot.async_telebot.AsyncTeleBot.send_document:26 +#: telebot.async_telebot.AsyncTeleBot.send_game:11 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:67 +#: telebot.async_telebot.AsyncTeleBot.send_location:25 +#: telebot.async_telebot.AsyncTeleBot.send_message:24 +#: telebot.async_telebot.AsyncTeleBot.send_photo:22 +#: telebot.async_telebot.AsyncTeleBot.send_poll:44 +#: telebot.async_telebot.AsyncTeleBot.send_venue:27 +#: telebot.async_telebot.AsyncTeleBot.send_video:35 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:27 +#: telebot.async_telebot.AsyncTeleBot.send_voice:31 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:25 +#: telebot.async_telebot.AsyncTeleBot.send_animation:34 +#: telebot.async_telebot.AsyncTeleBot.send_audio:57 +#: telebot.async_telebot.AsyncTeleBot.send_contact:38 +#: telebot.async_telebot.AsyncTeleBot.send_document:50 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:95 +#: telebot.async_telebot.AsyncTeleBot.send_location:43 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:15 +#: telebot.async_telebot.AsyncTeleBot.send_photo:25 +#: telebot.async_telebot.AsyncTeleBot.send_poll:64 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:30 +#: telebot.async_telebot.AsyncTeleBot.send_venue:51 +#: telebot.async_telebot.AsyncTeleBot.send_video:38 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:42 +#: telebot.async_telebot.AsyncTeleBot.send_voice:43 +msgid "Protects the contents of the sent message from forwarding and saving" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:28 +#: telebot.async_telebot.AsyncTeleBot.send_animation:37 +#: telebot.async_telebot.AsyncTeleBot.send_audio:29 +#: telebot.async_telebot.AsyncTeleBot.send_contact:23 +#: telebot.async_telebot.AsyncTeleBot.send_dice:15 +#: telebot.async_telebot.AsyncTeleBot.send_document:12 +#: telebot.async_telebot.AsyncTeleBot.send_game:14 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:70 +#: telebot.async_telebot.AsyncTeleBot.send_location:17 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:18 +#: telebot.async_telebot.AsyncTeleBot.send_message:30 +#: telebot.async_telebot.AsyncTeleBot.send_photo:28 +#: telebot.async_telebot.AsyncTeleBot.send_poll:47 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:13 +#: telebot.async_telebot.AsyncTeleBot.send_venue:30 +#: telebot.async_telebot.AsyncTeleBot.send_video:41 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:19 +#: telebot.async_telebot.AsyncTeleBot.send_voice:20 +msgid "If the message is a reply, ID of the original message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:31 +#: telebot.async_telebot.AsyncTeleBot.send_animation:54 +#: telebot.async_telebot.AsyncTeleBot.send_audio:54 +#: telebot.async_telebot.AsyncTeleBot.send_dice:26 +#: telebot.async_telebot.AsyncTeleBot.send_document:38 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:84 +#: telebot.async_telebot.AsyncTeleBot.send_location:40 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:24 +#: telebot.async_telebot.AsyncTeleBot.send_message:33 +#: telebot.async_telebot.AsyncTeleBot.send_photo:31 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:27 +#: telebot.async_telebot.AsyncTeleBot.send_video:44 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:39 +#: telebot.async_telebot.AsyncTeleBot.send_voice:40 +msgid "" +"Pass True, if the message should be sent even if the specified replied-to" +" message is not found" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:34 +#: telebot.async_telebot.AsyncTeleBot.send_animation:40 +#: telebot.async_telebot.AsyncTeleBot.send_contact:26 +#: telebot.async_telebot.AsyncTeleBot.send_dice:18 +#: telebot.async_telebot.AsyncTeleBot.send_document:18 +#: telebot.async_telebot.AsyncTeleBot.send_game:17 +#: telebot.async_telebot.AsyncTeleBot.send_location:20 +#: telebot.async_telebot.AsyncTeleBot.send_message:36 +#: telebot.async_telebot.AsyncTeleBot.send_photo:34 +#: telebot.async_telebot.AsyncTeleBot.send_poll:53 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:16 +#: telebot.async_telebot.AsyncTeleBot.send_venue:33 +#: telebot.async_telebot.AsyncTeleBot.send_video:47 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:22 +#: telebot.async_telebot.AsyncTeleBot.send_voice:23 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:39 +#: telebot.async_telebot.AsyncTeleBot.delete_message:19 +#: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:23 +#: telebot.async_telebot.AsyncTeleBot.forward_message:20 +#: telebot.async_telebot.AsyncTeleBot.send_animation:48 +#: telebot.async_telebot.AsyncTeleBot.send_audio:42 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:19 +#: telebot.async_telebot.AsyncTeleBot.send_contact:31 +#: telebot.async_telebot.AsyncTeleBot.send_dice:23 +#: telebot.async_telebot.AsyncTeleBot.send_document:29 +#: telebot.async_telebot.AsyncTeleBot.send_location:28 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:21 +#: telebot.async_telebot.AsyncTeleBot.send_message:40 +#: telebot.async_telebot.AsyncTeleBot.send_photo:39 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:24 +#: telebot.async_telebot.AsyncTeleBot.send_venue:38 +#: telebot.async_telebot.AsyncTeleBot.send_video:52 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:30 +#: telebot.async_telebot.AsyncTeleBot.send_voice:34 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:19 +msgid "Timeout in seconds for the request." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:42 +#: telebot.async_telebot.AsyncTeleBot.send_audio:60 +#: telebot.async_telebot.AsyncTeleBot.send_document:53 +#: telebot.async_telebot.AsyncTeleBot.send_location:46 +#: telebot.async_telebot.AsyncTeleBot.send_photo:42 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:36 +#: telebot.async_telebot.AsyncTeleBot.send_voice:46 +msgid "Identifier of a message thread, in which the message will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:45 +#: telebot.async_telebot.AsyncTeleBot.forward_message:26 +#: telebot.async_telebot.AsyncTeleBot.reply_to:11 +#: telebot.async_telebot.AsyncTeleBot.send_animation:60 +#: telebot.async_telebot.AsyncTeleBot.send_audio:63 +#: telebot.async_telebot.AsyncTeleBot.send_contact:44 +#: telebot.async_telebot.AsyncTeleBot.send_dice:35 +#: telebot.async_telebot.AsyncTeleBot.send_document:56 +#: telebot.async_telebot.AsyncTeleBot.send_game:32 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:101 +#: telebot.async_telebot.AsyncTeleBot.send_location:49 +#: telebot.async_telebot.AsyncTeleBot.send_message:46 +#: telebot.async_telebot.AsyncTeleBot.send_photo:45 +#: telebot.async_telebot.AsyncTeleBot.send_poll:70 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:39 +#: telebot.async_telebot.AsyncTeleBot.send_venue:57 +#: telebot.async_telebot.AsyncTeleBot.send_video:61 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:48 +#: telebot.async_telebot.AsyncTeleBot.send_voice:49 +msgid "On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:46 +#: telebot.async_telebot.AsyncTeleBot.forward_message:27 +#: telebot.async_telebot.AsyncTeleBot.reply_to:12 +#: telebot.async_telebot.AsyncTeleBot.send_animation:61 +#: telebot.async_telebot.AsyncTeleBot.send_audio:64 +#: telebot.async_telebot.AsyncTeleBot.send_contact:45 +#: telebot.async_telebot.AsyncTeleBot.send_dice:36 +#: telebot.async_telebot.AsyncTeleBot.send_document:57 +#: telebot.async_telebot.AsyncTeleBot.send_location:50 +#: telebot.async_telebot.AsyncTeleBot.send_message:47 +#: telebot.async_telebot.AsyncTeleBot.send_photo:46 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:40 +#: telebot.async_telebot.AsyncTeleBot.send_venue:58 +#: telebot.async_telebot.AsyncTeleBot.send_video:62 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:49 +msgid ":class:`telebot.types.Message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:1 +msgid "" +"Use this method to create an additional invite link for a chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. The link can be revoked using the " +"method revokeChatInviteLink. Returns the new invite link as " +"ChatInviteLink object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createchatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:8 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:6 +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:6 +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:7 +msgid "" +"Id: Unique identifier for the target chat or username of the target " +"channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:12 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:10 +msgid "Invite link name; 0-32 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:15 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:16 +msgid "Point in time (Unix timestamp) when the link will expire" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:18 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:19 +msgid "Maximum number of users that can be members of the chat simultaneously" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:21 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:22 +msgid "" +"True, if users joining the chat via the link need to be approved by chat " +"administrators. If True, member_limit can't be specified" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:24 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:25 +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:14 +msgid "Returns the new invite link as ChatInviteLink object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:25 +#: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:26 +#: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:15 +msgid ":class:`telebot.types.ChatInviteLink`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:1 +msgid "" +"Use this method to create a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights. Returns information about the " +"created topic as a ForumTopic object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:10 +msgid "Name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:13 +msgid "" +"Color of the topic icon in RGB format. Currently, must be one of " +"0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:16 +msgid "" +"Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " +"be exactly 1 character long" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:19 +msgid "" +"On success, information about the created topic is returned as a " +"ForumTopic object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:20 +msgid ":class:`telebot.types.ForumTopic`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:1 +msgid "" +"Use this method to create a link for an invoice. Returns the created " +"invoice link as String on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createinvoicelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:7 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:8 +msgid "Product name, 1-32 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:10 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:11 +msgid "Product description, 1-255 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:13 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:14 +msgid "" +"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " +"the user, use for your internal processes." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:17 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:18 +msgid "Payments provider token, obtained via @Botfather" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:20 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:21 +msgid "" +"Three-letter ISO 4217 currency code, see " +"https://core.telegram.org/bots/payments#supported-currencies" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:24 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:25 +msgid "" +"Price breakdown, a list of components (e.g. product price, tax, discount," +" delivery cost, delivery tax, bonus, etc.)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:28 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:87 +msgid "The maximum accepted amount for tips in the smallest units of the currency" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:31 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:90 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:36 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:77 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:40 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a photo of the invoice. People like it better when they see a photo of " +"what they are paying for." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:44 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:37 +msgid "Photo size in bytes" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:47 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:40 +msgid "Photo width" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:50 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:43 +msgid "Photo height" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:53 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:46 +msgid "Pass True, if you require the user's full name to complete the order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:56 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:49 +msgid "Pass True, if you require the user's phone number to complete the order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:59 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:52 +msgid "Pass True, if you require the user's email to complete the order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:62 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:55 +msgid "" +"Pass True, if you require the user's shipping address to complete the " +"order" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:65 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:61 +msgid "Pass True, if user's phone number should be sent to provider" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:68 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:64 +msgid "Pass True, if user's email address should be sent to provider" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:71 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:58 +msgid "Pass True, if the final price depends on the shipping method" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:74 +msgid "Created invoice link as String on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:75 +#: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:11 +#: telebot.async_telebot.AsyncTeleBot.get_file_url:7 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:1 +msgid "" +"Use this method to create new sticker set owned by a user. The bot will " +"be able to edit the created sticker set. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createnewstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:10 +msgid "" +"Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " +"animals). Can contain only English letters, digits and underscores. Must " +"begin with a letter, can't contain consecutive underscores and must end " +"in \"_by_\". is case insensitive. 1-64 " +"characters." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:15 +msgid "Sticker set title, 1-64 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:32 +msgid "" +"Pass True, if a set of mask stickers should be created. Deprecated since " +"Bot API 6.2, use sticker_type instead." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:36 +msgid "" +"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " +"emoji sticker sets can't be created via the Bot API at the moment. By " +"default, a regular sticker set is created." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:1 +msgid "" +"Use this method to decline a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#declinechatjoinrequest" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:1 +msgid "" +"Use this method to delete a chat photo. Photos can't be changed for " +"private chats. The bot must be an administrator in the chat for this to " +"work and must have the appropriate admin rights. Returns True on success." +" Note: In regular groups (non-supergroups), this method will only work if" +" the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:8 +#: telebot.async_telebot.AsyncTeleBot.set_chat_photo:9 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:7 +msgid "" +"Int or Str: Unique identifier for the target chat or username of the " +"target channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:1 +msgid "" +"Use this method to delete a group sticker set from a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights. Use the field can_set_sticker_set optionally " +"returned in getChat requests to check if the bot can use this method. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletechatstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:1 +msgid "" +"Use this method to delete a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights, unless it is the creator of the " +"topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:10 +msgid "Identifier of the topic to delete" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_message:1 +msgid "" +"Use this method to delete a message, including service messages, with the" +" following limitations: - A message can only be deleted if it was sent " +"less than 48 hours ago. - A dice message in a private chat can only be " +"deleted if it was sent more than 24 hours ago. - Bots can delete outgoing" +" messages in private chats, groups, and supergroups. - Bots can delete " +"incoming messages in private chats. - Bots granted can_post_messages " +"permissions can delete outgoing messages in channels. - If the bot is an " +"administrator of a group, it can delete any message there. - If the bot " +"has can_delete_messages permission in a supergroup or a channel, it can " +"delete any message there. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_message:11 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_message:16 +msgid "Identifier of the message to delete" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:1 +msgid "" +"Use this method to delete the list of the bot's commands for the given " +"scope and user language. After deletion, higher level commands will be " +"shown to affected users. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletemycommands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:7 +#: telebot.async_telebot.AsyncTeleBot.get_my_commands:6 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:8 +msgid "" +"The scope of users for which the commands are relevant. Defaults to " +"BotCommandScopeDefault." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:11 +#: telebot.async_telebot.AsyncTeleBot.get_my_commands:10 +#: telebot.async_telebot.AsyncTeleBot.set_my_commands:12 +msgid "" +"A two-letter ISO 639-1 language code. If empty, commands will be applied " +"to all users from the given scope, for whose language there are no " +"dedicated commands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_state:1 +msgid "Delete the current state of a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:1 +msgid "" +"Use this method to delete a sticker from a set created by the bot. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletestickerfromset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:5 +msgid "File identifier of the sticker" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:1 +msgid "" +"Use this method to remove webhook integration if you decide to switch " +"back to getUpdates. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:6 +#: telebot.async_telebot.AsyncTeleBot.set_webhook:36 +msgid "Pass True to drop all pending updates, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_webhook:9 +msgid "Request connection timeout, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:1 +msgid "Downloads file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:3 +msgid "Path where the file should be downloaded." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:6 +msgid "bytes" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.download_file:7 +msgid ":obj:`bytes`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:1 +msgid "" +"Use this method to edit a non-primary invite link created by the bot. The" +" bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editchatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:13 +msgid "The invite link to edit" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:1 +msgid "" +"Use this method to edit name and icon of a topic in a forum supergroup " +"chat. The bot must be an administrator in the chat for this to work and " +"must have can_manage_topics administrator rights, unless it is the " +"creator of the topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:5 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:10 +msgid "Identifier of the topic to edit" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:13 +msgid "New name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:16 +msgid "" +"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " +"must be exactly 1 character long" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:1 +msgid "Use this method to edit captions of messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagecaption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:5 +msgid "New caption of the message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:8 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:11 +msgid "Required if inline_message_id is not specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:14 +msgid "" +"Required if inline_message_id is not specified. Identifier of the inline " +"message." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:17 +msgid "New caption of the message, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:20 +msgid "" +"A JSON-serialized array of objects that describe how the caption should " +"be parsed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:23 +#: telebot.async_telebot.AsyncTeleBot.edit_message_media:19 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:14 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:26 +msgid "A JSON-serialized object for an inline keyboard." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:26 +#: telebot.async_telebot.AsyncTeleBot.edit_message_media:22 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:17 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:29 +msgid "" +"On success, if edited message is sent by the bot, the edited Message is " +"returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:27 +msgid ":obj:`types.Message` | :obj:`bool`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:3 +msgid "" +"Use this method to edit live location messages. A location can be edited " +"until its live_period expires or editing is explicitly" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:2 +msgid "" +"disabled by a call to stopMessageLiveLocation. On success, if the edited " +"message is not an inline message, the edited Message is returned, " +"otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagelivelocation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:7 +msgid "Latitude of new location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:10 +msgid "Longitude of new location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:16 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" to edit" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:19 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:15 +msgid "A JSON-serialized object for a new inline keyboard." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:26 +#: telebot.async_telebot.AsyncTeleBot.edit_message_media:16 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:11 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:14 +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:19 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:20 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:29 +#: telebot.async_telebot.AsyncTeleBot.send_location:31 +msgid "The radius of uncertainty for the location, measured in meters; 0-1500" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:32 +msgid "" +"Direction in which the user is moving, in degrees. Must be between 1 and " +"360 if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:35 +msgid "" +"The maximum distance for proximity alerts about approaching another chat " +"member, in meters. Must be between 1 and 100000 if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:38 +msgid "" +"On success, if the edited message is not an inline message, the edited " +"Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:39 +#: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:23 +msgid ":class:`telebot.types.Message` or bool" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:1 +msgid "" +"Use this method to edit animation, audio, document, photo, or video " +"messages. If a message is a part of a message album, then it can be " +"edited only to a photo or a video. Otherwise, message type can be changed" +" arbitrarily. When inline message is edited, new file can't be uploaded. " +"Use previously uploaded file via its file_id or specify a URL." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagemedia" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:8 +msgid "A JSON-serialized object for a new media content of the message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:10 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:5 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:8 +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:13 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:14 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel (in the format " +"@channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:13 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:8 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:11 +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:16 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:17 +msgid "" +"Required if inline_message_id is not specified. Identifier of the sent " +"message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_media:23 +#: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:18 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:30 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:27 +msgid ":obj:`types.Message` or :obj:`bool`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:1 +msgid "Use this method to edit only the reply markup of messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagereplymarkup" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:1 +msgid "Use this method to edit text and game messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#editmessagetext" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:5 +msgid "New text of the message, 1-4096 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:17 +#: telebot.async_telebot.AsyncTeleBot.send_message:15 +msgid "Mode for parsing entities in the message text." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:20 +msgid "" +"List of special entities that appear in the message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:23 +#: telebot.async_telebot.AsyncTeleBot.send_message:21 +msgid "Disables link previews for links in this message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:1 +msgid "" +"Handles new version of a channel post that is known to the bot and was " +"edited. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:1 +msgid "Handles new version of a message that is known to the bot and was edited." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:3 +msgid "" +"As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:17 +#: telebot.async_telebot.AsyncTeleBot.message_handler:47 +msgid "list of chat types" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:1 +msgid "Enable saving states (by default saving disabled)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:4 +msgid "" +"It is recommended to pass a " +":class:`~telebot.asyncio_storage.StatePickleStorage` instance as " +"state_storage to TeleBot class." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:7 +msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:1 +msgid "" +"Use this method to export an invite link to a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#exportchatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:10 +msgid "exported invite link as String on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:1 +msgid "Use this method to forward messages of any kind." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:17 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:23 +#: telebot.async_telebot.AsyncTeleBot.send_message:43 +msgid "" +"Unique identifier for the target message thread (topic) of the forum; for" +" forum supergroups only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:1 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:6 +#: telebot.async_telebot.AsyncTeleBot.get_chat_administrators:7 +#: telebot.async_telebot.AsyncTeleBot.get_chat_member_count:5 +#: telebot.async_telebot.AsyncTeleBot.leave_chat:5 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:9 +msgid "Chat information" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat:10 +msgid ":class:`telebot.types.Chat`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:1 +msgid "" +"Use this method to get a list of administrators in a chat. On success, " +"returns an Array of ChatMember objects that contains information about " +"all chat administrators except other bots." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatadministrators" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:9 +msgid "List made of ChatMember objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:10 +msgid ":obj:`list` of :class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:1 +msgid "" +"Use this method to get information about a member of a chat. Returns a " +"ChatMember object on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:11 +msgid "Returns ChatMember object on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member:12 +msgid ":class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:1 +msgid "Use this method to get the number of members in a chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatmembercount" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:8 +msgid "Number of members in the chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:9 +msgid ":obj:`int`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:1 +msgid "" +"Use this method to get the current value of the bot's menu button in a " +"private chat, or the default menu button. Returns MenuButton on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:5 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#getchatmenubutton" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:11 +msgid "types.MenuButton" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:12 +msgid ":class:`telebot.types.MenuButton`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:1 +msgid "" +"Use this method to get information about custom emoji stickers by their " +"identifiers. Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:4 +msgid "" +"List of custom emoji identifiers. At most 200 custom emoji identifiers " +"can be specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:7 +msgid "Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:8 +msgid ":obj:`list` of :class:`telebot.types.Sticker`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:1 +msgid "" +"Use this method to get basic info about a file and prepare it for " +"downloading. For the moment, bots can download files of up to 20MB in " +"size. On success, a File object is returned. It is guaranteed that the " +"link will be valid for at least 1 hour. When the link expires, a new one " +"can be requested by calling get_file again." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:9 +msgid "File identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file:12 +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file:14 +msgid ":class:`telebot.types.File`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file_url:1 +msgid "Get a valid URL for downloading a file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file_url:3 +msgid "File identifier to get download URL for." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_file_url:6 +msgid "URL for downloading the file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:1 +msgid "" +"Use this method to get custom emoji stickers, which can be used as a " +"forum topic icon by any user. Requires no parameters. Returns an Array of" +" Sticker objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getforumtopiciconstickers" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:6 +msgid "On success, a list of StickerSet objects is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:7 +msgid "List[:class:`telebot.types.StickerSet`]" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:1 +msgid "" +"Use this method to get data for high score tables. Will return the score " +"of the specified user and several of their neighbors in a game. On " +"success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:4 +msgid "" +"This method will currently return scores for the target user, plus two of" +" their closest neighbors on each side. Will also return the top three " +"users if the user and their neighbors are not among them. Please note " +"that this behavior is subject to change." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getgamehighscores" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:10 +#: telebot.async_telebot.AsyncTeleBot.retrieve_data:3 +#: telebot.async_telebot.AsyncTeleBot.set_game_score:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:9 +msgid "User identifier" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:22 +msgid "On success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_me:1 +msgid "Returns basic information about the bot in form of a User object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_me:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getme" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:1 +msgid "" +"Use this method to get the current list of the bot's commands. Returns " +"List of BotCommand on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:15 +msgid "List of BotCommand on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_commands:16 +msgid ":obj:`list` of :class:`telebot.types.BotCommand`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:1 +msgid "" +"Use this method to get the current default administrator rights of the " +"bot. Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmydefaultadministratorrights" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:6 +msgid "" +"Pass True to get the default administrator rights of the bot in channels." +" Otherwise, the default administrator rights of the bot for groups and " +"supergroups will be returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:9 +msgid "Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:10 +msgid ":class:`telebot.types.ChatAdministratorRights`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_state:1 +msgid "" +"Gets current state of a user. Not recommended to use this method. But it " +"is ok for debugging." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_state:10 +msgid "state of a user" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_state:11 +msgid ":obj:`int` or :obj:`str` or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:1 +msgid "" +"Use this method to get a sticker set. On success, a StickerSet object is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:8 +msgid "On success, a StickerSet object is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:9 +#: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:14 +msgid ":class:`telebot.types.StickerSet`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:1 +msgid "" +"Use this method to receive incoming updates using long polling (wiki). An" +" Array of Update objects is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:6 +msgid "" +"Identifier of the first update to be returned. Must be greater by one " +"than the highest among the identifiers of previously received updates. By" +" default, updates starting with the earliest unconfirmed update are " +"returned. An update is considered confirmed as soon as getUpdates is " +"called with an offset higher than its update_id. The negative offset can " +"be specified to retrieve updates starting from -offset update from the " +"end of the updates queue. All previous updates will forgotten." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:12 +msgid "" +"Limits the number of updates to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:15 +#: telebot.async_telebot.AsyncTeleBot.get_webhook_info:6 +#: telebot.async_telebot.AsyncTeleBot.polling:26 +msgid "Request connection timeout" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:18 +msgid "Array of string. List the types of updates you want your bot to receive." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:21 +msgid "Timeout in seconds for long polling." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:24 +msgid "An Array of Update objects is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_updates:25 +msgid ":obj:`list` of :class:`telebot.types.Update`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:1 +msgid "" +"Use this method to get a list of profile pictures for a user. Returns a " +":class:`telebot.types.UserProfilePhotos` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserprofilephotos" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:9 +msgid "" +"Sequential number of the first photo to be returned. By default, all " +"photos are returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:12 +msgid "" +"Limits the number of photos to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:15 +msgid "" +"`UserProfilePhotos " +"`_" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:16 +msgid ":class:`telebot.types.UserProfilePhotos`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:1 +msgid "" +"Use this method to get current webhook status. Requires no parameters. On" +" success, returns a WebhookInfo object. If the bot is using getUpdates, " +"will return an object with the url field empty." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:9 +msgid "On success, returns a WebhookInfo object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:10 +msgid ":class:`telebot.types.WebhookInfo`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:1 +msgid "" +"Wrap polling with infinite loop and exception handling to avoid bot stops" +" polling." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:4 +#: telebot.async_telebot.AsyncTeleBot.polling:15 +msgid "Install watchdog and psutil before using restart_on_change option." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:6 +#: telebot.async_telebot.AsyncTeleBot.polling:29 +msgid "Timeout in seconds for get_updates(Defaults to None)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:9 +#: telebot.async_telebot.AsyncTeleBot.polling:20 +msgid "skip old updates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:12 +msgid "Aiohttp's request timeout. Defaults to 5 minutes(aiohttp.ClientTimeout)." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:15 +msgid "" +"Custom logging level for infinity_polling logging. Use logger levels from" +" logging as a value. None/NOTSET = no error logging" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:19 +#: telebot.async_telebot.AsyncTeleBot.polling:32 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. 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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:19 +#: telebot.async_telebot.AsyncTeleBot.polling:32 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:25 +#: telebot.async_telebot.AsyncTeleBot.polling:38 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:29 +msgid "Restart a file on file(s) change. Defaults to False" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.infinity_polling:32 +#: telebot.async_telebot.AsyncTeleBot.polling:48 +msgid "Path to watch for changes. Defaults to current directory" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.inline_handler:1 +msgid "" +"Handles new incoming inline query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.InlineQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.kick_chat_member:1 +msgid "This function is deprecated. Use `ban_chat_member` instead" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.leave_chat:1 +msgid "" +"Use this method for your bot to leave a group, supergroup or channel. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.leave_chat:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#leavechat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.log_out:1 +msgid "" +"Use this method to log out from the cloud Bot API server before launching" +" the bot locally. You MUST log out the bot before running it locally, " +"otherwise there is no guarantee that the bot will receive updates. After " +"a successful call, you can immediately log in on a local server, but will" +" not be able to log in back to the cloud Bot API server for 10 minutes. " +"Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.log_out:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:1 +msgid "" +"Handles ew incoming message of any kind - text, photo, sticker, etc. As a" +" parameter to the decorator function, it passes " +":class:`telebot.types.Message` object. All message handlers are tested in" +" the order they were added." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:5 +msgid "Example:" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:7 +msgid "Usage of message_handler" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:40 +msgid "" +"Optional lambda function. The lambda receives the message to test as the " +"first parameter. It must return True if the command should handle the " +"message." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_handler:52 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:23 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:13 +msgid "decorated function" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:1 +msgid "" +"Handles update in a status of a bot. For private chats, this update is " +"received only when the bot is blocked or unblocked by the user. As a " +"parameter to the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:1 +msgid "" +"Use this method to pin a message in a supergroup. The bot must be an " +"administrator in the chat for this to work and must have the appropriate " +"admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:11 +msgid "Identifier of a message to pin" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:14 +msgid "" +"Pass True, if it is not necessary to send a notification to all group " +"members about the new pinned message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.poll_answer_handler:1 +msgid "" +"Handles change of user's answer in a non-anonymous poll(when user changes" +" the vote). Bots receive new votes only in polls that were sent by the " +"bot itself. As a parameter to the decorator function, it passes " +":class:`telebot.types.PollAnswer` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.poll_handler:1 +msgid "" +"Handles new state of a poll. Bots receive only updates about stopped " +"polls and polls, which are sent by the bot As a parameter to the " +"decorator function, it passes :class:`telebot.types.Poll` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:1 +msgid "" +"Runs bot in long-polling mode in a main loop. This allows the bot to " +"retrieve Updates automagically and notify listeners and message handlers " +"accordingly." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:4 +msgid "Warning: Do not call this function more than once!" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:6 +msgid "Always gets updates." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:10 +msgid "" +"Set non_stop=True if you want your bot to continue receiving updates if " +"there is an error." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:17 +msgid "Do not stop polling when an ApiException occurs." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:23 +msgid "Delay between two update retrivals" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:42 +msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.polling:45 +msgid "Restart a file on file(s) change. Defaults to False." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:1 +msgid "" +"New incoming pre-checkout query. Contains full information about " +"checkout. As a parameter to the decorator function, it passes " +":class:`telebot.types.PreCheckoutQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.process_new_updates:1 +msgid "" +"Process new updates. Just pass list of updates - each update should be " +"instance of Update object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.process_new_updates:5 +msgid "list of updates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:1 +msgid "" +"Use this method to promote or demote a user in a supergroup or a channel." +" The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Pass False for all boolean parameters " +"to demote a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#promotechatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:7 +msgid "" +"Unique identifier for the target chat or username of the target channel (" +" in the format @channelusername)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:14 +msgid "" +"Pass True, if the administrator can change chat title, photo and other " +"settings" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:17 +msgid "Pass True, if the administrator can create channel posts, channels only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:20 +msgid "" +"Pass True, if the administrator can edit messages of other users, " +"channels only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:23 +msgid "Pass True, if the administrator can delete messages of other users" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:26 +msgid "Pass True, if the administrator can invite new users to the chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:29 +msgid "Pass True, if the administrator can restrict, ban or unban chat members" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:32 +msgid "Pass True, if the administrator can pin messages, supergroups only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:35 +msgid "" +"Pass True, if the administrator can add new administrators with a subset " +"of his own privileges or demote administrators that he has promoted, " +"directly or indirectly (promoted by administrators that were appointed by" +" him)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:40 +msgid "Pass True, if the administrator's presence in the chat is hidden" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:43 +msgid "" +"Pass True, if the administrator can access the chat event log, chat " +"statistics, message statistics in channels, see channel members, see " +"anonymous administrators in supergroups and ignore slow mode. Implied by " +"any other administrator privilege" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:49 +msgid "" +"Pass True, if the administrator can manage voice chats For now, bots can " +"use this privilege only for passing to other administrators." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:53 +msgid "Deprecated, use can_manage_video_chats." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:56 +msgid "" +"Pass True if the user is allowed to create, rename, close, and reopen " +"forum topics, supergroups only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:1 +msgid "Registers callback query handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:3 +msgid "function to be called" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:18 +#: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:18 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_inline_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_poll_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:8 +#: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:9 +msgid "" +"True if you need to pass TeleBot instance to handler(useful for " +"separating handlers into different files)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:1 +msgid "Registers channel post message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_message_handler:9 +msgid "list of commands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:12 +msgid "Regular expression" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:1 +msgid "Registers chat join request handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:1 +msgid "Registers chat member handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:14 +msgid ":return:None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:1 +msgid "Registers chosen inline handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:1 +msgid "Registers edited channel post message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:1 +msgid "Registers edited message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:18 +msgid "True for private chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_inline_handler:1 +msgid "Registers inline handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_message_handler:1 +msgid "Registers message handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_message_handler:18 +msgid "List of chat types" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:1 +msgid "Registers my chat member handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:1 +msgid "Registers poll answer handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_poll_handler:1 +msgid "Registers poll handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:1 +msgid "Registers pre-checkout request handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:1 +msgid "Registers shipping query handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.remove_webhook:1 +msgid "Alternative for delete_webhook but uses set_webhook" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:1 +msgid "" +"Use this method to reopen a closed topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopenforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:10 +msgid "Identifier of the topic to reopen" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:1 +msgid "" +"Convenience function for `send_message(message.chat.id, text, " +"reply_to_message_id=message.message_id, **kwargs)`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:3 +msgid "Instance of :class:`telebot.types.Message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:6 +msgid "Text of the message." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reply_to:9 +msgid "" +"Additional keyword arguments which are passed to " +":meth:`telebot.TeleBot.send_message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reset_data:1 +msgid "Reset data for a user in chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:1 +msgid "" +"Use this method to restrict a user in a supergroup. The bot must be an " +"administrator in the supergroup for this to work and must have the " +"appropriate admin rights. Pass True for all boolean parameters to lift " +"restrictions from a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#restrictchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 +msgid "" +"Date when restrictions will be lifted for the user, unix time. If user is" +" restricted for more than 366 days or less than 30 seconds from the " +"current time, they are considered to be restricted forever" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:19 +msgid "" +"Pass True, if the user can send text messages, contacts, locations and " +"venues" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:22 +msgid "" +"Pass True, if the user can send audios, documents, photos, videos, video " +"notes and voice notes, implies can_send_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:26 +msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:29 +msgid "" +"Pass True, if the user can send animations, games, stickers and use " +"inline bots, implies can_send_media_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 +msgid "" +"Pass True, if the user may add web page previews to their messages, " +"implies can_send_media_messages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:36 +msgid "" +"Pass True, if the user is allowed to change the chat title, photo and " +"other settings. Ignored in public supergroups" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:40 +msgid "" +"Pass True, if the user is allowed to invite new users to the chat, " +"implies can_invite_users" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:44 +msgid "" +"Pass True, if the user is allowed to pin messages. Ignored in public " +"supergroups" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:47 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:14 +#: telebot.async_telebot.AsyncTeleBot.unban_chat_member:19 +msgid "True on success" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.retrieve_data:1 +msgid "Returns context manager with data for a user in chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.retrieve_data:6 +msgid "Chat's unique identifier, defaults to user_id" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.retrieve_data:9 +msgid "Context manager with data for a user in chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:1 +msgid "" +"Use this method to revoke an invite link created by the bot. Note: If the" +" primary link is revoked, a new link is automatically generated The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#revokechatinvitelink" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:11 +msgid "The invite link to revoke" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:1 +msgid "This class sets webhooks and listens to a given url and port." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:3 +msgid "IP address to listen to. Defaults to 0.0.0.0" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:5 +msgid "A port which will be used to listen to webhooks." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:6 +msgid "Path to the webhook. Defaults to /token" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:7 +msgid "Path to the certificate file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:8 +msgid "Path to the certificate key file." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:9 +msgid "Webhook URL." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:10 +msgid "" +"Maximum allowed number of simultaneous HTTPS connections to the webhook " +"for update delivery, 1-100. Defaults to 40. Use lower values to limit the" +" load on your bot's server, and higher values to increase your bot's " +"throughput." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:11 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all updates " +"regardless of type (default). If not specified, the previous setting will" +" be used." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:12 +msgid "" +"The fixed IP address which will be used to send webhook requests instead " +"of the IP address resolved through DNS" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:13 +msgid "Pass True to drop all pending updates" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:14 +msgid "Integer. Request connection timeout" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:15 +msgid "Secret token to be used to verify the webhook request." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:1 +msgid "" +"Use this method to send animation files (GIF or H.264/MPEG-4 AVC video " +"without sound). On success, the sent Message is returned. Bots can " +"currently send animation files of up to 50 MB in size, this limit may be " +"changed in the future." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:9 +msgid "" +"Animation to send. Pass a file_id as String to send an animation that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an animation from the Internet, or upload a " +"new animation using multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:13 +msgid "Duration of sent animation in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:16 +msgid "Animation width" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:19 +msgid "Animation height" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:22 +#: telebot.async_telebot.AsyncTeleBot.send_video:20 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:33 +msgid "" +"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 ." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:28 +msgid "" +"Animation caption (may also be used when resending animation by file_id)," +" 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:31 +msgid "Mode for parsing entities in the animation caption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:51 +#: telebot.async_telebot.AsyncTeleBot.send_video:29 +msgid "" +"List of special entities that appear in the caption, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_animation:57 +#: telebot.async_telebot.AsyncTeleBot.send_video:58 +msgid "Identifier of a message thread, in which the video will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:1 +msgid "" +"Use this method to send audio files, if you want Telegram clients to " +"display them in the music player. Your audio must be in the .MP3 or .M4A " +"format. On success, the sent Message is returned. Bots can currently send" +" audio files of up to 50 MB in size, this limit may be changed in the " +"future." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:5 +msgid "For sending voice messages, use the send_voice method instead." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendaudio" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:12 +msgid "" +"Audio file to send. Pass a file_id as String to send an audio file that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an audio file from the Internet, or upload a " +"new one using multipart/form-data. Audio must be in the .MP3 or .M4A " +"format." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:17 +msgid "Audio caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:20 +msgid "Duration of the audio in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:23 +msgid "Performer" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:26 +msgid "Track name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:36 +msgid "" +"Mode for parsing entities in the audio caption. See formatting options " +"for more details." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:45 +msgid "" +"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 " +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_audio:51 +#: telebot.async_telebot.AsyncTeleBot.send_document:35 +#: telebot.async_telebot.AsyncTeleBot.send_photo:19 +#: telebot.async_telebot.AsyncTeleBot.send_voice:37 +msgid "" +"A JSON-serialized list of special entities that appear in the caption, " +"which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:1 +msgid "" +"Use this method when you need to tell the user that something is " +"happening on the bot's side. The status is set for 5 seconds or less " +"(when a message arrives from your bot, Telegram clients clear its typing " +"status). Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:5 +msgid "" +"Example: The ImageBot needs some time to process a request and upload the" +" image. Instead of sending a text message along the lines of “Retrieving " +"image, please wait…”, the bot may use sendChatAction with action = " +"upload_photo. The user will see a “sending photo” status for the bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:10 +#: telebot.async_telebot.AsyncTeleBot.send_contact:5 +#: telebot.async_telebot.AsyncTeleBot.send_poll:6 +#: telebot.async_telebot.AsyncTeleBot.send_venue:5 +#: telebot.async_telebot.AsyncTeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:13 +msgid "" +"Type of action to broadcast. Choose one, depending on what the user is " +"about to receive: typing for text messages, upload_photo for photos, " +"record_video or upload_video for videos, record_voice or upload_voice for" +" voice notes, upload_document for general files, choose_sticker for " +"stickers, find_location for location data, record_video_note or " +"upload_video_note for video notes." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:1 +msgid "" +"Use this method to send phone contacts. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:8 +msgid "Contact's phone number" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:11 +msgid "Contact's first name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:14 +msgid "Contact's last name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:17 +msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:34 +#: telebot.async_telebot.AsyncTeleBot.send_game:23 +#: telebot.async_telebot.AsyncTeleBot.send_venue:41 +msgid "" +"Pass True, if the message should be sent even if one of the specified " +"replied-to messages is not found." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_contact:41 +#: telebot.async_telebot.AsyncTeleBot.send_venue:54 +msgid "The thread to which the message will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:1 +msgid "" +"Use this method to send an animated emoji that will display a random " +"value. On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:8 +msgid "" +"Emoji on which the dice throw animation is based. Currently, must be one " +"of “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, or “🎰”. Dice can have values 1-6 for “🎲”, “🎯”" +" and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " +"to “🎲”" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:29 +msgid "Protects the contents of the sent message from forwarding" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_dice:32 +msgid "" +"The identifier of a message thread, unique within the chat to which the " +"message with the thread identifier belongs" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:1 +msgid "Use this method to send general files." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddocument" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:8 +msgid "" +"(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" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:15 +msgid "" +"Document caption (may also be used when resending documents by file_id), " +"0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:23 +msgid "Mode for parsing entities in the document caption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:32 +msgid "" +"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 " +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:41 +msgid "" +"allows to define file name that will be visible in the Telegram instead " +"of original file name" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:44 +msgid "" +"Disables automatic server-side content type detection for files uploaded " +"using multipart/form-data" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_document:47 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:33 +#: telebot.async_telebot.AsyncTeleBot.send_video:55 +msgid "function typo miss compatibility: do not use it" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:1 +msgid "Used to send the game." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendgame" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:8 +msgid "" +"Short name of the game, serves as the unique identifier for the game. Set" +" up your games via @BotFather." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:20 +msgid "Timeout in seconds for waiting for a response from the bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:26 +msgid "" +"Pass True, if content of the message needs to be protected from being " +"viewed by the bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:29 +msgid "Identifier of the thread to which the message will be sent." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_game:33 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:102 +#: telebot.async_telebot.AsyncTeleBot.send_poll:71 +msgid ":obj:`types.Message`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:1 +msgid "Sends invoice." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendinvoice" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:5 +msgid "Unique identifier for the target private chat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:29 +msgid "" +"Unique deep-linking parameter that can be used to generate this invoice " +"when used as a start parameter" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:33 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a marketing image for a service. People like it better when they see what" +" they are paying for." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:73 +msgid "" +"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" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:81 +#: telebot.async_telebot.AsyncTeleBot.set_webhook:39 +msgid "Timeout of a request, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_invoice:98 +msgid "" +"The identifier of a message thread, in which the invoice message will be " +"sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:1 +msgid "" +"Use this method to send point on the map. On success, the sent Message is" +" returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendlocation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:8 +msgid "Latitude of the location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:11 +msgid "Longitude of the location" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:14 +msgid "" +"Period in seconds for which the location will be updated (see Live " +"Locations, should be between 60 and 86400." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:34 +msgid "" +"For live locations, a direction in which the user is moving, in degrees. " +"Must be between 1 and 360 if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_location:37 +msgid "" +"For live locations, a maximum distance for proximity alerts about " +"approaching another chat member, in meters. Must be between 1 and 100000 " +"if specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:1 +msgid "" +"Use this method to send a group of photos, videos, documents or audios as" +" an album. Documents and audio files can be only grouped in an album with" +" messages of the same type. On success, an array of Messages that were " +"sent is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:9 +msgid "" +"A JSON-serialized array describing messages to be sent, must include 2-10" +" items" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:12 +msgid "" +"Sends the messages silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:27 +msgid "Identifier of a message thread, in which the messages will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:30 +msgid "On success, an array of Messages that were sent is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:1 +msgid "Use this method to send text messages." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:3 +msgid "" +"Warning: Do not send more than about 4096 characters each message, " +"otherwise you'll risk an HTTP 414 error. If you must send more than 4096 " +"characters, use the `split_string` or `smart_split` function in util.py." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:12 +msgid "Text of the message to be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:18 +msgid "" +"List of special entities that appear in message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_message:27 +msgid "" +"If True, the message content will be hidden for all users except for the " +"target user" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:1 +msgid "Use this method to send photos. On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:8 +msgid "" +"Photo to send. Pass a file_id as String to send a photo that exists on " +"the Telegram servers (recommended), pass an HTTP URL as a String for " +"Telegram to get a photo from the Internet, or upload a new photo using " +"multipart/form-data. The photo must be at most 10 MB in size. The photo's" +" width and height must not exceed 10000 in total. Width and height ratio " +"must be at most 20." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:13 +msgid "" +"Photo caption (may also be used when resending photos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_photo:16 +msgid "Mode for parsing entities in the photo caption." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:1 +msgid "" +"Use this method to send a native poll. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:9 +msgid "Poll question, 1-300 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:12 +msgid "" +"A JSON-serialized list of answer options, 2-10 strings 1-100 characters " +"each" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:15 +msgid "True, if the poll needs to be anonymous, defaults to True" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:18 +msgid "Poll type, “quiz” or “regular”, defaults to “regular”" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:21 +msgid "" +"True, if the poll allows multiple answers, ignored for polls in quiz " +"mode, defaults to False" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:24 +msgid "" +"0-based identifier of the correct answer option. Available only for polls" +" in quiz mode, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:28 +msgid "" +"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" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:32 +msgid "" +"Mode for parsing entities in the explanation. See formatting options for " +"more details." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:35 +msgid "" +"Amount of time in seconds the poll will be active after creation, 5-600. " +"Can't be used together with close_date." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:38 +msgid "Point in time (Unix timestamp) when the poll will be automatically closed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:41 +msgid "" +"Pass True, if the poll needs to be immediately closed. This can be useful" +" for poll preview." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:50 +msgid "Pass True, if the poll allows multiple options to be voted simultaneously." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:57 +msgid "Timeout in seconds for waiting for a response from the user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:60 +msgid "" +"A JSON-serialized list of special entities that appear in the " +"explanation, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_poll:67 +msgid "The identifier of a message thread, in which the poll will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:1 +msgid "" +"Use this method to send static .WEBP, animated .TGS, or video .WEBM " +"stickers. On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendsticker" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:9 +msgid "" +"Sticker 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 .webp file from the Internet, or upload a new one using" +" multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:21 +msgid "to disable the notification" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:1 +msgid "" +"Use this method to send information about a venue. On success, the sent " +"Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:8 +msgid "Latitude of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:11 +msgid "Longitude of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:14 +msgid "Name of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:17 +msgid "Address of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:20 +msgid "Foursquare identifier of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:23 +msgid "" +"Foursquare type of the venue, if known. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:45 +msgid "Google Places identifier of the venue" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_venue:48 +msgid "Google Places type of the venue." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:1 +msgid "" +"Use this method to send video files, Telegram clients support mp4 videos " +"(other formats may be sent as Document)." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:8 +msgid "" +"Video to send. You can either pass a file_id as String to resend a video " +"that is already on the Telegram servers, or upload a new video file using" +" multipart/form-data." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:11 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:13 +msgid "Duration of sent video in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:14 +msgid "Video width" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:17 +msgid "Video height" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:23 +msgid "" +"Video caption (may also be used when resending videos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:26 +msgid "Mode for parsing entities in the video caption" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video:32 +msgid "Pass True, if the uploaded video is suitable for streaming" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:1 +msgid "" +"As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " +"to 1 minute long. Use this method to send video messages. On success, the" +" sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:9 +msgid "" +"Video note to send. Pass a file_id as String to send a video note that " +"exists on the Telegram servers (recommended) or upload a new video using " +"multipart/form-data. Sending video notes by a URL is currently " +"unsupported" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:16 +msgid "Video width and height, i.e. diameter of the video message" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_video_note:45 +msgid "Identifier of a message thread, in which the video note will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:10 +msgid "" +"Audio 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." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:14 +msgid "Voice message caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:17 +msgid "Duration of the voice message in seconds" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.send_voice:28 +msgid "" +"Mode for parsing entities in the voice message caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:1 +msgid "" +"Use this method to set a custom title for an administrator in a " +"supergroup promoted by the bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatadministratorcustomtitle" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:13 +msgid "" +"New custom title for the administrator; 0-16 characters, emoji are not " +"allowed" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_description:1 +msgid "" +"Use this method to change the description of a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_description:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatdescription" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_description:10 +msgid "Str: New chat description, 0-255 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:1 +msgid "" +"Use this method to change the bot's menu button in a private chat, or the" +" default menu button. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatmenubutton" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be changed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:11 +msgid "" +"A JSON-serialized object for the new bot's menu button. Defaults to " +"MenuButtonDefault" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:1 +msgid "" +"Use this method to set default chat permissions for all members. The bot " +"must be an administrator in the group or a supergroup for this to work " +"and must have the can_restrict_members admin rights." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatpermissions" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:11 +msgid "New default chat permissions" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:1 +msgid "" +"Use this method to set a new profile photo for the chat. Photos can't be " +"changed for private chats. The bot must be an administrator in the chat " +"for this to work and must have the appropriate admin rights. Returns True" +" on success. Note: In regular groups (non-supergroups), this method will " +"only work if the ‘All Members Are Admins’ setting is off in the target " +"group." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:13 +msgid "InputFile: New chat photo, uploaded using multipart/form-data" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:1 +msgid "" +"Use this method to set a new group sticker set for a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. Use the field can_set_sticker_set " +"optionally returned in getChat requests to check if the bot can use this " +"method. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatstickerset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:10 +msgid "Name of the sticker set to be set as the group sticker set" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:13 +msgid "StickerSet object" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_title:1 +msgid "" +"Use this method to change the title of a chat. Titles can't be changed " +"for private chats. The bot must be an administrator in the chat for this " +"to work and must have the appropriate admin rights. Returns True on " +"success. Note: In regular groups (non-supergroups), this method will only" +" work if the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_title:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_chat_title:13 +msgid "New chat title, 1-255 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:1 +msgid "Sets the value of points in the game to a specific user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setgamescore" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:8 +msgid "New score, must be non-negative" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:11 +msgid "" +"Pass True, if the high score is allowed to decrease. This can be useful " +"when fixing mistakes or banning cheaters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:23 +msgid "" +"Pass True, if the game message should not be automatically edited to " +"include the current scoreboard" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_game_score:26 +msgid "" +"On success, if the message was sent by the bot, returns the edited " +"Message, otherwise returns True." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_commands:1 +msgid "Use this method to change the list of the bot's commands." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_commands:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmycommands" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_commands:5 +msgid "List of BotCommand. At most 100 commands can be specified." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:1 +msgid "" +"Use this method to change the default administrator rights requested by " +"the bot when it's added as an administrator to groups or channels. These " +"rights will be suggested to users, but they are are free to modify the " +"list before adding the bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:7 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmydefaultadministratorrights" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:9 +msgid "" +"A JSON-serialized object describing new default administrator rights. If " +"not specified, the default administrator rights will be cleared." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:13 +msgid "" +"Pass True to change the default administrator rights of the bot in " +"channels. Otherwise, the default administrator rights of the bot for " +"groups and supergroups will be changed." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_state:1 +msgid "Sets a new state of a user." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_state:5 +msgid "" +"You should set both user id and chat id in order to set state for a user " +"in a chat. Otherwise, if you only set user_id, chat_id will equal to " +"user_id, this means that state will be set for the user in his private " +"chat with a bot." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_state:12 +msgid "new state. can be string, integer, or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:1 +msgid "" +"Use this method to move a sticker in a set created by the bot to a " +"specific position . Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickerpositioninset" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:8 +msgid "New sticker position in the set, zero-based" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:1 +msgid "" +"Use this method to set the thumbnail of a sticker set. Animated " +"thumbnails can be set for animated sticker sets only. Returns True on " +"success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickersetthumb" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_update_listener:1 +msgid "Update listener is a function that gets any update." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_update_listener:3 +msgid "function that should get update." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_update_listener:6 +msgid "Example on asynchronous update listeners." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:1 +msgid "" +"Use this method to specify a URL and receive incoming updates via an " +"outgoing webhook. Whenever there is an update for the bot, we will send " +"an HTTPS POST request to the specified URL, containing a JSON-serialized " +"Update. In case of an unsuccessful request, we will give up after a " +"reasonable amount of attempts. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:6 +msgid "" +"If you'd like to make sure that the webhook was set by you, you can " +"specify secret data in the parameter secret_token. If specified, the " +"request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the " +"secret token as content." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:9 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#setwebhook" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:11 +msgid "" +"HTTPS URL to send updates to. Use an empty string to remove webhook " +"integration, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:14 +msgid "" +"Upload your public key certificate so that the root certificate in use " +"can be checked, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:17 +msgid "" +"The maximum allowed number of simultaneous HTTPS connections to the " +"webhook for update delivery, 1-100. Defaults to 40. Use lower values to " +"limit the load on your bot's server, and higher values to increase your " +"bot's throughput, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. Please note that this parameter doesn't affect " +"updates created before the call to the setWebhook, so unwanted updates " +"may be received for a short period of time. Defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:27 +msgid "" +"Please note that this parameter doesn't affect updates created before the" +" call to the setWebhook, so unwanted updates may be received for a short " +"period of time. Defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:32 +msgid "" +"The fixed IP address which will be used to send webhook requests instead " +"of the IP address resolved through DNS, defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:42 +msgid "" +"A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” " +"in every webhook request, 1-256 characters. Only characters A-Z, a-z, " +"0-9, _ and - are allowed. The header is useful to ensure that the request" +" comes from a webhook set by you. Defaults to None" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_webhook:47 +msgid ":obj:`bool` if the request was successful." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.setup_middleware:1 +msgid "Setup middleware." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.setup_middleware:5 +msgid "" +"Take a look at the " +":class:`telebot.asyncio_handler_backends.BaseMiddleware` section for " +"more." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.setup_middleware:7 +msgid "Middleware-class." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.shipping_query_handler:1 +msgid "" +"Handles new incoming shipping query. Only for invoices with flexible " +"price. As a parameter to the decorator function, it passes " +":class:`telebot.types.ShippingQuery` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.skip_updates:1 +msgid "Skip existing updates. Only last update will remain on server." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:1 +msgid "" +"Use this method to stop updating a live location message before " +"live_period expires. On success, if the message is not an inline message," +" the edited Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#stopmessagelivelocation" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:9 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" with live location to stop" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:12 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message with live location to stop" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:22 +msgid "" +"On success, if the message is not an inline message, the edited Message " +"is returned, otherwise True is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:1 +msgid "" +"Use this method to stop a poll which was sent by the bot. On success, the" +" stopped Poll is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:8 +msgid "Identifier of the original message with the poll" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:11 +msgid "A JSON-serialized object for a new message markup." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:14 +msgid "On success, the stopped Poll is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.stop_poll:15 +msgid ":obj:`types.Poll`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:1 +msgid "" +"Use this method to unban a previously kicked user in a supergroup or " +"channel. The user will not return to the group or channel automatically, " +"but will be able to join via link, etc. The bot must be an administrator " +"for this to work. By default, this method guarantees that after the call " +"the user is not a member of the chat, but will be able to join it. So if " +"the user is a member of the chat they will also be removed from the chat." +" If you don't want this, use the parameter only_if_banned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:9 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @username)" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:16 +msgid "Do nothing if the user is not banned" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:1 +msgid "" +"Use this method to unban a previously banned channel chat in a supergroup" +" or channel. The bot must be an administrator for this to work and must " +"have the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unbanchatsenderchat" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:11 +msgid "Unique identifier of the target sender chat." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:1 +msgid "" +"Use this method to unpin a all pinned messages in a supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallchatmessages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:1 +msgid "" +"Use this method to clear the list of pinned messages in a forum topic. " +"The bot must be an administrator in the chat for this to work and must " +"have the can_pin_messages administrator right in the supergroup. Returns " +"True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:10 +msgid "Identifier of the topic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:1 +msgid "" +"Use this method to unpin specific pinned message in a supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinchatmessage" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:11 +msgid "Int: Identifier of a message to unpin" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:1 +msgid "" +"Use this method to upload a .png file with a sticker for later use in " +"createNewStickerSet and addStickerToSet methods (can be used multiple " +"times). Returns the uploaded File on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#uploadstickerfile" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:6 +msgid "User identifier of sticker set owner" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:9 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:13 +msgid "On success, the sent file is returned." +msgstr "" + +#: of telebot.async_telebot.ExceptionHandler:1 +msgid "Class for handling exceptions while Polling" +msgstr "" + +#: of telebot.async_telebot.Handler:1 +msgid "Class for (next step|reply) handlers" +msgstr "" + +#: ../../async_version/index.rst:22 +msgid "Asyncio filters" +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:1 +#: telebot.asyncio_filters.SimpleCustomFilter:1 +msgid "Bases: :py:class:`abc.ABC`" +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:1 +msgid "" +"Advanced Custom Filter base class. Create child class with check() " +"method. Accepts two parameters, returns bool: True - filter passed, False" +" - filter failed. message: Message class text: Filter value given in " +"handler" +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:7 +#: telebot.asyncio_filters.SimpleCustomFilter:5 +msgid "Child classes should have .key property." +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter:9 +msgid "Example on creating an advanced custom filter." +msgstr "" + +#: of telebot.asyncio_filters.AdvancedCustomFilter.check:1 +#: telebot.asyncio_filters.SimpleCustomFilter.check:1 +msgid "Perform a check." +msgstr "" + +#: of telebot.asyncio_filters.ChatFilter:1 +#: telebot.asyncio_filters.LanguageFilter:1 +#: telebot.asyncio_filters.StateFilter:1 +#: telebot.asyncio_filters.TextContainsFilter:1 +#: telebot.asyncio_filters.TextMatchFilter:1 +#: telebot.asyncio_filters.TextStartsFilter:1 +msgid "Bases: :py:class:`telebot.asyncio_filters.AdvancedCustomFilter`" +msgstr "" + +#: of telebot.asyncio_filters.ChatFilter:1 +msgid "Check whether chat_id corresponds to given chat_id." +msgstr "" + +#: of telebot.asyncio_filters.ChatFilter:3 +#: telebot.asyncio_filters.ForwardFilter:3 +#: telebot.asyncio_filters.IsAdminFilter:3 +#: telebot.asyncio_filters.IsDigitFilter:3 +#: telebot.asyncio_filters.IsReplyFilter:3 +#: telebot.asyncio_filters.LanguageFilter:3 +#: telebot.asyncio_filters.StateFilter:3 +#: telebot.asyncio_filters.TextContainsFilter:5 +#: telebot.asyncio_filters.TextMatchFilter:3 +#: telebot.asyncio_filters.TextStartsFilter:3 +msgid "Example on using this filter:" +msgstr "" + +#: of telebot.asyncio_filters.ForwardFilter:1 +#: telebot.asyncio_filters.IsAdminFilter:1 +#: telebot.asyncio_filters.IsDigitFilter:1 +#: telebot.asyncio_filters.IsReplyFilter:1 +msgid "Bases: :py:class:`telebot.asyncio_filters.SimpleCustomFilter`" +msgstr "" + +#: of telebot.asyncio_filters.ForwardFilter:1 +msgid "Check whether message was forwarded from channel or group." +msgstr "" + +#: of telebot.asyncio_filters.IsAdminFilter:1 +msgid "Check whether the user is administrator / owner of the chat." +msgstr "" + +#: of telebot.asyncio_filters.IsDigitFilter:1 +msgid "Filter to check whether the string is made up of only digits." +msgstr "" + +#: of telebot.asyncio_filters.IsReplyFilter:1 +msgid "Check whether message is a reply." +msgstr "" + +#: of telebot.asyncio_filters.LanguageFilter:1 +msgid "Check users language_code." +msgstr "" + +#: of telebot.asyncio_filters.SimpleCustomFilter:1 +msgid "" +"Simple Custom Filter base class. Create child class with check() method. " +"Accepts only message, returns bool value, that is compared with given in " +"handler." +msgstr "" + +#: of telebot.asyncio_filters.SimpleCustomFilter:7 +msgid "Example on creating a simple custom filter." +msgstr "" + +#: of telebot.asyncio_filters.StateFilter:1 +msgid "Filter to check state." +msgstr "" + +#: of telebot.asyncio_filters.TextContainsFilter:1 +msgid "Filter to check Text message. key: text" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:1 +msgid "" +"Advanced text filter to check (types.Message, types.CallbackQuery, " +"types.InlineQuery, types.Poll)" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:3 +msgid "" +"example of usage is in " +"examples/asynchronous_telebot/custom_filters/advanced_text_filter.py" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:5 +msgid "string, True if object's text is equal to passed string" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:8 +msgid "list[str] or tuple[str], True if any string element of iterable is in text" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:11 +#: telebot.asyncio_filters.TextFilter:14 +msgid "string, True if object's text starts with passed string" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:17 +msgid "bool (default False), case insensitive" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter +msgid "Raises" +msgstr "" + +#: of telebot.asyncio_filters.TextFilter:20 +msgid "if incorrect value for a parameter was supplied" +msgstr "" + +#: of telebot.asyncio_filters.TextMatchFilter:1 +msgid "Filter to check Text message." +msgstr "" + +#: of telebot.asyncio_filters.TextStartsFilter:1 +msgid "Filter to check whether message starts with some text." +msgstr "" + +#: ../../async_version/index.rst:30 +msgid "Asyncio handler backends" +msgstr "" + +#: of telebot.asyncio_handler_backends:1 +msgid "File with all middleware classes, states." +msgstr "" + +#: of telebot.asyncio_handler_backends.BaseMiddleware:1 +msgid "" +"Base class for middleware. Your middlewares should be inherited from this" +" class." +msgstr "" + +#: of telebot.asyncio_handler_backends.BaseMiddleware:4 +msgid "" +"Set update_sensitive=True if you want to get different updates on " +"different functions. For example, if you want to handle pre_process for " +"message update, then you will have to create pre_process_message " +"function, and so on. Same applies to post_process." +msgstr "" + +#: of telebot.asyncio_handler_backends.BaseMiddleware:9 +msgid "Example of class-based middlewares" +msgstr "" + +#: of telebot.asyncio_handler_backends.CancelUpdate:1 +msgid "" +"Class for canceling updates. Just return instance of this class in " +"middleware to skip update. Update will skip handler and execution of " +"post_process in middlewares." +msgstr "" + +#: of telebot.asyncio_handler_backends.ContinueHandling:1 +msgid "" +"Class for continue updates in handlers. Just return instance of this " +"class in handlers to continue process." +msgstr "" + +#: of telebot.asyncio_handler_backends.ContinueHandling:5 +msgid "Example of using ContinueHandling" +msgstr "" + +#: of telebot.asyncio_handler_backends.SkipHandler:1 +msgid "" +"Class for skipping handlers. Just return instance of this class in " +"middleware to skip handler. Update will go to post_process, but will skip" +" execution of handler." +msgstr "" + +#: of telebot.asyncio_handler_backends.State:1 +msgid "Class representing a state." +msgstr "" + +#: of telebot.asyncio_handler_backends.StatesGroup:1 +msgid "Class representing common states." +msgstr "" + +#: ../../async_version/index.rst:41 +msgid "Extensions" +msgstr "" + +#: of telebot.ext.aio.webhooks:1 +msgid "This file is used by AsyncTeleBot.run_webhooks() function." +msgstr "" + +#: of telebot.ext.aio.webhooks:3 +msgid "Fastapi and starlette(0.20.2+) libraries are required to run this script." +msgstr "" + +#: of telebot.ext.aio.webhooks.AsyncWebhookListener.run_app:1 +msgid "" +"Run app with the given parameters to init. Not supposed to be used " +"manually by user." +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/calldata.po b/docs/source/locales/ru/LC_MESSAGES/calldata.po new file mode 100644 index 000000000..03484e874 --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/calldata.po @@ -0,0 +1,127 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../calldata.rst:4 +msgid "Callback data factory" +msgstr "" + +#: ../../calldata.rst:6 +msgid "Callback data factory in pyTelegramBotAPI" +msgstr "" + +#: ../../calldata.rst:6 +msgid "" +"ptba, pytba, pyTelegramBotAPI, callbackdatafactory, guide, callbackdata, " +"factory" +msgstr "" + +#: ../../calldata.rst:12 +msgid "callback\\_data file" +msgstr "" + +#: of telebot.callback_data:1 +msgid "Callback data factory's file." +msgstr "" + +#: of telebot.callback_data.CallbackData:1 +#: telebot.callback_data.CallbackDataFilter:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.callback_data.CallbackData:1 +msgid "Callback data factory This class will help you to work with CallbackQuery" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter:1 +msgid "Generate filter" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter +#: telebot.callback_data.CallbackData.new +#: telebot.callback_data.CallbackData.parse +#: telebot.callback_data.CallbackDataFilter.check +msgid "Parameters" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter:3 +msgid "specified named parameters will be checked with CallbackQuery.data" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter +#: telebot.callback_data.CallbackData.new +#: telebot.callback_data.CallbackData.parse +#: telebot.callback_data.CallbackDataFilter.check +msgid "Returns" +msgstr "" + +#: of telebot.callback_data.CallbackData.filter:4 +msgid "CallbackDataFilter class" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:1 +msgid "Generate callback data" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:3 +msgid "positional parameters of CallbackData instance parts" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:4 +msgid "named parameters" +msgstr "" + +#: of telebot.callback_data.CallbackData.new:5 +msgid "str" +msgstr "" + +#: of telebot.callback_data.CallbackData.parse:1 +msgid "Parse data from the callback data" +msgstr "" + +#: of telebot.callback_data.CallbackData.parse:3 +msgid "" +"string, use to telebot.types.CallbackQuery to parse it from string to a " +"dict" +msgstr "" + +#: of telebot.callback_data.CallbackData.parse:4 +msgid "dict parsed from callback data" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter:1 +msgid "Filter for CallbackData." +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check:1 +msgid "Checks if query.data appropriates to specified config" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check:3 +msgid "telebot.types.CallbackQuery" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check:6 +msgid "True if query.data appropriates to specified config" +msgstr "" + +#: of telebot.callback_data.CallbackDataFilter.check +msgid "Return type" +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/formatting.po b/docs/source/locales/ru/LC_MESSAGES/formatting.po new file mode 100644 index 000000000..0f6eff001 --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/formatting.po @@ -0,0 +1,251 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../formatting.rst:3 +msgid "Formatting options" +msgstr "" + +#: ../../formatting.rst:5 +msgid "Formatting options in pyTelegramBotAPI" +msgstr "" + +#: ../../formatting.rst:5 +msgid "html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI" +msgstr "" + +#: of telebot.formatting:1 +msgid "Markdown & HTML formatting functions." +msgstr "" + +#: of telebot.formatting.escape_html:1 +msgid "Escapes HTML characters in a string of HTML." +msgstr "" + +#: of telebot.formatting.escape_html telebot.formatting.escape_markdown +#: telebot.formatting.format_text telebot.formatting.hbold +#: telebot.formatting.hcode telebot.formatting.hide_link +#: telebot.formatting.hitalic telebot.formatting.hlink telebot.formatting.hpre +#: telebot.formatting.hspoiler telebot.formatting.hstrikethrough +#: telebot.formatting.hunderline telebot.formatting.mbold +#: telebot.formatting.mcode telebot.formatting.mitalic telebot.formatting.mlink +#: telebot.formatting.mspoiler telebot.formatting.mstrikethrough +#: telebot.formatting.munderline +msgid "Parameters" +msgstr "" + +#: of telebot.formatting.escape_html:3 +msgid "The string of HTML to escape." +msgstr "" + +#: of telebot.formatting.escape_html telebot.formatting.escape_markdown +#: telebot.formatting.format_text telebot.formatting.hbold +#: telebot.formatting.hcode telebot.formatting.hide_link +#: telebot.formatting.hitalic telebot.formatting.hlink telebot.formatting.hpre +#: telebot.formatting.hspoiler telebot.formatting.hstrikethrough +#: telebot.formatting.hunderline telebot.formatting.mbold +#: telebot.formatting.mcode telebot.formatting.mitalic telebot.formatting.mlink +#: telebot.formatting.mspoiler telebot.formatting.mstrikethrough +#: telebot.formatting.munderline +msgid "Returns" +msgstr "" + +#: of telebot.formatting.escape_html:6 telebot.formatting.escape_markdown:8 +msgid "The escaped string." +msgstr "" + +#: of telebot.formatting.escape_html telebot.formatting.escape_markdown +#: telebot.formatting.format_text telebot.formatting.hbold +#: telebot.formatting.hcode telebot.formatting.hide_link +#: telebot.formatting.hitalic telebot.formatting.hlink telebot.formatting.hpre +#: telebot.formatting.hspoiler telebot.formatting.hstrikethrough +#: telebot.formatting.hunderline telebot.formatting.mbold +#: telebot.formatting.mcode telebot.formatting.mitalic telebot.formatting.mlink +#: telebot.formatting.mspoiler telebot.formatting.mstrikethrough +#: telebot.formatting.munderline +msgid "Return type" +msgstr "" + +#: of telebot.formatting.escape_html:7 telebot.formatting.escape_markdown:9 +#: telebot.formatting.format_text:17 telebot.formatting.hbold:10 +#: telebot.formatting.hcode:10 telebot.formatting.hide_link:7 +#: telebot.formatting.hitalic:10 telebot.formatting.hlink:13 +#: telebot.formatting.hpre:10 telebot.formatting.hspoiler:10 +#: telebot.formatting.hstrikethrough:10 telebot.formatting.hunderline:10 +#: telebot.formatting.mbold:10 telebot.formatting.mcode:10 +#: telebot.formatting.mitalic:10 telebot.formatting.mlink:13 +#: telebot.formatting.mspoiler:10 telebot.formatting.mstrikethrough:10 +#: telebot.formatting.munderline:10 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.formatting.escape_markdown:1 +msgid "Escapes Markdown characters in a string of Markdown." +msgstr "" + +#: of telebot.formatting.escape_markdown:3 +msgid "Credits to: simonsmh" +msgstr "" + +#: of telebot.formatting.escape_markdown:5 +msgid "The string of Markdown to escape." +msgstr "" + +#: of telebot.formatting.format_text:1 +msgid "Formats a list of strings into a single string." +msgstr "" + +#: of telebot.formatting.format_text:10 +msgid "Strings to format." +msgstr "" + +#: of telebot.formatting.format_text:13 +msgid "The separator to use between each string." +msgstr "" + +#: of telebot.formatting.format_text:16 telebot.formatting.hbold:9 +#: telebot.formatting.hcode:9 telebot.formatting.hitalic:9 +#: telebot.formatting.hlink:12 telebot.formatting.hpre:9 +#: telebot.formatting.hspoiler:9 telebot.formatting.hstrikethrough:9 +#: telebot.formatting.hunderline:9 telebot.formatting.mbold:9 +#: telebot.formatting.mcode:9 telebot.formatting.mitalic:9 +#: telebot.formatting.mlink:12 telebot.formatting.mspoiler:9 +#: telebot.formatting.mstrikethrough:9 telebot.formatting.munderline:9 +msgid "The formatted string." +msgstr "" + +#: of telebot.formatting.hbold:1 +msgid "Returns an HTML-formatted bold string." +msgstr "" + +#: of telebot.formatting.hbold:3 telebot.formatting.mbold:3 +msgid "The string to bold." +msgstr "" + +#: of telebot.formatting.hbold:6 telebot.formatting.hcode:6 +#: telebot.formatting.hitalic:6 telebot.formatting.hlink:9 +#: telebot.formatting.hpre:6 telebot.formatting.hspoiler:6 +#: telebot.formatting.hstrikethrough:6 telebot.formatting.hunderline:6 +#: telebot.formatting.mbold:6 telebot.formatting.mcode:6 +#: telebot.formatting.mitalic:6 telebot.formatting.mlink:9 +#: telebot.formatting.mspoiler:6 telebot.formatting.mstrikethrough:6 +#: telebot.formatting.munderline:6 +msgid "True if you need to escape special characters. Defaults to True." +msgstr "" + +#: of telebot.formatting.hcode:1 +msgid "Returns an HTML-formatted code string." +msgstr "" + +#: of telebot.formatting.hcode:3 telebot.formatting.mcode:3 +msgid "The string to code." +msgstr "" + +#: of telebot.formatting.hide_link:1 +msgid "Hide url of an image." +msgstr "" + +#: of telebot.formatting.hide_link:3 +msgid "The url of the image." +msgstr "" + +#: of telebot.formatting.hide_link:6 +msgid "The hidden url." +msgstr "" + +#: of telebot.formatting.hitalic:1 +msgid "Returns an HTML-formatted italic string." +msgstr "" + +#: of telebot.formatting.hitalic:3 telebot.formatting.mitalic:3 +msgid "The string to italicize." +msgstr "" + +#: of telebot.formatting.hlink:1 +msgid "Returns an HTML-formatted link string." +msgstr "" + +#: of telebot.formatting.hlink:3 telebot.formatting.mlink:3 +msgid "The string to link." +msgstr "" + +#: of telebot.formatting.hlink:6 telebot.formatting.mlink:6 +msgid "The URL to link to." +msgstr "" + +#: of telebot.formatting.hpre:1 +msgid "Returns an HTML-formatted preformatted string." +msgstr "" + +#: of telebot.formatting.hpre:3 +msgid "The string to preformatted." +msgstr "" + +#: of telebot.formatting.hspoiler:1 +msgid "Returns an HTML-formatted spoiler string." +msgstr "" + +#: of telebot.formatting.hspoiler:3 telebot.formatting.mspoiler:3 +msgid "The string to spoiler." +msgstr "" + +#: of telebot.formatting.hstrikethrough:1 +msgid "Returns an HTML-formatted strikethrough string." +msgstr "" + +#: of telebot.formatting.hstrikethrough:3 telebot.formatting.mstrikethrough:3 +msgid "The string to strikethrough." +msgstr "" + +#: of telebot.formatting.hunderline:1 +msgid "Returns an HTML-formatted underline string." +msgstr "" + +#: of telebot.formatting.hunderline:3 telebot.formatting.munderline:3 +msgid "The string to underline." +msgstr "" + +#: of telebot.formatting.mbold:1 +msgid "Returns a Markdown-formatted bold string." +msgstr "" + +#: of telebot.formatting.mcode:1 +msgid "Returns a Markdown-formatted code string." +msgstr "" + +#: of telebot.formatting.mitalic:1 +msgid "Returns a Markdown-formatted italic string." +msgstr "" + +#: of telebot.formatting.mlink:1 +msgid "Returns a Markdown-formatted link string." +msgstr "" + +#: of telebot.formatting.mspoiler:1 +msgid "Returns a Markdown-formatted spoiler string." +msgstr "" + +#: of telebot.formatting.mstrikethrough:1 +msgid "Returns a Markdown-formatted strikethrough string." +msgstr "" + +#: of telebot.formatting.munderline:1 +msgid "Returns a Markdown-formatted underline string." +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/index.po b/docs/source/locales/ru/LC_MESSAGES/index.po new file mode 100644 index 000000000..38a49544f --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/index.po @@ -0,0 +1,120 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../index.rst:8 +msgid "Welcome to pyTelegramBotAPI's documentation!" +msgstr "" + +#: ../../index.rst:10 +msgid "Official documentation of pyTelegramBotAPI" +msgstr "" + +#: ../../index.rst:10 +msgid "ptba, pytba, pyTelegramBotAPI, documentation, guide" +msgstr "" + +#: ../../index.rst:17 +msgid "TeleBot" +msgstr "" + +#: ../../index.rst:18 +msgid "" +"TeleBot is synchronous and asynchronous implementation of `Telegram Bot " +"API `_." +msgstr "" + +#: ../../index.rst:21 +msgid "Chats" +msgstr "" + +#: ../../index.rst:22 +msgid "" +"English chat: `Private chat " +"`__" +msgstr "" + +#: ../../index.rst:24 +msgid "" +"Russian chat: `@pytelegrambotapi_talks_ru " +"`__" +msgstr "" + +#: ../../index.rst:26 +msgid "News: `@pyTelegramBotAPI `__" +msgstr "" + +#: ../../index.rst:28 +msgid "Pypi: `Pypi `__" +msgstr "" + +#: ../../index.rst:30 +msgid "" +"Source: `Github repository " +"`__" +msgstr "" + +#: ../../index.rst:33 +msgid "Some features:" +msgstr "" + +#: ../../index.rst:34 +msgid "Easy to learn and use." +msgstr "" + +#: ../../index.rst:36 +msgid "Easy to understand." +msgstr "" + +#: ../../index.rst:38 +msgid "Both sync and async." +msgstr "" + +#: ../../index.rst:40 +msgid "Examples on features." +msgstr "" + +#: ../../index.rst:42 +msgid "States" +msgstr "" + +#: ../../index.rst:44 +msgid "And more..." +msgstr "" + +#: ../../index.rst:47 +msgid "Content" +msgstr "" + +#: ../../index.rst:63 +msgid "Indices and tables" +msgstr "" + +#: ../../index.rst:65 +msgid ":ref:`genindex`" +msgstr "" + +#: ../../index.rst:66 +msgid ":ref:`modindex`" +msgstr "" + +#: ../../index.rst:67 +msgid ":ref:`search`" +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/install.po b/docs/source/locales/ru/LC_MESSAGES/install.po new file mode 100644 index 000000000..0f4a3b386 --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/install.po @@ -0,0 +1,58 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../install.rst:3 +msgid "Installation Guide" +msgstr "" + +#: ../../install.rst:5 +msgid "Installation of pyTelegramBotAPI" +msgstr "" + +#: ../../install.rst:5 +msgid "ptba, pytba, pyTelegramBotAPI, installation, guide" +msgstr "" + +#: ../../install.rst:11 +msgid "Using PIP" +msgstr "" + +#: ../../install.rst:17 +msgid "Using pipenv" +msgstr "" + +#: ../../install.rst:23 +msgid "By cloning repository" +msgstr "" + +#: ../../install.rst:31 +msgid "Directly using pip" +msgstr "" + +#: ../../install.rst:37 +msgid "It is generally recommended to use the first option." +msgstr "" + +#: ../../install.rst:39 +msgid "" +"While the API is production-ready, it is still under development and it " +"has regular updates, do not forget to update it regularly by calling:" +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/quick_start.po b/docs/source/locales/ru/LC_MESSAGES/quick_start.po new file mode 100644 index 000000000..9e7c22fbb --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/quick_start.po @@ -0,0 +1,40 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../quick_start.rst:4 +msgid "Quick start" +msgstr "" + +#: ../../quick_start.rst:6 +msgid "Quickstart guide" +msgstr "" + +#: ../../quick_start.rst:6 +msgid "ptba, pytba, pyTelegramBotAPI, quickstart, guide" +msgstr "" + +#: ../../quick_start.rst:11 +msgid "Synchronous TeleBot" +msgstr "" + +#: ../../quick_start.rst:16 +msgid "Asynchronous TeleBot" +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po new file mode 100644 index 000000000..1c91ece19 --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -0,0 +1,4805 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../sync_version/index.rst:3 +msgid "TeleBot version" +msgstr "" + +#: ../../sync_version/index.rst:5 +msgid "Synchronous pyTelegramBotAPI documentation" +msgstr "" + +#: ../../sync_version/index.rst:5 +msgid "ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync" +msgstr "" + +#: ../../sync_version/index.rst:10 +msgid "TeleBot methods" +msgstr "" + +#: of telebot.ExceptionHandler:1 telebot.Handler:1 telebot.TeleBot:1 +#: telebot.custom_filters.TextFilter:1 +#: telebot.ext.sync.webhooks.SyncWebhookListener:1 +#: telebot.handler_backends.BaseMiddleware:1 +#: telebot.handler_backends.CancelUpdate:1 +#: telebot.handler_backends.ContinueHandling:1 +#: telebot.handler_backends.SkipHandler:1 telebot.handler_backends.State:1 +#: telebot.handler_backends.StatesGroup:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.ExceptionHandler:1 +msgid "Class for handling exceptions while Polling" +msgstr "" + +#: of telebot.Handler:1 +msgid "Class for (next step|reply) handlers." +msgstr "" + +#: ../../docstring of telebot.REPLY_MARKUP_TYPES:1 +msgid "telebot" +msgstr "" + +#: ../../docstring of telebot.REPLY_MARKUP_TYPES +msgid "type" +msgstr "" + +#: ../../docstring of telebot.REPLY_MARKUP_TYPES:3 +msgid "Module" +msgstr "" + +#: of telebot.TeleBot:1 +msgid "This is the main synchronous class for Bot." +msgstr "" + +#: of telebot.TeleBot:3 +msgid "It allows you to add handlers for different kind of updates." +msgstr "" + +#: of telebot.TeleBot:5 +msgid "Usage:" +msgstr "" + +#: of telebot.TeleBot:7 +msgid "Creating instance of TeleBot" +msgstr "" + +#: of telebot.TeleBot:15 +msgid "" +"See more examples in examples/ directory: " +"https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" +msgstr "" + +#: of telebot.TeleBot:20 +msgid "Install coloredlogs module to specify colorful_logs=True" +msgstr "" + +#: of telebot.TeleBot telebot.TeleBot.add_custom_filter +#: telebot.TeleBot.add_data telebot.TeleBot.add_sticker_to_set +#: telebot.TeleBot.answer_callback_query telebot.TeleBot.answer_inline_query +#: telebot.TeleBot.answer_pre_checkout_query +#: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query +#: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member +#: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler +#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.chat_join_request_handler +#: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler +#: telebot.TeleBot.clear_reply_handlers +#: telebot.TeleBot.clear_reply_handlers_by_message_id +#: telebot.TeleBot.clear_step_handler +#: telebot.TeleBot.clear_step_handler_by_chat_id +#: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo +#: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands +#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.edit_message_live_location +#: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup +#: telebot.TeleBot.edit_message_text +#: telebot.TeleBot.edited_channel_post_handler +#: telebot.TeleBot.edited_message_handler +#: telebot.TeleBot.enable_save_next_step_handlers +#: telebot.TeleBot.enable_save_reply_handlers +#: telebot.TeleBot.enable_saving_states telebot.TeleBot.export_chat_invite_link +#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat +#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member +#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file +#: telebot.TeleBot.get_file_url telebot.TeleBot.get_game_high_scores +#: telebot.TeleBot.get_my_commands +#: telebot.TeleBot.get_my_default_administrator_rights +#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers +#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler +#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member +#: telebot.TeleBot.register_callback_query_handler +#: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_join_request_handler +#: telebot.TeleBot.register_chat_member_handler +#: telebot.TeleBot.register_chosen_inline_handler +#: telebot.TeleBot.register_edited_channel_post_handler +#: telebot.TeleBot.register_edited_message_handler +#: telebot.TeleBot.register_for_reply +#: telebot.TeleBot.register_for_reply_by_message_id +#: telebot.TeleBot.register_inline_handler +#: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_middleware_handler +#: telebot.TeleBot.register_my_chat_member_handler +#: telebot.TeleBot.register_next_step_handler +#: telebot.TeleBot.register_next_step_handler_by_chat_id +#: telebot.TeleBot.register_poll_answer_handler +#: telebot.TeleBot.register_poll_handler +#: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_shipping_query_handler +#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member +#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link +#: telebot.TeleBot.run_webhooks telebot.TeleBot.send_animation +#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action +#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice +#: telebot.TeleBot.send_document telebot.TeleBot.send_game +#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location +#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message +#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll +#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note +#: telebot.TeleBot.send_voice +#: telebot.TeleBot.set_chat_administrator_custom_title +#: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button +#: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo +#: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_update_listener +#: telebot.TeleBot.set_webhook telebot.TeleBot.setup_middleware +#: telebot.TeleBot.shipping_query_handler +#: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll +#: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unpin_all_chat_messages +#: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file +#: telebot.custom_filters.TextFilter +msgid "Parameters" +msgstr "" + +#: of telebot.TeleBot:23 +msgid "Token of a bot, should be obtained from @BotFather" +msgstr "" + +#: of telebot.TeleBot:26 +msgid "Default parse mode, defaults to None" +msgstr "" + +#: of telebot.TeleBot:29 +msgid "Threaded or not, defaults to True" +msgstr "" + +#: of telebot.TeleBot:32 +msgid "Skips pending updates, defaults to False" +msgstr "" + +#: of telebot.TeleBot:35 +msgid "Number of maximum parallel threads, defaults to 2" +msgstr "" + +#: of telebot.TeleBot:38 +msgid "Next step backend class, defaults to None" +msgstr "" + +#: of telebot.TeleBot:41 +msgid "Reply step handler class, defaults to None" +msgstr "" + +#: of telebot.TeleBot:44 +msgid "Exception handler to handle errors, defaults to None" +msgstr "" + +#: of telebot.TeleBot:47 +msgid "Last update's id, defaults to 0" +msgstr "" + +#: of telebot.TeleBot:50 +msgid "Supress middleware exceptions, defaults to False" +msgstr "" + +#: of telebot.TeleBot:53 +msgid "Storage for states, defaults to StateMemoryStorage()" +msgstr "" + +#: of telebot.TeleBot:56 +msgid "Use class middlewares, defaults to False" +msgstr "" + +#: of telebot.TeleBot:59 +msgid "Default value for disable_web_page_preview, defaults to None" +msgstr "" + +#: of telebot.TeleBot:62 +msgid "Default value for disable_notification, defaults to None" +msgstr "" + +#: of telebot.TeleBot:65 +msgid "Default value for protect_content, defaults to None" +msgstr "" + +#: of telebot.TeleBot:68 +msgid "Default value for allow_sending_without_reply, defaults to None" +msgstr "" + +#: of telebot.TeleBot:72 +msgid "Outputs colorful logs" +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:1 +msgid "Create custom filter." +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:3 +msgid "Example on checking the text of a message" +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:12 +msgid "Class with check(message) method." +msgstr "" + +#: of telebot.TeleBot.add_custom_filter:13 +msgid "Custom filter class with key." +msgstr "" + +#: of telebot.TeleBot.add_data:1 +msgid "Add data to states." +msgstr "" + +#: of telebot.TeleBot.add_data:3 telebot.TeleBot.delete_state:3 +#: telebot.TeleBot.get_state:4 telebot.TeleBot.reset_data:3 +#: telebot.TeleBot.set_state:9 +msgid "User's identifier" +msgstr "" + +#: of telebot.TeleBot.add_data:6 telebot.TeleBot.delete_state:6 +#: telebot.TeleBot.get_state:7 telebot.TeleBot.reset_data:6 +#: telebot.TeleBot.set_state:15 +msgid "Chat's identifier" +msgstr "" + +#: of telebot.TeleBot.add_data:9 +msgid "Data to add" +msgstr "" + +#: of telebot.TeleBot.add_data telebot.TeleBot.add_sticker_to_set +#: telebot.TeleBot.answer_callback_query telebot.TeleBot.answer_inline_query +#: telebot.TeleBot.answer_pre_checkout_query +#: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query +#: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member +#: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler +#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.chat_join_request_handler +#: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler +#: telebot.TeleBot.clear_reply_handlers +#: telebot.TeleBot.clear_reply_handlers_by_message_id +#: telebot.TeleBot.clear_step_handler +#: telebot.TeleBot.clear_step_handler_by_chat_id telebot.TeleBot.close +#: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo +#: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands +#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.edit_message_live_location +#: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup +#: telebot.TeleBot.edit_message_text +#: telebot.TeleBot.edited_channel_post_handler +#: telebot.TeleBot.edited_message_handler +#: telebot.TeleBot.enable_save_next_step_handlers +#: telebot.TeleBot.export_chat_invite_link telebot.TeleBot.forward_message +#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators +#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count +#: telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file +#: telebot.TeleBot.get_file_url telebot.TeleBot.get_forum_topic_icon_stickers +#: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands +#: telebot.TeleBot.get_my_default_administrator_rights +#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.log_out telebot.TeleBot.message_handler +#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler +#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler +#: telebot.TeleBot.poll_handler telebot.TeleBot.polling +#: telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.promote_chat_member +#: telebot.TeleBot.register_callback_query_handler +#: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_join_request_handler +#: telebot.TeleBot.register_chosen_inline_handler +#: telebot.TeleBot.register_edited_channel_post_handler +#: telebot.TeleBot.register_edited_message_handler +#: telebot.TeleBot.register_for_reply +#: telebot.TeleBot.register_for_reply_by_message_id +#: telebot.TeleBot.register_inline_handler +#: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_middleware_handler +#: telebot.TeleBot.register_my_chat_member_handler +#: telebot.TeleBot.register_next_step_handler +#: telebot.TeleBot.register_next_step_handler_by_chat_id +#: telebot.TeleBot.register_poll_answer_handler +#: telebot.TeleBot.register_poll_handler +#: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_shipping_query_handler +#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic +#: telebot.TeleBot.reply_to telebot.TeleBot.reset_data +#: telebot.TeleBot.restrict_chat_member telebot.TeleBot.retrieve_data +#: telebot.TeleBot.revoke_chat_invite_link telebot.TeleBot.send_animation +#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action +#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice +#: telebot.TeleBot.send_document telebot.TeleBot.send_game +#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location +#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message +#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll +#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note +#: telebot.TeleBot.send_voice +#: telebot.TeleBot.set_chat_administrator_custom_title +#: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button +#: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo +#: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.setup_middleware telebot.TeleBot.shipping_query_handler +#: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll +#: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unpin_all_chat_messages +#: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file +#: telebot.TeleBot.user telebot.custom_filters.TextFilter +#: telebot.ext.sync.webhooks.SyncWebhookListener.run_app +msgid "Returns" +msgstr "" + +#: of telebot.TeleBot.add_data:10 telebot.TeleBot.callback_query_handler:9 +#: telebot.TeleBot.channel_post_handler:18 +#: telebot.TeleBot.chat_join_request_handler:10 +#: telebot.TeleBot.chat_member_handler:11 +#: telebot.TeleBot.chosen_inline_handler:10 +#: telebot.TeleBot.clear_reply_handlers:6 +#: telebot.TeleBot.clear_reply_handlers_by_message_id:6 +#: telebot.TeleBot.clear_step_handler:6 +#: telebot.TeleBot.clear_step_handler_by_chat_id:6 +#: telebot.TeleBot.delete_state:9 telebot.TeleBot.edited_message_handler:21 +#: telebot.TeleBot.enable_save_next_step_handlers:13 +#: telebot.TeleBot.inline_handler:9 telebot.TeleBot.my_chat_member_handler:10 +#: telebot.TeleBot.poll_answer_handler:10 telebot.TeleBot.poll_handler:8 +#: telebot.TeleBot.pre_checkout_query_handler:9 +#: telebot.TeleBot.register_callback_query_handler:14 +#: telebot.TeleBot.register_channel_post_handler:23 +#: telebot.TeleBot.register_chat_join_request_handler:14 +#: telebot.TeleBot.register_chosen_inline_handler:14 +#: telebot.TeleBot.register_edited_message_handler:26 +#: telebot.TeleBot.register_for_reply:15 +#: telebot.TeleBot.register_for_reply_by_message_id:15 +#: telebot.TeleBot.register_message_handler:26 +#: telebot.TeleBot.register_middleware_handler:18 +#: telebot.TeleBot.register_my_chat_member_handler:14 +#: telebot.TeleBot.register_next_step_handler:15 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:15 +#: telebot.TeleBot.register_poll_answer_handler:14 +#: telebot.TeleBot.register_poll_handler:14 +#: telebot.TeleBot.register_shipping_query_handler:14 +#: telebot.TeleBot.reset_data:9 telebot.TeleBot.set_state:18 +#: telebot.TeleBot.setup_middleware:5 telebot.TeleBot.shipping_query_handler:9 +#: telebot.custom_filters.TextFilter:22 +#: telebot.ext.sync.webhooks.SyncWebhookListener.run_app:4 +msgid "None" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:1 +msgid "" +"Use this method to add a new sticker to a set created by the bot. It's " +"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:7 +#: telebot.TeleBot.create_new_sticker_set:7 +msgid "User identifier of created sticker set owner" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:10 telebot.TeleBot.get_sticker_set:5 +#: telebot.TeleBot.set_sticker_set_thumb:6 +msgid "Sticker set name" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:13 +#: telebot.TeleBot.create_new_sticker_set:18 +msgid "One or more emoji corresponding to the sticker" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:16 +#: telebot.TeleBot.create_new_sticker_set:21 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px. Pass a file_id as a String to send a file that already " +"exists on the Telegram servers, 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." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:21 +#: telebot.TeleBot.create_new_sticker_set:26 +msgid "TGS animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:24 +#: telebot.TeleBot.create_new_sticker_set:29 +msgid "WebM animation with the sticker, uploaded using multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:27 +#: telebot.TeleBot.create_new_sticker_set:40 +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:30 +#: telebot.TeleBot.answer_callback_query:22 +#: telebot.TeleBot.answer_inline_query:35 +#: telebot.TeleBot.answer_pre_checkout_query:21 +#: telebot.TeleBot.answer_shipping_query:18 +#: telebot.TeleBot.close_forum_topic:13 +#: telebot.TeleBot.create_new_sticker_set:43 +#: telebot.TeleBot.delete_forum_topic:13 +#: telebot.TeleBot.delete_sticker_from_set:6 +#: telebot.TeleBot.edit_forum_topic:19 telebot.TeleBot.reopen_forum_topic:13 +#: telebot.TeleBot.set_sticker_position_in_set:11 +#: telebot.TeleBot.set_sticker_set_thumb:15 +#: telebot.TeleBot.unpin_all_forum_topic_messages:13 +msgid "On success, True is returned." +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set telebot.TeleBot.answer_callback_query +#: telebot.TeleBot.answer_inline_query +#: telebot.TeleBot.answer_pre_checkout_query +#: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query +#: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member +#: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.close_forum_topic +#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link +#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link +#: telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo +#: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_webhook +#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link +#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.edit_message_live_location +#: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup +#: telebot.TeleBot.edit_message_text telebot.TeleBot.export_chat_invite_link +#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat +#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member +#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file_url +#: telebot.TeleBot.get_forum_topic_icon_stickers +#: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands +#: telebot.TeleBot.get_my_default_administrator_rights +#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.log_out +#: telebot.TeleBot.pin_chat_message telebot.TeleBot.promote_chat_member +#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic +#: telebot.TeleBot.reply_to telebot.TeleBot.restrict_chat_member +#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link +#: telebot.TeleBot.send_animation telebot.TeleBot.send_audio +#: telebot.TeleBot.send_chat_action telebot.TeleBot.send_contact +#: telebot.TeleBot.send_dice telebot.TeleBot.send_document +#: telebot.TeleBot.send_game telebot.TeleBot.send_invoice +#: telebot.TeleBot.send_location telebot.TeleBot.send_media_group +#: telebot.TeleBot.send_message telebot.TeleBot.send_photo +#: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker +#: telebot.TeleBot.send_venue telebot.TeleBot.send_video +#: telebot.TeleBot.send_video_note +#: telebot.TeleBot.set_chat_administrator_custom_title +#: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button +#: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo +#: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll +#: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unpin_all_chat_messages +#: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file +#: telebot.TeleBot.user +msgid "Return type" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:31 +#: telebot.TeleBot.answer_callback_query:23 +#: telebot.TeleBot.answer_inline_query:36 +#: telebot.TeleBot.answer_pre_checkout_query:22 +#: telebot.TeleBot.answer_shipping_query:19 +#: telebot.TeleBot.approve_chat_join_request:15 +#: telebot.TeleBot.ban_chat_member:25 telebot.TeleBot.ban_chat_sender_chat:17 +#: telebot.TeleBot.close:9 telebot.TeleBot.close_forum_topic:14 +#: telebot.TeleBot.create_new_sticker_set:44 +#: telebot.TeleBot.decline_chat_join_request:15 +#: telebot.TeleBot.delete_chat_photo:13 +#: telebot.TeleBot.delete_chat_sticker_set:11 +#: telebot.TeleBot.delete_forum_topic:14 telebot.TeleBot.delete_message:23 +#: telebot.TeleBot.delete_my_commands:17 +#: telebot.TeleBot.delete_sticker_from_set:7 telebot.TeleBot.delete_webhook:13 +#: telebot.TeleBot.edit_forum_topic:20 telebot.TeleBot.leave_chat:8 +#: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 +#: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 +#: telebot.TeleBot.reopen_forum_topic:14 +#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:23 +#: telebot.TeleBot.set_chat_administrator_custom_title:18 +#: telebot.TeleBot.set_chat_description:14 +#: telebot.TeleBot.set_chat_menu_button:15 +#: telebot.TeleBot.set_chat_permissions:15 telebot.TeleBot.set_chat_photo:16 +#: telebot.TeleBot.set_chat_title:17 telebot.TeleBot.set_my_commands:18 +#: telebot.TeleBot.set_my_default_administrator_rights:18 +#: telebot.TeleBot.set_sticker_position_in_set:12 +#: telebot.TeleBot.set_sticker_set_thumb:16 +#: telebot.TeleBot.unban_chat_member:20 +#: telebot.TeleBot.unban_chat_sender_chat:15 +#: telebot.TeleBot.unpin_all_chat_messages:12 +#: telebot.TeleBot.unpin_all_forum_topic_messages:14 +#: telebot.TeleBot.unpin_chat_message:15 +msgid ":obj:`bool`" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:1 +msgid "" +"Use this method to send answers to callback queries sent from inline " +"keyboards. The answer will be displayed to the user as a notification at " +"the top of the chat screen or as an alert." +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answercallbackquery" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:6 +#: telebot.TeleBot.answer_pre_checkout_query:10 +#: telebot.TeleBot.answer_shipping_query:5 +#: telebot.TeleBot.answer_web_app_query:8 +msgid "Unique identifier for the query to be answered" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:9 +msgid "" +"Text of the notification. If not specified, nothing will be shown to the " +"user, 0-200 characters" +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:12 +msgid "" +"If True, an alert will be shown by the client instead of a notification " +"at the top of the chat screen. Defaults to false." +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:15 +msgid "" +"URL that will be opened by the user's client. If you have created a Game " +"and accepted the conditions via @BotFather, specify the URL that opens " +"your game - note that this will only work if the query comes from a " +"callback_game button." +msgstr "" + +#: of telebot.TeleBot.answer_callback_query:19 +msgid "" +"The maximum amount of time in seconds that the result of the callback " +"query may be cached client-side. Telegram apps will support caching " +"starting in version 3.14. Defaults to 0." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:1 +msgid "" +"Use this method to send answers to an inline query. On success, True is " +"returned. No more than 50 results per query are allowed." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerinlinequery" +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:6 +msgid "Unique identifier for the answered query" +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:9 +msgid "Array of results for the inline query" +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:12 +msgid "" +"The maximum amount of time in seconds that the result of the inline query" +" may be cached on the server." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:16 +msgid "" +"Pass True, if results may be cached on the server side only for the user " +"that sent the query." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:20 +msgid "" +"Pass the offset that a client should send in the next query with the same" +" text to receive more results." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:24 +msgid "" +"Deep-linking parameter for the /start message sent to the bot when user " +"presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - " +"are allowed. Example: An inline bot that sends YouTube videos can ask the" +" user to connect the bot to their YouTube account to adapt search results" +" accordingly. To do this, it displays a 'Connect your YouTube account' " +"button above the results, or even before showing any. The user presses " +"the button, switches to a private chat with the bot and, in doing so, " +"passes a start parameter that instructs the bot to return an OAuth link. " +"Once done, the bot can offer a switch_inline button so that the user can " +"easily return to the chat where they wanted to use the bot's inline " +"capabilities." +msgstr "" + +#: of telebot.TeleBot.answer_inline_query:32 +msgid "" +"Parameter for the start message sent to the bot when user presses the " +"switch button" +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:1 +msgid "" +"Once the user has confirmed their payment and shipping details, the Bot " +"API sends the final confirmation in the form of an Update with the field " +"pre_checkout_query. Use this method to respond to such pre-checkout " +"queries. On success, True is returned." +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:6 +msgid "" +"The Bot API must receive an answer within 10 seconds after the pre-" +"checkout query was sent." +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answerprecheckoutquery" +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:13 +msgid "" +"Specify True if everything is alright (goods are available, etc.) and the" +" bot is ready to proceed with the order. Use False if there are any " +"problems." +msgstr "" + +#: of telebot.TeleBot.answer_pre_checkout_query:16 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains the reason for failure to proceed with the checkout (e.g. " +"\"Sorry, somebody just bought the last of our amazing black T-shirts " +"while you were busy filling out your payment details. Please choose a " +"different color or garment!\"). Telegram will display this message to the" +" user." +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:1 +msgid "Asks for an answer to a shipping question." +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#answershippingquery" +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:8 +msgid "" +"Specify True if delivery to the specified address is possible and False " +"if there are any problems (for example, if delivery to the specified " +"address is not possible)" +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:11 +msgid "" +"Required if ok is True. A JSON-serialized array of available shipping " +"options." +msgstr "" + +#: of telebot.TeleBot.answer_shipping_query:14 +msgid "" +"Required if ok is False. Error message in human readable form that " +"explains why it is impossible to complete the order (e.g. \"Sorry, " +"delivery to your desired address is unavailable'). Telegram will display " +"this message to the user." +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:1 +msgid "" +"Use this method to set the result of an interaction with a Web App and " +"send a corresponding message on behalf of the user to the chat from which" +" the query originated. On success, a SentWebAppMessage object is " +"returned." +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:6 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#answerwebappquery" +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:11 +msgid "A JSON-serialized object describing the message to be sent" +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:14 +msgid "On success, a SentWebAppMessage object is returned." +msgstr "" + +#: of telebot.TeleBot.answer_web_app_query:15 +msgid ":class:`telebot.types.SentWebAppMessage`" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:1 +msgid "" +"Use this method to approve a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#approvechatjoinrequest" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:7 +#: telebot.TeleBot.decline_chat_join_request:7 +#: telebot.TeleBot.delete_chat_sticker_set:7 telebot.TeleBot.get_chat_member:5 +#: telebot.TeleBot.set_chat_administrator_custom_title:6 +#: telebot.TeleBot.set_chat_permissions:7 +#: telebot.TeleBot.set_chat_sticker_set:7 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup (in the format @supergroupusername)" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:11 +#: telebot.TeleBot.ban_chat_member:12 +#: telebot.TeleBot.decline_chat_join_request:11 +#: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_profile_photos:6 +#: telebot.TeleBot.promote_chat_member:11 +#: telebot.TeleBot.restrict_chat_member:11 +#: telebot.TeleBot.set_chat_administrator_custom_title:10 +#: telebot.TeleBot.unban_chat_member:13 +msgid "Unique identifier of the target user" +msgstr "" + +#: of telebot.TeleBot.approve_chat_join_request:14 +#: telebot.TeleBot.ban_chat_sender_chat:16 +#: telebot.TeleBot.decline_chat_join_request:14 +#: telebot.TeleBot.delete_chat_photo:12 telebot.TeleBot.delete_my_commands:16 +#: telebot.TeleBot.log_out:10 telebot.TeleBot.pin_chat_message:18 +#: telebot.TeleBot.promote_chat_member:60 telebot.TeleBot.remove_webhook:3 +#: telebot.TeleBot.set_chat_administrator_custom_title:17 +#: telebot.TeleBot.set_chat_description:13 +#: telebot.TeleBot.set_chat_menu_button:14 telebot.TeleBot.set_chat_photo:15 +#: telebot.TeleBot.set_chat_title:16 telebot.TeleBot.set_my_commands:17 +#: telebot.TeleBot.set_my_default_administrator_rights:17 +#: telebot.TeleBot.set_webhook:46 telebot.TeleBot.unban_chat_sender_chat:14 +#: telebot.TeleBot.unpin_all_chat_messages:11 +#: telebot.TeleBot.unpin_chat_message:14 +msgid "True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:1 +msgid "" +"Use this method to ban a user in a group, a supergroup or a channel. In " +"the case of supergroups and channels, the user will not be able to return" +" to the chat on their own using invite links, etc., unless unbanned " +"first. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:8 telebot.TeleBot.restrict_chat_member:7 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:15 +msgid "" +"Date when the user will be unbanned, unix time. If user is banned for " +"more than 366 days or less than 30 seconds from the current time they are" +" considered to be banned forever" +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:19 +msgid "" +"Bool: Pass True to delete all messages from the chat for the user that is" +" being removed. If False, the user will be able to see messages in the " +"group that were sent before the user was removed. Always True for " +"supergroups and channels." +msgstr "" + +#: of telebot.TeleBot.ban_chat_member:24 +#: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 +#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:22 +msgid "Returns True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:1 +msgid "" +"Use this method to ban a channel chat in a supergroup or a channel. The " +"owner of the chat will not be able to send messages and join live streams" +" on behalf of the chat, unless it is unbanned first. The bot must be an " +"administrator in the supergroup or channel for this to work and must have" +" the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#banchatsenderchat" +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:10 +#: telebot.TeleBot.close_forum_topic:7 telebot.TeleBot.copy_message:5 +#: telebot.TeleBot.create_forum_topic:7 telebot.TeleBot.delete_forum_topic:7 +#: telebot.TeleBot.delete_message:13 telebot.TeleBot.edit_forum_topic:7 +#: telebot.TeleBot.edit_message_live_location:13 +#: telebot.TeleBot.forward_message:8 telebot.TeleBot.pin_chat_message:7 +#: telebot.TeleBot.reopen_forum_topic:7 telebot.TeleBot.send_animation:6 +#: telebot.TeleBot.send_audio:9 telebot.TeleBot.send_dice:5 +#: telebot.TeleBot.send_document:5 telebot.TeleBot.send_game:5 +#: telebot.TeleBot.send_location:5 telebot.TeleBot.send_media_group:6 +#: telebot.TeleBot.send_message:9 telebot.TeleBot.send_photo:5 +#: telebot.TeleBot.send_sticker:6 telebot.TeleBot.send_video:5 +#: telebot.TeleBot.send_video_note:6 telebot.TeleBot.send_voice:7 +#: telebot.TeleBot.set_chat_description:6 telebot.TeleBot.set_chat_title:9 +#: telebot.TeleBot.stop_message_live_location:6 +#: telebot.TeleBot.unban_chat_sender_chat:8 +#: telebot.TeleBot.unpin_all_forum_topic_messages:7 +#: telebot.TeleBot.unpin_chat_message:7 +msgid "" +"Unique identifier for the target chat or username of the target channel " +"(in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.ban_chat_sender_chat:13 +msgid "Unique identifier of the target sender chat" +msgstr "" + +#: of telebot.TeleBot.callback_query_handler:1 +msgid "" +"Handles new incoming callback query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.CallbackQuery` object." +msgstr "" + +#: of telebot.TeleBot.callback_query_handler:4 +#: telebot.TeleBot.channel_post_handler:10 +#: telebot.TeleBot.chat_join_request_handler:5 +#: telebot.TeleBot.chat_member_handler:6 +#: telebot.TeleBot.chosen_inline_handler:5 +#: telebot.TeleBot.edited_channel_post_handler:10 +#: telebot.TeleBot.edited_message_handler:10 telebot.TeleBot.inline_handler:4 +#: telebot.TeleBot.my_chat_member_handler:5 +#: telebot.TeleBot.poll_answer_handler:5 telebot.TeleBot.poll_handler:4 +#: telebot.TeleBot.pre_checkout_query_handler:4 +#: telebot.TeleBot.register_callback_query_handler:6 +#: telebot.TeleBot.register_channel_post_handler:15 +#: telebot.TeleBot.register_chat_join_request_handler:6 +#: telebot.TeleBot.register_chat_member_handler:6 +#: telebot.TeleBot.register_chosen_inline_handler:6 +#: telebot.TeleBot.register_edited_channel_post_handler:15 +#: telebot.TeleBot.register_edited_message_handler:15 +#: telebot.TeleBot.register_inline_handler:6 +#: telebot.TeleBot.register_message_handler:15 +#: telebot.TeleBot.register_my_chat_member_handler:6 +#: telebot.TeleBot.register_poll_answer_handler:6 +#: telebot.TeleBot.register_poll_handler:6 +#: telebot.TeleBot.register_pre_checkout_query_handler:6 +#: telebot.TeleBot.register_shipping_query_handler:6 +#: telebot.TeleBot.shipping_query_handler:4 +msgid "Function executed as a filter" +msgstr "" + +#: of telebot.TeleBot.callback_query_handler:7 +#: telebot.TeleBot.channel_post_handler:16 +#: telebot.TeleBot.chat_join_request_handler:8 +#: telebot.TeleBot.chat_member_handler:9 +#: telebot.TeleBot.chosen_inline_handler:8 +#: telebot.TeleBot.edited_channel_post_handler:16 +#: telebot.TeleBot.edited_message_handler:19 telebot.TeleBot.inline_handler:7 +#: telebot.TeleBot.message_handler:50 telebot.TeleBot.my_chat_member_handler:8 +#: telebot.TeleBot.poll_answer_handler:8 telebot.TeleBot.poll_handler:7 +#: telebot.TeleBot.pre_checkout_query_handler:7 +#: telebot.TeleBot.register_callback_query_handler:12 +#: telebot.TeleBot.register_channel_post_handler:21 +#: telebot.TeleBot.register_chat_join_request_handler:12 +#: telebot.TeleBot.register_chat_member_handler:12 +#: telebot.TeleBot.register_chosen_inline_handler:12 +#: telebot.TeleBot.register_edited_channel_post_handler:21 +#: telebot.TeleBot.register_edited_message_handler:24 +#: telebot.TeleBot.register_inline_handler:12 +#: telebot.TeleBot.register_message_handler:24 +#: telebot.TeleBot.register_my_chat_member_handler:12 +#: telebot.TeleBot.register_poll_answer_handler:12 +#: telebot.TeleBot.register_poll_handler:12 +#: telebot.TeleBot.register_pre_checkout_query_handler:11 +#: telebot.TeleBot.register_shipping_query_handler:12 +#: telebot.TeleBot.shipping_query_handler:7 +msgid "Optional keyword arguments(custom filters)" +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:1 +msgid "" +"Handles new incoming channel post of any kind - text, photo, sticker, " +"etc. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:4 +#: telebot.TeleBot.edited_channel_post_handler:4 +#: telebot.TeleBot.edited_message_handler:4 telebot.TeleBot.message_handler:34 +msgid "Optional list of strings (commands to handle)." +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:7 +#: telebot.TeleBot.edited_channel_post_handler:7 +#: telebot.TeleBot.edited_message_handler:7 telebot.TeleBot.message_handler:37 +msgid "Optional regular expression." +msgstr "" + +#: of telebot.TeleBot.channel_post_handler:13 +#: telebot.TeleBot.edited_channel_post_handler:13 +#: telebot.TeleBot.edited_message_handler:13 telebot.TeleBot.message_handler:44 +#: telebot.TeleBot.register_channel_post_handler:6 +#: telebot.TeleBot.register_edited_channel_post_handler:6 +#: telebot.TeleBot.register_edited_message_handler:6 +#: telebot.TeleBot.register_message_handler:6 +msgid "Supported message content types. Must be a list. Defaults to ['text']." +msgstr "" + +#: of telebot.TeleBot.chat_join_request_handler:1 +msgid "" +"Handles a request to join the chat has been sent. The bot must have the " +"can_invite_users administrator right in the chat to receive these " +"updates. As a parameter to the decorator function, it passes " +":class:`telebot.types.ChatJoinRequest` object." +msgstr "" + +#: of telebot.TeleBot.chat_member_handler:1 +msgid "" +"Handles update in a status of a user in a chat. The bot must be an " +"administrator in the chat and must explicitly specify “chat_member” in " +"the list of allowed_updates to receive these updates. As a parameter to " +"the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.TeleBot.chosen_inline_handler:1 +msgid "" +"Handles the result of an inline query that was chosen by a user and sent " +"to their chat partner. Please see our documentation on the feedback " +"collecting for details on how to enable these updates for your bot. As a " +"parameter to the decorator function, it passes " +":class:`telebot.types.ChosenInlineResult` object." +msgstr "" + +#: of telebot.TeleBot.clear_reply_handlers:1 +#: telebot.TeleBot.clear_reply_handlers_by_message_id:1 +msgid "" +"Clears all callback functions registered by register_for_reply() and " +"register_for_reply_by_message_id()." +msgstr "" + +#: of telebot.TeleBot.clear_reply_handlers:3 +msgid "The message for which we want to clear reply handlers" +msgstr "" + +#: of telebot.TeleBot.clear_reply_handlers_by_message_id:3 +msgid "The message id for which we want to clear reply handlers" +msgstr "" + +#: of telebot.TeleBot.clear_step_handler:1 +#: telebot.TeleBot.clear_step_handler_by_chat_id:1 +msgid "Clears all callback functions registered by register_next_step_handler()." +msgstr "" + +#: of telebot.TeleBot.clear_step_handler:3 +msgid "" +"The message for which we want to handle new message after that in same " +"chat." +msgstr "" + +#: of telebot.TeleBot.clear_step_handler_by_chat_id:3 +msgid "The chat for which we want to clear next step handlers" +msgstr "" + +#: of telebot.TeleBot.close:1 +msgid "" +"Use this method to close the bot instance before moving it from one local" +" server to another. You need to delete the webhook before calling this " +"method to ensure that the bot isn't launched again after server restart. " +"The method will return error 429 in the first 10 minutes after the bot is" +" launched. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.close:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#close" +msgstr "" + +#: of telebot.TeleBot.close_forum_topic:1 +msgid "" +"Use this method to close an open topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.close_forum_topic:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" +msgstr "" + +#: of telebot.TeleBot.close_forum_topic:10 +msgid "Identifier of the topic to close" +msgstr "" + +#: of telebot.TeleBot.copy_message:1 +msgid "Use this method to copy messages of any kind." +msgstr "" + +#: of telebot.TeleBot.copy_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" +msgstr "" + +#: of telebot.TeleBot.copy_message:8 telebot.TeleBot.forward_message:11 +msgid "" +"Unique identifier for the chat where the original message was sent (or " +"channel username in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.copy_message:10 telebot.TeleBot.forward_message:14 +msgid "Message identifier in the chat specified in from_chat_id" +msgstr "" + +#: of telebot.TeleBot.copy_message:13 +msgid "" +"New caption for media, 0-1024 characters after entities parsing. If not " +"specified, the original caption is kept" +msgstr "" + +#: of telebot.TeleBot.copy_message:16 +msgid "Mode for parsing entities in the new caption." +msgstr "" + +#: of telebot.TeleBot.copy_message:19 +msgid "" +"A JSON-serialized list of special entities that appear in the new " +"caption, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 +#: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 +#: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:26 +#: telebot.TeleBot.send_game:11 telebot.TeleBot.send_invoice:67 +#: telebot.TeleBot.send_location:25 telebot.TeleBot.send_message:24 +#: telebot.TeleBot.send_photo:22 telebot.TeleBot.send_poll:44 +#: telebot.TeleBot.send_venue:27 telebot.TeleBot.send_video:35 +#: telebot.TeleBot.send_video_note:27 telebot.TeleBot.send_voice:31 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.TeleBot.copy_message:25 telebot.TeleBot.send_animation:34 +#: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:38 +#: telebot.TeleBot.send_document:50 telebot.TeleBot.send_invoice:95 +#: telebot.TeleBot.send_location:43 telebot.TeleBot.send_media_group:15 +#: telebot.TeleBot.send_photo:25 telebot.TeleBot.send_poll:64 +#: telebot.TeleBot.send_sticker:30 telebot.TeleBot.send_venue:51 +#: telebot.TeleBot.send_video:38 telebot.TeleBot.send_video_note:42 +#: telebot.TeleBot.send_voice:43 +msgid "Protects the contents of the sent message from forwarding and saving" +msgstr "" + +#: of telebot.TeleBot.copy_message:28 telebot.TeleBot.send_animation:37 +#: telebot.TeleBot.send_audio:29 telebot.TeleBot.send_contact:23 +#: telebot.TeleBot.send_dice:15 telebot.TeleBot.send_document:12 +#: telebot.TeleBot.send_game:14 telebot.TeleBot.send_invoice:70 +#: telebot.TeleBot.send_location:17 telebot.TeleBot.send_media_group:18 +#: telebot.TeleBot.send_message:30 telebot.TeleBot.send_photo:28 +#: telebot.TeleBot.send_poll:47 telebot.TeleBot.send_sticker:13 +#: telebot.TeleBot.send_venue:30 telebot.TeleBot.send_video:41 +#: telebot.TeleBot.send_video_note:19 telebot.TeleBot.send_voice:20 +msgid "If the message is a reply, ID of the original message" +msgstr "" + +#: of telebot.TeleBot.copy_message:31 telebot.TeleBot.send_animation:54 +#: telebot.TeleBot.send_audio:54 telebot.TeleBot.send_dice:26 +#: telebot.TeleBot.send_document:38 telebot.TeleBot.send_invoice:84 +#: telebot.TeleBot.send_location:40 telebot.TeleBot.send_media_group:24 +#: telebot.TeleBot.send_message:33 telebot.TeleBot.send_photo:31 +#: telebot.TeleBot.send_sticker:27 telebot.TeleBot.send_video:44 +#: telebot.TeleBot.send_video_note:39 telebot.TeleBot.send_voice:40 +msgid "" +"Pass True, if the message should be sent even if the specified replied-to" +" message is not found" +msgstr "" + +#: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:40 +#: telebot.TeleBot.send_contact:26 telebot.TeleBot.send_dice:18 +#: telebot.TeleBot.send_document:18 telebot.TeleBot.send_game:17 +#: telebot.TeleBot.send_location:20 telebot.TeleBot.send_message:36 +#: telebot.TeleBot.send_photo:34 telebot.TeleBot.send_poll:53 +#: telebot.TeleBot.send_sticker:16 telebot.TeleBot.send_venue:33 +#: telebot.TeleBot.send_video:47 telebot.TeleBot.send_video_note:22 +#: telebot.TeleBot.send_voice:23 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.copy_message:39 telebot.TeleBot.delete_message:19 +#: telebot.TeleBot.edit_message_live_location:23 +#: telebot.TeleBot.forward_message:20 telebot.TeleBot.send_animation:48 +#: telebot.TeleBot.send_audio:42 telebot.TeleBot.send_chat_action:19 +#: telebot.TeleBot.send_contact:31 telebot.TeleBot.send_dice:23 +#: telebot.TeleBot.send_document:29 telebot.TeleBot.send_location:28 +#: telebot.TeleBot.send_media_group:21 telebot.TeleBot.send_message:40 +#: telebot.TeleBot.send_photo:39 telebot.TeleBot.send_sticker:24 +#: telebot.TeleBot.send_venue:38 telebot.TeleBot.send_video:52 +#: telebot.TeleBot.send_video_note:30 telebot.TeleBot.send_voice:34 +#: telebot.TeleBot.stop_message_live_location:19 +msgid "Timeout in seconds for the request." +msgstr "" + +#: of telebot.TeleBot.copy_message:42 telebot.TeleBot.forward_message:23 +#: telebot.TeleBot.send_audio:60 telebot.TeleBot.send_dice:32 +#: telebot.TeleBot.send_location:46 telebot.TeleBot.send_message:43 +#: telebot.TeleBot.send_photo:42 telebot.TeleBot.send_voice:46 +msgid "Identifier of a message thread, in which the message will be sent" +msgstr "" + +#: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:60 +#: telebot.TeleBot.send_audio:63 telebot.TeleBot.send_contact:44 +#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:56 +#: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 +#: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 +#: telebot.TeleBot.send_photo:45 telebot.TeleBot.send_poll:70 +#: telebot.TeleBot.send_sticker:39 telebot.TeleBot.send_venue:57 +#: telebot.TeleBot.send_video:61 telebot.TeleBot.send_video_note:48 +#: telebot.TeleBot.send_voice:49 +msgid "On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 +#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:61 +#: telebot.TeleBot.send_audio:64 telebot.TeleBot.send_contact:45 +#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:57 +#: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 +#: telebot.TeleBot.send_photo:46 telebot.TeleBot.send_sticker:40 +#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:62 +#: telebot.TeleBot.send_video_note:49 +msgid ":class:`telebot.types.Message`" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:1 +msgid "" +"Use this method to create an additional invite link for a chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. The link can be revoked using the " +"method revokeChatInviteLink. Returns the new invite link as " +"ChatInviteLink object." +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createchatinvitelink" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:8 +#: telebot.TeleBot.edit_chat_invite_link:6 +#: telebot.TeleBot.export_chat_invite_link:6 +#: telebot.TeleBot.revoke_chat_invite_link:7 +msgid "" +"Id: Unique identifier for the target chat or username of the target " +"channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:12 +#: telebot.TeleBot.edit_chat_invite_link:10 +msgid "Invite link name; 0-32 characters" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:15 +#: telebot.TeleBot.edit_chat_invite_link:16 +msgid "Point in time (Unix timestamp) when the link will expire" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:18 +#: telebot.TeleBot.edit_chat_invite_link:19 +msgid "Maximum number of users that can be members of the chat simultaneously" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:21 +#: telebot.TeleBot.edit_chat_invite_link:22 +msgid "" +"True, if users joining the chat via the link need to be approved by chat " +"administrators. If True, member_limit can't be specified" +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:24 +#: telebot.TeleBot.edit_chat_invite_link:25 +#: telebot.TeleBot.revoke_chat_invite_link:14 +msgid "Returns the new invite link as ChatInviteLink object." +msgstr "" + +#: of telebot.TeleBot.create_chat_invite_link:25 +#: telebot.TeleBot.edit_chat_invite_link:26 +#: telebot.TeleBot.revoke_chat_invite_link:15 +msgid ":class:`telebot.types.ChatInviteLink`" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:1 +msgid "" +"Use this method to create a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights. Returns information about the " +"created topic as a ForumTopic object." +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createforumtopic" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:10 +msgid "Name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:13 +msgid "" +"Color of the topic icon in RGB format. Currently, must be one of " +"0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:16 +msgid "" +"Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " +"be exactly 1 character long" +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:19 +msgid "" +"On success, information about the created topic is returned as a " +"ForumTopic object." +msgstr "" + +#: of telebot.TeleBot.create_forum_topic:20 +msgid ":class:`telebot.types.ForumTopic`" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:1 +msgid "" +"Use this method to create a link for an invoice. Returns the created " +"invoice link as String on success." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createinvoicelink" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:7 telebot.TeleBot.send_invoice:8 +msgid "Product name, 1-32 characters" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:10 telebot.TeleBot.send_invoice:11 +msgid "Product description, 1-255 characters" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:13 telebot.TeleBot.send_invoice:14 +msgid "" +"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " +"the user, use for your internal processes." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:17 telebot.TeleBot.send_invoice:18 +msgid "Payments provider token, obtained via @Botfather" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:20 telebot.TeleBot.send_invoice:21 +msgid "" +"Three-letter ISO 4217 currency code, see " +"https://core.telegram.org/bots/payments#supported-currencies" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:24 telebot.TeleBot.send_invoice:25 +msgid "" +"Price breakdown, a list of components (e.g. product price, tax, discount," +" delivery cost, delivery tax, bonus, etc.)" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:28 telebot.TeleBot.send_invoice:87 +msgid "The maximum accepted amount for tips in the smallest units of the currency" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:31 telebot.TeleBot.send_invoice:90 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:77 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:40 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a photo of the invoice. People like it better when they see a photo of " +"what they are paying for." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:44 telebot.TeleBot.send_invoice:37 +msgid "Photo size in bytes" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:47 telebot.TeleBot.send_invoice:40 +msgid "Photo width" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:50 telebot.TeleBot.send_invoice:43 +msgid "Photo height" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:53 telebot.TeleBot.send_invoice:46 +msgid "Pass True, if you require the user's full name to complete the order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:56 telebot.TeleBot.send_invoice:49 +msgid "Pass True, if you require the user's phone number to complete the order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:59 telebot.TeleBot.send_invoice:52 +msgid "Pass True, if you require the user's email to complete the order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:62 telebot.TeleBot.send_invoice:55 +msgid "" +"Pass True, if you require the user's shipping address to complete the " +"order" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:65 telebot.TeleBot.send_invoice:61 +msgid "Pass True, if user's phone number should be sent to provider" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:68 telebot.TeleBot.send_invoice:64 +msgid "Pass True, if user's email address should be sent to provider" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:71 telebot.TeleBot.send_invoice:58 +msgid "Pass True, if the final price depends on the shipping method" +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:74 +msgid "Created invoice link as String on success." +msgstr "" + +#: of telebot.TeleBot.create_invoice_link:75 +#: telebot.TeleBot.export_chat_invite_link:11 telebot.TeleBot.get_file_url:7 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:1 +msgid "" +"Use this method to create new sticker set owned by a user. The bot will " +"be able to edit the created sticker set. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#createnewstickerset" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:10 +msgid "" +"Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " +"animals). Can contain only English letters, digits and underscores. Must " +"begin with a letter, can't contain consecutive underscores and must end " +"in \"_by_\". is case insensitive. 1-64 " +"characters." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:15 +msgid "Sticker set title, 1-64 characters" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:32 +msgid "" +"Pass True, if a set of mask stickers should be created. Deprecated since " +"Bot API 6.2, use sticker_type instead." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:36 +msgid "" +"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " +"emoji sticker sets can't be created via the Bot API at the moment. By " +"default, a regular sticker set is created." +msgstr "" + +#: of telebot.TeleBot.decline_chat_join_request:1 +msgid "" +"Use this method to decline a chat join request. The bot must be an " +"administrator in the chat for this to work and must have the " +"can_invite_users administrator right. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.decline_chat_join_request:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#declinechatjoinrequest" +msgstr "" + +#: of telebot.TeleBot.delete_chat_photo:1 +msgid "" +"Use this method to delete a chat photo. Photos can't be changed for " +"private chats. The bot must be an administrator in the chat for this to " +"work and must have the appropriate admin rights. Returns True on success." +" Note: In regular groups (non-supergroups), this method will only work if" +" the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.TeleBot.delete_chat_photo:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" +msgstr "" + +#: of telebot.TeleBot.delete_chat_photo:8 telebot.TeleBot.set_chat_photo:9 +#: telebot.TeleBot.unpin_all_chat_messages:7 +msgid "" +"Int or Str: Unique identifier for the target chat or username of the " +"target channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.delete_chat_sticker_set:1 +msgid "" +"Use this method to delete a group sticker set from a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights. Use the field can_set_sticker_set optionally " +"returned in getChat requests to check if the bot can use this method. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletechatstickerset" +msgstr "" + +#: of telebot.TeleBot.delete_forum_topic:1 +msgid "" +"Use this method to delete a topic in a forum supergroup chat. The bot " +"must be an administrator in the chat for this to work and must have the " +"can_manage_topics administrator rights, unless it is the creator of the " +"topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "" + +#: of telebot.TeleBot.delete_forum_topic:10 +msgid "Identifier of the topic to delete" +msgstr "" + +#: of telebot.TeleBot.delete_message:1 +msgid "" +"Use this method to delete a message, including service messages, with the" +" following limitations: - A message can only be deleted if it was sent " +"less than 48 hours ago. - A dice message in a private chat can only be " +"deleted if it was sent more than 24 hours ago. - Bots can delete outgoing" +" messages in private chats, groups, and supergroups. - Bots can delete " +"incoming messages in private chats. - Bots granted can_post_messages " +"permissions can delete outgoing messages in channels. - If the bot is an " +"administrator of a group, it can delete any message there. - If the bot " +"has can_delete_messages permission in a supergroup or a channel, it can " +"delete any message there. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_message:11 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" +msgstr "" + +#: of telebot.TeleBot.delete_message:16 +msgid "Identifier of the message to delete" +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:1 +msgid "" +"Use this method to delete the list of the bot's commands for the given " +"scope and user language. After deletion, higher level commands will be " +"shown to affected users. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletemycommands" +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:7 telebot.TeleBot.get_my_commands:6 +#: telebot.TeleBot.set_my_commands:8 +msgid "" +"The scope of users for which the commands are relevant. Defaults to " +"BotCommandScopeDefault." +msgstr "" + +#: of telebot.TeleBot.delete_my_commands:11 telebot.TeleBot.get_my_commands:10 +#: telebot.TeleBot.set_my_commands:12 +msgid "" +"A two-letter ISO 639-1 language code. If empty, commands will be applied " +"to all users from the given scope, for whose language there are no " +"dedicated commands" +msgstr "" + +#: of telebot.TeleBot.delete_state:1 +msgid "Delete the current state of a user." +msgstr "" + +#: of telebot.TeleBot.delete_sticker_from_set:1 +msgid "" +"Use this method to delete a sticker from a set created by the bot. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_sticker_from_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#deletestickerfromset" +msgstr "" + +#: of telebot.TeleBot.delete_sticker_from_set:5 +#: telebot.TeleBot.set_sticker_position_in_set:5 +msgid "File identifier of the sticker" +msgstr "" + +#: of telebot.TeleBot.delete_webhook:1 +msgid "" +"Use this method to remove webhook integration if you decide to switch " +"back to getUpdates. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_webhook:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" +msgstr "" + +#: of telebot.TeleBot.delete_webhook:6 telebot.TeleBot.run_webhooks:36 +#: telebot.TeleBot.set_webhook:36 +msgid "Pass True to drop all pending updates, defaults to None" +msgstr "" + +#: of telebot.TeleBot.delete_webhook:9 telebot.TeleBot.run_webhooks:39 +msgid "Request connection timeout, defaults to None" +msgstr "" + +#: of telebot.TeleBot.disable_save_next_step_handlers:1 +#: telebot.TeleBot.disable_save_reply_handlers:1 +msgid "Disable saving next step handlers (by default saving disable)" +msgstr "" + +#: of telebot.TeleBot.disable_save_next_step_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" disable file saving capability for handlers. For the same purpose, " +"MemoryHandlerBackend is reassigned as a new next_step_backend backend " +"instead of FileHandlerBackend." +msgstr "" + +#: of telebot.TeleBot.disable_save_reply_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" disable file saving capability for handlers. For the same purpose, " +"MemoryHandlerBackend is reassigned as a new reply_backend backend instead" +" of FileHandlerBackend." +msgstr "" + +#: of telebot.TeleBot.download_file:1 +msgid "Downloads file." +msgstr "" + +#: of telebot.TeleBot.download_file:3 +msgid "Path where the file should be downloaded." +msgstr "" + +#: of telebot.TeleBot.download_file:6 +msgid "bytes" +msgstr "" + +#: of telebot.TeleBot.download_file:7 +msgid ":obj:`bytes`" +msgstr "" + +#: of telebot.TeleBot.edit_chat_invite_link:1 +msgid "" +"Use this method to edit a non-primary invite link created by the bot. The" +" bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.edit_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editchatinvitelink" +msgstr "" + +#: of telebot.TeleBot.edit_chat_invite_link:13 +msgid "The invite link to edit" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:1 +msgid "" +"Use this method to edit name and icon of a topic in a forum supergroup " +"chat. The bot must be an administrator in the chat for this to work and " +"must have can_manage_topics administrator rights, unless it is the " +"creator of the topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:5 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:10 +msgid "Identifier of the topic to edit" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:13 +msgid "New name of the topic, 1-128 characters" +msgstr "" + +#: of telebot.TeleBot.edit_forum_topic:16 +msgid "" +"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " +"must be exactly 1 character long" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:1 +msgid "Use this method to edit captions of messages." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagecaption" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:5 +msgid "New caption of the message" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:8 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:11 +msgid "Required if inline_message_id is not specified." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:14 +msgid "" +"Required if inline_message_id is not specified. Identifier of the inline " +"message." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:17 +msgid "New caption of the message, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:20 +msgid "" +"A JSON-serialized array of objects that describe how the caption should " +"be parsed." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:23 +#: telebot.TeleBot.edit_message_media:19 +#: telebot.TeleBot.edit_message_reply_markup:14 +#: telebot.TeleBot.edit_message_text:26 +msgid "A JSON-serialized object for an inline keyboard." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:26 +#: telebot.TeleBot.edit_message_media:22 +#: telebot.TeleBot.edit_message_reply_markup:17 +#: telebot.TeleBot.edit_message_text:29 +msgid "" +"On success, if edited message is sent by the bot, the edited Message is " +"returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.edit_message_caption:27 +msgid ":obj:`types.Message` | :obj:`bool`" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:3 +msgid "" +"Use this method to edit live location messages. A location can be edited " +"until its live_period expires or editing is explicitly" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:2 +msgid "" +"disabled by a call to stopMessageLiveLocation. On success, if the edited " +"message is not an inline message, the edited Message is returned, " +"otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagelivelocation" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:7 +msgid "Latitude of new location" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:10 +msgid "Longitude of new location" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:16 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" to edit" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:19 +#: telebot.TeleBot.stop_message_live_location:15 +msgid "A JSON-serialized object for a new inline keyboard." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:26 +#: telebot.TeleBot.edit_message_media:16 +#: telebot.TeleBot.edit_message_reply_markup:11 +#: telebot.TeleBot.edit_message_text:14 telebot.TeleBot.get_game_high_scores:19 +#: telebot.TeleBot.set_game_score:20 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:29 +#: telebot.TeleBot.send_location:31 +msgid "The radius of uncertainty for the location, measured in meters; 0-1500" +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:32 +msgid "" +"Direction in which the user is moving, in degrees. Must be between 1 and " +"360 if specified." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:35 +msgid "" +"The maximum distance for proximity alerts about approaching another chat " +"member, in meters. Must be between 1 and 100000 if specified." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:38 +msgid "" +"On success, if the edited message is not an inline message, the edited " +"Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.edit_message_live_location:39 +#: telebot.TeleBot.stop_message_live_location:23 +msgid ":class:`telebot.types.Message` or bool" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:1 +msgid "" +"Use this method to edit animation, audio, document, photo, or video " +"messages. If a message is a part of a message album, then it can be " +"edited only to a photo or a video. Otherwise, message type can be changed" +" arbitrarily. When inline message is edited, new file can't be uploaded. " +"Use previously uploaded file via its file_id or specify a URL." +msgstr "" + +#: of telebot.TeleBot.edit_message_media:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagemedia" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:8 +msgid "A JSON-serialized object for a new media content of the message" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:10 +#: telebot.TeleBot.edit_message_reply_markup:5 +#: telebot.TeleBot.edit_message_text:8 telebot.TeleBot.get_game_high_scores:13 +#: telebot.TeleBot.set_game_score:14 +msgid "" +"Required if inline_message_id is not specified. Unique identifier for the" +" target chat or username of the target channel (in the format " +"@channelusername)" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:13 +#: telebot.TeleBot.edit_message_reply_markup:8 +#: telebot.TeleBot.edit_message_text:11 telebot.TeleBot.get_game_high_scores:16 +#: telebot.TeleBot.set_game_score:17 +msgid "" +"Required if inline_message_id is not specified. Identifier of the sent " +"message" +msgstr "" + +#: of telebot.TeleBot.edit_message_media:23 +#: telebot.TeleBot.edit_message_reply_markup:18 +#: telebot.TeleBot.edit_message_text:30 telebot.TeleBot.set_game_score:27 +msgid ":obj:`types.Message` or :obj:`bool`" +msgstr "" + +#: of telebot.TeleBot.edit_message_reply_markup:1 +msgid "Use this method to edit only the reply markup of messages." +msgstr "" + +#: of telebot.TeleBot.edit_message_reply_markup:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editmessagereplymarkup" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:1 +msgid "Use this method to edit text and game messages." +msgstr "" + +#: of telebot.TeleBot.edit_message_text:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#editmessagetext" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:5 +msgid "New text of the message, 1-4096 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:17 telebot.TeleBot.send_message:15 +msgid "Mode for parsing entities in the message text." +msgstr "" + +#: of telebot.TeleBot.edit_message_text:20 +msgid "" +"List of special entities that appear in the message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.edit_message_text:23 telebot.TeleBot.send_message:21 +msgid "Disables link previews for links in this message" +msgstr "" + +#: of telebot.TeleBot.edited_channel_post_handler:1 +msgid "" +"Handles new version of a channel post that is known to the bot and was " +"edited. As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.TeleBot.edited_message_handler:1 +msgid "" +"Handles new version of a message that is known to the bot and was edited." +" As a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object." +msgstr "" + +#: of telebot.TeleBot.edited_message_handler:16 +#: telebot.TeleBot.message_handler:47 +msgid "list of chat types" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:1 +msgid "Enable saving next step handlers (by default saving disabled)" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:3 +#: telebot.TeleBot.enable_save_reply_handlers:3 +msgid "" +"This function explicitly assigns FileHandlerBackend (instead of Saver) " +"just to keep backward compatibility whose purpose was to enable file " +"saving capability for handlers. And the same implementation is now " +"available with FileHandlerBackend" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:7 +#: telebot.TeleBot.enable_save_reply_handlers:7 +msgid "Delay between changes in handlers and saving, defaults to 120" +msgstr "" + +#: of telebot.TeleBot.enable_save_next_step_handlers:10 +msgid "Filename of save file, defaults to \"./.handler-saves/step.save\"" +msgstr "" + +#: of telebot.TeleBot.enable_save_reply_handlers:1 +msgid "Enable saving reply handlers (by default saving disable)" +msgstr "" + +#: of telebot.TeleBot.enable_save_reply_handlers:10 +msgid "Filename of save file, defaults to \"./.handler-saves/reply.save\"" +msgstr "" + +#: of telebot.TeleBot.enable_saving_states:1 +msgid "Enable saving states (by default saving disabled)" +msgstr "" + +#: of telebot.TeleBot.enable_saving_states:4 +msgid "" +"It is recommended to pass a :class:`~telebot.storage.StatePickleStorage` " +"instance as state_storage to TeleBot class." +msgstr "" + +#: of telebot.TeleBot.enable_saving_states:7 +msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" +msgstr "" + +#: of telebot.TeleBot.export_chat_invite_link:1 +msgid "" +"Use this method to export an invite link to a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.export_chat_invite_link:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#exportchatinvitelink" +msgstr "" + +#: of telebot.TeleBot.export_chat_invite_link:10 +msgid "exported invite link as String on success." +msgstr "" + +#: of telebot.TeleBot.forward_message:1 +msgid "Use this method to forward messages of any kind." +msgstr "" + +#: of telebot.TeleBot.forward_message:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" +msgstr "" + +#: of telebot.TeleBot.forward_message:5 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" + +#: of telebot.TeleBot.forward_message:17 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "" + +#: of telebot.TeleBot.get_chat:1 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.get_chat:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" +msgstr "" + +#: of telebot.TeleBot.get_chat:6 telebot.TeleBot.get_chat_administrators:7 +#: telebot.TeleBot.get_chat_member_count:5 telebot.TeleBot.leave_chat:5 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup or channel (in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.get_chat:9 +msgid "Chat information" +msgstr "" + +#: of telebot.TeleBot.get_chat:10 +msgid ":class:`telebot.types.Chat`" +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:1 +msgid "" +"Use this method to get a list of administrators in a chat. On success, " +"returns an Array of ChatMember objects that contains information about " +"all chat administrators except other bots." +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatadministrators" +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:9 +msgid "List made of ChatMember objects." +msgstr "" + +#: of telebot.TeleBot.get_chat_administrators:10 +msgid ":obj:`list` of :class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.TeleBot.get_chat_member:1 +msgid "" +"Use this method to get information about a member of a chat. Returns a " +"ChatMember object on success." +msgstr "" + +#: of telebot.TeleBot.get_chat_member:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" +msgstr "" + +#: of telebot.TeleBot.get_chat_member:11 +msgid "Returns ChatMember object on success." +msgstr "" + +#: of telebot.TeleBot.get_chat_member:12 +msgid ":class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:1 +msgid "Use this method to get the number of members in a chat." +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getchatmembercount" +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:8 +msgid "Number of members in the chat." +msgstr "" + +#: of telebot.TeleBot.get_chat_member_count:9 +msgid ":obj:`int`" +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:1 +msgid "" +"Use this method to get the current value of the bot's menu button in a " +"private chat, or the default menu button. Returns MenuButton on success." +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:5 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#getchatmenubutton" +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be returned." +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:11 +msgid "types.MenuButton" +msgstr "" + +#: of telebot.TeleBot.get_chat_menu_button:12 +msgid ":class:`telebot.types.MenuButton`" +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:1 +msgid "" +"Use this method to get information about custom emoji stickers by their " +"identifiers. Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:4 +msgid "" +"List of custom emoji identifiers. At most 200 custom emoji identifiers " +"can be specified." +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:7 +msgid "Returns an Array of Sticker objects." +msgstr "" + +#: of telebot.TeleBot.get_custom_emoji_stickers:8 +msgid ":obj:`list` of :class:`telebot.types.Sticker`" +msgstr "" + +#: of telebot.TeleBot.get_file:1 +msgid "" +"Use this method to get basic info about a file and prepare it for " +"downloading. For the moment, bots can download files of up to 20MB in " +"size. On success, a File object is returned. It is guaranteed that the " +"link will be valid for at least 1 hour. When the link expires, a new one " +"can be requested by calling get_file again." +msgstr "" + +#: of telebot.TeleBot.get_file:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" +msgstr "" + +#: of telebot.TeleBot.get_file:9 +msgid "File identifier" +msgstr "" + +#: of telebot.TeleBot.get_file:12 telebot.TeleBot.upload_sticker_file:14 +msgid ":class:`telebot.types.File`" +msgstr "" + +#: of telebot.TeleBot.get_file_url:1 +msgid "Get a valid URL for downloading a file." +msgstr "" + +#: of telebot.TeleBot.get_file_url:3 +msgid "File identifier to get download URL for." +msgstr "" + +#: of telebot.TeleBot.get_file_url:6 +msgid "URL for downloading the file." +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:1 +msgid "" +"Use this method to get custom emoji stickers, which can be used as a " +"forum topic icon by any user. Requires no parameters. Returns an Array of" +" Sticker objects." +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getforumtopiciconstickers" +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:6 +msgid "On success, a list of StickerSet objects is returned." +msgstr "" + +#: of telebot.TeleBot.get_forum_topic_icon_stickers:7 +msgid "List[:class:`telebot.types.StickerSet`]" +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:1 +msgid "" +"Use this method to get data for high score tables. Will return the score " +"of the specified user and several of their neighbors in a game. On " +"success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:4 +msgid "" +"This method will currently return scores for the target user, plus two of" +" their closest neighbors on each side. Will also return the top three " +"users if the user and their neighbors are not among them. Please note " +"that this behavior is subject to change." +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:8 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getgamehighscores" +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:10 telebot.TeleBot.retrieve_data:3 +#: telebot.TeleBot.set_game_score:5 telebot.TeleBot.set_sticker_set_thumb:9 +msgid "User identifier" +msgstr "" + +#: of telebot.TeleBot.get_game_high_scores:22 +msgid "On success, returns an Array of GameHighScore objects." +msgstr "" + +#: of telebot.TeleBot.get_me:1 +msgid "" +"A simple method for testing your bot's authentication token. Requires no " +"parameters. Returns basic information about the bot in form of a User " +"object." +msgstr "" + +#: of telebot.TeleBot.get_me:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getme" +msgstr "" + +#: of telebot.TeleBot.get_my_commands:1 +msgid "" +"Use this method to get the current list of the bot's commands. Returns " +"List of BotCommand on success." +msgstr "" + +#: of telebot.TeleBot.get_my_commands:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" +msgstr "" + +#: of telebot.TeleBot.get_my_commands:15 +msgid "List of BotCommand on success." +msgstr "" + +#: of telebot.TeleBot.get_my_commands:16 +msgid ":obj:`list` of :class:`telebot.types.BotCommand`" +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:1 +msgid "" +"Use this method to get the current default administrator rights of the " +"bot. Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmydefaultadministratorrights" +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:6 +msgid "" +"Pass True to get the default administrator rights of the bot in channels." +" Otherwise, the default administrator rights of the bot for groups and " +"supergroups will be returned." +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:9 +msgid "Returns ChatAdministratorRights on success." +msgstr "" + +#: of telebot.TeleBot.get_my_default_administrator_rights:10 +msgid ":class:`telebot.types.ChatAdministratorRights`" +msgstr "" + +#: of telebot.TeleBot.get_state:1 +msgid "" +"Gets current state of a user. Not recommended to use this method. But it " +"is ok for debugging." +msgstr "" + +#: of telebot.TeleBot.get_state:10 +msgid "state of a user" +msgstr "" + +#: of telebot.TeleBot.get_state:11 +msgid ":obj:`int` or :obj:`str` or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:1 +msgid "" +"Use this method to get a sticker set. On success, a StickerSet object is " +"returned." +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:8 +msgid "On success, a StickerSet object is returned." +msgstr "" + +#: of telebot.TeleBot.get_sticker_set:9 telebot.TeleBot.set_chat_sticker_set:14 +msgid ":class:`telebot.types.StickerSet`" +msgstr "" + +#: of telebot.TeleBot.get_updates:1 +msgid "" +"Use this method to receive incoming updates using long polling (wiki). An" +" Array of Update objects is returned." +msgstr "" + +#: of telebot.TeleBot.get_updates:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" +msgstr "" + +#: of telebot.TeleBot.get_updates:6 +msgid "" +"Identifier of the first update to be returned. Must be greater by one " +"than the highest among the identifiers of previously received updates. By" +" default, updates starting with the earliest unconfirmed update are " +"returned. An update is considered confirmed as soon as getUpdates is " +"called with an offset higher than its update_id. The negative offset can " +"be specified to retrieve updates starting from -offset update from the " +"end of the updates queue. All previous updates will forgotten." +msgstr "" + +#: of telebot.TeleBot.get_updates:12 +msgid "" +"Limits the number of updates to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.TeleBot.get_updates:15 telebot.TeleBot.get_webhook_info:6 +#: telebot.TeleBot.polling:21 +msgid "Request connection timeout" +msgstr "" + +#: of telebot.TeleBot.get_updates:18 +msgid "Array of string. List the types of updates you want your bot to receive." +msgstr "" + +#: of telebot.TeleBot.get_updates:21 +msgid "Timeout in seconds for long polling." +msgstr "" + +#: of telebot.TeleBot.get_updates:24 +msgid "An Array of Update objects is returned." +msgstr "" + +#: of telebot.TeleBot.get_updates:25 +msgid ":obj:`list` of :class:`telebot.types.Update`" +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:1 +msgid "" +"Use this method to get a list of profile pictures for a user. Returns a " +":class:`telebot.types.UserProfilePhotos` object." +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserprofilephotos" +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:9 +msgid "" +"Sequential number of the first photo to be returned. By default, all " +"photos are returned." +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:12 +msgid "" +"Limits the number of photos to be retrieved. Values between 1-100 are " +"accepted. Defaults to 100." +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:15 +msgid "" +"`UserProfilePhotos " +"`_" +msgstr "" + +#: of telebot.TeleBot.get_user_profile_photos:16 +msgid ":class:`telebot.types.UserProfilePhotos`" +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:1 +msgid "" +"Use this method to get current webhook status. Requires no parameters. On" +" success, returns a WebhookInfo object. If the bot is using getUpdates, " +"will return an object with the url field empty." +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:9 +msgid "On success, returns a WebhookInfo object." +msgstr "" + +#: of telebot.TeleBot.get_webhook_info:10 +msgid ":class:`telebot.types.WebhookInfo`" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:1 +msgid "" +"Wrap polling with infinite loop and exception handling to avoid bot stops" +" polling." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:5 telebot.TeleBot.polling:13 +msgid "Install watchdog and psutil before using restart_on_change option." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:7 +msgid "Request connection timeout." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:10 telebot.TeleBot.polling:27 +msgid "Timeout in seconds for long polling (see API docs)" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:13 telebot.TeleBot.polling:24 +msgid "skip old updates" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:16 telebot.TeleBot.polling:30 +msgid "" +"Custom (different from logger itself) logging level for infinity_polling " +"logging. Use logger levels from logging as a value. None/NOTSET = no " +"error logging" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:20 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. 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." +msgstr "" + +#: of telebot.TeleBot.infinity_polling:29 telebot.TeleBot.polling:47 +msgid "Restart a file on file(s) change. Defaults to False" +msgstr "" + +#: of telebot.TeleBot.infinity_polling:32 +msgid "Path to watch for changes. Defaults to current directory" +msgstr "" + +#: of telebot.TeleBot.inline_handler:1 +msgid "" +"Handles new incoming inline query. As a parameter to the decorator " +"function, it passes :class:`telebot.types.InlineQuery` object." +msgstr "" + +#: of telebot.TeleBot.leave_chat:1 +msgid "" +"Use this method for your bot to leave a group, supergroup or channel. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.leave_chat:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#leavechat" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:1 +msgid "Load next step handlers from save file" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" load handlers from file with the help of FileHandlerBackend and is only " +"recommended to use if next_step_backend was assigned as " +"FileHandlerBackend before entering this function" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:8 +msgid "" +"Filename of the file where handlers was saved, defaults to \"./.handler-" +"saves/step.save\"" +msgstr "" + +#: of telebot.TeleBot.load_next_step_handlers:11 +msgid "" +"If True is passed, after the loading file will be deleted, defaults to " +"True" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:1 +msgid "Load reply handlers from save file" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:3 +msgid "" +"This function is left to keep backward compatibility whose purpose was to" +" load handlers from file with the help of FileHandlerBackend and is only " +"recommended to use if reply_backend was assigned as FileHandlerBackend " +"before entering this function" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:7 +msgid "" +"Filename of the file where handlers was saved, defaults to \"./.handler-" +"saves/reply.save\"" +msgstr "" + +#: of telebot.TeleBot.load_reply_handlers:10 +msgid "" +"If True is passed, after the loading file will be deleted, defaults to " +"True, defaults to True" +msgstr "" + +#: of telebot.TeleBot.log_out:1 +msgid "" +"Use this method to log out from the cloud Bot API server before launching" +" the bot locally. You MUST log out the bot before running it locally, " +"otherwise there is no guarantee that the bot will receive updates. After " +"a successful call, you can immediately log in on a local server, but will" +" not be able to log in back to the cloud Bot API server for 10 minutes. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.log_out:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" +msgstr "" + +#: of telebot.TeleBot.message_handler:1 +msgid "" +"Handles New incoming message of any kind - text, photo, sticker, etc. As " +"a parameter to the decorator function, it passes " +":class:`telebot.types.Message` object. All message handlers are tested in" +" the order they were added." +msgstr "" + +#: of telebot.TeleBot.message_handler:5 telebot.TeleBot.middleware_handler:7 +#: telebot.TeleBot.register_middleware_handler:6 +msgid "Example:" +msgstr "" + +#: of telebot.TeleBot.message_handler:7 +msgid "Usage of message_handler" +msgstr "" + +#: of telebot.TeleBot.message_handler:40 +msgid "" +"Optional lambda function. The lambda receives the message to test as the " +"first parameter. It must return True if the command should handle the " +"message." +msgstr "" + +#: of telebot.TeleBot.message_handler:52 +#: telebot.TeleBot.register_edited_channel_post_handler:23 +#: telebot.TeleBot.register_inline_handler:14 +#: telebot.TeleBot.register_pre_checkout_query_handler:13 +msgid "decorated function" +msgstr "" + +#: of telebot.TeleBot.middleware_handler:1 +msgid "Function-based middleware handler decorator." +msgstr "" + +#: of telebot.TeleBot.middleware_handler:3 +msgid "" +"This decorator can be used to decorate functions that must be handled as " +"middlewares before entering any other message handlers But, be careful " +"and check type of the update inside the handler if more than one " +"update_type is given" +msgstr "" + +#: of telebot.TeleBot.middleware_handler:9 +msgid "Usage of middleware_handler" +msgstr "" + +#: of telebot.TeleBot.middleware_handler:24 +#: telebot.TeleBot.register_middleware_handler:15 +msgid "" +"Optional list of update types that can be passed into the middleware " +"handler." +msgstr "" + +#: of telebot.TeleBot.middleware_handler:27 +msgid "function" +msgstr "" + +#: of telebot.TeleBot.my_chat_member_handler:1 +msgid "" +"Handles update in a status of a bot. For private chats, this update is " +"received only when the bot is blocked or unblocked by the user. As a " +"parameter to the decorator function, it passes " +":class:`telebot.types.ChatMemberUpdated` object." +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:1 +msgid "" +"Use this method to pin a message in a supergroup. The bot must be an " +"administrator in the chat for this to work and must have the appropriate " +"admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:11 +msgid "Identifier of a message to pin" +msgstr "" + +#: of telebot.TeleBot.pin_chat_message:14 +msgid "" +"Pass True, if it is not necessary to send a notification to all group " +"members about the new pinned message" +msgstr "" + +#: of telebot.TeleBot.poll_answer_handler:1 +msgid "" +"Handles change of user's answer in a non-anonymous poll(when user changes" +" the vote). Bots receive new votes only in polls that were sent by the " +"bot itself. As a parameter to the decorator function, it passes " +":class:`telebot.types.PollAnswer` object." +msgstr "" + +#: of telebot.TeleBot.poll_handler:1 +msgid "" +"Handles new state of a poll. Bots receive only updates about stopped " +"polls and polls, which are sent by the bot As a parameter to the " +"decorator function, it passes :class:`telebot.types.Poll` object." +msgstr "" + +#: of telebot.TeleBot.polling:1 +msgid "" +"This function creates a new Thread that calls an internal " +"__retrieve_updates function. This allows the bot to retrieve Updates " +"automatically and notify listeners and message handlers accordingly." +msgstr "" + +#: of telebot.TeleBot.polling:4 +msgid "Warning: Do not call this function more than once!" +msgstr "" + +#: of telebot.TeleBot.polling:6 +msgid "Always gets updates." +msgstr "" + +#: of telebot.TeleBot.polling:8 +msgid "Use :meth:`infinity_polling` instead." +msgstr "" + +#: of telebot.TeleBot.polling:15 +msgid "Delay between two update retrivals" +msgstr "" + +#: of telebot.TeleBot.polling:18 +msgid "Do not stop polling when an ApiException occurs." +msgstr "" + +#: of telebot.TeleBot.polling:34 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. 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." +msgstr "" + +#: of telebot.TeleBot.polling:34 +msgid "" +"A list of the update types you want your bot to receive. For example, " +"specify [“message”, “edited_channel_post”, “callback_query”] to only " +"receive updates of these types. See util.update_types for a complete list" +" of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.TeleBot.polling:40 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.polling:44 +msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." +msgstr "" + +#: of telebot.TeleBot.polling:50 +msgid "Path to watch for changes. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.pre_checkout_query_handler:1 +msgid "" +"New incoming pre-checkout query. Contains full information about " +"checkout. As a parameter to the decorator function, it passes " +":class:`telebot.types.PreCheckoutQuery` object." +msgstr "" + +#: of telebot.TeleBot.process_new_updates:1 +msgid "" +"Processes new updates. Just pass list of subclasses of Update to this " +"method." +msgstr "" + +#: of telebot.TeleBot.process_new_updates:3 +msgid "List of :class:`telebot.types.Update` objects." +msgstr "" + +#: of telebot.TeleBot.process_new_updates +msgid "return None" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:1 +msgid "" +"Use this method to promote or demote a user in a supergroup or a channel." +" The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Pass False for all boolean parameters " +"to demote a user." +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#promotechatmember" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:7 +msgid "" +"Unique identifier for the target chat or username of the target channel (" +" in the format @channelusername)" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:14 +msgid "" +"Pass True, if the administrator can change chat title, photo and other " +"settings" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:17 +msgid "Pass True, if the administrator can create channel posts, channels only" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:20 +msgid "" +"Pass True, if the administrator can edit messages of other users, " +"channels only" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:23 +msgid "Pass True, if the administrator can delete messages of other users" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:26 +msgid "Pass True, if the administrator can invite new users to the chat" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:29 +msgid "Pass True, if the administrator can restrict, ban or unban chat members" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:32 +msgid "Pass True, if the administrator can pin messages, supergroups only" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:35 +msgid "" +"Pass True, if the administrator can add new administrators with a subset " +"of his own privileges or demote administrators that he has promoted, " +"directly or indirectly (promoted by administrators that were appointed by" +" him)" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:40 +msgid "Pass True, if the administrator's presence in the chat is hidden" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:43 +msgid "" +"Pass True, if the administrator can access the chat event log, chat " +"statistics, message statistics in channels, see channel members, see " +"anonymous administrators in supergroups and ignore slow mode. Implied by " +"any other administrator privilege" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:49 +msgid "" +"Pass True, if the administrator can manage voice chats For now, bots can " +"use this privilege only for passing to other administrators." +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:53 +msgid "Deprecated, use can_manage_video_chats." +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:56 +msgid "" +"Pass True if the user is allowed to create, rename, close, and reopen " +"forum topics, supergroups only" +msgstr "" + +#: of telebot.TeleBot.register_callback_query_handler:1 +msgid "Registers callback query handler." +msgstr "" + +#: of telebot.TeleBot.register_callback_query_handler:3 +#: telebot.TeleBot.register_channel_post_handler:3 +#: telebot.TeleBot.register_chat_join_request_handler:3 +#: telebot.TeleBot.register_chat_member_handler:3 +#: telebot.TeleBot.register_chosen_inline_handler:3 +#: telebot.TeleBot.register_edited_channel_post_handler:3 +#: telebot.TeleBot.register_edited_message_handler:3 +#: telebot.TeleBot.register_inline_handler:3 +#: telebot.TeleBot.register_message_handler:3 +#: telebot.TeleBot.register_my_chat_member_handler:3 +#: telebot.TeleBot.register_poll_answer_handler:3 +#: telebot.TeleBot.register_poll_handler:3 +#: telebot.TeleBot.register_pre_checkout_query_handler:3 +#: telebot.TeleBot.register_shipping_query_handler:3 +msgid "function to be called" +msgstr "" + +#: of telebot.TeleBot.register_callback_query_handler:9 +#: telebot.TeleBot.register_channel_post_handler:18 +#: telebot.TeleBot.register_chat_join_request_handler:9 +#: telebot.TeleBot.register_chat_member_handler:9 +#: telebot.TeleBot.register_chosen_inline_handler:9 +#: telebot.TeleBot.register_edited_channel_post_handler:18 +#: telebot.TeleBot.register_edited_message_handler:21 +#: telebot.TeleBot.register_inline_handler:9 +#: telebot.TeleBot.register_message_handler:21 +#: telebot.TeleBot.register_my_chat_member_handler:9 +#: telebot.TeleBot.register_poll_answer_handler:9 +#: telebot.TeleBot.register_poll_handler:9 +#: telebot.TeleBot.register_pre_checkout_query_handler:8 +#: telebot.TeleBot.register_shipping_query_handler:9 +msgid "" +"True if you need to pass TeleBot instance to handler(useful for " +"separating handlers into different files)" +msgstr "" + +#: of telebot.TeleBot.register_channel_post_handler:1 +msgid "Registers channel post message handler." +msgstr "" + +#: of telebot.TeleBot.register_channel_post_handler:9 +#: telebot.TeleBot.register_edited_channel_post_handler:9 +#: telebot.TeleBot.register_edited_message_handler:9 +#: telebot.TeleBot.register_message_handler:9 +msgid "list of commands" +msgstr "" + +#: of telebot.TeleBot.register_channel_post_handler:12 +#: telebot.TeleBot.register_edited_channel_post_handler:12 +#: telebot.TeleBot.register_edited_message_handler:12 +msgid "Regular expression" +msgstr "" + +#: of telebot.TeleBot.register_chat_join_request_handler:1 +msgid "Registers chat join request handler." +msgstr "" + +#: of telebot.TeleBot.register_chat_member_handler:1 +msgid "Registers chat member handler." +msgstr "" + +#: of telebot.TeleBot.register_chat_member_handler:14 +msgid ":return:None" +msgstr "" + +#: of telebot.TeleBot.register_chosen_inline_handler:1 +msgid "Registers chosen inline handler." +msgstr "" + +#: of telebot.TeleBot.register_edited_channel_post_handler:1 +msgid "Registers edited channel post message handler." +msgstr "" + +#: of telebot.TeleBot.register_edited_message_handler:1 +msgid "Registers edited message handler." +msgstr "" + +#: of telebot.TeleBot.register_edited_message_handler:18 +msgid "True for private chat" +msgstr "" + +#: of telebot.TeleBot.register_for_reply:1 +#: telebot.TeleBot.register_for_reply_by_message_id:1 +msgid "" +"Registers a callback function to be notified when a reply to `message` " +"arrives." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:3 +#: telebot.TeleBot.register_for_reply_by_message_id:3 +msgid "" +"Warning: In case `callback` as lambda function, saving reply handlers " +"will not work." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:5 +msgid "The message for which we are awaiting a reply." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:8 +#: telebot.TeleBot.register_for_reply_by_message_id:8 +msgid "" +"The callback function to be called when a reply arrives. Must accept one " +"`message` parameter, which will contain the replied message." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:12 +#: telebot.TeleBot.register_for_reply_by_message_id:12 +msgid "Optional arguments for the callback function." +msgstr "" + +#: of telebot.TeleBot.register_for_reply:13 +#: telebot.TeleBot.register_for_reply_by_message_id:13 +msgid "Optional keyword arguments for the callback function." +msgstr "" + +#: of telebot.TeleBot.register_for_reply_by_message_id:5 +msgid "The id of the message for which we are awaiting a reply." +msgstr "" + +#: of telebot.TeleBot.register_inline_handler:1 +msgid "Registers inline handler." +msgstr "" + +#: of telebot.TeleBot.register_message_handler:1 +msgid "Registers message handler." +msgstr "" + +#: of telebot.TeleBot.register_message_handler:18 +msgid "List of chat types" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:1 +msgid "Adds function-based middleware handler." +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:3 +msgid "" +"This function will register your decorator function. Function-based " +"middlewares are executed before handlers. But, be careful and check type " +"of the update inside the handler if more than one update_type is given" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:8 +msgid "bot = TeleBot('TOKEN')" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:10 +msgid "" +"bot.register_middleware_handler(print_channel_post_text, " +"update_types=['channel_post', 'edited_channel_post'])" +msgstr "" + +#: of telebot.TeleBot.register_middleware_handler:12 +msgid "Function that will be used as a middleware handler." +msgstr "" + +#: of telebot.TeleBot.register_my_chat_member_handler:1 +msgid "Registers my chat member handler." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:1 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:1 +msgid "" +"Registers a callback function to be notified when new message arrives " +"after `message`." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:3 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:3 +msgid "" +"Warning: In case `callback` as lambda function, saving next step handlers" +" will not work." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:5 +msgid "The message for which we want to handle new message in the same chat." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:8 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:8 +msgid "The callback function which next new message arrives." +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler:11 +#: telebot.TeleBot.register_next_step_handler:13 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:11 +#: telebot.TeleBot.register_next_step_handler_by_chat_id:13 +msgid "Args to pass in callback func" +msgstr "" + +#: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 +msgid "The chat for which we want to handle new message." +msgstr "" + +#: of telebot.TeleBot.register_poll_answer_handler:1 +msgid "Registers poll answer handler." +msgstr "" + +#: of telebot.TeleBot.register_poll_handler:1 +msgid "Registers poll handler." +msgstr "" + +#: of telebot.TeleBot.register_pre_checkout_query_handler:1 +msgid "Registers pre-checkout request handler." +msgstr "" + +#: of telebot.TeleBot.register_shipping_query_handler:1 +msgid "Registers shipping query handler." +msgstr "" + +#: of telebot.TeleBot.remove_webhook:1 +msgid "Deletes webhooks using set_webhook() function." +msgstr "" + +#: of telebot.TeleBot.reopen_forum_topic:1 +msgid "" +"Use this method to reopen a closed topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.reopen_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopenforumtopic" +msgstr "" + +#: of telebot.TeleBot.reopen_forum_topic:10 +msgid "Identifier of the topic to reopen" +msgstr "" + +#: of telebot.TeleBot.reply_to:1 +msgid "" +"Convenience function for `send_message(message.chat.id, text, " +"reply_to_message_id=message.message_id, **kwargs)`" +msgstr "" + +#: of telebot.TeleBot.reply_to:3 +msgid "Instance of :class:`telebot.types.Message`" +msgstr "" + +#: of telebot.TeleBot.reply_to:6 +msgid "Text of the message." +msgstr "" + +#: of telebot.TeleBot.reply_to:9 +msgid "" +"Additional keyword arguments which are passed to " +":meth:`telebot.TeleBot.send_message`" +msgstr "" + +#: of telebot.TeleBot.reset_data:1 +msgid "Reset data for a user in chat." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:1 +msgid "" +"Use this method to restrict a user in a supergroup. The bot must be an " +"administrator in the supergroup for this to work and must have the " +"appropriate admin rights. Pass True for all boolean parameters to lift " +"restrictions from a user." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#restrictchatmember" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:14 +msgid "" +"Date when restrictions will be lifted for the user, unix time. If user is" +" restricted for more than 366 days or less than 30 seconds from the " +"current time, they are considered to be restricted forever" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:19 +msgid "" +"Pass True, if the user can send text messages, contacts, locations and " +"venues" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:22 +msgid "" +"Pass True, if the user can send audios, documents, photos, videos, video " +"notes and voice notes, implies can_send_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:26 +msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:29 +msgid "" +"Pass True, if the user can send animations, games, stickers and use " +"inline bots, implies can_send_media_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:32 +msgid "" +"Pass True, if the user may add web page previews to their messages, " +"implies can_send_media_messages" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:36 +msgid "" +"Pass True, if the user is allowed to change the chat title, photo and " +"other settings. Ignored in public supergroups" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:40 +msgid "" +"Pass True, if the user is allowed to invite new users to the chat, " +"implies can_invite_users" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:44 +msgid "" +"Pass True, if the user is allowed to pin messages. Ignored in public " +"supergroups" +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:47 +#: telebot.TeleBot.set_chat_permissions:14 telebot.TeleBot.unban_chat_member:19 +msgid "True on success" +msgstr "" + +#: of telebot.TeleBot.retrieve_data:1 +msgid "Returns context manager with data for a user in chat." +msgstr "" + +#: of telebot.TeleBot.retrieve_data:6 +msgid "Chat's unique identifier, defaults to user_id" +msgstr "" + +#: of telebot.TeleBot.retrieve_data:9 +msgid "Context manager with data for a user in chat" +msgstr "" + +#: of telebot.TeleBot.revoke_chat_invite_link:1 +msgid "" +"Use this method to revoke an invite link created by the bot. Note: If the" +" primary link is revoked, a new link is automatically generated The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.revoke_chat_invite_link:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#revokechatinvitelink" +msgstr "" + +#: of telebot.TeleBot.revoke_chat_invite_link:11 +msgid "The invite link to revoke" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:1 +msgid "This class sets webhooks and listens to a given url and port." +msgstr "" + +#: of telebot.TeleBot.run_webhooks:3 +msgid "Requires fastapi, uvicorn, and latest version of starlette." +msgstr "" + +#: of telebot.TeleBot.run_webhooks:5 +msgid "IP address to listen to, defaults to \"127.0.0.1\"" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:8 +msgid "A port which will be used to listen to webhooks., defaults to 443" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:11 +msgid "Path to the webhook. Defaults to /token, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:14 +msgid "Path to the certificate file, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:17 +msgid "Path to the certificate key file, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:20 +msgid "Webhook URL to be set, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:23 +msgid "" +"Maximum allowed number of simultaneous HTTPS connections to the webhook " +"for update delivery, 1-100. Defaults to 40. Use lower values to limit the" +" load on your bot's server, and higher values to increase your bot's " +"throughput., defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:28 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all updates " +"regardless of type (default). If not specified, the previous setting will" +" be used. defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:33 telebot.TeleBot.set_webhook:32 +msgid "" +"The fixed IP address which will be used to send webhook requests instead " +"of the IP address resolved through DNS, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:42 +msgid "Secret token to be used to verify the webhook request, defaults to None" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:45 +msgid "Length of a secret token, defaults to 20" +msgstr "" + +#: of telebot.TeleBot.run_webhooks telebot.custom_filters.TextFilter +msgid "Raises" +msgstr "" + +#: of telebot.TeleBot.run_webhooks:48 +msgid "If necessary libraries were not installed." +msgstr "" + +#: of telebot.TeleBot.send_animation:1 +msgid "" +"Use this method to send animation files (GIF or H.264/MPEG-4 AVC video " +"without sound). On success, the sent Message is returned. Bots can " +"currently send animation files of up to 50 MB in size, this limit may be " +"changed in the future." +msgstr "" + +#: of telebot.TeleBot.send_animation:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" +msgstr "" + +#: of telebot.TeleBot.send_animation:9 +msgid "" +"Animation to send. Pass a file_id as String to send an animation that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an animation from the Internet, or upload a " +"new animation using multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.send_animation:13 +msgid "Duration of sent animation in seconds" +msgstr "" + +#: of telebot.TeleBot.send_animation:16 +msgid "Animation width" +msgstr "" + +#: of telebot.TeleBot.send_animation:19 +msgid "Animation height" +msgstr "" + +#: of telebot.TeleBot.send_animation:22 telebot.TeleBot.send_video:20 +#: telebot.TeleBot.send_video_note:33 +msgid "" +"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 ." +msgstr "" + +#: of telebot.TeleBot.send_animation:28 +msgid "" +"Animation caption (may also be used when resending animation by file_id)," +" 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_animation:31 +msgid "Mode for parsing entities in the animation caption" +msgstr "" + +#: of telebot.TeleBot.send_animation:51 telebot.TeleBot.send_video:29 +msgid "" +"List of special entities that appear in the caption, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_animation:57 telebot.TeleBot.send_video:58 +msgid "Identifier of a message thread, in which the video will be sent" +msgstr "" + +#: of telebot.TeleBot.send_audio:1 +msgid "" +"Use this method to send audio files, if you want Telegram clients to " +"display them in the music player. Your audio must be in the .MP3 or .M4A " +"format. On success, the sent Message is returned. Bots can currently send" +" audio files of up to 50 MB in size, this limit may be changed in the " +"future." +msgstr "" + +#: of telebot.TeleBot.send_audio:5 +msgid "For sending voice messages, use the send_voice method instead." +msgstr "" + +#: of telebot.TeleBot.send_audio:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendaudio" +msgstr "" + +#: of telebot.TeleBot.send_audio:12 +msgid "" +"Audio file to send. Pass a file_id as String to send an audio file that " +"exists on the Telegram servers (recommended), pass an HTTP URL as a " +"String for Telegram to get an audio file from the Internet, or upload a " +"new one using multipart/form-data. Audio must be in the .MP3 or .M4A " +"format." +msgstr "" + +#: of telebot.TeleBot.send_audio:17 +msgid "Audio caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_audio:20 +msgid "Duration of the audio in seconds" +msgstr "" + +#: of telebot.TeleBot.send_audio:23 +msgid "Performer" +msgstr "" + +#: of telebot.TeleBot.send_audio:26 +msgid "Track name" +msgstr "" + +#: of telebot.TeleBot.send_audio:36 +msgid "" +"Mode for parsing entities in the audio caption. See formatting options " +"for more details." +msgstr "" + +#: of telebot.TeleBot.send_audio:45 +msgid "" +"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 " +msgstr "" + +#: of telebot.TeleBot.send_audio:51 telebot.TeleBot.send_document:35 +#: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:37 +msgid "" +"A JSON-serialized list of special entities that appear in the caption, " +"which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_chat_action:1 +msgid "" +"Use this method when you need to tell the user that something is " +"happening on the bot's side. The status is set for 5 seconds or less " +"(when a message arrives from your bot, Telegram clients clear its typing " +"status). Returns True on success." +msgstr "" + +#: of telebot.TeleBot.send_chat_action:5 +msgid "" +"Example: The ImageBot needs some time to process a request and upload the" +" image. Instead of sending a text message along the lines of “Retrieving " +"image, please wait…”, the bot may use sendChatAction with action = " +"upload_photo. The user will see a “sending photo” status for the bot." +msgstr "" + +#: of telebot.TeleBot.send_chat_action:8 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" +msgstr "" + +#: of telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 +#: telebot.TeleBot.send_poll:6 telebot.TeleBot.send_venue:5 +#: telebot.TeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "" + +#: of telebot.TeleBot.send_chat_action:13 +msgid "" +"Type of action to broadcast. Choose one, depending on what the user is " +"about to receive: typing for text messages, upload_photo for photos, " +"record_video or upload_video for videos, record_voice or upload_voice for" +" voice notes, upload_document for general files, choose_sticker for " +"stickers, find_location for location data, record_video_note or " +"upload_video_note for video notes." +msgstr "" + +#: of telebot.TeleBot.send_contact:1 +msgid "" +"Use this method to send phone contacts. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.TeleBot.send_contact:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" +msgstr "" + +#: of telebot.TeleBot.send_contact:8 +msgid "Contact's phone number" +msgstr "" + +#: of telebot.TeleBot.send_contact:11 +msgid "Contact's first name" +msgstr "" + +#: of telebot.TeleBot.send_contact:14 +msgid "Contact's last name" +msgstr "" + +#: of telebot.TeleBot.send_contact:17 +msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" +msgstr "" + +#: of telebot.TeleBot.send_contact:34 telebot.TeleBot.send_game:23 +#: telebot.TeleBot.send_venue:41 +msgid "" +"Pass True, if the message should be sent even if one of the specified " +"replied-to messages is not found." +msgstr "" + +#: of telebot.TeleBot.send_contact:41 telebot.TeleBot.send_venue:54 +msgid "The thread identifier of a message from which the reply will be sent" +msgstr "" + +#: of telebot.TeleBot.send_dice:1 +msgid "" +"Use this method to send an animated emoji that will display a random " +"value. On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_dice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" +msgstr "" + +#: of telebot.TeleBot.send_dice:8 +msgid "" +"Emoji on which the dice throw animation is based. Currently, must be one " +"of “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, or “🎰”. Dice can have values 1-6 for “🎲”, “🎯”" +" and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " +"to “🎲”" +msgstr "" + +#: of telebot.TeleBot.send_dice:29 +msgid "Protects the contents of the sent message from forwarding" +msgstr "" + +#: of telebot.TeleBot.send_document:1 +msgid "Use this method to send general files." +msgstr "" + +#: of telebot.TeleBot.send_document:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#senddocument" +msgstr "" + +#: of telebot.TeleBot.send_document:8 +msgid "" +"(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" +msgstr "" + +#: of telebot.TeleBot.send_document:15 +msgid "" +"Document caption (may also be used when resending documents by file_id), " +"0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_document:23 +msgid "Mode for parsing entities in the document caption" +msgstr "" + +#: of telebot.TeleBot.send_document:32 +msgid "" +"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 " +msgstr "" + +#: of telebot.TeleBot.send_document:41 +msgid "" +"allows to define file name that will be visible in the Telegram instead " +"of original file name" +msgstr "" + +#: of telebot.TeleBot.send_document:44 +msgid "" +"Disables automatic server-side content type detection for files uploaded " +"using multipart/form-data" +msgstr "" + +#: of telebot.TeleBot.send_document:47 telebot.TeleBot.send_sticker:33 +#: telebot.TeleBot.send_video:55 +msgid "function typo miss compatibility: do not use it" +msgstr "" + +#: of telebot.TeleBot.send_document:53 telebot.TeleBot.send_sticker:36 +msgid "The thread to which the message will be sent" +msgstr "" + +#: of telebot.TeleBot.send_game:1 +msgid "Used to send the game." +msgstr "" + +#: of telebot.TeleBot.send_game:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendgame" +msgstr "" + +#: of telebot.TeleBot.send_game:8 +msgid "" +"Short name of the game, serves as the unique identifier for the game. Set" +" up your games via @BotFather." +msgstr "" + +#: of telebot.TeleBot.send_game:20 +msgid "Timeout in seconds for waiting for a response from the bot." +msgstr "" + +#: of telebot.TeleBot.send_game:26 +msgid "" +"Pass True, if content of the message needs to be protected from being " +"viewed by the bot." +msgstr "" + +#: of telebot.TeleBot.send_game:29 +msgid "" +"The identifier of a message thread, in which the game message will be " +"sent." +msgstr "" + +#: of telebot.TeleBot.send_game:33 telebot.TeleBot.send_invoice:102 +#: telebot.TeleBot.send_poll:71 +msgid ":obj:`types.Message`" +msgstr "" + +#: of telebot.TeleBot.send_invoice:1 +msgid "Sends invoice." +msgstr "" + +#: of telebot.TeleBot.send_invoice:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendinvoice" +msgstr "" + +#: of telebot.TeleBot.send_invoice:5 +msgid "Unique identifier for the target private chat" +msgstr "" + +#: of telebot.TeleBot.send_invoice:29 +msgid "" +"Unique deep-linking parameter that can be used to generate this invoice " +"when used as a start parameter" +msgstr "" + +#: of telebot.TeleBot.send_invoice:33 +msgid "" +"URL of the product photo for the invoice. Can be a photo of the goods or " +"a marketing image for a service. People like it better when they see what" +" they are paying for." +msgstr "" + +#: of telebot.TeleBot.send_invoice:73 +msgid "" +"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" +msgstr "" + +#: of telebot.TeleBot.send_invoice:81 telebot.TeleBot.set_webhook:39 +msgid "Timeout of a request, defaults to None" +msgstr "" + +#: of telebot.TeleBot.send_invoice:98 +msgid "" +"The identifier of a message thread, in which the invoice message will be " +"sent" +msgstr "" + +#: of telebot.TeleBot.send_location:1 +msgid "" +"Use this method to send point on the map. On success, the sent Message is" +" returned." +msgstr "" + +#: of telebot.TeleBot.send_location:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendlocation" +msgstr "" + +#: of telebot.TeleBot.send_location:8 +msgid "Latitude of the location" +msgstr "" + +#: of telebot.TeleBot.send_location:11 +msgid "Longitude of the location" +msgstr "" + +#: of telebot.TeleBot.send_location:14 +msgid "" +"Period in seconds for which the location will be updated (see Live " +"Locations, should be between 60 and 86400." +msgstr "" + +#: of telebot.TeleBot.send_location:34 +msgid "" +"For live locations, a direction in which the user is moving, in degrees. " +"Must be between 1 and 360 if specified." +msgstr "" + +#: of telebot.TeleBot.send_location:37 +msgid "" +"For live locations, a maximum distance for proximity alerts about " +"approaching another chat member, in meters. Must be between 1 and 100000 " +"if specified." +msgstr "" + +#: of telebot.TeleBot.send_media_group:1 +msgid "" +"Use this method to send a group of photos, videos, documents or audios as" +" an album. Documents and audio files can be only grouped in an album with" +" messages of the same type. On success, an array of Messages that were " +"sent is returned." +msgstr "" + +#: of telebot.TeleBot.send_media_group:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" +msgstr "" + +#: of telebot.TeleBot.send_media_group:9 +msgid "" +"A JSON-serialized array describing messages to be sent, must include 2-10" +" items" +msgstr "" + +#: of telebot.TeleBot.send_media_group:12 +msgid "" +"Sends the messages silently. Users will receive a notification with no " +"sound." +msgstr "" + +#: of telebot.TeleBot.send_media_group:27 +msgid "Identifier of a message thread, in which the media group will be sent" +msgstr "" + +#: of telebot.TeleBot.send_media_group:30 +msgid "On success, an array of Messages that were sent is returned." +msgstr "" + +#: of telebot.TeleBot.send_message:1 +msgid "Use this method to send text messages." +msgstr "" + +#: of telebot.TeleBot.send_message:3 +msgid "" +"Warning: Do not send more than about 4096 characters each message, " +"otherwise you'll risk an HTTP 414 error. If you must send more than 4096 " +"characters, use the `split_string` or `smart_split` function in util.py." +msgstr "" + +#: of telebot.TeleBot.send_message:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" +msgstr "" + +#: of telebot.TeleBot.send_message:12 +msgid "Text of the message to be sent" +msgstr "" + +#: of telebot.TeleBot.send_message:18 +msgid "" +"List of special entities that appear in message text, which can be " +"specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_message:27 +msgid "" +"If True, the message content will be hidden for all users except for the " +"target user" +msgstr "" + +#: of telebot.TeleBot.send_photo:1 +msgid "Use this method to send photos. On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_photo:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" +msgstr "" + +#: of telebot.TeleBot.send_photo:8 +msgid "" +"Photo to send. Pass a file_id as String to send a photo that exists on " +"the Telegram servers (recommended), pass an HTTP URL as a String for " +"Telegram to get a photo from the Internet, or upload a new photo using " +"multipart/form-data. The photo must be at most 10 MB in size. The photo's" +" width and height must not exceed 10000 in total. Width and height ratio " +"must be at most 20." +msgstr "" + +#: of telebot.TeleBot.send_photo:13 +msgid "" +"Photo caption (may also be used when resending photos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_photo:16 +msgid "Mode for parsing entities in the photo caption." +msgstr "" + +#: of telebot.TeleBot.send_poll:1 +msgid "" +"Use this method to send a native poll. On success, the sent Message is " +"returned." +msgstr "" + +#: of telebot.TeleBot.send_poll:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" +msgstr "" + +#: of telebot.TeleBot.send_poll:9 +msgid "Poll question, 1-300 characters" +msgstr "" + +#: of telebot.TeleBot.send_poll:12 +msgid "" +"A JSON-serialized list of answer options, 2-10 strings 1-100 characters " +"each" +msgstr "" + +#: of telebot.TeleBot.send_poll:15 +msgid "True, if the poll needs to be anonymous, defaults to True" +msgstr "" + +#: of telebot.TeleBot.send_poll:18 +msgid "Poll type, “quiz” or “regular”, defaults to “regular”" +msgstr "" + +#: of telebot.TeleBot.send_poll:21 +msgid "" +"True, if the poll allows multiple answers, ignored for polls in quiz " +"mode, defaults to False" +msgstr "" + +#: of telebot.TeleBot.send_poll:24 +msgid "" +"0-based identifier of the correct answer option. Available only for polls" +" in quiz mode, defaults to None" +msgstr "" + +#: of telebot.TeleBot.send_poll:28 +msgid "" +"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" +msgstr "" + +#: of telebot.TeleBot.send_poll:32 +msgid "" +"Mode for parsing entities in the explanation. See formatting options for " +"more details." +msgstr "" + +#: of telebot.TeleBot.send_poll:35 +msgid "" +"Amount of time in seconds the poll will be active after creation, 5-600. " +"Can't be used together with close_date." +msgstr "" + +#: of telebot.TeleBot.send_poll:38 +msgid "Point in time (Unix timestamp) when the poll will be automatically closed." +msgstr "" + +#: of telebot.TeleBot.send_poll:41 +msgid "" +"Pass True, if the poll needs to be immediately closed. This can be useful" +" for poll preview." +msgstr "" + +#: of telebot.TeleBot.send_poll:50 +msgid "Pass True, if the poll allows multiple options to be voted simultaneously." +msgstr "" + +#: of telebot.TeleBot.send_poll:57 +msgid "Timeout in seconds for waiting for a response from the user." +msgstr "" + +#: of telebot.TeleBot.send_poll:60 +msgid "" +"A JSON-serialized list of special entities that appear in the " +"explanation, which can be specified instead of parse_mode" +msgstr "" + +#: of telebot.TeleBot.send_poll:67 +msgid "The identifier of a message thread, in which the poll will be sent" +msgstr "" + +#: of telebot.TeleBot.send_sticker:1 +msgid "" +"Use this method to send static .WEBP, animated .TGS, or video .WEBM " +"stickers. On success, the sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_sticker:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendsticker" +msgstr "" + +#: of telebot.TeleBot.send_sticker:9 +msgid "" +"Sticker 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 .webp file from the Internet, or upload a new one using" +" multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.send_sticker:21 +msgid "to disable the notification" +msgstr "" + +#: of telebot.TeleBot.send_venue:1 +msgid "" +"Use this method to send information about a venue. On success, the sent " +"Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_venue:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" +msgstr "" + +#: of telebot.TeleBot.send_venue:8 +msgid "Latitude of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:11 +msgid "Longitude of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:14 +msgid "Name of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:17 +msgid "Address of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:20 +msgid "Foursquare identifier of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:23 +msgid "" +"Foursquare type of the venue, if known. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.TeleBot.send_venue:45 +msgid "Google Places identifier of the venue" +msgstr "" + +#: of telebot.TeleBot.send_venue:48 +msgid "Google Places type of the venue." +msgstr "" + +#: of telebot.TeleBot.send_video:1 +msgid "" +"Use this method to send video files, Telegram clients support mp4 videos " +"(other formats may be sent as Document)." +msgstr "" + +#: of telebot.TeleBot.send_video:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" +msgstr "" + +#: of telebot.TeleBot.send_video:8 +msgid "" +"Video to send. You can either pass a file_id as String to resend a video " +"that is already on the Telegram servers, or upload a new video file using" +" multipart/form-data." +msgstr "" + +#: of telebot.TeleBot.send_video:11 telebot.TeleBot.send_video_note:13 +msgid "Duration of sent video in seconds" +msgstr "" + +#: of telebot.TeleBot.send_video:14 +msgid "Video width" +msgstr "" + +#: of telebot.TeleBot.send_video:17 +msgid "Video height" +msgstr "" + +#: of telebot.TeleBot.send_video:23 +msgid "" +"Video caption (may also be used when resending videos by file_id), 0-1024" +" characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_video:26 +msgid "Mode for parsing entities in the video caption" +msgstr "" + +#: of telebot.TeleBot.send_video:32 +msgid "Pass True, if the uploaded video is suitable for streaming" +msgstr "" + +#: of telebot.TeleBot.send_video_note:1 +msgid "" +"As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " +"to 1 minute long. Use this method to send video messages. On success, the" +" sent Message is returned." +msgstr "" + +#: of telebot.TeleBot.send_video_note:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" +msgstr "" + +#: of telebot.TeleBot.send_video_note:9 +msgid "" +"Video note to send. Pass a file_id as String to send a video note that " +"exists on the Telegram servers (recommended) or upload a new video using " +"multipart/form-data. Sending video notes by a URL is currently " +"unsupported" +msgstr "" + +#: of telebot.TeleBot.send_video_note:16 +msgid "Video width and height, i.e. diameter of the video message" +msgstr "" + +#: of telebot.TeleBot.send_video_note:45 +msgid "Identifier of a message thread, in which the video note will be sent" +msgstr "" + +#: of telebot.TeleBot.send_voice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.TeleBot.send_voice:5 +msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" +msgstr "" + +#: of telebot.TeleBot.send_voice:10 +msgid "" +"Audio 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." +msgstr "" + +#: of telebot.TeleBot.send_voice:14 +msgid "Voice message caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.TeleBot.send_voice:17 +msgid "Duration of the voice message in seconds" +msgstr "" + +#: of telebot.TeleBot.send_voice:28 +msgid "" +"Mode for parsing entities in the voice message caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.TeleBot.set_chat_administrator_custom_title:1 +msgid "" +"Use this method to set a custom title for an administrator in a " +"supergroup promoted by the bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_chat_administrator_custom_title:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatadministratorcustomtitle" +msgstr "" + +#: of telebot.TeleBot.set_chat_administrator_custom_title:13 +msgid "" +"New custom title for the administrator; 0-16 characters, emoji are not " +"allowed" +msgstr "" + +#: of telebot.TeleBot.set_chat_description:1 +msgid "" +"Use this method to change the description of a supergroup or a channel. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights." +msgstr "" + +#: of telebot.TeleBot.set_chat_description:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatdescription" +msgstr "" + +#: of telebot.TeleBot.set_chat_description:10 +msgid "Str: New chat description, 0-255 characters" +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:1 +msgid "" +"Use this method to change the bot's menu button in a private chat, or the" +" default menu button. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatmenubutton" +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:7 +msgid "" +"Unique identifier for the target private chat. If not specified, default " +"bot's menu button will be changed." +msgstr "" + +#: of telebot.TeleBot.set_chat_menu_button:11 +msgid "" +"A JSON-serialized object for the new bot's menu button. Defaults to " +"MenuButtonDefault" +msgstr "" + +#: of telebot.TeleBot.set_chat_permissions:1 +msgid "" +"Use this method to set default chat permissions for all members. The bot " +"must be an administrator in the group or a supergroup for this to work " +"and must have the can_restrict_members admin rights." +msgstr "" + +#: of telebot.TeleBot.set_chat_permissions:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatpermissions" +msgstr "" + +#: of telebot.TeleBot.set_chat_permissions:11 +msgid "New default chat permissions" +msgstr "" + +#: of telebot.TeleBot.set_chat_photo:1 +msgid "" +"Use this method to set a new profile photo for the chat. Photos can't be " +"changed for private chats. The bot must be an administrator in the chat " +"for this to work and must have the appropriate admin rights. Returns True" +" on success. Note: In regular groups (non-supergroups), this method will " +"only work if the ‘All Members Are Admins’ setting is off in the target " +"group." +msgstr "" + +#: of telebot.TeleBot.set_chat_photo:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" +msgstr "" + +#: of telebot.TeleBot.set_chat_photo:13 +msgid "InputFile: New chat photo, uploaded using multipart/form-data" +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:1 +msgid "" +"Use this method to set a new group sticker set for a supergroup. The bot " +"must be an administrator in the chat for this to work and must have the " +"appropriate administrator rights. Use the field can_set_sticker_set " +"optionally returned in getChat requests to check if the bot can use this " +"method. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setchatstickerset" +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:10 +msgid "Name of the sticker set to be set as the group sticker set" +msgstr "" + +#: of telebot.TeleBot.set_chat_sticker_set:13 +msgid "StickerSet object" +msgstr "" + +#: of telebot.TeleBot.set_chat_title:1 +msgid "" +"Use this method to change the title of a chat. Titles can't be changed " +"for private chats. The bot must be an administrator in the chat for this " +"to work and must have the appropriate admin rights. Returns True on " +"success. Note: In regular groups (non-supergroups), this method will only" +" work if the ‘All Members Are Admins’ setting is off in the target group." +msgstr "" + +#: of telebot.TeleBot.set_chat_title:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" +msgstr "" + +#: of telebot.TeleBot.set_chat_title:13 +msgid "New chat title, 1-255 characters" +msgstr "" + +#: of telebot.TeleBot.set_game_score:1 +msgid "Sets the value of points in the game to a specific user." +msgstr "" + +#: of telebot.TeleBot.set_game_score:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setgamescore" +msgstr "" + +#: of telebot.TeleBot.set_game_score:8 +msgid "New score, must be non-negative" +msgstr "" + +#: of telebot.TeleBot.set_game_score:11 +msgid "" +"Pass True, if the high score is allowed to decrease. This can be useful " +"when fixing mistakes or banning cheaters" +msgstr "" + +#: of telebot.TeleBot.set_game_score:23 +msgid "" +"Pass True, if the game message should not be automatically edited to " +"include the current scoreboard" +msgstr "" + +#: of telebot.TeleBot.set_game_score:26 +msgid "" +"On success, if the message was sent by the bot, returns the edited " +"Message, otherwise returns True." +msgstr "" + +#: of telebot.TeleBot.set_my_commands:1 +msgid "Use this method to change the list of the bot's commands." +msgstr "" + +#: of telebot.TeleBot.set_my_commands:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmycommands" +msgstr "" + +#: of telebot.TeleBot.set_my_commands:5 +msgid "List of BotCommand. At most 100 commands can be specified." +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:1 +msgid "" +"Use this method to change the default administrator rights requested by " +"the bot when it's added as an administrator to groups or channels. These " +"rights will be suggested to users, but they are are free to modify the " +"list before adding the bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:7 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmydefaultadministratorrights" +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:9 +msgid "" +"A JSON-serialized object describing new default administrator rights. If " +"not specified, the default administrator rights will be cleared." +msgstr "" + +#: of telebot.TeleBot.set_my_default_administrator_rights:13 +msgid "" +"Pass True to change the default administrator rights of the bot in " +"channels. Otherwise, the default administrator rights of the bot for " +"groups and supergroups will be changed." +msgstr "" + +#: of telebot.TeleBot.set_state:1 +msgid "Sets a new state of a user." +msgstr "" + +#: of telebot.TeleBot.set_state:5 +msgid "" +"You should set both user id and chat id in order to set state for a user " +"in a chat. Otherwise, if you only set user_id, chat_id will equal to " +"user_id, this means that state will be set for the user in his private " +"chat with a bot." +msgstr "" + +#: of telebot.TeleBot.set_state:12 +msgid "new state. can be string, integer, or :class:`telebot.types.State`" +msgstr "" + +#: of telebot.TeleBot.set_sticker_position_in_set:1 +msgid "" +"Use this method to move a sticker in a set created by the bot to a " +"specific position . Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_position_in_set:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickerpositioninset" +msgstr "" + +#: of telebot.TeleBot.set_sticker_position_in_set:8 +msgid "New sticker position in the set, zero-based" +msgstr "" + +#: of telebot.TeleBot.set_sticker_set_thumb:1 +msgid "" +"Use this method to set the thumbnail of a sticker set. Animated " +"thumbnails can be set for animated sticker sets only. Returns True on " +"success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_set_thumb:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setstickersetthumb" +msgstr "" + +#: of telebot.TeleBot.set_update_listener:1 +msgid "Sets a listener function to be called when a new update is received." +msgstr "" + +#: of telebot.TeleBot.set_update_listener:3 +msgid "Listener function." +msgstr "" + +#: of telebot.TeleBot.set_webhook:1 +msgid "" +"Use this method to specify a URL and receive incoming updates via an " +"outgoing webhook. Whenever there is an update for the bot, we will send " +"an HTTPS POST request to the specified URL, containing a JSON-serialized " +"Update. In case of an unsuccessful request, we will give up after a " +"reasonable amount of attempts. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_webhook:6 +msgid "" +"If you'd like to make sure that the webhook was set by you, you can " +"specify secret data in the parameter secret_token. If specified, the " +"request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the " +"secret token as content." +msgstr "" + +#: of telebot.TeleBot.set_webhook:9 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#setwebhook" +msgstr "" + +#: of telebot.TeleBot.set_webhook:11 +msgid "" +"HTTPS URL to send updates to. Use an empty string to remove webhook " +"integration, defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:14 +msgid "" +"Upload your public key certificate so that the root certificate in use " +"can be checked, defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:17 +msgid "" +"The maximum allowed number of simultaneous HTTPS connections to the " +"webhook for update delivery, 1-100. Defaults to 40. Use lower values to " +"limit the load on your bot's server, and higher values to increase your " +"bot's throughput, defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used. Please note that this parameter doesn't affect " +"updates created before the call to the setWebhook, so unwanted updates " +"may be received for a short period of time. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:22 +msgid "" +"A JSON-serialized list of the update types you want your bot to receive. " +"For example, specify [“message”, “edited_channel_post”, “callback_query”]" +" to only receive updates of these types. See Update for a complete list " +"of available update types. Specify an empty list to receive all update " +"types except chat_member (default). If not specified, the previous " +"setting will be used." +msgstr "" + +#: of telebot.TeleBot.set_webhook:27 +msgid "" +"Please note that this parameter doesn't affect updates created before the" +" call to the setWebhook, so unwanted updates may be received for a short " +"period of time. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:42 +msgid "" +"A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” " +"in every webhook request, 1-256 characters. Only characters A-Z, a-z, " +"0-9, _ and - are allowed. The header is useful to ensure that the request" +" comes from a webhook set by you. Defaults to None" +msgstr "" + +#: of telebot.TeleBot.set_webhook:47 +msgid ":obj:`bool` if the request was successful." +msgstr "" + +#: of telebot.TeleBot.setup_middleware:1 +msgid "Registers class-based middleware." +msgstr "" + +#: of telebot.TeleBot.setup_middleware:3 +msgid "Subclass of :class:`telebot.handler_backends.BaseMiddleware`" +msgstr "" + +#: of telebot.TeleBot.shipping_query_handler:1 +msgid "" +"Handles new incoming shipping query. Only for invoices with flexible " +"price. As a parameter to the decorator function, it passes " +":class:`telebot.types.ShippingQuery` object." +msgstr "" + +#: of telebot.TeleBot.stop_bot:1 +msgid "Stops bot by stopping polling and closing the worker pool." +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:1 +msgid "" +"Use this method to stop updating a live location message before " +"live_period expires. On success, if the message is not an inline message," +" the edited Message is returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#stopmessagelivelocation" +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:9 +msgid "" +"Required if inline_message_id is not specified. Identifier of the message" +" with live location to stop" +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:12 +msgid "" +"Required if chat_id and message_id are not specified. Identifier of the " +"inline message with live location to stop" +msgstr "" + +#: of telebot.TeleBot.stop_message_live_location:22 +msgid "" +"On success, if the message is not an inline message, the edited Message " +"is returned, otherwise True is returned." +msgstr "" + +#: of telebot.TeleBot.stop_poll:1 +msgid "" +"Use this method to stop a poll which was sent by the bot. On success, the" +" stopped Poll is returned." +msgstr "" + +#: of telebot.TeleBot.stop_poll:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" +msgstr "" + +#: of telebot.TeleBot.stop_poll:8 +msgid "Identifier of the original message with the poll" +msgstr "" + +#: of telebot.TeleBot.stop_poll:11 +msgid "A JSON-serialized object for a new message markup." +msgstr "" + +#: of telebot.TeleBot.stop_poll:14 +msgid "On success, the stopped Poll is returned." +msgstr "" + +#: of telebot.TeleBot.stop_poll:15 +msgid ":obj:`types.Poll`" +msgstr "" + +#: of telebot.TeleBot.stop_polling:1 +msgid "Stops polling." +msgstr "" + +#: of telebot.TeleBot.stop_polling:3 +msgid "Does not accept any arguments." +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:1 +msgid "" +"Use this method to unban a previously kicked user in a supergroup or " +"channel. The user will not return to the group or channel automatically, " +"but will be able to join via link, etc. The bot must be an administrator " +"for this to work. By default, this method guarantees that after the call " +"the user is not a member of the chat, but will be able to join it. So if " +"the user is a member of the chat they will also be removed from the chat." +" If you don't want this, use the parameter only_if_banned." +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:7 +msgid "Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember" +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:9 +msgid "" +"Unique identifier for the target group or username of the target " +"supergroup or channel (in the format @username)" +msgstr "" + +#: of telebot.TeleBot.unban_chat_member:16 +msgid "Do nothing if the user is not banned" +msgstr "" + +#: of telebot.TeleBot.unban_chat_sender_chat:1 +msgid "" +"Use this method to unban a previously banned channel chat in a supergroup" +" or channel. The bot must be an administrator for this to work and must " +"have the appropriate administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unban_chat_sender_chat:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unbanchatsenderchat" +msgstr "" + +#: of telebot.TeleBot.unban_chat_sender_chat:11 +msgid "Unique identifier of the target sender chat." +msgstr "" + +#: of telebot.TeleBot.unpin_all_chat_messages:1 +msgid "" +"Use this method to unpin a all pinned messages in a supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unpin_all_chat_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallchatmessages" +msgstr "" + +#: of telebot.TeleBot.unpin_all_forum_topic_messages:1 +msgid "" +"Use this method to clear the list of pinned messages in a forum topic. " +"The bot must be an administrator in the chat for this to work and must " +"have the can_pin_messages administrator right in the supergroup. Returns " +"True on success." +msgstr "" + +#: of telebot.TeleBot.unpin_all_forum_topic_messages:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" +msgstr "" + +#: of telebot.TeleBot.unpin_all_forum_topic_messages:10 +msgid "Identifier of the topic" +msgstr "" + +#: of telebot.TeleBot.unpin_chat_message:1 +msgid "" +"Use this method to unpin specific pinned message in a supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unpin_chat_message:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinchatmessage" +msgstr "" + +#: of telebot.TeleBot.unpin_chat_message:11 +msgid "Int: Identifier of a message to unpin" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:1 +msgid "" +"Use this method to upload a .png file with a sticker for later use in " +"createNewStickerSet and addStickerToSet methods (can be used multiple " +"times). Returns the uploaded File on success." +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#uploadstickerfile" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:6 +msgid "User identifier of sticker set owner" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:9 +msgid "" +"PNG image with the sticker, must be up to 512 kilobytes in size, " +"dimensions must not exceed 512px, and either width or height must be " +"exactly 512px." +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:13 +msgid "On success, the sent file is returned." +msgstr "" + +#: of telebot.TeleBot.user:1 +msgid "" +"The User object representing this bot. Equivalent to bot.get_me() but the" +" result is cached so only one API call is needed." +msgstr "" + +#: of telebot.TeleBot.user:4 +msgid "Bot's info." +msgstr "" + +#: of telebot.TeleBot.user:5 +msgid ":class:`telebot.types.User`" +msgstr "" + +#: ../../sync_version/index.rst:17 +msgid "custom_filters file" +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:1 +#: telebot.custom_filters.SimpleCustomFilter:1 +msgid "Bases: :py:class:`abc.ABC`" +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:1 +msgid "" +"Advanced Custom Filter base class. Create child class with check() " +"method. Accepts two parameters, returns bool: True - filter passed, False" +" - filter failed. message: Message class text: Filter value given in " +"handler" +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:7 +#: telebot.custom_filters.SimpleCustomFilter:5 +msgid "Child classes should have .key property." +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter:9 +msgid "Example on creating an advanced custom filter." +msgstr "" + +#: of telebot.custom_filters.AdvancedCustomFilter.check:1 +#: telebot.custom_filters.SimpleCustomFilter.check:1 +msgid "Perform a check." +msgstr "" + +#: of telebot.custom_filters.ChatFilter:1 +#: telebot.custom_filters.LanguageFilter:1 telebot.custom_filters.StateFilter:1 +#: telebot.custom_filters.TextContainsFilter:1 +#: telebot.custom_filters.TextMatchFilter:1 +#: telebot.custom_filters.TextStartsFilter:1 +msgid "Bases: :py:class:`telebot.custom_filters.AdvancedCustomFilter`" +msgstr "" + +#: of telebot.custom_filters.ChatFilter:1 +msgid "Check whether chat_id corresponds to given chat_id." +msgstr "" + +#: of telebot.custom_filters.ChatFilter:3 +#: telebot.custom_filters.ForwardFilter:3 +#: telebot.custom_filters.IsAdminFilter:3 +#: telebot.custom_filters.IsDigitFilter:3 +#: telebot.custom_filters.IsReplyFilter:3 +#: telebot.custom_filters.LanguageFilter:3 telebot.custom_filters.StateFilter:3 +#: telebot.custom_filters.TextContainsFilter:5 +#: telebot.custom_filters.TextMatchFilter:3 +#: telebot.custom_filters.TextStartsFilter:3 +msgid "Example on using this filter:" +msgstr "" + +#: of telebot.custom_filters.ForwardFilter:1 +#: telebot.custom_filters.IsAdminFilter:1 +#: telebot.custom_filters.IsDigitFilter:1 +#: telebot.custom_filters.IsReplyFilter:1 +msgid "Bases: :py:class:`telebot.custom_filters.SimpleCustomFilter`" +msgstr "" + +#: of telebot.custom_filters.ForwardFilter:1 +msgid "Check whether message was forwarded from channel or group." +msgstr "" + +#: of telebot.custom_filters.IsAdminFilter:1 +msgid "Check whether the user is administrator / owner of the chat." +msgstr "" + +#: of telebot.custom_filters.IsDigitFilter:1 +msgid "Filter to check whether the string is made up of only digits." +msgstr "" + +#: of telebot.custom_filters.IsReplyFilter:1 +msgid "Check whether message is a reply." +msgstr "" + +#: of telebot.custom_filters.LanguageFilter:1 +msgid "Check users language_code." +msgstr "" + +#: of telebot.custom_filters.SimpleCustomFilter:1 +msgid "" +"Simple Custom Filter base class. Create child class with check() method. " +"Accepts only message, returns bool value, that is compared with given in " +"handler." +msgstr "" + +#: of telebot.custom_filters.SimpleCustomFilter:7 +msgid "Example on creating a simple custom filter." +msgstr "" + +#: of telebot.custom_filters.StateFilter:1 +msgid "Filter to check state." +msgstr "" + +#: of telebot.custom_filters.TextContainsFilter:1 +msgid "Filter to check Text message. key: text" +msgstr "" + +#: of telebot.custom_filters.TextFilter:1 +msgid "" +"Advanced text filter to check (types.Message, types.CallbackQuery, " +"types.InlineQuery, types.Poll)" +msgstr "" + +#: of telebot.custom_filters.TextFilter:3 +msgid "example of usage is in examples/custom_filters/advanced_text_filter.py" +msgstr "" + +#: of telebot.custom_filters.TextFilter:5 +msgid "string, True if object's text is equal to passed string" +msgstr "" + +#: of telebot.custom_filters.TextFilter:8 +msgid "list[str] or tuple[str], True if any string element of iterable is in text" +msgstr "" + +#: of telebot.custom_filters.TextFilter:11 telebot.custom_filters.TextFilter:14 +msgid "string, True if object's text starts with passed string" +msgstr "" + +#: of telebot.custom_filters.TextFilter:17 +msgid "bool (default False), case insensitive" +msgstr "" + +#: of telebot.custom_filters.TextFilter:20 +msgid "if incorrect value for a parameter was supplied" +msgstr "" + +#: of telebot.custom_filters.TextMatchFilter:1 +msgid "Filter to check Text message." +msgstr "" + +#: of telebot.custom_filters.TextStartsFilter:1 +msgid "Filter to check whether message starts with some text." +msgstr "" + +#: ../../sync_version/index.rst:25 +msgid "handler_backends file" +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:1 +msgid "" +"Base class for middleware. Your middlewares should be inherited from this" +" class." +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:4 +msgid "" +"Set update_sensitive=True if you want to get different updates on " +"different functions. For example, if you want to handle pre_process for " +"message update, then you will have to create pre_process_message " +"function, and so on. Same applies to post_process." +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:10 +msgid "" +"If you want to use middleware, you have to set use_class_middlewares=True" +" in your TeleBot instance." +msgstr "" + +#: of telebot.handler_backends.BaseMiddleware:13 +msgid "Example of class-based middlewares." +msgstr "" + +#: of telebot.handler_backends.CancelUpdate:1 +msgid "" +"Class for canceling updates. Just return instance of this class in " +"middleware to skip update. Update will skip handler and execution of " +"post_process in middlewares." +msgstr "" + +#: of telebot.handler_backends.ContinueHandling:1 +msgid "" +"Class for continue updates in handlers. Just return instance of this " +"class in handlers to continue process." +msgstr "" + +#: of telebot.handler_backends.ContinueHandling:5 +msgid "Example of using ContinueHandling" +msgstr "" + +#: of telebot.handler_backends.SkipHandler:1 +msgid "" +"Class for skipping handlers. Just return instance of this class in " +"middleware to skip handler. Update will go to post_process, but will skip" +" execution of handler." +msgstr "" + +#: of telebot.handler_backends.State:1 +msgid "Class representing a state." +msgstr "" + +#: of telebot.handler_backends.StatesGroup:1 +msgid "Class representing common states." +msgstr "" + +#: ../../sync_version/index.rst:34 +msgid "Extensions" +msgstr "" + +#: of telebot.ext.sync.webhooks:1 +msgid "" +"This file is used by TeleBot.run_webhooks() function. Fastapi is required" +" to run this script." +msgstr "" + +#: of telebot.ext.sync.webhooks.SyncWebhookListener.run_app:1 +msgid "" +"Run app with the given parameters to init. Not supposed to be used " +"manually by user." +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/types.po b/docs/source/locales/ru/LC_MESSAGES/types.po new file mode 100644 index 000000000..e9417f378 --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/types.po @@ -0,0 +1,5507 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../types.rst:3 +msgid "Types of API" +msgstr "" + +#: of telebot.types.Animation:1 telebot.types.Audio:1 +#: telebot.types.CallbackQuery:1 telebot.types.Chat:1 +#: telebot.types.ChatJoinRequest:1 telebot.types.ChatMember:1 +#: telebot.types.ChatMemberUpdated:1 telebot.types.ChatPhoto:1 +#: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 +#: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 +#: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 +#: telebot.types.ForumTopicReopened:1 telebot.types.Game:1 +#: telebot.types.GameHighScore:1 telebot.types.InlineQuery:1 +#: telebot.types.Invoice:1 telebot.types.Message:1 +#: telebot.types.MessageAutoDeleteTimerChanged:1 telebot.types.MessageID:1 +#: telebot.types.OrderInfo:1 telebot.types.PhotoSize:1 telebot.types.Poll:1 +#: telebot.types.PollOption:1 telebot.types.PreCheckoutQuery:1 +#: telebot.types.ProximityAlertTriggered:1 telebot.types.ShippingAddress:1 +#: telebot.types.ShippingQuery:1 telebot.types.Sticker:1 +#: telebot.types.StickerSet:1 telebot.types.SuccessfulPayment:1 +#: telebot.types.Update:1 telebot.types.UserProfilePhotos:1 +#: telebot.types.Venue:1 telebot.types.Video:1 telebot.types.VideoChatEnded:1 +#: telebot.types.VideoChatParticipantsInvited:1 +#: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 +#: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 +msgid "Bases: :py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.Animation:1 +msgid "" +"This object represents an animation file (GIF or H.264/MPEG-4 AVC video " +"without sound)." +msgstr "" + +#: of telebot.types.Animation:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#animation" +msgstr "" + +#: of telebot.types.Animation telebot.types.Audio telebot.types.BotCommand +#: telebot.types.BotCommandScopeAllChatAdministrators +#: telebot.types.BotCommandScopeAllGroupChats +#: telebot.types.BotCommandScopeAllPrivateChats +#: telebot.types.BotCommandScopeChat +#: telebot.types.BotCommandScopeChatAdministrators +#: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault +#: telebot.types.CallbackQuery telebot.types.Chat +#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChosenInlineResult +#: telebot.types.Contact telebot.types.Dice telebot.types.Document +#: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic +#: telebot.types.ForumTopicCreated telebot.types.Game +#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery +#: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio +#: telebot.types.InlineQueryResultCachedAudio +#: telebot.types.InlineQueryResultCachedDocument +#: telebot.types.InlineQueryResultCachedGif +#: telebot.types.InlineQueryResultCachedMpeg4Gif +#: telebot.types.InlineQueryResultCachedPhoto +#: telebot.types.InlineQueryResultCachedSticker +#: telebot.types.InlineQueryResultCachedVideo +#: telebot.types.InlineQueryResultCachedVoice +#: telebot.types.InlineQueryResultContact +#: telebot.types.InlineQueryResultDocument telebot.types.InlineQueryResultGame +#: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation +#: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto +#: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo +#: telebot.types.InlineQueryResultVoice +#: telebot.types.InputContactMessageContent telebot.types.InputFile +#: telebot.types.InputInvoiceMessageContent +#: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation +#: telebot.types.InputMediaAudio telebot.types.InputMediaDocument +#: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo +#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent +#: telebot.types.Invoice telebot.types.KeyboardButton +#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault +#: telebot.types.MenuButtonWebApp telebot.types.Message +#: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity +#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize +#: telebot.types.Poll telebot.types.Poll.add telebot.types.PollAnswer +#: telebot.types.PollOption telebot.types.PreCheckoutQuery +#: telebot.types.ProximityAlertTriggered telebot.types.ReplyKeyboardMarkup +#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row +#: telebot.types.ReplyKeyboardRemove telebot.types.SentWebAppMessage +#: telebot.types.ShippingAddress telebot.types.ShippingOption +#: telebot.types.ShippingOption.add_price telebot.types.ShippingQuery +#: telebot.types.Sticker telebot.types.StickerSet +#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User +#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited +#: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice +#: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +msgid "Parameters" +msgstr "" + +#: of telebot.types.Animation:5 telebot.types.Audio:5 telebot.types.Document:5 +#: telebot.types.File:5 telebot.types.PhotoSize:5 telebot.types.Sticker:5 +#: telebot.types.Video:5 telebot.types.VideoNote:5 telebot.types.Voice:5 +msgid "Identifier for this file, which can be used to download or reuse the file" +msgstr "" + +#: of telebot.types.Animation:8 telebot.types.Audio:8 telebot.types.Document:8 +#: telebot.types.File:8 telebot.types.PhotoSize:8 telebot.types.Sticker:8 +#: telebot.types.Video:8 telebot.types.VideoNote:8 telebot.types.Voice:8 +msgid "" +"Unique identifier for this file, which is supposed to be the same over " +"time and for different bots. Can't be used to download or reuse the file." +msgstr "" + +#: of telebot.types.Animation:12 telebot.types.Video:12 +msgid "Video width as defined by sender" +msgstr "" + +#: of telebot.types.Animation:15 telebot.types.Video:15 +msgid "Video height as defined by sender" +msgstr "" + +#: of telebot.types.Animation:18 telebot.types.Video:18 +#: telebot.types.VideoNote:15 +msgid "Duration of the video in seconds as defined by sender" +msgstr "" + +#: of telebot.types.Animation:21 +msgid "Optional. Animation thumbnail as defined by sender" +msgstr "" + +#: of telebot.types.Animation:24 +msgid "Optional. Original animation filename as defined by sender" +msgstr "" + +#: of telebot.types.Animation:27 telebot.types.Audio:24 +#: telebot.types.Document:18 telebot.types.Video:27 telebot.types.Voice:15 +msgid "Optional. MIME type of the file as defined by sender" +msgstr "" + +#: of telebot.types.Animation:30 telebot.types.Audio:27 +#: telebot.types.Document:21 telebot.types.File:12 telebot.types.Video:30 +#: telebot.types.Voice:18 +msgid "" +"Optional. File size in bytes. It can be bigger than 2^31 and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it has at most 52 significant bits, so a signed 64-bit integer or" +" double-precision float type are safe for storing this value." +msgstr "" + +#: of telebot.types.Animation telebot.types.Audio telebot.types.BotCommand +#: telebot.types.BotCommandScope +#: telebot.types.BotCommandScopeAllChatAdministrators +#: telebot.types.BotCommandScopeAllGroupChats +#: telebot.types.BotCommandScopeAllPrivateChats +#: telebot.types.BotCommandScopeChat +#: telebot.types.BotCommandScopeChatAdministrators +#: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault +#: telebot.types.CallbackQuery telebot.types.Chat +#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice +#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game +#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery +#: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio +#: telebot.types.InlineQueryResultCachedAudio +#: telebot.types.InlineQueryResultCachedDocument +#: telebot.types.InlineQueryResultCachedGif +#: telebot.types.InlineQueryResultCachedMpeg4Gif +#: telebot.types.InlineQueryResultCachedPhoto +#: telebot.types.InlineQueryResultCachedSticker +#: telebot.types.InlineQueryResultCachedVideo +#: telebot.types.InlineQueryResultCachedVoice +#: telebot.types.InlineQueryResultContact +#: telebot.types.InlineQueryResultDocument telebot.types.InlineQueryResultGame +#: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation +#: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto +#: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo +#: telebot.types.InlineQueryResultVoice +#: telebot.types.InputContactMessageContent +#: telebot.types.InputInvoiceMessageContent +#: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation +#: telebot.types.InputMediaAudio telebot.types.InputMediaDocument +#: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo +#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent +#: telebot.types.Invoice telebot.types.KeyboardButton +#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault +#: telebot.types.MenuButtonWebApp telebot.types.Message +#: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity +#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize +#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption +#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered +#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add +#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.SentWebAppMessage telebot.types.ShippingAddress +#: telebot.types.ShippingOption telebot.types.ShippingOption.add_price +#: telebot.types.ShippingQuery telebot.types.Sticker telebot.types.StickerSet +#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User +#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited +#: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice +#: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +msgid "Returns" +msgstr "" + +#: of telebot.types.Animation:35 telebot.types.Audio:35 +#: telebot.types.BotCommand:12 telebot.types.BotCommandScope:38 +#: telebot.types.BotCommandScopeAllChatAdministrators:8 +#: telebot.types.BotCommandScopeAllGroupChats:8 +#: telebot.types.BotCommandScopeAllPrivateChats:8 +#: telebot.types.BotCommandScopeChat:12 +#: telebot.types.BotCommandScopeChatAdministrators:12 +#: telebot.types.BotCommandScopeChatMember:15 +#: telebot.types.BotCommandScopeDefault:8 telebot.types.CallbackQuery:30 +#: telebot.types.Chat:101 telebot.types.ChatAdministratorRights:46 +#: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:20 +#: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:59 +#: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 +#: telebot.types.ChatMemberMember:11 telebot.types.ChatMemberOwner:17 +#: telebot.types.ChatMemberRestricted:47 telebot.types.ChatMemberUpdated:24 +#: telebot.types.ChatPermissions:38 telebot.types.ChatPhoto:21 +#: telebot.types.ChosenInlineResult:21 telebot.types.Contact:22 +#: telebot.types.Dice:11 telebot.types.Document:26 telebot.types.File:21 +#: telebot.types.ForceReply:18 telebot.types.ForumTopic:17 +#: telebot.types.ForumTopicCreated:14 telebot.types.Game:25 +#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:45 +#: telebot.types.InlineKeyboardMarkup:22 telebot.types.InlineQuery:26 +#: telebot.types.InlineQueryResultArticle:38 +#: telebot.types.InlineQueryResultAudio:40 +#: telebot.types.InlineQueryResultCachedAudio:31 +#: telebot.types.InlineQueryResultCachedDocument:37 +#: telebot.types.InlineQueryResultCachedGif:33 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:33 +#: telebot.types.InlineQueryResultCachedPhoto:37 +#: telebot.types.InlineQueryResultCachedSticker:20 +#: telebot.types.InlineQueryResultCachedVideo:37 +#: telebot.types.InlineQueryResultCachedVoice:34 +#: telebot.types.InlineQueryResultContact:38 +#: telebot.types.InlineQueryResultDocument:49 +#: telebot.types.InlineQueryResultGame:17 telebot.types.InlineQueryResultGif:49 +#: telebot.types.InlineQueryResultLocation:50 +#: telebot.types.InlineQueryResultMpeg4Gif:49 +#: telebot.types.InlineQueryResultPhoto:46 +#: telebot.types.InlineQueryResultVenue:51 +#: telebot.types.InlineQueryResultVideo:53 +#: telebot.types.InlineQueryResultVoice:37 +#: telebot.types.InputContactMessageContent:17 +#: telebot.types.InputInvoiceMessageContent:75 +#: telebot.types.InputLocationMessageContent:26 +#: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:40 +#: telebot.types.InputMediaDocument:35 telebot.types.InputMediaPhoto:24 +#: telebot.types.InputMediaVideo:43 telebot.types.InputTextMessageContent:19 +#: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 +#: telebot.types.KeyboardButton:25 telebot.types.KeyboardButtonPollType:9 +#: telebot.types.LabeledPrice:13 telebot.types.Location:25 +#: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 +#: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 +#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:230 +#: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 +#: telebot.types.MessageID:8 telebot.types.OrderInfo:17 +#: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:15 +#: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 +#: telebot.types.ProximityAlertTriggered:14 +#: telebot.types.ReplyKeyboardMarkup:42 telebot.types.ReplyKeyboardRemove:18 +#: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 +#: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 +#: telebot.types.Sticker:49 telebot.types.StickerSet:30 +#: telebot.types.SuccessfulPayment:28 telebot.types.Update:61 +#: telebot.types.User:41 telebot.types.UserProfilePhotos:11 +#: telebot.types.Venue:27 telebot.types.Video:35 telebot.types.VideoChatEnded:8 +#: telebot.types.VideoChatParticipantsInvited:8 +#: telebot.types.VideoChatScheduled:9 telebot.types.VideoNote:24 +#: telebot.types.Voice:23 telebot.types.WebAppData:12 +#: telebot.types.WebAppInfo:8 telebot.types.WebhookInfo:37 +msgid "Instance of the class" +msgstr "" + +#: of telebot.types.Animation telebot.types.Audio telebot.types.BotCommand +#: telebot.types.BotCommandScope +#: telebot.types.BotCommandScopeAllChatAdministrators +#: telebot.types.BotCommandScopeAllGroupChats +#: telebot.types.BotCommandScopeAllPrivateChats +#: telebot.types.BotCommandScopeChat +#: telebot.types.BotCommandScopeChatAdministrators +#: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault +#: telebot.types.CallbackQuery telebot.types.Chat +#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice +#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game +#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery +#: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio +#: telebot.types.InlineQueryResultCachedAudio +#: telebot.types.InlineQueryResultCachedDocument +#: telebot.types.InlineQueryResultCachedGif +#: telebot.types.InlineQueryResultCachedMpeg4Gif +#: telebot.types.InlineQueryResultCachedPhoto +#: telebot.types.InlineQueryResultCachedSticker +#: telebot.types.InlineQueryResultCachedVideo +#: telebot.types.InlineQueryResultCachedVoice +#: telebot.types.InlineQueryResultContact +#: telebot.types.InlineQueryResultDocument telebot.types.InlineQueryResultGame +#: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation +#: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto +#: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo +#: telebot.types.InlineQueryResultVoice +#: telebot.types.InputContactMessageContent +#: telebot.types.InputInvoiceMessageContent +#: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation +#: telebot.types.InputMediaAudio telebot.types.InputMediaDocument +#: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo +#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent +#: telebot.types.Invoice telebot.types.KeyboardButton +#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault +#: telebot.types.MenuButtonWebApp telebot.types.Message +#: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity +#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize +#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption +#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered +#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add +#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.SentWebAppMessage telebot.types.ShippingAddress +#: telebot.types.ShippingOption telebot.types.ShippingQuery +#: telebot.types.Sticker telebot.types.StickerSet +#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User +#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited +#: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice +#: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +msgid "Return type" +msgstr "" + +#: of telebot.types.Animation:36 +msgid ":class:`telebot.types.Animation`" +msgstr "" + +#: of telebot.types.Audio:1 +msgid "" +"This object represents an audio file to be treated as music by the " +"Telegram clients." +msgstr "" + +#: of telebot.types.Audio:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#audio" +msgstr "" + +#: of telebot.types.Audio:12 telebot.types.Voice:12 +msgid "Duration of the audio in seconds as defined by sender" +msgstr "" + +#: of telebot.types.Audio:15 +msgid "Optional. Performer of the audio as defined by sender or by audio tags" +msgstr "" + +#: of telebot.types.Audio:18 +msgid "Optional. Title of the audio as defined by sender or by audio tags" +msgstr "" + +#: of telebot.types.Audio:21 telebot.types.Document:15 telebot.types.Video:24 +msgid "Optional. Original filename as defined by sender" +msgstr "" + +#: of telebot.types.Audio:32 +msgid "Optional. Thumbnail of the album cover to which the music file belongs" +msgstr "" + +#: of telebot.types.Audio:36 +msgid ":class:`telebot.types.Audio`" +msgstr "" + +#: of telebot.types.BotCommand:1 telebot.types.ChatInviteLink:1 +#: telebot.types.ChatLocation:1 telebot.types.PollAnswer:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.BotCommand:1 +msgid "This object represents a bot command." +msgstr "" + +#: of telebot.types.BotCommand:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#botcommand" +msgstr "" + +#: of telebot.types.BotCommand:5 +msgid "" +"Text of the command; 1-32 characters. Can contain only lowercase English " +"letters, digits and underscores." +msgstr "" + +#: of telebot.types.BotCommand:9 +msgid "Description of the command; 1-256 characters." +msgstr "" + +#: of telebot.types.BotCommand:13 +msgid ":class:`telebot.types.BotCommand`" +msgstr "" + +#: of telebot.types.BotCommandScope:1 +#: telebot.types.InlineQueryResultCachedBase:1 +msgid "Bases: :py:class:`abc.ABC`, :py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.BotCommandScope:1 +msgid "" +"This object represents the scope to which bot commands are applied. " +"Currently, the following 7 scopes are supported:" +msgstr "" + +#: of telebot.types.BotCommandScope:3 telebot.types.BotCommandScope:21 +#: telebot.types.BotCommandScope:36 +msgid ":class:`BotCommandScopeDefault`" +msgstr "" + +#: of telebot.types.BotCommandScope:4 telebot.types.BotCommandScope:19 +msgid ":class:`BotCommandScopeAllPrivateChats`" +msgstr "" + +#: of telebot.types.BotCommandScope:5 telebot.types.BotCommandScope:34 +msgid ":class:`BotCommandScopeAllGroupChats`" +msgstr "" + +#: of telebot.types.BotCommandScope:6 +msgid ":class:`BotCommandScopeAllChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScope:7 telebot.types.BotCommandScope:17 +#: telebot.types.BotCommandScope:30 +msgid ":class:`BotCommandScopeChat`" +msgstr "" + +#: of telebot.types.BotCommandScope:8 +msgid ":class:`BotCommandScopeChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScope:9 telebot.types.BotCommandScope:26 +msgid ":class:`BotCommandScopeChatMember`" +msgstr "" + +#: of telebot.types.BotCommandScope:11 +msgid "" +"Determining list of commands The following algorithm is used to determine" +" the list of commands for a particular user viewing the bot menu. The " +"first list of commands which is set is returned:" +msgstr "" + +#: of telebot.types.BotCommandScope:14 +msgid "Commands in the chat with the bot:" +msgstr "" + +#: of telebot.types.BotCommandScope:16 telebot.types.BotCommandScope:29 +msgid ":class:`BotCommandScopeChat` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:18 +msgid ":class:`BotCommandScopeAllPrivateChats` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:20 telebot.types.BotCommandScope:35 +msgid ":class:`BotCommandScopeDefault` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:23 +msgid "Commands in group and supergroup chats:" +msgstr "" + +#: of telebot.types.BotCommandScope:25 +msgid ":class:`BotCommandScopeChatMember` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:27 +msgid "" +":class:`BotCommandScopeChatAdministrators` + language_code " +"(administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:28 +msgid ":class:`BotCommandScopeChatAdministrators` (administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:31 +msgid "" +":class:`BotCommandScopeAllChatAdministrators` + language_code " +"(administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:32 +msgid ":class:`BotCommandScopeAllChatAdministrators` (administrators only)" +msgstr "" + +#: of telebot.types.BotCommandScope:33 +msgid ":class:`BotCommandScopeAllGroupChats` + language_code" +msgstr "" + +#: of telebot.types.BotCommandScope:39 +msgid ":class:`telebot.types.BotCommandScope`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:1 +#: telebot.types.BotCommandScopeAllGroupChats:1 +#: telebot.types.BotCommandScopeAllPrivateChats:1 +#: telebot.types.BotCommandScopeChat:1 +#: telebot.types.BotCommandScopeChatAdministrators:1 +#: telebot.types.BotCommandScopeChatMember:1 +#: telebot.types.BotCommandScopeDefault:1 +msgid "Bases: :py:class:`telebot.types.BotCommandScope`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:1 +msgid "" +"Represents the scope of bot commands, covering all group and supergroup " +"chat administrators." +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopeallchatadministrators" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:5 +msgid "Scope type, must be all_chat_administrators" +msgstr "" + +#: of telebot.types.BotCommandScopeAllChatAdministrators:9 +msgid ":class:`telebot.types.BotCommandScopeAllChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:1 +msgid "" +"Represents the scope of bot commands, covering all group and supergroup " +"chats." +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopeallgroupchats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:5 +msgid "Scope type, must be all_group_chats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllGroupChats:9 +msgid ":class:`telebot.types.BotCommandScopeAllGroupChats`" +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:1 +msgid "Represents the scope of bot commands, covering all private chats." +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopeallprivatechats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:5 +msgid "Scope type, must be all_private_chats" +msgstr "" + +#: of telebot.types.BotCommandScopeAllPrivateChats:9 +msgid ":class:`telebot.types.BotCommandScopeAllPrivateChats`" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:1 +msgid "Represents the scope of bot commands, covering a specific chat." +msgstr "" + +#: of telebot.types.BotCommandScopeChat:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopechat" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:5 +msgid "Scope type, must be chat" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:8 +#: telebot.types.BotCommandScopeChatAdministrators:8 +#: telebot.types.BotCommandScopeChatMember:8 +msgid "" +"Unique identifier for the target chat or username of the target " +"supergroup (in the format @supergroupusername)" +msgstr "" + +#: of telebot.types.BotCommandScopeChat:13 +msgid ":class:`telebot.types.BotCommandScopeChat`" +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:1 +msgid "" +"Represents the scope of bot commands, covering all administrators of a " +"specific group or supergroup chat." +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopechatadministrators" +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:5 +msgid "Scope type, must be chat_administrators" +msgstr "" + +#: of telebot.types.BotCommandScopeChatAdministrators:13 +msgid ":class:`telebot.types.BotCommandScopeChatAdministrators`" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:1 +msgid "" +"Represents the scope of bot commands, covering a specific member of a " +"group or supergroup chat." +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopechatmember" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:5 +msgid "Scope type, must be chat_member" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:12 +msgid "Unique identifier of the target user" +msgstr "" + +#: of telebot.types.BotCommandScopeChatMember:16 +msgid ":class:`telebot.types.BotCommandScopeChatMember`" +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:1 +msgid "" +"Represents the default scope of bot commands. Default commands are used " +"if no commands with a narrower scope are specified for the user." +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#botcommandscopedefault" +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:5 +msgid "Scope type, must be default" +msgstr "" + +#: of telebot.types.BotCommandScopeDefault:9 +msgid ":class:`telebot.types.BotCommandScopeDefault`" +msgstr "" + +#: of telebot.types.CallbackQuery:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.CallbackQuery:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#callbackquery" +msgstr "" + +#: of telebot.types.CallbackQuery:5 telebot.types.InlineQuery:5 +msgid "Unique identifier for this query" +msgstr "" + +#: of telebot.types.CallbackQuery:8 telebot.types.InlineQuery:8 +msgid "Sender" +msgstr "" + +#: of telebot.types.CallbackQuery:11 +msgid "" +"Optional. Message with the callback button that originated the query. " +"Note that message content and message date will not be available if the " +"message is too old" +msgstr "" + +#: of telebot.types.CallbackQuery:15 +msgid "" +"Optional. Identifier of the message sent via the bot in inline mode, that" +" originated the query." +msgstr "" + +#: of telebot.types.CallbackQuery:19 +msgid "" +"Global identifier, uniquely corresponding to the chat to which the " +"message with the callback button was sent. Useful for high scores in " +"games." +msgstr "" + +#: of telebot.types.CallbackQuery:23 +msgid "" +"Optional. Data associated with the callback button. Be aware that the " +"message originated the query can contain no callback buttons with this " +"data." +msgstr "" + +#: of telebot.types.CallbackQuery:27 +msgid "" +"Optional. Short name of a Game to be returned, serves as the unique " +"identifier for the game" +msgstr "" + +#: of telebot.types.CallbackQuery:31 +msgid ":class:`telebot.types.CallbackQuery`" +msgstr "" + +#: of telebot.types.Chat:1 +msgid "This object represents a chat." +msgstr "" + +#: of telebot.types.Chat:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chat" +msgstr "" + +#: of telebot.types.Chat:5 +msgid "" +"Unique identifier for this chat. This number may have more than 32 " +"significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a signed 64-bit integer or double-precision float " +"type are safe for storing this identifier." +msgstr "" + +#: of telebot.types.Chat:10 +msgid "Type of chat, can be either “private”, “group”, “supergroup” or “channel”" +msgstr "" + +#: of telebot.types.Chat:13 +msgid "Optional. Title, for supergroups, channels and group chats" +msgstr "" + +#: of telebot.types.Chat:16 +msgid "" +"Optional. Username, for private chats, supergroups and channels if " +"available" +msgstr "" + +#: of telebot.types.Chat:19 +msgid "Optional. First name of the other party in a private chat" +msgstr "" + +#: of telebot.types.Chat:22 +msgid "Optional. Last name of the other party in a private chat" +msgstr "" + +#: of telebot.types.Chat:25 +msgid "Optional. True, if the supergroup chat is a forum (has topics enabled)" +msgstr "" + +#: of telebot.types.Chat:28 +msgid "Optional. Chat photo. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:31 +msgid "" +"Optional. If non-empty, the list of all active chat usernames; for " +"private chats, supergroups and channels. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:35 +msgid "" +"Optional. Custom emoji identifier of emoji status of the other party in a" +" private chat. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:39 +msgid "" +"Optional. Bio of the other party in a private chat. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:42 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Chat:46 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Chat:48 +msgid ":type :obj:`bool`" +msgstr "" + +#: of telebot.types.Chat:50 +msgid "" +"Optional. :obj:`bool`, if users need to join the supergroup before they " +"can send messages. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:54 +msgid "" +"Optional. :obj:`bool`, if all users directly joining the supergroup need " +"to be approved by supergroup administrators. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:58 +msgid "" +"Optional. Description, for groups, supergroups and channel chats. " +"Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:61 +msgid "" +"Optional. Primary invite link, for groups, supergroups and channel chats." +" Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:65 +msgid "" +"Optional. The most recent pinned message (by sending date). Returned only" +" in getChat." +msgstr "" + +#: of telebot.types.Chat:68 +msgid "" +"Optional. Default chat member permissions, for groups and supergroups. " +"Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:72 +msgid "" +"Optional. For supergroups, the minimum allowed delay between consecutive " +"messages sent by each unpriviledged user; in seconds. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:76 +msgid "" +"Optional. The time after which all messages sent to the chat will be " +"automatically deleted; in seconds. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:80 +msgid "" +"Optional. :obj:`bool`, if messages from the chat can't be forwarded to " +"other chats. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:84 +msgid "" +"Optional. For supergroups, name of group sticker set. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:87 +msgid "" +"Optional. :obj:`bool`, if the bot can change the group sticker set. " +"Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:91 +msgid "" +"Optional. Unique identifier for the linked chat, i.e. the discussion " +"group identifier for a channel and vice versa; for supergroups and " +"channel chats. This identifier may be greater than 32 bits and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it is smaller than 52 bits, so a signed 64 bit integer or double-" +"precision float type are safe for storing this identifier. Returned only " +"in getChat." +msgstr "" + +#: of telebot.types.Chat:97 +msgid "" +"Optional. For supergroups, the location to which the supergroup is " +"connected. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:102 +msgid ":class:`telebot.types.Chat`" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:1 telebot.types.ChatPermissions:1 +#: telebot.types.Location:1 telebot.types.MenuButton:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:1 +msgid "Represents the rights of an administrator in a chat." +msgstr "" + +#: of telebot.types.ChatAdministratorRights:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatadministratorrights" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:5 +#: telebot.types.ChatMemberAdministrator:14 telebot.types.ChatMemberOwner:11 +msgid "True, if the user's presence in the chat is hidden" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:8 +#: telebot.types.ChatMemberAdministrator:17 +msgid "" +"True, if the administrator can access the chat event log, chat " +"statistics, message statistics in channels, see channel members, see " +"anonymous administrators in supergroups and ignore slow mode. Implied by " +"any other administrator privilege" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:13 +#: telebot.types.ChatMemberAdministrator:22 +msgid "True, if the administrator can delete messages of other users" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:16 +#: telebot.types.ChatMemberAdministrator:25 +msgid "True, if the administrator can manage video chats" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:19 +#: telebot.types.ChatMemberAdministrator:28 +msgid "True, if the administrator can restrict, ban or unban chat members" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:22 +#: telebot.types.ChatMemberAdministrator:31 +msgid "" +"True, if the administrator can add new administrators with a subset of " +"their own privileges or demote administrators that he has promoted, " +"directly or indirectly (promoted by administrators that were appointed by" +" the user)" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:27 +#: telebot.types.ChatMemberAdministrator:36 +#: telebot.types.ChatMemberRestricted:14 +msgid "" +"True, if the user is allowed to change the chat title, photo and other " +"settings" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:30 +#: telebot.types.ChatMemberAdministrator:39 +#: telebot.types.ChatMemberRestricted:17 +msgid "True, if the user is allowed to invite new users to the chat" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:33 +#: telebot.types.ChatMemberAdministrator:42 +msgid "" +"Optional. True, if the administrator can post in the channel; channels " +"only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:36 +#: telebot.types.ChatMemberAdministrator:45 +msgid "" +"Optional. True, if the administrator can edit messages of other users and" +" can pin messages; channels only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:40 +#: telebot.types.ChatMemberAdministrator:49 +msgid "" +"Optional. True, if the user is allowed to pin messages; groups and " +"supergroups only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:43 +#: telebot.types.ChatMemberAdministrator:52 +msgid "" +"Optional. True, if the user is allowed to create, rename, close, and " +"reopen forum topics; supergroups only" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:47 +msgid ":class:`telebot.types.ChatAdministratorRights`" +msgstr "" + +#: of telebot.types.ChatInviteLink:1 +msgid "Represents an invite link for a chat." +msgstr "" + +#: of telebot.types.ChatInviteLink:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatinvitelink" +msgstr "" + +#: of telebot.types.ChatInviteLink:5 +msgid "" +"The invite link. If the link was created by another chat administrator, " +"then the second part of the link will be replaced with “…”." +msgstr "" + +#: of telebot.types.ChatInviteLink:9 +msgid "Creator of the link" +msgstr "" + +#: of telebot.types.ChatInviteLink:12 +msgid "" +"True, if users joining the chat via the link need to be approved by chat " +"administrators" +msgstr "" + +#: of telebot.types.ChatInviteLink:15 +msgid "True, if the link is primary" +msgstr "" + +#: of telebot.types.ChatInviteLink:18 +msgid "True, if the link is revoked" +msgstr "" + +#: of telebot.types.ChatInviteLink:21 +msgid "Optional. Invite link name" +msgstr "" + +#: of telebot.types.ChatInviteLink:24 +msgid "" +"Optional. Point in time (Unix timestamp) when the link will expire or has" +" been expired" +msgstr "" + +#: of telebot.types.ChatInviteLink:27 +msgid "" +"Optional. The maximum number of users that can be members of the chat " +"simultaneously after joining the chat via this invite link; 1-99999" +msgstr "" + +#: of telebot.types.ChatInviteLink:31 +msgid "Optional. Number of pending join requests created using this link" +msgstr "" + +#: of telebot.types.ChatInviteLink:35 +msgid ":class:`telebot.types.ChatInviteLink`" +msgstr "" + +#: of telebot.types.ChatJoinRequest:1 +msgid "Represents a join request sent to a chat." +msgstr "" + +#: of telebot.types.ChatJoinRequest:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatjoinrequest" +msgstr "" + +#: of telebot.types.ChatJoinRequest:5 +msgid "Chat to which the request was sent" +msgstr "" + +#: of telebot.types.ChatJoinRequest:8 +msgid "User that sent the join request" +msgstr "" + +#: of telebot.types.ChatJoinRequest:11 +msgid "Date the request was sent in Unix time" +msgstr "" + +#: of telebot.types.ChatJoinRequest:14 +msgid "Optional. Bio of the user." +msgstr "" + +#: of telebot.types.ChatJoinRequest:17 +msgid "" +"Optional. Chat invite link that was used by the user to send the join " +"request" +msgstr "" + +#: of telebot.types.ChatJoinRequest:21 +msgid ":class:`telebot.types.ChatJoinRequest`" +msgstr "" + +#: of telebot.types.ChatLocation:1 +msgid "Represents a location to which a chat is connected." +msgstr "" + +#: of telebot.types.ChatLocation:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatlocation" +msgstr "" + +#: of telebot.types.ChatLocation:5 +msgid "" +"The location to which the supergroup is connected. Can't be a live " +"location." +msgstr "" + +#: of telebot.types.ChatLocation:8 +msgid "Location address; 1-64 characters, as defined by the chat owner" +msgstr "" + +#: of telebot.types.ChatLocation:12 +msgid ":class:`telebot.types.ChatLocation`" +msgstr "" + +#: of telebot.types.ChatMember:1 +msgid "" +"This object contains information about one member of a chat. Currently, " +"the following 6 types of chat members are supported:" +msgstr "" + +#: of telebot.types.ChatMember:4 telebot.types.ChatMemberOwner:18 +msgid ":class:`telebot.types.ChatMemberOwner`" +msgstr "" + +#: of telebot.types.ChatMember:5 telebot.types.ChatMemberAdministrator:60 +msgid ":class:`telebot.types.ChatMemberAdministrator`" +msgstr "" + +#: of telebot.types.ChatMember:6 telebot.types.ChatMemberMember:12 +msgid ":class:`telebot.types.ChatMemberMember`" +msgstr "" + +#: of telebot.types.ChatMember:7 telebot.types.ChatMemberRestricted:48 +msgid ":class:`telebot.types.ChatMemberRestricted`" +msgstr "" + +#: of telebot.types.ChatMember:8 telebot.types.ChatMemberLeft:12 +msgid ":class:`telebot.types.ChatMemberLeft`" +msgstr "" + +#: of telebot.types.ChatMember:9 telebot.types.ChatMemberBanned:16 +msgid ":class:`telebot.types.ChatMemberBanned`" +msgstr "" + +#: of telebot.types.ChatMember:11 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatmember" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:1 telebot.types.ChatMemberBanned:1 +#: telebot.types.ChatMemberLeft:1 telebot.types.ChatMemberMember:1 +#: telebot.types.ChatMemberOwner:1 telebot.types.ChatMemberRestricted:1 +msgid "Bases: :py:class:`telebot.types.ChatMember`" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:1 +msgid "Represents a chat member that has some additional privileges." +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberadministrator" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:5 +msgid "The member's status in the chat, always “administrator”" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:8 telebot.types.ChatMemberBanned:8 +#: telebot.types.ChatMemberLeft:8 telebot.types.ChatMemberMember:8 +#: telebot.types.ChatMemberOwner:8 telebot.types.ChatMemberRestricted:8 +msgid "Information about the user" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:11 +msgid "True, if the bot is allowed to edit administrator privileges of that user" +msgstr "" + +#: of telebot.types.ChatMemberAdministrator:56 telebot.types.ChatMemberOwner:14 +msgid "Optional. Custom title for this user" +msgstr "" + +#: of telebot.types.ChatMemberBanned:1 +msgid "" +"Represents a chat member that was banned in the chat and can't return to " +"the chat or view chat messages." +msgstr "" + +#: of telebot.types.ChatMemberBanned:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberbanned" +msgstr "" + +#: of telebot.types.ChatMemberBanned:5 +msgid "The member's status in the chat, always “kicked”" +msgstr "" + +#: of telebot.types.ChatMemberBanned:11 +msgid "" +"Date when restrictions will be lifted for this user; unix time. If 0, " +"then the user is banned forever" +msgstr "" + +#: of telebot.types.ChatMemberLeft:1 +msgid "" +"Represents a chat member that isn't currently a member of the chat, but " +"may join it themselves." +msgstr "" + +#: of telebot.types.ChatMemberLeft:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatmemberleft" +msgstr "" + +#: of telebot.types.ChatMemberLeft:5 +msgid "The member's status in the chat, always “left”" +msgstr "" + +#: of telebot.types.ChatMemberMember:1 +msgid "" +"Represents a chat member that has no additional privileges or " +"restrictions." +msgstr "" + +#: of telebot.types.ChatMemberMember:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmembermember" +msgstr "" + +#: of telebot.types.ChatMemberMember:5 +msgid "The member's status in the chat, always “member”" +msgstr "" + +#: of telebot.types.ChatMemberOwner:1 +msgid "" +"Represents a chat member that owns the chat and has all administrator " +"privileges." +msgstr "" + +#: of telebot.types.ChatMemberOwner:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatmemberowner" +msgstr "" + +#: of telebot.types.ChatMemberOwner:5 +msgid "The member's status in the chat, always “creator”" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:1 +msgid "" +"Represents a chat member that is under certain restrictions in the chat. " +"Supergroups only." +msgstr "" + +#: of telebot.types.ChatMemberRestricted:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberrestricted" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:5 +msgid "The member's status in the chat, always “restricted”" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:11 +msgid "True, if the user is a member of the chat at the moment of the request" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:20 +msgid "True, if the user is allowed to pin messages" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:23 +msgid "True, if the user is allowed to create forum topics" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:26 +msgid "" +"True, if the user is allowed to send text messages, contacts, locations " +"and venues" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:29 +msgid "" +"True, if the user is allowed to send audios, documents, photos, videos, " +"video notes and voice notes" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:33 +msgid "True, if the user is allowed to send polls" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:36 +msgid "" +"True, if the user is allowed to send animations, games, stickers and use " +"inline bots" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:40 +msgid "True, if the user is allowed to add web page previews to their messages" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:43 +msgid "" +"Date when restrictions will be lifted for this user; unix time. If 0, " +"then the user is restricted forever" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:1 +msgid "This object represents changes in the status of a chat member." +msgstr "" + +#: of telebot.types.ChatMemberUpdated:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#chatmemberupdated" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:5 +msgid "Chat the user belongs to" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:8 +msgid "Performer of the action, which resulted in the change" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:11 +msgid "Date the change was done in Unix time" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:14 +msgid "Previous information about the chat member" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:17 +msgid "New information about the chat member" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:20 +msgid "" +"Optional. Chat invite link, which was used by the user to join the chat; " +"for joining by invite link events only." +msgstr "" + +#: of telebot.types.ChatMemberUpdated:25 +msgid ":class:`telebot.types.ChatMemberUpdated`" +msgstr "" + +#: of telebot.types.ChatMemberUpdated.difference:1 +msgid "" +"Get the difference between `old_chat_member` and `new_chat_member` as a " +"dict in the following format {'parameter': [old_value, new_value]} E.g " +"{'status': ['member', 'kicked'], 'until_date': [None, 1625055092]}" +msgstr "" + +#: of telebot.types.ChatMemberUpdated.difference:6 +msgid "Dict of differences" +msgstr "" + +#: of telebot.types.ChatPermissions:1 +msgid "" +"Describes actions that a non-administrator user is allowed to take in a " +"chat." +msgstr "" + +#: of telebot.types.ChatPermissions:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatpermissions" +msgstr "" + +#: of telebot.types.ChatPermissions:5 +msgid "" +"Optional. True, if the user is allowed to send text messages, contacts, " +"locations and venues" +msgstr "" + +#: of telebot.types.ChatPermissions:9 +msgid "" +"Optional. True, if the user is allowed to send audios, documents, photos," +" videos, video notes and voice notes, implies can_send_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:13 +msgid "" +"Optional. True, if the user is allowed to send polls, implies " +"can_send_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:16 +msgid "" +"Optional. True, if the user is allowed to send animations, games, " +"stickers and use inline bots, implies can_send_media_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:20 +msgid "" +"Optional. True, if the user is allowed to add web page previews to their " +"messages, implies can_send_media_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:24 +msgid "" +"Optional. True, if the user is allowed to change the chat title, photo " +"and other settings. Ignored in public supergroups" +msgstr "" + +#: of telebot.types.ChatPermissions:28 +msgid "Optional. True, if the user is allowed to invite new users to the chat" +msgstr "" + +#: of telebot.types.ChatPermissions:31 +msgid "" +"Optional. True, if the user is allowed to pin messages. Ignored in public" +" supergroups" +msgstr "" + +#: of telebot.types.ChatPermissions:34 +msgid "" +"Optional. True, if the user is allowed to create forum topics. If omitted" +" defaults to the value of can_pin_messages" +msgstr "" + +#: of telebot.types.ChatPermissions:39 +msgid ":class:`telebot.types.ChatPermissions`" +msgstr "" + +#: of telebot.types.ChatPhoto:1 +msgid "This object represents a chat photo." +msgstr "" + +#: of telebot.types.ChatPhoto:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#chatphoto" +msgstr "" + +#: of telebot.types.ChatPhoto:5 +msgid "" +"File identifier of small (160x160) chat photo. This file_id can be used " +"only for photo download and only for as long as the photo is not changed." +msgstr "" + +#: of telebot.types.ChatPhoto:9 +msgid "" +"Unique file identifier of small (160x160) chat photo, which is supposed " +"to be the same over time and for different bots. Can't be used to " +"download or reuse the file." +msgstr "" + +#: of telebot.types.ChatPhoto:13 +msgid "" +"File identifier of big (640x640) chat photo. This file_id can be used " +"only for photo download and only for as long as the photo is not changed." +msgstr "" + +#: of telebot.types.ChatPhoto:17 +msgid "" +"Unique file identifier of big (640x640) chat photo, which is supposed to " +"be the same over time and for different bots. Can't be used to download " +"or reuse the file." +msgstr "" + +#: of telebot.types.ChatPhoto:22 +msgid ":class:`telebot.types.ChatPhoto`" +msgstr "" + +#: of telebot.types.ChosenInlineResult:1 +msgid "" +"Represents a result of an inline query that was chosen by the user and " +"sent to their chat partner." +msgstr "" + +#: of telebot.types.ChosenInlineResult:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#choseninlineresult" +msgstr "" + +#: of telebot.types.ChosenInlineResult:5 +msgid "The unique identifier for the result that was chosen" +msgstr "" + +#: of telebot.types.ChosenInlineResult:8 +msgid "The user that chose the result" +msgstr "" + +#: of telebot.types.ChosenInlineResult:11 +msgid "Optional. Sender location, only for bots that require user location" +msgstr "" + +#: of telebot.types.ChosenInlineResult:14 +msgid "" +"Optional. Identifier of the sent inline message. Available only if there " +"is an inline keyboard attached to the message. Will be also received in " +"callback queries and can be used to edit the message." +msgstr "" + +#: of telebot.types.ChosenInlineResult:18 +msgid "The query that was used to obtain the result" +msgstr "" + +#: of telebot.types.ChosenInlineResult:22 +msgid ":class:`telebot.types.ChosenInlineResult`" +msgstr "" + +#: of telebot.types.Contact:1 +msgid "This object represents a phone contact." +msgstr "" + +#: of telebot.types.Contact:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#contact" +msgstr "" + +#: of telebot.types.Contact:5 telebot.types.InlineQueryResultContact:11 +#: telebot.types.InputContactMessageContent:5 +msgid "Contact's phone number" +msgstr "" + +#: of telebot.types.Contact:8 telebot.types.InlineQueryResultContact:14 +#: telebot.types.InputContactMessageContent:8 +msgid "Contact's first name" +msgstr "" + +#: of telebot.types.Contact:11 telebot.types.InlineQueryResultContact:17 +#: telebot.types.InputContactMessageContent:11 +msgid "Optional. Contact's last name" +msgstr "" + +#: of telebot.types.Contact:14 +msgid "" +"Optional. Contact's user identifier in Telegram. This number may have " +"more than 32 significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier." +msgstr "" + +#: of telebot.types.Contact:19 +msgid "Optional. Additional data about the contact in the form of a vCard" +msgstr "" + +#: of telebot.types.Contact:23 +msgid ":class:`telebot.types.Contact`" +msgstr "" + +#: of telebot.types.Dice:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.Dice:1 +msgid "This object represents an animated emoji that displays a random value." +msgstr "" + +#: of telebot.types.Dice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#dice" +msgstr "" + +#: of telebot.types.Dice:5 +msgid "Emoji on which the dice throw animation is based" +msgstr "" + +#: of telebot.types.Dice:8 +msgid "" +"Value of the dice, 1-6 for “🎲”, “🎯” and “🎳” base emoji, 1-5 for “🏀” and " +"“⚽” base emoji, 1-64 for “🎰” base emoji" +msgstr "" + +#: of telebot.types.Dice:12 +msgid ":class:`telebot.types.Dice`" +msgstr "" + +#: of telebot.types.Dictionaryable:1 telebot.types.InputFile:1 +#: telebot.types.JsonDeserializable:1 telebot.types.JsonSerializable:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of telebot.types.Dictionaryable:1 +msgid "" +"Subclasses of this class are guaranteed to be able to be converted to " +"dictionary. All subclasses of this class must override to_dict." +msgstr "" + +#: of telebot.types.Document:1 +msgid "" +"This object represents a general file (as opposed to photos, voice " +"messages and audio files)." +msgstr "" + +#: of telebot.types.Document:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#document" +msgstr "" + +#: of telebot.types.Document:12 +msgid "Optional. Document thumbnail as defined by sender" +msgstr "" + +#: of telebot.types.Document:27 +msgid ":class:`telebot.types.Document`" +msgstr "" + +#: of telebot.types.File:1 +msgid "" +"This object represents a file ready to be downloaded. The file can be " +"downloaded via the link " +"https://api.telegram.org/file/bot/. It is guaranteed " +"that the link will be valid for at least 1 hour. When the link expires, a" +" new one can be requested by calling getFile." +msgstr "" + +#: of telebot.types.File:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#file" +msgstr "" + +#: of telebot.types.File:17 +msgid "" +"Optional. File path. Use " +"https://api.telegram.org/file/bot/ to get the file." +msgstr "" + +#: of telebot.types.File:22 +msgid ":class:`telebot.types.File`" +msgstr "" + +#: of telebot.types.ForceReply:1 telebot.types.ReplyKeyboardMarkup:1 +#: telebot.types.ReplyKeyboardRemove:1 telebot.types.ShippingOption:1 +msgid "Bases: :py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.ForceReply:1 +msgid "" +"Upon receiving a message with this object, Telegram clients will display " +"a reply interface to the user (act as if the user has selected the bot's " +"message and tapped 'Reply'). This can be extremely useful if you want to " +"create user-friendly step-by-step interfaces without having to sacrifice " +"privacy mode." +msgstr "" + +#: of telebot.types.ForceReply:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#forcereply" +msgstr "" + +#: of telebot.types.ForceReply:5 +msgid "" +"Shows reply interface to the user, as if they manually selected the bot's" +" message and tapped 'Reply'" +msgstr "" + +#: of telebot.types.ForceReply:9 +msgid "" +"Optional. The placeholder to be shown in the input field when the reply " +"is active; 1-64 characters" +msgstr "" + +#: of telebot.types.ForceReply:13 +msgid "" +"Optional. Use this parameter if you want to force reply from specific " +"users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message." +msgstr "" + +#: of telebot.types.ForceReply:19 +msgid ":class:`telebot.types.ForceReply`" +msgstr "" + +#: of telebot.types.ForumTopic:1 +msgid "This object represents a forum topic." +msgstr "" + +#: of telebot.types.ForumTopic:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#forumtopic" +msgstr "" + +#: of telebot.types.ForumTopic:5 +msgid "Unique identifier of the forum topic" +msgstr "" + +#: of telebot.types.ForumTopic:8 telebot.types.ForumTopicCreated:5 +msgid "Name of the topic" +msgstr "" + +#: of telebot.types.ForumTopic:11 telebot.types.ForumTopicCreated:8 +msgid "Color of the topic icon in RGB format" +msgstr "" + +#: of telebot.types.ForumTopic:14 telebot.types.ForumTopicCreated:11 +msgid "Optional. Unique identifier of the custom emoji shown as the topic icon" +msgstr "" + +#: of telebot.types.ForumTopic:18 +msgid ":class:`telebot.types.ForumTopic`" +msgstr "" + +#: of telebot.types.ForumTopicClosed:1 +msgid "" +"This object represents a service message about a forum topic closed in " +"the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.ForumTopicClosed:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopicclosed" +msgstr "" + +#: of telebot.types.ForumTopicCreated:1 +msgid "" +"This object represents a service message about a new forum topic created " +"in the chat." +msgstr "" + +#: of telebot.types.ForumTopicCreated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopiccreated" +msgstr "" + +#: of telebot.types.ForumTopicCreated:15 +msgid ":class:`telebot.types.ForumTopicCreated`" +msgstr "" + +#: of telebot.types.ForumTopicReopened:1 +msgid "" +"This object represents a service message about a forum topic reopened in " +"the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.ForumTopicReopened:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopicreopened" +msgstr "" + +#: of telebot.types.Game:1 +msgid "" +"This object represents a game. Use BotFather to create and edit games, " +"their short names will act as unique identifiers." +msgstr "" + +#: of telebot.types.Game:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#game" +msgstr "" + +#: of telebot.types.Game:5 +msgid "Title of the game" +msgstr "" + +#: of telebot.types.Game:8 +msgid "Description of the game" +msgstr "" + +#: of telebot.types.Game:11 +msgid "Photo that will be displayed in the game message in chats." +msgstr "" + +#: of telebot.types.Game:14 +msgid "" +"Optional. Brief description of the game or high scores included in the " +"game message. Can be automatically edited to include current high scores " +"for the game when the bot calls setGameScore, or manually edited using " +"editMessageText. 0-4096 characters." +msgstr "" + +#: of telebot.types.Game:19 +msgid "" +"Optional. Special entities that appear in text, such as usernames, URLs, " +"bot commands, etc." +msgstr "" + +#: of telebot.types.Game:22 +msgid "" +"Optional. Animation that will be displayed in the game message in chats. " +"Upload via BotFather" +msgstr "" + +#: of telebot.types.Game:26 +msgid ":class:`telebot.types.Game`" +msgstr "" + +#: of telebot.types.Game.parse_entities:1 +msgid "Parse the message entity array into a list of MessageEntity objects" +msgstr "" + +#: of telebot.types.Game.parse_photo:1 +msgid "Parse the photo array into a list of PhotoSize objects" +msgstr "" + +#: of telebot.types.GameHighScore:1 +msgid "This object represents one row of the high scores table for a game." +msgstr "" + +#: of telebot.types.GameHighScore:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#gamehighscore" +msgstr "" + +#: of telebot.types.GameHighScore:5 +msgid "Position in high score table for the game" +msgstr "" + +#: of telebot.types.GameHighScore:8 +msgid "User" +msgstr "" + +#: of telebot.types.GameHighScore:11 +msgid "Score" +msgstr "" + +#: of telebot.types.GameHighScore:15 +msgid ":class:`telebot.types.GameHighScore`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 +#: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 +msgid "" +"This object represents one button of an inline keyboard. You must use " +"exactly one of the optional fields." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinekeyboardbutton" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:5 +msgid "Label text on the button" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:8 +msgid "" +"Optional. HTTP or tg:// URL to be opened when the button is pressed. " +"Links tg://user?id= can be used to mention a user by their ID " +"without using a username, if this is allowed by their privacy settings." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:12 +msgid "" +"Optional. Data to be sent in a callback query to the bot when button is " +"pressed, 1-64 bytes" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:15 +msgid "" +"Optional. Description of the Web App that will be launched when the user " +"presses the button. The Web App will be able to send an arbitrary message" +" on behalf of the user using the method answerWebAppQuery. Available only" +" in private chats between a user and the bot." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:20 +msgid "" +"Optional. An HTTPS URL used to automatically authorize the user. Can be " +"used as a replacement for the Telegram Login Widget." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:24 +msgid "" +"Optional. If set, pressing the button will prompt the user to select one " +"of their chats, open that chat and insert the bot's username and the " +"specified inline query in the input field. May be empty, in which case " +"just the bot's username will be inserted.Note: This offers an easy way " +"for users to start using your bot in inline mode when they are currently " +"in a private chat with it. Especially useful when combined with " +"switch_pm… actions - in this case the user will be automatically returned" +" to the chat they switched from, skipping the chat selection screen." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:31 +msgid "" +"Optional. If set, pressing the button will insert the bot's username and " +"the specified inline query in the current chat's input field. May be " +"empty, in which case only the bot's username will be inserted.This offers" +" a quick way for the user to open your bot in inline mode in the same " +"chat - good for selecting something from multiple options." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:37 +msgid "" +"Optional. Description of the game that will be launched when the user " +"presses the button. NOTE: This type of button must always be the first " +"button in the first row." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:41 +msgid "" +"Optional. Specify True, to send a Pay button. NOTE: This type of button " +"must always be the first button in the first row and can only be used in " +"invoice messages." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:46 +msgid ":class:`telebot.types.InlineKeyboardButton`" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:1 +msgid "" +"This object represents an inline keyboard that appears right next to the " +"message it belongs to." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:4 +msgid "It is recommended to use :meth:`telebot.util.quick_markup` instead." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:6 +msgid "Example of a custom keyboard with buttons." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:16 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinekeyboardmarkup" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:18 +msgid "" +":obj:`list` of button rows, each represented by an :obj:`list` of " +":class:`telebot.types.InlineKeyboardButton` objects" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:23 +#: telebot.types.InlineKeyboardMarkup.add:17 +#: telebot.types.InlineKeyboardMarkup.row:12 +msgid ":class:`telebot.types.InlineKeyboardMarkup`" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:1 +msgid "This method adds buttons to the keyboard without exceeding row_width." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:3 +msgid "" +"E.g. InlineKeyboardMarkup.add(\"A\", \"B\", \"C\") yields the json " +"result: {keyboard: [[\"A\"], [\"B\"], [\"C\"]]} when row_width is set to " +"1. When row_width is set to 2, the result: {keyboard: [[\"A\", \"B\"], " +"[\"C\"]]} See https://core.telegram.org/bots/api#inlinekeyboardmarkup" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:10 +#: telebot.types.InlineKeyboardMarkup.row:8 +msgid "Array of InlineKeyboardButton to append to the keyboard" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:13 +#: telebot.types.ReplyKeyboardMarkup.add:10 +msgid "width of row" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.add:16 +#: telebot.types.InlineKeyboardMarkup.row:11 +#: telebot.types.ReplyKeyboardMarkup.add:13 +#: telebot.types.ReplyKeyboardMarkup.row:8 +msgid "self, to allow function chaining." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.row:1 +msgid "" +"Adds a list of InlineKeyboardButton to the keyboard. This method does not" +" consider row_width." +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup.row:4 +msgid "" +"InlineKeyboardMarkup.row(\"A\").row(\"B\", \"C\").to_json() outputs: " +"'{keyboard: [[\"A\"], [\"B\", \"C\"]]}' See " +"https://core.telegram.org/bots/api#inlinekeyboardmarkup" +msgstr "" + +#: of telebot.types.InlineQuery:1 +msgid "" +"This object represents an incoming inline query. When the user sends an " +"empty query, your bot could return some default or trending results." +msgstr "" + +#: of telebot.types.InlineQuery:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inlinequery" +msgstr "" + +#: of telebot.types.InlineQuery:11 +msgid "Text of the query (up to 256 characters)" +msgstr "" + +#: of telebot.types.InlineQuery:14 +msgid "Offset of the results to be returned, can be controlled by the bot" +msgstr "" + +#: of telebot.types.InlineQuery:17 +msgid "" +"Optional. Type of the chat from which the inline query was sent. Can be " +"either “sender” for a private chat with the inline query sender, " +"“private”, “group”, “supergroup”, or “channel”. The chat type should be " +"always known for requests sent from official clients and most third-party" +" clients, unless the request was sent from a secret chat" +msgstr "" + +#: of telebot.types.InlineQuery:23 +msgid "Optional. Sender location, only for bots that request user location" +msgstr "" + +#: of telebot.types.InlineQuery:27 +msgid ":class:`telebot.types.InlineQuery`" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:1 +#: telebot.types.InlineQueryResultAudio:1 +#: telebot.types.InlineQueryResultContact:1 +#: telebot.types.InlineQueryResultDocument:1 +#: telebot.types.InlineQueryResultGame:1 telebot.types.InlineQueryResultGif:1 +#: telebot.types.InlineQueryResultLocation:1 +#: telebot.types.InlineQueryResultMpeg4Gif:1 +#: telebot.types.InlineQueryResultPhoto:1 +#: telebot.types.InlineQueryResultVenue:1 +#: telebot.types.InlineQueryResultVideo:1 +#: telebot.types.InlineQueryResultVoice:1 +msgid "Bases: :py:class:`telebot.types.InlineQueryResultBase`" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:1 +msgid "Represents a link to an article or web page." +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultarticle" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:5 +msgid "Type of the result, must be article" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:8 +#: telebot.types.InlineQueryResultContact:8 +#: telebot.types.InlineQueryResultLocation:8 +#: telebot.types.InlineQueryResultVenue:8 +msgid "Unique identifier for this result, 1-64 Bytes" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:11 +msgid "Title of the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:14 +msgid "Content of the message to be sent" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:17 +#: telebot.types.InlineQueryResultAudio:34 +#: telebot.types.InlineQueryResultCachedAudio:25 +#: telebot.types.InlineQueryResultCachedDocument:31 +#: telebot.types.InlineQueryResultCachedGif:27 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:27 +#: telebot.types.InlineQueryResultCachedPhoto:31 +#: telebot.types.InlineQueryResultCachedSticker:14 +#: telebot.types.InlineQueryResultCachedVideo:31 +#: telebot.types.InlineQueryResultCachedVoice:28 +#: telebot.types.InlineQueryResultContact:23 +#: telebot.types.InlineQueryResultDocument:34 +#: telebot.types.InlineQueryResultGame:14 telebot.types.InlineQueryResultGif:43 +#: telebot.types.InlineQueryResultLocation:35 +#: telebot.types.InlineQueryResultMpeg4Gif:43 +#: telebot.types.InlineQueryResultPhoto:40 +#: telebot.types.InlineQueryResultVenue:36 +#: telebot.types.InlineQueryResultVideo:46 +#: telebot.types.InlineQueryResultVoice:31 +msgid "Optional. Inline keyboard attached to the message" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:20 +msgid "Optional. URL of the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:23 +msgid "Optional. Pass True, if you don't want the URL to be shown in the message" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:26 +#: telebot.types.InlineQueryResultCachedDocument:17 +#: telebot.types.InlineQueryResultCachedPhoto:17 +#: telebot.types.InlineQueryResultCachedVideo:17 +#: telebot.types.InlineQueryResultDocument:31 +#: telebot.types.InlineQueryResultPhoto:26 +#: telebot.types.InlineQueryResultVideo:43 +msgid "Optional. Short description of the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:29 +#: telebot.types.InlineQueryResultContact:29 +#: telebot.types.InlineQueryResultLocation:41 +#: telebot.types.InlineQueryResultVenue:42 +msgid "Optional. Url of the thumbnail for the result" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:32 +#: telebot.types.InlineQueryResultContact:32 +#: telebot.types.InlineQueryResultDocument:43 +#: telebot.types.InlineQueryResultLocation:44 +#: telebot.types.InlineQueryResultVenue:45 +msgid "Optional. Thumbnail width" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:35 +#: telebot.types.InlineQueryResultContact:35 +#: telebot.types.InlineQueryResultDocument:46 +#: telebot.types.InlineQueryResultLocation:47 +#: telebot.types.InlineQueryResultVenue:48 +msgid "Optional. Thumbnail height" +msgstr "" + +#: of telebot.types.InlineQueryResultArticle:39 +msgid ":class:`telebot.types.InlineQueryResultArticle`" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultaudio" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:5 +#: telebot.types.InlineQueryResultCachedAudio:5 telebot.types.InputMediaAudio:5 +msgid "Type of the result, must be audio" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:8 +#: telebot.types.InlineQueryResultCachedAudio:8 +#: telebot.types.InlineQueryResultCachedDocument:8 +#: telebot.types.InlineQueryResultCachedGif:8 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:8 +#: telebot.types.InlineQueryResultCachedPhoto:8 +#: telebot.types.InlineQueryResultCachedSticker:8 +#: telebot.types.InlineQueryResultCachedVideo:8 +#: telebot.types.InlineQueryResultCachedVoice:8 +#: telebot.types.InlineQueryResultDocument:8 +#: telebot.types.InlineQueryResultGame:8 telebot.types.InlineQueryResultGif:8 +#: telebot.types.InlineQueryResultMpeg4Gif:8 +#: telebot.types.InlineQueryResultPhoto:8 +#: telebot.types.InlineQueryResultVideo:8 +#: telebot.types.InlineQueryResultVoice:8 +msgid "Unique identifier for this result, 1-64 bytes" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:11 +msgid "A valid URL for the audio file" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:14 +msgid "Title" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:17 +#: telebot.types.InlineQueryResultCachedAudio:14 +#: telebot.types.InlineQueryResultCachedVoice:17 +#: telebot.types.InlineQueryResultVoice:17 +msgid "Optional. Caption, 0-1024 characters after entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:20 +#: telebot.types.InlineQueryResultCachedAudio:17 +#: telebot.types.InputMediaAudio:23 +msgid "" +"Optional. Mode for parsing entities in the audio caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:24 +#: telebot.types.InlineQueryResultCachedAudio:21 +#: telebot.types.InlineQueryResultCachedDocument:27 +#: telebot.types.InlineQueryResultCachedGif:23 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:23 +#: telebot.types.InlineQueryResultCachedPhoto:27 +#: telebot.types.InlineQueryResultCachedVideo:27 +#: telebot.types.InlineQueryResultCachedVoice:24 +#: telebot.types.InlineQueryResultDocument:21 +#: telebot.types.InlineQueryResultGif:39 +#: telebot.types.InlineQueryResultMpeg4Gif:39 +#: telebot.types.InlineQueryResultPhoto:36 +#: telebot.types.InlineQueryResultVideo:30 +#: telebot.types.InlineQueryResultVoice:24 telebot.types.InputMediaAnimation:27 +#: telebot.types.InputMediaAudio:27 telebot.types.InputMediaDocument:27 +#: telebot.types.InputMediaPhoto:20 telebot.types.InputMediaVideo:27 +msgid "" +"Optional. List of special entities that appear in the caption, which can " +"be specified instead of parse_mode" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:28 +msgid "Optional. Performer" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:31 +msgid "Optional. Audio duration in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:37 +#: telebot.types.InlineQueryResultCachedAudio:28 +msgid "Optional. Content of the message to be sent instead of the audio" +msgstr "" + +#: of telebot.types.InlineQueryResultAudio:41 +msgid ":class:`telebot.types.InlineQueryResultAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:1 +msgid "" +"Bases: :py:class:`abc.ABC`, :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:1 +msgid "" +"This object represents one result of an inline query. Telegram clients " +"currently support results of the following 20 types:" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:3 +msgid ":class:`InlineQueryResultCachedAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:4 +msgid ":class:`InlineQueryResultCachedDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:5 +msgid ":class:`InlineQueryResultCachedGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:6 +msgid ":class:`InlineQueryResultCachedMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:7 +msgid ":class:`InlineQueryResultCachedPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:8 +msgid ":class:`InlineQueryResultCachedSticker`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:9 +msgid ":class:`InlineQueryResultCachedVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:10 +msgid ":class:`InlineQueryResultCachedVoice`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:11 +msgid ":class:`InlineQueryResultArticle`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:12 +msgid ":class:`InlineQueryResultAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:13 +msgid ":class:`InlineQueryResultContact`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:14 +msgid ":class:`InlineQueryResultGame`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:15 +msgid ":class:`InlineQueryResultDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:16 +msgid ":class:`InlineQueryResultGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:17 +msgid ":class:`InlineQueryResultLocation`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:18 +msgid ":class:`InlineQueryResultMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:19 +msgid ":class:`InlineQueryResultPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:20 +msgid ":class:`InlineQueryResultVenue`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:21 +msgid ":class:`InlineQueryResultVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:22 +msgid ":class:`InlineQueryResultVoice`" +msgstr "" + +#: of telebot.types.InlineQueryResultBase:24 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresult" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:1 +#: telebot.types.InlineQueryResultCachedDocument:1 +#: telebot.types.InlineQueryResultCachedGif:1 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:1 +#: telebot.types.InlineQueryResultCachedPhoto:1 +#: telebot.types.InlineQueryResultCachedSticker:1 +#: telebot.types.InlineQueryResultCachedVideo:1 +#: telebot.types.InlineQueryResultCachedVoice:1 +msgid "Bases: :py:class:`telebot.types.InlineQueryResultCachedBase`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedaudio" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:11 +msgid "A valid file identifier for the audio file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedAudio:32 +msgid ":class:`telebot.types.InlineQueryResultCachedAudio`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedBase:1 +msgid "Base class of all InlineQueryResultCached* classes." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcacheddocument" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:5 +#: telebot.types.InlineQueryResultDocument:5 telebot.types.InputMediaDocument:5 +msgid "Type of the result, must be document" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:11 +#: telebot.types.InlineQueryResultCachedVideo:14 +#: telebot.types.InlineQueryResultDocument:11 +#: telebot.types.InlineQueryResultVideo:20 +msgid "Title for the result" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:14 +msgid "A valid file identifier for the file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:20 +#: telebot.types.InlineQueryResultDocument:14 +#: telebot.types.InputMediaDocument:20 +msgid "" +"Optional. Caption of the document to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:23 +#: telebot.types.InlineQueryResultDocument:17 +#: telebot.types.InputMediaDocument:23 +msgid "" +"Optional. Mode for parsing entities in the document caption. See " +"formatting options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:34 +#: telebot.types.InlineQueryResultDocument:37 +msgid "Optional. Content of the message to be sent instead of the file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedDocument:38 +msgid ":class:`telebot.types.InlineQueryResultCachedDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedgif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:5 +#: telebot.types.InlineQueryResultGif:5 +msgid "Type of the result, must be gif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:11 +msgid "A valid file identifier for the GIF file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:14 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:14 +#: telebot.types.InlineQueryResultCachedPhoto:14 +#: telebot.types.InlineQueryResultGif:30 +#: telebot.types.InlineQueryResultMpeg4Gif:30 +#: telebot.types.InlineQueryResultPhoto:23 +msgid "Optional. Title for the result" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:17 +#: telebot.types.InlineQueryResultGif:33 +msgid "" +"Optional. Caption of the GIF file to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:20 +#: telebot.types.InlineQueryResultCachedMpeg4Gif:20 +#: telebot.types.InlineQueryResultGif:36 +#: telebot.types.InlineQueryResultMpeg4Gif:36 +msgid "" +"Optional. Mode for parsing entities in the caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:30 +#: telebot.types.InlineQueryResultGif:46 +msgid "Optional. Content of the message to be sent instead of the GIF animation" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedGif:34 +msgid ":class:`telebot.types.InlineQueryResultCachedGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:5 +#: telebot.types.InlineQueryResultMpeg4Gif:5 +msgid "Type of the result, must be mpeg4_gif" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:11 +msgid "A valid file identifier for the MPEG4 file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:17 +#: telebot.types.InlineQueryResultMpeg4Gif:33 +msgid "" +"Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:30 +#: telebot.types.InlineQueryResultMpeg4Gif:46 +msgid "Optional. Content of the message to be sent instead of the video animation" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedMpeg4Gif:34 +msgid ":class:`telebot.types.InlineQueryResultCachedMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedphoto" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:5 +#: telebot.types.InlineQueryResultPhoto:5 telebot.types.InputMediaPhoto:5 +msgid "Type of the result, must be photo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:11 +msgid "A valid file identifier of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:20 +#: telebot.types.InlineQueryResultPhoto:29 telebot.types.InputMediaPhoto:13 +msgid "" +"Optional. Caption of the photo to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:23 +#: telebot.types.InlineQueryResultPhoto:32 telebot.types.InputMediaPhoto:16 +msgid "" +"Optional. Mode for parsing entities in the photo caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:34 +#: telebot.types.InlineQueryResultPhoto:43 +msgid "Optional. Content of the message to be sent instead of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedPhoto:38 +msgid ":class:`telebot.types.InlineQueryResultCachedPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedsticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:5 +msgid "Type of the result, must be sticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:11 +msgid "A valid file identifier of the sticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:17 +msgid "Optional. Content of the message to be sent instead of the sticker" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedSticker:21 +msgid ":class:`telebot.types.InlineQueryResultCachedSticker`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedvideo" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:5 +#: telebot.types.InlineQueryResultVideo:5 telebot.types.InputMediaVideo:5 +msgid "Type of the result, must be video" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:11 +msgid "A valid file identifier for the video file" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:20 +#: telebot.types.InlineQueryResultVideo:23 telebot.types.InputMediaVideo:20 +msgid "" +"Optional. Caption of the video to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:23 +#: telebot.types.InlineQueryResultVideo:26 telebot.types.InputMediaVideo:23 +msgid "" +"Optional. Mode for parsing entities in the video caption. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:34 +msgid "Optional. Content of the message to be sent instead of the video" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVideo:38 +msgid ":class:`telebot.types.InlineQueryResultCachedVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcachedvoice" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:5 +#: telebot.types.InlineQueryResultVoice:5 +msgid "Type of the result, must be voice" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:11 +msgid "A valid file identifier for the voice message" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:14 +msgid "Voice message title" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:20 +#: telebot.types.InlineQueryResultVoice:20 +msgid "" +"Optional. Mode for parsing entities in the voice message caption. See " +"formatting options for more details." +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:31 +msgid "Optional. Content of the message to be sent instead of the voice message" +msgstr "" + +#: of telebot.types.InlineQueryResultCachedVoice:35 +msgid ":class:`telebot.types.InlineQueryResultCachedVoice`" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultContact:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultcontact" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:5 +msgid "Type of the result, must be contact" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:20 +#: telebot.types.InputContactMessageContent:14 +msgid "" +"Optional. Additional data about the contact in the form of a vCard, " +"0-2048 bytes" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:26 +msgid "Optional. Content of the message to be sent instead of the contact" +msgstr "" + +#: of telebot.types.InlineQueryResultContact:39 +msgid ":class:`telebot.types.InlineQueryResultContact`" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultdocument" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:25 +msgid "A valid URL for the file" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:28 +msgid "" +"MIME type of the content of the file, either “application/pdf” or " +"“application/zip”" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:40 +msgid "Optional. URL of the thumbnail (JPEG only) for the file" +msgstr "" + +#: of telebot.types.InlineQueryResultDocument:50 +msgid ":class:`telebot.types.InlineQueryResultDocument`" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:1 +msgid "Represents a Game." +msgstr "" + +#: of telebot.types.InlineQueryResultGame:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultgame" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:5 +msgid "Type of the result, must be game" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:11 +msgid "Short name of the game" +msgstr "" + +#: of telebot.types.InlineQueryResultGame:18 +msgid ":class:`telebot.types.InlineQueryResultGame`" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultGif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultgif" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:11 +msgid "A valid URL for the GIF file. File size must not exceed 1MB" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:14 +msgid "Optional. Width of the GIF" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:17 +msgid "Optional. Height of the GIF" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:20 +msgid "Optional. Duration of the GIF in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:23 +#: telebot.types.InlineQueryResultMpeg4Gif:23 +msgid "" +"URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the " +"result" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:26 +#: telebot.types.InlineQueryResultMpeg4Gif:26 +msgid "" +"Optional. MIME type of the thumbnail, must be one of “image/jpeg”, " +"“image/gif”, or “video/mp4”. Defaults to “image/jpeg”" +msgstr "" + +#: of telebot.types.InlineQueryResultGif:50 +msgid ":class:`telebot.types.InlineQueryResultGif`" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultlocation" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:5 +msgid "Type of the result, must be location" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:11 +msgid "Location latitude in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:14 +msgid "Location longitude in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:17 +msgid "Location title" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:20 +#: telebot.types.InputLocationMessageContent:11 telebot.types.Location:11 +msgid "" +"Optional. The radius of uncertainty for the location, measured in meters;" +" 0-1500" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:23 +#: telebot.types.InputLocationMessageContent:14 +msgid "" +"Optional. Period in seconds for which the location can be updated, should" +" be between 60 and 86400." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:27 +#: telebot.types.InputLocationMessageContent:18 +msgid "" +"Optional. For live locations, a direction in which the user is moving, in" +" degrees. Must be between 1 and 360 if specified." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:31 +#: telebot.types.InputLocationMessageContent:22 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:38 +msgid "Optional. Content of the message to be sent instead of the location" +msgstr "" + +#: of telebot.types.InlineQueryResultLocation:51 +msgid ":class:`telebot.types.InlineQueryResultLocation`" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:11 +msgid "A valid URL for the MPEG4 file. File size must not exceed 1MB" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:14 +#: telebot.types.InlineQueryResultVideo:34 telebot.types.InputMediaVideo:31 +msgid "Optional. Video width" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:17 +#: telebot.types.InlineQueryResultVideo:37 telebot.types.InputMediaVideo:34 +msgid "Optional. Video height" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:20 +#: telebot.types.InlineQueryResultVideo:40 telebot.types.InputMediaVideo:37 +msgid "Optional. Video duration in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultMpeg4Gif:50 +msgid ":class:`telebot.types.InlineQueryResultMpeg4Gif`" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultphoto" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:11 +msgid "" +"A valid URL of the photo. Photo must be in JPEG format. Photo size must " +"not exceed 5MB" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:14 +msgid "URL of the thumbnail for the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:17 +msgid "Optional. Width of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:20 +msgid "Optional. Height of the photo" +msgstr "" + +#: of telebot.types.InlineQueryResultPhoto:47 +msgid ":class:`telebot.types.InlineQueryResultPhoto`" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultvenue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:5 +msgid "Type of the result, must be venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:11 +msgid "Latitude of the venue location in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:14 +msgid "Longitude of the venue location in degrees" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:17 +msgid "Title of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:20 +#: telebot.types.InputVenueMessageContent:14 telebot.types.Venue:11 +msgid "Address of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:23 +msgid "Optional. Foursquare identifier of the venue if known" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:26 +#: telebot.types.InputVenueMessageContent:20 +msgid "" +"Optional. Foursquare type of the venue, if known. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:30 +#: telebot.types.InputVenueMessageContent:24 telebot.types.Venue:21 +msgid "Optional. Google Places identifier of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:33 +#: telebot.types.InputVenueMessageContent:27 telebot.types.Venue:24 +msgid "Optional. Google Places type of the venue. (See supported types.)" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:39 +msgid "Optional. Content of the message to be sent instead of the venue" +msgstr "" + +#: of telebot.types.InlineQueryResultVenue:52 +msgid ":class:`telebot.types.InlineQueryResultVenue`" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultvideo" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:11 +msgid "A valid URL for the embedded video player or video file" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:14 +msgid "MIME type of the content of the video URL, “text/html” or “video/mp4”" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:17 +msgid "URL of the thumbnail (JPEG only) for the video" +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:49 +msgid "" +"Optional. Content of the message to be sent instead of the video. This " +"field is required if InlineQueryResultVideo is used to send an HTML-page " +"as a result (e.g., a YouTube video)." +msgstr "" + +#: of telebot.types.InlineQueryResultVideo:54 +msgid ":class:`telebot.types.InlineQueryResultVideo`" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:1 +msgid "" +"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." +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultvoice" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:11 +msgid "A valid URL for the voice recording" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:14 +msgid "Recording title" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:28 +msgid "Optional. Recording duration in seconds" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:34 +msgid "Optional. Content of the message to be sent instead of the voice recording" +msgstr "" + +#: of telebot.types.InlineQueryResultVoice:38 +msgid ":class:`telebot.types.InlineQueryResultVoice`" +msgstr "" + +#: of telebot.types.InputContactMessageContent:1 +#: telebot.types.InputInvoiceMessageContent:1 +#: telebot.types.InputLocationMessageContent:1 +#: telebot.types.InputTextMessageContent:1 +#: telebot.types.InputVenueMessageContent:1 +#: telebot.types.KeyboardButtonPollType:1 +msgid "Bases: :py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.InputContactMessageContent:1 +msgid "" +"Represents the content of a contact message to be sent as the result of " +"an inline query." +msgstr "" + +#: of telebot.types.InputContactMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputcontactmessagecontent" +msgstr "" + +#: of telebot.types.InputContactMessageContent:18 +msgid ":class:`telebot.types.InputContactMessageContent`" +msgstr "" + +#: of telebot.types.InputFile:1 +msgid "A class to send files through Telegram Bot API." +msgstr "" + +#: of telebot.types.InputFile:3 +msgid "" +"You need to pass a file, which should be an instance of " +":class:`io.IOBase` or :class:`pathlib.Path`, or :obj:`str`." +msgstr "" + +#: of telebot.types.InputFile:6 +msgid "" +"If you pass an :obj:`str` as a file, it will be opened and closed by the " +"class." +msgstr "" + +#: of telebot.types.InputFile:8 +msgid "A file to send." +msgstr "" + +#: of telebot.types.InputFile:11 +msgid "Example on sending a file using this class" +msgstr "" + +#: of telebot.types.InputFile.file:1 +msgid "File object." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:1 +msgid "" +"Represents the content of an invoice message to be sent as the result of " +"an inline query." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputinvoicemessagecontent" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:5 +msgid "Product name, 1-32 characters" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:8 +msgid "Product description, 1-255 characters" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:11 +msgid "" +"Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " +"the user, use for your internal processes." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:15 +msgid "Payment provider token, obtained via @BotFather" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:18 +msgid "Three-letter ISO 4217 currency code, see more on currencies" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:21 +msgid "" +"Price breakdown, a JSON-serialized list of components (e.g. product " +"price, tax, discount, delivery cost, delivery tax, bonus, etc.)" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:25 +msgid "" +"Optional. The maximum accepted amount for tips in the smallest units of " +"the currency (integer, not float/double). For example, for a maximum tip " +"of US$ 1.45 pass max_tip_amount = 145. See the exp parameter in " +"currencies.json, it shows the number of digits past the decimal point for" +" each currency (2 for the majority of currencies). Defaults to 0" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:31 +msgid "" +"Optional. A JSON-serialized array of suggested amounts of tip in the " +"smallest units of the currency (integer, not float/double). 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." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:36 +msgid "" +"Optional. A JSON-serialized object for data about the invoice, which will" +" be shared with the payment provider. A detailed description of the " +"required fields should be provided by the payment provider." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:40 +msgid "" +"Optional. URL of the product photo for the invoice. Can be a photo of the" +" goods or a marketing image for a service." +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:44 +msgid "Optional. Photo size in bytes" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:47 +msgid "Optional. Photo width" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:50 +msgid "Optional. Photo height" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:53 +msgid "" +"Optional. Pass True, if you require the user's full name to complete the " +"order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:56 +msgid "" +"Optional. Pass True, if you require the user's phone number to complete " +"the order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:59 +msgid "" +"Optional. Pass True, if you require the user's email address to complete " +"the order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:62 +msgid "" +"Optional. Pass True, if you require the user's shipping address to " +"complete the order" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:66 +msgid "Optional. Pass True, if the user's phone number should be sent to provider" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:69 +msgid "" +"Optional. Pass True, if the user's email address should be sent to " +"provider" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:72 +msgid "Optional. Pass True, if the final price depends on the shipping method" +msgstr "" + +#: of telebot.types.InputInvoiceMessageContent:76 +msgid ":class:`telebot.types.InputInvoiceMessageContent`" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:1 +msgid "" +"Represents the content of a location message to be sent as the result of " +"an inline query." +msgstr "" + +#: of telebot.types.InputLocationMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputlocationmessagecontent" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:5 +msgid "Latitude of the location in degrees" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:8 +msgid "Longitude of the location in degrees" +msgstr "" + +#: of telebot.types.InputLocationMessageContent:27 +msgid ":class:`telebot.types.InputLocationMessageContent`" +msgstr "" + +#: of telebot.types.InputMedia:1 telebot.types.KeyboardButton:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.InputMedia:1 +msgid "" +"This object represents the content of a media message to be sent. It " +"should be one of" +msgstr "" + +#: of telebot.types.InputMedia:3 +msgid ":class:`InputMediaAnimation`" +msgstr "" + +#: of telebot.types.InputMedia:4 +msgid ":class:`InputMediaDocument`" +msgstr "" + +#: of telebot.types.InputMedia:5 +msgid ":class:`InputMediaAudio`" +msgstr "" + +#: of telebot.types.InputMedia:6 +msgid ":class:`InputMediaPhoto`" +msgstr "" + +#: of telebot.types.InputMedia:7 +msgid ":class:`InputMediaVideo`" +msgstr "" + +#: of telebot.types.InputMediaAnimation:1 telebot.types.InputMediaAudio:1 +#: telebot.types.InputMediaDocument:1 telebot.types.InputMediaPhoto:1 +#: telebot.types.InputMediaVideo:1 +msgid "Bases: :py:class:`telebot.types.InputMedia`" +msgstr "" + +#: of telebot.types.InputMediaAnimation:1 +msgid "" +"Represents an animation file (GIF or H.264/MPEG-4 AVC video without " +"sound) to be sent." +msgstr "" + +#: of telebot.types.InputMediaAnimation:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputmediaanimation" +msgstr "" + +#: of telebot.types.InputMediaAnimation:5 +msgid "Type of the result, must be animation" +msgstr "" + +#: of telebot.types.InputMediaAnimation:8 telebot.types.InputMediaAudio:8 +#: telebot.types.InputMediaDocument:8 telebot.types.InputMediaPhoto:8 +#: telebot.types.InputMediaVideo:8 +msgid "" +"File to send. Pass a file_id to send a file that exists on the Telegram " +"servers (recommended), pass an HTTP URL for Telegram to get a file from " +"the Internet, or pass “attach://” to upload a new one " +"using multipart/form-data under name. More information" +" on Sending Files »" +msgstr "" + +#: of telebot.types.InputMediaAnimation:13 telebot.types.InputMediaAudio:13 +#: telebot.types.InputMediaDocument:13 telebot.types.InputMediaVideo:13 +msgid "" +"Optional. 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 " +". More information on Sending Files »" +msgstr "" + +#: of telebot.types.InputMediaAnimation:20 +msgid "" +"Optional. Caption of the animation to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InputMediaAnimation:23 +msgid "" +"Optional. Mode for parsing entities in the animation caption. See " +"formatting options for more details." +msgstr "" + +#: of telebot.types.InputMediaAnimation:31 +msgid "Optional. Animation width" +msgstr "" + +#: of telebot.types.InputMediaAnimation:34 +msgid "Optional. Animation height" +msgstr "" + +#: of telebot.types.InputMediaAnimation:37 +msgid "Optional. Animation duration in seconds" +msgstr "" + +#: of telebot.types.InputMediaAnimation:41 +msgid ":class:`telebot.types.InputMediaAnimation`" +msgstr "" + +#: of telebot.types.InputMediaAudio:1 +msgid "Represents an audio file to be treated as music to be sent." +msgstr "" + +#: of telebot.types.InputMediaAudio:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio" +msgstr "" + +#: of telebot.types.InputMediaAudio:20 +msgid "" +"Optional. Caption of the audio to be sent, 0-1024 characters after " +"entities parsing" +msgstr "" + +#: of telebot.types.InputMediaAudio:31 +msgid "Optional. Duration of the audio in seconds" +msgstr "" + +#: of telebot.types.InputMediaAudio:34 +msgid "Optional. Performer of the audio" +msgstr "" + +#: of telebot.types.InputMediaAudio:37 +msgid "Optional. Title of the audio" +msgstr "" + +#: of telebot.types.InputMediaAudio:41 +msgid ":class:`telebot.types.InputMediaAudio`" +msgstr "" + +#: of telebot.types.InputMediaDocument:1 +msgid "Represents a general file to be sent." +msgstr "" + +#: of telebot.types.InputMediaDocument:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputmediadocument" +msgstr "" + +#: of telebot.types.InputMediaDocument:31 +msgid "" +"Optional. Disables automatic server-side content type detection for files" +" uploaded using multipart/form-data. Always True, if the document is sent" +" as part of an album." +msgstr "" + +#: of telebot.types.InputMediaDocument:36 +msgid ":class:`telebot.types.InputMediaDocument`" +msgstr "" + +#: of telebot.types.InputMediaPhoto:1 +msgid "Represents a photo to be sent." +msgstr "" + +#: of telebot.types.InputMediaPhoto:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto" +msgstr "" + +#: of telebot.types.InputMediaPhoto:25 +msgid ":class:`telebot.types.InputMediaPhoto`" +msgstr "" + +#: of telebot.types.InputMediaVideo:1 +msgid "Represents a video to be sent." +msgstr "" + +#: of telebot.types.InputMediaVideo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo" +msgstr "" + +#: of telebot.types.InputMediaVideo:40 +msgid "Optional. Pass True, if the uploaded video is suitable for streaming" +msgstr "" + +#: of telebot.types.InputMediaVideo:44 +msgid ":class:`telebot.types.InputMediaVideo`" +msgstr "" + +#: of telebot.types.InputTextMessageContent:1 +msgid "" +"Represents the content of a text message to be sent as the result of an " +"inline query." +msgstr "" + +#: of telebot.types.InputTextMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputtextmessagecontent" +msgstr "" + +#: of telebot.types.InputTextMessageContent:5 +msgid "Text of the message to be sent, 1-4096 characters" +msgstr "" + +#: of telebot.types.InputTextMessageContent:8 +msgid "" +"Optional. Mode for parsing entities in the message text. See formatting " +"options for more details." +msgstr "" + +#: of telebot.types.InputTextMessageContent:12 +msgid "" +"Optional. List of special entities that appear in message text, which can" +" be specified instead of parse_mode" +msgstr "" + +#: of telebot.types.InputTextMessageContent:16 +msgid "Optional. Disables link previews for links in the sent message" +msgstr "" + +#: of telebot.types.InputTextMessageContent:20 +msgid ":class:`telebot.types.InputTextMessageContent`" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:1 +msgid "" +"Represents the content of a venue message to be sent as the result of an " +"inline query." +msgstr "" + +#: of telebot.types.InputVenueMessageContent:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inputvenuemessagecontent" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:5 +msgid "Latitude of the venue in degrees" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:8 +msgid "Longitude of the venue in degrees" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:11 telebot.types.Venue:8 +msgid "Name of the venue" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:17 +msgid "Optional. Foursquare identifier of the venue, if known" +msgstr "" + +#: of telebot.types.InputVenueMessageContent:31 +msgid ":class:`telebot.types.InputVenueMessageContent`" +msgstr "" + +#: of telebot.types.Invoice:1 +msgid "This object contains basic information about an invoice." +msgstr "" + +#: of telebot.types.Invoice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#invoice" +msgstr "" + +#: of telebot.types.Invoice:5 +msgid "Product name" +msgstr "" + +#: of telebot.types.Invoice:8 +msgid "Product description" +msgstr "" + +#: of telebot.types.Invoice:11 +msgid "" +"Unique bot deep-linking parameter that can be used to generate this " +"invoice" +msgstr "" + +#: of telebot.types.Invoice:14 telebot.types.PreCheckoutQuery:11 +#: telebot.types.SuccessfulPayment:5 +msgid "Three-letter ISO 4217 currency code" +msgstr "" + +#: of telebot.types.Invoice:17 telebot.types.PreCheckoutQuery:14 +#: telebot.types.SuccessfulPayment:8 +msgid "" +"Total price in the smallest units of the currency (integer, not " +"float/double). For example, for a price of US$ 1.45 pass amount = 145. " +"See the exp parameter in currencies.json, it shows the number of digits " +"past the decimal point for each currency (2 for the majority of " +"currencies)." +msgstr "" + +#: of telebot.types.Invoice:23 +msgid ":class:`telebot.types.Invoice`" +msgstr "" + +#: of telebot.types.JsonDeserializable:1 +msgid "" +"Subclasses of this class are guaranteed to be able to be created from a " +"json-style dict or json formatted string. All subclasses of this class " +"must override de_json." +msgstr "" + +#: of telebot.types.JsonSerializable:1 +msgid "" +"Subclasses of this class are guaranteed to be able to be converted to " +"JSON format. All subclasses of this class must override to_json." +msgstr "" + +#: of telebot.types.KeyboardButton:1 +msgid "" +"This object represents one button of the reply keyboard. For simple text " +"buttons String can be used instead of this object to specify text of the " +"button. Optional fields web_app, request_contact, request_location, and " +"request_poll are mutually exclusive." +msgstr "" + +#: of telebot.types.KeyboardButton:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#keyboardbutton" +msgstr "" + +#: of telebot.types.KeyboardButton:5 +msgid "" +"Text of the button. If none of the optional fields are used, it will be " +"sent as a message when the button is pressed" +msgstr "" + +#: of telebot.types.KeyboardButton:9 +msgid "" +"Optional. If True, the user's phone number will be sent as a contact when" +" the button is pressed. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:13 +msgid "" +"Optional. If True, the user's current location will be sent when the " +"button is pressed. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:17 +msgid "" +"Optional. If specified, the user will be asked to create a poll and send " +"it to the bot when the button is pressed. Available in private chats " +"only." +msgstr "" + +#: of telebot.types.KeyboardButton:21 +msgid "" +"Optional. If specified, the described Web App will be launched when the " +"button is pressed. The Web App will be able to send a “web_app_data” " +"service message. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:26 +msgid ":class:`telebot.types.KeyboardButton`" +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:1 +msgid "" +"This object represents type of a poll, which is allowed to be created and" +" sent when the corresponding button is pressed." +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#keyboardbuttonpolltype" +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:5 +msgid "" +"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." +msgstr "" + +#: of telebot.types.KeyboardButtonPollType:10 +msgid ":class:`telebot.types.KeyboardButtonPollType`" +msgstr "" + +#: of telebot.types.LabeledPrice:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.LabeledPrice:1 +msgid "This object represents a portion of the price for goods or services." +msgstr "" + +#: of telebot.types.LabeledPrice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#labeledprice" +msgstr "" + +#: of telebot.types.LabeledPrice:5 +msgid "Portion label" +msgstr "" + +#: of telebot.types.LabeledPrice:8 +msgid "" +"Price of the product in the smallest units of the currency (integer, not " +"float/double). For example, for a price of US$ 1.45 pass amount = 145. " +"See the exp parameter in currencies.json, it shows the number of digits " +"past the decimal point for each currency (2 for the majority of " +"currencies)." +msgstr "" + +#: of telebot.types.LabeledPrice:14 +msgid ":class:`telebot.types.LabeledPrice`" +msgstr "" + +#: of telebot.types.Location:1 +msgid "This object represents a point on the map." +msgstr "" + +#: of telebot.types.Location:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#location" +msgstr "" + +#: of telebot.types.Location:5 +msgid "Longitude as defined by sender" +msgstr "" + +#: of telebot.types.Location:8 +msgid "Latitude as defined by sender" +msgstr "" + +#: of telebot.types.Location:14 +msgid "" +"Optional. Time relative to the message sending date, during which the " +"location can be updated; in seconds. For active live locations only." +msgstr "" + +#: of telebot.types.Location:18 +msgid "" +"Optional. The direction in which user is moving, in degrees; 1-360. For " +"active live locations only." +msgstr "" + +#: of telebot.types.Location:21 +msgid "" +"Optional. The maximum distance for proximity alerts about approaching " +"another chat member, in meters. For sent live locations only." +msgstr "" + +#: of telebot.types.Location:26 +msgid ":class:`telebot.types.Location`" +msgstr "" + +#: of telebot.types.LoginUrl:1 +msgid "" +"This object represents a parameter of the inline keyboard button used to " +"automatically authorize a user. Serves as a great replacement for the " +"Telegram Login Widget when the user is coming from Telegram. All the user" +" needs to do is tap/click a button and confirm that they want to log in:" +msgstr "" + +#: of telebot.types.LoginUrl:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#loginurl" +msgstr "" + +#: of telebot.types.LoginUrl:5 +msgid "" +"An HTTPS URL to be opened with user authorization data added to the query" +" string when the button is pressed. If the user refuses to provide " +"authorization data, the original URL without information about the user " +"will be opened. The data added is the same as described in Receiving " +"authorization data. NOTE: You must always check the hash of the received " +"data to verify the authentication and the integrity of the data as " +"described in Checking authorization." +msgstr "" + +#: of telebot.types.LoginUrl:12 +msgid "Optional. New text of the button in forwarded messages." +msgstr "" + +#: of telebot.types.LoginUrl:15 +msgid "" +"Optional. Username of a bot, which will be used for user authorization. " +"See Setting up a bot for more details. If not specified, the current " +"bot's username will be assumed. The url's domain must be the same as the " +"domain linked with the bot. See Linking your domain to the bot for more " +"details." +msgstr "" + +#: of telebot.types.LoginUrl:20 +msgid "" +"Optional. Pass True to request the permission for your bot to send " +"messages to the user." +msgstr "" + +#: of telebot.types.LoginUrl:25 +msgid ":class:`telebot.types.LoginUrl`" +msgstr "" + +#: of telebot.types.MaskPosition:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.MaskPosition:1 +msgid "" +"This object describes the position on faces where a mask should be placed" +" by default." +msgstr "" + +#: of telebot.types.MaskPosition:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#maskposition" +msgstr "" + +#: of telebot.types.MaskPosition:5 +msgid "" +"The part of the face relative to which the mask should be placed. One of " +"“forehead”, “eyes”, “mouth”, or “chin”." +msgstr "" + +#: of telebot.types.MaskPosition:9 +msgid "" +"Shift by X-axis measured in widths of the mask scaled to the face size, " +"from left to right. For example, choosing -1.0 will place mask just to " +"the left of the default mask position." +msgstr "" + +#: of telebot.types.MaskPosition:13 +msgid "" +"Shift by Y-axis measured in heights of the mask scaled to the face size, " +"from top to bottom. For example, 1.0 will place the mask just below the " +"default mask position." +msgstr "" + +#: of telebot.types.MaskPosition:17 +msgid "Mask scaling coefficient. For example, 2.0 means double size." +msgstr "" + +#: of telebot.types.MaskPosition:21 +msgid ":class:`telebot.types.MaskPosition`" +msgstr "" + +#: of telebot.types.MenuButton:1 +msgid "" +"This object describes the bot's menu button in a private chat. It should " +"be one of" +msgstr "" + +#: of telebot.types.MenuButton:3 +msgid ":class:`MenuButtonCommands`" +msgstr "" + +#: of telebot.types.MenuButton:4 +msgid ":class:`MenuButtonWebApp`" +msgstr "" + +#: of telebot.types.MenuButton:5 +msgid ":class:`MenuButtonDefault`" +msgstr "" + +#: of telebot.types.MenuButton:7 +msgid "" +"If a menu button other than MenuButtonDefault is set for a private chat, " +"then it is applied in the chat. Otherwise the default menu button is " +"applied. By default, the menu button opens the list of bot commands." +msgstr "" + +#: of telebot.types.MenuButtonCommands:1 telebot.types.MenuButtonDefault:1 +#: telebot.types.MenuButtonWebApp:1 +msgid "Bases: :py:class:`telebot.types.MenuButton`" +msgstr "" + +#: of telebot.types.MenuButtonCommands:1 +msgid "Represents a menu button, which opens the bot's list of commands." +msgstr "" + +#: of telebot.types.MenuButtonCommands:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#menubuttoncommands" +msgstr "" + +#: of telebot.types.MenuButtonCommands:5 +msgid "Type of the button, must be commands" +msgstr "" + +#: of telebot.types.MenuButtonCommands:9 +msgid ":class:`telebot.types.MenuButtonCommands`" +msgstr "" + +#: of telebot.types.MenuButtonDefault:1 +msgid "Describes that no specific value for the menu button was set." +msgstr "" + +#: of telebot.types.MenuButtonDefault:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#menubuttondefault" +msgstr "" + +#: of telebot.types.MenuButtonDefault:5 +msgid "Type of the button, must be default" +msgstr "" + +#: of telebot.types.MenuButtonDefault:9 +msgid ":class:`telebot.types.MenuButtonDefault`" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:1 +msgid "Represents a menu button, which launches a Web App." +msgstr "" + +#: of telebot.types.MenuButtonWebApp:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#menubuttonwebapp" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:5 +msgid "Type of the button, must be web_app" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:8 +msgid "Text on the button" +msgstr "" + +#: of telebot.types.MenuButtonWebApp:11 +msgid "" +"Description of the Web App that will be launched when the user presses " +"the button. The Web App will be able to send an arbitrary message on " +"behalf of the user using the method answerWebAppQuery." +msgstr "" + +#: of telebot.types.MenuButtonWebApp:16 +msgid ":class:`telebot.types.MenuButtonWebApp`" +msgstr "" + +#: of telebot.types.Message:1 +msgid "This object represents a message." +msgstr "" + +#: of telebot.types.Message:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#message" +msgstr "" + +#: of telebot.types.Message:5 +msgid "Unique message identifier inside this chat" +msgstr "" + +#: of telebot.types.Message:8 +msgid "" +"Optional. Unique identifier of a message thread to which the message " +"belongs; for supergroups only" +msgstr "" + +#: of telebot.types.Message:11 +msgid "" +"Optional. Sender of the message; empty for messages sent to channels. For" +" backward compatibility, the field contains a fake sender user in non-" +"channel chats, if the message was sent on behalf of a chat." +msgstr "" + +#: of telebot.types.Message:15 +msgid "" +"Optional. Sender of the message, sent on behalf of a chat. For example, " +"the channel itself for channel posts, the supergroup itself for messages " +"from anonymous group administrators, the linked channel for messages " +"automatically forwarded to the discussion group. For backward " +"compatibility, the field from contains a fake sender user in non-channel " +"chats, if the message was sent on behalf of a chat." +msgstr "" + +#: of telebot.types.Message:21 +msgid "Date the message was sent in Unix time" +msgstr "" + +#: of telebot.types.Message:24 +msgid "Conversation the message belongs to" +msgstr "" + +#: of telebot.types.Message:27 +msgid "Optional. For forwarded messages, sender of the original message" +msgstr "" + +#: of telebot.types.Message:30 +msgid "" +"Optional. For messages forwarded from channels or from anonymous " +"administrators, information about the original sender chat" +msgstr "" + +#: of telebot.types.Message:34 +msgid "" +"Optional. For messages forwarded from channels, identifier of the " +"original message in the channel" +msgstr "" + +#: of telebot.types.Message:38 +msgid "" +"Optional. For forwarded messages that were originally sent in channels or" +" by an anonymous chat administrator, signature of the message sender if " +"present" +msgstr "" + +#: of telebot.types.Message:42 +msgid "" +"Optional. Sender's name for messages forwarded from users who disallow " +"adding a link to their account in forwarded messages" +msgstr "" + +#: of telebot.types.Message:46 +msgid "" +"Optional. For forwarded messages, date the original message was sent in " +"Unix time" +msgstr "" + +#: of telebot.types.Message:49 +msgid "Optional. True, if the message is sent to a forum topic" +msgstr "" + +#: of telebot.types.Message:52 +msgid "" +"Optional. :obj:`bool`, if the message is a channel post that was " +"automatically forwarded to the connected discussion group" +msgstr "" + +#: of telebot.types.Message:56 +msgid "" +"Optional. For replies, the original message. Note that the Message object" +" in this field will not contain further reply_to_message fields even if " +"it itself is a reply." +msgstr "" + +#: of telebot.types.Message:60 +msgid "Optional. Bot through which the message was sent" +msgstr "" + +#: of telebot.types.Message:63 +msgid "Optional. Date the message was last edited in Unix time" +msgstr "" + +#: of telebot.types.Message:66 +msgid "Optional. :obj:`bool`, if the message can't be forwarded" +msgstr "" + +#: of telebot.types.Message:69 +msgid "" +"Optional. The unique identifier of a media message group this message " +"belongs to" +msgstr "" + +#: of telebot.types.Message:72 +msgid "" +"Optional. Signature of the post author for messages in channels, or the " +"custom title of an anonymous group administrator" +msgstr "" + +#: of telebot.types.Message:76 +msgid "Optional. For text messages, the actual UTF-8 text of the message" +msgstr "" + +#: of telebot.types.Message:79 +msgid "" +"Optional. For text messages, special entities like usernames, URLs, bot " +"commands, etc. that appear in the text" +msgstr "" + +#: of telebot.types.Message:83 +msgid "" +"Optional. Message is an animation, information about the animation. For " +"backward compatibility, when this field is set, the document field will " +"also be set" +msgstr "" + +#: of telebot.types.Message:87 +msgid "Optional. Message is an audio file, information about the file" +msgstr "" + +#: of telebot.types.Message:90 +msgid "Optional. Message is a general file, information about the file" +msgstr "" + +#: of telebot.types.Message:93 +msgid "Optional. Message is a photo, available sizes of the photo" +msgstr "" + +#: of telebot.types.Message:96 +msgid "Optional. Message is a sticker, information about the sticker" +msgstr "" + +#: of telebot.types.Message:99 +msgid "Optional. Message is a video, information about the video" +msgstr "" + +#: of telebot.types.Message:102 +msgid "Optional. Message is a video note, information about the video message" +msgstr "" + +#: of telebot.types.Message:105 +msgid "Optional. Message is a voice message, information about the file" +msgstr "" + +#: of telebot.types.Message:108 +msgid "" +"Optional. Caption for the animation, audio, document, photo, video or " +"voice" +msgstr "" + +#: of telebot.types.Message:111 +msgid "" +"Optional. For messages with a caption, special entities like usernames, " +"URLs, bot commands, etc. that appear in the caption" +msgstr "" + +#: of telebot.types.Message:115 +msgid "Optional. Message is a shared contact, information about the contact" +msgstr "" + +#: of telebot.types.Message:118 +msgid "Optional. Message is a dice with random value" +msgstr "" + +#: of telebot.types.Message:121 +msgid "" +"Optional. Message is a game, information about the game. More about games" +" »" +msgstr "" + +#: of telebot.types.Message:124 +msgid "Optional. Message is a native poll, information about the poll" +msgstr "" + +#: of telebot.types.Message:127 +msgid "" +"Optional. Message is a venue, information about the venue. For backward " +"compatibility, when this field is set, the location field will also be " +"set" +msgstr "" + +#: of telebot.types.Message:131 +msgid "Optional. Message is a shared location, information about the location" +msgstr "" + +#: of telebot.types.Message:134 +msgid "" +"Optional. New members that were added to the group or supergroup and " +"information about them (the bot itself may be one of these members)" +msgstr "" + +#: of telebot.types.Message:138 +msgid "" +"Optional. A member was removed from the group, information about them " +"(this member may be the bot itself)" +msgstr "" + +#: of telebot.types.Message:142 +msgid "Optional. A chat title was changed to this value" +msgstr "" + +#: of telebot.types.Message:145 +msgid "Optional. A chat photo was change to this value" +msgstr "" + +#: of telebot.types.Message:148 +msgid "Optional. Service message: the chat photo was deleted" +msgstr "" + +#: of telebot.types.Message:151 +msgid "Optional. Service message: the group has been created" +msgstr "" + +#: of telebot.types.Message:154 +msgid "" +"Optional. Service message: the supergroup has been created. This field " +"can't be received in a message coming through updates, because bot can't " +"be a member of a supergroup when it is created. It can only be found in " +"reply_to_message if someone replies to a very first message in a directly" +" created supergroup." +msgstr "" + +#: of telebot.types.Message:159 +msgid "" +"Optional. Service message: the channel has been created. This field can't" +" be received in a message coming through updates, because bot can't be a " +"member of a channel when it is created. It can only be found in " +"reply_to_message if someone replies to a very first message in a channel." +msgstr "" + +#: of telebot.types.Message:164 +msgid "Optional. Service message: auto-delete timer settings changed in the chat" +msgstr "" + +#: of telebot.types.Message:168 +msgid "" +"Optional. The group has been migrated to a supergroup with the specified " +"identifier. This number may have more than 32 significant bits and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it has at most 52 significant bits, so a signed 64-bit integer or" +" double-precision float type are safe for storing this identifier." +msgstr "" + +#: of telebot.types.Message:174 +msgid "" +"Optional. The supergroup has been migrated from a group with the " +"specified identifier. This number may have more than 32 significant bits " +"and some programming languages may have difficulty/silent defects in " +"interpreting it. But it has at most 52 significant bits, so a signed " +"64-bit integer or double-precision float type are safe for storing this " +"identifier." +msgstr "" + +#: of telebot.types.Message:180 +msgid "" +"Optional. Specified message was pinned. Note that the Message object in " +"this field will not contain further reply_to_message fields even if it is" +" itself a reply." +msgstr "" + +#: of telebot.types.Message:184 +msgid "" +"Optional. Message is an invoice for a payment, information about the " +"invoice. More about payments »" +msgstr "" + +#: of telebot.types.Message:187 +msgid "" +"Optional. Message is a service message about a successful payment, " +"information about the payment. More about payments »" +msgstr "" + +#: of telebot.types.Message:191 +msgid "" +"Optional. The domain name of the website on which the user has logged in." +" More about Telegram Login »" +msgstr "" + +#: of telebot.types.Message:195 +msgid "Optional. Telegram Passport data" +msgstr "" + +#: of telebot.types.Message:198 +msgid "" +"Optional. Service message. A user in the chat triggered another user's " +"proximity alert while sharing Live Location." +msgstr "" + +#: of telebot.types.Message:202 +msgid "Optional. Service message: forum topic created" +msgstr "" + +#: of telebot.types.Message:205 +msgid "Optional. Service message: forum topic closed" +msgstr "" + +#: of telebot.types.Message:208 +msgid "Optional. Service message: forum topic reopened" +msgstr "" + +#: of telebot.types.Message:211 +msgid "Optional. Service message: video chat scheduled" +msgstr "" + +#: of telebot.types.Message:214 +msgid "Optional. Service message: video chat started" +msgstr "" + +#: of telebot.types.Message:217 +msgid "Optional. Service message: video chat ended" +msgstr "" + +#: of telebot.types.Message:220 +msgid "Optional. Service message: new participants invited to a video chat" +msgstr "" + +#: of telebot.types.Message:223 +msgid "Optional. Service message: data sent by a Web App" +msgstr "" + +#: of telebot.types.Message:226 +msgid "" +"Optional. Inline keyboard attached to the message. login_url buttons are " +"represented as ordinary url buttons." +msgstr "" + +#: of telebot.types.Message:231 +msgid ":class:`telebot.types.Message`" +msgstr "" + +#: of telebot.types.Message.html_caption:1 +msgid "Returns html-rendered caption." +msgstr "" + +#: of telebot.types.Message.html_text:1 +msgid "Returns html-rendered text." +msgstr "" + +#: of telebot.types.Message.parse_chat:1 +msgid "Parses chat." +msgstr "" + +#: of telebot.types.Message.parse_entities:1 +msgid "Parses message entity array." +msgstr "" + +#: of telebot.types.Message.parse_photo:1 +msgid "Parses photo array." +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:1 +msgid "" +"This object represents a service message about a change in auto-delete " +"timer settings." +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#messageautodeletetimerchanged" +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:5 +msgid "New auto-delete time for messages in the chat; in seconds" +msgstr "" + +#: of telebot.types.MessageAutoDeleteTimerChanged:9 +msgid ":class:`telebot.types.MessageAutoDeleteTimerChanged`" +msgstr "" + +#: of telebot.types.MessageEntity:1 +msgid "" +"This object represents one special entity in a text message. For example," +" hashtags, usernames, URLs, etc." +msgstr "" + +#: of telebot.types.MessageEntity:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#messageentity" +msgstr "" + +#: of telebot.types.MessageEntity:5 +msgid "" +"Type of the entity. Currently, can be “mention” (@username), “hashtag” " +"(#hashtag), “cashtag” ($USD), “bot_command” (/start@jobs_bot), “url” " +"(https://telegram.org), “email” (do-not-reply@telegram.org), " +"“phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic " +"text), “underline” (underlined text), “strikethrough” (strikethrough " +"text), “spoiler” (spoiler message), “code” (monowidth string), “pre” " +"(monowidth block), “text_link” (for clickable text URLs), “text_mention” " +"(for users without usernames), “custom_emoji” (for inline custom emoji " +"stickers)" +msgstr "" + +#: of telebot.types.MessageEntity:13 +msgid "Offset in UTF-16 code units to the start of the entity" +msgstr "" + +#: of telebot.types.MessageEntity:16 +msgid "Length of the entity in UTF-16 code units" +msgstr "" + +#: of telebot.types.MessageEntity:19 +msgid "" +"Optional. For “text_link” only, URL that will be opened after user taps " +"on the text" +msgstr "" + +#: of telebot.types.MessageEntity:22 +msgid "Optional. For “text_mention” only, the mentioned user" +msgstr "" + +#: of telebot.types.MessageEntity:25 +msgid "Optional. For “pre” only, the programming language of the entity text" +msgstr "" + +#: of telebot.types.MessageEntity:28 +msgid "" +"Optional. For “custom_emoji” only, unique identifier of the custom emoji." +" Use get_custom_emoji_stickers to get full information about the sticker." +msgstr "" + +#: of telebot.types.MessageEntity:33 +msgid ":class:`telebot.types.MessageEntity`" +msgstr "" + +#: of telebot.types.MessageEntity.to_list_of_dicts:1 +msgid "Converts a list of MessageEntity objects to a list of dictionaries." +msgstr "" + +#: of telebot.types.MessageID:1 +msgid "This object represents a unique message identifier." +msgstr "" + +#: of telebot.types.MessageID:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#messageid" +msgstr "" + +#: of telebot.types.MessageID:5 +msgid "Unique message identifier" +msgstr "" + +#: of telebot.types.MessageID:9 +msgid ":class:`telebot.types.MessageId`" +msgstr "" + +#: of telebot.types.OrderInfo:1 +msgid "This object represents information about an order." +msgstr "" + +#: of telebot.types.OrderInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#orderinfo" +msgstr "" + +#: of telebot.types.OrderInfo:5 +msgid "Optional. User name" +msgstr "" + +#: of telebot.types.OrderInfo:8 +msgid "Optional. User's phone number" +msgstr "" + +#: of telebot.types.OrderInfo:11 +msgid "Optional. User email" +msgstr "" + +#: of telebot.types.OrderInfo:14 +msgid "Optional. User shipping address" +msgstr "" + +#: of telebot.types.OrderInfo:18 +msgid ":class:`telebot.types.OrderInfo`" +msgstr "" + +#: of telebot.types.PhotoSize:1 +msgid "This object represents one size of a photo or a file / sticker thumbnail." +msgstr "" + +#: of telebot.types.PhotoSize:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#photosize" +msgstr "" + +#: of telebot.types.PhotoSize:12 +msgid "Photo width" +msgstr "" + +#: of telebot.types.PhotoSize:15 +msgid "Photo height" +msgstr "" + +#: of telebot.types.PhotoSize:18 telebot.types.Sticker:46 +#: telebot.types.VideoNote:21 +msgid "Optional. File size in bytes" +msgstr "" + +#: of telebot.types.PhotoSize:22 +msgid ":class:`telebot.types.PhotoSize`" +msgstr "" + +#: of telebot.types.Poll:1 +msgid "This object contains information about a poll." +msgstr "" + +#: of telebot.types.Poll:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#poll" +msgstr "" + +#: of telebot.types.Poll:5 telebot.types.PollAnswer:5 +msgid "Unique poll identifier" +msgstr "" + +#: of telebot.types.Poll:8 +msgid "Poll question, 1-300 characters" +msgstr "" + +#: of telebot.types.Poll:11 +msgid "List of poll options" +msgstr "" + +#: of telebot.types.Poll:14 +msgid "Total number of users that voted in the poll" +msgstr "" + +#: of telebot.types.Poll:17 +msgid "True, if the poll is closed" +msgstr "" + +#: of telebot.types.Poll:20 +msgid "True, if the poll is anonymous" +msgstr "" + +#: of telebot.types.Poll:23 +msgid "Poll type, currently can be “regular” or “quiz”" +msgstr "" + +#: of telebot.types.Poll:26 +msgid "True, if the poll allows multiple answers" +msgstr "" + +#: of telebot.types.Poll:29 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Poll:33 +msgid "" +"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" +msgstr "" + +#: of telebot.types.Poll:37 +msgid "" +"Optional. Special entities like usernames, URLs, bot commands, etc. that " +"appear in the explanation" +msgstr "" + +#: of telebot.types.Poll:41 +msgid "Optional. Amount of time in seconds the poll will be active after creation" +msgstr "" + +#: of telebot.types.Poll:44 +msgid "" +"Optional. Point in time (Unix timestamp) when the poll will be " +"automatically closed" +msgstr "" + +#: of telebot.types.Poll:48 +msgid ":class:`telebot.types.Poll`" +msgstr "" + +#: of telebot.types.Poll.add:1 +msgid "Add an option to the poll." +msgstr "" + +#: of telebot.types.Poll.add:3 +msgid "Option to add" +msgstr "" + +#: of telebot.types.PollAnswer:1 +msgid "This object represents an answer of a user in a non-anonymous poll." +msgstr "" + +#: of telebot.types.PollAnswer:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#pollanswer" +msgstr "" + +#: of telebot.types.PollAnswer:8 +msgid "The user, who changed the answer to the poll" +msgstr "" + +#: of telebot.types.PollAnswer:11 +msgid "" +"0-based identifiers of answer options, chosen by the user. May be empty " +"if the user retracted their vote." +msgstr "" + +#: of telebot.types.PollAnswer:16 +msgid ":class:`telebot.types.PollAnswer`" +msgstr "" + +#: of telebot.types.PollOption:1 +msgid "This object contains information about one answer option in a poll." +msgstr "" + +#: of telebot.types.PollOption:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#polloption" +msgstr "" + +#: of telebot.types.PollOption:5 +msgid "Option text, 1-100 characters" +msgstr "" + +#: of telebot.types.PollOption:8 +msgid "Number of users that voted for this option" +msgstr "" + +#: of telebot.types.PollOption:12 +msgid ":class:`telebot.types.PollOption`" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:1 +msgid "This object contains information about an incoming pre-checkout query." +msgstr "" + +#: of telebot.types.PreCheckoutQuery:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#precheckoutquery" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:5 telebot.types.ShippingQuery:5 +msgid "Unique query identifier" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:8 telebot.types.ShippingQuery:8 +msgid "User who sent the query" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:19 telebot.types.ShippingQuery:11 +#: telebot.types.SuccessfulPayment:13 +msgid "Bot specified invoice payload" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:22 telebot.types.SuccessfulPayment:16 +msgid "Optional. Identifier of the shipping option chosen by the user" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:25 telebot.types.SuccessfulPayment:19 +msgid "Optional. Order information provided by the user" +msgstr "" + +#: of telebot.types.PreCheckoutQuery:29 +msgid ":class:`telebot.types.PreCheckoutQuery`" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:1 +msgid "" +"This object represents the content of a service message, sent whenever a " +"user in the chat triggers a proximity alert set by another user." +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#proximityalerttriggered" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:5 +msgid "User that triggered the alert" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:8 +msgid "User that set the alert" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:11 +msgid "The distance between the users" +msgstr "" + +#: of telebot.types.ProximityAlertTriggered:15 +msgid ":class:`telebot.types.ProximityAlertTriggered`" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:1 +msgid "" +"This object represents a custom keyboard with reply options (see " +"Introduction to bots for details and examples)." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:3 +msgid "Example on creating ReplyKeyboardMarkup object" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:16 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#replykeyboardmarkup" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:18 +msgid "" +":obj:`list` of button rows, each represented by an :obj:`list` of " +":class:`telebot.types.KeyboardButton` objects" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:22 +msgid "" +"Optional. Requests clients to resize the keyboard vertically for optimal " +"fit (e.g., make the keyboard smaller if there are just two rows of " +"buttons). Defaults to false, in which case the custom keyboard is always " +"of the same height as the app's standard keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:27 +msgid "" +"Optional. Requests clients to hide the keyboard as soon as it's been " +"used. The keyboard will still be available, but clients will " +"automatically display the usual letter-keyboard in the chat - the user " +"can press a special button in the input field to see the custom keyboard " +"again. Defaults to false." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:32 +msgid "" +"Optional. The placeholder to be shown in the input field when the " +"keyboard is active; 1-64 characters" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:36 +msgid "" +"Optional. Use this parameter if you want to show the keyboard to specific" +" users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message.Example: A user " +"requests to change the bot's language, bot replies to the request with a " +"keyboard to select the new language. Other users in the group don't see " +"the keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:43 +#: telebot.types.ReplyKeyboardMarkup.add:14 +#: telebot.types.ReplyKeyboardMarkup.row:9 +msgid ":class:`telebot.types.ReplyKeyboardMarkup`" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.add:1 +msgid "" +"This function adds strings to the keyboard, while not exceeding " +"row_width. E.g. ReplyKeyboardMarkup#add(\"A\", \"B\", \"C\") yields the " +"json result {keyboard: [[\"A\"], [\"B\"], [\"C\"]]} when row_width is set" +" to 1. When row_width is set to 2, the following is the result of this " +"function: {keyboard: [[\"A\", \"B\"], [\"C\"]]} See " +"https://core.telegram.org/bots/api#replykeyboardmarkup" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.add:7 +msgid "KeyboardButton to append to the keyboard" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.row:1 +msgid "" +"Adds a list of KeyboardButton to the keyboard. This function does not " +"consider row_width. ReplyKeyboardMarkup#row(\"A\")#row(\"B\", " +"\"C\")#to_json() outputs '{keyboard: [[\"A\"], [\"B\", \"C\"]]}' See " +"https://core.telegram.org/bots/api#replykeyboardmarkup" +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup.row:5 +msgid "strings" +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:1 +msgid "" +"Upon receiving a message with this object, Telegram clients will remove " +"the current custom keyboard and display the default letter-keyboard. By " +"default, custom keyboards are displayed until a new keyboard is sent by a" +" bot. An exception is made for one-time keyboards that are hidden " +"immediately after the user presses a button (see ReplyKeyboardMarkup)." +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#replykeyboardremove" +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:5 +msgid "" +"Requests clients to remove the custom keyboard (user will not be able to " +"summon this keyboard; if you want to hide the keyboard from sight but " +"keep it accessible, use one_time_keyboard in ReplyKeyboardMarkup) Note " +"that this parameter is set to True by default by the library. You cannot " +"modify it." +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:11 +msgid "" +"Optional. Use this parameter if you want to remove the keyboard for " +"specific users only. Targets: 1) users that are @mentioned in the text of" +" the Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message.Example: A user " +"votes in a poll, bot returns confirmation message in reply to the vote " +"and removes the keyboard for that user, while still showing the keyboard " +"with poll options to users who haven't voted yet." +msgstr "" + +#: of telebot.types.ReplyKeyboardRemove:19 +msgid ":class:`telebot.types.ReplyKeyboardRemove`" +msgstr "" + +#: of telebot.types.SentWebAppMessage:1 telebot.types.WebAppData:1 +#: telebot.types.WebAppInfo:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.SentWebAppMessage:1 +msgid "Describes an inline message sent by a Web App on behalf of a user." +msgstr "" + +#: of telebot.types.SentWebAppMessage:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#sentwebappmessage" +msgstr "" + +#: of telebot.types.SentWebAppMessage:5 +msgid "" +"Optional. Identifier of the sent inline message. Available only if there " +"is an inline keyboard attached to the message." +msgstr "" + +#: of telebot.types.SentWebAppMessage:10 +msgid ":class:`telebot.types.SentWebAppMessage`" +msgstr "" + +#: of telebot.types.ShippingAddress:1 +msgid "This object represents a shipping address." +msgstr "" + +#: of telebot.types.ShippingAddress:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#shippingaddress" +msgstr "" + +#: of telebot.types.ShippingAddress:5 +msgid "Two-letter ISO 3166-1 alpha-2 country code" +msgstr "" + +#: of telebot.types.ShippingAddress:8 +msgid "State, if applicable" +msgstr "" + +#: of telebot.types.ShippingAddress:11 +msgid "City" +msgstr "" + +#: of telebot.types.ShippingAddress:14 +msgid "First line for the address" +msgstr "" + +#: of telebot.types.ShippingAddress:17 +msgid "Second line for the address" +msgstr "" + +#: of telebot.types.ShippingAddress:20 +msgid "Address post code" +msgstr "" + +#: of telebot.types.ShippingAddress:24 +msgid ":class:`telebot.types.ShippingAddress`" +msgstr "" + +#: of telebot.types.ShippingOption:1 +msgid "This object represents one shipping option." +msgstr "" + +#: of telebot.types.ShippingOption:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#shippingoption" +msgstr "" + +#: of telebot.types.ShippingOption:5 +msgid "Shipping option identifier" +msgstr "" + +#: of telebot.types.ShippingOption:8 +msgid "Option title" +msgstr "" + +#: of telebot.types.ShippingOption:11 +msgid "List of price portions" +msgstr "" + +#: of telebot.types.ShippingOption:15 +msgid ":class:`telebot.types.ShippingOption`" +msgstr "" + +#: of telebot.types.ShippingOption.add_price:1 +msgid "Add LabeledPrice to ShippingOption" +msgstr "" + +#: of telebot.types.ShippingOption.add_price:3 +msgid "LabeledPrices" +msgstr "" + +#: of telebot.types.ShippingOption.add_price:6 +msgid "None" +msgstr "" + +#: of telebot.types.ShippingQuery:1 +msgid "This object contains information about an incoming shipping query." +msgstr "" + +#: of telebot.types.ShippingQuery:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#shippingquery" +msgstr "" + +#: of telebot.types.ShippingQuery:14 +msgid "User specified shipping address" +msgstr "" + +#: of telebot.types.ShippingQuery:18 +msgid ":class:`telebot.types.ShippingQuery`" +msgstr "" + +#: of telebot.types.Sticker:1 +msgid "This object represents a sticker." +msgstr "" + +#: of telebot.types.Sticker:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#sticker" +msgstr "" + +#: of telebot.types.Sticker:12 +msgid "" +"Type of the sticker, currently one of “regular”, “mask”, “custom_emoji”. " +"The type of the sticker is independent from its format, which is " +"determined by the fields is_animated and is_video." +msgstr "" + +#: of telebot.types.Sticker:16 +msgid "Sticker width" +msgstr "" + +#: of telebot.types.Sticker:19 +msgid "Sticker height" +msgstr "" + +#: of telebot.types.Sticker:22 +msgid "True, if the sticker is animated" +msgstr "" + +#: of telebot.types.Sticker:25 +msgid "True, if the sticker is a video sticker" +msgstr "" + +#: of telebot.types.Sticker:28 +msgid "Optional. Sticker thumbnail in the .WEBP or .JPG format" +msgstr "" + +#: of telebot.types.Sticker:31 +msgid "Optional. Emoji associated with the sticker" +msgstr "" + +#: of telebot.types.Sticker:34 +msgid "Optional. Name of the sticker set to which the sticker belongs" +msgstr "" + +#: of telebot.types.Sticker:37 +msgid "Optional. Premium animation for the sticker, if the sticker is premium" +msgstr "" + +#: of telebot.types.Sticker:40 +msgid "Optional. For mask stickers, the position where the mask should be placed" +msgstr "" + +#: of telebot.types.Sticker:43 +msgid "Optional. For custom emoji stickers, unique identifier of the custom emoji" +msgstr "" + +#: of telebot.types.Sticker:50 +msgid ":class:`telebot.types.Sticker`" +msgstr "" + +#: of telebot.types.StickerSet:1 +msgid "This object represents a sticker set." +msgstr "" + +#: of telebot.types.StickerSet:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#stickerset" +msgstr "" + +#: of telebot.types.StickerSet:5 +msgid "Sticker set name" +msgstr "" + +#: of telebot.types.StickerSet:8 +msgid "Sticker set title" +msgstr "" + +#: of telebot.types.StickerSet:11 +msgid "" +"Type of stickers in the set, currently one of “regular”, “mask”, " +"“custom_emoji”" +msgstr "" + +#: of telebot.types.StickerSet:14 +msgid "True, if the sticker set contains animated stickers" +msgstr "" + +#: of telebot.types.StickerSet:17 +msgid "True, if the sticker set contains video stickers" +msgstr "" + +#: of telebot.types.StickerSet:20 +msgid "" +"True, if the sticker set contains masks. Deprecated since Bot API 6.2, " +"use sticker_type instead." +msgstr "" + +#: of telebot.types.StickerSet:24 +msgid "List of all set stickers" +msgstr "" + +#: of telebot.types.StickerSet:27 +msgid "Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format" +msgstr "" + +#: of telebot.types.StickerSet:31 +msgid ":class:`telebot.types.StickerSet`" +msgstr "" + +#: of telebot.types.StickerSet.contains_masks:1 +msgid "Deprecated since Bot API 6.2, use sticker_type instead." +msgstr "" + +#: of telebot.types.SuccessfulPayment:1 +msgid "This object contains basic information about a successful payment." +msgstr "" + +#: of telebot.types.SuccessfulPayment:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#successfulpayment" +msgstr "" + +#: of telebot.types.SuccessfulPayment:22 +msgid "Telegram payment identifier" +msgstr "" + +#: of telebot.types.SuccessfulPayment:25 +msgid "Provider payment identifier" +msgstr "" + +#: of telebot.types.SuccessfulPayment:29 +msgid ":class:`telebot.types.SuccessfulPayment`" +msgstr "" + +#: of telebot.types.Update:1 +msgid "" +"This object represents an incoming update.At most one of the optional " +"parameters can be present in any given update." +msgstr "" + +#: of telebot.types.Update:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#update" +msgstr "" + +#: of telebot.types.Update:5 +msgid "" +"The update's unique identifier. Update identifiers start from a certain " +"positive number and increase sequentially. This ID becomes especially " +"handy if you're using webhooks, since it allows you to ignore repeated " +"updates or to restore the correct update sequence, should they get out of" +" order. If there are no new updates for at least a week, then identifier " +"of the next update will be chosen randomly instead of sequentially." +msgstr "" + +#: of telebot.types.Update:11 +msgid "Optional. New incoming message of any kind - text, photo, sticker, etc." +msgstr "" + +#: of telebot.types.Update:14 +msgid "Optional. New version of a message that is known to the bot and was edited" +msgstr "" + +#: of telebot.types.Update:17 +msgid "" +"Optional. New incoming channel post of any kind - text, photo, sticker, " +"etc." +msgstr "" + +#: of telebot.types.Update:20 +msgid "" +"Optional. New version of a channel post that is known to the bot and was " +"edited" +msgstr "" + +#: of telebot.types.Update:23 +msgid "Optional. New incoming inline query" +msgstr "" + +#: of telebot.types.Update:26 +msgid "" +"Optional. The result of an inline query that was chosen by a user and " +"sent to their chat partner. Please see our documentation on the feedback " +"collecting for details on how to enable these updates for your bot." +msgstr "" + +#: of telebot.types.Update:31 +msgid "Optional. New incoming callback query" +msgstr "" + +#: of telebot.types.Update:34 +msgid "" +"Optional. New incoming shipping query. Only for invoices with flexible " +"price" +msgstr "" + +#: of telebot.types.Update:37 +msgid "" +"Optional. New incoming pre-checkout query. Contains full information " +"about checkout" +msgstr "" + +#: of telebot.types.Update:41 +msgid "" +"Optional. New poll state. Bots receive only updates about stopped polls " +"and polls, which are sent by the bot" +msgstr "" + +#: of telebot.types.Update:45 +msgid "" +"Optional. A user changed their answer in a non-anonymous poll. Bots " +"receive new votes only in polls that were sent by the bot itself." +msgstr "" + +#: of telebot.types.Update:49 +msgid "" +"Optional. The bot's chat member status was updated in a chat. For private" +" chats, this update is received only when the bot is blocked or unblocked" +" by the user." +msgstr "" + +#: of telebot.types.Update:53 +msgid "" +"Optional. A chat member's status was updated in a chat. The bot must be " +"an administrator in the chat and must explicitly specify “chat_member” in" +" the list of allowed_updates to receive these updates." +msgstr "" + +#: of telebot.types.Update:57 +msgid "" +"Optional. A request to join the chat has been sent. The bot must have the" +" can_invite_users administrator right in the chat to receive these " +"updates." +msgstr "" + +#: of telebot.types.Update:62 +msgid ":class:`telebot.types.Update`" +msgstr "" + +#: of telebot.types.User:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.User:1 +msgid "This object represents a Telegram user or bot." +msgstr "" + +#: of telebot.types.User:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#user" +msgstr "" + +#: of telebot.types.User:5 +msgid "" +"Unique identifier for this user or bot. This number may have more than 32" +" significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier." +msgstr "" + +#: of telebot.types.User:10 +msgid "True, if this user is a bot" +msgstr "" + +#: of telebot.types.User:13 +msgid "User's or bot's first name" +msgstr "" + +#: of telebot.types.User:16 +msgid "Optional. User's or bot's last name" +msgstr "" + +#: of telebot.types.User:19 +msgid "Optional. User's or bot's username" +msgstr "" + +#: of telebot.types.User:22 +msgid "Optional. IETF language tag of the user's language" +msgstr "" + +#: of telebot.types.User:25 +msgid "Optional. :obj:`bool`, if this user is a Telegram Premium user" +msgstr "" + +#: of telebot.types.User:28 +msgid "Optional. :obj:`bool`, if this user added the bot to the attachment menu" +msgstr "" + +#: of telebot.types.User:31 +msgid "" +"Optional. True, if the bot can be invited to groups. Returned only in " +"getMe." +msgstr "" + +#: of telebot.types.User:34 +msgid "" +"Optional. True, if privacy mode is disabled for the bot. Returned only in" +" getMe." +msgstr "" + +#: of telebot.types.User:38 +msgid "" +"Optional. True, if the bot supports inline queries. Returned only in " +"getMe." +msgstr "" + +#: of telebot.types.User:42 +msgid ":class:`telebot.types.User`" +msgstr "" + +#: of telebot.types.User.full_name:1 +msgid "User's full name" +msgstr "" + +#: of telebot.types.User.full_name +msgid "type" +msgstr "" + +#: of telebot.types.User.full_name:3 +msgid "return" +msgstr "" + +#: of telebot.types.UserProfilePhotos:1 +msgid "This object represent a user's profile pictures." +msgstr "" + +#: of telebot.types.UserProfilePhotos:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#userprofilephotos" +msgstr "" + +#: of telebot.types.UserProfilePhotos:5 +msgid "Total number of profile pictures the target user has" +msgstr "" + +#: of telebot.types.UserProfilePhotos:8 +msgid "Requested profile pictures (in up to 4 sizes each)" +msgstr "" + +#: of telebot.types.UserProfilePhotos:12 +msgid ":class:`telebot.types.UserProfilePhotos`" +msgstr "" + +#: of telebot.types.Venue:1 +msgid "This object represents a venue." +msgstr "" + +#: of telebot.types.Venue:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#venue" +msgstr "" + +#: of telebot.types.Venue:5 +msgid "Venue location. Can't be a live location" +msgstr "" + +#: of telebot.types.Venue:14 +msgid "Optional. Foursquare identifier of the venue" +msgstr "" + +#: of telebot.types.Venue:17 +msgid "" +"Optional. Foursquare type of the venue. (For example, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” or " +"“food/icecream”.)" +msgstr "" + +#: of telebot.types.Venue:28 +msgid ":class:`telebot.types.Venue`" +msgstr "" + +#: of telebot.types.Video:1 +msgid "This object represents a video file." +msgstr "" + +#: of telebot.types.Video:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#video" +msgstr "" + +#: of telebot.types.Video:21 telebot.types.VideoNote:18 +msgid "Optional. Video thumbnail" +msgstr "" + +#: of telebot.types.Video:36 +msgid ":class:`telebot.types.Video`" +msgstr "" + +#: of telebot.types.VideoChatEnded:1 +msgid "" +"This object represents a service message about a video chat ended in the " +"chat." +msgstr "" + +#: of telebot.types.VideoChatEnded:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#videochatended" +msgstr "" + +#: of telebot.types.VideoChatEnded:5 +msgid "Video chat duration in seconds" +msgstr "" + +#: of telebot.types.VideoChatEnded:9 +msgid ":class:`telebot.types.VideoChatEnded`" +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:1 +msgid "" +"This object represents a service message about new members invited to a " +"video chat." +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#videochatparticipantsinvited" +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:5 +msgid "New members that were invited to the video chat" +msgstr "" + +#: of telebot.types.VideoChatParticipantsInvited:9 +msgid ":class:`telebot.types.VideoChatParticipantsInvited`" +msgstr "" + +#: of telebot.types.VideoChatScheduled:1 +msgid "" +"This object represents a service message about a video chat scheduled in " +"the chat." +msgstr "" + +#: of telebot.types.VideoChatScheduled:3 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#videochatscheduled" +msgstr "" + +#: of telebot.types.VideoChatScheduled:5 +msgid "" +"Point in time (Unix timestamp) when the video chat is supposed to be " +"started by a chat administrator" +msgstr "" + +#: of telebot.types.VideoChatScheduled:10 +msgid ":class:`telebot.types.VideoChatScheduled`" +msgstr "" + +#: of telebot.types.VideoChatStarted:1 +msgid "" +"This object represents a service message about a video chat started in " +"the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.VideoNote:1 +msgid "" +"This object represents a video message (available in Telegram apps as of " +"v.4.0)." +msgstr "" + +#: of telebot.types.VideoNote:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#videonote" +msgstr "" + +#: of telebot.types.VideoNote:12 +msgid "" +"Video width and height (diameter of the video message) as defined by " +"sender" +msgstr "" + +#: of telebot.types.VideoNote:25 +msgid ":class:`telebot.types.VideoNote`" +msgstr "" + +#: of telebot.types.Voice:1 +msgid "This object represents a voice note." +msgstr "" + +#: of telebot.types.Voice:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#voice" +msgstr "" + +#: of telebot.types.Voice:24 +msgid ":class:`telebot.types.Voice`" +msgstr "" + +#: of telebot.types.VoiceChatEnded:1 +msgid "Bases: :py:class:`telebot.types.VideoChatEnded`" +msgstr "" + +#: of telebot.types.VoiceChatEnded:1 +msgid "Deprecated, use :class:`VideoChatEnded` instead." +msgstr "" + +#: of telebot.types.VoiceChatParticipantsInvited:1 +msgid "Bases: :py:class:`telebot.types.VideoChatParticipantsInvited`" +msgstr "" + +#: of telebot.types.VoiceChatParticipantsInvited:1 +msgid "Deprecated, use :class:`VideoChatParticipantsInvited` instead." +msgstr "" + +#: of telebot.types.VoiceChatScheduled:1 +msgid "Bases: :py:class:`telebot.types.VideoChatScheduled`" +msgstr "" + +#: of telebot.types.VoiceChatScheduled:1 +msgid "Deprecated, use :class:`VideoChatScheduled` instead." +msgstr "" + +#: of telebot.types.VoiceChatStarted:1 +msgid "Bases: :py:class:`telebot.types.VideoChatStarted`" +msgstr "" + +#: of telebot.types.VoiceChatStarted:1 +msgid "Deprecated, use :class:`VideoChatStarted` instead." +msgstr "" + +#: of telebot.types.WebAppData:1 +msgid "Describes data sent from a Web App to the bot." +msgstr "" + +#: of telebot.types.WebAppData:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#webappdata" +msgstr "" + +#: of telebot.types.WebAppData:5 +msgid "" +"The data. Be aware that a bad client can send arbitrary data in this " +"field." +msgstr "" + +#: of telebot.types.WebAppData:8 +msgid "" +"Text of the web_app keyboard button from which the Web App was opened. Be" +" aware that a bad client can send arbitrary data in this field." +msgstr "" + +#: of telebot.types.WebAppData:13 +msgid ":class:`telebot.types.WebAppData`" +msgstr "" + +#: of telebot.types.WebAppInfo:1 +msgid "Describes a Web App." +msgstr "" + +#: of telebot.types.WebAppInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#webappinfo" +msgstr "" + +#: of telebot.types.WebAppInfo:5 +msgid "" +"An HTTPS URL of a Web App to be opened with additional data as specified " +"in Initializing Web Apps" +msgstr "" + +#: of telebot.types.WebAppInfo:9 +msgid ":class:`telebot.types.WebAppInfo`" +msgstr "" + +#: of telebot.types.WebhookInfo:1 +msgid "Describes the current status of a webhook." +msgstr "" + +#: of telebot.types.WebhookInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#webhookinfo" +msgstr "" + +#: of telebot.types.WebhookInfo:5 +msgid "Webhook URL, may be empty if webhook is not set up" +msgstr "" + +#: of telebot.types.WebhookInfo:8 +msgid "True, if a custom certificate was provided for webhook certificate checks" +msgstr "" + +#: of telebot.types.WebhookInfo:11 +msgid "Number of updates awaiting delivery" +msgstr "" + +#: of telebot.types.WebhookInfo:14 +msgid "Optional. Currently used webhook IP address" +msgstr "" + +#: of telebot.types.WebhookInfo:17 +msgid "" +"Optional. Unix time for the most recent error that happened when trying " +"to deliver an update via webhook" +msgstr "" + +#: of telebot.types.WebhookInfo:21 +msgid "" +"Optional. Error message in human-readable format for the most recent " +"error that happened when trying to deliver an update via webhook" +msgstr "" + +#: of telebot.types.WebhookInfo:25 +msgid "" +"Optional. Unix time of the most recent error that happened when trying to" +" synchronize available updates with Telegram datacenters" +msgstr "" + +#: of telebot.types.WebhookInfo:29 +msgid "" +"Optional. The maximum allowed number of simultaneous HTTPS connections to" +" the webhook for update delivery" +msgstr "" + +#: of telebot.types.WebhookInfo:33 +msgid "" +"Optional. A list of update types the bot is subscribed to. Defaults to " +"all update types except chat_member" +msgstr "" + +#: of telebot.types.WebhookInfo:38 +msgid ":class:`telebot.types.WebhookInfo`" +msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/util.po b/docs/source/locales/ru/LC_MESSAGES/util.po new file mode 100644 index 000000000..afba83905 --- /dev/null +++ b/docs/source/locales/ru/LC_MESSAGES/util.po @@ -0,0 +1,345 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2022, coder2020official +# This file is distributed under the same license as the pyTelegramBotAPI +# Documentation package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pyTelegramBotAPI Documentation \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../util.rst:3 +msgid "Utils" +msgstr "" + +#: ../../util.rst:5 +msgid "Utils in pyTelegramBotAPI" +msgstr "" + +#: ../../util.rst:5 +msgid "ptba, pytba, pyTelegramBotAPI, utils, guide" +msgstr "" + +#: ../../util.rst:11 +msgid "util file" +msgstr "" + +#: of telebot.util.antiflood:1 +msgid "" +"Use this function inside loops in order to avoid getting TooManyRequests " +"error. Example:" +msgstr "" + +#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_bytes telebot.util.is_command +#: telebot.util.is_dict telebot.util.is_pil_image +#: telebot.util.parse_web_app_data telebot.util.quick_markup +#: telebot.util.smart_split telebot.util.split_string telebot.util.user_link +#: telebot.util.validate_web_app_data telebot.util.webhook_google_functions +msgid "Parameters" +msgstr "" + +#: of telebot.util.antiflood:10 +msgid "The function to call" +msgstr "" + +#: of telebot.util.antiflood:13 +msgid "The arguments to pass to the function" +msgstr "" + +#: of telebot.util.antiflood:16 +msgid "The keyword arguments to pass to the function" +msgstr "" + +#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.generate_random_token +#: telebot.util.is_bytes telebot.util.is_command telebot.util.is_dict +#: telebot.util.is_pil_image telebot.util.parse_web_app_data +#: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string +#: telebot.util.user_link telebot.util.validate_web_app_data +#: telebot.util.webhook_google_functions +msgid "Returns" +msgstr "" + +#: of telebot.util.antiflood:19 +msgid "None" +msgstr "" + +#: of telebot.util.chunks:1 +msgid "Yield successive n-sized chunks from lst." +msgstr "" + +#: ../../docstring of telebot.util.content_type_media:1 +msgid "Contains all media content types." +msgstr "" + +#: ../../docstring of telebot.util.content_type_service:1 +msgid "Contains all service content types such as `User joined the group`." +msgstr "" + +#: of telebot.util.escape:1 +msgid "" +"Replaces the following chars in `text` ('&' with '&', '<' with '<'" +" and '>' with '>')." +msgstr "" + +#: of telebot.util.escape:3 +msgid "the text to escape" +msgstr "" + +#: of telebot.util.escape:4 +msgid "the escaped text" +msgstr "" + +#: of telebot.util.extract_arguments:1 +msgid "Returns the argument after the command." +msgstr "" + +#: of telebot.util.extract_arguments:3 telebot.util.extract_command:4 +msgid "Examples:" +msgstr "" + +#: of telebot.util.extract_arguments:10 +msgid "String to extract the arguments from a command" +msgstr "" + +#: of telebot.util.extract_arguments:13 +msgid "the arguments if `text` is a command (according to is_command), else None." +msgstr "" + +#: of telebot.util.extract_arguments telebot.util.extract_command +#: telebot.util.generate_random_token telebot.util.is_bytes +#: telebot.util.is_command telebot.util.is_dict telebot.util.is_pil_image +#: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string +#: telebot.util.user_link +msgid "Return type" +msgstr "" + +#: of telebot.util.extract_arguments:14 telebot.util.extract_command:16 +msgid ":obj:`str` or :obj:`None`" +msgstr "" + +#: of telebot.util.extract_command:1 +msgid "" +"Extracts the command from `text` (minus the '/') if `text` is a command " +"(see is_command). If `text` is not a command, this function returns None." +msgstr "" + +#: of telebot.util.extract_command:12 +msgid "String to extract the command from" +msgstr "" + +#: of telebot.util.extract_command:15 +msgid "the command if `text` is a command (according to is_command), else None." +msgstr "" + +#: of telebot.util.generate_random_token:1 +msgid "" +"Generates a random token consisting of letters and digits, 16 characters " +"long." +msgstr "" + +#: of telebot.util.generate_random_token:3 +msgid "a random token" +msgstr "" + +#: of telebot.util.generate_random_token:4 telebot.util.user_link:22 +msgid ":obj:`str`" +msgstr "" + +#: of telebot.util.is_bytes:1 +msgid "Returns True if the given object is a bytes object." +msgstr "" + +#: of telebot.util.is_bytes:3 telebot.util.is_dict:3 +#: telebot.util.is_pil_image:3 +msgid "object to be checked" +msgstr "" + +#: of telebot.util.is_bytes:6 +msgid "True if the given object is a bytes object." +msgstr "" + +#: of telebot.util.is_bytes:7 telebot.util.is_command:7 telebot.util.is_dict:7 +#: telebot.util.is_pil_image:7 +msgid ":obj:`bool`" +msgstr "" + +#: of telebot.util.is_command:1 +msgid "" +"Checks if `text` is a command. Telegram chat commands start with the '/' " +"character." +msgstr "" + +#: of telebot.util.is_command:3 +msgid "Text to check." +msgstr "" + +#: of telebot.util.is_command:6 +msgid "True if `text` is a command, else False." +msgstr "" + +#: of telebot.util.is_dict:1 +msgid "Returns True if the given object is a dictionary." +msgstr "" + +#: of telebot.util.is_dict:6 +msgid "True if the given object is a dictionary." +msgstr "" + +#: of telebot.util.is_pil_image:1 +msgid "Returns True if the given object is a PIL.Image.Image object." +msgstr "" + +#: of telebot.util.is_pil_image:6 +msgid "True if the given object is a PIL.Image.Image object." +msgstr "" + +#: of telebot.util.is_string:1 +msgid "Returns True if the given object is a string." +msgstr "" + +#: of telebot.util.parse_web_app_data:1 +msgid "Parses web app data." +msgstr "" + +#: of telebot.util.parse_web_app_data:3 telebot.util.validate_web_app_data:3 +msgid "The bot token" +msgstr "" + +#: of telebot.util.parse_web_app_data:6 telebot.util.validate_web_app_data:6 +msgid "The raw init data" +msgstr "" + +#: of telebot.util.parse_web_app_data:9 telebot.util.validate_web_app_data:9 +msgid "The parsed init data" +msgstr "" + +#: of telebot.util.quick_markup:1 +msgid "" +"Returns a reply markup from a dict in this format: {'text': kwargs} This " +"is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2" +" = InlineKeyboardButton(...)'" +msgstr "" + +#: of telebot.util.quick_markup:4 telebot.util.user_link:5 +msgid "Example:" +msgstr "" + +#: of telebot.util.quick_markup:6 +msgid "Using quick_markup:" +msgstr "" + +#: of telebot.util.quick_markup:29 +msgid "" +"a dict containing all buttons to create in this format: {text: kwargs} " +"{str:}" +msgstr "" + +#: of telebot.util.quick_markup:32 +msgid "int row width" +msgstr "" + +#: of telebot.util.quick_markup:35 +msgid "InlineKeyboardMarkup" +msgstr "" + +#: of telebot.util.quick_markup:36 +msgid ":obj:`types.InlineKeyboardMarkup`" +msgstr "" + +#: of telebot.util.smart_split:1 +msgid "" +"Splits one string into multiple strings, with a maximum amount of " +"`chars_per_string` characters per string. This is very useful for " +"splitting one giant message into multiples. If `chars_per_string` > 4096:" +" `chars_per_string` = 4096. Splits by '\\n', '. ' or ' ' in exactly this " +"priority." +msgstr "" + +#: of telebot.util.smart_split:6 telebot.util.split_string:4 +msgid "The text to split" +msgstr "" + +#: of telebot.util.smart_split:9 +msgid "The number of maximum characters per part the text is split to." +msgstr "" + +#: of telebot.util.smart_split:12 telebot.util.split_string:10 +msgid "The splitted text as a list of strings." +msgstr "" + +#: of telebot.util.smart_split:13 telebot.util.split_string:11 +msgid ":obj:`list` of :obj:`str`" +msgstr "" + +#: of telebot.util.split_string:1 +msgid "" +"Splits one string into multiple strings, with a maximum amount of " +"`chars_per_string` characters per string. This is very useful for " +"splitting one giant message into multiples." +msgstr "" + +#: of telebot.util.split_string:7 +msgid "The number of characters per line the text is split into." +msgstr "" + +#: ../../docstring of telebot.util.update_types:1 +msgid "All update types, should be used for allowed_updates parameter in polling." +msgstr "" + +#: of telebot.util.user_link:1 +msgid "" +"Returns an HTML user link. This is useful for reports. Attention: Don't " +"forget to set parse_mode to 'HTML'!" +msgstr "" + +#: of telebot.util.user_link:11 +msgid "" +"You can use formatting.* for all other formatting options(bold, italic, " +"links, and etc.) This method is kept for backward compatibility, and it " +"is recommended to use formatting.* for more options." +msgstr "" + +#: of telebot.util.user_link:15 +msgid "the user (not the user_id)" +msgstr "" + +#: of telebot.util.user_link:18 +msgid "include the user_id" +msgstr "" + +#: of telebot.util.user_link:21 +msgid "HTML user link" +msgstr "" + +#: of telebot.util.validate_web_app_data:1 +msgid "Validates web app data." +msgstr "" + +#: of telebot.util.webhook_google_functions:1 +msgid "A webhook endpoint for Google Cloud Functions FaaS." +msgstr "" + +#: of telebot.util.webhook_google_functions:3 +msgid "The bot instance" +msgstr "" + +#: of telebot.util.webhook_google_functions:6 +msgid "The request object" +msgstr "" + +#: of telebot.util.webhook_google_functions:9 +msgid "The response object" +msgstr "" + From 736c03fe84990bab4621ae10242cfbda7564889b Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 29 Nov 2022 14:49:32 +0400 Subject: [PATCH 1175/1808] Update conf.py --- docs/source/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index d55b51a6f..03c9239a2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -68,3 +68,5 @@ "light_logo": "logo.png", "dark_logo": "logo2.png", } + +locale_dirs = ["locales"] From d7e9d3accc641442cd02f752a0a2bf7c8bc22b6b Mon Sep 17 00:00:00 2001 From: coder2020official Date: Tue, 29 Nov 2022 14:59:17 +0400 Subject: [PATCH 1176/1808] Update conf.py --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 03c9239a2..360e2a4e2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -69,4 +69,4 @@ "dark_logo": "logo2.png", } -locale_dirs = ["locales"] +locale_dirs = ["locales/"] From 06a28380d71fc76e35e56a87726a712ab5d74c1a Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 29 Nov 2022 17:33:42 +0400 Subject: [PATCH 1177/1808] Update doc_req.txt --- doc_req.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_req.txt b/doc_req.txt index ff01a213f..95555d8fd 100644 --- a/doc_req.txt +++ b/doc_req.txt @@ -2,4 +2,4 @@ furo sphinx_copybutton -git+https://github.com/eternnoir/pyTelegramBotAPI.git +git+https://github.com/coder2020official/pyTelegramBotAPI.git From d636cdf88b06eaa293786bd917c0bfb5450ea068 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 29 Nov 2022 17:37:16 +0400 Subject: [PATCH 1178/1808] Update doc_req.txt --- doc_req.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_req.txt b/doc_req.txt index 95555d8fd..ff01a213f 100644 --- a/doc_req.txt +++ b/doc_req.txt @@ -2,4 +2,4 @@ furo sphinx_copybutton -git+https://github.com/coder2020official/pyTelegramBotAPI.git +git+https://github.com/eternnoir/pyTelegramBotAPI.git From cce03dab78122e48465d3f11ca6078b30e5973d9 Mon Sep 17 00:00:00 2001 From: abdullaev388 <78722918+abdullaev388@users.noreply.github.com> Date: Tue, 29 Nov 2022 09:21:24 -0500 Subject: [PATCH 1179/1808] Update quick_start.po translated into russian --- docs/source/locales/ru/LC_MESSAGES/quick_start.po | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/quick_start.po b/docs/source/locales/ru/LC_MESSAGES/quick_start.po index 9e7c22fbb..cca355561 100644 --- a/docs/source/locales/ru/LC_MESSAGES/quick_start.po +++ b/docs/source/locales/ru/LC_MESSAGES/quick_start.po @@ -20,21 +20,21 @@ msgstr "" #: ../../quick_start.rst:4 msgid "Quick start" -msgstr "" +msgstr "Быстрый старт" #: ../../quick_start.rst:6 msgid "Quickstart guide" -msgstr "" +msgstr "Быстрый старт - гайд" #: ../../quick_start.rst:6 msgid "ptba, pytba, pyTelegramBotAPI, quickstart, guide" -msgstr "" +msgstr "ptba, pytba, pyTelegramBotAPI, быстрый старт, гайд" #: ../../quick_start.rst:11 msgid "Synchronous TeleBot" -msgstr "" +msgstr "Синхронный телебот" #: ../../quick_start.rst:16 msgid "Asynchronous TeleBot" -msgstr "" +msgstr "Асинхронный телебот" From 29befa4d0c54185013444c385a34e3ee166dcb54 Mon Sep 17 00:00:00 2001 From: abdullaev388 <78722918+abdullaev388@users.noreply.github.com> Date: Tue, 29 Nov 2022 09:25:10 -0500 Subject: [PATCH 1180/1808] Update install.po translated into russian --- docs/source/locales/ru/LC_MESSAGES/install.po | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/install.po b/docs/source/locales/ru/LC_MESSAGES/install.po index 0f4a3b386..21f07e311 100644 --- a/docs/source/locales/ru/LC_MESSAGES/install.po +++ b/docs/source/locales/ru/LC_MESSAGES/install.po @@ -20,39 +20,40 @@ msgstr "" #: ../../install.rst:3 msgid "Installation Guide" -msgstr "" +msgstr "Гайд по установке" #: ../../install.rst:5 msgid "Installation of pyTelegramBotAPI" -msgstr "" +msgstr "Установка pyTelegramBotAPI" #: ../../install.rst:5 msgid "ptba, pytba, pyTelegramBotAPI, installation, guide" -msgstr "" +msgstr "ptba, pytba, pyTelegramBotAPI, установка, гайд" #: ../../install.rst:11 msgid "Using PIP" -msgstr "" +msgstr "Используя PIP" #: ../../install.rst:17 msgid "Using pipenv" -msgstr "" +msgstr "Используя pipend" #: ../../install.rst:23 msgid "By cloning repository" -msgstr "" +msgstr "Клонируя репозиторий" #: ../../install.rst:31 msgid "Directly using pip" -msgstr "" +msgstr "Напрямую используя pip" #: ../../install.rst:37 msgid "It is generally recommended to use the first option." -msgstr "" +msgstr "Рекомендуется использовать первый варант." #: ../../install.rst:39 msgid "" "While the API is production-ready, it is still under development and it " "has regular updates, do not forget to update it regularly by calling:" -msgstr "" +msgstr "В то время как API готова к продакшену, библиотека все еще под разработкой" +"и имеет регулярные обновления, не забывайте обновляться вызывая:" From 15f6bbeacb05049581466dc1c590e0f3677b87a6 Mon Sep 17 00:00:00 2001 From: abdullaev388 <78722918+abdullaev388@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:09:48 -0500 Subject: [PATCH 1181/1808] Update docs/source/locales/ru/LC_MESSAGES/install.po Co-authored-by: _run --- docs/source/locales/ru/LC_MESSAGES/install.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/install.po b/docs/source/locales/ru/LC_MESSAGES/install.po index 21f07e311..469ca5bda 100644 --- a/docs/source/locales/ru/LC_MESSAGES/install.po +++ b/docs/source/locales/ru/LC_MESSAGES/install.po @@ -36,7 +36,7 @@ msgstr "Используя PIP" #: ../../install.rst:17 msgid "Using pipenv" -msgstr "Используя pipend" +msgstr "Используя pipenv" #: ../../install.rst:23 msgid "By cloning repository" From cc87dbce500481cb545648738809dea8562e5b65 Mon Sep 17 00:00:00 2001 From: abdullaev388 <78722918+abdullaev388@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:11:21 -0500 Subject: [PATCH 1182/1808] Update docs/source/locales/ru/LC_MESSAGES/install.po Co-authored-by: _run --- docs/source/locales/ru/LC_MESSAGES/install.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/install.po b/docs/source/locales/ru/LC_MESSAGES/install.po index 469ca5bda..e08179e19 100644 --- a/docs/source/locales/ru/LC_MESSAGES/install.po +++ b/docs/source/locales/ru/LC_MESSAGES/install.po @@ -54,6 +54,6 @@ msgstr "Рекомендуется использовать первый вар msgid "" "While the API is production-ready, it is still under development and it " "has regular updates, do not forget to update it regularly by calling:" -msgstr "В то время как API готова к продакшену, библиотека все еще под разработкой" +msgstr "Новые версии библиотеки имеют больше фич, улучшений и баг фиксов. Не забывайте" "и имеет регулярные обновления, не забывайте обновляться вызывая:" From 848a2cc7ec631b350a23a5b3b3bd328c12eda31e Mon Sep 17 00:00:00 2001 From: abdullaev388 <78722918+abdullaev388@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:11:27 -0500 Subject: [PATCH 1183/1808] Update docs/source/locales/ru/LC_MESSAGES/install.po Co-authored-by: _run --- docs/source/locales/ru/LC_MESSAGES/install.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/install.po b/docs/source/locales/ru/LC_MESSAGES/install.po index e08179e19..db5ab6c4e 100644 --- a/docs/source/locales/ru/LC_MESSAGES/install.po +++ b/docs/source/locales/ru/LC_MESSAGES/install.po @@ -55,5 +55,5 @@ msgid "" "While the API is production-ready, it is still under development and it " "has regular updates, do not forget to update it regularly by calling:" msgstr "Новые версии библиотеки имеют больше фич, улучшений и баг фиксов. Не забывайте" -"и имеет регулярные обновления, не забывайте обновляться вызывая:" +"обновляться вызывая:" From 44309797d145e4ad4289c269a0a4e1469f8ea7ad Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 29 Nov 2022 19:21:05 +0400 Subject: [PATCH 1184/1808] Update install.po --- docs/source/locales/ru/LC_MESSAGES/install.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/install.po b/docs/source/locales/ru/LC_MESSAGES/install.po index db5ab6c4e..cbe2dfba5 100644 --- a/docs/source/locales/ru/LC_MESSAGES/install.po +++ b/docs/source/locales/ru/LC_MESSAGES/install.po @@ -48,12 +48,12 @@ msgstr "Напрямую используя pip" #: ../../install.rst:37 msgid "It is generally recommended to use the first option." -msgstr "Рекомендуется использовать первый варант." +msgstr "Рекомендуется использовать первый вариант." #: ../../install.rst:39 msgid "" "While the API is production-ready, it is still under development and it " "has regular updates, do not forget to update it regularly by calling:" msgstr "Новые версии библиотеки имеют больше фич, улучшений и баг фиксов. Не забывайте" -"обновляться вызывая:" +" обновляться вызывая:" From 1c2111d6899c9aac9179bbf5900381d1bcf9de79 Mon Sep 17 00:00:00 2001 From: Cub1tor Date: Tue, 29 Nov 2022 20:19:25 +0200 Subject: [PATCH 1185/1808] Update formatting.po --- .../locales/ru/LC_MESSAGES/formatting.po | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/formatting.po b/docs/source/locales/ru/LC_MESSAGES/formatting.po index 0f6eff001..33ed2e687 100644 --- a/docs/source/locales/ru/LC_MESSAGES/formatting.po +++ b/docs/source/locales/ru/LC_MESSAGES/formatting.po @@ -20,23 +20,23 @@ msgstr "" #: ../../formatting.rst:3 msgid "Formatting options" -msgstr "" +msgstr "Параметры форматирования" #: ../../formatting.rst:5 msgid "Formatting options in pyTelegramBotAPI" -msgstr "" +msgstr "Параметры форматирования в pyTelegramBotAPI" #: ../../formatting.rst:5 msgid "html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI" -msgstr "" +msgstr "html, markdown, parse_mode, форматирование, ptba, pytba, pyTelergamBotAPI" #: of telebot.formatting:1 msgid "Markdown & HTML formatting functions." -msgstr "" +msgstr "Функции форматирования Markdown & HTML." #: of telebot.formatting.escape_html:1 msgid "Escapes HTML characters in a string of HTML." -msgstr "" +msgstr "Экранирует HTML символы в HTML строке." #: of telebot.formatting.escape_html telebot.formatting.escape_markdown #: telebot.formatting.format_text telebot.formatting.hbold @@ -52,7 +52,7 @@ msgstr "" #: of telebot.formatting.escape_html:3 msgid "The string of HTML to escape." -msgstr "" +msgstr "HTML строка для экранирования." #: of telebot.formatting.escape_html telebot.formatting.escape_markdown #: telebot.formatting.format_text telebot.formatting.hbold @@ -68,7 +68,7 @@ msgstr "" #: of telebot.formatting.escape_html:6 telebot.formatting.escape_markdown:8 msgid "The escaped string." -msgstr "" +msgstr "Экранированная строка." #: of telebot.formatting.escape_html telebot.formatting.escape_markdown #: telebot.formatting.format_text telebot.formatting.hbold @@ -97,7 +97,7 @@ msgstr "" #: of telebot.formatting.escape_markdown:1 msgid "Escapes Markdown characters in a string of Markdown." -msgstr "" +msgstr "Экранирует Markdown символы в Markdown строке." #: of telebot.formatting.escape_markdown:3 msgid "Credits to: simonsmh" @@ -105,19 +105,19 @@ msgstr "" #: of telebot.formatting.escape_markdown:5 msgid "The string of Markdown to escape." -msgstr "" +msgstr "Markdown строка для экранирования." #: of telebot.formatting.format_text:1 msgid "Formats a list of strings into a single string." -msgstr "" +msgstr "Преобразовывает набор строк в одну." #: of telebot.formatting.format_text:10 msgid "Strings to format." -msgstr "" +msgstr "Строки для преобразования." #: of telebot.formatting.format_text:13 msgid "The separator to use between each string." -msgstr "" +msgstr "Символ для разделения строк." #: of telebot.formatting.format_text:16 telebot.formatting.hbold:9 #: telebot.formatting.hcode:9 telebot.formatting.hitalic:9 @@ -128,15 +128,15 @@ msgstr "" #: telebot.formatting.mlink:12 telebot.formatting.mspoiler:9 #: telebot.formatting.mstrikethrough:9 telebot.formatting.munderline:9 msgid "The formatted string." -msgstr "" +msgstr "Преобразованная строка." #: of telebot.formatting.hbold:1 msgid "Returns an HTML-formatted bold string." -msgstr "" +msgstr "Возвращает выделенную жирным шрифтом HTML строку." #: of telebot.formatting.hbold:3 telebot.formatting.mbold:3 msgid "The string to bold." -msgstr "" +msgstr "Строка для выделения жирным шрифтом." #: of telebot.formatting.hbold:6 telebot.formatting.hcode:6 #: telebot.formatting.hitalic:6 telebot.formatting.hlink:9 @@ -147,105 +147,105 @@ msgstr "" #: telebot.formatting.mspoiler:6 telebot.formatting.mstrikethrough:6 #: telebot.formatting.munderline:6 msgid "True if you need to escape special characters. Defaults to True." -msgstr "" +msgstr "True если вам нужно экранировать спец. символы. По умолчанию True." #: of telebot.formatting.hcode:1 msgid "Returns an HTML-formatted code string." -msgstr "" +msgstr "Возвращает выделенную как код HTML строку." #: of telebot.formatting.hcode:3 telebot.formatting.mcode:3 msgid "The string to code." -msgstr "" +msgstr "Строка для выделения как код." #: of telebot.formatting.hide_link:1 msgid "Hide url of an image." -msgstr "" +msgstr "Делает невидимым URL изображения." #: of telebot.formatting.hide_link:3 msgid "The url of the image." -msgstr "" +msgstr "URL изображения." #: of telebot.formatting.hide_link:6 msgid "The hidden url." -msgstr "" +msgstr "Невидимый URL." #: of telebot.formatting.hitalic:1 msgid "Returns an HTML-formatted italic string." -msgstr "" +msgstr "Возвращает выделенную курсивом HTML строку." #: of telebot.formatting.hitalic:3 telebot.formatting.mitalic:3 msgid "The string to italicize." -msgstr "" +msgstr "Строка для выделения курсивом." #: of telebot.formatting.hlink:1 msgid "Returns an HTML-formatted link string." -msgstr "" +msgstr "Возвращает HTML строку с гиперссылкой." #: of telebot.formatting.hlink:3 telebot.formatting.mlink:3 msgid "The string to link." -msgstr "" +msgstr "Строка для добавления гиперссылки." #: of telebot.formatting.hlink:6 telebot.formatting.mlink:6 msgid "The URL to link to." -msgstr "" +msgstr "URL для создания гиперссылки." #: of telebot.formatting.hpre:1 msgid "Returns an HTML-formatted preformatted string." -msgstr "" +msgstr "Возвращает предварительно отформатированную HTML строку." #: of telebot.formatting.hpre:3 msgid "The string to preformatted." -msgstr "" +msgstr "Строка для предварительного форматирования." #: of telebot.formatting.hspoiler:1 msgid "Returns an HTML-formatted spoiler string." -msgstr "" +msgstr "Возвращает выделенную как спойлер HTML строку." #: of telebot.formatting.hspoiler:3 telebot.formatting.mspoiler:3 msgid "The string to spoiler." -msgstr "" +msgstr "Строка для выделения как спойлер." #: of telebot.formatting.hstrikethrough:1 msgid "Returns an HTML-formatted strikethrough string." -msgstr "" +msgstr "Возвращает зачеркнутую HTML строку." #: of telebot.formatting.hstrikethrough:3 telebot.formatting.mstrikethrough:3 msgid "The string to strikethrough." -msgstr "" +msgstr "Строка для зачеркивания." #: of telebot.formatting.hunderline:1 msgid "Returns an HTML-formatted underline string." -msgstr "" +msgstr "Возвращает подчеркнутую HTML строку." #: of telebot.formatting.hunderline:3 telebot.formatting.munderline:3 msgid "The string to underline." -msgstr "" +msgstr "Строка для подчёркивания." #: of telebot.formatting.mbold:1 msgid "Returns a Markdown-formatted bold string." -msgstr "" +msgstr "Возвращает выделенную жирным шрифтом Markdown строку." #: of telebot.formatting.mcode:1 msgid "Returns a Markdown-formatted code string." -msgstr "" +msgstr "Возвращает выделенную как код Markdown строку." #: of telebot.formatting.mitalic:1 msgid "Returns a Markdown-formatted italic string." -msgstr "" +msgstr "Возвращает выделенную курсивом Markdown строку." #: of telebot.formatting.mlink:1 msgid "Returns a Markdown-formatted link string." -msgstr "" +msgstr "Возвращает Markdown строку с гиперссылкой." #: of telebot.formatting.mspoiler:1 msgid "Returns a Markdown-formatted spoiler string." -msgstr "" +msgstr "Возвращает выделенную как спойлер Markdown строку." #: of telebot.formatting.mstrikethrough:1 msgid "Returns a Markdown-formatted strikethrough string." -msgstr "" +msgstr "Возвращает зачеркнутую Markdown строку." #: of telebot.formatting.munderline:1 msgid "Returns a Markdown-formatted underline string." -msgstr "" +msgstr "Возвращает подчеркнутую Markdown строку." From 91ff06eebae30a5b8c695cf247dfc5dc040a8171 Mon Sep 17 00:00:00 2001 From: Konstantin Ostashenko Date: Wed, 30 Nov 2022 16:49:46 +0200 Subject: [PATCH 1186/1808] Update docs/source/locales/ru/LC_MESSAGES/formatting.po Co-authored-by: _run --- docs/source/locales/ru/LC_MESSAGES/formatting.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/formatting.po b/docs/source/locales/ru/LC_MESSAGES/formatting.po index 33ed2e687..c2933ab21 100644 --- a/docs/source/locales/ru/LC_MESSAGES/formatting.po +++ b/docs/source/locales/ru/LC_MESSAGES/formatting.po @@ -28,7 +28,7 @@ msgstr "Параметры форматирования в pyTelegramBotAPI" #: ../../formatting.rst:5 msgid "html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI" -msgstr "html, markdown, parse_mode, форматирование, ptba, pytba, pyTelergamBotAPI" +msgstr "html, markdown, parse_mode, форматирование, ptba, pytba, pyTelegramBotAPI" #: of telebot.formatting:1 msgid "Markdown & HTML formatting functions." From 6cf60a3dcb8583842e5a59e295295600b02e0b80 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Wed, 30 Nov 2022 17:33:06 +0200 Subject: [PATCH 1187/1808] Update formatting.po according to comments --- docs/source/locales/ru/LC_MESSAGES/formatting.po | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/formatting.po b/docs/source/locales/ru/LC_MESSAGES/formatting.po index 33ed2e687..c4b48960f 100644 --- a/docs/source/locales/ru/LC_MESSAGES/formatting.po +++ b/docs/source/locales/ru/LC_MESSAGES/formatting.po @@ -28,7 +28,7 @@ msgstr "Параметры форматирования в pyTelegramBotAPI" #: ../../formatting.rst:5 msgid "html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI" -msgstr "html, markdown, parse_mode, форматирование, ptba, pytba, pyTelergamBotAPI" +msgstr "html, markdown, parse_mode, форматирование, ptba, pytba, pyTelegramBotAPI" #: of telebot.formatting:1 msgid "Markdown & HTML formatting functions." @@ -36,7 +36,7 @@ msgstr "Функции форматирования Markdown & HTML." #: of telebot.formatting.escape_html:1 msgid "Escapes HTML characters in a string of HTML." -msgstr "Экранирует HTML символы в HTML строке." +msgstr "Пропускает HTML символы в HTML строке." #: of telebot.formatting.escape_html telebot.formatting.escape_markdown #: telebot.formatting.format_text telebot.formatting.hbold @@ -52,7 +52,7 @@ msgstr "" #: of telebot.formatting.escape_html:3 msgid "The string of HTML to escape." -msgstr "HTML строка для экранирования." +msgstr "HTML строка, которую нужно пропустить." #: of telebot.formatting.escape_html telebot.formatting.escape_markdown #: telebot.formatting.format_text telebot.formatting.hbold @@ -68,7 +68,7 @@ msgstr "" #: of telebot.formatting.escape_html:6 telebot.formatting.escape_markdown:8 msgid "The escaped string." -msgstr "Экранированная строка." +msgstr "Пропускаемая строка." #: of telebot.formatting.escape_html telebot.formatting.escape_markdown #: telebot.formatting.format_text telebot.formatting.hbold @@ -97,7 +97,7 @@ msgstr "" #: of telebot.formatting.escape_markdown:1 msgid "Escapes Markdown characters in a string of Markdown." -msgstr "Экранирует Markdown символы в Markdown строке." +msgstr "Пропускает Markdown символы в Markdown строке." #: of telebot.formatting.escape_markdown:3 msgid "Credits to: simonsmh" @@ -105,7 +105,7 @@ msgstr "" #: of telebot.formatting.escape_markdown:5 msgid "The string of Markdown to escape." -msgstr "Markdown строка для экранирования." +msgstr "Markdown строка, которую нужно пропустить." #: of telebot.formatting.format_text:1 msgid "Formats a list of strings into a single string." @@ -147,7 +147,7 @@ msgstr "Строка для выделения жирным шрифтом." #: telebot.formatting.mspoiler:6 telebot.formatting.mstrikethrough:6 #: telebot.formatting.munderline:6 msgid "True if you need to escape special characters. Defaults to True." -msgstr "True если вам нужно экранировать спец. символы. По умолчанию True." +msgstr "True если вам нужно пропустить спец. символы. По умолчанию True." #: of telebot.formatting.hcode:1 msgid "Returns an HTML-formatted code string." From e358abc1bd90ca8e0f632891286e5b37360f9d2f Mon Sep 17 00:00:00 2001 From: Cub11k Date: Wed, 30 Nov 2022 21:07:46 +0200 Subject: [PATCH 1188/1808] Update util.po --- docs/source/locales/ru/LC_MESSAGES/util.po | 127 ++++++++++++--------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/util.po b/docs/source/locales/ru/LC_MESSAGES/util.po index afba83905..9f74a957f 100644 --- a/docs/source/locales/ru/LC_MESSAGES/util.po +++ b/docs/source/locales/ru/LC_MESSAGES/util.po @@ -20,25 +20,25 @@ msgstr "" #: ../../util.rst:3 msgid "Utils" -msgstr "" +msgstr "Утилиты" #: ../../util.rst:5 msgid "Utils in pyTelegramBotAPI" -msgstr "" +msgstr "Утилиты в pyTelegramBotAPI" #: ../../util.rst:5 msgid "ptba, pytba, pyTelegramBotAPI, utils, guide" -msgstr "" +msgstr "ptba, pytba, pyTelegramBotAPI, утилиты, гайд" #: ../../util.rst:11 msgid "util file" -msgstr "" +msgstr "Файл util" #: of telebot.util.antiflood:1 msgid "" "Use this function inside loops in order to avoid getting TooManyRequests " "error. Example:" -msgstr "" +msgstr "Используйте эту функцию в циклах, чтобы избежать ошибки TooManyRequests. Пример:" #: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments #: telebot.util.extract_command telebot.util.is_bytes telebot.util.is_command @@ -51,15 +51,15 @@ msgstr "" #: of telebot.util.antiflood:10 msgid "The function to call" -msgstr "" +msgstr "Вызываемая функция" #: of telebot.util.antiflood:13 msgid "The arguments to pass to the function" -msgstr "" +msgstr "Аргументы, для передачи в function" #: of telebot.util.antiflood:16 msgid "The keyword arguments to pass to the function" -msgstr "" +msgstr "Именованные аргументы для передачи в function" #: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments #: telebot.util.extract_command telebot.util.generate_random_token @@ -77,45 +77,46 @@ msgstr "" #: of telebot.util.chunks:1 msgid "Yield successive n-sized chunks from lst." -msgstr "" +msgstr "Генерирует последовательные части списка, состоящие из n элементов." #: ../../docstring of telebot.util.content_type_media:1 msgid "Contains all media content types." -msgstr "" +msgstr "Содержит все виды медиа." #: ../../docstring of telebot.util.content_type_service:1 msgid "Contains all service content types such as `User joined the group`." -msgstr "" +msgstr "Содержит все виды сервисных сообщений, такие как `User joined the group`." #: of telebot.util.escape:1 msgid "" "Replaces the following chars in `text` ('&' with '&', '<' with '<'" " and '>' with '>')." -msgstr "" +msgstr "Заменяет следующие символы в `text` ('&' на '&', '<' на '<'" +" и '>' на '>')." #: of telebot.util.escape:3 msgid "the text to escape" -msgstr "" +msgstr "Текст для замены символов" #: of telebot.util.escape:4 msgid "the escaped text" -msgstr "" +msgstr "Отформатированный текст" #: of telebot.util.extract_arguments:1 msgid "Returns the argument after the command." -msgstr "" +msgstr "Возвращает аргументы команды." #: of telebot.util.extract_arguments:3 telebot.util.extract_command:4 msgid "Examples:" -msgstr "" +msgstr "Примеры:" #: of telebot.util.extract_arguments:10 msgid "String to extract the arguments from a command" -msgstr "" +msgstr "Строка для извлечения аргументов команды" #: of telebot.util.extract_arguments:13 msgid "the arguments if `text` is a command (according to is_command), else None." -msgstr "" +msgstr "Аргументы, если `text` является командой (согласно is_command), в остальных случаях None." #: of telebot.util.extract_arguments telebot.util.extract_command #: telebot.util.generate_random_token telebot.util.is_bytes @@ -127,31 +128,33 @@ msgstr "" #: of telebot.util.extract_arguments:14 telebot.util.extract_command:16 msgid ":obj:`str` or :obj:`None`" -msgstr "" +msgstr ":obj:`str` или :obj:`None`" #: of telebot.util.extract_command:1 msgid "" "Extracts the command from `text` (minus the '/') if `text` is a command " "(see is_command). If `text` is not a command, this function returns None." msgstr "" +"Извлекает команду из `text` (исключает '/') если `text` является командой " +"(см. is_command). Если `text` не является командой, эта функция возвращает None." #: of telebot.util.extract_command:12 msgid "String to extract the command from" -msgstr "" +msgstr "Строка, из которой нужно извлечь команду" #: of telebot.util.extract_command:15 msgid "the command if `text` is a command (according to is_command), else None." -msgstr "" +msgstr "Команда, если `text` является командой (согласно is_command), в остальных случаях None." #: of telebot.util.generate_random_token:1 msgid "" "Generates a random token consisting of letters and digits, 16 characters " "long." -msgstr "" +msgstr "Генерирует рандомный токен, состоящий из латинских букв и цифр длиной 16 символов." #: of telebot.util.generate_random_token:3 msgid "a random token" -msgstr "" +msgstr "Сгенерированный токен" #: of telebot.util.generate_random_token:4 telebot.util.user_link:22 msgid ":obj:`str`" @@ -159,16 +162,16 @@ msgstr "" #: of telebot.util.is_bytes:1 msgid "Returns True if the given object is a bytes object." -msgstr "" +msgstr "Возвращает True если полученный объект является bytes." #: of telebot.util.is_bytes:3 telebot.util.is_dict:3 #: telebot.util.is_pil_image:3 msgid "object to be checked" -msgstr "" +msgstr "Объект для проверки" #: of telebot.util.is_bytes:6 msgid "True if the given object is a bytes object." -msgstr "" +msgstr "True, если полученный объект является bytes." #: of telebot.util.is_bytes:7 telebot.util.is_command:7 telebot.util.is_dict:7 #: telebot.util.is_pil_image:7 @@ -180,50 +183,52 @@ msgid "" "Checks if `text` is a command. Telegram chat commands start with the '/' " "character." msgstr "" +"Проверяет, является ли `text` командой. Команды в Telegram начинаются " +"с символа '/'." #: of telebot.util.is_command:3 msgid "Text to check." -msgstr "" +msgstr "Текст для проверки." #: of telebot.util.is_command:6 msgid "True if `text` is a command, else False." -msgstr "" +msgstr "True, если `text` является командой, иначе False." #: of telebot.util.is_dict:1 msgid "Returns True if the given object is a dictionary." -msgstr "" +msgstr "Возвращает True, если полученный объект является словарём (dict)." #: of telebot.util.is_dict:6 msgid "True if the given object is a dictionary." -msgstr "" +msgstr "True, если полученный объект является словарём (dict)." #: of telebot.util.is_pil_image:1 msgid "Returns True if the given object is a PIL.Image.Image object." -msgstr "" +msgstr "Возвращает True, если полученный объект является PIL.Image.Image." #: of telebot.util.is_pil_image:6 msgid "True if the given object is a PIL.Image.Image object." -msgstr "" +msgstr "True, если полученный объект является PIL.Image.Image." #: of telebot.util.is_string:1 msgid "Returns True if the given object is a string." -msgstr "" +msgstr "Возвращает True, если полученный объект является строкой (str)." #: of telebot.util.parse_web_app_data:1 msgid "Parses web app data." -msgstr "" +msgstr "Обрабатывает данные, полученные от web app." #: of telebot.util.parse_web_app_data:3 telebot.util.validate_web_app_data:3 msgid "The bot token" -msgstr "" +msgstr "Токен бота" #: of telebot.util.parse_web_app_data:6 telebot.util.validate_web_app_data:6 msgid "The raw init data" -msgstr "" +msgstr "Необработанные данные" #: of telebot.util.parse_web_app_data:9 telebot.util.validate_web_app_data:9 msgid "The parsed init data" -msgstr "" +msgstr "Обработанные данные" #: of telebot.util.quick_markup:1 msgid "" @@ -231,24 +236,29 @@ msgid "" "is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2" " = InlineKeyboardButton(...)'" msgstr "" +"Возвращает reply markup из словаря следующего формата: {'text': kwargs}. Удобно " +"использовать вместо постоянного использования 'btn1 = InlineKeyboardButton(...)' 'btn2" +" = InlineKeyboardButton(...)'" #: of telebot.util.quick_markup:4 telebot.util.user_link:5 msgid "Example:" -msgstr "" +msgstr "Пример:" #: of telebot.util.quick_markup:6 msgid "Using quick_markup:" -msgstr "" +msgstr "Используя quick_markup:" #: of telebot.util.quick_markup:29 msgid "" "a dict containing all buttons to create in this format: {text: kwargs} " "{str:}" msgstr "" +"Словарь, содержащий все кнопки для создания reply markup в следующем " +"формате: {text: kwargs} {str:}" #: of telebot.util.quick_markup:32 msgid "int row width" -msgstr "" +msgstr "Количество кнопок в одной строке, int" #: of telebot.util.quick_markup:35 msgid "InlineKeyboardMarkup" @@ -266,18 +276,22 @@ msgid "" " `chars_per_string` = 4096. Splits by '\\n', '. ' or ' ' in exactly this " "priority." msgstr "" +"Разбивает строку на несколько, каждая из которых будет не длиннее `characters_per_string`. " +"Удобно использовать для разбиения одного гигантского сообщения на несколько. " +"Если `chars_per_string` > 4096: `chars_per_string` = 4096. Разбивает строку по '\\n', '. ' или ' ' " +"именно в таком порядке." #: of telebot.util.smart_split:6 telebot.util.split_string:4 msgid "The text to split" -msgstr "" +msgstr "Текст для разбиения" #: of telebot.util.smart_split:9 msgid "The number of maximum characters per part the text is split to." -msgstr "" +msgstr "Максимальное количество символов в части текста, на которые он будет разбит." #: of telebot.util.smart_split:12 telebot.util.split_string:10 msgid "The splitted text as a list of strings." -msgstr "" +msgstr "Список частей разбитого текста." #: of telebot.util.smart_split:13 telebot.util.split_string:11 msgid ":obj:`list` of :obj:`str`" @@ -289,20 +303,24 @@ msgid "" "`chars_per_string` characters per string. This is very useful for " "splitting one giant message into multiples." msgstr "" +"Разбивает одну строку на несколько, каждая из которых будет не длиннее `characters_per_string`. " +"Удобно использовать для разбиения одного гигантского сообщения на несколько." #: of telebot.util.split_string:7 msgid "The number of characters per line the text is split into." -msgstr "" +msgstr "Количество символов в одной строке, на которые будет разбит текст." #: ../../docstring of telebot.util.update_types:1 msgid "All update types, should be used for allowed_updates parameter in polling." -msgstr "" +msgstr "Все виды апдейтов, рекомендуется использовать в качестве параметра allowed_updates функции polling." #: of telebot.util.user_link:1 msgid "" "Returns an HTML user link. This is useful for reports. Attention: Don't " "forget to set parse_mode to 'HTML'!" msgstr "" +"Возвращает HTML ссылку на пользователя. Удобно использовать для отчетов. Важно: Не " +"забудьте установить значение 'HTML' в parse_mode!" #: of telebot.util.user_link:11 msgid "" @@ -310,36 +328,39 @@ msgid "" "links, and etc.) This method is kept for backward compatibility, and it " "is recommended to use formatting.* for more options." msgstr "" +"Вы можете использовать formatting.* во всех остальных вариантах форматирования(bold, italic, " +"links, и прочее). Этот метод сохранён для обратной совместимости, рекомендуется " +"использовать formatting.* для большего количества вариантов." #: of telebot.util.user_link:15 msgid "the user (not the user_id)" -msgstr "" +msgstr "Пользователь (не id пользователя)" #: of telebot.util.user_link:18 msgid "include the user_id" -msgstr "" +msgstr "Добавить id пользователя" #: of telebot.util.user_link:21 msgid "HTML user link" -msgstr "" +msgstr "Ссылка на пользователя в формате HTML" #: of telebot.util.validate_web_app_data:1 msgid "Validates web app data." -msgstr "" +msgstr "Проверяет данные, полученные от web app." #: of telebot.util.webhook_google_functions:1 msgid "A webhook endpoint for Google Cloud Functions FaaS." -msgstr "" +msgstr "Endpoint вебхука для Google Cloud Functions FaaS." #: of telebot.util.webhook_google_functions:3 msgid "The bot instance" -msgstr "" +msgstr "Инстанс бота" #: of telebot.util.webhook_google_functions:6 msgid "The request object" -msgstr "" +msgstr "HTTP-запрос" #: of telebot.util.webhook_google_functions:9 msgid "The response object" -msgstr "" +msgstr "Объект, полученный в качестве ответа" From 8e1c8a2742faf3fbdf69645d658607cbc722187b Mon Sep 17 00:00:00 2001 From: Konstantin Ostashenko Date: Wed, 30 Nov 2022 23:12:25 +0200 Subject: [PATCH 1189/1808] Update docs/source/locales/ru/LC_MESSAGES/util.po Co-authored-by: _run --- docs/source/locales/ru/LC_MESSAGES/util.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/util.po b/docs/source/locales/ru/LC_MESSAGES/util.po index 9f74a957f..28ca8a67d 100644 --- a/docs/source/locales/ru/LC_MESSAGES/util.po +++ b/docs/source/locales/ru/LC_MESSAGES/util.po @@ -55,7 +55,7 @@ msgstr "Вызываемая функция" #: of telebot.util.antiflood:13 msgid "The arguments to pass to the function" -msgstr "Аргументы, для передачи в function" +msgstr "Аргументы, для передачи в функцию" #: of telebot.util.antiflood:16 msgid "The keyword arguments to pass to the function" From e255d9cbabb97f1b6a8b6dbfefd5857d52be92eb Mon Sep 17 00:00:00 2001 From: Konstantin Ostashenko Date: Wed, 30 Nov 2022 23:12:33 +0200 Subject: [PATCH 1190/1808] Update docs/source/locales/ru/LC_MESSAGES/util.po Co-authored-by: _run --- docs/source/locales/ru/LC_MESSAGES/util.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/util.po b/docs/source/locales/ru/LC_MESSAGES/util.po index 28ca8a67d..0f3c4957b 100644 --- a/docs/source/locales/ru/LC_MESSAGES/util.po +++ b/docs/source/locales/ru/LC_MESSAGES/util.po @@ -59,7 +59,7 @@ msgstr "Аргументы, для передачи в функцию" #: of telebot.util.antiflood:16 msgid "The keyword arguments to pass to the function" -msgstr "Именованные аргументы для передачи в function" +msgstr "Именованные аргументы для передачи в функцию" #: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments #: telebot.util.extract_command telebot.util.generate_random_token From cd4dc899a197c475bdc481069547b63080d49186 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 2 Dec 2022 23:46:26 +0300 Subject: [PATCH 1191/1808] Fix caption_entities miss in InputMediaXXX --- telebot/types.py | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f6a03c401..5e843d286 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5765,9 +5765,6 @@ class InputMediaPhoto(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto - :param type: Type of the result, must be photo - :type type: :obj:`str` - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5787,11 +5784,12 @@ class InputMediaPhoto(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaPhoto` """ - def __init__(self, media, caption=None, parse_mode=None): + def __init__(self, media, caption=None, parse_mode=None, caption_entities=None): if util.is_pil_image(media): media = util.pil_image_to_file(media) - super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) + super(InputMediaPhoto, self).__init__( + type="photo", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) def to_dict(self): return super(InputMediaPhoto, self).to_dict() @@ -5803,9 +5801,6 @@ class InputMediaVideo(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo - :param type: Type of the result, must be video - :type type: :obj:`str` - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5844,9 +5839,10 @@ class InputMediaVideo(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaVideo` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None, - supports_streaming=None): - super(InputMediaVideo, self).__init__(type="video", media=media, caption=caption, parse_mode=parse_mode) + def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + width=None, height=None, duration=None, supports_streaming=None): + super(InputMediaVideo, self).__init__( + type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumb = thumb self.width = width self.height = height @@ -5874,9 +5870,6 @@ class InputMediaAnimation(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediaanimation - :param type: Type of the result, must be animation - :type type: :obj:`str` - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5912,8 +5905,10 @@ class InputMediaAnimation(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAnimation` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, width=None, height=None, duration=None): - super(InputMediaAnimation, self).__init__(type="animation", media=media, caption=caption, parse_mode=parse_mode) + def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + width=None, height=None, duration=None): + super(InputMediaAnimation, self).__init__( + type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumb = thumb self.width = width self.height = height @@ -5938,9 +5933,6 @@ class InputMediaAudio(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio - :param type: Type of the result, must be audio - :type type: :obj:`str` - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » @@ -5976,8 +5968,10 @@ class InputMediaAudio(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAudio` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, duration=None, performer=None, title=None): - super(InputMediaAudio, self).__init__(type="audio", media=media, caption=caption, parse_mode=parse_mode) + def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + duration=None, performer=None, title=None): + super(InputMediaAudio, self).__init__( + type="audio", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumb = thumb self.duration = duration self.performer = performer @@ -6002,10 +5996,7 @@ class InputMediaDocument(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediadocument - :param type: Type of the result, must be document - :type type: :obj:`str` - - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` @@ -6035,8 +6026,10 @@ class InputMediaDocument(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaDocument` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, disable_content_type_detection=None): - super(InputMediaDocument, self).__init__(type="document", media=media, caption=caption, parse_mode=parse_mode) + def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + disable_content_type_detection=None): + super(InputMediaDocument, self).__init__( + type="document", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumb = thumb self.disable_content_type_detection = disable_content_type_detection From 43abedbff7cc51961477db37099763cb545b5f0d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 2 Dec 2022 23:59:59 +0300 Subject: [PATCH 1192/1808] Fixed register_next_step_handler_by_chat_id chat_id description --- telebot/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c0a73db6..eebfe3011 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4981,14 +4981,14 @@ def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): self.current_states.set_data(chat_id, user_id, key, value) def register_next_step_handler_by_chat_id( - self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None: + self, chat_id: int, callback: Callable, *args, **kwargs) -> None: """ - Registers a callback function to be notified when new message arrives after `message`. + Registers a callback function to be notified when new message arrives in the given chat. Warning: In case `callback` as lambda function, saving next step handlers will not work. - :param chat_id: The chat for which we want to handle new message. - :type chat_id: :obj:`int` or :obj:`str` + :param chat_id: The chat (chat ID) for which we want to handle new message. + :type chat_id: :obj:`int` :param callback: The callback function which next new message arrives. :type callback: :obj:`Callable[[telebot.types.Message], None]` From 34acae9a59e182059c973ee29e1faa57963ed1d2 Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 13:33:22 +0100 Subject: [PATCH 1193/1808] fixing escape() fixing escape() as replacing a None would throw an exception 'NoneType' object has no attribute 'replace'. useful in case of escaping a None string given from message.from_user.last_name as you dont know wether the user has a last name or not --- telebot/util.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 485d8e6b0..5eee539d6 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -399,8 +399,12 @@ def escape(text: str) -> str: :return: the escaped text """ chars = {"&": "&", "<": "<", ">": ">"} - for old, new in chars.items(): text = text.replace(old, new) - return text + if text == None: + return None + else: + for old, new in chars.items(): + text = text.replace(old, new) + return text def user_link(user: types.User, include_id: bool=False) -> str: From 669c18fdc01fbcbc19315f87275301045631064d Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:11:07 +0100 Subject: [PATCH 1194/1808] update updated "== None" to "is None" and adjusted the else statement --- telebot/util.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 5eee539d6..86528a8b9 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -399,11 +399,10 @@ def escape(text: str) -> str: :return: the escaped text """ chars = {"&": "&", "<": "<", ">": ">"} - if text == None: + if text is None: return None else: - for old, new in chars.items(): - text = text.replace(old, new) + for old, new in chars.items(): text = text.replace(old, new) return text From ae20cb9f315c0af45973ebf064c4d22f33c1b67f Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:21:31 +0100 Subject: [PATCH 1195/1808] Update util.py --- telebot/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 86528a8b9..cc0dae3a1 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -401,8 +401,7 @@ def escape(text: str) -> str: chars = {"&": "&", "<": "<", ">": ">"} if text is None: return None - else: - for old, new in chars.items(): text = text.replace(old, new) + for old, new in chars.items(): text = text.replace(old, new) return text From 4ed460b137685846f75de219f84440fb21200fe6 Mon Sep 17 00:00:00 2001 From: reddere <111050360+reddere@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:23:10 +0100 Subject: [PATCH 1196/1808] Update util.py --- telebot/util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index cc0dae3a1..e52ee83a5 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -401,8 +401,9 @@ def escape(text: str) -> str: chars = {"&": "&", "<": "<", ">": ">"} if text is None: return None - for old, new in chars.items(): text = text.replace(old, new) - return text + for old, new in chars.items(): + text = text.replace(old, new) + return text def user_link(user: types.User, include_id: bool=False) -> str: From 45fe2ea31976e8308cdde8aa180950e0038631eb Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 13 Dec 2022 18:44:41 +0400 Subject: [PATCH 1197/1808] Update reloader.py --- telebot/ext/reloader.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/ext/reloader.py b/telebot/ext/reloader.py index d57453fc5..b6fd49b75 100644 --- a/telebot/ext/reloader.py +++ b/telebot/ext/reloader.py @@ -18,8 +18,14 @@ def restart_file(): p = psutil.Process(os.getpid()) for handler in p.open_files() + p.connections(): os.close(handler.fd) + except OSError: + pass except Exception as e: logger.error(e) python = sys.executable - os.execl(python, python, *sys.argv) + + if os.name == 'nt': + os.execv(sys.executable, ['python'] + sys.argv) + else: + os.execl(python, python, *sys.argv) From add240adfdcb5584b978349e8953aa44e34b38a2 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 14 Dec 2022 12:41:30 +0400 Subject: [PATCH 1198/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 6118d8446..9f5b59a71 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -171,10 +171,10 @@ async def get_file(token, file_id): async def get_file_url(token, file_id): if FILE_URL is None: - return "https://api.telegram.org/file/bot{0}/{1}".format(token, await get_file(token, file_id)['file_path']) + return "https://api.telegram.org/file/bot{0}/{1}".format(token, (await get_file(token, file_id))['file_path']) else: # noinspection PyUnresolvedReferences - return FILE_URL.format(token, await get_file(token, file_id)['file_path']) + return FILE_URL.format(token, (await get_file(token, file_id))['file_path']) async def download_file(token, file_path): From 171172d12eef05da18793de0bacc85e98331692d Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 19 Dec 2022 17:12:15 +0400 Subject: [PATCH 1199/1808] Update async_telebot.py --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1083a2802..983fc2508 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2116,7 +2116,7 @@ async def run_webhooks(self, from telebot.ext.aio import AsyncWebhookListener except (NameError, ImportError): raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") - self.webhook_listener = AsyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path, debug) + self.webhook_listener = AsyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path) await self.webhook_listener.run_app() From 5b279b7ad9f821e0f7d8fd53eaf06428b1ef8d4a Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 19 Dec 2022 17:28:05 +0400 Subject: [PATCH 1200/1808] (A)SyncWebhookListener changes --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- telebot/ext/aio/webhooks.py | 3 ++- telebot/ext/sync/webhooks.py | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c0a73db6..fef57c61a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -540,7 +540,7 @@ def run_webhooks(self, from telebot.ext.sync import SyncWebhookListener except (NameError, ImportError): raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") - self.webhook_listener = SyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path) + self.webhook_listener = SyncWebhookListener(bot=self, secret_token=secret_token, host=listen, port=port, ssl_context=ssl_context, url_path='/'+url_path) self.webhook_listener.run_app() diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 983fc2508..136d45326 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2116,7 +2116,7 @@ async def run_webhooks(self, from telebot.ext.aio import AsyncWebhookListener except (NameError, ImportError): raise ImportError("Please install uvicorn and fastapi in order to use `run_webhooks` method.") - self.webhook_listener = AsyncWebhookListener(self, secret_token, listen, port, ssl_context, '/'+url_path) + self.webhook_listener = AsyncWebhookListener(bot=self, secret_token=secret_token, host=listen, port=port, ssl_context=ssl_context, url_path='/'+url_path) await self.webhook_listener.run_app() diff --git a/telebot/ext/aio/webhooks.py b/telebot/ext/aio/webhooks.py index 56af59276..5a692b304 100644 --- a/telebot/ext/aio/webhooks.py +++ b/telebot/ext/aio/webhooks.py @@ -25,7 +25,8 @@ class AsyncWebhookListener: def __init__(self, bot, - secret_token: str, host: Optional[str]="127.0.0.1", + secret_token: str, + host: Optional[str]="127.0.0.1", port: Optional[int]=443, ssl_context: Optional[tuple]=None, url_path: Optional[str]=None, diff --git a/telebot/ext/sync/webhooks.py b/telebot/ext/sync/webhooks.py index e15e8c8a8..0d41b7205 100644 --- a/telebot/ext/sync/webhooks.py +++ b/telebot/ext/sync/webhooks.py @@ -21,7 +21,8 @@ class SyncWebhookListener: def __init__(self, bot, - secret_token: str, host: Optional[str]="127.0.0.1", + secret_token: str, + host: Optional[str]="127.0.0.1", port: Optional[int]=443, ssl_context: Optional[tuple]=None, url_path: Optional[str]=None, From 925f7012f1230b2d49f64fb10b2a5bf7bc4712e7 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 20 Dec 2022 23:44:32 +0400 Subject: [PATCH 1201/1808] Update formatting.py --- telebot/formatting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 7b6a9f3f9..4af826385 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -61,8 +61,8 @@ def escape_markdown(content: str) -> str: :rtype: :obj:`str` """ - parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!])", r"\\\1", content) - reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!])", r"\1", parse) + parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!\{}])", r"\\\1", content) + reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!\{}])", r"\1", parse) return reparse @@ -323,4 +323,4 @@ def hide_link(url: str) -> str: :return: The hidden url. :rtype: :obj:`str` """ - return f'' \ No newline at end of file + return f'' From c5e733a4c14d9e17c5e37acc46129b10556025e4 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 20 Dec 2022 23:47:12 +0400 Subject: [PATCH 1202/1808] Escape both metachars --- telebot/formatting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 4af826385..ec421c617 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -61,8 +61,8 @@ def escape_markdown(content: str) -> str: :rtype: :obj:`str` """ - parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!\{}])", r"\\\1", content) - reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!\{}])", r"\1", parse) + parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!\{\}])", r"\\\1", content) + reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!\{\}])", r"\1", parse) return reparse From d211db90cf2c3abe5bf7a228b08d552c6e6ef393 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Wed, 21 Dec 2022 15:04:43 +0200 Subject: [PATCH 1203/1808] Update sync_version.po --- .../locales/ru/LC_MESSAGES/sync_version.po | 544 +++++++++++++----- 1 file changed, 414 insertions(+), 130 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 1c91ece19..4664234b9 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -20,19 +20,19 @@ msgstr "" #: ../../sync_version/index.rst:3 msgid "TeleBot version" -msgstr "" +msgstr "Синхронный TeleBot" #: ../../sync_version/index.rst:5 msgid "Synchronous pyTelegramBotAPI documentation" -msgstr "" +msgstr "Документация синхронного pyTelegramBotAPI" #: ../../sync_version/index.rst:5 msgid "ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync" -msgstr "" +msgstr "ptba, pytba, pyTelegramBotAPI, методы, гайд, файлы, sync" #: ../../sync_version/index.rst:10 msgid "TeleBot methods" -msgstr "" +msgstr "Методы класса TeleBot" #: of telebot.ExceptionHandler:1 telebot.Handler:1 telebot.TeleBot:1 #: telebot.custom_filters.TextFilter:1 @@ -43,15 +43,15 @@ msgstr "" #: telebot.handler_backends.SkipHandler:1 telebot.handler_backends.State:1 #: telebot.handler_backends.StatesGroup:1 msgid "Bases: :py:class:`object`" -msgstr "" +msgstr "Базовые классы: :py:class:`object`" #: of telebot.ExceptionHandler:1 msgid "Class for handling exceptions while Polling" -msgstr "" +msgstr "Класс для обработки исключений, возникающих во время поллинга." #: of telebot.Handler:1 msgid "Class for (next step|reply) handlers." -msgstr "" +msgstr "Класс для (next step|reply) хендлеров." #: ../../docstring of telebot.REPLY_MARKUP_TYPES:1 msgid "telebot" @@ -59,7 +59,7 @@ msgstr "" #: ../../docstring of telebot.REPLY_MARKUP_TYPES msgid "type" -msgstr "" +msgstr "тип" #: ../../docstring of telebot.REPLY_MARKUP_TYPES:3 msgid "Module" @@ -67,29 +67,31 @@ msgstr "" #: of telebot.TeleBot:1 msgid "This is the main synchronous class for Bot." -msgstr "" +msgstr "Это основной класс для синхронного бота." #: of telebot.TeleBot:3 msgid "It allows you to add handlers for different kind of updates." -msgstr "" +msgstr "Позволяет добавить хендлеры для различных апдейтов." #: of telebot.TeleBot:5 msgid "Usage:" -msgstr "" +msgstr "Использование:" #: of telebot.TeleBot:7 msgid "Creating instance of TeleBot" -msgstr "" +msgstr "Создание инстанса класса TeleBot" #: of telebot.TeleBot:15 msgid "" "See more examples in examples/ directory: " "https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" msgstr "" +"Больше примеров в папке examples/ : " +"https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" #: of telebot.TeleBot:20 msgid "Install coloredlogs module to specify colorful_logs=True" -msgstr "" +msgstr "Установите пакет coloredlogs для использования colorful_los=True" #: of telebot.TeleBot telebot.TeleBot.add_custom_filter #: telebot.TeleBot.add_data telebot.TeleBot.add_sticker_to_set @@ -193,107 +195,107 @@ msgstr "" #: of telebot.TeleBot:23 msgid "Token of a bot, should be obtained from @BotFather" -msgstr "" +msgstr "Токен бота, нужно получить от @BotFather" #: of telebot.TeleBot:26 msgid "Default parse mode, defaults to None" -msgstr "" +msgstr "Глобальный parse_mode, по умолчанию None" #: of telebot.TeleBot:29 msgid "Threaded or not, defaults to True" -msgstr "" +msgstr "Использовать несколько потоков, по умолчанию True" #: of telebot.TeleBot:32 msgid "Skips pending updates, defaults to False" -msgstr "" +msgstr "Игнорировать апдейты, полученные до запуска, по умолчанию False" #: of telebot.TeleBot:35 msgid "Number of maximum parallel threads, defaults to 2" -msgstr "" +msgstr "Максимальное количество одновременно запущенных потоков, по умолчанию 2" #: of telebot.TeleBot:38 msgid "Next step backend class, defaults to None" -msgstr "" +msgstr "Класс для сохранения next step хендлеров, по умолчанию None" #: of telebot.TeleBot:41 msgid "Reply step handler class, defaults to None" -msgstr "" +msgstr "Класс для сохранения reply хендлеров, по умолчанию None" #: of telebot.TeleBot:44 msgid "Exception handler to handle errors, defaults to None" -msgstr "" +msgstr "Класс для обработки исключений, по умолчанию None" #: of telebot.TeleBot:47 msgid "Last update's id, defaults to 0" -msgstr "" +msgstr "id последнего полученного апдейта, по умолчанию 0" #: of telebot.TeleBot:50 msgid "Supress middleware exceptions, defaults to False" -msgstr "" +msgstr "Игнорировать исключения, вызванные Middleware, по умолчанию False" #: of telebot.TeleBot:53 msgid "Storage for states, defaults to StateMemoryStorage()" -msgstr "" +msgstr "Хранилище состояний (стейтов), по умолчанию StateMemoryStorage()" #: of telebot.TeleBot:56 msgid "Use class middlewares, defaults to False" -msgstr "" +msgstr "Использовать Middlewares, по умолчанию False" #: of telebot.TeleBot:59 msgid "Default value for disable_web_page_preview, defaults to None" -msgstr "" +msgstr "Глобальное значение disable_web_page_preview, по умолчанию None" #: of telebot.TeleBot:62 msgid "Default value for disable_notification, defaults to None" -msgstr "" +msgstr "Глобальное значение disable_notification, по умолчанию None" #: of telebot.TeleBot:65 msgid "Default value for protect_content, defaults to None" -msgstr "" +msgstr "Глобальное значение protect_content, по умолчанию None" #: of telebot.TeleBot:68 msgid "Default value for allow_sending_without_reply, defaults to None" -msgstr "" +msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" #: of telebot.TeleBot:72 msgid "Outputs colorful logs" -msgstr "" +msgstr "Использовать разноцветные логи" #: of telebot.TeleBot.add_custom_filter:1 msgid "Create custom filter." -msgstr "" +msgstr "Создать кастомный фильтр." #: of telebot.TeleBot.add_custom_filter:3 msgid "Example on checking the text of a message" -msgstr "" +msgstr "Пример проверки текста сообщения" #: of telebot.TeleBot.add_custom_filter:12 msgid "Class with check(message) method." -msgstr "" +msgstr "Класс с методом check(message)" #: of telebot.TeleBot.add_custom_filter:13 msgid "Custom filter class with key." -msgstr "" +msgstr "Класс кастомного фильтра с ключом." #: of telebot.TeleBot.add_data:1 msgid "Add data to states." -msgstr "" +msgstr "Добавить данные в состояние (стейт)." #: of telebot.TeleBot.add_data:3 telebot.TeleBot.delete_state:3 #: telebot.TeleBot.get_state:4 telebot.TeleBot.reset_data:3 #: telebot.TeleBot.set_state:9 msgid "User's identifier" -msgstr "" +msgstr "id пользователя" #: of telebot.TeleBot.add_data:6 telebot.TeleBot.delete_state:6 #: telebot.TeleBot.get_state:7 telebot.TeleBot.reset_data:6 #: telebot.TeleBot.set_state:15 msgid "Chat's identifier" -msgstr "" +msgstr "id чата" #: of telebot.TeleBot.add_data:9 msgid "Data to add" -msgstr "" +msgstr "Данные для добавления" #: of telebot.TeleBot.add_data telebot.TeleBot.add_sticker_to_set #: telebot.TeleBot.answer_callback_query telebot.TeleBot.answer_inline_query @@ -433,25 +435,28 @@ msgid "" "Use this method to add a new sticker to a set created by the bot. It's " "required to pass `png_sticker` or `tgs_sticker`. Returns True on success." msgstr "" +"Используйте этот метод, чтобы добавить новый стикер в стикерпак, созданный ботом. Необходимо " +"передать либо `png_sticker`, либо `tgs_sticker`, либо `webm_sticker`. " +"Возвращает True в случае успешного добавления." #: of telebot.TeleBot.add_sticker_to_set:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#addstickertoset" #: of telebot.TeleBot.add_sticker_to_set:7 #: telebot.TeleBot.create_new_sticker_set:7 msgid "User identifier of created sticker set owner" -msgstr "" +msgstr "id пользователя, создавшего стикерпак" #: of telebot.TeleBot.add_sticker_to_set:10 telebot.TeleBot.get_sticker_set:5 #: telebot.TeleBot.set_sticker_set_thumb:6 msgid "Sticker set name" -msgstr "" +msgstr "Имя стикерпака" #: of telebot.TeleBot.add_sticker_to_set:13 #: telebot.TeleBot.create_new_sticker_set:18 msgid "One or more emoji corresponding to the sticker" -msgstr "" +msgstr "Один или несколько эмодзи, относящихся к стикеру" #: of telebot.TeleBot.add_sticker_to_set:16 #: telebot.TeleBot.create_new_sticker_set:21 @@ -463,16 +468,21 @@ msgid "" " to get a file from the Internet, or upload a new one using multipart" "/form-data." msgstr "" +"Изображение стикера в формате PNG, весом не более 512 килобайт, " +"размеры не должны превышать 512px, либо ширина, либо высота должны быть " +"ровно 512px. Передайте file_id в формате str, чтобы отправить уже загруженный " +"на сервера Telegram файл, передайте HTTP URL в формате str, чтобы Telegram " +"скачал файл из интернета, или загрузите новый файл с помощью multipart/form-data." #: of telebot.TeleBot.add_sticker_to_set:21 #: telebot.TeleBot.create_new_sticker_set:26 msgid "TGS animation with the sticker, uploaded using multipart/form-data." -msgstr "" +msgstr "Анимированный стикер в формате TGS, загруженный с помощью multipart/form-data." #: of telebot.TeleBot.add_sticker_to_set:24 #: telebot.TeleBot.create_new_sticker_set:29 msgid "WebM animation with the sticker, uploaded using multipart/form-data." -msgstr "" +msgstr "Анимированный стикер в формате WebM, загруженный с помощью multipart/form-data." #: of telebot.TeleBot.add_sticker_to_set:27 #: telebot.TeleBot.create_new_sticker_set:40 @@ -480,6 +490,7 @@ msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" msgstr "" +"Позиция для размещения маски на лицах в формате JSON" #: of telebot.TeleBot.add_sticker_to_set:30 #: telebot.TeleBot.answer_callback_query:22 @@ -495,7 +506,7 @@ msgstr "" #: telebot.TeleBot.set_sticker_set_thumb:15 #: telebot.TeleBot.unpin_all_forum_topic_messages:13 msgid "On success, True is returned." -msgstr "" +msgstr "В случае успеха возвращается True." #: of telebot.TeleBot.add_sticker_to_set telebot.TeleBot.answer_callback_query #: telebot.TeleBot.answer_inline_query @@ -597,31 +608,38 @@ msgid "" "keyboards. The answer will be displayed to the user as a notification at " "the top of the chat screen or as an alert." msgstr "" +"Используйте этот метод для отправки ответов на callback запросы, отправленные с помощью inline " +"кнопок. Ответ будет показан пользователю как уведомление поверх чата или pop-up предупреждение." + #: of telebot.TeleBot.answer_callback_query:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answercallbackquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answercallbackquery" #: of telebot.TeleBot.answer_callback_query:6 #: telebot.TeleBot.answer_pre_checkout_query:10 #: telebot.TeleBot.answer_shipping_query:5 #: telebot.TeleBot.answer_web_app_query:8 msgid "Unique identifier for the query to be answered" -msgstr "" +msgstr "Уникальный id запроса для ответа" #: of telebot.TeleBot.answer_callback_query:9 msgid "" "Text of the notification. If not specified, nothing will be shown to the " "user, 0-200 characters" msgstr "" +"Текст уведомления. если не задан, то уведомление не будет показано, 0-200 символов" #: of telebot.TeleBot.answer_callback_query:12 msgid "" "If True, an alert will be shown by the client instead of a notification " "at the top of the chat screen. Defaults to false." msgstr "" +"Если True, вместо уведомления поверх чата будет показано pop-up предупреждение, по умолчанию False." #: of telebot.TeleBot.answer_callback_query:15 msgid "" @@ -630,6 +648,10 @@ msgid "" "your game - note that this will only work if the query comes from a " "callback_game button." msgstr "" +"URL, который будет открыт пользовательским клиентом. Если вы создали игру " +"и приняли условия через @BotFather, задайте URL, открывающий вашу игру " +"- учитывайте, что это сработает только если запрос был отправлен с помощью " +"callback_game кнопки." #: of telebot.TeleBot.answer_callback_query:19 msgid "" @@ -637,44 +659,55 @@ msgid "" "query may be cached client-side. Telegram apps will support caching " "starting in version 3.14. Defaults to 0." msgstr "" +"Максимальная длительность хранения ответа на callback запрос пользовательским клиентом в секундах. " +"Приложения Telegram поддерживают хранение ответов начиная с версии 3.14, по умолчанию 0." #: of telebot.TeleBot.answer_inline_query:1 msgid "" "Use this method to send answers to an inline query. On success, True is " "returned. No more than 50 results per query are allowed." msgstr "" +"Используйте этот метод для отправки ответов на inline запрос. В случае успеха возвращается True. " +"Разрешено отправить не более 50 результатов на один запрос." #: of telebot.TeleBot.answer_inline_query:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answerinlinequery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answerinlinequery" #: of telebot.TeleBot.answer_inline_query:6 msgid "Unique identifier for the answered query" -msgstr "" +msgstr "Уникальный id запроса для ответа" #: of telebot.TeleBot.answer_inline_query:9 msgid "Array of results for the inline query" -msgstr "" +msgstr "Массив результатов для ответа на inline запрос" #: of telebot.TeleBot.answer_inline_query:12 msgid "" "The maximum amount of time in seconds that the result of the inline query" " may be cached on the server." msgstr "" +"Максимальная длительность хранения результатов inline запроса на сервере в секундах." #: of telebot.TeleBot.answer_inline_query:16 msgid "" "Pass True, if results may be cached on the server side only for the user " "that sent the query." msgstr "" +"Передайте True, если результаты должны быть сохранены на сервере только для пользователя, " +"отправившего запрос." #: of telebot.TeleBot.answer_inline_query:20 msgid "" "Pass the offset that a client should send in the next query with the same" " text to receive more results." msgstr "" +"Передайте смещение, которое клиент должен отправить в следующем запросе с таким же " +"текстом, чтобы получить новые результаты." #: of telebot.TeleBot.answer_inline_query:24 msgid "" @@ -690,12 +723,23 @@ msgid "" "easily return to the chat where they wanted to use the bot's inline " "capabilities." msgstr "" +"Параметр для команды /start, отправляемой боту, когда пользователь " +"нажимает кнопку переключения. 1-64 символа, разрешены только A-Z, a-z, 0-9, _ и -. " +"Пример: Inline бот, который отправляет видео с YouTube может попросить " +"пользователя подключить бота к его YouTube аккаунту, чтобы поиск соответствовал " +"предпочтениям пользователя. Чтобы это сделать, бот отправляет пользователю кнопку " +"'Подключить YouTube аккаунт' над результатами, или даже до их показа. Пользователь нажимает " +"на кнопку, автоматически переходит в приватный чат с ботом и в это время " +"передаёт стартовый параметр, по которому бот возвращает ссылку для авторизации (OAuth). " +"Как только авторизация пройдена, бот может предложить switch_inline кнопку, чтобы пользователь мог " +"легко вернуться в чат, где он хотел использовать возможности inline бота." #: of telebot.TeleBot.answer_inline_query:32 msgid "" "Parameter for the start message sent to the bot when user presses the " "switch button" msgstr "" +"Параметр для передачи боту вместе с сообщением /start, отправленному при нажатии кнопки переключения" #: of telebot.TeleBot.answer_pre_checkout_query:1 msgid "" @@ -704,18 +748,24 @@ msgid "" "pre_checkout_query. Use this method to respond to such pre-checkout " "queries. On success, True is returned." msgstr "" +"Как только пользователь подтвердил детали оплаты и доставки, Bot API " +"отправляет финальное подтверждение в виде апдейта с полем pre_checkout_query. " +"Используйте этот метод для ответа на такие pre-checkout запросы. В случае успеха возвращается True." #: of telebot.TeleBot.answer_pre_checkout_query:6 msgid "" "The Bot API must receive an answer within 10 seconds after the pre-" "checkout query was sent." msgstr "" +"Bot API должно получить ответ в течение 10 секунд после отправки pre-checkout запроса." #: of telebot.TeleBot.answer_pre_checkout_query:8 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answerprecheckoutquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answerprecheckoutquery" #: of telebot.TeleBot.answer_pre_checkout_query:13 msgid "" @@ -723,6 +773,8 @@ msgid "" " bot is ready to proceed with the order. Use False if there are any " "problems." msgstr "" +"Задайте True если всё правильно (выбранные товары доступны и т.д.) и бот " +"готов обработать заказ. Задайте False если есть какие-то проблемы." #: of telebot.TeleBot.answer_pre_checkout_query:16 msgid "" @@ -733,16 +785,23 @@ msgid "" "different color or garment!\"). Telegram will display this message to the" " user." msgstr "" +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать человек, объясняющее " +"причину, по которой бот не может обработать заказ (например " +"\"Извините, кто-то только что купил последнюю из наших прекрасных черных футболок с коротким рукавом " +"пока вы заполняли детали оплаты. Пожалуйста выберите другой цвет или фасон!\"). " +"Telegram покажет это сообщение пользователю." #: of telebot.TeleBot.answer_shipping_query:1 msgid "Asks for an answer to a shipping question." -msgstr "" +msgstr "Запрашивает ответ на вопрос о доставке." #: of telebot.TeleBot.answer_shipping_query:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answershippingquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answershippingquery" #: of telebot.TeleBot.answer_shipping_query:8 msgid "" @@ -750,12 +809,15 @@ msgid "" "if there are any problems (for example, if delivery to the specified " "address is not possible)" msgstr "" +"Задайте True если доставка по выбранному адресу возможна и False, если есть " +"какие-то проблемы (например, доставка по выбранному адресу не осуществляется)" #: of telebot.TeleBot.answer_shipping_query:11 msgid "" "Required if ok is True. A JSON-serialized array of available shipping " "options." msgstr "" +"Обязательный в случае, когда ok - True. Массив вариантов доставки в формате JSON." #: of telebot.TeleBot.answer_shipping_query:14 msgid "" @@ -764,6 +826,10 @@ msgid "" "delivery to your desired address is unavailable'). Telegram will display " "this message to the user." msgstr "" +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать " +"человек, объясняющее причину, по которой невозможно завершить заказ (например " +"\"Извините, доставка по запрошенному адресу недоступна\"). " +"Telegram покажет это сообщение пользователю." #: of telebot.TeleBot.answer_web_app_query:1 msgid "" @@ -772,20 +838,25 @@ msgid "" " the query originated. On success, a SentWebAppMessage object is " "returned." msgstr "" +"Используйте этот метод, чтобы задать результат взаимодействия с Web App и " +"отправить соответствующее сообщение от лица пользователя в чат, из которого пришел запрос. " +"В случае успеха возвращается объект SentWebAppMessage." #: of telebot.TeleBot.answer_web_app_query:6 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#answerwebappquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answerwebappquery" #: of telebot.TeleBot.answer_web_app_query:11 msgid "A JSON-serialized object describing the message to be sent" -msgstr "" +msgstr "Объект в формате JSON, описывающий сообщение, которое нужно отправить" #: of telebot.TeleBot.answer_web_app_query:14 msgid "On success, a SentWebAppMessage object is returned." -msgstr "" +msgstr "В случае успеха возвращается объект SentWebAppMessage." #: of telebot.TeleBot.answer_web_app_query:15 msgid ":class:`telebot.types.SentWebAppMessage`" @@ -797,12 +868,17 @@ msgid "" "administrator in the chat for this to work and must have the " "can_invite_users administrator right. Returns True on success." msgstr "" +"Используйте этот метод, чтобы одобрить запрос на вступление в чат. " +"Бот должен быть администратором чата и иметь права администратора " +"can_invite_users. Возвращает True в случае успеха." #: of telebot.TeleBot.approve_chat_join_request:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#approvechatjoinrequest" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#approvechatjoinrequest" #: of telebot.TeleBot.approve_chat_join_request:7 #: telebot.TeleBot.decline_chat_join_request:7 @@ -814,6 +890,7 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup (in the format @supergroupusername)" msgstr "" +"Уникальный id чата или username супергруппы (в формате @supergroupusername)" #: of telebot.TeleBot.approve_chat_join_request:11 #: telebot.TeleBot.ban_chat_member:12 @@ -824,7 +901,7 @@ msgstr "" #: telebot.TeleBot.set_chat_administrator_custom_title:10 #: telebot.TeleBot.unban_chat_member:13 msgid "Unique identifier of the target user" -msgstr "" +msgstr "Уникальный id сделавшего запрос пользователя" #: of telebot.TeleBot.approve_chat_join_request:14 #: telebot.TeleBot.ban_chat_sender_chat:16 @@ -841,7 +918,7 @@ msgstr "" #: telebot.TeleBot.unpin_all_chat_messages:11 #: telebot.TeleBot.unpin_chat_message:14 msgid "True on success." -msgstr "" +msgstr "True в случае успеха." #: of telebot.TeleBot.ban_chat_member:1 msgid "" @@ -850,16 +927,21 @@ msgid "" " to the chat on their own using invite links, etc., unless unbanned " "first. Returns True on success." msgstr "" +"Используйте этот метод, чтобы заблокировать пользователя в группе, супергруппе или канале. В " +"случае супергрупп и каналов, пользователь не сможет вернуться в чат самостоятельно, используя " +"ссылки с приглашением и т.д., пока не будет разблокирован. Возвращает True в случае успеха." #: of telebot.TeleBot.ban_chat_member:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#banchatmember" #: of telebot.TeleBot.ban_chat_member:8 telebot.TeleBot.restrict_chat_member:7 msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" +"Уникальный id группы или username супергруппы или канала " +"(в формате @channelusername)" #: of telebot.TeleBot.ban_chat_member:15 msgid "" @@ -867,6 +949,9 @@ msgid "" "more than 366 days or less than 30 seconds from the current time they are" " considered to be banned forever" msgstr "" +"Дата, когда пользователь будет разблокирован, в формате UNIX time. Если пользователь " +"заблокирован больше чем на 366 дней или меньше чем на 30 секунд, то он будет заблокирован " +"до ручной разблокировки" #: of telebot.TeleBot.ban_chat_member:19 msgid "" @@ -875,12 +960,15 @@ msgid "" "group that were sent before the user was removed. Always True for " "supergroups and channels." msgstr "" +"Bool: Передайте True, чтобы удалить все сообщения пользователя из чата. " +"Если False, пользователю будут доступны все сообщения в группе, отправленные до его блокировки. " +"Всегда True для супергрупп и каналов." #: of telebot.TeleBot.ban_chat_member:24 #: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 #: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:22 msgid "Returns True on success." -msgstr "" +msgstr "Возвращает True в случае успеха." #: of telebot.TeleBot.ban_chat_sender_chat:1 msgid "" @@ -890,12 +978,19 @@ msgid "" "administrator in the supergroup or channel for this to work and must have" " the appropriate administrator rights. Returns True on success." msgstr "" +"Используйте этот метод, чтобы заблокировать канал в супергруппе или канале. Владелец " +"канала не сможет отправлять сообщения и участвовать в прямых эфирах " +"от лица канала, пока канал не будет разблокирован. Бот должен быть " +"администратором супергруппы или канала и иметь соответствующие права администратора." +"Возвращает True в случае успеха." #: of telebot.TeleBot.ban_chat_sender_chat:8 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#banchatsenderchat" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#banchatsenderchat" #: of telebot.TeleBot.ban_chat_sender_chat:10 #: telebot.TeleBot.close_forum_topic:7 telebot.TeleBot.copy_message:5 @@ -919,16 +1014,20 @@ msgid "" "Unique identifier for the target chat or username of the target channel " "(in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала " +"(в формате @channelusername)" #: of telebot.TeleBot.ban_chat_sender_chat:13 msgid "Unique identifier of the target sender chat" -msgstr "" +msgstr "Уникальный id канала для блокировки" #: of telebot.TeleBot.callback_query_handler:1 msgid "" "Handles new incoming callback query. As a parameter to the decorator " "function, it passes :class:`telebot.types.CallbackQuery` object." msgstr "" +"Обрабатывает новый callback запрос. В качестве параметра передаёт в декорируемую " +"функцию объект :class:`telebot.types.CallbackQuery`." #: of telebot.TeleBot.callback_query_handler:4 #: telebot.TeleBot.channel_post_handler:10 @@ -956,7 +1055,7 @@ msgstr "" #: telebot.TeleBot.register_shipping_query_handler:6 #: telebot.TeleBot.shipping_query_handler:4 msgid "Function executed as a filter" -msgstr "" +msgstr "Функция, используемая в качестве фильтра" #: of telebot.TeleBot.callback_query_handler:7 #: telebot.TeleBot.channel_post_handler:16 @@ -984,7 +1083,7 @@ msgstr "" #: telebot.TeleBot.register_shipping_query_handler:12 #: telebot.TeleBot.shipping_query_handler:7 msgid "Optional keyword arguments(custom filters)" -msgstr "" +msgstr "Необязательные именованные аргументы(кастомные фильтры)" #: of telebot.TeleBot.channel_post_handler:1 msgid "" @@ -992,18 +1091,21 @@ msgid "" "etc. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" +"Обрабатывает новый пост любого типа в канале - текст, фото, стикер и т.д. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`." #: of telebot.TeleBot.channel_post_handler:4 #: telebot.TeleBot.edited_channel_post_handler:4 #: telebot.TeleBot.edited_message_handler:4 telebot.TeleBot.message_handler:34 msgid "Optional list of strings (commands to handle)." -msgstr "" +msgstr "Необязательный список строк - команд для обработки." #: of telebot.TeleBot.channel_post_handler:7 #: telebot.TeleBot.edited_channel_post_handler:7 #: telebot.TeleBot.edited_message_handler:7 telebot.TeleBot.message_handler:37 msgid "Optional regular expression." -msgstr "" +msgstr "Необязательное регулярное выражение." #: of telebot.TeleBot.channel_post_handler:13 #: telebot.TeleBot.edited_channel_post_handler:13 @@ -1013,7 +1115,7 @@ msgstr "" #: telebot.TeleBot.register_edited_message_handler:6 #: telebot.TeleBot.register_message_handler:6 msgid "Supported message content types. Must be a list. Defaults to ['text']." -msgstr "" +msgstr "Обрабатываемые виды контента. Обязан быть списком. По умолчанию ['text']" #: of telebot.TeleBot.chat_join_request_handler:1 msgid "" @@ -1022,6 +1124,10 @@ msgid "" "updates. As a parameter to the decorator function, it passes " ":class:`telebot.types.ChatJoinRequest` object." msgstr "" +"Обрабатывает запрос на вступление в чат. Бот должен иметь права администратора" +"can_invite_users в чате, чтобы получать такие апдейты. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ChatJoinRequest`." #: of telebot.TeleBot.chat_member_handler:1 msgid "" @@ -1031,6 +1137,11 @@ msgid "" "the decorator function, it passes " ":class:`telebot.types.ChatMemberUpdated` object." msgstr "" +"Обрабатывает изменение статуса пользователя в чате. Бот должен быть администратором " +"чата и явно указать “chat_member“ в allowed_updates, чтобы получать такие апдейты. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ChatMemberUpdated`." + #: of telebot.TeleBot.chosen_inline_handler:1 msgid "" @@ -1040,6 +1151,10 @@ msgid "" "parameter to the decorator function, it passes " ":class:`telebot.types.ChosenInlineResult` object." msgstr "" +"Обрабатывает результат inline запроса, который был выбран пользователем и отправлен " +"собеседнику в чате. Пожалуйста ознакомьтесь с документацией по сбору фидбека " +"для получения таких апдейтов вашим ботом. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.ChosenInlineResult`." #: of telebot.TeleBot.clear_reply_handlers:1 #: telebot.TeleBot.clear_reply_handlers_by_message_id:1 @@ -1047,29 +1162,32 @@ msgid "" "Clears all callback functions registered by register_for_reply() and " "register_for_reply_by_message_id()." msgstr "" +"Очищает список функций, зарегистрированных с помощью register_for_reply() и " +"register_for_reply_by_message_id()." #: of telebot.TeleBot.clear_reply_handlers:3 msgid "The message for which we want to clear reply handlers" -msgstr "" +msgstr "Сообщение, у которого нужно очистить список reply хендлеров" #: of telebot.TeleBot.clear_reply_handlers_by_message_id:3 msgid "The message id for which we want to clear reply handlers" -msgstr "" +msgstr "id сообщения, у которого нужно очистить список reply хендлеров" #: of telebot.TeleBot.clear_step_handler:1 #: telebot.TeleBot.clear_step_handler_by_chat_id:1 msgid "Clears all callback functions registered by register_next_step_handler()." -msgstr "" +msgstr "Очищает список функций, зарегистрированных с помощью register_next_step_handler()." #: of telebot.TeleBot.clear_step_handler:3 msgid "" "The message for which we want to handle new message after that in same " "chat." msgstr "" +"Сообщение, после которого нужно обработать новое сообщение в том же чате." #: of telebot.TeleBot.clear_step_handler_by_chat_id:3 msgid "The chat for which we want to clear next step handlers" -msgstr "" +msgstr "Чат, в котором мы хотим очистить список next step хендлеров" #: of telebot.TeleBot.close:1 msgid "" @@ -1079,60 +1197,70 @@ msgid "" "The method will return error 429 in the first 10 minutes after the bot is" " launched. Returns True on success." msgstr "" +"Используйте этот метод чтобы закрыть инстанс бота прежде чем перемещать его с одного " +"локального сервера на другой. Вы должны удалить вебхук перед вызовом этого метода, " +"чтобы убедиться. что бот не будет запущен повторно после перезапуска сервера. " +"Метод будет возвращать ошибку 429 в течение 10 минут после запуска бота. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.close:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#close" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#close" #: of telebot.TeleBot.close_forum_topic:1 msgid "" -"Use this method to close an open topic in a forum supergroup chat. The " -"bot must be an administrator in the chat for this to work and must have " -"the can_manage_topics administrator rights, unless it is the creator of " -"the topic. Returns True on success." +"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics, за исключением " +"случаев, когда бот является создателем топика. Возвращает True в случае успеха." msgstr "" #: of telebot.TeleBot.close_forum_topic:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#closeforumtopic" #: of telebot.TeleBot.close_forum_topic:10 msgid "Identifier of the topic to close" -msgstr "" +msgstr "id топика для закрытия" #: of telebot.TeleBot.copy_message:1 msgid "Use this method to copy messages of any kind." -msgstr "" +msgstr "Используйте этот метод для копирования любых сообщений." #: of telebot.TeleBot.copy_message:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#copymessage" #: of telebot.TeleBot.copy_message:8 telebot.TeleBot.forward_message:11 msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" msgstr "" +"Уникальный id чата, в который было отправлено исходное сообщение " +"(или username канала в формате @channelusername)" #: of telebot.TeleBot.copy_message:10 telebot.TeleBot.forward_message:14 msgid "Message identifier in the chat specified in from_chat_id" -msgstr "" +msgstr "id сообщения в чате, заданном в from_chat_id" #: of telebot.TeleBot.copy_message:13 msgid "" "New caption for media, 0-1024 characters after entities parsing. If not " "specified, the original caption is kept" msgstr "" +"Новая подпись для медиа, 0-1024 символа после форматирования. Если не задано, " +"используется исходная подпись" #: of telebot.TeleBot.copy_message:16 msgid "Mode for parsing entities in the new caption." -msgstr "" +msgstr "Режим форматирования новой подписи." #: of telebot.TeleBot.copy_message:19 msgid "" "A JSON-serialized list of special entities that appear in the new " "caption, which can be specified instead of parse_mode" msgstr "" +"Список отформатированных частей новой подписи в формате JSON, " +"можно использовать вместо parse_mode" #: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 #: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 @@ -1146,6 +1274,7 @@ msgid "" "Sends the message silently. Users will receive a notification with no " "sound." msgstr "" +"Отправить сообщение, при получении которого пользователи получат уведомление без звука." #: of telebot.TeleBot.copy_message:25 telebot.TeleBot.send_animation:34 #: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:38 @@ -1156,7 +1285,7 @@ msgstr "" #: telebot.TeleBot.send_video:38 telebot.TeleBot.send_video_note:42 #: telebot.TeleBot.send_voice:43 msgid "Protects the contents of the sent message from forwarding and saving" -msgstr "" +msgstr "Запретить пересылку и сохранение содержимого сообщения" #: of telebot.TeleBot.copy_message:28 telebot.TeleBot.send_animation:37 #: telebot.TeleBot.send_audio:29 telebot.TeleBot.send_contact:23 @@ -1168,7 +1297,7 @@ msgstr "" #: telebot.TeleBot.send_venue:30 telebot.TeleBot.send_video:41 #: telebot.TeleBot.send_video_note:19 telebot.TeleBot.send_voice:20 msgid "If the message is a reply, ID of the original message" -msgstr "" +msgstr "Если сообщение является ответом - id сообщения, на которое дан ответ" #: of telebot.TeleBot.copy_message:31 telebot.TeleBot.send_animation:54 #: telebot.TeleBot.send_audio:54 telebot.TeleBot.send_dice:26 @@ -1181,6 +1310,9 @@ msgid "" "Pass True, if the message should be sent even if the specified replied-to" " message is not found" msgstr "" +"Передайте True, если сообщение нужно отправить даже в случае отсутствия сообщения, на " +"которое дан ответ" + #: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:40 #: telebot.TeleBot.send_contact:26 telebot.TeleBot.send_dice:18 @@ -1195,6 +1327,8 @@ msgid "" "keyboard, custom reply keyboard, instructions to remove reply keyboard or" " to force a reply from the user." msgstr "" +"Дополнительные элементы интерфейса. Inline клавиатура, текстовая клавиатура, " +"запрос на удаление текстовой клавиатуры или запрос на ответ от пользователя." #: of telebot.TeleBot.copy_message:39 telebot.TeleBot.delete_message:19 #: telebot.TeleBot.edit_message_live_location:23 @@ -1208,14 +1342,14 @@ msgstr "" #: telebot.TeleBot.send_video_note:30 telebot.TeleBot.send_voice:34 #: telebot.TeleBot.stop_message_live_location:19 msgid "Timeout in seconds for the request." -msgstr "" +msgstr "Таймаут запроса в секундах." #: of telebot.TeleBot.copy_message:42 telebot.TeleBot.forward_message:23 #: telebot.TeleBot.send_audio:60 telebot.TeleBot.send_dice:32 #: telebot.TeleBot.send_location:46 telebot.TeleBot.send_message:43 #: telebot.TeleBot.send_photo:42 telebot.TeleBot.send_voice:46 msgid "Identifier of a message thread, in which the message will be sent" -msgstr "" +msgstr "id топика, в который нужно отправить сообщение" #: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 #: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:60 @@ -1228,7 +1362,7 @@ msgstr "" #: telebot.TeleBot.send_video:61 telebot.TeleBot.send_video_note:48 #: telebot.TeleBot.send_voice:49 msgid "On success, the sent Message is returned." -msgstr "" +msgstr "В случае успеха возвращает отправленное сообщение (Message)." #: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 #: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:61 @@ -1249,12 +1383,18 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" +"используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " +"должен быть администратором чата и иметь соответствующие права администратора. " +"Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " +"ссылку-приглашение (ChatInviteLink)." #: of telebot.TeleBot.create_chat_invite_link:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createchatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createchatinvitelink" #: of telebot.TeleBot.create_chat_invite_link:8 #: telebot.TeleBot.edit_chat_invite_link:6 @@ -1264,21 +1404,22 @@ msgid "" "Id: Unique identifier for the target chat or username of the target " "channel (in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.TeleBot.create_chat_invite_link:12 #: telebot.TeleBot.edit_chat_invite_link:10 msgid "Invite link name; 0-32 characters" -msgstr "" +msgstr "Название ссылки-приглашения; 0-32 символа" #: of telebot.TeleBot.create_chat_invite_link:15 #: telebot.TeleBot.edit_chat_invite_link:16 msgid "Point in time (Unix timestamp) when the link will expire" -msgstr "" +msgstr "Время, когда ссылка будет аннулирована в формате Unix timestamp" #: of telebot.TeleBot.create_chat_invite_link:18 #: telebot.TeleBot.edit_chat_invite_link:19 msgid "Maximum number of users that can be members of the chat simultaneously" -msgstr "" +msgstr "Максимальное количество пользователей в чате" #: of telebot.TeleBot.create_chat_invite_link:21 #: telebot.TeleBot.edit_chat_invite_link:22 @@ -1286,12 +1427,14 @@ msgid "" "True, if users joining the chat via the link need to be approved by chat " "administrators. If True, member_limit can't be specified" msgstr "" +"True, если пользователи, использующие эту ссылку должны быть одобрены " +"администраторами чата. Нельзя использовать True вместе с member_limit" #: of telebot.TeleBot.create_chat_invite_link:24 #: telebot.TeleBot.edit_chat_invite_link:25 #: telebot.TeleBot.revoke_chat_invite_link:14 msgid "Returns the new invite link as ChatInviteLink object." -msgstr "" +msgstr "Возвращает новую ссылку-приглашение (ChatInviteLink)." #: of telebot.TeleBot.create_chat_invite_link:25 #: telebot.TeleBot.edit_chat_invite_link:26 @@ -1306,34 +1449,44 @@ msgid "" "can_manage_topics administrator rights. Returns information about the " "created topic as a ForumTopic object." msgstr "" +"Используйте этот метод, чтобы создать топик в супергруппе. Бот должен быть " +"администратором чата и иметь права администратора can_manage_topics. " +"Возвращает информацию о созданном топике (ForumTopic)." #: of telebot.TeleBot.create_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createforumtopic" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createforumtopic" #: of telebot.TeleBot.create_forum_topic:10 msgid "Name of the topic, 1-128 characters" -msgstr "" +msgstr "Имя топика, 1-128 символов" #: of telebot.TeleBot.create_forum_topic:13 msgid "" "Color of the topic icon in RGB format. Currently, must be one of " "0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" msgstr "" +"Цвет иконки топика в формате RGB. В текущий момент, доступны цвета " +"0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" #: of telebot.TeleBot.create_forum_topic:16 msgid "" "Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " "be exactly 1 character long" msgstr "" +"Кастомный эмодзи для использования в качестве иконки топика. Должно быть “tgs” " +"эмодзи и быть ровно 1 символом" #: of telebot.TeleBot.create_forum_topic:19 msgid "" "On success, information about the created topic is returned as a " "ForumTopic object." msgstr "" +"В случае успеха возвращается информация о созданном топике (ForumTopic)." #: of telebot.TeleBot.create_forum_topic:20 msgid ":class:`telebot.types.ForumTopic`" @@ -1344,46 +1497,56 @@ msgid "" "Use this method to create a link for an invoice. Returns the created " "invoice link as String on success." msgstr "" +"используйте этот метод, чтобы создать ссылку-инвойс. Возвращает созданную ссылку " +"в случае успеха (String)." #: of telebot.TeleBot.create_invoice_link:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createinvoicelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createinvoicelink" #: of telebot.TeleBot.create_invoice_link:7 telebot.TeleBot.send_invoice:8 msgid "Product name, 1-32 characters" -msgstr "" +msgstr "Название товара, 1-32 символа" #: of telebot.TeleBot.create_invoice_link:10 telebot.TeleBot.send_invoice:11 msgid "Product description, 1-255 characters" -msgstr "" +msgstr "Описание товара, 1-255 символов" #: of telebot.TeleBot.create_invoice_link:13 telebot.TeleBot.send_invoice:14 msgid "" "Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " "the user, use for your internal processes." msgstr "" +"Дополнительные данные, 1-128 байт. Не будет показано пользователю, " +"используйте во внутренних процессах." #: of telebot.TeleBot.create_invoice_link:17 telebot.TeleBot.send_invoice:18 msgid "Payments provider token, obtained via @Botfather" -msgstr "" +msgstr "Токен платежной системы, полученный через @BotFather" #: of telebot.TeleBot.create_invoice_link:20 telebot.TeleBot.send_invoice:21 msgid "" "Three-letter ISO 4217 currency code, see " "https://core.telegram.org/bots/payments#supported-currencies" msgstr "" +"Трехбуквенный код валюты в формате ISO 4217, см. " +"https://core.telegram.org/bots/payments#supported-currencies" #: of telebot.TeleBot.create_invoice_link:24 telebot.TeleBot.send_invoice:25 msgid "" "Price breakdown, a list of components (e.g. product price, tax, discount," " delivery cost, delivery tax, bonus, etc.)" msgstr "" +"Детали цены, список компонент (например цена продукта, налог, скидка, " +"стоимость доставки, налог на доставку, бонус и т.д.)" #: of telebot.TeleBot.create_invoice_link:28 telebot.TeleBot.send_invoice:87 msgid "The maximum accepted amount for tips in the smallest units of the currency" -msgstr "" +msgstr "Максимальный размер чаевых в наименьших единицах выбранной валюты" #: of telebot.TeleBot.create_invoice_link:31 telebot.TeleBot.send_invoice:90 msgid "" @@ -1392,6 +1555,9 @@ msgid "" " The suggested tip amounts must be positive, passed in a strictly " "increased order and must not exceed max_tip_amount." msgstr "" +"Массив предлагаемых вариантов чаевых в наименьших единицах выбранной валюты " +"в формате JSON. Можно задать не более 4 вариантов. Варианты чаевых должны быть " +"больше нуля, перечисленные в порядке строгого возрастания и не превышать max_tip_amount." #: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:77 msgid "" @@ -1399,6 +1565,9 @@ msgid "" "payment provider. A detailed description of required fields should be " "provided by the payment provider." msgstr "" +"Данные о инвойсе в формате JSON, которые будут переданы платежной системе. " +"Подробное описание обязательных полей должно быть предоставлено провайдером " +"платежной системы." #: of telebot.TeleBot.create_invoice_link:40 msgid "" @@ -1406,52 +1575,56 @@ msgid "" "a photo of the invoice. People like it better when they see a photo of " "what they are paying for." msgstr "" +"URL изображения товара для инвойса. Может быть изображением товаров или " +"изображением инвойса. Людям больше нравится видеть фото товара, " +"за который они платят." #: of telebot.TeleBot.create_invoice_link:44 telebot.TeleBot.send_invoice:37 msgid "Photo size in bytes" -msgstr "" +msgstr "Вес изображения в байтах" #: of telebot.TeleBot.create_invoice_link:47 telebot.TeleBot.send_invoice:40 msgid "Photo width" -msgstr "" +msgstr "Ширина изображения" #: of telebot.TeleBot.create_invoice_link:50 telebot.TeleBot.send_invoice:43 msgid "Photo height" -msgstr "" +msgstr "Высота изображения" #: of telebot.TeleBot.create_invoice_link:53 telebot.TeleBot.send_invoice:46 msgid "Pass True, if you require the user's full name to complete the order" -msgstr "" +msgstr "Передайте True, если для совершения заказа требуется полное имя пользователя" #: of telebot.TeleBot.create_invoice_link:56 telebot.TeleBot.send_invoice:49 msgid "Pass True, if you require the user's phone number to complete the order" -msgstr "" +msgstr "Передайте True, если для совершения заказа требуется номер телефона пользователя" #: of telebot.TeleBot.create_invoice_link:59 telebot.TeleBot.send_invoice:52 msgid "Pass True, if you require the user's email to complete the order" -msgstr "" +msgstr "Передайте True, если для совершения заказа требуется email пользователя" #: of telebot.TeleBot.create_invoice_link:62 telebot.TeleBot.send_invoice:55 msgid "" "Pass True, if you require the user's shipping address to complete the " "order" msgstr "" +"Передайте True, если для совершения заказа требуется адрес доставки" #: of telebot.TeleBot.create_invoice_link:65 telebot.TeleBot.send_invoice:61 msgid "Pass True, if user's phone number should be sent to provider" -msgstr "" +msgstr "Передайте True, если номер телефона пользователя нужно отправить платежной системе" #: of telebot.TeleBot.create_invoice_link:68 telebot.TeleBot.send_invoice:64 msgid "Pass True, if user's email address should be sent to provider" -msgstr "" +msgstr "Передайте True, если email пользователя нужно отправить платежной системе" #: of telebot.TeleBot.create_invoice_link:71 telebot.TeleBot.send_invoice:58 msgid "Pass True, if the final price depends on the shipping method" -msgstr "" +msgstr "Передайте True, если окончательная цена зависит от способа доставки" #: of telebot.TeleBot.create_invoice_link:74 msgid "Created invoice link as String on success." -msgstr "" +msgstr "Созданная ссылка-инвойс (String) в случае успеха." #: of telebot.TeleBot.create_invoice_link:75 #: telebot.TeleBot.export_chat_invite_link:11 telebot.TeleBot.get_file_url:7 @@ -1463,12 +1636,17 @@ msgid "" "Use this method to create new sticker set owned by a user. The bot will " "be able to edit the created sticker set. Returns True on success." msgstr "" +"Используйте этот метод, чтобы создать новый стикерпак, владельцем которого " +"станет пользователь. Бот будет иметь возможность редактировать созданный стикерпак. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.create_new_sticker_set:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createnewstickerset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createnewstickerset" #: of telebot.TeleBot.create_new_sticker_set:10 msgid "" @@ -1478,16 +1656,23 @@ msgid "" "in \"_by_\". is case insensitive. 1-64 " "characters." msgstr "" +"Короткое имя стикерпака для использования в ссылках вида t.me/addstickers/ " +"(например animals). Может содержать только латинские буквы, цифры и " +"нижние подчеркивания. Должно начинаться с буквы, не может содержать подряд " +"идущие нижние подчеркивания и должно заканчиваться на \"_by_\". " +" учитывает регистр. 1-64 символа." #: of telebot.TeleBot.create_new_sticker_set:15 msgid "Sticker set title, 1-64 characters" -msgstr "" +msgstr "Название стикерпака, 1-64 символа" #: of telebot.TeleBot.create_new_sticker_set:32 msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." msgstr "" +"Передайте True, если создаётся стикерпак масок. Устарело, начиная с " +"Bot API 6.2, используйте sticker_type." #: of telebot.TeleBot.create_new_sticker_set:36 msgid "" @@ -1495,6 +1680,9 @@ msgid "" "emoji sticker sets can't be created via the Bot API at the moment. By " "default, a regular sticker set is created." msgstr "" +"Необязательный, тип стикерпака, передайте “regular” или “mask”. Стикерпаки " +"кастомных эмодзи пока что не могут быть созданы с помощью Bot API. По умолчанию " +"будет создан обычный стикерпак." #: of telebot.TeleBot.decline_chat_join_request:1 msgid "" @@ -1502,12 +1690,17 @@ msgid "" "administrator in the chat for this to work and must have the " "can_invite_users administrator right. Returns True on success." msgstr "" +"Используйте этот метод, чтобы отклонить запрос на вступление в чат. Бот " +"должен быть администратором чата и иметь права администратора can_invite_users. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.decline_chat_join_request:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#declinechatjoinrequest" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#declinechatjoinrequest" #: of telebot.TeleBot.delete_chat_photo:1 msgid "" @@ -1517,10 +1710,15 @@ msgid "" " Note: In regular groups (non-supergroups), this method will only work if" " the ‘All Members Are Admins’ setting is off in the target group." msgstr "" +"Используйте этот метод, чтобы удалить фото чата. Нельзя изменить фото в " +"приватных чатах. Бот должен быть администратором чата и иметь соответствующие " +"права администратора. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппах), метод будет работать только " +"в случаях, когда настройка ‘All Members Are Admins’ выключена." #: of telebot.TeleBot.delete_chat_photo:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletechatphoto" #: of telebot.TeleBot.delete_chat_photo:8 telebot.TeleBot.set_chat_photo:9 #: telebot.TeleBot.unpin_all_chat_messages:7 @@ -1528,6 +1726,7 @@ msgid "" "Int or Str: Unique identifier for the target chat or username of the " "target channel (in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.TeleBot.delete_chat_sticker_set:1 msgid "" @@ -1537,12 +1736,18 @@ msgid "" "returned in getChat requests to check if the bot can use this method. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить стикерпак группы из супергруппы. Бот " +"должен быть администратором чата и иметь соответствующие права администратора. " +"Используйте поле can_set_sticker_set, возвращаемое методом getChat, чтобы " +"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." #: of telebot.TeleBot.delete_chat_sticker_set:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletechatstickerset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deletechatstickerset" #: of telebot.TeleBot.delete_forum_topic:1 msgid "" @@ -1551,16 +1756,22 @@ msgid "" "can_manage_topics administrator rights, unless it is the creator of the " "topic. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics, " +"за исключением случае, когда бот является создателем топика. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.delete_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deleteforumtopic" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deleteforumtopic" #: of telebot.TeleBot.delete_forum_topic:10 msgid "Identifier of the topic to delete" -msgstr "" +msgstr "id топика, который нужно удалить" #: of telebot.TeleBot.delete_message:1 msgid "" @@ -1575,14 +1786,24 @@ msgid "" "has can_delete_messages permission in a supergroup or a channel, it can " "delete any message there. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить сообщение, в том числе сервисное, " +"ограничения: - Сообщение может быть удалено только если оно было отправлено " +"менее 48 часов назад. - Dice-сообщение в приватном чате может быть удалено " +"только если оно было отправлено более 24 часов назад. - Боты могут удалять " +"свои сообщения в приватных чатах, группах и супергруппах. - Боты могут удалять " +"чужие сообщение в приватных чатах. - Боты с правами администратора can_post_messages " +"могут удалять сообщения в каналах. - Если бот является администратором группы, он " +"может удалить любое сообщение в ней. - Если бот имеет права администратора " +"can_delete_messages в супергруппе или канале, он может удалить любое сообщение в них. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.delete_message:11 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletemessage" #: of telebot.TeleBot.delete_message:16 msgid "Identifier of the message to delete" -msgstr "" +msgstr "id сообщения, которое нужно удалить" #: of telebot.TeleBot.delete_my_commands:1 msgid "" @@ -1590,12 +1811,18 @@ msgid "" "scope and user language. After deletion, higher level commands will be " "shown to affected users. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить список команд бота для заданных поля " +"видимости и языка. После удаления, команды более широкого поля видимости " +"будут доступны пользователям, которых коснулись изменения. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.delete_my_commands:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletemycommands" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deletemycommands" #: of telebot.TeleBot.delete_my_commands:7 telebot.TeleBot.get_my_commands:6 #: telebot.TeleBot.set_my_commands:8 @@ -1603,6 +1830,7 @@ msgid "" "The scope of users for which the commands are relevant. Defaults to " "BotCommandScopeDefault." msgstr "" +"Область видимости команд. По умолчанию BotCommandScopeDefault." #: of telebot.TeleBot.delete_my_commands:11 telebot.TeleBot.get_my_commands:10 #: telebot.TeleBot.set_my_commands:12 @@ -1611,51 +1839,59 @@ msgid "" "to all users from the given scope, for whose language there are no " "dedicated commands" msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения коснутся " +"команд для всех пользователей в заданном поле видимости, не имеющих команд на их языке" #: of telebot.TeleBot.delete_state:1 msgid "Delete the current state of a user." -msgstr "" +msgstr "Удалить текущее состояние (стейт) пользователя." #: of telebot.TeleBot.delete_sticker_from_set:1 msgid "" "Use this method to delete a sticker from a set created by the bot. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного ботом. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.delete_sticker_from_set:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletestickerfromset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deletestickerfromset" #: of telebot.TeleBot.delete_sticker_from_set:5 #: telebot.TeleBot.set_sticker_position_in_set:5 msgid "File identifier of the sticker" -msgstr "" +msgstr "id файла стикера" #: of telebot.TeleBot.delete_webhook:1 msgid "" "Use this method to remove webhook integration if you decide to switch " "back to getUpdates. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить вебхук, если вы решите перейти обратно на getUpdates. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.delete_webhook:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletewebhook" #: of telebot.TeleBot.delete_webhook:6 telebot.TeleBot.run_webhooks:36 #: telebot.TeleBot.set_webhook:36 msgid "Pass True to drop all pending updates, defaults to None" -msgstr "" +msgstr "Передайте True, чтобы удалить все предшествующие запуску бота апдейты, по умолчанию None" #: of telebot.TeleBot.delete_webhook:9 telebot.TeleBot.run_webhooks:39 msgid "Request connection timeout, defaults to None" -msgstr "" +msgstr "Тайм-аут запроса, по умолчанию None" #: of telebot.TeleBot.disable_save_next_step_handlers:1 #: telebot.TeleBot.disable_save_reply_handlers:1 msgid "Disable saving next step handlers (by default saving disable)" -msgstr "" +msgstr "Отключить сохранение next step хендлеров (по умолчанию сохранение отключено)" #: of telebot.TeleBot.disable_save_next_step_handlers:3 msgid "" @@ -1664,6 +1900,9 @@ msgid "" "MemoryHandlerBackend is reassigned as a new next_step_backend backend " "instead of FileHandlerBackend." msgstr "" +"Эта функция оставлена для обратной совместимости, для отключения возможности " +"сохранения хендлеров в файл. В тех же целях MemoryHandlerBackend переопределен " +"как новый next_step_backend вместо FileHandlerBackend." #: of telebot.TeleBot.disable_save_reply_handlers:3 msgid "" @@ -1672,14 +1911,17 @@ msgid "" "MemoryHandlerBackend is reassigned as a new reply_backend backend instead" " of FileHandlerBackend." msgstr "" +"Эта функция оставлена для обратной совместимости, для отключения возможности " +"сохранения хендлеров в файл. В тех же целях MemoryHandlerBackend переопределен " +"как новый reply_backend вместо FileHandlerBackend." #: of telebot.TeleBot.download_file:1 msgid "Downloads file." -msgstr "" +msgstr "Скачивает файл." #: of telebot.TeleBot.download_file:3 msgid "Path where the file should be downloaded." -msgstr "" +msgstr "Путь, куда файл нужно сохранить." #: of telebot.TeleBot.download_file:6 msgid "bytes" @@ -1695,16 +1937,22 @@ msgid "" " bot must be an administrator in the chat for this to work and must have " "the appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы изменить неосновную ссылку-приглашение, " +"созданную ботом. Бот должен быть администратором чата и иметь соответствующие " +"права администратора." #: of telebot.TeleBot.edit_chat_invite_link:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editchatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editchatinvitelink" + #: of telebot.TeleBot.edit_chat_invite_link:13 msgid "The invite link to edit" -msgstr "" +msgstr "Ссылка-приглашение для изменения" #: of telebot.TeleBot.edit_forum_topic:1 msgid "" @@ -1713,71 +1961,83 @@ msgid "" "must have can_manage_topics administrator rights, unless it is the " "creator of the topic. Returns True on success." msgstr "" +"Используйте этот метод, чтобы изменить название и иконку топика в супергруппе. " +"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " +"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.TeleBot.edit_forum_topic:5 msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" #: of telebot.TeleBot.edit_forum_topic:10 msgid "Identifier of the topic to edit" -msgstr "" +msgstr "id топика для изменения" #: of telebot.TeleBot.edit_forum_topic:13 msgid "New name of the topic, 1-128 characters" -msgstr "" +msgstr "Новое название топика, 1-128 символов" #: of telebot.TeleBot.edit_forum_topic:16 msgid "" "New custom emoji for the topic icon. Must be an emoji of type “tgs” and " "must be exactly 1 character long" msgstr "" +"Новый кастомный эмодзи для иконки топика. Должно быть “tgs” эмодзи, " +"длиной ровно в 1 символ" #: of telebot.TeleBot.edit_message_caption:1 msgid "Use this method to edit captions of messages." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить подписи к медиа в сообщениях" #: of telebot.TeleBot.edit_message_caption:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagecaption" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagecaption" #: of telebot.TeleBot.edit_message_caption:5 msgid "New caption of the message" -msgstr "" +msgstr "Новая подпись к медиа" #: of telebot.TeleBot.edit_message_caption:8 msgid "" "Required if inline_message_id is not specified. Unique identifier for the" " target chat or username of the target channel" msgstr "" +"Обязательный, если не указан inline_message_id. Уникальный id чата или username" +" канала" #: of telebot.TeleBot.edit_message_caption:11 msgid "Required if inline_message_id is not specified." -msgstr "" +msgstr "Обязательный, если не указан inline_message_id." #: of telebot.TeleBot.edit_message_caption:14 msgid "" "Required if inline_message_id is not specified. Identifier of the inline " "message." msgstr "" +"Обязательный, если не указан inline_message_id. id inline сообщения." #: of telebot.TeleBot.edit_message_caption:17 msgid "New caption of the message, 0-1024 characters after entities parsing" -msgstr "" +msgstr "Новая подпись к медиа в сообщении, 0-1024 символа после форматирования" #: of telebot.TeleBot.edit_message_caption:20 msgid "" "A JSON-serialized array of objects that describe how the caption should " "be parsed." msgstr "" +"Массив объектов, описывающих то, как будет происходить парсинг подписи к " +"медиа в формате JSON." #: of telebot.TeleBot.edit_message_caption:23 #: telebot.TeleBot.edit_message_media:19 #: telebot.TeleBot.edit_message_reply_markup:14 #: telebot.TeleBot.edit_message_text:26 msgid "A JSON-serialized object for an inline keyboard." -msgstr "" +msgstr "JSON-сериализованный объект inline клавиатуры." #: of telebot.TeleBot.edit_message_caption:26 #: telebot.TeleBot.edit_message_media:22 @@ -1787,6 +2047,8 @@ msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." msgstr "" +"В случае успеха, если изменённое сообщение отправлено ботом, возвращается новый объект Message, " +"иначе (inline сообщения) возвращается True." #: of telebot.TeleBot.edit_message_caption:27 msgid ":obj:`types.Message` | :obj:`bool`" @@ -1797,12 +2059,14 @@ msgid "" "Use this method to edit live location messages. A location can be edited " "until its live_period expires or editing is explicitly" msgstr "" +"Используйте этот метод, чтобы изменить live местоположение в сообщении. Местоположение может быть " +"изменено пока не истечёт live_period или не" #: of telebot.TeleBot.edit_message_live_location:2 msgid "" -"disabled by a call to stopMessageLiveLocation. On success, if the edited " -"message is not an inline message, the edited Message is returned, " -"otherwise True is returned." +"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " +"сообщение не является inline сообщением, возвращается новый объект Message, " +"иначе возвращается True." msgstr "" #: of telebot.TeleBot.edit_message_live_location:5 @@ -1810,25 +2074,30 @@ msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagelivelocation" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagelivelocation" + #: of telebot.TeleBot.edit_message_live_location:7 msgid "Latitude of new location" -msgstr "" +msgstr "Широта нового местоположения" #: of telebot.TeleBot.edit_message_live_location:10 msgid "Longitude of new location" -msgstr "" +msgstr "Долгота нового местоположения" #: of telebot.TeleBot.edit_message_live_location:16 msgid "" "Required if inline_message_id is not specified. Identifier of the message" " to edit" msgstr "" +"Обязательный, если не указан inline_message_id. id сообщения, которое нужно " +"изменить" #: of telebot.TeleBot.edit_message_live_location:19 #: telebot.TeleBot.stop_message_live_location:15 msgid "A JSON-serialized object for a new inline keyboard." -msgstr "" +msgstr "JSON-сериализованный объект новой inline клавиатуры." #: of telebot.TeleBot.edit_message_live_location:26 #: telebot.TeleBot.edit_message_media:16 @@ -1839,29 +2108,36 @@ msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message" msgstr "" +"Обязательный, если не указаны chat_id и message_id. id inline сообщения" #: of telebot.TeleBot.edit_message_live_location:29 #: telebot.TeleBot.send_location:31 msgid "The radius of uncertainty for the location, measured in meters; 0-1500" -msgstr "" +msgstr "Радиус погрешности местоположения, измеряется в метрах; 0-1500" #: of telebot.TeleBot.edit_message_live_location:32 msgid "" "Direction in which the user is moving, in degrees. Must be between 1 and " "360 if specified." msgstr "" +"Направление, в котором пользователь двигается, в градусах. Если указано, должно " +"быть от 1 до 360." #: of telebot.TeleBot.edit_message_live_location:35 msgid "" "The maximum distance for proximity alerts about approaching another chat " "member, in meters. Must be between 1 and 100000 if specified." msgstr "" +"Максимальное расстояние для показа уведомлений о приближении других участников " +"чата, в метрах. Если указано, должно быть от 1 до 100000." #: of telebot.TeleBot.edit_message_live_location:38 msgid "" "On success, if the edited message is not an inline message, the edited " "Message is returned, otherwise True is returned." msgstr "" +"В случае успеха, если измененное сообщение не является inline сообщением, возвращается " +"новый объект Message, иначе возвращается True." #: of telebot.TeleBot.edit_message_live_location:39 #: telebot.TeleBot.stop_message_live_location:23 @@ -1876,16 +2152,24 @@ msgid "" " arbitrarily. When inline message is edited, new file can't be uploaded. " "Use previously uploaded file via its file_id or specify a URL." msgstr "" +"Используйте этот метод, чтобы изменить гифку, аудио, документ, фото или видео " +"в сообщении. Если сообщение является частью альбома, оно может быть " +"изменено только на фото или видео. Иначе, тип сообщения может быть изменен" +" на любой. При изменении inline сообщения, нельзя загрузить новый файл. " +"используйте ранее загруженные файлы через file_id или укажите URL." #: of telebot.TeleBot.edit_message_media:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagemedia" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagemedia" + #: of telebot.TeleBot.edit_message_media:8 msgid "A JSON-serialized object for a new media content of the message" -msgstr "" +msgstr "JSON-сериализованный объект нового медиа контента" #: of telebot.TeleBot.edit_message_media:10 #: telebot.TeleBot.edit_message_reply_markup:5 From 2aaab0851784c68e279cb7d1b41c2f855c3a3778 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 19:49:24 +0000 Subject: [PATCH 1204/1808] Bump wheel from 0.24.0 to 0.38.1 Bumps [wheel](https://github.com/pypa/wheel) from 0.24.0 to 0.38.1. - [Release notes](https://github.com/pypa/wheel/releases) - [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst) - [Commits](https://github.com/pypa/wheel/compare/0.24.0...0.38.1) --- updated-dependencies: - dependency-name: wheel dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d2b82566c..516882869 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest requests==2.20.0 -wheel==0.24.0 +wheel==0.38.1 aiohttp>=3.8.0,<3.9.0 \ No newline at end of file From 2f25b5665968747c66fd73102ef8c3576dfb1057 Mon Sep 17 00:00:00 2001 From: Rudy Ayitinya Sulley Date: Tue, 27 Dec 2022 02:46:07 +0000 Subject: [PATCH 1205/1808] Update README.md Adds a bot to Bots using this library list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4e70e6c6..b8c4a6c84 100644 --- a/README.md +++ b/README.md @@ -877,6 +877,7 @@ Here are some examples of template: * [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development. * [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search. * [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram. +* [Commerce Telegram Bot](https://github.com/ayitinya/commerce-telegram-bot/). Make purchases of items in a store with an Admin panel for data control and notifications. * [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram. * [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English * [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX. From a50a6e2e5434bd1db52ec6a150d9379f9afed826 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Wed, 28 Dec 2022 20:00:06 +0200 Subject: [PATCH 1206/1808] Fixed typehints for register__handler() --- telebot/async_telebot.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1083a2802..b60408b22 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -978,7 +978,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) - def register_message_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + def register_message_handler(self, callback: Callable[[Any], Awaitable], content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message handler. @@ -1090,7 +1090,7 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) - def register_edited_message_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, + def register_edited_message_handler(self, callback: Callable[[Any], Awaitable], content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): """ @@ -1195,7 +1195,7 @@ def add_channel_post_handler(self, handler_dict): """ self.channel_post_handlers.append(handler_dict) - def register_channel_post_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + def register_channel_post_handler(self, callback: Callable[[Any], Awaitable], content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers channel post message handler. @@ -1295,7 +1295,7 @@ def add_edited_channel_post_handler(self, handler_dict): """ self.edited_channel_post_handlers.append(handler_dict) - def register_edited_channel_post_handler(self, callback: Awaitable, content_types: Optional[List[str]]=None, + def register_edited_channel_post_handler(self, callback: Callable[[Any], Awaitable], content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers edited channel post message handler. @@ -1371,7 +1371,7 @@ def add_inline_handler(self, handler_dict): """ self.inline_handlers.append(handler_dict) - def register_inline_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_inline_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers inline handler. @@ -1424,7 +1424,7 @@ def add_chosen_inline_handler(self, handler_dict): """ self.chosen_inline_handlers.append(handler_dict) - def register_chosen_inline_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_chosen_inline_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers chosen inline handler. @@ -1476,7 +1476,7 @@ def add_callback_query_handler(self, handler_dict): """ self.callback_query_handlers.append(handler_dict) - def register_callback_query_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_callback_query_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers callback query handler. @@ -1528,7 +1528,7 @@ def add_shipping_query_handler(self, handler_dict): """ self.shipping_query_handlers.append(handler_dict) - def register_shipping_query_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_shipping_query_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers shipping query handler. @@ -1580,7 +1580,7 @@ def add_pre_checkout_query_handler(self, handler_dict): """ self.pre_checkout_query_handlers.append(handler_dict) - def register_pre_checkout_query_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_pre_checkout_query_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers pre-checkout request handler. @@ -1630,7 +1630,7 @@ def add_poll_handler(self, handler_dict): """ self.poll_handlers.append(handler_dict) - def register_poll_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_poll_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll handler. @@ -1683,7 +1683,7 @@ def add_poll_answer_handler(self, handler_dict): """ self.poll_answer_handlers.append(handler_dict) - def register_poll_answer_handler(self, callback: Awaitable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_poll_answer_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll answer handler. @@ -1736,7 +1736,7 @@ def add_my_chat_member_handler(self, handler_dict): """ self.my_chat_member_handlers.append(handler_dict) - def register_my_chat_member_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): + def register_my_chat_member_handler(self, callback: Callable[[Any], Awaitable], func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers my chat member handler. @@ -1790,7 +1790,7 @@ def add_chat_member_handler(self, handler_dict): """ self.chat_member_handlers.append(handler_dict) - def register_chat_member_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): + def register_chat_member_handler(self, callback: Callable[[Any], Awaitable], func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers chat member handler. @@ -1843,7 +1843,7 @@ def add_chat_join_request_handler(self, handler_dict): """ self.chat_join_request_handlers.append(handler_dict) - def register_chat_join_request_handler(self, callback: Awaitable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + def register_chat_join_request_handler(self, callback: Callable[[Any], Awaitable], func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): """ Registers chat join request handler. From 69afd7232e29b962f5174bc9e1f30c9249559174 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 30 Dec 2022 14:02:02 +0200 Subject: [PATCH 1207/1808] Fixed typehints for reply markup in editing methods --- telebot/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c0a73db6..e58fad5eb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2551,7 +2551,7 @@ def edit_message_live_location( chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, @@ -2607,7 +2607,7 @@ def stop_message_live_location( self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None) -> types.Message or bool: """ Use this method to stop updating a live location message before live_period expires. @@ -3592,7 +3592,7 @@ def edit_message_text( parse_mode: Optional[str]=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]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -3638,7 +3638,7 @@ def edit_message_media( self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -3673,7 +3673,7 @@ def edit_message_reply_markup( self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. @@ -4264,7 +4264,7 @@ def edit_message_caption( inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. From 3812fd05e3b1bffa11a5261b4bf6a5f6f1a0309d Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 30 Dec 2022 14:04:12 +0200 Subject: [PATCH 1208/1808] Fixed typehints for reply markup in editing methods (async) --- telebot/async_telebot.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index b60408b22..d66933be5 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3409,7 +3409,7 @@ async def edit_message_live_location( chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, @@ -3465,7 +3465,7 @@ async def stop_message_live_location( self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None) -> types.Message: """ Use this method to stop updating a live location message before live_period expires. @@ -4458,7 +4458,7 @@ async def edit_message_text( parse_mode: Optional[str]=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]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -4504,7 +4504,7 @@ async def edit_message_media( self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -4539,7 +4539,7 @@ async def edit_message_reply_markup( self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. @@ -5128,7 +5128,7 @@ async def edit_message_caption( inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. From 24cd014410280748252d3e50c034e81a1344306f Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 30 Dec 2022 18:52:00 +0400 Subject: [PATCH 1209/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8c4a6c84..5eb412ce7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.3! +##

Supported Bot API version: 6.4!

Official documentation

From f0a1cefdda5b846ff3bb28c8042215d26b7d09e0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:08:37 +0400 Subject: [PATCH 1210/1808] Added the field is_persistent to the class ReplyKeyboardMarkup Added the field is_persistent to the class ReplyKeyboardMarkup, allowing to control when the keyboard is shown. --- telebot/types.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 5e843d286..b510962f5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2055,13 +2055,21 @@ class ReplyKeyboardMarkup(JsonSerializable): replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard. :type selective: :obj:`bool` + :param is_persistent: Optional. Use this parameter if you want to show the keyboard to specific users only. + Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a + reply (has reply_to_message_id), sender of the original message. + + Example: A user requests to change the bot's language, bot replies to the request with a keyboard to + select the new language. Other users in the group don't see the keyboard. + :return: Instance of the class :rtype: :class:`telebot.types.ReplyKeyboardMarkup` """ max_row_keys = 12 def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, - selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None): + selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None, + is_persistent: Optional[bool]=None): if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases if not DISABLE_KEYLEN_ERROR: @@ -2074,6 +2082,7 @@ def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Opti self.row_width: int = row_width self.input_field_placeholder: str = input_field_placeholder self.keyboard: List[List[KeyboardButton]] = [] + self.is_persistent: bool = is_persistent def add(self, *args, row_width=None): """ @@ -2139,6 +2148,8 @@ def to_json(self): json_dict['selective'] = self.selective if self.input_field_placeholder: json_dict['input_field_placeholder'] = self.input_field_placeholder + if self.is_persistent is not None: + json_dict['is_persistent'] = self.is_persistent return json.dumps(json_dict) From 4d11e97c25b8920492e8e2ac7c525629e8085568 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:20:23 +0400 Subject: [PATCH 1211/1808] has_spoiler parameter Added the parameter has_spoiler to the methods sendPhoto, sendVideo, and sendAnimation. --- telebot/__init__.py | 24 ++++++++++++++++++------ telebot/apihelper.py | 13 ++++++++++--- telebot/async_telebot.py | 24 ++++++++++++++++++------ telebot/asyncio_helper.py | 13 ++++++++++--- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6a4dd7ba1..bf94fd530 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1763,7 +1763,8 @@ def send_photo( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + has_spoiler: Optional[bool]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -1808,6 +1809,9 @@ def send_photo( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + + :param has_spoiler: Pass True, if the photo should be sent as a spoiler + :type has_spoiler: :obj:`bool` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -1821,7 +1825,7 @@ def send_photo( apihelper.send_photo( self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content, message_thread_id)) + allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) # TODO: Rewrite this method like in API. def send_audio( @@ -2171,7 +2175,8 @@ def send_video( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, data: Optional[Union[Any, str]]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + has_spoiler: Optional[bool]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2233,6 +2238,9 @@ def send_video( :param message_thread_id: Identifier of a message thread, in which the video will be sent :type message_thread_id: :obj:`int` + :param has_spoiler: Pass True, if the video should be sent as a spoiler + :type has_spoiler: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2249,7 +2257,7 @@ def send_video( apihelper.send_video( self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, - caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) + caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -2266,7 +2274,8 @@ def send_animation( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + has_spoiler: Optional[bool]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2327,6 +2336,9 @@ def send_animation( :param message_thread_id: Identifier of a message thread, in which the video will be sent :type message_thread_id: :obj:`int` + :param has_spoiler: Pass True, if the animation should be sent as a spoiler + :type has_spoiler: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2339,7 +2351,7 @@ def send_animation( apihelper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply, protect_content, width, height, message_thread_id)) + caption_entities, allow_sending_without_reply, protect_content, width, height, message_thread_id, has_spoiler)) # TODO: Rewrite this method like in API. def send_video_note( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b25e0365e..005ac337e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -459,7 +459,7 @@ def send_photo( caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + message_thread_id=None, has_spoiler=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -489,6 +489,8 @@ def send_photo( payload['protect_content'] = protect_content if message_thread_id is not None: payload['message_thread_id'] = message_thread_id + if has_spoiler is not None: + payload['has_spoiler'] = has_spoiler return _make_request(token, method_url, params=payload, files=files, method='post') @@ -666,7 +668,7 @@ def send_chat_action(token, chat_id, action, timeout=None): def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + message_thread_id=None, has_spoiler=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -710,13 +712,16 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if has_spoiler is not None: + payload['has_spoiler'] = has_spoiler return _make_request(token, method_url, params=payload, files=files, method='post') def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, protect_content=None, width=None, height=None, message_thread_id=None): + allow_sending_without_reply=None, protect_content=None, width=None, height=None, message_thread_id=None, + has_spoiler=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -758,6 +763,8 @@ def send_animation( payload['height'] = height if message_thread_id: payload['message_thread_id'] = message_thread_id + if has_spoiler is not None: + payload['has_spoiler'] = has_spoiler return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e8249fc86..bbaeb539c 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2627,7 +2627,8 @@ async def send_photo( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + has_spoiler: Optional[bool]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2672,6 +2673,9 @@ async def send_photo( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + + :param has_spoiler: Pass True, if the photo should be sent as a spoiler + :type has_spoiler: :obj:`bool` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2685,7 +2689,7 @@ async def send_photo( await asyncio_helper.send_photo( self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content, message_thread_id)) + allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -3031,7 +3035,8 @@ async def send_video( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, data: Optional[Union[Any, str]]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + has_spoiler: Optional[bool]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -3093,6 +3098,9 @@ async def send_video( :param message_thread_id: Identifier of a message thread, in which the video will be sent :type message_thread_id: :obj:`int` + :param has_spoiler: Pass True, if the video should be sent as a spoiler + :type has_spoiler: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3110,7 +3118,7 @@ async def send_video( await asyncio_helper.send_video( self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, - caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) + caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -3127,7 +3135,8 @@ async def send_animation( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + has_spoiler: Optional[bool]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -3188,6 +3197,9 @@ async def send_animation( :param message_thread_id: Identifier of a message thread, in which the video will be sent :type message_thread_id: :obj:`int` + :param has_spoiler: Pass True, if the animation should be sent as a spoiler + :type has_spoiler: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3200,7 +3212,7 @@ async def send_animation( await asyncio_helper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup, parse_mode, disable_notification, timeout, thumb, - caption_entities, allow_sending_without_reply, width, height, protect_content, message_thread_id)) + caption_entities, allow_sending_without_reply, width, height, protect_content, message_thread_id, has_spoiler)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 9f5b59a71..2ae19341d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -453,7 +453,7 @@ async def send_photo( caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + message_thread_id=None, has_spoiler=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -483,6 +483,8 @@ async def send_photo( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if has_spoiler is not None: + payload['has_spoiler'] = has_spoiler return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -658,7 +660,7 @@ async def send_chat_action(token, chat_id, action, timeout=None): async def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, - protect_content=None, message_thread_id=None): + protect_content=None, message_thread_id=None, has_spoiler=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -702,13 +704,16 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if has_spoiler is not None: + payload['has_spoiler'] = has_spoiler return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, width=None, height=None, protect_content=None, message_thread_id=None): + allow_sending_without_reply=None, width=None, height=None, protect_content=None, message_thread_id=None, + has_spoiler=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -750,6 +755,8 @@ async def send_animation( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if has_spoiler is not None: + payload['has_spoiler'] = has_spoiler return await _process_request(token, method_url, params=payload, files=files, method='post') From 4537b237c80ce7f257c721dffdd4e554e62dc32e Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:27:38 +0400 Subject: [PATCH 1212/1808] has_spoiler for types.py Added the field has_spoiler to the classes InputMediaPhoto, InputMediaVideo, and InputMediaAnimation. --- telebot/types.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index b510962f5..bf1c8b153 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5792,18 +5792,26 @@ class InputMediaPhoto(InputMedia): instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param has_spoiler: Optional. True, if the uploaded photo is a spoiler + :type has_spoiler: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InputMediaPhoto` """ - def __init__(self, media, caption=None, parse_mode=None, caption_entities=None): + def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, has_spoiler=None): if util.is_pil_image(media): media = util.pil_image_to_file(media) super(InputMediaPhoto, self).__init__( type="photo", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) + self.has_spoiler: Optional[bool] = has_spoiler + def to_dict(self): - return super(InputMediaPhoto, self).to_dict() + ret = super(InputMediaPhoto, self).to_dict() + if self.has_spoiler: + ret['has_spoiler'] = self.has_spoiler + return ret class InputMediaVideo(InputMedia): @@ -5847,11 +5855,14 @@ class InputMediaVideo(InputMedia): :param supports_streaming: Optional. Pass True, if the uploaded video is suitable for streaming :type supports_streaming: :obj:`bool` + :param has_spoiler: Optional. True, if the uploaded video is a spoiler + :type has_spoiler: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InputMediaVideo` """ def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, - width=None, height=None, duration=None, supports_streaming=None): + width=None, height=None, duration=None, supports_streaming=None, has_spoiler=None): super(InputMediaVideo, self).__init__( type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumb = thumb @@ -5859,6 +5870,7 @@ def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_ent self.height = height self.duration = duration self.supports_streaming = supports_streaming + self.has_spoiler: Optional[bool] = has_spoiler def to_dict(self): ret = super(InputMediaVideo, self).to_dict() @@ -5872,6 +5884,8 @@ def to_dict(self): ret['duration'] = self.duration if self.supports_streaming: ret['supports_streaming'] = self.supports_streaming + if self.has_spoiler: + ret['has_spoiler'] = self.has_spoiler return ret @@ -5913,17 +5927,21 @@ class InputMediaAnimation(InputMedia): :param duration: Optional. Animation duration in seconds :type duration: :obj:`int` + :param has_spoiler: Optional. True, if the uploaded animation is a spoiler + :type has_spoiler: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAnimation` """ def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, - width=None, height=None, duration=None): + width=None, height=None, duration=None, has_spoiler=None): super(InputMediaAnimation, self).__init__( type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumb = thumb self.width = width self.height = height self.duration = duration + self.has_spoiler: Optional[bool] = has_spoiler def to_dict(self): ret = super(InputMediaAnimation, self).to_dict() @@ -5935,6 +5953,8 @@ def to_dict(self): ret['height'] = self.height if self.duration: ret['duration'] = self.duration + if self.has_spoiler: + ret['has_spoiler'] = self.has_spoiler return ret From 9f5d9861a489fa2d1e924e0556ca4a74010aac3d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:41:46 +0400 Subject: [PATCH 1213/1808] Added the field has_media_spoiler to the class Message. Added the field has_media_spoiler to the class Message. --- telebot/types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index bf1c8b153..266f80cfd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -812,6 +812,9 @@ class Message(JsonDeserializable): commands, etc. that appear in the caption :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation + :type has_media_spoiler: :obj:`bool` + :param contact: Optional. Message is a shared contact, information about the contact :type contact: :class:`telebot.types.Contact` @@ -1110,6 +1113,8 @@ def de_json(cls, json_string): if 'forum_topic_reopened' in obj: opts['forum_topic_reopened'] = ForumTopicReopened.de_json(obj['forum_topic_reopened']) content_type = 'forum_topic_reopened' + if 'has_media_spoiler' in obj: + opts['has_media_spoiler'] = obj['has_media_spoiler'] return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1200,6 +1205,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.forum_topic_created: Optional[ForumTopicCreated] = None self.forum_topic_closed: Optional[ForumTopicClosed] = None self.forum_topic_reopened: Optional[ForumTopicReopened] = None + self.has_media_spoiler: Optional[bool] = None for key in options: setattr(self, key, options[key]) self.json = json_string From 107f92314bcd161c7d31a7bad621af13b43d4343 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 19:50:14 +0400 Subject: [PATCH 1214/1808] icon_custom_emoji_id and name parameters made optional for edit_forum_topic The parameters name and icon_custom_emoji_id of the method editForumTopic are now optional. If they are omitted, the existing values are kept. --- telebot/__init__.py | 11 +++++++---- telebot/apihelper.py | 8 ++++++-- telebot/async_telebot.py | 11 +++++++---- telebot/asyncio_helper.py | 8 ++++++-- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index bf94fd530..95ec9364a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4648,8 +4648,8 @@ def create_forum_topic(self, def edit_forum_topic( self, chat_id: Union[int, str], - message_thread_id: int, name: str, - icon_custom_emoji_id: str, + message_thread_id: int, name: Optional[str]=None, + icon_custom_emoji_id: Optional[str]=None ) -> bool: """ Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an @@ -4664,10 +4664,13 @@ def edit_forum_topic( :param message_thread_id: Identifier of the topic to edit :type message_thread_id: :obj:`int` - :param name: New name of the topic, 1-128 characters + :param name: Optional, New name of the topic, 1-128 characters. If not specififed or empty, + the current name of the topic will be kept :type name: :obj:`str` - :param icon_custom_emoji_id: New custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long + :param icon_custom_emoji_id: Optional, New unique identifier of the custom emoji shown as the topic icon. + Use getForumTopicIconStickers to get all allowed custom emoji identifiers. Pass an empty string to remove the + icon. If not specified, the current icon will be kept :type icon_custom_emoji_id: :obj:`str` :return: On success, True is returned. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 005ac337e..4f5670578 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1773,9 +1773,13 @@ def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_emoji_ payload['icon_custom_emoji_id'] = icon_custom_emoji_id return _make_request(token, method_url, params=payload) -def edit_forum_topic(token, chat_id, message_thread_id, name, icon_custom_emoji_id): +def edit_forum_topic(token, chat_id, message_thread_id, name=None, icon_custom_emoji_id=None): method_url = r'editForumTopic' - payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id, 'name': name, 'icon_custom_emoji_id': icon_custom_emoji_id} + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + if name is not None: + payload['name'] = name + if icon_custom_emoji_id is not None: + payload['icon_custom_emoji_id'] = icon_custom_emoji_id return _make_request(token, method_url, params=payload) def close_forum_topic(token, chat_id, message_thread_id): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index bbaeb539c..65accb8dc 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5514,8 +5514,8 @@ async def create_forum_topic(self, async def edit_forum_topic( self, chat_id: Union[int, str], - message_thread_id: int, name: str, - icon_custom_emoji_id: str, + message_thread_id: int, name: Optional[str]=None, + icon_custom_emoji_id: Optional[str]=None ) -> bool: """ Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an @@ -5530,10 +5530,13 @@ async def edit_forum_topic( :param message_thread_id: Identifier of the topic to edit :type message_thread_id: :obj:`int` - :param name: New name of the topic, 1-128 characters + :param name: Optional, New name of the topic, 1-128 characters. If not specififed or empty, + the current name of the topic will be kept :type name: :obj:`str` - :param icon_custom_emoji_id: New custom emoji for the topic icon. Must be an emoji of type “tgs” and must be exactly 1 character long + :param icon_custom_emoji_id: Optional, New unique identifier of the custom emoji shown as the topic icon. + Use getForumTopicIconStickers to get all allowed custom emoji identifiers. Pass an empty string to remove the + icon. If not specified, the current icon will be kept :type icon_custom_emoji_id: :obj:`str` :return: On success, True is returned. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 2ae19341d..8bbdc3daa 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1764,9 +1764,13 @@ async def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_ payload['icon_custom_emoji_id'] = icon_custom_emoji_id return await _process_request(token, method_url, params=payload) -async def edit_forum_topic(token, chat_id, message_thread_id, name, icon_custom_emoji_id): +async def edit_forum_topic(token, chat_id, message_thread_id, name=None, icon_custom_emoji_id=None): method_url = r'editForumTopic' - payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id, 'name': name, 'icon_custom_emoji_id': icon_custom_emoji_id} + payload = {'chat_id': chat_id, 'message_thread_id': message_thread_id} + if name is not None: + payload['name'] = name + if icon_custom_emoji_id is not None: + payload['icon_custom_emoji_id'] = icon_custom_emoji_id return await _process_request(token, method_url, params=payload) async def close_forum_topic(token, chat_id, message_thread_id): From a20a3ae32179489fc7d2d345316c891a830c1cdd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:07:38 +0400 Subject: [PATCH 1215/1808] topic events and write_access_allowed Added the classes ForumTopicEdited, GeneralForumTopicHidden, GeneralForumTopicUnhidden, and WriteAccessAllowed and the fields forum_topic_edited, general_forum_topic_hidden, general_forum_topic_unhidden, and write_access_allowed to the class Message. --- telebot/types.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 266f80cfd..015947769 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -895,6 +895,10 @@ class Message(JsonDeserializable): Telegram Login » :type connected_website: :obj:`str` + :param write_access_allowed: Optional. Service message: the user allowed the bot added to the attachment + menu to write messages + :type write_access_allowed: :class:`telebot.types.WriteAccessAllowed` + :param passport_data: Optional. Telegram Passport data :type passport_data: :class:`telebot.types.PassportData` @@ -905,12 +909,21 @@ class Message(JsonDeserializable): :param forum_topic_created: Optional. Service message: forum topic created :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` + :param forum_topic_edited: Optional. Service message: forum topic edited + :type forum_topic_edited: :class:`telebot.types.ForumTopicEdited` + :param forum_topic_closed: Optional. Service message: forum topic closed :type forum_topic_closed: :class:`telebot.types.ForumTopicClosed` :param forum_topic_reopened: Optional. Service message: forum topic reopened :type forum_topic_reopened: :class:`telebot.types.ForumTopicReopened` + :param general_forum_topic_hidden: Optional. Service message: the 'General' forum topic hidden + :type general_forum_topic_hidden: :class:`telebot.types.GeneralForumTopicHidden` + + :param general_forum_topic_unhidden: Optional. Service message: the 'General' forum topic unhidden + :type general_forum_topic_unhidden: :class:`telebot.types.GeneralForumTopicUnhidden` + :param video_chat_scheduled: Optional. Service message: video chat scheduled :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` @@ -1115,6 +1128,18 @@ def de_json(cls, json_string): content_type = 'forum_topic_reopened' if 'has_media_spoiler' in obj: opts['has_media_spoiler'] = obj['has_media_spoiler'] + if 'forum_topic_edited' in obj: + opts['forum_topic_edited'] = ForumTopicEdited.de_json(obj['forum_topic_edited']) + content_type = 'forum_topic_edited' + if 'general_forum_topic_hidden' in obj: + opts['general_forum_topic_hidden'] = GeneralForumTopicHidden.de_json(obj['general_forum_topic_hidden']) + content_type = 'general_forum_topic_hidden' + if 'general_forum_topic_unhidden' in obj: + opts['general_forum_topic_unhidden'] = GeneralForumTopicUnhidden.de_json(obj['general_forum_topic_unhidden']) + content_type = 'general_forum_topic_unhidden' + if 'write_access_allowed' in obj: + opts['write_access_allowed'] = WriteAccessAllowed.de_json(obj['write_access_allowed']) + content_type = 'write_access_allowed' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1206,6 +1231,10 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.forum_topic_closed: Optional[ForumTopicClosed] = None self.forum_topic_reopened: Optional[ForumTopicReopened] = None self.has_media_spoiler: Optional[bool] = None + self.forum_topic_edited: Optional[ForumTopicEdited] = None + self.general_forum_topic_hidden: Optional[GeneralForumTopicHidden] = None + self.general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = None + self.write_access_allowed: Optional[WriteAccessAllowed] = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -6866,6 +6895,61 @@ def de_json(cls, json_string): def __init__(self) -> None: pass +class ForumTopicEdited(JsonDeserializable): + """ + This object represents a service message about an edited forum topic. + + Telegram documentation: https://core.telegram.org/bots/api#forumtopicedited + + :param name: Optional, Name of the topic(if updated) + :type name: :obj:`str` + + :param icon_custom_emoji_id: Optional. New identifier of the custom emoji shown as the topic icon, if it was edited; + an empty string if the icon was removed + :type icon_custom_emoji_id: :obj:`str` + """ + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + def __init__(self, name: Optional[str]=None, icon_custom_emoji_id: Optional[str]=None) -> None: + self.name: Optional[str] = name + self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id + + +class GeneralForumTopicHidden(JsonDeserializable): + """ + This object represents a service message about General forum topic hidden in the chat. + Currently holds no information. + + Telegram documentation: https://core.telegram.org/bots/api#generalforumtopichidden + """ + @classmethod + def de_json(cls, json_string): + return cls() + + def __init__(self) -> None: + pass + + +class GeneralForumTopicUnhidden(JsonDeserializable): + """ + This object represents a service message about General forum topic unhidden in the chat. + Currently holds no information. + + Telegram documentation: https://core.telegram.org/bots/api#generalforumtopicunhidden + """ + + @classmethod + def de_json(cls, json_string): + return cls() + + def __init__(self) -> None: + pass + + class ForumTopic(JsonDeserializable): """ @@ -6902,6 +6986,19 @@ def __init__(self, message_thread_id: int, name: str, icon_color: int, icon_cust self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id +class WriteAccessAllowed(JsonDeserializable): + """ + This object represents a service message about a user allowed to post messages in the chat. + Currently holds no information. + + Telegram documentation: https://core.telegram.org/bots/api#writeaccessallowed + """ + @classmethod + def de_json(cls, json_string): + return cls() + + def __init__(self) -> None: + pass From f297ad23c7511f50223a46c9fe00384a13ad2abd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:19:50 +0400 Subject: [PATCH 1216/1808] Added methods for topic management Added the methods editGeneralForumTopic, closeGeneralForumTopic, reopenGeneralForumTopic, hideGeneralForumTopic, unhideGeneralForumTopic for managing the General topic in forums. --- telebot/__init__.py | 69 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 25 ++++++++++++++ telebot/async_telebot.py | 69 +++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 25 ++++++++++++++ 4 files changed, 188 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 95ec9364a..fa0c11b82 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4754,6 +4754,75 @@ def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message_threa """ return apihelper.unpin_all_forum_topic_messages(self.token, chat_id, message_thread_id) + def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool: + """ + Use this method to edit the name of the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#editgeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param name: New topic name, 1-128 characters + :type name: :obj:`str` + """ + + return apihelper.edit_general_forum_topic(self.token, chat_id, name) + + def close_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to close the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#closegeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return apihelper.close_general_forum_topic(self.token, chat_id) + + def reopen_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to reopen the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#reopengeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return apihelper.reopen_general_forum_topic(self.token, chat_id) + + def hide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to hide the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#hidegeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return apihelper.hide_general_forum_topic(self.token, chat_id) + + def unhide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to unhide the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unhidegeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return apihelper.unhide_general_forum_topic(self.token, chat_id) + def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: """ Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4f5670578..f1fdb7cfb 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1813,6 +1813,31 @@ def stop_poll(token, chat_id, message_id, reply_markup=None): payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) +def edit_general_forum_topic(token, chat_id, name): + method_url = r'editGeneralForumTopic' + payload = {'chat_id': chat_id, 'name': name} + return _make_request(token, method_url, params=payload) + +def close_general_forum_topic(token, chat_id): + method_url = r'closeGeneralForumTopic' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + +def reopen_general_forum_topic(token, chat_id): + method_url = r'reopenGeneralForumTopic' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + +def hide_general_forum_topic(token, chat_id): + method_url = r'hideGeneralForumTopic' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + +def unhide_general_forum_topic(token, chat_id): + method_url = r'unhideGeneralForumTopic' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload) + def _convert_list_json_serializable(results): ret = '' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 65accb8dc..0ee8f0623 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5620,6 +5620,75 @@ async def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message """ return await asyncio_helper.unpin_all_forum_topic_messages(self.token, chat_id, message_thread_id) + async def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool: + """ + Use this method to edit the name of the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#editgeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param name: New topic name, 1-128 characters + :type name: :obj:`str` + """ + + return await asyncio_helper.edit_general_forum_topic(self.token, chat_id, name) + + async def close_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to close the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#closegeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return await asyncio_helper.close_general_forum_topic(self.token, chat_id) + + async def reopen_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to reopen the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#reopengeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return await asyncio_helper.reopen_general_forum_topic(self.token, chat_id) + + async def hide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to hide the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#hidegeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return await asyncio_helper.hide_general_forum_topic(self.token, chat_id) + + async def unhide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to unhide the 'General' topic in a forum supergroup chat. + The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unhidegeneralforumtopic + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + """ + return await asyncio_helper.unhide_general_forum_topic(self.token, chat_id) + async def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: """ Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 8bbdc3daa..86468d04c 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1797,6 +1797,31 @@ async def get_forum_topic_icon_stickers(token): method_url = r'getForumTopicIconStickers' return await _process_request(token, method_url) +async def edit_general_forum_topic(token, chat_id, name): + method_url = r'editGeneralForumTopic' + payload = {'chat_id': chat_id, 'name': name} + return await _process_request(token, method_url, params=payload) + +async def close_general_forum_topic(token, chat_id): + method_url = r'closeGeneralForumTopic' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + +async def reopen_general_forum_topic(token, chat_id): + method_url = r'reopenGeneralForumTopic' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + +async def hide_general_forum_topic(token, chat_id): + method_url = r'hideGeneralForumTopic' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + +async def unhide_general_forum_topic(token, chat_id): + method_url = r'unhideGeneralForumTopic' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload) + async def _convert_list_json_serializable(results): ret = '' for r in results: From 9f8256607afa58472b78eb1cb46fdd0a87abf330 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:23:53 +0400 Subject: [PATCH 1217/1808] Added the parameter message_thread_id to the method sendChatAction for sending chat actions to a specific message thread or a forum topic. Added the parameter message_thread_id to the method sendChatAction for sending chat actions to a specific message thread or a forum topic. --- telebot/__init__.py | 7 +++++-- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 7 +++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fa0c11b82..ef8ab7146 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2806,7 +2806,7 @@ def send_contact( allow_sending_without_reply, protect_content, message_thread_id)) def send_chat_action( - self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: + self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). @@ -2829,10 +2829,13 @@ def send_chat_action( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param message_thread_id: The thread identifier of a message from which the reply will be sent(supergroups only) + :type message_thread_id: :obj:`int` + :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.send_chat_action(self.token, chat_id, action, timeout) + return apihelper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id) @util.deprecated(deprecation_text="Use ban_chat_member instead") def kick_chat_member( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f1fdb7cfb..1086f54e9 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -657,11 +657,13 @@ def send_contact( return _make_request(token, method_url, params=payload) -def send_chat_action(token, chat_id, action, timeout=None): +def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None): method_url = r'sendChatAction' payload = {'chat_id': chat_id, 'action': action} if timeout: payload['timeout'] = timeout + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id return _make_request(token, method_url, params=payload) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0ee8f0623..6e3ec9ad7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3664,7 +3664,7 @@ async def send_contact( ) async def send_chat_action( - self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None) -> bool: + self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). @@ -3687,10 +3687,13 @@ async def send_chat_action( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param message_thread_id: The thread to which the message will be sent(supergroups only) + :type message_thread_id: :obj:`int` + :return: Returns True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout) + return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id) async def kick_chat_member( self, chat_id: Union[int, str], user_id: int, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 86468d04c..437188566 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -649,11 +649,13 @@ async def send_contact( return await _process_request(token, method_url, params=payload) -async def send_chat_action(token, chat_id, action, timeout=None): +async def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None): method_url = r'sendChatAction' payload = {'chat_id': chat_id, 'action': action} if timeout: payload['timeout'] = timeout + if message_thread_id: + payload['message_thread_id'] = message_thread_id return await _process_request(token, method_url, params=payload) From eed56be59645b9d3c8c9c24982ace3e81c63ef56 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 30 Dec 2022 20:38:26 +0400 Subject: [PATCH 1218/1808] Added fields has_hidden_members and has_aggressive_anti_spam_enabled to class Chat --- telebot/types.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 015947769..efcf6de94 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -570,6 +570,14 @@ class Chat(JsonDeserializable): automatically deleted; in seconds. Returned only in getChat. :type message_auto_delete_time: :obj:`int` + :param has_aggressive_anti_spam_enabled: Optional. :obj:`bool`, if the chat has enabled aggressive anti-spam + protection. Returned only in getChat. + :type has_aggressive_anti_spam_enabled: :obj:`bool` + + :param has_hidden_members: Optional. :obj:`bool`, if the chat has enabled hidden members. Returned only in + getChat. + :type has_hidden_members: :obj:`bool` + :param has_protected_content: Optional. :obj:`bool`, if messages from the chat can't be forwarded to other chats. Returned only in getChat. :type has_protected_content: :obj:`bool` @@ -615,7 +623,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, message_auto_delete_time=None, has_protected_content=None, sticker_set_name=None, can_set_sticker_set=None, linked_chat_id=None, location=None, join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None, - is_forum=None, active_usernames=None, emoji_status_custom_emoji_id=None, **kwargs): + is_forum=None, active_usernames=None, emoji_status_custom_emoji_id=None, + has_hidden_members=None, has_aggressive_anti_spam_enabled=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -642,6 +651,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.location: ChatLocation = location self.active_usernames: List[str] = active_usernames self.emoji_status_custom_emoji_id: str = emoji_status_custom_emoji_id + self.has_hidden_members: bool = has_hidden_members + self.has_aggressive_anti_spam_enabled: bool = has_aggressive_anti_spam_enabled class MessageID(JsonDeserializable): From 19544ecc58d85ff470107b0e07c95d536d43240c Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 30 Dec 2022 21:24:13 +0200 Subject: [PATCH 1219/1808] Copied translations from sync_version.po to async_version.po Minor fixes in sync_version.po, found while copying --- .../locales/ru/LC_MESSAGES/async_version.po | 511 ++++++++++++++---- .../locales/ru/LC_MESSAGES/sync_version.po | 14 +- 2 files changed, 401 insertions(+), 124 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index f731d317e..de4ce541e 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -24,15 +24,15 @@ msgstr "" #: ../../async_version/index.rst:6 msgid "Asynchronous pyTelegramBotAPI" -msgstr "" +msgstr "Асинхронный pyTelegramBotAPI" #: ../../async_version/index.rst:6 msgid "ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation" -msgstr "" +msgstr "ptba, pytba, pyTelegramBotAPI, asynctelebot, документация" #: ../../async_version/index.rst:12 msgid "AsyncTeleBot methods" -msgstr "" +msgstr "Методы класса AsyncTeleBot" #: of telebot.async_telebot.AsyncTeleBot:1 #: telebot.async_telebot.ExceptionHandler:1 telebot.async_telebot.Handler:1 @@ -45,33 +45,35 @@ msgstr "" #: telebot.asyncio_handler_backends.StatesGroup:1 #: telebot.ext.aio.webhooks.AsyncWebhookListener:1 msgid "Bases: :py:class:`object`" -msgstr "" +msgstr "Базовые классы: :py:class:`object`" #: of telebot.async_telebot.AsyncTeleBot:1 msgid "This is the main asynchronous class for Bot." -msgstr "" +msgstr "Это основной класс для асинхронного бота." #: of telebot.async_telebot.AsyncTeleBot:3 msgid "It allows you to add handlers for different kind of updates." -msgstr "" +msgstr "Позволяет добавить хендлеры для различных апдейтов." #: of telebot.async_telebot.AsyncTeleBot:5 msgid "Usage:" -msgstr "" +msgstr "Использование:" #: of telebot.async_telebot.AsyncTeleBot:7 msgid "Using asynchronous implementation of TeleBot." -msgstr "" +msgstr "Использование асинхронной реализации TeleBot-а." #: of telebot.async_telebot.AsyncTeleBot:16 msgid "" "See more examples in examples/ directory: " "https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" msgstr "" +"Больше примеров в папке examples/ : " +"https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples" #: of telebot.async_telebot.AsyncTeleBot:21 msgid "Install coloredlogs module to specify colorful_logs=True" -msgstr "" +msgstr "Установите пакет coloredlogs для использования colorful_los=True" #: of telebot.async_telebot.AsyncTeleBot #: telebot.async_telebot.AsyncTeleBot.add_custom_filter @@ -215,58 +217,58 @@ msgid "Parameters" msgstr "" #: of telebot.async_telebot.AsyncTeleBot:24 -msgid "Token of a bot, obtained from @BotFather" -msgstr "" +msgid "Token of a bot, should be obtained from @BotFather" +msgstr "Токен бота, нужно получить от @BotFather" #: of telebot.async_telebot.AsyncTeleBot:27 msgid "Default parse mode, defaults to None" -msgstr "" +msgstr "Глобальный parse_mode, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot:30 msgid "Offset used in get_updates, defaults to None" -msgstr "" +msgstr "Смещение, используемое в get_updates, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot:33 msgid "" "Exception handler, which will handle the exception occured, defaults to " "None" -msgstr "" +msgstr "Класс для обработки исключений, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot:36 msgid "Storage for states, defaults to StateMemoryStorage()" -msgstr "" +msgstr "Хранилище состояний (стейтов), по умолчанию StateMemoryStorage()" #: of telebot.async_telebot.AsyncTeleBot:39 msgid "Default value for disable_web_page_preview, defaults to None" -msgstr "" +msgstr "Глобальное значение disable_web_page_preview, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot:42 msgid "Default value for disable_notification, defaults to None" -msgstr "" +msgstr "Глобальное значение disable_notification, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot:45 msgid "Default value for protect_content, defaults to None" -msgstr "" +msgstr "Глобальное значение protect_content, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot:48 msgid "Default value for allow_sending_without_reply, defaults to None" -msgstr "" +msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot:51 msgid "Outputs colorful logs" -msgstr "" +msgstr "Использовать разноцветные логи" #: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:1 msgid "Create custom filter." -msgstr "" +msgstr "Создать кастомный фильтр." #: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:3 msgid "Example on checking the text of a message" -msgstr "" +msgstr "Пример проверки текста сообщения" #: of telebot.async_telebot.AsyncTeleBot.add_custom_filter:12 msgid "Class with check(message) method." -msgstr "" +msgstr "Класс с методом check(message)" #: of telebot.async_telebot.AsyncTeleBot.add_custom_filter #: telebot.async_telebot.AsyncTeleBot.add_data @@ -448,7 +450,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.add_data:1 msgid "Add data to states." -msgstr "" +msgstr "Добавить данные в состояние (стейт)." #: of telebot.async_telebot.AsyncTeleBot.add_data:3 #: telebot.async_telebot.AsyncTeleBot.delete_state:3 @@ -456,7 +458,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.reset_data:3 #: telebot.async_telebot.AsyncTeleBot.set_state:9 msgid "User's identifier" -msgstr "" +msgstr "id пользователя" #: of telebot.async_telebot.AsyncTeleBot.add_data:6 #: telebot.async_telebot.AsyncTeleBot.delete_state:6 @@ -464,37 +466,40 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.reset_data:6 #: telebot.async_telebot.AsyncTeleBot.set_state:15 msgid "Chat's identifier" -msgstr "" +msgstr "id чата" #: of telebot.async_telebot.AsyncTeleBot.add_data:9 msgid "Data to add" -msgstr "" +msgstr "Данные для добавления" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:1 msgid "" "Use this method to add a new sticker to a set created by the bot. It's " "required to pass `png_sticker` or `tgs_sticker`. Returns True on success." msgstr "" +"Используйте этот метод, чтобы добавить новый стикер в стикерпак, созданный ботом. Необходимо " +"передать либо `png_sticker`, либо `tgs_sticker`, либо `webm_sticker`. " +"Возвращает True в случае успешного добавления." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#addstickertoset" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:7 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:7 msgid "User identifier of created sticker set owner" -msgstr "" +msgstr "id пользователя, создавшего стикерпак" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:10 #: telebot.async_telebot.AsyncTeleBot.get_sticker_set:5 #: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:6 msgid "Sticker set name" -msgstr "" +msgstr "Имя стикерпака" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:13 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:18 msgid "One or more emoji corresponding to the sticker" -msgstr "" +msgstr "Один или несколько эмодзи, относящихся к стикеру" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:16 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:21 @@ -506,16 +511,22 @@ msgid "" " to get a file from the Internet, or upload a new one using multipart" "/form-data." msgstr "" +"Изображение стикера в формате PNG, весом не более 512 килобайт, " +"размеры не должны превышать 512px, либо ширина, либо высота должны быть " +"ровно 512px. Передайте file_id в формате str, чтобы отправить уже загруженный " +"на сервера Telegram файл, передайте HTTP URL в формате str, чтобы Telegram " +"скачал файл из интернета, или загрузите новый файл с помощью multipart/form-data." + #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:26 msgid "TGS animation with the sticker, uploaded using multipart/form-data." -msgstr "" +msgstr "Анимированный стикер в формате TGS, загруженный с помощью multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:24 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 msgid "WebM animation with the sticker, uploaded using multipart/form-data." -msgstr "" +msgstr "Анимированный стикер в формате WebM, загруженный с помощью multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:27 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:40 @@ -523,6 +534,7 @@ msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" msgstr "" +"Позиция для размещения маски на лицах в формате JSON" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:30 #: telebot.async_telebot.AsyncTeleBot.answer_callback_query:22 @@ -539,7 +551,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:13 msgid "On success, True is returned." -msgstr "" +msgstr "В случае успеха возвращается True." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set #: telebot.async_telebot.AsyncTeleBot.answer_callback_query @@ -690,31 +702,37 @@ msgid "" "keyboards. The answer will be displayed to the user as a notification at " "the top of the chat screen or as an alert." msgstr "" +"Используйте этот метод для отправки ответов на callback запросы, отправленные с помощью inline " +"кнопок. Ответ будет показан пользователю как уведомление поверх чата или pop-up предупреждение." #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answercallbackquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answercallbackquery" #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:6 #: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:10 #: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:5 #: telebot.async_telebot.AsyncTeleBot.answer_web_app_query:8 msgid "Unique identifier for the query to be answered" -msgstr "" +msgstr "Уникальный id запроса для ответа" #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:9 msgid "" "Text of the notification. If not specified, nothing will be shown to the " "user, 0-200 characters" msgstr "" +"Текст уведомления. если не задан, то уведомление не будет показано, 0-200 символов" #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:12 msgid "" "If True, an alert will be shown by the client instead of a notification " "at the top of the chat screen. Defaults to false." msgstr "" +"Если True, вместо уведомления поверх чата будет показано pop-up предупреждение, по умолчанию False." #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:15 msgid "" @@ -723,6 +741,10 @@ msgid "" "your game - note that this will only work if the query comes from a " "callback_game button." msgstr "" +"URL, который будет открыт пользовательским клиентом. Если вы создали игру " +"и приняли условия через @BotFather, задайте URL, открывающий вашу игру " +"- учитывайте, что это сработает только если запрос был отправлен с помощью " +"callback_game кнопки." #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:19 msgid "" @@ -730,44 +752,55 @@ msgid "" "query may be cached client-side. Telegram apps will support caching " "starting in version 3.14. Defaults to 0." msgstr "" +"Максимальная длительность хранения ответа на callback запрос пользовательским клиентом в секундах. " +"Приложения Telegram поддерживают хранение ответов начиная с версии 3.14, по умолчанию 0." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:1 msgid "" "Use this method to send answers to an inline query. On success, True is " "returned. No more than 50 results per query are allowed." msgstr "" +"Используйте этот метод для отправки ответов на inline запрос. В случае успеха возвращается True. " +"Разрешено отправить не более 50 результатов на один запрос." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answerinlinequery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answerinlinequery" #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:6 msgid "Unique identifier for the answered query" -msgstr "" +msgstr "Уникальный id запроса для ответа" #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:9 msgid "Array of results for the inline query" -msgstr "" +msgstr "Массив результатов для ответа на inline запрос" #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:12 msgid "" "The maximum amount of time in seconds that the result of the inline query" " may be cached on the server." msgstr "" +"Максимальная длительность хранения результатов inline запроса на сервере в секундах." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:16 msgid "" "Pass True, if results may be cached on the server side only for the user " "that sent the query." msgstr "" +"Передайте True, если результаты должны быть сохранены на сервере только для пользователя, " +"отправившего запрос." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:20 msgid "" "Pass the offset that a client should send in the next query with the same" " text to receive more results." msgstr "" +"Передайте смещение, которое клиент должен отправить в следующем запросе с таким же " +"текстом, чтобы получить новые результаты." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:24 msgid "" @@ -783,12 +816,23 @@ msgid "" "easily return to the chat where they wanted to use the bot's inline " "capabilities." msgstr "" +"Параметр для команды /start, отправляемой боту, когда пользователь " +"нажимает кнопку переключения. 1-64 символа, разрешены только A-Z, a-z, 0-9, _ и -. " +"Пример: Inline бот, который отправляет видео с YouTube может попросить " +"пользователя подключить бота к его YouTube аккаунту, чтобы поиск соответствовал " +"предпочтениям пользователя. Чтобы это сделать, бот отправляет пользователю кнопку " +"'Подключить YouTube аккаунт' над результатами, или даже до их показа. Пользователь нажимает " +"на кнопку, автоматически переходит в приватный чат с ботом и в это время " +"передаёт стартовый параметр, по которому бот возвращает ссылку для авторизации (OAuth). " +"Как только авторизация пройдена, бот может предложить switch_inline кнопку, чтобы пользователь мог " +"легко вернуться в чат, где он хотел использовать возможности inline бота." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:32 msgid "" "Parameter for the start message sent to the bot when user presses the " "switch button" msgstr "" +"Параметр для передачи боту вместе с сообщением /start, отправленному при нажатии кнопки переключения" #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:1 msgid "" @@ -797,18 +841,24 @@ msgid "" "pre_checkout_query. Use this method to respond to such pre-checkout " "queries. On success, True is returned." msgstr "" +"Как только пользователь подтвердил детали оплаты и доставки, Bot API " +"отправляет финальное подтверждение в виде апдейта с полем pre_checkout_query. " +"Используйте этот метод для ответа на такие pre-checkout запросы. В случае успеха возвращается True." #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:6 msgid "" "The Bot API must receive an answer within 10 seconds after the pre-" "checkout query was sent." msgstr "" +"Bot API должно получить ответ в течение 10 секунд после отправки pre-checkout запроса." #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:8 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answerprecheckoutquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answerprecheckoutquery" #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:13 msgid "" @@ -816,6 +866,8 @@ msgid "" " bot is ready to proceed with the order. Use False if there are any " "problems." msgstr "" +"Задайте True если всё правильно (выбранные товары доступны и т.д.) и бот " +"готов обработать заказ. Задайте False если есть какие-то проблемы." #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:16 msgid "" @@ -826,16 +878,23 @@ msgid "" "different color or garment!\"). Telegram will display this message to the" " user." msgstr "" +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать человек, объясняющее " +"причину, по которой бот не может обработать заказ (например " +"\"Извините, кто-то только что купил последнюю из наших прекрасных черных футболок с коротким рукавом " +"пока вы заполняли детали оплаты. Пожалуйста выберите другой цвет или фасон!\"). " +"Telegram покажет это сообщение пользователю." #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:1 msgid "Asks for an answer to a shipping question." -msgstr "" +msgstr "Запрашивает ответ на вопрос о доставке." #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#answershippingquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answershippingquery" #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:8 msgid "" @@ -843,12 +902,15 @@ msgid "" "if there are any problems (for example, if delivery to the specified " "address is not possible)" msgstr "" +"Задайте True если доставка по выбранному адресу возможна и False, если есть " +"какие-то проблемы (например, доставка по выбранному адресу не осуществляется)" #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:11 msgid "" "Required if ok is True. A JSON-serialized array of available shipping " "options." msgstr "" +"Обязательный в случае, когда ok - True. Массив вариантов доставки в формате JSON." #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:14 msgid "" @@ -857,6 +919,10 @@ msgid "" "delivery to your desired address is unavailable'). Telegram will display " "this message to the user." msgstr "" +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать " +"человек, объясняющее причину, по которой невозможно завершить заказ (например " +"\"Извините, доставка по запрошенному адресу недоступна\"). " +"Telegram покажет это сообщение пользователю." #: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:1 msgid "" @@ -865,20 +931,25 @@ msgid "" " the query originated. On success, a SentWebAppMessage object is " "returned." msgstr "" +"Используйте этот метод, чтобы задать результат взаимодействия с Web App и " +"отправить соответствующее сообщение от лица пользователя в чат, из которого пришел запрос. " +"В случае успеха возвращается объект SentWebAppMessage." #: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:6 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#answerwebappquery" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#answerwebappquery" #: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:11 msgid "A JSON-serialized object describing the message to be sent" -msgstr "" +msgstr "Объект в формате JSON, описывающий сообщение, которое нужно отправить" #: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:14 msgid "On success, a SentWebAppMessage object is returned." -msgstr "" +msgstr "В случае успеха возвращается объект SentWebAppMessage." #: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:15 msgid ":class:`telebot.types.SentWebAppMessage`" @@ -890,12 +961,17 @@ msgid "" "administrator in the chat for this to work and must have the " "can_invite_users administrator right. Returns True on success." msgstr "" +"Используйте этот метод, чтобы одобрить запрос на вступление в чат. " +"Бот должен быть администратором чата и иметь права администратора " +"can_invite_users. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#approvechatjoinrequest" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#approvechatjoinrequest" #: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:7 #: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:7 @@ -908,6 +984,7 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup (in the format @supergroupusername)" msgstr "" +"Уникальный id чата или username супергруппы (в формате @supergroupusername)" #: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:11 #: telebot.async_telebot.AsyncTeleBot.ban_chat_member:12 @@ -919,7 +996,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:10 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:13 msgid "Unique identifier of the target user" -msgstr "" +msgstr "Уникальный id сделавшего запрос пользователя" #: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:14 #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:16 @@ -941,7 +1018,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:11 #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:14 msgid "True on success." -msgstr "" +msgstr "True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:1 msgid "" @@ -950,10 +1027,13 @@ msgid "" " to the chat on their own using invite links, etc., unless unbanned " "first. Returns True on success." msgstr "" +"Используйте этот метод, чтобы заблокировать пользователя в группе, супергруппе или канале. В " +"случае супергрупп и каналов, пользователь не сможет вернуться в чат самостоятельно, используя " +"ссылки с приглашением и т.д., пока не будет разблокирован. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#banchatmember" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:8 #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:7 @@ -961,6 +1041,8 @@ msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" +"Уникальный id группы или username супергруппы или канала " +"(в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:15 msgid "" @@ -968,6 +1050,9 @@ msgid "" "more than 366 days or less than 30 seconds from the current time they are" " considered to be banned forever" msgstr "" +"Дата, когда пользователь будет разблокирован, в формате UNIX time. Если пользователь " +"заблокирован больше чем на 366 дней или меньше чем на 30 секунд, то он будет заблокирован " +"до ручной разблокировки" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:19 msgid "" @@ -976,6 +1061,9 @@ msgid "" "group that were sent before the user was removed. Always True for " "supergroups and channels." msgstr "" +"Bool: Передайте True, чтобы удалить все сообщения пользователя из чата. " +"Если False, пользователю будут доступны все сообщения в группе, отправленные до его блокировки. " +"Всегда True для супергрупп и каналов." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 @@ -983,7 +1071,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:22 msgid "Returns True on success." -msgstr "" +msgstr "Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:1 msgid "" @@ -993,12 +1081,19 @@ msgid "" "administrator in the supergroup or channel for this to work and must have" " the appropriate administrator rights. Returns True on success." msgstr "" +"Используйте этот метод, чтобы заблокировать канал в супергруппе или канале. Владелец " +"канала не сможет отправлять сообщения и участвовать в прямых эфирах " +"от лица канала, пока канал не будет разблокирован. Бот должен быть " +"администратором супергруппы или канала и иметь соответствующие права администратора." +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:8 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#banchatsenderchat" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#banchatsenderchat" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:10 #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:7 @@ -1034,16 +1129,20 @@ msgid "" "Unique identifier for the target chat or username of the target channel " "(in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала " +"(в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:13 msgid "Unique identifier of the target sender chat" -msgstr "" +msgstr "Уникальный id канала для блокировки" #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:1 msgid "" "Handles new incoming callback query. As a parameter to the decorator " "function, it passes :class:`telebot.types.CallbackQuery` object." msgstr "" +"Обрабатывает новый callback запрос. В качестве параметра передаёт в декорируемую " +"функцию объект :class:`telebot.types.CallbackQuery`." #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:4 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:10 @@ -1073,7 +1172,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:6 #: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:4 msgid "Function executed as a filter" -msgstr "" +msgstr "Функция, используемая в качестве фильтра" #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:7 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:16 @@ -1104,7 +1203,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:12 #: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:7 msgid "Optional keyword arguments(custom filters)" -msgstr "" +msgstr "Необязательные именованные аргументы(кастомные фильтры)" #: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:1 msgid "" @@ -1112,20 +1211,23 @@ msgid "" "etc. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" +"Обрабатывает новый пост любого типа в канале - текст, фото, стикер и т.д. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`." #: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:4 #: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:4 #: telebot.async_telebot.AsyncTeleBot.edited_message_handler:5 #: telebot.async_telebot.AsyncTeleBot.message_handler:34 msgid "Optional list of strings (commands to handle)." -msgstr "" +msgstr "Необязательный список строк - команд для обработки." #: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:7 #: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:7 #: telebot.async_telebot.AsyncTeleBot.edited_message_handler:8 #: telebot.async_telebot.AsyncTeleBot.message_handler:37 msgid "Optional regular expression." -msgstr "" +msgstr "Необязательное регулярное выражение." #: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:13 #: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:13 @@ -1136,7 +1238,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:6 msgid "Supported message content types. Must be a list. Defaults to ['text']." -msgstr "" +msgstr "Обрабатываемые виды контента. Обязан быть списком. По умолчанию ['text']" #: of telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:1 msgid "" @@ -1145,6 +1247,10 @@ msgid "" "updates. As a parameter to the decorator function, it passes " ":class:`telebot.types.ChatJoinRequest` object." msgstr "" +"Обрабатывает запрос на вступление в чат. Бот должен иметь права администратора" +"can_invite_users в чате, чтобы получать такие апдейты. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ChatJoinRequest`." #: of telebot.async_telebot.AsyncTeleBot.chat_member_handler:1 msgid "" @@ -1154,15 +1260,23 @@ msgid "" "the decorator function, it passes " ":class:`telebot.types.ChatMemberUpdated` object." msgstr "" +"Обрабатывает изменение статуса пользователя в чате. Бот должен быть администратором " +"чата и явно указать “chat_member“ в allowed_updates, чтобы получать такие апдейты. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ChatMemberUpdated`." #: of telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:1 msgid "" -"The result of an inline query that was chosen by a user and sent to their" -" chat partner. Please see our documentation on the feedback collecting " -"for details on how to enable these updates for your bot. As a parameter " -"to the decorator function, it passes " +"Handles the result of an inline query that was chosen by a user and sent " +"to their chat partner. Please see our documentation on the feedback " +"collecting for details on how to enable these updates for your bot. As a " +"parameter to the decorator function, it passes " ":class:`telebot.types.ChosenInlineResult` object." msgstr "" +"Обрабатывает результат inline запроса, который был выбран пользователем и отправлен " +"собеседнику в чате. Пожалуйста ознакомьтесь с документацией по сбору фидбека " +"для получения таких апдейтов вашим ботом. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.ChosenInlineResult`." #: of telebot.async_telebot.AsyncTeleBot.close:1 msgid "" @@ -1172,10 +1286,15 @@ msgid "" "The method will return error 429 in the first 10 minutes after the bot is" " launched. Returns True on success." msgstr "" +"Используйте этот метод чтобы закрыть инстанс бота прежде чем перемещать его с одного " +"локального сервера на другой. Вы должны удалить вебхук перед вызовом этого метода, " +"чтобы убедиться. что бот не будет запущен повторно после перезапуска сервера. " +"Метод будет возвращать ошибку 429 в течение 10 минут после запуска бота. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.close:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#close" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#close" #: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:1 msgid "" @@ -1184,28 +1303,25 @@ msgid "" "the can_manage_topics administrator rights, unless it is the creator of " "the topic. Returns True on success." msgstr "" +"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics, за исключением " +"случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#closeforumtopic" #: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:10 msgid "Identifier of the topic to close" -msgstr "" - -#: of telebot.async_telebot.AsyncTeleBot.close_session:1 -msgid "" -"Closes existing session of aiohttp. Use this function if you stop " -"polling/webhooks." -msgstr "" +msgstr "id топика для закрытия" #: of telebot.async_telebot.AsyncTeleBot.copy_message:1 msgid "Use this method to copy messages of any kind." -msgstr "" +msgstr "Используйте этот метод для копирования любых сообщений." #: of telebot.async_telebot.AsyncTeleBot.copy_message:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#copymessage" #: of telebot.async_telebot.AsyncTeleBot.copy_message:8 #: telebot.async_telebot.AsyncTeleBot.forward_message:11 @@ -1213,27 +1329,33 @@ msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" msgstr "" +"Уникальный id чата, в который было отправлено исходное сообщение " +"(или username канала в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.copy_message:10 #: telebot.async_telebot.AsyncTeleBot.forward_message:14 msgid "Message identifier in the chat specified in from_chat_id" -msgstr "" +msgstr "id сообщения в чате, заданном в from_chat_id" #: of telebot.async_telebot.AsyncTeleBot.copy_message:13 msgid "" "New caption for media, 0-1024 characters after entities parsing. If not " "specified, the original caption is kept" msgstr "" +"Новая подпись для медиа, 0-1024 символа после форматирования. Если не задано, " +"используется исходная подпись" #: of telebot.async_telebot.AsyncTeleBot.copy_message:16 msgid "Mode for parsing entities in the new caption." -msgstr "" +msgstr "Режим форматирования новой подписи." #: of telebot.async_telebot.AsyncTeleBot.copy_message:19 msgid "" "A JSON-serialized list of special entities that appear in the new " "caption, which can be specified instead of parse_mode" msgstr "" +"Список отформатированных частей новой подписи в формате JSON, " +"можно использовать вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.copy_message:22 #: telebot.async_telebot.AsyncTeleBot.send_animation:45 @@ -1255,6 +1377,7 @@ msgid "" "Sends the message silently. Users will receive a notification with no " "sound." msgstr "" +"Отправить сообщение, при получении которого пользователи получат уведомление без звука." #: of telebot.async_telebot.AsyncTeleBot.copy_message:25 #: telebot.async_telebot.AsyncTeleBot.send_animation:34 @@ -1272,7 +1395,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_video_note:42 #: telebot.async_telebot.AsyncTeleBot.send_voice:43 msgid "Protects the contents of the sent message from forwarding and saving" -msgstr "" +msgstr "Запретить пересылку и сохранение содержимого сообщения" #: of telebot.async_telebot.AsyncTeleBot.copy_message:28 #: telebot.async_telebot.AsyncTeleBot.send_animation:37 @@ -1293,7 +1416,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_video_note:19 #: telebot.async_telebot.AsyncTeleBot.send_voice:20 msgid "If the message is a reply, ID of the original message" -msgstr "" +msgstr "Если сообщение является ответом - id сообщения, на которое дан ответ" #: of telebot.async_telebot.AsyncTeleBot.copy_message:31 #: telebot.async_telebot.AsyncTeleBot.send_animation:54 @@ -1313,6 +1436,8 @@ msgid "" "Pass True, if the message should be sent even if the specified replied-to" " message is not found" msgstr "" +"Передайте True, если сообщение нужно отправить даже в случае отсутствия сообщения, на " +"которое дан ответ" #: of telebot.async_telebot.AsyncTeleBot.copy_message:34 #: telebot.async_telebot.AsyncTeleBot.send_animation:40 @@ -1334,6 +1459,8 @@ msgid "" "keyboard, custom reply keyboard, instructions to remove reply keyboard or" " to force a reply from the user." msgstr "" +"Дополнительные элементы интерфейса. Inline клавиатура, текстовая клавиатура, " +"запрос на удаление текстовой клавиатуры или запрос на ответ от пользователя." #: of telebot.async_telebot.AsyncTeleBot.copy_message:39 #: telebot.async_telebot.AsyncTeleBot.delete_message:19 @@ -1356,7 +1483,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_voice:34 #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:19 msgid "Timeout in seconds for the request." -msgstr "" +msgstr "Таймаут запроса в секундах." #: of telebot.async_telebot.AsyncTeleBot.copy_message:42 #: telebot.async_telebot.AsyncTeleBot.send_audio:60 @@ -1366,7 +1493,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_sticker:36 #: telebot.async_telebot.AsyncTeleBot.send_voice:46 msgid "Identifier of a message thread, in which the message will be sent" -msgstr "" +msgstr "id топика, в который нужно отправить сообщение" #: of telebot.async_telebot.AsyncTeleBot.copy_message:45 #: telebot.async_telebot.AsyncTeleBot.forward_message:26 @@ -1388,7 +1515,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_video_note:48 #: telebot.async_telebot.AsyncTeleBot.send_voice:49 msgid "On success, the sent Message is returned." -msgstr "" +msgstr "В случае успеха возвращает отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.copy_message:46 #: telebot.async_telebot.AsyncTeleBot.forward_message:27 @@ -1416,12 +1543,18 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" +"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " +"должен быть администратором чата и иметь соответствующие права администратора. " +"Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " +"ссылку-приглашение (ChatInviteLink)." #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createchatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createchatinvitelink" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:8 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:6 @@ -1431,21 +1564,22 @@ msgid "" "Id: Unique identifier for the target chat or username of the target " "channel (in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:12 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:10 msgid "Invite link name; 0-32 characters" -msgstr "" +msgstr "Название ссылки-приглашения; 0-32 символа" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:15 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:16 msgid "Point in time (Unix timestamp) when the link will expire" -msgstr "" +msgstr "Время, когда ссылка будет аннулирована в формате Unix timestamp" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:18 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:19 msgid "Maximum number of users that can be members of the chat simultaneously" -msgstr "" +msgstr "Максимальное количество пользователей в чате" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:21 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:22 @@ -1453,12 +1587,14 @@ msgid "" "True, if users joining the chat via the link need to be approved by chat " "administrators. If True, member_limit can't be specified" msgstr "" +"True, если пользователи, использующие эту ссылку должны быть одобрены " +"администраторами чата. Нельзя использовать True вместе с member_limit" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:24 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:25 #: telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:14 msgid "Returns the new invite link as ChatInviteLink object." -msgstr "" +msgstr "Возвращает новую ссылку-приглашение (ChatInviteLink)." #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:25 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:26 @@ -1473,34 +1609,44 @@ msgid "" "can_manage_topics administrator rights. Returns information about the " "created topic as a ForumTopic object." msgstr "" +"Используйте этот метод, чтобы создать топик в супергруппе. Бот должен быть " +"администратором чата и иметь права администратора can_manage_topics. " +"Возвращает информацию о созданном топике (ForumTopic)." #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createforumtopic" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createforumtopic" #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:10 msgid "Name of the topic, 1-128 characters" -msgstr "" +msgstr "Имя топика, 1-128 символов" #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:13 msgid "" "Color of the topic icon in RGB format. Currently, must be one of " "0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" msgstr "" +"Цвет иконки топика в формате RGB. В текущий момент, доступны цвета " +"0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F" #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:16 msgid "" "Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " "be exactly 1 character long" msgstr "" +"Кастомный эмодзи для использования в качестве иконки топика. Должно быть “tgs” " +"эмодзи и быть ровно 1 символом" #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:19 msgid "" "On success, information about the created topic is returned as a " "ForumTopic object." msgstr "" +"В случае успеха возвращается информация о созданном топике (ForumTopic)." #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:20 msgid ":class:`telebot.types.ForumTopic`" @@ -1511,22 +1657,26 @@ msgid "" "Use this method to create a link for an invoice. Returns the created " "invoice link as String on success." msgstr "" +"используйте этот метод, чтобы создать ссылку-инвойс. Возвращает созданную ссылку " +"в случае успеха (String)." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createinvoicelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createinvoicelink" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:7 #: telebot.async_telebot.AsyncTeleBot.send_invoice:8 msgid "Product name, 1-32 characters" -msgstr "" +msgstr "Название товара, 1-32 символа" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:10 #: telebot.async_telebot.AsyncTeleBot.send_invoice:11 msgid "Product description, 1-255 characters" -msgstr "" +msgstr "Описание товара, 1-255 символов" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:13 #: telebot.async_telebot.AsyncTeleBot.send_invoice:14 @@ -1534,11 +1684,13 @@ msgid "" "Bot-defined invoice payload, 1-128 bytes. This will not be displayed to " "the user, use for your internal processes." msgstr "" +"Дополнительные данные, 1-128 байт. Не будет показано пользователю, " +"используйте во внутренних процессах." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:17 #: telebot.async_telebot.AsyncTeleBot.send_invoice:18 msgid "Payments provider token, obtained via @Botfather" -msgstr "" +msgstr "Токен платежной системы, полученный через @BotFather" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:20 #: telebot.async_telebot.AsyncTeleBot.send_invoice:21 @@ -1546,6 +1698,8 @@ msgid "" "Three-letter ISO 4217 currency code, see " "https://core.telegram.org/bots/payments#supported-currencies" msgstr "" +"Трехбуквенный код валюты в формате ISO 4217, см. " +"https://core.telegram.org/bots/payments#supported-currencies" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:24 #: telebot.async_telebot.AsyncTeleBot.send_invoice:25 @@ -1553,11 +1707,13 @@ msgid "" "Price breakdown, a list of components (e.g. product price, tax, discount," " delivery cost, delivery tax, bonus, etc.)" msgstr "" +"Детали цены, список компонент (например цена продукта, налог, скидка, " +"стоимость доставки, налог на доставку, бонус и т.д.)" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:28 #: telebot.async_telebot.AsyncTeleBot.send_invoice:87 msgid "The maximum accepted amount for tips in the smallest units of the currency" -msgstr "" +msgstr "Максимальный размер чаевых в наименьших единицах выбранной валюты" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:31 #: telebot.async_telebot.AsyncTeleBot.send_invoice:90 @@ -1567,6 +1723,9 @@ msgid "" " The suggested tip amounts must be positive, passed in a strictly " "increased order and must not exceed max_tip_amount." msgstr "" +"Массив предлагаемых вариантов чаевых в наименьших единицах выбранной валюты " +"в формате JSON. Можно задать не более 4 вариантов. Варианты чаевых должны быть " +"больше нуля, перечисленные в порядке строгого возрастания и не превышать max_tip_amount." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:36 #: telebot.async_telebot.AsyncTeleBot.send_invoice:77 @@ -1575,6 +1734,9 @@ msgid "" "payment provider. A detailed description of required fields should be " "provided by the payment provider." msgstr "" +"Данные о инвойсе в формате JSON, которые будут переданы платежной системе. " +"Подробное описание обязательных полей должно быть предоставлено провайдером " +"платежной системы." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:40 msgid "" @@ -1582,36 +1744,39 @@ msgid "" "a photo of the invoice. People like it better when they see a photo of " "what they are paying for." msgstr "" +"URL изображения товара для инвойса. Может быть изображением товаров или " +"изображением инвойса. Людям больше нравится видеть фото товара, " +"за который они платят." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:44 #: telebot.async_telebot.AsyncTeleBot.send_invoice:37 msgid "Photo size in bytes" -msgstr "" +msgstr "Вес изображения в байтах" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:47 #: telebot.async_telebot.AsyncTeleBot.send_invoice:40 msgid "Photo width" -msgstr "" +msgstr "Ширина изображения" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:50 #: telebot.async_telebot.AsyncTeleBot.send_invoice:43 msgid "Photo height" -msgstr "" +msgstr "Высота изображения" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:53 #: telebot.async_telebot.AsyncTeleBot.send_invoice:46 msgid "Pass True, if you require the user's full name to complete the order" -msgstr "" +msgstr "Передайте True, если для совершения заказа требуется полное имя пользователя" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:56 #: telebot.async_telebot.AsyncTeleBot.send_invoice:49 msgid "Pass True, if you require the user's phone number to complete the order" -msgstr "" +msgstr "Передайте True, если для совершения заказа требуется номер телефона пользователя" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:59 #: telebot.async_telebot.AsyncTeleBot.send_invoice:52 msgid "Pass True, if you require the user's email to complete the order" -msgstr "" +msgstr "Передайте True, если для совершения заказа требуется email пользователя" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:62 #: telebot.async_telebot.AsyncTeleBot.send_invoice:55 @@ -1619,25 +1784,26 @@ msgid "" "Pass True, if you require the user's shipping address to complete the " "order" msgstr "" +"Передайте True, если для совершения заказа требуется адрес доставки" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:65 #: telebot.async_telebot.AsyncTeleBot.send_invoice:61 msgid "Pass True, if user's phone number should be sent to provider" -msgstr "" +msgstr "Передайте True, если номер телефона пользователя нужно отправить платежной системе" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:68 #: telebot.async_telebot.AsyncTeleBot.send_invoice:64 msgid "Pass True, if user's email address should be sent to provider" -msgstr "" +msgstr "Передайте True, если email пользователя нужно отправить платежной системе" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:71 #: telebot.async_telebot.AsyncTeleBot.send_invoice:58 msgid "Pass True, if the final price depends on the shipping method" -msgstr "" +msgstr "Передайте True, если окончательная цена зависит от способа доставки" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:74 msgid "Created invoice link as String on success." -msgstr "" +msgstr "Созданная ссылка-инвойс (String) в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:75 #: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:11 @@ -1650,12 +1816,17 @@ msgid "" "Use this method to create new sticker set owned by a user. The bot will " "be able to edit the created sticker set. Returns True on success." msgstr "" +"Используйте этот метод, чтобы создать новый стикерпак, владельцем которого " +"станет пользователь. Бот будет иметь возможность редактировать созданный стикерпак. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createnewstickerset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#createnewstickerset" #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:10 msgid "" @@ -1665,16 +1836,23 @@ msgid "" "in \"_by_\". is case insensitive. 1-64 " "characters." msgstr "" +"Короткое имя стикерпака для использования в ссылках вида t.me/addstickers/ " +"(например animals). Может содержать только латинские буквы, цифры и " +"нижние подчеркивания. Должно начинаться с буквы, не может содержать подряд " +"идущие нижние подчеркивания и должно заканчиваться на \"_by_\". " +" учитывает регистр. 1-64 символа." #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:15 msgid "Sticker set title, 1-64 characters" -msgstr "" +msgstr "Название стикерпака, 1-64 символа" #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:32 msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." msgstr "" +"Передайте True, если создаётся стикерпак масок. Устарело, начиная с " +"Bot API 6.2, используйте sticker_type." #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:36 msgid "" @@ -1682,6 +1860,9 @@ msgid "" "emoji sticker sets can't be created via the Bot API at the moment. By " "default, a regular sticker set is created." msgstr "" +"Необязательный, тип стикерпака, передайте “regular” или “mask”. Стикерпаки " +"кастомных эмодзи пока что не могут быть созданы с помощью Bot API. По умолчанию " +"будет создан обычный стикерпак." #: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:1 msgid "" @@ -1689,12 +1870,17 @@ msgid "" "administrator in the chat for this to work and must have the " "can_invite_users administrator right. Returns True on success." msgstr "" +"Используйте этот метод, чтобы отклонить запрос на вступление в чат. Бот " +"должен быть администратором чата и иметь права администратора can_invite_users. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#declinechatjoinrequest" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#declinechatjoinrequest" #: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:1 msgid "" @@ -1704,10 +1890,15 @@ msgid "" " Note: In regular groups (non-supergroups), this method will only work if" " the ‘All Members Are Admins’ setting is off in the target group." msgstr "" +"Используйте этот метод, чтобы удалить фото чата. Нельзя изменить фото в " +"приватных чатах. Бот должен быть администратором чата и иметь соответствующие " +"права администратора. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппах), метод будет работать только " +"в случаях, когда настройка ‘All Members Are Admins’ выключена." #: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletechatphoto" #: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:8 #: telebot.async_telebot.AsyncTeleBot.set_chat_photo:9 @@ -1716,6 +1907,7 @@ msgid "" "Int or Str: Unique identifier for the target chat or username of the " "target channel (in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:1 msgid "" @@ -1725,12 +1917,18 @@ msgid "" "returned in getChat requests to check if the bot can use this method. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить стикерпак группы из супергруппы. Бот " +"должен быть администратором чата и иметь соответствующие права администратора. " +"Используйте поле can_set_sticker_set, возвращаемое методом getChat, чтобы " +"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletechatstickerset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deletechatstickerset" #: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:1 msgid "" @@ -1739,16 +1937,22 @@ msgid "" "can_manage_topics administrator rights, unless it is the creator of the " "topic. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics, " +"за исключением случае, когда бот является создателем топика. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deleteforumtopic" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deleteforumtopic" #: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:10 msgid "Identifier of the topic to delete" -msgstr "" +msgstr "id топика, который нужно удалить" #: of telebot.async_telebot.AsyncTeleBot.delete_message:1 msgid "" @@ -1763,14 +1967,24 @@ msgid "" "has can_delete_messages permission in a supergroup or a channel, it can " "delete any message there. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить сообщение, в том числе сервисное, " +"ограничения: - Сообщение может быть удалено только если оно было отправлено " +"менее 48 часов назад. - Dice-сообщение в приватном чате может быть удалено " +"только если оно было отправлено более 24 часов назад. - Боты могут удалять " +"свои сообщения в приватных чатах, группах и супергруппах. - Боты могут удалять " +"чужие сообщение в приватных чатах. - Боты с правами администратора can_post_messages " +"могут удалять сообщения в каналах. - Если бот является администратором группы, он " +"может удалить любое сообщение в ней. - Если бот имеет права администратора " +"can_delete_messages в супергруппе или канале, он может удалить любое сообщение в них. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_message:11 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletemessage" #: of telebot.async_telebot.AsyncTeleBot.delete_message:16 msgid "Identifier of the message to delete" -msgstr "" +msgstr "id сообщения, которое нужно удалить" #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:1 msgid "" @@ -1778,12 +1992,18 @@ msgid "" "scope and user language. After deletion, higher level commands will be " "shown to affected users. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить список команд бота для заданных поля " +"видимости и языка. После удаления, команды более широкого поля видимости " +"будут доступны пользователям, которых коснулись изменения. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletemycommands" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deletemycommands" #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:7 #: telebot.async_telebot.AsyncTeleBot.get_my_commands:6 @@ -1792,6 +2012,7 @@ msgid "" "The scope of users for which the commands are relevant. Defaults to " "BotCommandScopeDefault." msgstr "" +"Область видимости команд. По умолчанию BotCommandScopeDefault." #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:11 #: telebot.async_telebot.AsyncTeleBot.get_my_commands:10 @@ -1801,54 +2022,62 @@ msgid "" "to all users from the given scope, for whose language there are no " "dedicated commands" msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения коснутся " +"команд для всех пользователей в заданном поле видимости, не имеющих команд на их языке" #: of telebot.async_telebot.AsyncTeleBot.delete_state:1 msgid "Delete the current state of a user." -msgstr "" +msgstr "Удалить текущее состояние (стейт) пользователя." #: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:1 msgid "" "Use this method to delete a sticker from a set created by the bot. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного ботом. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletestickerfromset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#deletestickerfromset" #: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:5 #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:5 msgid "File identifier of the sticker" -msgstr "" +msgstr "id файла стикера" #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:1 msgid "" "Use this method to remove webhook integration if you decide to switch " "back to getUpdates. Returns True on success." msgstr "" +"Используйте этот метод, чтобы удалить вебхук, если вы решите перейти обратно на getUpdates. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletewebhook" #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:6 #: telebot.async_telebot.AsyncTeleBot.set_webhook:36 msgid "Pass True to drop all pending updates, defaults to None" -msgstr "" +msgstr "Передайте True, чтобы удалить все предшествующие запуску бота апдейты, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:9 msgid "Request connection timeout, defaults to None" -msgstr "" +msgstr "Тайм-аут запроса, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.download_file:1 msgid "Downloads file." -msgstr "" +msgstr "Скачивает файл." #: of telebot.async_telebot.AsyncTeleBot.download_file:3 msgid "Path where the file should be downloaded." -msgstr "" +msgstr "Путь, куда файл нужно сохранить." #: of telebot.async_telebot.AsyncTeleBot.download_file:6 msgid "bytes" @@ -1864,16 +2093,21 @@ msgid "" " bot must be an administrator in the chat for this to work and must have " "the appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы изменить неосновную ссылку-приглашение, " +"созданную ботом. Бот должен быть администратором чата и иметь соответствующие " +"права администратора." #: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editchatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editchatinvitelink" #: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:13 msgid "The invite link to edit" -msgstr "" +msgstr "Ссылка-приглашение для изменения" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:1 msgid "" @@ -1882,71 +2116,83 @@ msgid "" "must have can_manage_topics administrator rights, unless it is the " "creator of the topic. Returns True on success." msgstr "" +"Используйте этот метод, чтобы изменить название и иконку топика в супергруппе. " +"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " +"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:5 msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:10 msgid "Identifier of the topic to edit" -msgstr "" +msgstr "id топика для изменения" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:13 msgid "New name of the topic, 1-128 characters" -msgstr "" +msgstr "Новое название топика, 1-128 символов" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:16 msgid "" "New custom emoji for the topic icon. Must be an emoji of type “tgs” and " "must be exactly 1 character long" msgstr "" +"Новый кастомный эмодзи для иконки топика. Должно быть “tgs” эмодзи, " +"длиной ровно в 1 символ" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:1 msgid "Use this method to edit captions of messages." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить подписи к медиа в сообщениях" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagecaption" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagecaption" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:5 msgid "New caption of the message" -msgstr "" +msgstr "Новая подпись к медиа" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:8 msgid "" "Required if inline_message_id is not specified. Unique identifier for the" " target chat or username of the target channel" msgstr "" +"Обязательный, если не указан inline_message_id. Уникальный id чата или username" +" канала" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:11 msgid "Required if inline_message_id is not specified." -msgstr "" +msgstr "Обязательный, если не указан inline_message_id." #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:14 msgid "" "Required if inline_message_id is not specified. Identifier of the inline " "message." msgstr "" +"Обязательный, если не указан inline_message_id. id inline сообщения." #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:17 msgid "New caption of the message, 0-1024 characters after entities parsing" -msgstr "" +msgstr "Новая подпись к медиа в сообщении, 0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:20 msgid "" "A JSON-serialized array of objects that describe how the caption should " "be parsed." msgstr "" +"Массив объектов, описывающих то, как будет происходить парсинг подписи к " +"медиа в формате JSON." #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:23 #: telebot.async_telebot.AsyncTeleBot.edit_message_media:19 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:14 #: telebot.async_telebot.AsyncTeleBot.edit_message_text:26 msgid "A JSON-serialized object for an inline keyboard." -msgstr "" +msgstr "JSON-сериализованный объект inline клавиатуры." #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:26 #: telebot.async_telebot.AsyncTeleBot.edit_message_media:22 @@ -1956,6 +2202,8 @@ msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." msgstr "" +"В случае успеха, если изменённое сообщение отправлено ботом, возвращается новый объект Message, " +"иначе (inline сообщения) возвращается True." #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:27 msgid ":obj:`types.Message` | :obj:`bool`" @@ -1966,6 +2214,8 @@ msgid "" "Use this method to edit live location messages. A location can be edited " "until its live_period expires or editing is explicitly" msgstr "" +"Используйте этот метод, чтобы изменить live местоположение в сообщении. Местоположение может быть " +"изменено пока не истечёт live_period или не" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:2 msgid "" @@ -1973,65 +2223,78 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" +"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " +"сообщение не является inline сообщением, возвращается новый объект Message, " +"иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagelivelocation" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagelivelocation" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:7 msgid "Latitude of new location" -msgstr "" +msgstr "Широта нового местоположения" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:10 msgid "Longitude of new location" -msgstr "" +msgstr "Долгота нового местоположения" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:16 msgid "" "Required if inline_message_id is not specified. Identifier of the message" " to edit" msgstr "" +"Обязательный, если не указан inline_message_id. id сообщения, которое нужно " +"изменить" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:19 #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:15 msgid "A JSON-serialized object for a new inline keyboard." -msgstr "" +msgstr "JSON-сериализованный объект новой inline клавиатуры." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:26 #: telebot.async_telebot.AsyncTeleBot.edit_message_media:16 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:11 -#: telebot.async_telebot.AsyncTeleBot.edit_message_text:14 -#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:19 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:14 telebot.async_telebot.AsyncTeleBot.get_game_high_scores:19 #: telebot.async_telebot.AsyncTeleBot.set_game_score:20 msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message" msgstr "" +"Обязательный, если не указаны chat_id и message_id. id inline сообщения" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:29 #: telebot.async_telebot.AsyncTeleBot.send_location:31 msgid "The radius of uncertainty for the location, measured in meters; 0-1500" -msgstr "" +msgstr "Радиус погрешности местоположения, измеряется в метрах; 0-1500" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:32 msgid "" "Direction in which the user is moving, in degrees. Must be between 1 and " "360 if specified." msgstr "" +"Направление, в котором пользователь двигается, в градусах. Если указано, должно " +"быть от 1 до 360." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:35 msgid "" "The maximum distance for proximity alerts about approaching another chat " "member, in meters. Must be between 1 and 100000 if specified." msgstr "" +"Максимальное расстояние для показа уведомлений о приближении других участников " +"чата, в метрах. Если указано, должно быть от 1 до 100000." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:38 msgid "" "On success, if the edited message is not an inline message, the edited " "Message is returned, otherwise True is returned." msgstr "" +"В случае успеха, если измененное сообщение не является inline сообщением, возвращается " +"новый объект Message, иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:39 #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:23 @@ -2046,16 +2309,24 @@ msgid "" " arbitrarily. When inline message is edited, new file can't be uploaded. " "Use previously uploaded file via its file_id or specify a URL." msgstr "" +"Используйте этот метод, чтобы изменить гифку, аудио, документ, фото или видео " +"в сообщении. Если сообщение является частью альбома, оно может быть " +"изменено только на фото или видео. Иначе, тип сообщения может быть изменен" +" на любой. При изменении inline сообщения, нельзя загрузить новый файл. " +"используйте ранее загруженные файлы через file_id или укажите URL." #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagemedia" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagemedia" + #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:8 msgid "A JSON-serialized object for a new media content of the message" -msgstr "" +msgstr "JSON-сериализованный объект нового медиа контента" #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:10 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:5 @@ -3086,7 +3357,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.reply_to:9 msgid "" "Additional keyword arguments which are passed to " -":meth:`telebot.TeleBot.send_message`" +":meth:`telebot.async_telebot.AsyncTeleBot.send_message`" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.reset_data:1 diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 4664234b9..11555e9bf 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -1209,10 +1209,14 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#cl #: of telebot.TeleBot.close_forum_topic:1 msgid "" +"Use this method to close an open topic in a forum supergroup chat. The " +"bot must be an administrator in the chat for this to work and must have " +"the can_manage_topics administrator rights, unless it is the creator of " +"the topic. Returns True on success." +msgstr "" "Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. Бот должен " "быть администратором чата и иметь права администратора can_manage_topics, за исключением " "случаев, когда бот является создателем топика. Возвращает True в случае успеха." -msgstr "" #: of telebot.TeleBot.close_forum_topic:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" @@ -1383,7 +1387,7 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" -"используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " +"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " "должен быть администратором чата и иметь соответствующие права администратора. " "Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " "ссылку-приглашение (ChatInviteLink)." @@ -2064,10 +2068,13 @@ msgstr "" #: of telebot.TeleBot.edit_message_live_location:2 msgid "" +"disabled by a call to stopMessageLiveLocation. On success, if the edited " +"message is not an inline message, the edited Message is returned, " +"otherwise True is returned." +msgstr "" "запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " "сообщение не является inline сообщением, возвращается новый объект Message, " "иначе возвращается True." -msgstr "" #: of telebot.TeleBot.edit_message_live_location:5 msgid "" @@ -2077,7 +2084,6 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#editmessagelivelocation" - #: of telebot.TeleBot.edit_message_live_location:7 msgid "Latitude of new location" msgstr "Широта нового местоположения" From 68edb4990ce8deee3e272515f0f01818bbf0b1a5 Mon Sep 17 00:00:00 2001 From: Konstantin Ostashenko Date: Fri, 30 Dec 2022 21:42:50 +0200 Subject: [PATCH 1220/1808] Fix async_version.po --- docs/source/locales/ru/LC_MESSAGES/async_version.po | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index de4ce541e..ad3ae7a72 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -1315,6 +1315,14 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#cl msgid "Identifier of the topic to close" msgstr "id топика для закрытия" +#: of telebot.async_telebot.AsyncTeleBot.close_session:1 +msgid "" +"Closes existing session of aiohttp. Use this function if you stop " +"polling/webhooks." +msgstr "" +"Закрывает текущую aiohttp сессию. Используйте эту функцию для завершения работы " +"поллинга или вебхука." + #: of telebot.async_telebot.AsyncTeleBot.copy_message:1 msgid "Use this method to copy messages of any kind." msgstr "Используйте этот метод для копирования любых сообщений." @@ -2259,7 +2267,8 @@ msgstr "JSON-сериализованный объект новой inline кл #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:26 #: telebot.async_telebot.AsyncTeleBot.edit_message_media:16 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:11 -#: telebot.async_telebot.AsyncTeleBot.edit_message_text:14 telebot.async_telebot.AsyncTeleBot.get_game_high_scores:19 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:14 +#: telebot.async_telebot.AsyncTeleBot.get_game_high_scores:19 #: telebot.async_telebot.AsyncTeleBot.set_game_score:20 msgid "" "Required if chat_id and message_id are not specified. Identifier of the " From 8e9d566d5cc7255af7586a234356c7c251353496 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Sat, 31 Dec 2022 00:56:38 +0200 Subject: [PATCH 1221/1808] Minor fixes and some translations Up to lines sync_version.po:2382 and async_version.po:2528 --- .../locales/ru/LC_MESSAGES/async_version.po | 73 +++++++++++------ .../locales/ru/LC_MESSAGES/sync_version.po | 81 ++++++++++++------- 2 files changed, 99 insertions(+), 55 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index ad3ae7a72..1ad8088ed 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -217,7 +217,7 @@ msgid "Parameters" msgstr "" #: of telebot.async_telebot.AsyncTeleBot:24 -msgid "Token of a bot, should be obtained from @BotFather" +msgid "Token of a bot, obtained from @BotFather" msgstr "Токен бота, нужно получить от @BotFather" #: of telebot.async_telebot.AsyncTeleBot:27 @@ -1365,7 +1365,8 @@ msgstr "" "Список отформатированных частей новой подписи в формате JSON, " "можно использовать вместо parse_mode" -#: of telebot.async_telebot.AsyncTeleBot.copy_message:22 +#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 +#: telebot.async_telebot.AsyncTeleBot.copy_message:22 #: telebot.async_telebot.AsyncTeleBot.send_animation:45 #: telebot.async_telebot.AsyncTeleBot.send_audio:39 #: telebot.async_telebot.AsyncTeleBot.send_contact:20 @@ -1551,7 +1552,7 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" -"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " +"Используйте этот метод, чтобы создать дополнительную ссылку-приглашение в чат. Бот " "должен быть администратором чата и иметь соответствующие права администратора. " "Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " "ссылку-приглашение (ChatInviteLink)." @@ -2231,7 +2232,7 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" -"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " +"отключено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " "сообщение не является inline сообщением, возвращается новый объект Message, " "иначе возвращается True." @@ -2347,6 +2348,8 @@ msgid "" " target chat or username of the target channel (in the format " "@channelusername)" msgstr "" +"Обязательный, если не указан inline_message_id. Уникальный id чата " +"или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:13 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:8 @@ -2357,6 +2360,7 @@ msgid "" "Required if inline_message_id is not specified. Identifier of the sent " "message" msgstr "" +"Обязательный, если не указан inline_message_id. id отправленного сообщения" #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:23 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:18 @@ -2367,41 +2371,45 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:1 msgid "Use this method to edit only the reply markup of messages." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить только reply markup сообщения." #: of telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagereplymarkup" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagereplymarkup" #: of telebot.async_telebot.AsyncTeleBot.edit_message_text:1 msgid "Use this method to edit text and game messages." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить текстовые и игровые сообщения." #: of telebot.async_telebot.AsyncTeleBot.edit_message_text:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#editmessagetext" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editmessagetext" #: of telebot.async_telebot.AsyncTeleBot.edit_message_text:5 msgid "New text of the message, 1-4096 characters after entities parsing" -msgstr "" +msgstr "Новый текст сообщения, 1-4096 символов после форматирования" #: of telebot.async_telebot.AsyncTeleBot.edit_message_text:17 #: telebot.async_telebot.AsyncTeleBot.send_message:15 msgid "Mode for parsing entities in the message text." -msgstr "" +msgstr "Режим форматирования в тексте сообщения." #: of telebot.async_telebot.AsyncTeleBot.edit_message_text:20 msgid "" "List of special entities that appear in the message text, which can be " "specified instead of parse_mode" msgstr "" +"Список отформатированных частей в тексте сообщения, " +"можно использовать вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.edit_message_text:23 #: telebot.async_telebot.AsyncTeleBot.send_message:21 msgid "Disables link previews for links in this message" -msgstr "" +msgstr "Отключает превью ссылок в сообщении" #: of telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:1 msgid "" @@ -2409,25 +2417,30 @@ msgid "" "edited. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" +"Обрабатывает новую версию поста в канале, который доступен боту и был изменён. " +"В качестве параметра, передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`." #: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:1 msgid "Handles new version of a message that is known to the bot and was edited." -msgstr "" +msgstr "Обрабатывает новую версию сообщения, которое доступно боту и было изменено." #: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:3 msgid "" "As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" +"В качестве параметра, передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`." #: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:17 #: telebot.async_telebot.AsyncTeleBot.message_handler:47 msgid "list of chat types" -msgstr "" +msgstr "список видов чатов" #: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:1 msgid "Enable saving states (by default saving disabled)" -msgstr "" +msgstr "Разрешить сохранение стейтов (по умолчанию сохранение отключено)" #: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:4 msgid "" @@ -2435,10 +2448,12 @@ msgid "" ":class:`~telebot.asyncio_storage.StatePickleStorage` instance as " "state_storage to TeleBot class." msgstr "" +"Рекомендуется передавать экземпляр класса :class:`~telebot.storage.StatePickleStorage` " +"в качестве state_storage при инициализации класса TeleBot вместо использования этой функции." #: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:7 msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" -msgstr "" +msgstr "Имя файла для сохранения, по умолчанию \"./.state-save/states.pkl\"" #: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:1 msgid "" @@ -2446,34 +2461,33 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы создать или заменить главную ссылку-приглашение " +"в супер-группу или канал, созданную ботом. Бот должен быть администратором чата " +"и иметь соответствующие права администратора." #: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#exportchatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#exportchatinvitelink" #: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:10 msgid "exported invite link as String on success." -msgstr "" +msgstr "новая ссылка-приглашение (String) в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.forward_message:1 msgid "Use this method to forward messages of any kind." -msgstr "" +msgstr "Используйте этот метод, чтобы переслать любое сообщение." #: of telebot.async_telebot.AsyncTeleBot.forward_message:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" -msgstr "" - -#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 -msgid "" -"Sends the message silently. Users will receive a notification with no " -"sound" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#forwardmessage" #: of telebot.async_telebot.AsyncTeleBot.forward_message:17 msgid "Protects the contents of the forwarded message from forwarding and saving" -msgstr "" +msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" #: of telebot.async_telebot.AsyncTeleBot.forward_message:23 #: telebot.async_telebot.AsyncTeleBot.send_message:43 @@ -2481,6 +2495,8 @@ msgid "" "Unique identifier for the target message thread (topic) of the forum; for" " forum supergroups only" msgstr "" +"Уникальный id топика, в который нужно переслать сообщение; только для " +"супер-групп с топиками" #: of telebot.async_telebot.AsyncTeleBot.get_chat:1 msgid "" @@ -2488,10 +2504,13 @@ msgid "" "name of the user for one-on-one conversations, current username of a " "user, group or channel, etc.). Returns a Chat object on success." msgstr "" +"Используйте этот метод, чтобы получить актуальную информацию о чате (текущее " +"имя пользователя для персональных диалогов, текущий username пользователя, " +"группы или канала и т.д.). В случае успеха возвращает объект Chat." #: of telebot.async_telebot.AsyncTeleBot.get_chat:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchat" #: of telebot.async_telebot.AsyncTeleBot.get_chat:6 #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators:7 @@ -2501,10 +2520,12 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" +"Уникальный id чата или username супер-группы или канала " +"(в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.get_chat:9 msgid "Chat information" -msgstr "" +msgstr "Информация о чате" #: of telebot.async_telebot.AsyncTeleBot.get_chat:10 msgid ":class:`telebot.types.Chat`" diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 11555e9bf..3e3c13c34 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -1266,7 +1266,8 @@ msgstr "" "Список отформатированных частей новой подписи в формате JSON, " "можно использовать вместо parse_mode" -#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 +#: of telebot.TeleBot.forward_message:5 +#: telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 #: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 #: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:26 #: telebot.TeleBot.send_game:11 telebot.TeleBot.send_invoice:67 @@ -1387,7 +1388,7 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" -"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " +"Используйте этот метод, чтобы создать дополнительную ссылку-приглашение в чат. Бот " "должен быть администратором чата и иметь соответствующие права администратора. " "Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " "ссылку-приглашение (ChatInviteLink)." @@ -2072,7 +2073,7 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" -"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " +"отключено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " "сообщение не является inline сообщением, возвращается новый объект Message, " "иначе возвращается True." @@ -2186,6 +2187,8 @@ msgid "" " target chat or username of the target channel (in the format " "@channelusername)" msgstr "" +"Обязательный, если не указан inline_message_id. Уникальный id чата " +"или username канала (в формате @channelusername)" #: of telebot.TeleBot.edit_message_media:13 #: telebot.TeleBot.edit_message_reply_markup:8 @@ -2195,6 +2198,7 @@ msgid "" "Required if inline_message_id is not specified. Identifier of the sent " "message" msgstr "" +"Обязательный, если не указан inline_message_id. id отправленного сообщения" #: of telebot.TeleBot.edit_message_media:23 #: telebot.TeleBot.edit_message_reply_markup:18 @@ -2204,39 +2208,43 @@ msgstr "" #: of telebot.TeleBot.edit_message_reply_markup:1 msgid "Use this method to edit only the reply markup of messages." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить только reply markup сообщения." #: of telebot.TeleBot.edit_message_reply_markup:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagereplymarkup" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagereplymarkup" #: of telebot.TeleBot.edit_message_text:1 msgid "Use this method to edit text and game messages." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить текстовые и игровые сообщения." #: of telebot.TeleBot.edit_message_text:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#editmessagetext" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editmessagetext" #: of telebot.TeleBot.edit_message_text:5 msgid "New text of the message, 1-4096 characters after entities parsing" -msgstr "" +msgstr "Новый текст сообщения, 1-4096 символов после форматирования" #: of telebot.TeleBot.edit_message_text:17 telebot.TeleBot.send_message:15 msgid "Mode for parsing entities in the message text." -msgstr "" +msgstr "Режим форматирования в тексте сообщения." #: of telebot.TeleBot.edit_message_text:20 msgid "" "List of special entities that appear in the message text, which can be " "specified instead of parse_mode" msgstr "" +"Список отформатированных частей в тексте сообщения, " +"можно использовать вместо parse_mode" #: of telebot.TeleBot.edit_message_text:23 telebot.TeleBot.send_message:21 msgid "Disables link previews for links in this message" -msgstr "" +msgstr "Отключает превью ссылок в сообщении" #: of telebot.TeleBot.edited_channel_post_handler:1 msgid "" @@ -2244,6 +2252,9 @@ msgid "" "edited. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" +"Обрабатывает новую версию поста в канале, который доступен боту и был изменён. " +"В качестве параметра, передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`." #: of telebot.TeleBot.edited_message_handler:1 msgid "" @@ -2251,15 +2262,18 @@ msgid "" " As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" +"Обрабатывает новую версию сообщения, которое доступно боту и было изменено. " +"В качестве параметра, передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`." #: of telebot.TeleBot.edited_message_handler:16 #: telebot.TeleBot.message_handler:47 msgid "list of chat types" -msgstr "" +msgstr "список видов чатов" #: of telebot.TeleBot.enable_save_next_step_handlers:1 msgid "Enable saving next step handlers (by default saving disabled)" -msgstr "" +msgstr "Разрешить сохранение next step хендлеров (по умолчанию сохранение отключено)" #: of telebot.TeleBot.enable_save_next_step_handlers:3 #: telebot.TeleBot.enable_save_reply_handlers:3 @@ -2269,37 +2283,42 @@ msgid "" "saving capability for handlers. And the same implementation is now " "available with FileHandlerBackend" msgstr "" +"Эта функция, целью которой было включить возможность сохранения файлов для обработчиков, " +"явно назначает FileHandlerBackend (вместо Saver) просто для сохранения обратной совместимости. " +"Та же реализация теперь доступна с FileHandlerBackend." #: of telebot.TeleBot.enable_save_next_step_handlers:7 #: telebot.TeleBot.enable_save_reply_handlers:7 msgid "Delay between changes in handlers and saving, defaults to 120" -msgstr "" +msgstr "Задержка между изменениями в хендлерах и сохранении, по умолчанию 120" #: of telebot.TeleBot.enable_save_next_step_handlers:10 msgid "Filename of save file, defaults to \"./.handler-saves/step.save\"" -msgstr "" +msgstr "Имя файла для сохранения, по умолчанию \"./.handler-saves/step.save\"" #: of telebot.TeleBot.enable_save_reply_handlers:1 msgid "Enable saving reply handlers (by default saving disable)" -msgstr "" +msgstr "Разрешить сохранение reply хендлеров (по умолчанию сохранение отключено)" #: of telebot.TeleBot.enable_save_reply_handlers:10 msgid "Filename of save file, defaults to \"./.handler-saves/reply.save\"" -msgstr "" +msgstr "Имя файла для сохранения, по умолчанию \"./.handler-saves/reply.save\"" #: of telebot.TeleBot.enable_saving_states:1 msgid "Enable saving states (by default saving disabled)" -msgstr "" +msgstr "Разрешить сохранение стейтов (по умолчанию сохранение отключено)" #: of telebot.TeleBot.enable_saving_states:4 msgid "" "It is recommended to pass a :class:`~telebot.storage.StatePickleStorage` " "instance as state_storage to TeleBot class." msgstr "" +"Рекомендуется передавать экземпляр класса :class:`~telebot.storage.StatePickleStorage` " +"в качестве state_storage при инициализации класса TeleBot вместо использования этой функции." #: of telebot.TeleBot.enable_saving_states:7 msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" -msgstr "" +msgstr "Имя файла для сохранения, по умолчанию \"./.state-save/states.pkl\"" #: of telebot.TeleBot.export_chat_invite_link:1 msgid "" @@ -2307,34 +2326,33 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы создать или заменить главную ссылку-приглашение " +"в супер-группу или канал, созданную ботом. Бот должен быть администратором чата " +"и иметь соответствующие права администратора." #: of telebot.TeleBot.export_chat_invite_link:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#exportchatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#exportchatinvitelink" #: of telebot.TeleBot.export_chat_invite_link:10 msgid "exported invite link as String on success." -msgstr "" +msgstr "новая ссылка-приглашение (String) в случае успеха." #: of telebot.TeleBot.forward_message:1 msgid "Use this method to forward messages of any kind." -msgstr "" +msgstr "Используйте этот метод, чтобы переслать любое сообщение." #: of telebot.TeleBot.forward_message:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" -msgstr "" - -#: of telebot.TeleBot.forward_message:5 -msgid "" -"Sends the message silently. Users will receive a notification with no " -"sound" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#forwardmessage" #: of telebot.TeleBot.forward_message:17 msgid "Protects the contents of the forwarded message from forwarding and saving" -msgstr "" +msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" #: of telebot.TeleBot.get_chat:1 msgid "" @@ -2342,10 +2360,13 @@ msgid "" "name of the user for one-on-one conversations, current username of a " "user, group or channel, etc.). Returns a Chat object on success." msgstr "" +"Используйте этот метод, чтобы получить актуальную информацию о чате (текущее " +"имя пользователя для персональных диалогов, текущий username пользователя, " +"группы или канала и т.д.). В случае успеха возвращает объект Chat." #: of telebot.TeleBot.get_chat:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchat" #: of telebot.TeleBot.get_chat:6 telebot.TeleBot.get_chat_administrators:7 #: telebot.TeleBot.get_chat_member_count:5 telebot.TeleBot.leave_chat:5 @@ -2353,10 +2374,12 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" +"Уникальный id чата или username супер-группы или канала " +"(в формате @channelusername)" #: of telebot.TeleBot.get_chat:9 msgid "Chat information" -msgstr "" +msgstr "Информация о чате" #: of telebot.TeleBot.get_chat:10 msgid ":class:`telebot.types.Chat`" From dd50273c951f1c79a32a969ef936a3f00d9fae81 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Sat, 31 Dec 2022 14:16:13 +0200 Subject: [PATCH 1222/1808] Some translations Up to lines sync_version.po:2709 and async_version.po:2853 --- .../locales/ru/LC_MESSAGES/async_version.po | 100 +++++++++++++----- .../locales/ru/LC_MESSAGES/sync_version.po | 98 +++++++++++++---- 2 files changed, 150 insertions(+), 48 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index 1ad8088ed..b84d41b7e 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -2462,7 +2462,7 @@ msgid "" "have the appropriate admin rights." msgstr "" "Используйте этот метод, чтобы создать или заменить главную ссылку-приглашение " -"в супер-группу или канал, созданную ботом. Бот должен быть администратором чата " +"в супергруппу или канал, созданную ботом. Бот должен быть администратором чата " "и иметь соответствующие права администратора." #: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:4 @@ -2496,7 +2496,7 @@ msgid "" " forum supergroups only" msgstr "" "Уникальный id топика, в который нужно переслать сообщение; только для " -"супер-групп с топиками" +"супергрупп с топиками" #: of telebot.async_telebot.AsyncTeleBot.get_chat:1 msgid "" @@ -2520,7 +2520,7 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" -"Уникальный id чата или username супер-группы или канала " +"Уникальный id чата или username супергруппы или канала " "(в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.get_chat:9 @@ -2537,16 +2537,21 @@ msgid "" "returns an Array of ChatMember objects that contains information about " "all chat administrators except other bots." msgstr "" +"Используйте этот метод, чтобы получить список администраторов чата. " +"В случае успеха, возвращает массив объектов ChatMember, содержащих информацию " +"обо всех администраторах чата, кроме других ботов." #: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getchatadministrators" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getchatadministrators" #: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:9 msgid "List made of ChatMember objects." -msgstr "" +msgstr "Список объектов ChatMember." #: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:10 msgid ":obj:`list` of :class:`telebot.types.ChatMember`" @@ -2557,14 +2562,16 @@ msgid "" "Use this method to get information about a member of a chat. Returns a " "ChatMember object on success." msgstr "" +"Используйте этот метод, чтобы получить информацию об участнике чата. Возвращает " +"объект ChatMember в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_chat_member:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchatmember" #: of telebot.async_telebot.AsyncTeleBot.get_chat_member:11 msgid "Returns ChatMember object on success." -msgstr "" +msgstr "Возвращает объект ChatMember в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_chat_member:12 msgid ":class:`telebot.types.ChatMember`" @@ -2572,17 +2579,19 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:1 msgid "Use this method to get the number of members in a chat." -msgstr "" +msgstr "Используйте этот метод, чтобы получить количество участников чата." #: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getchatmembercount" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getchatmembercount" #: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:8 msgid "Number of members in the chat." -msgstr "" +msgstr "Количество участников чата." #: of telebot.async_telebot.AsyncTeleBot.get_chat_member_count:9 msgid ":obj:`int`" @@ -2593,18 +2602,24 @@ msgid "" "Use this method to get the current value of the bot's menu button in a " "private chat, or the default menu button. Returns MenuButton on success." msgstr "" +"Используйте этот метод, чтобы получить текущее значение кнопки menu в " +"приватном чате, или кнопку menu по умолчанию. Возвращает MenuButton в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:5 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#getchatmenubutton" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getchatmenubutton" #: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:7 msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be returned." msgstr "" +"Уникальный id приватного чата. Если не указан, будет возвращена " +"кнопка menu по умолчанию." #: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:11 msgid "types.MenuButton" @@ -2619,16 +2634,19 @@ msgid "" "Use this method to get information about custom emoji stickers by their " "identifiers. Returns an Array of Sticker objects." msgstr "" +"Используйте этот метод, чтобы получить информацию о кастомных эмодзи по их " +"id. Возвращает массив объектов Sticker." #: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:4 msgid "" "List of custom emoji identifiers. At most 200 custom emoji identifiers " "can be specified." msgstr "" +"Список id кастомных эмодзи. Можно указать не более 200 id." #: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:7 msgid "Returns an Array of Sticker objects." -msgstr "" +msgstr "Возвращает массив объектов Sticker." #: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:8 msgid ":obj:`list` of :class:`telebot.types.Sticker`" @@ -2642,14 +2660,19 @@ msgid "" "link will be valid for at least 1 hour. When the link expires, a new one " "can be requested by calling get_file again." msgstr "" +"Используйте этот метод, чтобы получить базовую информацию о файле и подготовить " +"его к скачиванию. На текущий момент, боты могут скачивать файлы весом до 20MB. " +"В случае успеха, возвращается объект File. Гарантируется, что ссылка на скачивание " +"будет актуальна как минимум 1 час. Когда ссылка перестаёт быть актуальной, новая " +"может быть снова запрошена с помощью get_file." #: of telebot.async_telebot.AsyncTeleBot.get_file:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getfile" #: of telebot.async_telebot.AsyncTeleBot.get_file:9 msgid "File identifier" -msgstr "" +msgstr "id файла" #: of telebot.async_telebot.AsyncTeleBot.get_file:12 #: telebot.async_telebot.AsyncTeleBot.upload_sticker_file:14 @@ -2658,15 +2681,15 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.get_file_url:1 msgid "Get a valid URL for downloading a file." -msgstr "" +msgstr "Получить актуальную ссылку для скачивания файла." #: of telebot.async_telebot.AsyncTeleBot.get_file_url:3 msgid "File identifier to get download URL for." -msgstr "" +msgstr "id файла для получения ссылки на скачивание." #: of telebot.async_telebot.AsyncTeleBot.get_file_url:6 msgid "URL for downloading the file." -msgstr "" +msgstr "Ссылка для скачивания файла." #: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:1 msgid "" @@ -2674,16 +2697,21 @@ msgid "" "forum topic icon by any user. Requires no parameters. Returns an Array of" " Sticker objects." msgstr "" +"Используйте этот метод, чтобы получить кастомные эмодзи, которые могут быть " +"использованы любыми пользователями в качестве иконок топиков. Не требует параметров. " +"Возвращает массив объектов Sticker." #: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getforumtopiciconstickers" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getforumtopiciconstickers" #: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:6 msgid "On success, a list of StickerSet objects is returned." -msgstr "" +msgstr "В случае успеха, возвращается список объектов StickerSet." #: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:7 msgid "List[:class:`telebot.types.StickerSet`]" @@ -2695,6 +2723,9 @@ msgid "" "of the specified user and several of their neighbors in a game. On " "success, returns an Array of GameHighScore objects." msgstr "" +"Используйте этот метод, чтобы получить данные для таблицы рекордов. Вернёт " +"очки указанного пользователя и несколько соседних результатов. В случае успеха, " +"возвращает массив объектов GameHighScore." #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:4 msgid "" @@ -2703,45 +2734,53 @@ msgid "" "users if the user and their neighbors are not among them. Please note " "that this behavior is subject to change." msgstr "" +"На текущий момент этот метод вернёт очки указанного пользователя и по два соседних " +"результата с каждой стороны. Также вернет результаты трёх лучших игроков, если " +"результат пользователя и соседние не являются тремя лучшими. Пожалуйста учитывайте, " +"что это поведение может быть изменено." #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:8 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getgamehighscores" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getgamehighscores" #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:10 #: telebot.async_telebot.AsyncTeleBot.retrieve_data:3 #: telebot.async_telebot.AsyncTeleBot.set_game_score:5 #: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:9 msgid "User identifier" -msgstr "" +msgstr "id пользователя" #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:22 msgid "On success, returns an Array of GameHighScore objects." -msgstr "" +msgstr "В случае успеха, возвращает массив объектов GameHighScore." #: of telebot.async_telebot.AsyncTeleBot.get_me:1 msgid "Returns basic information about the bot in form of a User object." -msgstr "" +msgstr "Возвращает базовую информацию о боте в виде объекта User." #: of telebot.async_telebot.AsyncTeleBot.get_me:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getme" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getme" #: of telebot.async_telebot.AsyncTeleBot.get_my_commands:1 msgid "" "Use this method to get the current list of the bot's commands. Returns " "List of BotCommand on success." msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. Возвращает " +"список объектов BotCommand в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_my_commands:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getmycommands" #: of telebot.async_telebot.AsyncTeleBot.get_my_commands:15 msgid "List of BotCommand on success." -msgstr "" +msgstr "Список объектов BotCommand в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_my_commands:16 msgid ":obj:`list` of :class:`telebot.types.BotCommand`" @@ -2752,12 +2791,16 @@ msgid "" "Use this method to get the current default administrator rights of the " "bot. Returns ChatAdministratorRights on success." msgstr "" +"Используйте этот метод, чтобы получить текущие права администратора для бота по умолчанию. " +"Возвращает объект ChatAdministratorRights в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getmydefaultadministratorrights" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getmydefaultadministratorrights" #: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:6 msgid "" @@ -2765,10 +2808,13 @@ msgid "" " Otherwise, the default administrator rights of the bot for groups and " "supergroups will be returned." msgstr "" +"Передайте True, чтобы получить права администратора для бота по умолчанию в каналах. " +"Иначе, будут возвращены права администратора для бота по умолчанию в группах и " +"супергруппах." #: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:9 msgid "Returns ChatAdministratorRights on success." -msgstr "" +msgstr "Возвращает объект ChatAdministratorRights в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:10 msgid ":class:`telebot.types.ChatAdministratorRights`" @@ -2779,10 +2825,12 @@ msgid "" "Gets current state of a user. Not recommended to use this method. But it " "is ok for debugging." msgstr "" +"Получает текущее состояние (стейт) пользователя. Не рекомендуется " +"использовать этот метод. Но это удобно для дебага." #: of telebot.async_telebot.AsyncTeleBot.get_state:10 msgid "state of a user" -msgstr "" +msgstr "состояние (стейт) пользователя" #: of telebot.async_telebot.AsyncTeleBot.get_state:11 msgid ":obj:`int` or :obj:`str` or :class:`telebot.types.State`" @@ -2793,14 +2841,16 @@ msgid "" "Use this method to get a sticker set. On success, a StickerSet object is " "returned." msgstr "" +"Используйте этот метод, чтобы получить стикерпак. В случае успеха, возвращается " +"объект StickerSet." #: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getstickerset" #: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:8 msgid "On success, a StickerSet object is returned." -msgstr "" +msgstr "В случае успеха, возвращается объект StickerSet." #: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:9 #: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:14 diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 3e3c13c34..1bde42d63 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -2327,7 +2327,7 @@ msgid "" "have the appropriate admin rights." msgstr "" "Используйте этот метод, чтобы создать или заменить главную ссылку-приглашение " -"в супер-группу или канал, созданную ботом. Бот должен быть администратором чата " +"в супергруппу или канал, созданную ботом. Бот должен быть администратором чата " "и иметь соответствующие права администратора." #: of telebot.TeleBot.export_chat_invite_link:4 @@ -2374,7 +2374,7 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" -"Уникальный id чата или username супер-группы или канала " +"Уникальный id чата или username супергруппы или канала " "(в формате @channelusername)" #: of telebot.TeleBot.get_chat:9 @@ -2391,16 +2391,21 @@ msgid "" "returns an Array of ChatMember objects that contains information about " "all chat administrators except other bots." msgstr "" +"Используйте этот метод, чтобы получить список администраторов чата. " +"В случае успеха, возвращает массив объектов ChatMember, содержащих информацию " +"обо всех администраторах чата, кроме других ботов." #: of telebot.TeleBot.get_chat_administrators:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getchatadministrators" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getchatadministrators" #: of telebot.TeleBot.get_chat_administrators:9 msgid "List made of ChatMember objects." -msgstr "" +msgstr "Список объектов ChatMember." #: of telebot.TeleBot.get_chat_administrators:10 msgid ":obj:`list` of :class:`telebot.types.ChatMember`" @@ -2411,14 +2416,16 @@ msgid "" "Use this method to get information about a member of a chat. Returns a " "ChatMember object on success." msgstr "" +"Используйте этот метод, чтобы получить информацию об участнике чата. Возвращает " +"объект ChatMember в случае успеха." #: of telebot.TeleBot.get_chat_member:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchatmember" #: of telebot.TeleBot.get_chat_member:11 msgid "Returns ChatMember object on success." -msgstr "" +msgstr "Возвращает объект ChatMember в случае успеха." #: of telebot.TeleBot.get_chat_member:12 msgid ":class:`telebot.types.ChatMember`" @@ -2426,17 +2433,19 @@ msgstr "" #: of telebot.TeleBot.get_chat_member_count:1 msgid "Use this method to get the number of members in a chat." -msgstr "" +msgstr "Используйте этот метод, чтобы получить количество участников чата." #: of telebot.TeleBot.get_chat_member_count:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getchatmembercount" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getchatmembercount" #: of telebot.TeleBot.get_chat_member_count:8 msgid "Number of members in the chat." -msgstr "" +msgstr "Количество участников чата." #: of telebot.TeleBot.get_chat_member_count:9 msgid ":obj:`int`" @@ -2447,18 +2456,24 @@ msgid "" "Use this method to get the current value of the bot's menu button in a " "private chat, or the default menu button. Returns MenuButton on success." msgstr "" +"Используйте этот метод, чтобы получить текущее значение кнопки menu в " +"приватном чате, или кнопку menu по умолчанию. Возвращает MenuButton в случае успеха." #: of telebot.TeleBot.get_chat_menu_button:5 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#getchatmenubutton" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getchatmenubutton" #: of telebot.TeleBot.get_chat_menu_button:7 msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be returned." msgstr "" +"Уникальный id приватного чата. Если не указан, будет возвращена " +"кнопка menu по умолчанию." #: of telebot.TeleBot.get_chat_menu_button:11 msgid "types.MenuButton" @@ -2473,16 +2488,19 @@ msgid "" "Use this method to get information about custom emoji stickers by their " "identifiers. Returns an Array of Sticker objects." msgstr "" +"Используйте этот метод, чтобы получить информацию о кастомных эмодзи по их " +"id. Возвращает массив объектов Sticker." #: of telebot.TeleBot.get_custom_emoji_stickers:4 msgid "" "List of custom emoji identifiers. At most 200 custom emoji identifiers " "can be specified." msgstr "" +"Список id кастомных эмодзи. Можно указать не более 200 id." #: of telebot.TeleBot.get_custom_emoji_stickers:7 msgid "Returns an Array of Sticker objects." -msgstr "" +msgstr "Возвращает массив объектов Sticker." #: of telebot.TeleBot.get_custom_emoji_stickers:8 msgid ":obj:`list` of :class:`telebot.types.Sticker`" @@ -2496,14 +2514,19 @@ msgid "" "link will be valid for at least 1 hour. When the link expires, a new one " "can be requested by calling get_file again." msgstr "" +"Используйте этот метод, чтобы получить базовую информацию о файле и подготовить " +"его к скачиванию. На текущий момент, боты могут скачивать файлы весом до 20MB. " +"В случае успеха, возвращается объект File. Гарантируется, что ссылка на скачивание " +"будет актуальна как минимум 1 час. Когда ссылка перестаёт быть актуальной, новая " +"может быть снова запрошена с помощью get_file." #: of telebot.TeleBot.get_file:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getfile" #: of telebot.TeleBot.get_file:9 msgid "File identifier" -msgstr "" +msgstr "id файла" #: of telebot.TeleBot.get_file:12 telebot.TeleBot.upload_sticker_file:14 msgid ":class:`telebot.types.File`" @@ -2511,15 +2534,15 @@ msgstr "" #: of telebot.TeleBot.get_file_url:1 msgid "Get a valid URL for downloading a file." -msgstr "" +msgstr "Получить актуальную ссылку для скачивания файла." #: of telebot.TeleBot.get_file_url:3 msgid "File identifier to get download URL for." -msgstr "" +msgstr "id файла для получения ссылки на скачивание." #: of telebot.TeleBot.get_file_url:6 msgid "URL for downloading the file." -msgstr "" +msgstr "Ссылка для скачивания файла." #: of telebot.TeleBot.get_forum_topic_icon_stickers:1 msgid "" @@ -2527,16 +2550,21 @@ msgid "" "forum topic icon by any user. Requires no parameters. Returns an Array of" " Sticker objects." msgstr "" +"Используйте этот метод, чтобы получить кастомные эмодзи, которые могут быть " +"использованы любыми пользователями в качестве иконок топиков. Не требует параметров. " +"Возвращает массив объектов Sticker." #: of telebot.TeleBot.get_forum_topic_icon_stickers:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getforumtopiciconstickers" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getforumtopiciconstickers" #: of telebot.TeleBot.get_forum_topic_icon_stickers:6 msgid "On success, a list of StickerSet objects is returned." -msgstr "" +msgstr "В случае успеха, возвращается список объектов StickerSet." #: of telebot.TeleBot.get_forum_topic_icon_stickers:7 msgid "List[:class:`telebot.types.StickerSet`]" @@ -2548,6 +2576,9 @@ msgid "" "of the specified user and several of their neighbors in a game. On " "success, returns an Array of GameHighScore objects." msgstr "" +"Используйте этот метод, чтобы получить данные для таблицы рекордов. Вернёт " +"очки указанного пользователя и несколько соседних результатов. В случае успеха, " +"возвращает массив объектов GameHighScore." #: of telebot.TeleBot.get_game_high_scores:4 msgid "" @@ -2556,21 +2587,27 @@ msgid "" "users if the user and their neighbors are not among them. Please note " "that this behavior is subject to change." msgstr "" +"На текущий момент этот метод вернёт очки указанного пользователя и по два соседних " +"результата с каждой стороны. Также вернет результаты трёх лучших игроков, если " +"результат пользователя и соседние не являются тремя лучшими. Пожалуйста учитывайте, " +"что это поведение может быть изменено." #: of telebot.TeleBot.get_game_high_scores:8 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getgamehighscores" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getgamehighscores" #: of telebot.TeleBot.get_game_high_scores:10 telebot.TeleBot.retrieve_data:3 #: telebot.TeleBot.set_game_score:5 telebot.TeleBot.set_sticker_set_thumb:9 msgid "User identifier" -msgstr "" +msgstr "id пользователя" #: of telebot.TeleBot.get_game_high_scores:22 msgid "On success, returns an Array of GameHighScore objects." -msgstr "" +msgstr "В случае успеха, возвращает массив объектов GameHighScore." #: of telebot.TeleBot.get_me:1 msgid "" @@ -2578,24 +2615,28 @@ msgid "" "parameters. Returns basic information about the bot in form of a User " "object." msgstr "" +"Простой метод для тестирования токена бота. Не требует параметров. " +"Возвращает базовую информацию о боте в виде объекта User." #: of telebot.TeleBot.get_me:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getme" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getme" #: of telebot.TeleBot.get_my_commands:1 msgid "" "Use this method to get the current list of the bot's commands. Returns " "List of BotCommand on success." msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. Возвращает " +"список объектов BotCommand в случае успеха." #: of telebot.TeleBot.get_my_commands:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getmycommands" #: of telebot.TeleBot.get_my_commands:15 msgid "List of BotCommand on success." -msgstr "" +msgstr "Список объектов BotCommand в случае успеха." #: of telebot.TeleBot.get_my_commands:16 msgid ":obj:`list` of :class:`telebot.types.BotCommand`" @@ -2606,12 +2647,16 @@ msgid "" "Use this method to get the current default administrator rights of the " "bot. Returns ChatAdministratorRights on success." msgstr "" +"Используйте этот метод, чтобы получить текущие права администратора для бота по умолчанию. " +"Возвращает объект ChatAdministratorRights в случае успеха." #: of telebot.TeleBot.get_my_default_administrator_rights:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getmydefaultadministratorrights" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getmydefaultadministratorrights" #: of telebot.TeleBot.get_my_default_administrator_rights:6 msgid "" @@ -2619,10 +2664,13 @@ msgid "" " Otherwise, the default administrator rights of the bot for groups and " "supergroups will be returned." msgstr "" +"Передайте True, чтобы получить права администратора для бота по умолчанию в каналах. " +"Иначе, будут возвращены права администратора для бота по умолчанию в группах и " +"супергруппах." #: of telebot.TeleBot.get_my_default_administrator_rights:9 msgid "Returns ChatAdministratorRights on success." -msgstr "" +msgstr "Возвращает объект ChatAdministratorRights в случае успеха." #: of telebot.TeleBot.get_my_default_administrator_rights:10 msgid ":class:`telebot.types.ChatAdministratorRights`" @@ -2633,10 +2681,12 @@ msgid "" "Gets current state of a user. Not recommended to use this method. But it " "is ok for debugging." msgstr "" +"Получает текущее состояние (стейт) пользователя. Не рекомендуется " +"использовать этот метод. Но это удобно для дебага." #: of telebot.TeleBot.get_state:10 msgid "state of a user" -msgstr "" +msgstr "состояние (стейт) пользователя" #: of telebot.TeleBot.get_state:11 msgid ":obj:`int` or :obj:`str` or :class:`telebot.types.State`" @@ -2647,14 +2697,16 @@ msgid "" "Use this method to get a sticker set. On success, a StickerSet object is " "returned." msgstr "" +"Используйте этот метод, чтобы получить стикерпак. В случае успеха, возвращается " +"объект StickerSet." #: of telebot.TeleBot.get_sticker_set:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getstickerset" #: of telebot.TeleBot.get_sticker_set:8 msgid "On success, a StickerSet object is returned." -msgstr "" +msgstr "В случае успеха, возвращается объект StickerSet." #: of telebot.TeleBot.get_sticker_set:9 telebot.TeleBot.set_chat_sticker_set:14 msgid ":class:`telebot.types.StickerSet`" From 667e82d0736c3b6e7a45b01d66008b4fd96016c3 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 2 Jan 2023 17:54:35 +0400 Subject: [PATCH 1223/1808] Update telebot/types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index efcf6de94..920fecc96 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5855,7 +5855,7 @@ def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, def to_dict(self): ret = super(InputMediaPhoto, self).to_dict() - if self.has_spoiler: + if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler return ret From 267a33c3298c76105f2a6d17573d0ee18770b4f4 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 2 Jan 2023 17:55:14 +0400 Subject: [PATCH 1224/1808] Update telebot/types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 920fecc96..8a5cfa403 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5930,7 +5930,7 @@ def to_dict(self): ret['duration'] = self.duration if self.supports_streaming: ret['supports_streaming'] = self.supports_streaming - if self.has_spoiler: + if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler return ret From 3be5015f9e79d9b691c9d164cf74633dd9507f59 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 2 Jan 2023 17:55:30 +0400 Subject: [PATCH 1225/1808] Update telebot/types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 8a5cfa403..2938674bb 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5999,7 +5999,7 @@ def to_dict(self): ret['height'] = self.height if self.duration: ret['duration'] = self.duration - if self.has_spoiler: + if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler return ret From 43cc2036545964a47e8a1779e02f113d3a9a55c9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 2 Jan 2023 18:00:20 +0300 Subject: [PATCH 1226/1808] Bump version to 4.9/0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 360e2a4e2..e4107399e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.8.0' +release = '4.9.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index c757e00e6..1e28db82b 100644 --- a/telebot/version.py +++ b/telebot/version.py @@ -1,3 +1,3 @@ # Versions should comply with PEP440. # This line is parsed in setup.py: -__version__ = '4.8.0' +__version__ = '4.9.0' From 0f7464e8c49629a9bd82e3dfe9687463e2d453a4 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Mon, 2 Jan 2023 17:09:48 +0200 Subject: [PATCH 1227/1808] Some translations Up to lines sync_version.po:3008 and async_version.po:3122 --- .../locales/ru/LC_MESSAGES/async_version.po | 80 ++++++++++++---- .../locales/ru/LC_MESSAGES/sync_version.po | 93 +++++++++++++++---- 2 files changed, 140 insertions(+), 33 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index b84d41b7e..c4b9f3e20 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -2862,10 +2862,12 @@ msgid "" "Use this method to receive incoming updates using long polling (wiki). An" " Array of Update objects is returned." msgstr "" +"Используйте этот метод, чтобы получить новые апдейты с помощью long polling-а (wiki). " +"Возвращается массив объектов Update." #: of telebot.async_telebot.AsyncTeleBot.get_updates:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getupdates" #: of telebot.async_telebot.AsyncTeleBot.get_updates:6 msgid "" @@ -2877,30 +2879,38 @@ msgid "" "be specified to retrieve updates starting from -offset update from the " "end of the updates queue. All previous updates will forgotten." msgstr "" +"id первого апдейта. Должен быть на единицу больше наибольшего id среди " +"ранее полученных апдейтов. По умолчанию, возвращается список апдейтов, " +"начиная с самого раннего неполученного. Апдейт считается полученным как " +"только вызван метод getUpdates со смещением больше, чем id этого апдейта. " +"Отрицательное смещение может быть указано для получения последних offset апдейтов. " +"Все предыдущие апдейты будут считаться полученными." #: of telebot.async_telebot.AsyncTeleBot.get_updates:12 msgid "" "Limits the number of updates to be retrieved. Values between 1-100 are " "accepted. Defaults to 100." msgstr "" +"Максимальное число апдейтов для получения. Допускаются значения от 1 до 100. " +"По умолчанию 100." #: of telebot.async_telebot.AsyncTeleBot.get_updates:15 #: telebot.async_telebot.AsyncTeleBot.get_webhook_info:6 #: telebot.async_telebot.AsyncTeleBot.polling:26 msgid "Request connection timeout" -msgstr "" +msgstr "Тайм-аут запроса" #: of telebot.async_telebot.AsyncTeleBot.get_updates:18 msgid "Array of string. List the types of updates you want your bot to receive." -msgstr "" +msgstr "Массив строк. Список видов апдейтов, которые вы хотите получать." #: of telebot.async_telebot.AsyncTeleBot.get_updates:21 msgid "Timeout in seconds for long polling." -msgstr "" +msgstr "Тайм-аут поллинга в секундах." #: of telebot.async_telebot.AsyncTeleBot.get_updates:24 msgid "An Array of Update objects is returned." -msgstr "" +msgstr "Возвращается массив объектов Update." #: of telebot.async_telebot.AsyncTeleBot.get_updates:25 msgid ":obj:`list` of :class:`telebot.types.Update`" @@ -2911,24 +2921,32 @@ msgid "" "Use this method to get a list of profile pictures for a user. Returns a " ":class:`telebot.types.UserProfilePhotos` object." msgstr "" +"Используйте этот метод, чтобы получить список аватарок пользователя. " +"Возвращает объект :class:`telebot.types.UserProfilePhotos`." #: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getuserprofilephotos" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getuserprofilephotos" #: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:9 msgid "" "Sequential number of the first photo to be returned. By default, all " "photos are returned." msgstr "" +"Порядковый номер первого фото для получения. По умолчанию, возвращаются " +"все фото." #: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:12 msgid "" "Limits the number of photos to be retrieved. Values between 1-100 are " "accepted. Defaults to 100." msgstr "" +"Максимальное число фото для получения. Допускаются значения от 1 до 100. " +"По умолчанию 100." #: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:15 msgid "" @@ -2946,14 +2964,17 @@ msgid "" " success, returns a WebhookInfo object. If the bot is using getUpdates, " "will return an object with the url field empty." msgstr "" +"Используйте этот метод, чтобы получить текущий статус вебхука. Не требует параметров. " +"В случае успеха возвращает объект WebhookInfo. Если бот использует getUpdates, " +"вернёт объект с пустым атрибутом url." #: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getwebhookinfo" #: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:9 msgid "On success, returns a WebhookInfo object." -msgstr "" +msgstr "В случае успеха, возвращает объект WebhookInfo." #: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:10 msgid ":class:`telebot.types.WebhookInfo`" @@ -2964,31 +2985,35 @@ msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" " polling." msgstr "" +"Запустить поллинг в бесконечном цикле с обработкой исключений, чтобы избежать " +"непредвиденных остановок поллинга." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:4 #: telebot.async_telebot.AsyncTeleBot.polling:15 msgid "Install watchdog and psutil before using restart_on_change option." -msgstr "" +msgstr "Установите watchdog и psutil, чтобы использовать restart_on_change." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:6 #: telebot.async_telebot.AsyncTeleBot.polling:29 msgid "Timeout in seconds for get_updates(Defaults to None)" -msgstr "" +msgstr "Тайм-аут get_updates в секундах(по умолчанию None)" #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:9 #: telebot.async_telebot.AsyncTeleBot.polling:20 msgid "skip old updates" -msgstr "" +msgstr "пропускать старые апдейты" #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:12 msgid "Aiohttp's request timeout. Defaults to 5 minutes(aiohttp.ClientTimeout)." -msgstr "" +msgstr "Тайм-аут запроса aiohttp. По умолчанию 5 минут(aiohttp.ClientTimeout)." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:15 msgid "" "Custom logging level for infinity_polling logging. Use logger levels from" " logging as a value. None/NOTSET = no error logging" msgstr "" +"Кастомный уровень логирования для infinity_polling. Используйте уровни из " +"logging в качестве значений. None/NOTSET = не логировать ошибки." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:19 #: telebot.async_telebot.AsyncTeleBot.polling:32 @@ -3013,6 +3038,12 @@ msgid "" "types except chat_member (default). If not specified, the previous " "setting will be used." msgstr "" +"Список видов апдейтов, которые вы хотите получать. Например, укажите " +"[“message”, “edited_channel_post”, “callback_query”], чтобы получать " +"апдейты только этих видов. Полный список доступных видов апдейтов - " +"util.update_types. Укажите пустой список, чтобы получать все апдейты, " +"кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:25 #: telebot.async_telebot.AsyncTeleBot.polling:38 @@ -3021,35 +3052,42 @@ msgid "" " call to the get_updates, so unwanted updates may be received for a short" " period of time." msgstr "" +"Пожалуйста учитывайте, что этот параметр не влияет на апдейты, отправленные " +"до вызова get_updates, поэтому нежелательные апдейты могут быть получены " +"в течение короткого периода времени." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:29 msgid "Restart a file on file(s) change. Defaults to False" -msgstr "" +msgstr "Перезапуск при изменении файлов. По умолчанию False" #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:32 #: telebot.async_telebot.AsyncTeleBot.polling:48 msgid "Path to watch for changes. Defaults to current directory" -msgstr "" +msgstr "Путь для мониторинга изменений. По умолчанию текущая директория." #: of telebot.async_telebot.AsyncTeleBot.inline_handler:1 msgid "" "Handles new incoming inline query. As a parameter to the decorator " "function, it passes :class:`telebot.types.InlineQuery` object." msgstr "" +"Обрабатывает inline query. В качестве параметра, передаёт в декорируемую " +"функцию объект :class:`telebot.types.InlineQuery`." #: of telebot.async_telebot.AsyncTeleBot.kick_chat_member:1 msgid "This function is deprecated. Use `ban_chat_member` instead" -msgstr "" +msgstr "Эта функция устарела. Используйте `ban_chat_member`" #: of telebot.async_telebot.AsyncTeleBot.leave_chat:1 msgid "" "Use this method for your bot to leave a group, supergroup or channel. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы покинуть группу, супергруппу или канал. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.leave_chat:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#leavechat" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#leavechat" #: of telebot.async_telebot.AsyncTeleBot.log_out:1 msgid "" @@ -3060,10 +3098,16 @@ msgid "" " not be able to log in back to the cloud Bot API server for 10 minutes. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы отключиться от облачного Bot API сервера " +"перед локальным запуском бота. Вы ДОЛЖНЫ отключить бота перед тем, как " +"запускать его локально, иначе нет никаких гарантий, что бот будет получать " +"апдейты. После успешного вызова, вы можете тут же подключиться к локальному " +"серверу, но не сможете подключиться обратно к облачному Bot API серверу в " +"течение 10 минут. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.log_out:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#logout" #: of telebot.async_telebot.AsyncTeleBot.message_handler:1 msgid "" @@ -3072,6 +3116,10 @@ msgid "" ":class:`telebot.types.Message` object. All message handlers are tested in" " the order they were added." msgstr "" +"Обрабатывает входящие сообщения всех видов - text, photo, sticker, и т.д. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`. Все хендлеры сообщений проверяются в том " +"порядке, в котором были добавлены." #: of telebot.async_telebot.AsyncTeleBot.message_handler:5 msgid "Example:" diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 1bde42d63..c36f54d93 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -2717,10 +2717,12 @@ msgid "" "Use this method to receive incoming updates using long polling (wiki). An" " Array of Update objects is returned." msgstr "" +"Используйте этот метод, чтобы получить новые апдейты с помощью long polling-а (wiki). " +"Возвращается массив объектов Update." #: of telebot.TeleBot.get_updates:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getupdates" #: of telebot.TeleBot.get_updates:6 msgid "" @@ -2732,29 +2734,37 @@ msgid "" "be specified to retrieve updates starting from -offset update from the " "end of the updates queue. All previous updates will forgotten." msgstr "" +"id первого апдейта. Должен быть на единицу больше наибольшего id среди " +"ранее полученных апдейтов. По умолчанию, возвращается список апдейтов, " +"начиная с самого раннего неполученного. Апдейт считается полученным как " +"только вызван метод getUpdates со смещением больше, чем id этого апдейта. " +"Отрицательное смещение может быть указано для получения последних offset апдейтов. " +"Все предыдущие апдейты будут считаться полученными." #: of telebot.TeleBot.get_updates:12 msgid "" "Limits the number of updates to be retrieved. Values between 1-100 are " "accepted. Defaults to 100." msgstr "" +"Максимальное число апдейтов для получения. Допускаются значения от 1 до 100. " +"По умолчанию 100." #: of telebot.TeleBot.get_updates:15 telebot.TeleBot.get_webhook_info:6 #: telebot.TeleBot.polling:21 msgid "Request connection timeout" -msgstr "" +msgstr "Тайм-аут запроса" #: of telebot.TeleBot.get_updates:18 msgid "Array of string. List the types of updates you want your bot to receive." -msgstr "" +msgstr "Массив строк. Список видов апдейтов, которые вы хотите получать." #: of telebot.TeleBot.get_updates:21 msgid "Timeout in seconds for long polling." -msgstr "" +msgstr "Тайм-аут поллинга в секундах." #: of telebot.TeleBot.get_updates:24 msgid "An Array of Update objects is returned." -msgstr "" +msgstr "Возвращается массив объектов Update." #: of telebot.TeleBot.get_updates:25 msgid ":obj:`list` of :class:`telebot.types.Update`" @@ -2765,24 +2775,32 @@ msgid "" "Use this method to get a list of profile pictures for a user. Returns a " ":class:`telebot.types.UserProfilePhotos` object." msgstr "" +"Используйте этот метод, чтобы получить список аватарок пользователя. " +"Возвращает объект :class:`telebot.types.UserProfilePhotos`." #: of telebot.TeleBot.get_user_profile_photos:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getuserprofilephotos" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#getuserprofilephotos" #: of telebot.TeleBot.get_user_profile_photos:9 msgid "" "Sequential number of the first photo to be returned. By default, all " "photos are returned." msgstr "" +"Порядковый номер первого фото для получения. По умолчанию, возвращаются " +"все фото." #: of telebot.TeleBot.get_user_profile_photos:12 msgid "" "Limits the number of photos to be retrieved. Values between 1-100 are " "accepted. Defaults to 100." msgstr "" +"Максимальное число фото для получения. Допускаются значения от 1 до 100. " +"По умолчанию 100." #: of telebot.TeleBot.get_user_profile_photos:15 msgid "" @@ -2800,14 +2818,17 @@ msgid "" " success, returns a WebhookInfo object. If the bot is using getUpdates, " "will return an object with the url field empty." msgstr "" +"Используйте этот метод, чтобы получить текущий статус вебхука. Не требует параметров. " +"В случае успеха возвращает объект WebhookInfo. Если бот использует getUpdates, " +"вернёт объект с пустым атрибутом url." #: of telebot.TeleBot.get_webhook_info:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getwebhookinfo" #: of telebot.TeleBot.get_webhook_info:9 msgid "On success, returns a WebhookInfo object." -msgstr "" +msgstr "В случае успеха, возвращает объект WebhookInfo." #: of telebot.TeleBot.get_webhook_info:10 msgid ":class:`telebot.types.WebhookInfo`" @@ -2818,22 +2839,24 @@ msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" " polling." msgstr "" +"Запустить поллинг в бесконечном цикле с обработкой исключений, чтобы избежать " +"непредвиденных остановок поллинга." #: of telebot.TeleBot.infinity_polling:5 telebot.TeleBot.polling:13 msgid "Install watchdog and psutil before using restart_on_change option." -msgstr "" +msgstr "Установите watchdog и psutil, чтобы использовать restart_on_change." #: of telebot.TeleBot.infinity_polling:7 msgid "Request connection timeout." -msgstr "" +msgstr "Тайм-аут запроса." #: of telebot.TeleBot.infinity_polling:10 telebot.TeleBot.polling:27 msgid "Timeout in seconds for long polling (see API docs)" -msgstr "" +msgstr "Тайм-аут поллинга в секундах (см. документацию API)" #: of telebot.TeleBot.infinity_polling:13 telebot.TeleBot.polling:24 msgid "skip old updates" -msgstr "" +msgstr "пропускать старые апдейты" #: of telebot.TeleBot.infinity_polling:16 telebot.TeleBot.polling:30 msgid "" @@ -2841,6 +2864,8 @@ msgid "" "logging. Use logger levels from logging as a value. None/NOTSET = no " "error logging" msgstr "" +"Кастомный (отличающийся от логгера) уровень логирования для infinity_polling. " +"Используйте уровни из logging в качестве значений. None/NOTSET = не логировать ошибки." #: of telebot.TeleBot.infinity_polling:20 msgid "" @@ -2853,34 +2878,46 @@ msgid "" "updates created before the call to the get_updates, so unwanted updates " "may be received for a short period of time." msgstr "" +"Список видов апдейтов, которые вы хотите получать. Например, укажите " +"[“message”, “edited_channel_post”, “callback_query”], чтобы получать " +"апдейты только этих видов. Полный список доступных видов апдейтов - " +"util.update_types. Укажите пустой список, чтобы получать все апдейты, " +"кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка. Пожалуйста учитывайте, что этот параметр не влияет " +"на апдейты, отправленные до вызова get_updates, поэтому нежелательные апдейты " +"могут быть получены в течение короткого периода времени." #: of telebot.TeleBot.infinity_polling:29 telebot.TeleBot.polling:47 msgid "Restart a file on file(s) change. Defaults to False" -msgstr "" +msgstr "Перезапуск при изменении файлов. По умолчанию False" #: of telebot.TeleBot.infinity_polling:32 msgid "Path to watch for changes. Defaults to current directory" -msgstr "" +msgstr "Путь для мониторинга изменений. По умолчанию текущая директория." #: of telebot.TeleBot.inline_handler:1 msgid "" "Handles new incoming inline query. As a parameter to the decorator " "function, it passes :class:`telebot.types.InlineQuery` object." msgstr "" +"Обрабатывает inline query. В качестве параметра, передаёт в декорируемую " +"функцию объект :class:`telebot.types.InlineQuery`." #: of telebot.TeleBot.leave_chat:1 msgid "" "Use this method for your bot to leave a group, supergroup or channel. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы покинуть группу, супергруппу или канал. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.leave_chat:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#leavechat" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#leavechat" #: of telebot.TeleBot.load_next_step_handlers:1 msgid "Load next step handlers from save file" -msgstr "" +msgstr "Загрузить next step хендлеры из файла" #: of telebot.TeleBot.load_next_step_handlers:3 msgid "" @@ -2889,22 +2926,28 @@ msgid "" "recommended to use if next_step_backend was assigned as " "FileHandlerBackend before entering this function" msgstr "" +"Эта функция оставлена для обратной совместимости, для загрузки хендлеров " +"из файла с помощью FileHandlerBackend и рекомендуется к использованию только " +"если next_step_backend был определён как FileHandlerBackend до вызова этой функции" #: of telebot.TeleBot.load_next_step_handlers:8 msgid "" "Filename of the file where handlers was saved, defaults to \"./.handler-" "saves/step.save\"" msgstr "" +"Имя файла, в котором были сохранены хендлеры, по умолчанию \"./.handler-" +"saves/step.save\"" #: of telebot.TeleBot.load_next_step_handlers:11 msgid "" "If True is passed, after the loading file will be deleted, defaults to " "True" msgstr "" +"Если передано True, файл будет удалён после загрузки, по умолчанию True" #: of telebot.TeleBot.load_reply_handlers:1 msgid "Load reply handlers from save file" -msgstr "" +msgstr "Загрузить reply хендлеры из файла" #: of telebot.TeleBot.load_reply_handlers:3 msgid "" @@ -2913,18 +2956,24 @@ msgid "" "recommended to use if reply_backend was assigned as FileHandlerBackend " "before entering this function" msgstr "" +"Эта функция оставлена для обратной совместимости, для загрузки хендлеров " +"из файла с помощью FileHandlerBackend и рекомендуется к использованию только " +"если reply_backend был определён как FileHandlerBackend до вызова этой функции" #: of telebot.TeleBot.load_reply_handlers:7 msgid "" "Filename of the file where handlers was saved, defaults to \"./.handler-" "saves/reply.save\"" msgstr "" +"Имя файла, в котором были сохранены хендлеры, по умолчанию \"./.handler-" +"saves/reply.save\"" #: of telebot.TeleBot.load_reply_handlers:10 msgid "" "If True is passed, after the loading file will be deleted, defaults to " "True, defaults to True" msgstr "" +"Если передано True, файл будет удалён после загрузки, по умолчанию True" #: of telebot.TeleBot.log_out:1 msgid "" @@ -2935,10 +2984,16 @@ msgid "" " not be able to log in back to the cloud Bot API server for 10 minutes. " "Returns True on success." msgstr "" +"Используйте этот метод, чтобы отключиться от облачного Bot API сервера " +"перед локальным запуском бота. Вы ДОЛЖНЫ отключить бота перед тем, как " +"запускать его локально, иначе нет никаких гарантий, что бот будет получать " +"апдейты. После успешного вызова, вы можете тут же подключиться к локальному " +"серверу, но не сможете подключиться обратно к облачному Bot API серверу в " +"течение 10 минут. Возвращает True в случае успеха." #: of telebot.TeleBot.log_out:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#logout" #: of telebot.TeleBot.message_handler:1 msgid "" @@ -2947,6 +3002,10 @@ msgid "" ":class:`telebot.types.Message` object. All message handlers are tested in" " the order they were added." msgstr "" +"Обрабатывает входящие сообщения всех видов - text, photo, sticker, и т.д. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.Message`. Все хендлеры сообщений проверяются в том " +"порядке, в котором были добавлены." #: of telebot.TeleBot.message_handler:5 telebot.TeleBot.middleware_handler:7 #: telebot.TeleBot.register_middleware_handler:6 From 611bf4235c2ea7ee06a5637b62692a8396be5c14 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Mon, 2 Jan 2023 19:27:57 +0400 Subject: [PATCH 1228/1808] Updated documentation locales --- .../locales/en/LC_MESSAGES/async_version.po | 154 +++- .../locales/en/LC_MESSAGES/sync_version.po | 210 ++++- docs/source/locales/en/LC_MESSAGES/types.po | 309 +++++-- .../locales/ru/LC_MESSAGES/async_version.po | 713 ++++++++++------ .../locales/ru/LC_MESSAGES/sync_version.po | 780 +++++++++++------- docs/source/locales/ru/LC_MESSAGES/types.po | 309 +++++-- 6 files changed, 1665 insertions(+), 810 deletions(-) diff --git a/docs/source/locales/en/LC_MESSAGES/async_version.po b/docs/source/locales/en/LC_MESSAGES/async_version.po index f731d317e..083d1096f 100644 --- a/docs/source/locales/en/LC_MESSAGES/async_version.po +++ b/docs/source/locales/en/LC_MESSAGES/async_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-01-02 19:24+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,19 +18,19 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../async_version/index.rst:3 +#: ../../source/async_version/index.rst:3 msgid "AsyncTeleBot" msgstr "" -#: ../../async_version/index.rst:6 +#: ../../source/async_version/index.rst:6 msgid "Asynchronous pyTelegramBotAPI" msgstr "" -#: ../../async_version/index.rst:6 +#: ../../source/async_version/index.rst:6 msgid "ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation" msgstr "" -#: ../../async_version/index.rst:12 +#: ../../source/async_version/index.rst:12 msgid "AsyncTeleBot methods" msgstr "" @@ -91,6 +91,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.chat_member_handler #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic @@ -108,6 +109,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.edit_message_caption #: telebot.async_telebot.AsyncTeleBot.edit_message_live_location #: telebot.async_telebot.AsyncTeleBot.edit_message_media @@ -134,6 +136,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_updates #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.infinity_polling #: telebot.async_telebot.AsyncTeleBot.inline_handler #: telebot.async_telebot.AsyncTeleBot.leave_chat @@ -161,6 +164,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.reply_to #: telebot.async_telebot.AsyncTeleBot.reset_data #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member @@ -206,6 +210,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.stop_poll #: telebot.async_telebot.AsyncTeleBot.unban_chat_member #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message @@ -533,7 +538,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:43 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:13 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:6 -#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:19 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:22 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:13 #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 #: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:15 @@ -658,14 +663,14 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_my_commands:17 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:7 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:13 -#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:20 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:23 #: telebot.async_telebot.AsyncTeleBot.leave_chat:8 #: telebot.async_telebot.AsyncTeleBot.log_out:11 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:48 -#: telebot.async_telebot.AsyncTeleBot.send_chat_action:23 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:26 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:18 #: telebot.async_telebot.AsyncTeleBot.set_chat_description:14 #: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:15 @@ -981,7 +986,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 #: telebot.async_telebot.AsyncTeleBot.delete_message:22 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 -#: telebot.async_telebot.AsyncTeleBot.send_chat_action:22 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:25 msgid "Returns True on success." msgstr "" @@ -1002,15 +1007,19 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:10 #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.copy_message:5 #: telebot.async_telebot.AsyncTeleBot.create_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_message:13 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:13 #: telebot.async_telebot.AsyncTeleBot.forward_message:8 +#: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:7 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.send_animation:6 #: telebot.async_telebot.AsyncTeleBot.send_audio:9 #: telebot.async_telebot.AsyncTeleBot.send_dice:5 @@ -1028,6 +1037,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_title:9 #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:6 #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:8 +#: telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:7 #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:7 msgid "" @@ -1193,6 +1203,19 @@ msgstr "" msgid "Identifier of the topic to close" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:1 +msgid "" +"Use this method to close the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#closegeneralforumtopic" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.close_session:1 msgid "" "Closes existing session of aiohttp. Use this function if you stop " @@ -1371,7 +1394,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.copy_message:45 #: telebot.async_telebot.AsyncTeleBot.forward_message:26 #: telebot.async_telebot.AsyncTeleBot.reply_to:11 -#: telebot.async_telebot.AsyncTeleBot.send_animation:60 +#: telebot.async_telebot.AsyncTeleBot.send_animation:63 #: telebot.async_telebot.AsyncTeleBot.send_audio:63 #: telebot.async_telebot.AsyncTeleBot.send_contact:44 #: telebot.async_telebot.AsyncTeleBot.send_dice:35 @@ -1380,11 +1403,11 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_invoice:101 #: telebot.async_telebot.AsyncTeleBot.send_location:49 #: telebot.async_telebot.AsyncTeleBot.send_message:46 -#: telebot.async_telebot.AsyncTeleBot.send_photo:45 +#: telebot.async_telebot.AsyncTeleBot.send_photo:48 #: telebot.async_telebot.AsyncTeleBot.send_poll:70 #: telebot.async_telebot.AsyncTeleBot.send_sticker:39 #: telebot.async_telebot.AsyncTeleBot.send_venue:57 -#: telebot.async_telebot.AsyncTeleBot.send_video:61 +#: telebot.async_telebot.AsyncTeleBot.send_video:64 #: telebot.async_telebot.AsyncTeleBot.send_video_note:48 #: telebot.async_telebot.AsyncTeleBot.send_voice:49 msgid "On success, the sent Message is returned." @@ -1393,17 +1416,17 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.copy_message:46 #: telebot.async_telebot.AsyncTeleBot.forward_message:27 #: telebot.async_telebot.AsyncTeleBot.reply_to:12 -#: telebot.async_telebot.AsyncTeleBot.send_animation:61 +#: telebot.async_telebot.AsyncTeleBot.send_animation:64 #: telebot.async_telebot.AsyncTeleBot.send_audio:64 #: telebot.async_telebot.AsyncTeleBot.send_contact:45 #: telebot.async_telebot.AsyncTeleBot.send_dice:36 #: telebot.async_telebot.AsyncTeleBot.send_document:57 #: telebot.async_telebot.AsyncTeleBot.send_location:50 #: telebot.async_telebot.AsyncTeleBot.send_message:47 -#: telebot.async_telebot.AsyncTeleBot.send_photo:46 +#: telebot.async_telebot.AsyncTeleBot.send_photo:49 #: telebot.async_telebot.AsyncTeleBot.send_sticker:40 #: telebot.async_telebot.AsyncTeleBot.send_venue:58 -#: telebot.async_telebot.AsyncTeleBot.send_video:62 +#: telebot.async_telebot.AsyncTeleBot.send_video:65 #: telebot.async_telebot.AsyncTeleBot.send_video_note:49 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1892,13 +1915,35 @@ msgid "Identifier of the topic to edit" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:13 -msgid "New name of the topic, 1-128 characters" +msgid "" +"Optional, New name of the topic, 1-128 characters. If not specififed or " +"empty, the current name of the topic will be kept" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:17 +msgid "" +"Optional, New unique identifier of the custom emoji shown as the topic " +"icon. Use getForumTopicIconStickers to get all allowed custom emoji " +"identifiers. Pass an empty string to remove the icon. If not specified, " +"the current icon will be kept" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:1 +msgid "" +"Use this method to edit the name of the 'General' topic in a forum " +"supergroup chat. The bot must be an administrator in the chat for this to" +" work and must have can_manage_topics administrator rights. Returns True " +"on success." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:16 +#: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:5 msgid "" -"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " -"must be exactly 1 character long" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editgeneralforumtopic" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:10 +msgid "New topic name, 1-128 characters" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:1 @@ -2608,6 +2653,19 @@ msgstr "" msgid ":class:`telebot.types.WebhookInfo`" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:1 +msgid "" +"Use this method to hide the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#hidegeneralforumtopic" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:1 msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" @@ -3069,6 +3127,19 @@ msgstr "" msgid "Identifier of the topic to reopen" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:1 +msgid "" +"Use this method to reopen the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopengeneralforumtopic" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.reply_to:1 msgid "" "Convenience function for `send_message(message.chat.id, text, " @@ -3327,6 +3398,10 @@ msgstr "" msgid "Identifier of a message thread, in which the video will be sent" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_animation:60 +msgid "Pass True, if the animation should be sent as a spoiler" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -3433,6 +3508,10 @@ msgid "" "upload_video_note for video notes." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:22 +msgid "The thread to which the message will be sent(supergroups only)" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_contact:1 msgid "" "Use this method to send phone contacts. On success, the sent Message is " @@ -3761,6 +3840,10 @@ msgstr "" msgid "Mode for parsing entities in the photo caption." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_photo:45 +msgid "Pass True, if the photo should be sent as a spoiler" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_poll:1 msgid "" "Use this method to send a native poll. On success, the sent Message is " @@ -3959,6 +4042,10 @@ msgstr "" msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_video:61 +msgid "Pass True, if the video should be sent as a spoiler" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_video_note:1 msgid "" "As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " @@ -4495,6 +4582,19 @@ msgstr "" msgid "Unique identifier of the target sender chat." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:1 +msgid "" +"Use this method to unhide the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unhidegeneralforumtopic" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:1 msgid "" "Use this method to unpin a all pinned messages in a supergroup chat. The " @@ -4579,7 +4679,7 @@ msgstr "" msgid "Class for (next step|reply) handlers" msgstr "" -#: ../../async_version/index.rst:22 +#: ../../source/async_version/index.rst:22 msgid "Asyncio filters" msgstr "" @@ -4727,7 +4827,7 @@ msgstr "" msgid "Filter to check whether message starts with some text." msgstr "" -#: ../../async_version/index.rst:30 +#: ../../source/async_version/index.rst:30 msgid "Asyncio handler backends" msgstr "" @@ -4785,7 +4885,7 @@ msgstr "" msgid "Class representing common states." msgstr "" -#: ../../async_version/index.rst:41 +#: ../../source/async_version/index.rst:41 msgid "Extensions" msgstr "" @@ -4803,3 +4903,13 @@ msgid "" "manually by user." msgstr "" +#~ msgid "New name of the topic, 1-128 characters" +#~ msgstr "" + +#~ msgid "" +#~ "New custom emoji for the topic " +#~ "icon. Must be an emoji of type " +#~ "“tgs” and must be exactly 1 " +#~ "character long" +#~ msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/sync_version.po b/docs/source/locales/en/LC_MESSAGES/sync_version.po index 1c91ece19..766e23cb1 100644 --- a/docs/source/locales/en/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/en/LC_MESSAGES/sync_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-01-02 19:24+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,19 +18,19 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../sync_version/index.rst:3 +#: ../../source/sync_version/index.rst:3 msgid "TeleBot version" msgstr "" -#: ../../sync_version/index.rst:5 +#: ../../source/sync_version/index.rst:5 msgid "Synchronous pyTelegramBotAPI documentation" msgstr "" -#: ../../sync_version/index.rst:5 +#: ../../source/sync_version/index.rst:5 msgid "ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync" msgstr "" -#: ../../sync_version/index.rst:10 +#: ../../source/sync_version/index.rst:10 msgid "TeleBot methods" msgstr "" @@ -105,15 +105,17 @@ msgstr "" #: telebot.TeleBot.clear_reply_handlers_by_message_id #: telebot.TeleBot.clear_step_handler #: telebot.TeleBot.clear_step_handler_by_chat_id -#: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message -#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic -#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.close_forum_topic telebot.TeleBot.close_general_forum_topic +#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link +#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link +#: telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands #: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set #: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file #: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_general_forum_topic #: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup @@ -132,13 +134,14 @@ msgstr "" #: telebot.TeleBot.get_my_default_administrator_rights #: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set #: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling -#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat -#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers -#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler -#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message -#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler -#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.hide_general_forum_topic +#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler +#: telebot.TeleBot.leave_chat telebot.TeleBot.load_next_step_handlers +#: telebot.TeleBot.load_reply_handlers telebot.TeleBot.message_handler +#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler +#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler +#: telebot.TeleBot.poll_handler telebot.TeleBot.polling +#: telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler @@ -159,7 +162,8 @@ msgstr "" #: telebot.TeleBot.register_poll_handler #: telebot.TeleBot.register_pre_checkout_query_handler #: telebot.TeleBot.register_shipping_query_handler -#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.reopen_forum_topic +#: telebot.TeleBot.reopen_general_forum_topic telebot.TeleBot.reply_to #: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member #: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link #: telebot.TeleBot.run_webhooks telebot.TeleBot.send_animation @@ -184,6 +188,7 @@ msgstr "" #: telebot.TeleBot.shipping_query_handler #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unhide_general_forum_topic #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file @@ -490,7 +495,7 @@ msgstr "" #: telebot.TeleBot.create_new_sticker_set:43 #: telebot.TeleBot.delete_forum_topic:13 #: telebot.TeleBot.delete_sticker_from_set:6 -#: telebot.TeleBot.edit_forum_topic:19 telebot.TeleBot.reopen_forum_topic:13 +#: telebot.TeleBot.edit_forum_topic:22 telebot.TeleBot.reopen_forum_topic:13 #: telebot.TeleBot.set_sticker_position_in_set:11 #: telebot.TeleBot.set_sticker_set_thumb:15 #: telebot.TeleBot.unpin_all_forum_topic_messages:13 @@ -570,11 +575,11 @@ msgstr "" #: telebot.TeleBot.delete_forum_topic:14 telebot.TeleBot.delete_message:23 #: telebot.TeleBot.delete_my_commands:17 #: telebot.TeleBot.delete_sticker_from_set:7 telebot.TeleBot.delete_webhook:13 -#: telebot.TeleBot.edit_forum_topic:20 telebot.TeleBot.leave_chat:8 +#: telebot.TeleBot.edit_forum_topic:23 telebot.TeleBot.leave_chat:8 #: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 #: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 #: telebot.TeleBot.reopen_forum_topic:14 -#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:23 +#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:26 #: telebot.TeleBot.set_chat_administrator_custom_title:18 #: telebot.TeleBot.set_chat_description:14 #: telebot.TeleBot.set_chat_menu_button:15 @@ -878,7 +883,7 @@ msgstr "" #: of telebot.TeleBot.ban_chat_member:24 #: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 -#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:22 +#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:25 msgid "Returns True on success." msgstr "" @@ -898,21 +903,26 @@ msgid "" msgstr "" #: of telebot.TeleBot.ban_chat_sender_chat:10 -#: telebot.TeleBot.close_forum_topic:7 telebot.TeleBot.copy_message:5 +#: telebot.TeleBot.close_forum_topic:7 +#: telebot.TeleBot.close_general_forum_topic:7 telebot.TeleBot.copy_message:5 #: telebot.TeleBot.create_forum_topic:7 telebot.TeleBot.delete_forum_topic:7 #: telebot.TeleBot.delete_message:13 telebot.TeleBot.edit_forum_topic:7 +#: telebot.TeleBot.edit_general_forum_topic:7 #: telebot.TeleBot.edit_message_live_location:13 -#: telebot.TeleBot.forward_message:8 telebot.TeleBot.pin_chat_message:7 -#: telebot.TeleBot.reopen_forum_topic:7 telebot.TeleBot.send_animation:6 -#: telebot.TeleBot.send_audio:9 telebot.TeleBot.send_dice:5 -#: telebot.TeleBot.send_document:5 telebot.TeleBot.send_game:5 -#: telebot.TeleBot.send_location:5 telebot.TeleBot.send_media_group:6 -#: telebot.TeleBot.send_message:9 telebot.TeleBot.send_photo:5 -#: telebot.TeleBot.send_sticker:6 telebot.TeleBot.send_video:5 -#: telebot.TeleBot.send_video_note:6 telebot.TeleBot.send_voice:7 -#: telebot.TeleBot.set_chat_description:6 telebot.TeleBot.set_chat_title:9 +#: telebot.TeleBot.forward_message:8 telebot.TeleBot.hide_general_forum_topic:7 +#: telebot.TeleBot.pin_chat_message:7 telebot.TeleBot.reopen_forum_topic:7 +#: telebot.TeleBot.reopen_general_forum_topic:7 +#: telebot.TeleBot.send_animation:6 telebot.TeleBot.send_audio:9 +#: telebot.TeleBot.send_dice:5 telebot.TeleBot.send_document:5 +#: telebot.TeleBot.send_game:5 telebot.TeleBot.send_location:5 +#: telebot.TeleBot.send_media_group:6 telebot.TeleBot.send_message:9 +#: telebot.TeleBot.send_photo:5 telebot.TeleBot.send_sticker:6 +#: telebot.TeleBot.send_video:5 telebot.TeleBot.send_video_note:6 +#: telebot.TeleBot.send_voice:7 telebot.TeleBot.set_chat_description:6 +#: telebot.TeleBot.set_chat_title:9 #: telebot.TeleBot.stop_message_live_location:6 #: telebot.TeleBot.unban_chat_sender_chat:8 +#: telebot.TeleBot.unhide_general_forum_topic:7 #: telebot.TeleBot.unpin_all_forum_topic_messages:7 #: telebot.TeleBot.unpin_chat_message:7 msgid "" @@ -1100,6 +1110,19 @@ msgstr "" msgid "Identifier of the topic to close" msgstr "" +#: of telebot.TeleBot.close_general_forum_topic:1 +msgid "" +"Use this method to close the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.close_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#closegeneralforumtopic" +msgstr "" + #: of telebot.TeleBot.copy_message:1 msgid "Use this method to copy messages of any kind." msgstr "" @@ -1218,25 +1241,25 @@ msgid "Identifier of a message thread, in which the message will be sent" msgstr "" #: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 -#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:60 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:63 #: telebot.TeleBot.send_audio:63 telebot.TeleBot.send_contact:44 #: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:56 #: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 #: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 -#: telebot.TeleBot.send_photo:45 telebot.TeleBot.send_poll:70 +#: telebot.TeleBot.send_photo:48 telebot.TeleBot.send_poll:70 #: telebot.TeleBot.send_sticker:39 telebot.TeleBot.send_venue:57 -#: telebot.TeleBot.send_video:61 telebot.TeleBot.send_video_note:48 +#: telebot.TeleBot.send_video:64 telebot.TeleBot.send_video_note:48 #: telebot.TeleBot.send_voice:49 msgid "On success, the sent Message is returned." msgstr "" #: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 -#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:61 +#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:64 #: telebot.TeleBot.send_audio:64 telebot.TeleBot.send_contact:45 #: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:57 #: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 -#: telebot.TeleBot.send_photo:46 telebot.TeleBot.send_sticker:40 -#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:62 +#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:40 +#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:65 #: telebot.TeleBot.send_video_note:49 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1723,13 +1746,35 @@ msgid "Identifier of the topic to edit" msgstr "" #: of telebot.TeleBot.edit_forum_topic:13 -msgid "New name of the topic, 1-128 characters" +msgid "" +"Optional, New name of the topic, 1-128 characters. If not specififed or " +"empty, the current name of the topic will be kept" msgstr "" -#: of telebot.TeleBot.edit_forum_topic:16 +#: of telebot.TeleBot.edit_forum_topic:17 msgid "" -"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " -"must be exactly 1 character long" +"Optional, New unique identifier of the custom emoji shown as the topic " +"icon. Use getForumTopicIconStickers to get all allowed custom emoji " +"identifiers. Pass an empty string to remove the icon. If not specified, " +"the current icon will be kept" +msgstr "" + +#: of telebot.TeleBot.edit_general_forum_topic:1 +msgid "" +"Use this method to edit the name of the 'General' topic in a forum " +"supergroup chat. The bot must be an administrator in the chat for this to" +" work and must have can_manage_topics administrator rights. Returns True " +"on success." +msgstr "" + +#: of telebot.TeleBot.edit_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editgeneralforumtopic" +msgstr "" + +#: of telebot.TeleBot.edit_general_forum_topic:10 +msgid "New topic name, 1-128 characters" msgstr "" #: of telebot.TeleBot.edit_message_caption:1 @@ -2448,6 +2493,19 @@ msgstr "" msgid ":class:`telebot.types.WebhookInfo`" msgstr "" +#: of telebot.TeleBot.hide_general_forum_topic:1 +msgid "" +"Use this method to hide the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.hide_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#hidegeneralforumtopic" +msgstr "" + #: of telebot.TeleBot.infinity_polling:1 msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" @@ -3016,7 +3074,6 @@ msgid "Registers my chat member handler." msgstr "" #: of telebot.TeleBot.register_next_step_handler:1 -#: telebot.TeleBot.register_next_step_handler_by_chat_id:1 msgid "" "Registers a callback function to be notified when new message arrives " "after `message`." @@ -3045,8 +3102,14 @@ msgstr "" msgid "Args to pass in callback func" msgstr "" +#: of telebot.TeleBot.register_next_step_handler_by_chat_id:1 +msgid "" +"Registers a callback function to be notified when new message arrives in " +"the given chat." +msgstr "" + #: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 -msgid "The chat for which we want to handle new message." +msgid "The chat (chat ID) for which we want to handle new message." msgstr "" #: of telebot.TeleBot.register_poll_answer_handler:1 @@ -3087,6 +3150,19 @@ msgstr "" msgid "Identifier of the topic to reopen" msgstr "" +#: of telebot.TeleBot.reopen_general_forum_topic:1 +msgid "" +"Use this method to reopen the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.reopen_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopengeneralforumtopic" +msgstr "" + #: of telebot.TeleBot.reply_to:1 msgid "" "Convenience function for `send_message(message.chat.id, text, " @@ -3349,6 +3425,10 @@ msgstr "" msgid "Identifier of a message thread, in which the video will be sent" msgstr "" +#: of telebot.TeleBot.send_animation:60 +msgid "Pass True, if the animation should be sent as a spoiler" +msgstr "" + #: of telebot.TeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -3451,6 +3531,12 @@ msgid "" "upload_video_note for video notes." msgstr "" +#: of telebot.TeleBot.send_chat_action:22 +msgid "" +"The thread identifier of a message from which the reply will be " +"sent(supergroups only)" +msgstr "" + #: of telebot.TeleBot.send_contact:1 msgid "" "Use this method to send phone contacts. On success, the sent Message is " @@ -3774,6 +3860,10 @@ msgstr "" msgid "Mode for parsing entities in the photo caption." msgstr "" +#: of telebot.TeleBot.send_photo:45 +msgid "Pass True, if the photo should be sent as a spoiler" +msgstr "" + #: of telebot.TeleBot.send_poll:1 msgid "" "Use this method to send a native poll. On success, the sent Message is " @@ -3971,6 +4061,10 @@ msgstr "" msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "" +#: of telebot.TeleBot.send_video:61 +msgid "Pass True, if the video should be sent as a spoiler" +msgstr "" + #: of telebot.TeleBot.send_video_note:1 msgid "" "As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " @@ -4498,6 +4592,19 @@ msgstr "" msgid "Unique identifier of the target sender chat." msgstr "" +#: of telebot.TeleBot.unhide_general_forum_topic:1 +msgid "" +"Use this method to unhide the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unhide_general_forum_topic:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unhidegeneralforumtopic" +msgstr "" + #: of telebot.TeleBot.unpin_all_chat_messages:1 msgid "" "Use this method to unpin a all pinned messages in a supergroup chat. The " @@ -4588,7 +4695,7 @@ msgstr "" msgid ":class:`telebot.types.User`" msgstr "" -#: ../../sync_version/index.rst:17 +#: ../../source/sync_version/index.rst:17 msgid "custom_filters file" msgstr "" @@ -4727,7 +4834,7 @@ msgstr "" msgid "Filter to check whether message starts with some text." msgstr "" -#: ../../sync_version/index.rst:25 +#: ../../source/sync_version/index.rst:25 msgid "handler_backends file" msgstr "" @@ -4787,7 +4894,7 @@ msgstr "" msgid "Class representing common states." msgstr "" -#: ../../sync_version/index.rst:34 +#: ../../source/sync_version/index.rst:34 msgid "Extensions" msgstr "" @@ -4803,3 +4910,16 @@ msgid "" "manually by user." msgstr "" +#~ msgid "New name of the topic, 1-128 characters" +#~ msgstr "" + +#~ msgid "" +#~ "New custom emoji for the topic " +#~ "icon. Must be an emoji of type " +#~ "“tgs” and must be exactly 1 " +#~ "character long" +#~ msgstr "" + +#~ msgid "The chat for which we want to handle new message." +#~ msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/types.po b/docs/source/locales/en/LC_MESSAGES/types.po index e9417f378..db003a268 100644 --- a/docs/source/locales/en/LC_MESSAGES/types.po +++ b/docs/source/locales/en/LC_MESSAGES/types.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-01-02 19:24+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../types.rst:3 +#: ../../source/types.rst:3 msgid "Types of API" msgstr "" @@ -29,8 +29,10 @@ msgstr "" #: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 #: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 #: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 -#: telebot.types.ForumTopicReopened:1 telebot.types.Game:1 -#: telebot.types.GameHighScore:1 telebot.types.InlineQuery:1 +#: telebot.types.ForumTopicEdited:1 telebot.types.ForumTopicReopened:1 +#: telebot.types.Game:1 telebot.types.GameHighScore:1 +#: telebot.types.GeneralForumTopicHidden:1 +#: telebot.types.GeneralForumTopicUnhidden:1 telebot.types.InlineQuery:1 #: telebot.types.Invoice:1 telebot.types.Message:1 #: telebot.types.MessageAutoDeleteTimerChanged:1 telebot.types.MessageID:1 #: telebot.types.OrderInfo:1 telebot.types.PhotoSize:1 telebot.types.Poll:1 @@ -43,6 +45,7 @@ msgstr "" #: telebot.types.VideoChatParticipantsInvited:1 #: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 #: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 +#: telebot.types.WriteAccessAllowed:1 msgid "Bases: :py:class:`telebot.types.JsonDeserializable`" msgstr "" @@ -73,9 +76,10 @@ msgstr "" #: telebot.types.ChatPhoto telebot.types.ChosenInlineResult #: telebot.types.Contact telebot.types.Dice telebot.types.Document #: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic -#: telebot.types.ForumTopicCreated telebot.types.Game -#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton -#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.ForumTopicCreated telebot.types.ForumTopicEdited +#: telebot.types.Game telebot.types.GameHighScore +#: telebot.types.InlineKeyboardButton telebot.types.InlineKeyboardMarkup +#: telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio #: telebot.types.InlineQueryResultCachedAudio @@ -245,7 +249,7 @@ msgstr "" #: telebot.types.BotCommandScopeChatAdministrators:12 #: telebot.types.BotCommandScopeChatMember:15 #: telebot.types.BotCommandScopeDefault:8 telebot.types.CallbackQuery:30 -#: telebot.types.Chat:101 telebot.types.ChatAdministratorRights:46 +#: telebot.types.Chat:109 telebot.types.ChatAdministratorRights:46 #: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:20 #: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:59 #: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 @@ -280,21 +284,21 @@ msgstr "" #: telebot.types.InputContactMessageContent:17 #: telebot.types.InputInvoiceMessageContent:75 #: telebot.types.InputLocationMessageContent:26 -#: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:40 -#: telebot.types.InputMediaDocument:35 telebot.types.InputMediaPhoto:24 +#: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:37 +#: telebot.types.InputMediaDocument:32 telebot.types.InputMediaPhoto:24 #: telebot.types.InputMediaVideo:43 telebot.types.InputTextMessageContent:19 #: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 #: telebot.types.KeyboardButton:25 telebot.types.KeyboardButtonPollType:9 #: telebot.types.LabeledPrice:13 telebot.types.Location:25 #: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 #: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 -#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:230 +#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:246 #: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 #: telebot.types.MessageID:8 telebot.types.OrderInfo:17 #: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:15 #: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 #: telebot.types.ProximityAlertTriggered:14 -#: telebot.types.ReplyKeyboardMarkup:42 telebot.types.ReplyKeyboardRemove:18 +#: telebot.types.ReplyKeyboardMarkup:49 telebot.types.ReplyKeyboardRemove:18 #: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 #: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 #: telebot.types.Sticker:49 telebot.types.StickerSet:30 @@ -892,23 +896,35 @@ msgstr "" #: of telebot.types.Chat:80 msgid "" +"Optional. :obj:`bool`, if the chat has enabled aggressive anti-spam " +"protection. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:84 +msgid "" +"Optional. :obj:`bool`, if the chat has enabled hidden members. Returned " +"only in getChat." +msgstr "" + +#: of telebot.types.Chat:88 +msgid "" "Optional. :obj:`bool`, if messages from the chat can't be forwarded to " "other chats. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:84 +#: of telebot.types.Chat:92 msgid "" "Optional. For supergroups, name of group sticker set. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:87 +#: of telebot.types.Chat:95 msgid "" "Optional. :obj:`bool`, if the bot can change the group sticker set. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:91 +#: of telebot.types.Chat:99 msgid "" "Optional. Unique identifier for the linked chat, i.e. the discussion " "group identifier for a channel and vice versa; for supergroups and " @@ -919,13 +935,13 @@ msgid "" "in getChat." msgstr "" -#: of telebot.types.Chat:97 +#: of telebot.types.Chat:105 msgid "" "Optional. For supergroups, the location to which the supergroup is " "connected. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:102 +#: of telebot.types.Chat:110 msgid ":class:`telebot.types.Chat`" msgstr "" @@ -1750,6 +1766,26 @@ msgstr "" msgid ":class:`telebot.types.ForumTopicCreated`" msgstr "" +#: of telebot.types.ForumTopicEdited:1 +msgid "This object represents a service message about an edited forum topic." +msgstr "" + +#: of telebot.types.ForumTopicEdited:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopicedited" +msgstr "" + +#: of telebot.types.ForumTopicEdited:5 +msgid "Optional, Name of the topic(if updated)" +msgstr "" + +#: of telebot.types.ForumTopicEdited:8 +msgid "" +"Optional. New identifier of the custom emoji shown as the topic icon, if " +"it was edited; an empty string if the icon was removed" +msgstr "" + #: of telebot.types.ForumTopicReopened:1 msgid "" "This object represents a service message about a forum topic reopened in " @@ -1840,6 +1876,30 @@ msgstr "" msgid ":class:`telebot.types.GameHighScore`" msgstr "" +#: of telebot.types.GeneralForumTopicHidden:1 +msgid "" +"This object represents a service message about General forum topic hidden" +" in the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.GeneralForumTopicHidden:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#generalforumtopichidden" +msgstr "" + +#: of telebot.types.GeneralForumTopicUnhidden:1 +msgid "" +"This object represents a service message about General forum topic " +"unhidden in the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.GeneralForumTopicUnhidden:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#generalforumtopicunhidden" +msgstr "" + #: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 #: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 msgid "" @@ -2163,7 +2223,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultAudio:5 -#: telebot.types.InlineQueryResultCachedAudio:5 telebot.types.InputMediaAudio:5 +#: telebot.types.InlineQueryResultCachedAudio:5 msgid "Type of the result, must be audio" msgstr "" @@ -2202,7 +2262,7 @@ msgstr "" #: of telebot.types.InlineQueryResultAudio:20 #: telebot.types.InlineQueryResultCachedAudio:17 -#: telebot.types.InputMediaAudio:23 +#: telebot.types.InputMediaAudio:20 msgid "" "Optional. Mode for parsing entities in the audio caption. See formatting " "options for more details." @@ -2221,9 +2281,9 @@ msgstr "" #: telebot.types.InlineQueryResultMpeg4Gif:39 #: telebot.types.InlineQueryResultPhoto:36 #: telebot.types.InlineQueryResultVideo:30 -#: telebot.types.InlineQueryResultVoice:24 telebot.types.InputMediaAnimation:27 -#: telebot.types.InputMediaAudio:27 telebot.types.InputMediaDocument:27 -#: telebot.types.InputMediaPhoto:20 telebot.types.InputMediaVideo:27 +#: telebot.types.InlineQueryResultVoice:24 telebot.types.InputMediaAnimation:24 +#: telebot.types.InputMediaAudio:24 telebot.types.InputMediaDocument:24 +#: telebot.types.InputMediaPhoto:17 telebot.types.InputMediaVideo:24 msgid "" "Optional. List of special entities that appear in the caption, which can " "be specified instead of parse_mode" @@ -2396,7 +2456,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultCachedDocument:5 -#: telebot.types.InlineQueryResultDocument:5 telebot.types.InputMediaDocument:5 +#: telebot.types.InlineQueryResultDocument:5 msgid "Type of the result, must be document" msgstr "" @@ -2413,7 +2473,7 @@ msgstr "" #: of telebot.types.InlineQueryResultCachedDocument:20 #: telebot.types.InlineQueryResultDocument:14 -#: telebot.types.InputMediaDocument:20 +#: telebot.types.InputMediaDocument:17 msgid "" "Optional. Caption of the document to be sent, 0-1024 characters after " "entities parsing" @@ -2421,7 +2481,7 @@ msgstr "" #: of telebot.types.InlineQueryResultCachedDocument:23 #: telebot.types.InlineQueryResultDocument:17 -#: telebot.types.InputMediaDocument:23 +#: telebot.types.InputMediaDocument:20 msgid "" "Optional. Mode for parsing entities in the document caption. See " "formatting options for more details." @@ -2548,7 +2608,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultCachedPhoto:5 -#: telebot.types.InlineQueryResultPhoto:5 telebot.types.InputMediaPhoto:5 +#: telebot.types.InlineQueryResultPhoto:5 msgid "Type of the result, must be photo" msgstr "" @@ -2557,14 +2617,14 @@ msgid "A valid file identifier of the photo" msgstr "" #: of telebot.types.InlineQueryResultCachedPhoto:20 -#: telebot.types.InlineQueryResultPhoto:29 telebot.types.InputMediaPhoto:13 +#: telebot.types.InlineQueryResultPhoto:29 telebot.types.InputMediaPhoto:10 msgid "" "Optional. Caption of the photo to be sent, 0-1024 characters after " "entities parsing" msgstr "" #: of telebot.types.InlineQueryResultCachedPhoto:23 -#: telebot.types.InlineQueryResultPhoto:32 telebot.types.InputMediaPhoto:16 +#: telebot.types.InlineQueryResultPhoto:32 telebot.types.InputMediaPhoto:13 msgid "" "Optional. Mode for parsing entities in the photo caption. See formatting " "options for more details." @@ -2624,7 +2684,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultCachedVideo:5 -#: telebot.types.InlineQueryResultVideo:5 telebot.types.InputMediaVideo:5 +#: telebot.types.InlineQueryResultVideo:5 msgid "Type of the result, must be video" msgstr "" @@ -2633,14 +2693,14 @@ msgid "A valid file identifier for the video file" msgstr "" #: of telebot.types.InlineQueryResultCachedVideo:20 -#: telebot.types.InlineQueryResultVideo:23 telebot.types.InputMediaVideo:20 +#: telebot.types.InlineQueryResultVideo:23 telebot.types.InputMediaVideo:17 msgid "" "Optional. Caption of the video to be sent, 0-1024 characters after " "entities parsing" msgstr "" #: of telebot.types.InlineQueryResultCachedVideo:23 -#: telebot.types.InlineQueryResultVideo:26 telebot.types.InputMediaVideo:23 +#: telebot.types.InlineQueryResultVideo:26 telebot.types.InputMediaVideo:20 msgid "" "Optional. Mode for parsing entities in the video caption. See formatting " "options for more details." @@ -2916,17 +2976,17 @@ msgid "A valid URL for the MPEG4 file. File size must not exceed 1MB" msgstr "" #: of telebot.types.InlineQueryResultMpeg4Gif:14 -#: telebot.types.InlineQueryResultVideo:34 telebot.types.InputMediaVideo:31 +#: telebot.types.InlineQueryResultVideo:34 telebot.types.InputMediaVideo:28 msgid "Optional. Video width" msgstr "" #: of telebot.types.InlineQueryResultMpeg4Gif:17 -#: telebot.types.InlineQueryResultVideo:37 telebot.types.InputMediaVideo:34 +#: telebot.types.InlineQueryResultVideo:37 telebot.types.InputMediaVideo:31 msgid "Optional. Video height" msgstr "" #: of telebot.types.InlineQueryResultMpeg4Gif:20 -#: telebot.types.InlineQueryResultVideo:40 telebot.types.InputMediaVideo:37 +#: telebot.types.InlineQueryResultVideo:40 telebot.types.InputMediaVideo:34 msgid "Optional. Video duration in seconds" msgstr "" @@ -3357,13 +3417,9 @@ msgid "" "https://core.telegram.org/bots/api#inputmediaanimation" msgstr "" -#: of telebot.types.InputMediaAnimation:5 -msgid "Type of the result, must be animation" -msgstr "" - -#: of telebot.types.InputMediaAnimation:8 telebot.types.InputMediaAudio:8 -#: telebot.types.InputMediaDocument:8 telebot.types.InputMediaPhoto:8 -#: telebot.types.InputMediaVideo:8 +#: of telebot.types.InputMediaAnimation:5 telebot.types.InputMediaAudio:5 +#: telebot.types.InputMediaDocument:5 telebot.types.InputMediaPhoto:5 +#: telebot.types.InputMediaVideo:5 msgid "" "File to send. Pass a file_id to send a file that exists on the Telegram " "servers (recommended), pass an HTTP URL for Telegram to get a file from " @@ -3372,8 +3428,8 @@ msgid "" " on Sending Files »" msgstr "" -#: of telebot.types.InputMediaAnimation:13 telebot.types.InputMediaAudio:13 -#: telebot.types.InputMediaDocument:13 telebot.types.InputMediaVideo:13 +#: of telebot.types.InputMediaAnimation:10 telebot.types.InputMediaAudio:10 +#: telebot.types.InputMediaDocument:10 telebot.types.InputMediaVideo:10 msgid "" "Optional. Thumbnail of the file sent; can be ignored if thumbnail " "generation for the file is supported server-side. The thumbnail should be" @@ -3385,30 +3441,34 @@ msgid "" ". More information on Sending Files »" msgstr "" -#: of telebot.types.InputMediaAnimation:20 +#: of telebot.types.InputMediaAnimation:17 msgid "" "Optional. Caption of the animation to be sent, 0-1024 characters after " "entities parsing" msgstr "" -#: of telebot.types.InputMediaAnimation:23 +#: of telebot.types.InputMediaAnimation:20 msgid "" "Optional. Mode for parsing entities in the animation caption. See " "formatting options for more details." msgstr "" -#: of telebot.types.InputMediaAnimation:31 +#: of telebot.types.InputMediaAnimation:28 msgid "Optional. Animation width" msgstr "" -#: of telebot.types.InputMediaAnimation:34 +#: of telebot.types.InputMediaAnimation:31 msgid "Optional. Animation height" msgstr "" -#: of telebot.types.InputMediaAnimation:37 +#: of telebot.types.InputMediaAnimation:34 msgid "Optional. Animation duration in seconds" msgstr "" +#: of telebot.types.InputMediaAnimation:37 +msgid "Optional. True, if the uploaded animation is a spoiler" +msgstr "" + #: of telebot.types.InputMediaAnimation:41 msgid ":class:`telebot.types.InputMediaAnimation`" msgstr "" @@ -3421,25 +3481,25 @@ msgstr "" msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio" msgstr "" -#: of telebot.types.InputMediaAudio:20 +#: of telebot.types.InputMediaAudio:17 msgid "" "Optional. Caption of the audio to be sent, 0-1024 characters after " "entities parsing" msgstr "" -#: of telebot.types.InputMediaAudio:31 +#: of telebot.types.InputMediaAudio:28 msgid "Optional. Duration of the audio in seconds" msgstr "" -#: of telebot.types.InputMediaAudio:34 +#: of telebot.types.InputMediaAudio:31 msgid "Optional. Performer of the audio" msgstr "" -#: of telebot.types.InputMediaAudio:37 +#: of telebot.types.InputMediaAudio:34 msgid "Optional. Title of the audio" msgstr "" -#: of telebot.types.InputMediaAudio:41 +#: of telebot.types.InputMediaAudio:38 msgid ":class:`telebot.types.InputMediaAudio`" msgstr "" @@ -3453,14 +3513,14 @@ msgid "" "https://core.telegram.org/bots/api#inputmediadocument" msgstr "" -#: of telebot.types.InputMediaDocument:31 +#: of telebot.types.InputMediaDocument:28 msgid "" "Optional. Disables automatic server-side content type detection for files" " uploaded using multipart/form-data. Always True, if the document is sent" " as part of an album." msgstr "" -#: of telebot.types.InputMediaDocument:36 +#: of telebot.types.InputMediaDocument:33 msgid ":class:`telebot.types.InputMediaDocument`" msgstr "" @@ -3472,6 +3532,10 @@ msgstr "" msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto" msgstr "" +#: of telebot.types.InputMediaPhoto:21 +msgid "Optional. True, if the uploaded photo is a spoiler" +msgstr "" + #: of telebot.types.InputMediaPhoto:25 msgid ":class:`telebot.types.InputMediaPhoto`" msgstr "" @@ -3484,10 +3548,14 @@ msgstr "" msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo" msgstr "" -#: of telebot.types.InputMediaVideo:40 +#: of telebot.types.InputMediaVideo:37 msgid "Optional. Pass True, if the uploaded video is suitable for streaming" msgstr "" +#: of telebot.types.InputMediaVideo:40 +msgid "Optional. True, if the uploaded video is a spoiler" +msgstr "" + #: of telebot.types.InputMediaVideo:44 msgid ":class:`telebot.types.InputMediaVideo`" msgstr "" @@ -4118,63 +4186,67 @@ msgid "" msgstr "" #: of telebot.types.Message:115 -msgid "Optional. Message is a shared contact, information about the contact" +msgid "Optional. True, if the message media is covered by a spoiler animation" msgstr "" #: of telebot.types.Message:118 -msgid "Optional. Message is a dice with random value" +msgid "Optional. Message is a shared contact, information about the contact" msgstr "" #: of telebot.types.Message:121 +msgid "Optional. Message is a dice with random value" +msgstr "" + +#: of telebot.types.Message:124 msgid "" "Optional. Message is a game, information about the game. More about games" " »" msgstr "" -#: of telebot.types.Message:124 +#: of telebot.types.Message:127 msgid "Optional. Message is a native poll, information about the poll" msgstr "" -#: of telebot.types.Message:127 +#: of telebot.types.Message:130 msgid "" "Optional. Message is a venue, information about the venue. For backward " "compatibility, when this field is set, the location field will also be " "set" msgstr "" -#: of telebot.types.Message:131 +#: of telebot.types.Message:134 msgid "Optional. Message is a shared location, information about the location" msgstr "" -#: of telebot.types.Message:134 +#: of telebot.types.Message:137 msgid "" "Optional. New members that were added to the group or supergroup and " "information about them (the bot itself may be one of these members)" msgstr "" -#: of telebot.types.Message:138 +#: of telebot.types.Message:141 msgid "" "Optional. A member was removed from the group, information about them " "(this member may be the bot itself)" msgstr "" -#: of telebot.types.Message:142 +#: of telebot.types.Message:145 msgid "Optional. A chat title was changed to this value" msgstr "" -#: of telebot.types.Message:145 +#: of telebot.types.Message:148 msgid "Optional. A chat photo was change to this value" msgstr "" -#: of telebot.types.Message:148 +#: of telebot.types.Message:151 msgid "Optional. Service message: the chat photo was deleted" msgstr "" -#: of telebot.types.Message:151 +#: of telebot.types.Message:154 msgid "Optional. Service message: the group has been created" msgstr "" -#: of telebot.types.Message:154 +#: of telebot.types.Message:157 msgid "" "Optional. Service message: the supergroup has been created. This field " "can't be received in a message coming through updates, because bot can't " @@ -4183,7 +4255,7 @@ msgid "" " created supergroup." msgstr "" -#: of telebot.types.Message:159 +#: of telebot.types.Message:162 msgid "" "Optional. Service message: the channel has been created. This field can't" " be received in a message coming through updates, because bot can't be a " @@ -4191,11 +4263,11 @@ msgid "" "reply_to_message if someone replies to a very first message in a channel." msgstr "" -#: of telebot.types.Message:164 +#: of telebot.types.Message:167 msgid "Optional. Service message: auto-delete timer settings changed in the chat" msgstr "" -#: of telebot.types.Message:168 +#: of telebot.types.Message:171 msgid "" "Optional. The group has been migrated to a supergroup with the specified " "identifier. This number may have more than 32 significant bits and some " @@ -4204,7 +4276,7 @@ msgid "" " double-precision float type are safe for storing this identifier." msgstr "" -#: of telebot.types.Message:174 +#: of telebot.types.Message:177 msgid "" "Optional. The supergroup has been migrated from a group with the " "specified identifier. This number may have more than 32 significant bits " @@ -4214,80 +4286,98 @@ msgid "" "identifier." msgstr "" -#: of telebot.types.Message:180 +#: of telebot.types.Message:183 msgid "" "Optional. Specified message was pinned. Note that the Message object in " "this field will not contain further reply_to_message fields even if it is" " itself a reply." msgstr "" -#: of telebot.types.Message:184 +#: of telebot.types.Message:187 msgid "" "Optional. Message is an invoice for a payment, information about the " "invoice. More about payments »" msgstr "" -#: of telebot.types.Message:187 +#: of telebot.types.Message:190 msgid "" "Optional. Message is a service message about a successful payment, " "information about the payment. More about payments »" msgstr "" -#: of telebot.types.Message:191 +#: of telebot.types.Message:194 msgid "" "Optional. The domain name of the website on which the user has logged in." " More about Telegram Login »" msgstr "" -#: of telebot.types.Message:195 +#: of telebot.types.Message:198 +msgid "" +"Optional. Service message: the user allowed the bot added to the " +"attachment menu to write messages" +msgstr "" + +#: of telebot.types.Message:202 msgid "Optional. Telegram Passport data" msgstr "" -#: of telebot.types.Message:198 +#: of telebot.types.Message:205 msgid "" "Optional. Service message. A user in the chat triggered another user's " "proximity alert while sharing Live Location." msgstr "" -#: of telebot.types.Message:202 +#: of telebot.types.Message:209 msgid "Optional. Service message: forum topic created" msgstr "" -#: of telebot.types.Message:205 +#: of telebot.types.Message:212 +msgid "Optional. Service message: forum topic edited" +msgstr "" + +#: of telebot.types.Message:215 msgid "Optional. Service message: forum topic closed" msgstr "" -#: of telebot.types.Message:208 +#: of telebot.types.Message:218 msgid "Optional. Service message: forum topic reopened" msgstr "" -#: of telebot.types.Message:211 +#: of telebot.types.Message:221 +msgid "Optional. Service message: the 'General' forum topic hidden" +msgstr "" + +#: of telebot.types.Message:224 +msgid "Optional. Service message: the 'General' forum topic unhidden" +msgstr "" + +#: of telebot.types.Message:227 msgid "Optional. Service message: video chat scheduled" msgstr "" -#: of telebot.types.Message:214 +#: of telebot.types.Message:230 msgid "Optional. Service message: video chat started" msgstr "" -#: of telebot.types.Message:217 +#: of telebot.types.Message:233 msgid "Optional. Service message: video chat ended" msgstr "" -#: of telebot.types.Message:220 +#: of telebot.types.Message:236 msgid "Optional. Service message: new participants invited to a video chat" msgstr "" -#: of telebot.types.Message:223 +#: of telebot.types.Message:239 msgid "Optional. Service message: data sent by a Web App" msgstr "" -#: of telebot.types.Message:226 +#: of telebot.types.Message:242 msgid "" "Optional. Inline keyboard attached to the message. login_url buttons are " "represented as ordinary url buttons." msgstr "" -#: of telebot.types.Message:231 +#: of telebot.types.Message:247 msgid ":class:`telebot.types.Message`" msgstr "" @@ -4701,7 +4791,33 @@ msgid "" "the keyboard." msgstr "" -#: of telebot.types.ReplyKeyboardMarkup:43 +#: of telebot.types.ReplyKeyboardMarkup:42 +msgid "" +"Optional. Use this parameter if you want to show the keyboard to specific" +" users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message. Example: A user " +"requests to change the bot's language, bot replies to the request with a " +"keyboard to select the new language. Other users in the group don't see " +"the keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:42 +msgid "" +"Optional. Use this parameter if you want to show the keyboard to specific" +" users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:46 +msgid "" +"Example: A user requests to change the bot's language, bot replies to the" +" request with a keyboard to select the new language. Other users in the " +"group don't see the keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:50 #: telebot.types.ReplyKeyboardMarkup.add:14 #: telebot.types.ReplyKeyboardMarkup.row:9 msgid ":class:`telebot.types.ReplyKeyboardMarkup`" @@ -5505,3 +5621,18 @@ msgstr "" msgid ":class:`telebot.types.WebhookInfo`" msgstr "" +#: of telebot.types.WriteAccessAllowed:1 +msgid "" +"This object represents a service message about a user allowed to post " +"messages in the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.WriteAccessAllowed:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#writeaccessallowed" +msgstr "" + +#~ msgid "Type of the result, must be animation" +#~ msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index ad3ae7a72..daac63eb9 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-01-02 19:24+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,19 +18,19 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../async_version/index.rst:3 +#: ../../source/async_version/index.rst:3 msgid "AsyncTeleBot" msgstr "" -#: ../../async_version/index.rst:6 +#: ../../source/async_version/index.rst:6 msgid "Asynchronous pyTelegramBotAPI" msgstr "Асинхронный pyTelegramBotAPI" -#: ../../async_version/index.rst:6 +#: ../../source/async_version/index.rst:6 msgid "ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation" msgstr "ptba, pytba, pyTelegramBotAPI, asynctelebot, документация" -#: ../../async_version/index.rst:12 +#: ../../source/async_version/index.rst:12 msgid "AsyncTeleBot methods" msgstr "Методы класса AsyncTeleBot" @@ -93,6 +93,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.chat_member_handler #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.close_forum_topic +#: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic @@ -110,6 +111,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic +#: telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.edit_message_caption #: telebot.async_telebot.AsyncTeleBot.edit_message_live_location #: telebot.async_telebot.AsyncTeleBot.edit_message_media @@ -136,6 +138,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.get_updates #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info +#: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.infinity_polling #: telebot.async_telebot.AsyncTeleBot.inline_handler #: telebot.async_telebot.AsyncTeleBot.leave_chat @@ -163,6 +166,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic +#: telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.reply_to #: telebot.async_telebot.AsyncTeleBot.reset_data #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member @@ -208,6 +212,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.stop_poll #: telebot.async_telebot.AsyncTeleBot.unban_chat_member #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat +#: telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message @@ -217,7 +222,8 @@ msgid "Parameters" msgstr "" #: of telebot.async_telebot.AsyncTeleBot:24 -msgid "Token of a bot, should be obtained from @BotFather" +#, fuzzy +msgid "Token of a bot, obtained from @BotFather" msgstr "Токен бота, нужно получить от @BotFather" #: of telebot.async_telebot.AsyncTeleBot:27 @@ -477,9 +483,10 @@ msgid "" "Use this method to add a new sticker to a set created by the bot. It's " "required to pass `png_sticker` or `tgs_sticker`. Returns True on success." msgstr "" -"Используйте этот метод, чтобы добавить новый стикер в стикерпак, созданный ботом. Необходимо " -"передать либо `png_sticker`, либо `tgs_sticker`, либо `webm_sticker`. " -"Возвращает True в случае успешного добавления." +"Используйте этот метод, чтобы добавить новый стикер в стикерпак, " +"созданный ботом. Необходимо передать либо `png_sticker`, либо " +"`tgs_sticker`, либо `webm_sticker`. Возвращает True в случае успешного " +"добавления." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" @@ -511,30 +518,33 @@ msgid "" " to get a file from the Internet, or upload a new one using multipart" "/form-data." msgstr "" -"Изображение стикера в формате PNG, весом не более 512 килобайт, " -"размеры не должны превышать 512px, либо ширина, либо высота должны быть " -"ровно 512px. Передайте file_id в формате str, чтобы отправить уже загруженный " -"на сервера Telegram файл, передайте HTTP URL в формате str, чтобы Telegram " -"скачал файл из интернета, или загрузите новый файл с помощью multipart/form-data." - +"Изображение стикера в формате PNG, весом не более 512 килобайт, размеры " +"не должны превышать 512px, либо ширина, либо высота должны быть ровно " +"512px. Передайте file_id в формате str, чтобы отправить уже загруженный " +"на сервера Telegram файл, передайте HTTP URL в формате str, чтобы " +"Telegram скачал файл из интернета, или загрузите новый файл с помощью " +"multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:26 msgid "TGS animation with the sticker, uploaded using multipart/form-data." -msgstr "Анимированный стикер в формате TGS, загруженный с помощью multipart/form-data." +msgstr "" +"Анимированный стикер в формате TGS, загруженный с помощью multipart/form-" +"data." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:24 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 msgid "WebM animation with the sticker, uploaded using multipart/form-data." -msgstr "Анимированный стикер в формате WebM, загруженный с помощью multipart/form-data." +msgstr "" +"Анимированный стикер в формате WebM, загруженный с помощью multipart" +"/form-data." #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:27 #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:40 msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" -msgstr "" -"Позиция для размещения маски на лицах в формате JSON" +msgstr "Позиция для размещения маски на лицах в формате JSON" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:30 #: telebot.async_telebot.AsyncTeleBot.answer_callback_query:22 @@ -545,7 +555,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:43 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:13 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:6 -#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:19 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:22 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:13 #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 #: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:15 @@ -670,14 +680,14 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_my_commands:17 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:7 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:13 -#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:20 +#: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:23 #: telebot.async_telebot.AsyncTeleBot.leave_chat:8 #: telebot.async_telebot.AsyncTeleBot.log_out:11 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:48 -#: telebot.async_telebot.AsyncTeleBot.send_chat_action:23 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:26 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:18 #: telebot.async_telebot.AsyncTeleBot.set_chat_description:14 #: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:15 @@ -702,8 +712,9 @@ msgid "" "keyboards. The answer will be displayed to the user as a notification at " "the top of the chat screen or as an alert." msgstr "" -"Используйте этот метод для отправки ответов на callback запросы, отправленные с помощью inline " -"кнопок. Ответ будет показан пользователю как уведомление поверх чата или pop-up предупреждение." +"Используйте этот метод для отправки ответов на callback запросы, " +"отправленные с помощью inline кнопок. Ответ будет показан пользователю " +"как уведомление поверх чата или pop-up предупреждение." #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:4 msgid "" @@ -725,14 +736,16 @@ msgid "" "Text of the notification. If not specified, nothing will be shown to the " "user, 0-200 characters" msgstr "" -"Текст уведомления. если не задан, то уведомление не будет показано, 0-200 символов" +"Текст уведомления. если не задан, то уведомление не будет показано, 0-200" +" символов" #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:12 msgid "" "If True, an alert will be shown by the client instead of a notification " "at the top of the chat screen. Defaults to false." msgstr "" -"Если True, вместо уведомления поверх чата будет показано pop-up предупреждение, по умолчанию False." +"Если True, вместо уведомления поверх чата будет показано pop-up " +"предупреждение, по умолчанию False." #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:15 msgid "" @@ -741,10 +754,10 @@ msgid "" "your game - note that this will only work if the query comes from a " "callback_game button." msgstr "" -"URL, который будет открыт пользовательским клиентом. Если вы создали игру " -"и приняли условия через @BotFather, задайте URL, открывающий вашу игру " -"- учитывайте, что это сработает только если запрос был отправлен с помощью " -"callback_game кнопки." +"URL, который будет открыт пользовательским клиентом. Если вы создали игру" +" и приняли условия через @BotFather, задайте URL, открывающий вашу игру -" +" учитывайте, что это сработает только если запрос был отправлен с помощью" +" callback_game кнопки." #: of telebot.async_telebot.AsyncTeleBot.answer_callback_query:19 msgid "" @@ -752,16 +765,18 @@ msgid "" "query may be cached client-side. Telegram apps will support caching " "starting in version 3.14. Defaults to 0." msgstr "" -"Максимальная длительность хранения ответа на callback запрос пользовательским клиентом в секундах. " -"Приложения Telegram поддерживают хранение ответов начиная с версии 3.14, по умолчанию 0." +"Максимальная длительность хранения ответа на callback запрос " +"пользовательским клиентом в секундах. Приложения Telegram поддерживают " +"хранение ответов начиная с версии 3.14, по умолчанию 0." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:1 msgid "" "Use this method to send answers to an inline query. On success, True is " "returned. No more than 50 results per query are allowed." msgstr "" -"Используйте этот метод для отправки ответов на inline запрос. В случае успеха возвращается True. " -"Разрешено отправить не более 50 результатов на один запрос." +"Используйте этот метод для отправки ответов на inline запрос. В случае " +"успеха возвращается True. Разрешено отправить не более 50 результатов на " +"один запрос." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:4 msgid "" @@ -784,23 +799,24 @@ msgid "" "The maximum amount of time in seconds that the result of the inline query" " may be cached on the server." msgstr "" -"Максимальная длительность хранения результатов inline запроса на сервере в секундах." +"Максимальная длительность хранения результатов inline запроса на сервере " +"в секундах." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:16 msgid "" "Pass True, if results may be cached on the server side only for the user " "that sent the query." msgstr "" -"Передайте True, если результаты должны быть сохранены на сервере только для пользователя, " -"отправившего запрос." +"Передайте True, если результаты должны быть сохранены на сервере только " +"для пользователя, отправившего запрос." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:20 msgid "" "Pass the offset that a client should send in the next query with the same" " text to receive more results." msgstr "" -"Передайте смещение, которое клиент должен отправить в следующем запросе с таким же " -"текстом, чтобы получить новые результаты." +"Передайте смещение, которое клиент должен отправить в следующем запросе с" +" таким же текстом, чтобы получить новые результаты." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:24 msgid "" @@ -817,22 +833,25 @@ msgid "" "capabilities." msgstr "" "Параметр для команды /start, отправляемой боту, когда пользователь " -"нажимает кнопку переключения. 1-64 символа, разрешены только A-Z, a-z, 0-9, _ и -. " -"Пример: Inline бот, который отправляет видео с YouTube может попросить " -"пользователя подключить бота к его YouTube аккаунту, чтобы поиск соответствовал " -"предпочтениям пользователя. Чтобы это сделать, бот отправляет пользователю кнопку " -"'Подключить YouTube аккаунт' над результатами, или даже до их показа. Пользователь нажимает " -"на кнопку, автоматически переходит в приватный чат с ботом и в это время " -"передаёт стартовый параметр, по которому бот возвращает ссылку для авторизации (OAuth). " -"Как только авторизация пройдена, бот может предложить switch_inline кнопку, чтобы пользователь мог " -"легко вернуться в чат, где он хотел использовать возможности inline бота." +"нажимает кнопку переключения. 1-64 символа, разрешены только A-Z, a-z, " +"0-9, _ и -. Пример: Inline бот, который отправляет видео с YouTube может " +"попросить пользователя подключить бота к его YouTube аккаунту, чтобы " +"поиск соответствовал предпочтениям пользователя. Чтобы это сделать, бот " +"отправляет пользователю кнопку 'Подключить YouTube аккаунт' над " +"результатами, или даже до их показа. Пользователь нажимает на кнопку, " +"автоматически переходит в приватный чат с ботом и в это время передаёт " +"стартовый параметр, по которому бот возвращает ссылку для авторизации " +"(OAuth). Как только авторизация пройдена, бот может предложить " +"switch_inline кнопку, чтобы пользователь мог легко вернуться в чат, где " +"он хотел использовать возможности inline бота." #: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:32 msgid "" "Parameter for the start message sent to the bot when user presses the " "switch button" msgstr "" -"Параметр для передачи боту вместе с сообщением /start, отправленному при нажатии кнопки переключения" +"Параметр для передачи боту вместе с сообщением /start, отправленному при " +"нажатии кнопки переключения" #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:1 msgid "" @@ -842,15 +861,17 @@ msgid "" "queries. On success, True is returned." msgstr "" "Как только пользователь подтвердил детали оплаты и доставки, Bot API " -"отправляет финальное подтверждение в виде апдейта с полем pre_checkout_query. " -"Используйте этот метод для ответа на такие pre-checkout запросы. В случае успеха возвращается True." +"отправляет финальное подтверждение в виде апдейта с полем " +"pre_checkout_query. Используйте этот метод для ответа на такие pre-" +"checkout запросы. В случае успеха возвращается True." #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:6 msgid "" "The Bot API must receive an answer within 10 seconds after the pre-" "checkout query was sent." msgstr "" -"Bot API должно получить ответ в течение 10 секунд после отправки pre-checkout запроса." +"Bot API должно получить ответ в течение 10 секунд после отправки pre-" +"checkout запроса." #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:8 msgid "" @@ -878,11 +899,12 @@ msgid "" "different color or garment!\"). Telegram will display this message to the" " user." msgstr "" -"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать человек, объясняющее " -"причину, по которой бот не может обработать заказ (например " -"\"Извините, кто-то только что купил последнюю из наших прекрасных черных футболок с коротким рукавом " -"пока вы заполняли детали оплаты. Пожалуйста выберите другой цвет или фасон!\"). " -"Telegram покажет это сообщение пользователю." +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое " +"может прочитать человек, объясняющее причину, по которой бот не может " +"обработать заказ (например \"Извините, кто-то только что купил последнюю " +"из наших прекрасных черных футболок с коротким рукавом пока вы заполняли " +"детали оплаты. Пожалуйста выберите другой цвет или фасон!\"). Telegram " +"покажет это сообщение пользователю." #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:1 msgid "Asks for an answer to a shipping question." @@ -902,15 +924,17 @@ msgid "" "if there are any problems (for example, if delivery to the specified " "address is not possible)" msgstr "" -"Задайте True если доставка по выбранному адресу возможна и False, если есть " -"какие-то проблемы (например, доставка по выбранному адресу не осуществляется)" +"Задайте True если доставка по выбранному адресу возможна и False, если " +"есть какие-то проблемы (например, доставка по выбранному адресу не " +"осуществляется)" #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:11 msgid "" "Required if ok is True. A JSON-serialized array of available shipping " "options." msgstr "" -"Обязательный в случае, когда ok - True. Массив вариантов доставки в формате JSON." +"Обязательный в случае, когда ok - True. Массив вариантов доставки в " +"формате JSON." #: of telebot.async_telebot.AsyncTeleBot.answer_shipping_query:14 msgid "" @@ -919,10 +943,10 @@ msgid "" "delivery to your desired address is unavailable'). Telegram will display " "this message to the user." msgstr "" -"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать " -"человек, объясняющее причину, по которой невозможно завершить заказ (например " -"\"Извините, доставка по запрошенному адресу недоступна\"). " -"Telegram покажет это сообщение пользователю." +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое " +"может прочитать человек, объясняющее причину, по которой невозможно " +"завершить заказ (например \"Извините, доставка по запрошенному адресу " +"недоступна\"). Telegram покажет это сообщение пользователю." #: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:1 msgid "" @@ -931,9 +955,10 @@ msgid "" " the query originated. On success, a SentWebAppMessage object is " "returned." msgstr "" -"Используйте этот метод, чтобы задать результат взаимодействия с Web App и " -"отправить соответствующее сообщение от лица пользователя в чат, из которого пришел запрос. " -"В случае успеха возвращается объект SentWebAppMessage." +"Используйте этот метод, чтобы задать результат взаимодействия с Web App и" +" отправить соответствующее сообщение от лица пользователя в чат, из " +"которого пришел запрос. В случае успеха возвращается объект " +"SentWebAppMessage." #: of telebot.async_telebot.AsyncTeleBot.answer_web_app_query:6 msgid "" @@ -961,8 +986,8 @@ msgid "" "administrator in the chat for this to work and must have the " "can_invite_users administrator right. Returns True on success." msgstr "" -"Используйте этот метод, чтобы одобрить запрос на вступление в чат. " -"Бот должен быть администратором чата и иметь права администратора " +"Используйте этот метод, чтобы одобрить запрос на вступление в чат. Бот " +"должен быть администратором чата и иметь права администратора " "can_invite_users. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:5 @@ -984,7 +1009,8 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup (in the format @supergroupusername)" msgstr "" -"Уникальный id чата или username супергруппы (в формате @supergroupusername)" +"Уникальный id чата или username супергруппы (в формате " +"@supergroupusername)" #: of telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:11 #: telebot.async_telebot.AsyncTeleBot.ban_chat_member:12 @@ -1027,9 +1053,10 @@ msgid "" " to the chat on their own using invite links, etc., unless unbanned " "first. Returns True on success." msgstr "" -"Используйте этот метод, чтобы заблокировать пользователя в группе, супергруппе или канале. В " -"случае супергрупп и каналов, пользователь не сможет вернуться в чат самостоятельно, используя " -"ссылки с приглашением и т.д., пока не будет разблокирован. Возвращает True в случае успеха." +"Используйте этот метод, чтобы заблокировать пользователя в группе, " +"супергруппе или канале. В случае супергрупп и каналов, пользователь не " +"сможет вернуться в чат самостоятельно, используя ссылки с приглашением и " +"т.д., пока не будет разблокирован. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" @@ -1041,8 +1068,8 @@ msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" -"Уникальный id группы или username супергруппы или канала " -"(в формате @channelusername)" +"Уникальный id группы или username супергруппы или канала (в формате " +"@channelusername)" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:15 msgid "" @@ -1050,9 +1077,9 @@ msgid "" "more than 366 days or less than 30 seconds from the current time they are" " considered to be banned forever" msgstr "" -"Дата, когда пользователь будет разблокирован, в формате UNIX time. Если пользователь " -"заблокирован больше чем на 366 дней или меньше чем на 30 секунд, то он будет заблокирован " -"до ручной разблокировки" +"Дата, когда пользователь будет разблокирован, в формате UNIX time. Если " +"пользователь заблокирован больше чем на 366 дней или меньше чем на 30 " +"секунд, то он будет заблокирован до ручной разблокировки" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:19 msgid "" @@ -1062,14 +1089,14 @@ msgid "" "supergroups and channels." msgstr "" "Bool: Передайте True, чтобы удалить все сообщения пользователя из чата. " -"Если False, пользователю будут доступны все сообщения в группе, отправленные до его блокировки. " -"Всегда True для супергрупп и каналов." +"Если False, пользователю будут доступны все сообщения в группе, " +"отправленные до его блокировки. Всегда True для супергрупп и каналов." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 #: telebot.async_telebot.AsyncTeleBot.delete_message:22 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 -#: telebot.async_telebot.AsyncTeleBot.send_chat_action:22 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:25 msgid "Returns True on success." msgstr "Возвращает True в случае успеха." @@ -1081,11 +1108,11 @@ msgid "" "administrator in the supergroup or channel for this to work and must have" " the appropriate administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы заблокировать канал в супергруппе или канале. Владелец " -"канала не сможет отправлять сообщения и участвовать в прямых эфирах " -"от лица канала, пока канал не будет разблокирован. Бот должен быть " -"администратором супергруппы или канала и иметь соответствующие права администратора." -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы заблокировать канал в супергруппе или " +"канале. Владелец канала не сможет отправлять сообщения и участвовать в " +"прямых эфирах от лица канала, пока канал не будет разблокирован. Бот " +"должен быть администратором супергруппы или канала и иметь " +"соответствующие права администратора.Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:8 msgid "" @@ -1097,15 +1124,19 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:10 #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.copy_message:5 #: telebot.async_telebot.AsyncTeleBot.create_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_message:13 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:13 #: telebot.async_telebot.AsyncTeleBot.forward_message:8 +#: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:7 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:7 +#: telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.send_animation:6 #: telebot.async_telebot.AsyncTeleBot.send_audio:9 #: telebot.async_telebot.AsyncTeleBot.send_dice:5 @@ -1123,14 +1154,13 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_title:9 #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:6 #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:8 +#: telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:7 #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:7 msgid "" "Unique identifier for the target chat or username of the target channel " "(in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала " -"(в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:13 msgid "Unique identifier of the target sender chat" @@ -1141,8 +1171,8 @@ msgid "" "Handles new incoming callback query. As a parameter to the decorator " "function, it passes :class:`telebot.types.CallbackQuery` object." msgstr "" -"Обрабатывает новый callback запрос. В качестве параметра передаёт в декорируемую " -"функцию объект :class:`telebot.types.CallbackQuery`." +"Обрабатывает новый callback запрос. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:4 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:10 @@ -1211,8 +1241,8 @@ msgid "" "etc. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" -"Обрабатывает новый пост любого типа в канале - текст, фото, стикер и т.д. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает новый пост любого типа в канале - текст, фото, стикер и т.д." +" В качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.Message`." #: of telebot.async_telebot.AsyncTeleBot.channel_post_handler:4 @@ -1247,9 +1277,9 @@ msgid "" "updates. As a parameter to the decorator function, it passes " ":class:`telebot.types.ChatJoinRequest` object." msgstr "" -"Обрабатывает запрос на вступление в чат. Бот должен иметь права администратора" -"can_invite_users в чате, чтобы получать такие апдейты. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает запрос на вступление в чат. Бот должен иметь права " +"администратораcan_invite_users в чате, чтобы получать такие апдейты. В " +"качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.ChatJoinRequest`." #: of telebot.async_telebot.AsyncTeleBot.chat_member_handler:1 @@ -1260,23 +1290,25 @@ msgid "" "the decorator function, it passes " ":class:`telebot.types.ChatMemberUpdated` object." msgstr "" -"Обрабатывает изменение статуса пользователя в чате. Бот должен быть администратором " -"чата и явно указать “chat_member“ в allowed_updates, чтобы получать такие апдейты. " -"В качестве параметра передаёт в декорируемую функцию объект " -":class:`telebot.types.ChatMemberUpdated`." +"Обрабатывает изменение статуса пользователя в чате. Бот должен быть " +"администратором чата и явно указать “chat_member“ в allowed_updates, " +"чтобы получать такие апдейты. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.ChatMemberUpdated`." #: of telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:1 +#, fuzzy msgid "" -"Handles the result of an inline query that was chosen by a user and sent " -"to their chat partner. Please see our documentation on the feedback " -"collecting for details on how to enable these updates for your bot. As a " -"parameter to the decorator function, it passes " +"The result of an inline query that was chosen by a user and sent to their" +" chat partner. Please see our documentation on the feedback collecting " +"for details on how to enable these updates for your bot. As a parameter " +"to the decorator function, it passes " ":class:`telebot.types.ChosenInlineResult` object." msgstr "" -"Обрабатывает результат inline запроса, который был выбран пользователем и отправлен " -"собеседнику в чате. Пожалуйста ознакомьтесь с документацией по сбору фидбека " -"для получения таких апдейтов вашим ботом. В качестве параметра передаёт в " -"декорируемую функцию объект :class:`telebot.types.ChosenInlineResult`." +"Обрабатывает результат inline запроса, который был выбран пользователем и" +" отправлен собеседнику в чате. Пожалуйста ознакомьтесь с документацией по" +" сбору фидбека для получения таких апдейтов вашим ботом. В качестве " +"параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ChosenInlineResult`." #: of telebot.async_telebot.AsyncTeleBot.close:1 msgid "" @@ -1286,11 +1318,11 @@ msgid "" "The method will return error 429 in the first 10 minutes after the bot is" " launched. Returns True on success." msgstr "" -"Используйте этот метод чтобы закрыть инстанс бота прежде чем перемещать его с одного " -"локального сервера на другой. Вы должны удалить вебхук перед вызовом этого метода, " -"чтобы убедиться. что бот не будет запущен повторно после перезапуска сервера. " -"Метод будет возвращать ошибку 429 в течение 10 минут после запуска бота. " -"Возвращает True в случае успеха." +"Используйте этот метод чтобы закрыть инстанс бота прежде чем перемещать " +"его с одного локального сервера на другой. Вы должны удалить вебхук перед" +" вызовом этого метода, чтобы убедиться. что бот не будет запущен повторно" +" после перезапуска сервера. Метод будет возвращать ошибку 429 в течение " +"10 минут после запуска бота. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.close:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#close" @@ -1303,9 +1335,10 @@ msgid "" "the can_manage_topics administrator rights, unless it is the creator of " "the topic. Returns True on success." msgstr "" -"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics, за исключением " -"случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. " +"Бот должен быть администратором чата и иметь права администратора " +"can_manage_topics, за исключением случаев, когда бот является создателем " +"топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.close_forum_topic:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" @@ -1315,13 +1348,32 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#cl msgid "Identifier of the topic to close" msgstr "id топика для закрытия" +#: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to close the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. " +"Бот должен быть администратором чата и иметь права администратора " +"can_manage_topics, за исключением случаев, когда бот является создателем " +"топика. Возвращает True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#closegeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#closeforumtopic" + #: of telebot.async_telebot.AsyncTeleBot.close_session:1 msgid "" "Closes existing session of aiohttp. Use this function if you stop " "polling/webhooks." msgstr "" -"Закрывает текущую aiohttp сессию. Используйте эту функцию для завершения работы " -"поллинга или вебхука." +"Закрывает текущую aiohttp сессию. Используйте эту функцию для завершения " +"работы поллинга или вебхука." #: of telebot.async_telebot.AsyncTeleBot.copy_message:1 msgid "Use this method to copy messages of any kind." @@ -1337,8 +1389,8 @@ msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" msgstr "" -"Уникальный id чата, в который было отправлено исходное сообщение " -"(или username канала в формате @channelusername)" +"Уникальный id чата, в который было отправлено исходное сообщение (или " +"username канала в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.copy_message:10 #: telebot.async_telebot.AsyncTeleBot.forward_message:14 @@ -1350,8 +1402,8 @@ msgid "" "New caption for media, 0-1024 characters after entities parsing. If not " "specified, the original caption is kept" msgstr "" -"Новая подпись для медиа, 0-1024 символа после форматирования. Если не задано, " -"используется исходная подпись" +"Новая подпись для медиа, 0-1024 символа после форматирования. Если не " +"задано, используется исходная подпись" #: of telebot.async_telebot.AsyncTeleBot.copy_message:16 msgid "Mode for parsing entities in the new caption." @@ -1362,8 +1414,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the new " "caption, which can be specified instead of parse_mode" msgstr "" -"Список отформатированных частей новой подписи в формате JSON, " -"можно использовать вместо parse_mode" +"Список отформатированных частей новой подписи в формате JSON, можно " +"использовать вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.copy_message:22 #: telebot.async_telebot.AsyncTeleBot.send_animation:45 @@ -1385,7 +1437,8 @@ msgid "" "Sends the message silently. Users will receive a notification with no " "sound." msgstr "" -"Отправить сообщение, при получении которого пользователи получат уведомление без звука." +"Отправить сообщение, при получении которого пользователи получат " +"уведомление без звука." #: of telebot.async_telebot.AsyncTeleBot.copy_message:25 #: telebot.async_telebot.AsyncTeleBot.send_animation:34 @@ -1444,8 +1497,8 @@ msgid "" "Pass True, if the message should be sent even if the specified replied-to" " message is not found" msgstr "" -"Передайте True, если сообщение нужно отправить даже в случае отсутствия сообщения, на " -"которое дан ответ" +"Передайте True, если сообщение нужно отправить даже в случае отсутствия " +"сообщения, на которое дан ответ" #: of telebot.async_telebot.AsyncTeleBot.copy_message:34 #: telebot.async_telebot.AsyncTeleBot.send_animation:40 @@ -1467,8 +1520,9 @@ msgid "" "keyboard, custom reply keyboard, instructions to remove reply keyboard or" " to force a reply from the user." msgstr "" -"Дополнительные элементы интерфейса. Inline клавиатура, текстовая клавиатура, " -"запрос на удаление текстовой клавиатуры или запрос на ответ от пользователя." +"Дополнительные элементы интерфейса. Inline клавиатура, текстовая " +"клавиатура, запрос на удаление текстовой клавиатуры или запрос на ответ " +"от пользователя." #: of telebot.async_telebot.AsyncTeleBot.copy_message:39 #: telebot.async_telebot.AsyncTeleBot.delete_message:19 @@ -1506,7 +1560,7 @@ msgstr "id топика, в который нужно отправить соо #: of telebot.async_telebot.AsyncTeleBot.copy_message:45 #: telebot.async_telebot.AsyncTeleBot.forward_message:26 #: telebot.async_telebot.AsyncTeleBot.reply_to:11 -#: telebot.async_telebot.AsyncTeleBot.send_animation:60 +#: telebot.async_telebot.AsyncTeleBot.send_animation:63 #: telebot.async_telebot.AsyncTeleBot.send_audio:63 #: telebot.async_telebot.AsyncTeleBot.send_contact:44 #: telebot.async_telebot.AsyncTeleBot.send_dice:35 @@ -1515,11 +1569,11 @@ msgstr "id топика, в который нужно отправить соо #: telebot.async_telebot.AsyncTeleBot.send_invoice:101 #: telebot.async_telebot.AsyncTeleBot.send_location:49 #: telebot.async_telebot.AsyncTeleBot.send_message:46 -#: telebot.async_telebot.AsyncTeleBot.send_photo:45 +#: telebot.async_telebot.AsyncTeleBot.send_photo:48 #: telebot.async_telebot.AsyncTeleBot.send_poll:70 #: telebot.async_telebot.AsyncTeleBot.send_sticker:39 #: telebot.async_telebot.AsyncTeleBot.send_venue:57 -#: telebot.async_telebot.AsyncTeleBot.send_video:61 +#: telebot.async_telebot.AsyncTeleBot.send_video:64 #: telebot.async_telebot.AsyncTeleBot.send_video_note:48 #: telebot.async_telebot.AsyncTeleBot.send_voice:49 msgid "On success, the sent Message is returned." @@ -1528,17 +1582,17 @@ msgstr "В случае успеха возвращает отправленно #: of telebot.async_telebot.AsyncTeleBot.copy_message:46 #: telebot.async_telebot.AsyncTeleBot.forward_message:27 #: telebot.async_telebot.AsyncTeleBot.reply_to:12 -#: telebot.async_telebot.AsyncTeleBot.send_animation:61 +#: telebot.async_telebot.AsyncTeleBot.send_animation:64 #: telebot.async_telebot.AsyncTeleBot.send_audio:64 #: telebot.async_telebot.AsyncTeleBot.send_contact:45 #: telebot.async_telebot.AsyncTeleBot.send_dice:36 #: telebot.async_telebot.AsyncTeleBot.send_document:57 #: telebot.async_telebot.AsyncTeleBot.send_location:50 #: telebot.async_telebot.AsyncTeleBot.send_message:47 -#: telebot.async_telebot.AsyncTeleBot.send_photo:46 +#: telebot.async_telebot.AsyncTeleBot.send_photo:49 #: telebot.async_telebot.AsyncTeleBot.send_sticker:40 #: telebot.async_telebot.AsyncTeleBot.send_venue:58 -#: telebot.async_telebot.AsyncTeleBot.send_video:62 +#: telebot.async_telebot.AsyncTeleBot.send_video:65 #: telebot.async_telebot.AsyncTeleBot.send_video_note:49 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1551,10 +1605,11 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" -"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " -"должен быть администратором чата и иметь соответствующие права администратора. " -"Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " -"ссылку-приглашение (ChatInviteLink)." +"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот" +" должен быть администратором чата и иметь соответствующие права " +"администратора. Ссылка может быть аннулирована методом " +"revokeChatInviteLink. Возвращает новую ссылку-приглашение " +"(ChatInviteLink)." #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:6 msgid "" @@ -1571,8 +1626,7 @@ msgstr "" msgid "" "Id: Unique identifier for the target chat or username of the target " "channel (in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала (в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:12 #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:10 @@ -1617,17 +1671,15 @@ msgid "" "can_manage_topics administrator rights. Returns information about the " "created topic as a ForumTopic object." msgstr "" -"Используйте этот метод, чтобы создать топик в супергруппе. Бот должен быть " -"администратором чата и иметь права администратора can_manage_topics. " -"Возвращает информацию о созданном топике (ForumTopic)." +"Используйте этот метод, чтобы создать топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics." +" Возвращает информацию о созданном топике (ForumTopic)." #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createforumtopic" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#createforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#createforumtopic" #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:10 msgid "Name of the topic, 1-128 characters" @@ -1646,15 +1698,14 @@ msgid "" "Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " "be exactly 1 character long" msgstr "" -"Кастомный эмодзи для использования в качестве иконки топика. Должно быть “tgs” " -"эмодзи и быть ровно 1 символом" +"Кастомный эмодзи для использования в качестве иконки топика. Должно быть " +"“tgs” эмодзи и быть ровно 1 символом" #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:19 msgid "" "On success, information about the created topic is returned as a " "ForumTopic object." -msgstr "" -"В случае успеха возвращается информация о созданном топике (ForumTopic)." +msgstr "В случае успеха возвращается информация о созданном топике (ForumTopic)." #: of telebot.async_telebot.AsyncTeleBot.create_forum_topic:20 msgid ":class:`telebot.types.ForumTopic`" @@ -1665,8 +1716,8 @@ msgid "" "Use this method to create a link for an invoice. Returns the created " "invoice link as String on success." msgstr "" -"используйте этот метод, чтобы создать ссылку-инвойс. Возвращает созданную ссылку " -"в случае успеха (String)." +"используйте этот метод, чтобы создать ссылку-инвойс. Возвращает созданную" +" ссылку в случае успеха (String)." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:4 msgid "" @@ -1731,9 +1782,10 @@ msgid "" " The suggested tip amounts must be positive, passed in a strictly " "increased order and must not exceed max_tip_amount." msgstr "" -"Массив предлагаемых вариантов чаевых в наименьших единицах выбранной валюты " -"в формате JSON. Можно задать не более 4 вариантов. Варианты чаевых должны быть " -"больше нуля, перечисленные в порядке строгого возрастания и не превышать max_tip_amount." +"Массив предлагаемых вариантов чаевых в наименьших единицах выбранной " +"валюты в формате JSON. Можно задать не более 4 вариантов. Варианты чаевых" +" должны быть больше нуля, перечисленные в порядке строгого возрастания и " +"не превышать max_tip_amount." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:36 #: telebot.async_telebot.AsyncTeleBot.send_invoice:77 @@ -1742,9 +1794,9 @@ msgid "" "payment provider. A detailed description of required fields should be " "provided by the payment provider." msgstr "" -"Данные о инвойсе в формате JSON, которые будут переданы платежной системе. " -"Подробное описание обязательных полей должно быть предоставлено провайдером " -"платежной системы." +"Данные о инвойсе в формате JSON, которые будут переданы платежной " +"системе. Подробное описание обязательных полей должно быть предоставлено " +"провайдером платежной системы." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:40 msgid "" @@ -1753,8 +1805,8 @@ msgid "" "what they are paying for." msgstr "" "URL изображения товара для инвойса. Может быть изображением товаров или " -"изображением инвойса. Людям больше нравится видеть фото товара, " -"за который они платят." +"изображением инвойса. Людям больше нравится видеть фото товара, за " +"который они платят." #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:44 #: telebot.async_telebot.AsyncTeleBot.send_invoice:37 @@ -1774,12 +1826,16 @@ msgstr "Высота изображения" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:53 #: telebot.async_telebot.AsyncTeleBot.send_invoice:46 msgid "Pass True, if you require the user's full name to complete the order" -msgstr "Передайте True, если для совершения заказа требуется полное имя пользователя" +msgstr "" +"Передайте True, если для совершения заказа требуется полное имя " +"пользователя" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:56 #: telebot.async_telebot.AsyncTeleBot.send_invoice:49 msgid "Pass True, if you require the user's phone number to complete the order" -msgstr "Передайте True, если для совершения заказа требуется номер телефона пользователя" +msgstr "" +"Передайте True, если для совершения заказа требуется номер телефона " +"пользователя" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:59 #: telebot.async_telebot.AsyncTeleBot.send_invoice:52 @@ -1791,13 +1847,14 @@ msgstr "Передайте True, если для совершения заказ msgid "" "Pass True, if you require the user's shipping address to complete the " "order" -msgstr "" -"Передайте True, если для совершения заказа требуется адрес доставки" +msgstr "Передайте True, если для совершения заказа требуется адрес доставки" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:65 #: telebot.async_telebot.AsyncTeleBot.send_invoice:61 msgid "Pass True, if user's phone number should be sent to provider" -msgstr "Передайте True, если номер телефона пользователя нужно отправить платежной системе" +msgstr "" +"Передайте True, если номер телефона пользователя нужно отправить " +"платежной системе" #: of telebot.async_telebot.AsyncTeleBot.create_invoice_link:68 #: telebot.async_telebot.AsyncTeleBot.send_invoice:64 @@ -1824,9 +1881,9 @@ msgid "" "Use this method to create new sticker set owned by a user. The bot will " "be able to edit the created sticker set. Returns True on success." msgstr "" -"Используйте этот метод, чтобы создать новый стикерпак, владельцем которого " -"станет пользователь. Бот будет иметь возможность редактировать созданный стикерпак. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы создать новый стикерпак, владельцем " +"которого станет пользователь. Бот будет иметь возможность редактировать " +"созданный стикерпак. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:5 msgid "" @@ -1844,11 +1901,11 @@ msgid "" "in \"_by_\". is case insensitive. 1-64 " "characters." msgstr "" -"Короткое имя стикерпака для использования в ссылках вида t.me/addstickers/ " -"(например animals). Может содержать только латинские буквы, цифры и " -"нижние подчеркивания. Должно начинаться с буквы, не может содержать подряд " -"идущие нижние подчеркивания и должно заканчиваться на \"_by_\". " -" учитывает регистр. 1-64 символа." +"Короткое имя стикерпака для использования в ссылках вида " +"t.me/addstickers/ (например animals). Может содержать только латинские " +"буквы, цифры и нижние подчеркивания. Должно начинаться с буквы, не может " +"содержать подряд идущие нижние подчеркивания и должно заканчиваться на " +"\"_by_\". учитывает регистр. 1-64 символа." #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:15 msgid "Sticker set title, 1-64 characters" @@ -1859,8 +1916,8 @@ msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." msgstr "" -"Передайте True, если создаётся стикерпак масок. Устарело, начиная с " -"Bot API 6.2, используйте sticker_type." +"Передайте True, если создаётся стикерпак масок. Устарело, начиная с Bot " +"API 6.2, используйте sticker_type." #: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:36 msgid "" @@ -1868,9 +1925,9 @@ msgid "" "emoji sticker sets can't be created via the Bot API at the moment. By " "default, a regular sticker set is created." msgstr "" -"Необязательный, тип стикерпака, передайте “regular” или “mask”. Стикерпаки " -"кастомных эмодзи пока что не могут быть созданы с помощью Bot API. По умолчанию " -"будет создан обычный стикерпак." +"Необязательный, тип стикерпака, передайте “regular” или “mask”. " +"Стикерпаки кастомных эмодзи пока что не могут быть созданы с помощью Bot " +"API. По умолчанию будет создан обычный стикерпак." #: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:1 msgid "" @@ -1879,8 +1936,8 @@ msgid "" "can_invite_users administrator right. Returns True on success." msgstr "" "Используйте этот метод, чтобы отклонить запрос на вступление в чат. Бот " -"должен быть администратором чата и иметь права администратора can_invite_users. " -"Возвращает True в случае успеха." +"должен быть администратором чата и иметь права администратора " +"can_invite_users. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:5 msgid "" @@ -1899,10 +1956,10 @@ msgid "" " the ‘All Members Are Admins’ setting is off in the target group." msgstr "" "Используйте этот метод, чтобы удалить фото чата. Нельзя изменить фото в " -"приватных чатах. Бот должен быть администратором чата и иметь соответствующие " -"права администратора. Возвращает True в случае успеха. " -"Примечание: В обычных группах (не супергруппах), метод будет работать только " -"в случаях, когда настройка ‘All Members Are Admins’ выключена." +"приватных чатах. Бот должен быть администратором чата и иметь " +"соответствующие права администратора. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппах), метод будет работать " +"только в случаях, когда настройка ‘All Members Are Admins’ выключена." #: of telebot.async_telebot.AsyncTeleBot.delete_chat_photo:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" @@ -1914,8 +1971,7 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#de msgid "" "Int or Str: Unique identifier for the target chat or username of the " "target channel (in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала (в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:1 msgid "" @@ -1925,10 +1981,11 @@ msgid "" "returned in getChat requests to check if the bot can use this method. " "Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить стикерпак группы из супергруппы. Бот " -"должен быть администратором чата и иметь соответствующие права администратора. " -"Используйте поле can_set_sticker_set, возвращаемое методом getChat, чтобы " -"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." +"Используйте этот метод, чтобы удалить стикерпак группы из супергруппы. " +"Бот должен быть администратором чата и иметь соответствующие права " +"администратора. Используйте поле can_set_sticker_set, возвращаемое " +"методом getChat, чтобы проверить, что бот может использовать этот метод. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:5 msgid "" @@ -1946,17 +2003,15 @@ msgid "" "topic. Returns True on success." msgstr "" "Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics, " -"за исключением случае, когда бот является создателем топика. " -"Возвращает True в случае успеха." +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deleteforumtopic" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" #: of telebot.async_telebot.AsyncTeleBot.delete_forum_topic:10 msgid "Identifier of the topic to delete" @@ -1976,15 +2031,16 @@ msgid "" "delete any message there. Returns True on success." msgstr "" "Используйте этот метод, чтобы удалить сообщение, в том числе сервисное, " -"ограничения: - Сообщение может быть удалено только если оно было отправлено " -"менее 48 часов назад. - Dice-сообщение в приватном чате может быть удалено " -"только если оно было отправлено более 24 часов назад. - Боты могут удалять " -"свои сообщения в приватных чатах, группах и супергруппах. - Боты могут удалять " -"чужие сообщение в приватных чатах. - Боты с правами администратора can_post_messages " -"могут удалять сообщения в каналах. - Если бот является администратором группы, он " -"может удалить любое сообщение в ней. - Если бот имеет права администратора " -"can_delete_messages в супергруппе или канале, он может удалить любое сообщение в них. " -"Возвращает True в случае успеха." +"ограничения: - Сообщение может быть удалено только если оно было " +"отправлено менее 48 часов назад. - Dice-сообщение в приватном чате может " +"быть удалено только если оно было отправлено более 24 часов назад. - Боты" +" могут удалять свои сообщения в приватных чатах, группах и супергруппах. " +"- Боты могут удалять чужие сообщение в приватных чатах. - Боты с правами " +"администратора can_post_messages могут удалять сообщения в каналах. - " +"Если бот является администратором группы, он может удалить любое " +"сообщение в ней. - Если бот имеет права администратора " +"can_delete_messages в супергруппе или канале, он может удалить любое " +"сообщение в них. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_message:11 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" @@ -2000,18 +2056,16 @@ msgid "" "scope and user language. After deletion, higher level commands will be " "shown to affected users. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить список команд бота для заданных поля " -"видимости и языка. После удаления, команды более широкого поля видимости " -"будут доступны пользователям, которых коснулись изменения. " +"Используйте этот метод, чтобы удалить список команд бота для заданных " +"поля видимости и языка. После удаления, команды более широкого поля " +"видимости будут доступны пользователям, которых коснулись изменения. " "Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletemycommands" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#deletemycommands" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletemycommands" #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:7 #: telebot.async_telebot.AsyncTeleBot.get_my_commands:6 @@ -2019,8 +2073,7 @@ msgstr "" msgid "" "The scope of users for which the commands are relevant. Defaults to " "BotCommandScopeDefault." -msgstr "" -"Область видимости команд. По умолчанию BotCommandScopeDefault." +msgstr "Область видимости команд. По умолчанию BotCommandScopeDefault." #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:11 #: telebot.async_telebot.AsyncTeleBot.get_my_commands:10 @@ -2030,8 +2083,9 @@ msgid "" "to all users from the given scope, for whose language there are no " "dedicated commands" msgstr "" -"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения коснутся " -"команд для всех пользователей в заданном поле видимости, не имеющих команд на их языке" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" #: of telebot.async_telebot.AsyncTeleBot.delete_state:1 msgid "Delete the current state of a user." @@ -2042,8 +2096,8 @@ msgid "" "Use this method to delete a sticker from a set created by the bot. " "Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного ботом. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного " +"ботом. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:3 msgid "" @@ -2063,8 +2117,8 @@ msgid "" "Use this method to remove webhook integration if you decide to switch " "back to getUpdates. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить вебхук, если вы решите перейти обратно на getUpdates. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы удалить вебхук, если вы решите перейти " +"обратно на getUpdates. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" @@ -2073,7 +2127,9 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#de #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:6 #: telebot.async_telebot.AsyncTeleBot.set_webhook:36 msgid "Pass True to drop all pending updates, defaults to None" -msgstr "Передайте True, чтобы удалить все предшествующие запуску бота апдейты, по умолчанию None" +msgstr "" +"Передайте True, чтобы удалить все предшествующие запуску бота апдейты, по" +" умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:9 msgid "Request connection timeout, defaults to None" @@ -2102,8 +2158,8 @@ msgid "" "the appropriate admin rights." msgstr "" "Используйте этот метод, чтобы изменить неосновную ссылку-приглашение, " -"созданную ботом. Бот должен быть администратором чата и иметь соответствующие " -"права администратора." +"созданную ботом. Бот должен быть администратором чата и иметь " +"соответствующие права администратора." #: of telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link:4 msgid "" @@ -2124,9 +2180,10 @@ msgid "" "must have can_manage_topics administrator rights, unless it is the " "creator of the topic. Returns True on success." msgstr "" -"Используйте этот метод, чтобы изменить название и иконку топика в супергруппе. " -"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " -"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы изменить название и иконку топика в " +"супергруппе. Бот должен быть администратором чата и иметь права " +"администратора can_manage_topics, кроме случаев, когда бот является " +"создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:5 msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" @@ -2137,16 +2194,43 @@ msgid "Identifier of the topic to edit" msgstr "id топика для изменения" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:13 -msgid "New name of the topic, 1-128 characters" -msgstr "Новое название топика, 1-128 символов" +msgid "" +"Optional, New name of the topic, 1-128 characters. If not specififed or " +"empty, the current name of the topic will be kept" +msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:16 +#: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:17 +msgid "" +"Optional, New unique identifier of the custom emoji shown as the topic " +"icon. Use getForumTopicIconStickers to get all allowed custom emoji " +"identifiers. Pass an empty string to remove the icon. If not specified, " +"the current icon will be kept" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:1 +#, fuzzy msgid "" -"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " -"must be exactly 1 character long" +"Use this method to edit the name of the 'General' topic in a forum " +"supergroup chat. The bot must be an administrator in the chat for this to" +" work and must have can_manage_topics administrator rights. Returns True " +"on success." msgstr "" -"Новый кастомный эмодзи для иконки топика. Должно быть “tgs” эмодзи, " -"длиной ровно в 1 символ" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editgeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" + +#: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:10 +#, fuzzy +msgid "New topic name, 1-128 characters" +msgstr "Название товара, 1-32 символа" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:1 msgid "Use this method to edit captions of messages." @@ -2169,8 +2253,8 @@ msgid "" "Required if inline_message_id is not specified. Unique identifier for the" " target chat or username of the target channel" msgstr "" -"Обязательный, если не указан inline_message_id. Уникальный id чата или username" -" канала" +"Обязательный, если не указан inline_message_id. Уникальный id чата или " +"username канала" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:11 msgid "Required if inline_message_id is not specified." @@ -2180,8 +2264,7 @@ msgstr "Обязательный, если не указан inline_message_id." msgid "" "Required if inline_message_id is not specified. Identifier of the inline " "message." -msgstr "" -"Обязательный, если не указан inline_message_id. id inline сообщения." +msgstr "Обязательный, если не указан inline_message_id. id inline сообщения." #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:17 msgid "New caption of the message, 0-1024 characters after entities parsing" @@ -2210,8 +2293,8 @@ msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." msgstr "" -"В случае успеха, если изменённое сообщение отправлено ботом, возвращается новый объект Message, " -"иначе (inline сообщения) возвращается True." +"В случае успеха, если изменённое сообщение отправлено ботом, возвращается" +" новый объект Message, иначе (inline сообщения) возвращается True." #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:27 msgid ":obj:`types.Message` | :obj:`bool`" @@ -2222,8 +2305,8 @@ msgid "" "Use this method to edit live location messages. A location can be edited " "until its live_period expires or editing is explicitly" msgstr "" -"Используйте этот метод, чтобы изменить live местоположение в сообщении. Местоположение может быть " -"изменено пока не истечёт live_period или не" +"Используйте этот метод, чтобы изменить live местоположение в сообщении. " +"Местоположение может быть изменено пока не истечёт live_period или не" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:2 msgid "" @@ -2231,9 +2314,9 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" -"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " -"сообщение не является inline сообщением, возвращается новый объект Message, " -"иначе возвращается True." +"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если " +"измененное сообщение не является inline сообщением, возвращается новый " +"объект Message, иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:5 msgid "" @@ -2256,8 +2339,8 @@ msgid "" "Required if inline_message_id is not specified. Identifier of the message" " to edit" msgstr "" -"Обязательный, если не указан inline_message_id. id сообщения, которое нужно " -"изменить" +"Обязательный, если не указан inline_message_id. id сообщения, которое " +"нужно изменить" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:19 #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:15 @@ -2273,8 +2356,7 @@ msgstr "JSON-сериализованный объект новой inline кл msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message" -msgstr "" -"Обязательный, если не указаны chat_id и message_id. id inline сообщения" +msgstr "Обязательный, если не указаны chat_id и message_id. id inline сообщения" #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:29 #: telebot.async_telebot.AsyncTeleBot.send_location:31 @@ -2286,24 +2368,24 @@ msgid "" "Direction in which the user is moving, in degrees. Must be between 1 and " "360 if specified." msgstr "" -"Направление, в котором пользователь двигается, в градусах. Если указано, должно " -"быть от 1 до 360." +"Направление, в котором пользователь двигается, в градусах. Если указано, " +"должно быть от 1 до 360." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:35 msgid "" "The maximum distance for proximity alerts about approaching another chat " "member, in meters. Must be between 1 and 100000 if specified." msgstr "" -"Максимальное расстояние для показа уведомлений о приближении других участников " -"чата, в метрах. Если указано, должно быть от 1 до 100000." +"Максимальное расстояние для показа уведомлений о приближении других " +"участников чата, в метрах. Если указано, должно быть от 1 до 100000." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:38 msgid "" "On success, if the edited message is not an inline message, the edited " "Message is returned, otherwise True is returned." msgstr "" -"В случае успеха, если измененное сообщение не является inline сообщением, возвращается " -"новый объект Message, иначе возвращается True." +"В случае успеха, если измененное сообщение не является inline сообщением," +" возвращается новый объект Message, иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:39 #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location:23 @@ -2318,20 +2400,17 @@ msgid "" " arbitrarily. When inline message is edited, new file can't be uploaded. " "Use previously uploaded file via its file_id or specify a URL." msgstr "" -"Используйте этот метод, чтобы изменить гифку, аудио, документ, фото или видео " -"в сообщении. Если сообщение является частью альбома, оно может быть " -"изменено только на фото или видео. Иначе, тип сообщения может быть изменен" -" на любой. При изменении inline сообщения, нельзя загрузить новый файл. " -"используйте ранее загруженные файлы через file_id или укажите URL." +"Используйте этот метод, чтобы изменить гифку, аудио, документ, фото или " +"видео в сообщении. Если сообщение является частью альбома, оно может быть" +" изменено только на фото или видео. Иначе, тип сообщения может быть " +"изменен на любой. При изменении inline сообщения, нельзя загрузить новый " +"файл. используйте ранее загруженные файлы через file_id или укажите URL." #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagemedia" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#editmessagemedia" - +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editmessagemedia" #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:8 msgid "A JSON-serialized object for a new media content of the message" @@ -2888,6 +2967,25 @@ msgstr "" msgid ":class:`telebot.types.WebhookInfo`" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to hide the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#hidegeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" + #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:1 msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" @@ -3349,6 +3447,25 @@ msgstr "" msgid "Identifier of the topic to reopen" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to reopen the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopengeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#createforumtopic" + #: of telebot.async_telebot.AsyncTeleBot.reply_to:1 msgid "" "Convenience function for `send_message(message.chat.id, text, " @@ -3366,7 +3483,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.reply_to:9 msgid "" "Additional keyword arguments which are passed to " -":meth:`telebot.async_telebot.AsyncTeleBot.send_message`" +":meth:`telebot.TeleBot.send_message`" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.reset_data:1 @@ -3607,6 +3724,11 @@ msgstr "" msgid "Identifier of a message thread, in which the video will be sent" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_animation:60 +#, fuzzy +msgid "Pass True, if the animation should be sent as a spoiler" +msgstr "Передайте True, если email пользователя нужно отправить платежной системе" + #: of telebot.async_telebot.AsyncTeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -3713,6 +3835,11 @@ msgid "" "upload_video_note for video notes." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:22 +#, fuzzy +msgid "The thread to which the message will be sent(supergroups only)" +msgstr "id топика, в который нужно отправить сообщение" + #: of telebot.async_telebot.AsyncTeleBot.send_contact:1 msgid "" "Use this method to send phone contacts. On success, the sent Message is " @@ -4041,6 +4168,13 @@ msgstr "" msgid "Mode for parsing entities in the photo caption." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_photo:45 +#, fuzzy +msgid "Pass True, if the photo should be sent as a spoiler" +msgstr "" +"Передайте True, если номер телефона пользователя нужно отправить " +"платежной системе" + #: of telebot.async_telebot.AsyncTeleBot.send_poll:1 msgid "" "Use this method to send a native poll. On success, the sent Message is " @@ -4239,6 +4373,13 @@ msgstr "" msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_video:61 +#, fuzzy +msgid "Pass True, if the video should be sent as a spoiler" +msgstr "" +"Передайте True, если номер телефона пользователя нужно отправить " +"платежной системе" + #: of telebot.async_telebot.AsyncTeleBot.send_video_note:1 msgid "" "As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " @@ -4775,6 +4916,25 @@ msgstr "" msgid "Unique identifier of the target sender chat." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to unhide the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unhidegeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" + #: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:1 msgid "" "Use this method to unpin a all pinned messages in a supergroup chat. The " @@ -4859,7 +5019,7 @@ msgstr "" msgid "Class for (next step|reply) handlers" msgstr "" -#: ../../async_version/index.rst:22 +#: ../../source/async_version/index.rst:22 msgid "Asyncio filters" msgstr "" @@ -5007,7 +5167,7 @@ msgstr "" msgid "Filter to check whether message starts with some text." msgstr "" -#: ../../async_version/index.rst:30 +#: ../../source/async_version/index.rst:30 msgid "Asyncio handler backends" msgstr "" @@ -5065,7 +5225,7 @@ msgstr "" msgid "Class representing common states." msgstr "" -#: ../../async_version/index.rst:41 +#: ../../source/async_version/index.rst:41 msgid "Extensions" msgstr "" @@ -5083,3 +5243,22 @@ msgid "" "manually by user." msgstr "" +#~ msgid "New name of the topic, 1-128 characters" +#~ msgstr "Новое название топика, 1-128 символов" + +#~ msgid "" +#~ "New custom emoji for the topic " +#~ "icon. Must be an emoji of type " +#~ "“tgs” and must be exactly 1 " +#~ "character long" +#~ msgstr "" +#~ "Новый кастомный эмодзи для иконки " +#~ "топика. Должно быть “tgs” эмодзи, длиной" +#~ " ровно в 1 символ" + +#~ msgid "" +#~ "Additional keyword arguments which are " +#~ "passed to " +#~ ":meth:`telebot.async_telebot.AsyncTeleBot.send_message`" +#~ msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 11555e9bf..585feec1f 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-01-02 19:24+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,19 +18,19 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../sync_version/index.rst:3 +#: ../../source/sync_version/index.rst:3 msgid "TeleBot version" msgstr "Синхронный TeleBot" -#: ../../sync_version/index.rst:5 +#: ../../source/sync_version/index.rst:5 msgid "Synchronous pyTelegramBotAPI documentation" msgstr "Документация синхронного pyTelegramBotAPI" -#: ../../sync_version/index.rst:5 +#: ../../source/sync_version/index.rst:5 msgid "ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync" msgstr "ptba, pytba, pyTelegramBotAPI, методы, гайд, файлы, sync" -#: ../../sync_version/index.rst:10 +#: ../../source/sync_version/index.rst:10 msgid "TeleBot methods" msgstr "Методы класса TeleBot" @@ -107,15 +107,17 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.clear_reply_handlers_by_message_id #: telebot.TeleBot.clear_step_handler #: telebot.TeleBot.clear_step_handler_by_chat_id -#: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message -#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic -#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.close_forum_topic telebot.TeleBot.close_general_forum_topic +#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link +#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link +#: telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands #: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set #: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file #: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_general_forum_topic #: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup @@ -134,13 +136,14 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.get_my_default_administrator_rights #: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set #: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling -#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat -#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers -#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler -#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message -#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler -#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.hide_general_forum_topic +#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler +#: telebot.TeleBot.leave_chat telebot.TeleBot.load_next_step_handlers +#: telebot.TeleBot.load_reply_handlers telebot.TeleBot.message_handler +#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler +#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler +#: telebot.TeleBot.poll_handler telebot.TeleBot.polling +#: telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler @@ -161,7 +164,8 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.register_poll_handler #: telebot.TeleBot.register_pre_checkout_query_handler #: telebot.TeleBot.register_shipping_query_handler -#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.reopen_forum_topic +#: telebot.TeleBot.reopen_general_forum_topic telebot.TeleBot.reply_to #: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member #: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link #: telebot.TeleBot.run_webhooks telebot.TeleBot.send_animation @@ -186,6 +190,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.shipping_query_handler #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat +#: telebot.TeleBot.unhide_general_forum_topic #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file @@ -435,9 +440,10 @@ msgid "" "Use this method to add a new sticker to a set created by the bot. It's " "required to pass `png_sticker` or `tgs_sticker`. Returns True on success." msgstr "" -"Используйте этот метод, чтобы добавить новый стикер в стикерпак, созданный ботом. Необходимо " -"передать либо `png_sticker`, либо `tgs_sticker`, либо `webm_sticker`. " -"Возвращает True в случае успешного добавления." +"Используйте этот метод, чтобы добавить новый стикер в стикерпак, " +"созданный ботом. Необходимо передать либо `png_sticker`, либо " +"`tgs_sticker`, либо `webm_sticker`. Возвращает True в случае успешного " +"добавления." #: of telebot.TeleBot.add_sticker_to_set:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" @@ -468,29 +474,33 @@ msgid "" " to get a file from the Internet, or upload a new one using multipart" "/form-data." msgstr "" -"Изображение стикера в формате PNG, весом не более 512 килобайт, " -"размеры не должны превышать 512px, либо ширина, либо высота должны быть " -"ровно 512px. Передайте file_id в формате str, чтобы отправить уже загруженный " -"на сервера Telegram файл, передайте HTTP URL в формате str, чтобы Telegram " -"скачал файл из интернета, или загрузите новый файл с помощью multipart/form-data." +"Изображение стикера в формате PNG, весом не более 512 килобайт, размеры " +"не должны превышать 512px, либо ширина, либо высота должны быть ровно " +"512px. Передайте file_id в формате str, чтобы отправить уже загруженный " +"на сервера Telegram файл, передайте HTTP URL в формате str, чтобы " +"Telegram скачал файл из интернета, или загрузите новый файл с помощью " +"multipart/form-data." #: of telebot.TeleBot.add_sticker_to_set:21 #: telebot.TeleBot.create_new_sticker_set:26 msgid "TGS animation with the sticker, uploaded using multipart/form-data." -msgstr "Анимированный стикер в формате TGS, загруженный с помощью multipart/form-data." +msgstr "" +"Анимированный стикер в формате TGS, загруженный с помощью multipart/form-" +"data." #: of telebot.TeleBot.add_sticker_to_set:24 #: telebot.TeleBot.create_new_sticker_set:29 msgid "WebM animation with the sticker, uploaded using multipart/form-data." -msgstr "Анимированный стикер в формате WebM, загруженный с помощью multipart/form-data." +msgstr "" +"Анимированный стикер в формате WebM, загруженный с помощью multipart" +"/form-data." #: of telebot.TeleBot.add_sticker_to_set:27 #: telebot.TeleBot.create_new_sticker_set:40 msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" -msgstr "" -"Позиция для размещения маски на лицах в формате JSON" +msgstr "Позиция для размещения маски на лицах в формате JSON" #: of telebot.TeleBot.add_sticker_to_set:30 #: telebot.TeleBot.answer_callback_query:22 @@ -501,7 +511,7 @@ msgstr "" #: telebot.TeleBot.create_new_sticker_set:43 #: telebot.TeleBot.delete_forum_topic:13 #: telebot.TeleBot.delete_sticker_from_set:6 -#: telebot.TeleBot.edit_forum_topic:19 telebot.TeleBot.reopen_forum_topic:13 +#: telebot.TeleBot.edit_forum_topic:22 telebot.TeleBot.reopen_forum_topic:13 #: telebot.TeleBot.set_sticker_position_in_set:11 #: telebot.TeleBot.set_sticker_set_thumb:15 #: telebot.TeleBot.unpin_all_forum_topic_messages:13 @@ -581,11 +591,11 @@ msgstr "" #: telebot.TeleBot.delete_forum_topic:14 telebot.TeleBot.delete_message:23 #: telebot.TeleBot.delete_my_commands:17 #: telebot.TeleBot.delete_sticker_from_set:7 telebot.TeleBot.delete_webhook:13 -#: telebot.TeleBot.edit_forum_topic:20 telebot.TeleBot.leave_chat:8 +#: telebot.TeleBot.edit_forum_topic:23 telebot.TeleBot.leave_chat:8 #: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 #: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 #: telebot.TeleBot.reopen_forum_topic:14 -#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:23 +#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:26 #: telebot.TeleBot.set_chat_administrator_custom_title:18 #: telebot.TeleBot.set_chat_description:14 #: telebot.TeleBot.set_chat_menu_button:15 @@ -608,9 +618,9 @@ msgid "" "keyboards. The answer will be displayed to the user as a notification at " "the top of the chat screen or as an alert." msgstr "" -"Используйте этот метод для отправки ответов на callback запросы, отправленные с помощью inline " -"кнопок. Ответ будет показан пользователю как уведомление поверх чата или pop-up предупреждение." - +"Используйте этот метод для отправки ответов на callback запросы, " +"отправленные с помощью inline кнопок. Ответ будет показан пользователю " +"как уведомление поверх чата или pop-up предупреждение." #: of telebot.TeleBot.answer_callback_query:4 msgid "" @@ -632,14 +642,16 @@ msgid "" "Text of the notification. If not specified, nothing will be shown to the " "user, 0-200 characters" msgstr "" -"Текст уведомления. если не задан, то уведомление не будет показано, 0-200 символов" +"Текст уведомления. если не задан, то уведомление не будет показано, 0-200" +" символов" #: of telebot.TeleBot.answer_callback_query:12 msgid "" "If True, an alert will be shown by the client instead of a notification " "at the top of the chat screen. Defaults to false." msgstr "" -"Если True, вместо уведомления поверх чата будет показано pop-up предупреждение, по умолчанию False." +"Если True, вместо уведомления поверх чата будет показано pop-up " +"предупреждение, по умолчанию False." #: of telebot.TeleBot.answer_callback_query:15 msgid "" @@ -648,10 +660,10 @@ msgid "" "your game - note that this will only work if the query comes from a " "callback_game button." msgstr "" -"URL, который будет открыт пользовательским клиентом. Если вы создали игру " -"и приняли условия через @BotFather, задайте URL, открывающий вашу игру " -"- учитывайте, что это сработает только если запрос был отправлен с помощью " -"callback_game кнопки." +"URL, который будет открыт пользовательским клиентом. Если вы создали игру" +" и приняли условия через @BotFather, задайте URL, открывающий вашу игру -" +" учитывайте, что это сработает только если запрос был отправлен с помощью" +" callback_game кнопки." #: of telebot.TeleBot.answer_callback_query:19 msgid "" @@ -659,16 +671,18 @@ msgid "" "query may be cached client-side. Telegram apps will support caching " "starting in version 3.14. Defaults to 0." msgstr "" -"Максимальная длительность хранения ответа на callback запрос пользовательским клиентом в секундах. " -"Приложения Telegram поддерживают хранение ответов начиная с версии 3.14, по умолчанию 0." +"Максимальная длительность хранения ответа на callback запрос " +"пользовательским клиентом в секундах. Приложения Telegram поддерживают " +"хранение ответов начиная с версии 3.14, по умолчанию 0." #: of telebot.TeleBot.answer_inline_query:1 msgid "" "Use this method to send answers to an inline query. On success, True is " "returned. No more than 50 results per query are allowed." msgstr "" -"Используйте этот метод для отправки ответов на inline запрос. В случае успеха возвращается True. " -"Разрешено отправить не более 50 результатов на один запрос." +"Используйте этот метод для отправки ответов на inline запрос. В случае " +"успеха возвращается True. Разрешено отправить не более 50 результатов на " +"один запрос." #: of telebot.TeleBot.answer_inline_query:4 msgid "" @@ -691,23 +705,24 @@ msgid "" "The maximum amount of time in seconds that the result of the inline query" " may be cached on the server." msgstr "" -"Максимальная длительность хранения результатов inline запроса на сервере в секундах." +"Максимальная длительность хранения результатов inline запроса на сервере " +"в секундах." #: of telebot.TeleBot.answer_inline_query:16 msgid "" "Pass True, if results may be cached on the server side only for the user " "that sent the query." msgstr "" -"Передайте True, если результаты должны быть сохранены на сервере только для пользователя, " -"отправившего запрос." +"Передайте True, если результаты должны быть сохранены на сервере только " +"для пользователя, отправившего запрос." #: of telebot.TeleBot.answer_inline_query:20 msgid "" "Pass the offset that a client should send in the next query with the same" " text to receive more results." msgstr "" -"Передайте смещение, которое клиент должен отправить в следующем запросе с таким же " -"текстом, чтобы получить новые результаты." +"Передайте смещение, которое клиент должен отправить в следующем запросе с" +" таким же текстом, чтобы получить новые результаты." #: of telebot.TeleBot.answer_inline_query:24 msgid "" @@ -724,22 +739,25 @@ msgid "" "capabilities." msgstr "" "Параметр для команды /start, отправляемой боту, когда пользователь " -"нажимает кнопку переключения. 1-64 символа, разрешены только A-Z, a-z, 0-9, _ и -. " -"Пример: Inline бот, который отправляет видео с YouTube может попросить " -"пользователя подключить бота к его YouTube аккаунту, чтобы поиск соответствовал " -"предпочтениям пользователя. Чтобы это сделать, бот отправляет пользователю кнопку " -"'Подключить YouTube аккаунт' над результатами, или даже до их показа. Пользователь нажимает " -"на кнопку, автоматически переходит в приватный чат с ботом и в это время " -"передаёт стартовый параметр, по которому бот возвращает ссылку для авторизации (OAuth). " -"Как только авторизация пройдена, бот может предложить switch_inline кнопку, чтобы пользователь мог " -"легко вернуться в чат, где он хотел использовать возможности inline бота." +"нажимает кнопку переключения. 1-64 символа, разрешены только A-Z, a-z, " +"0-9, _ и -. Пример: Inline бот, который отправляет видео с YouTube может " +"попросить пользователя подключить бота к его YouTube аккаунту, чтобы " +"поиск соответствовал предпочтениям пользователя. Чтобы это сделать, бот " +"отправляет пользователю кнопку 'Подключить YouTube аккаунт' над " +"результатами, или даже до их показа. Пользователь нажимает на кнопку, " +"автоматически переходит в приватный чат с ботом и в это время передаёт " +"стартовый параметр, по которому бот возвращает ссылку для авторизации " +"(OAuth). Как только авторизация пройдена, бот может предложить " +"switch_inline кнопку, чтобы пользователь мог легко вернуться в чат, где " +"он хотел использовать возможности inline бота." #: of telebot.TeleBot.answer_inline_query:32 msgid "" "Parameter for the start message sent to the bot when user presses the " "switch button" msgstr "" -"Параметр для передачи боту вместе с сообщением /start, отправленному при нажатии кнопки переключения" +"Параметр для передачи боту вместе с сообщением /start, отправленному при " +"нажатии кнопки переключения" #: of telebot.TeleBot.answer_pre_checkout_query:1 msgid "" @@ -749,15 +767,17 @@ msgid "" "queries. On success, True is returned." msgstr "" "Как только пользователь подтвердил детали оплаты и доставки, Bot API " -"отправляет финальное подтверждение в виде апдейта с полем pre_checkout_query. " -"Используйте этот метод для ответа на такие pre-checkout запросы. В случае успеха возвращается True." +"отправляет финальное подтверждение в виде апдейта с полем " +"pre_checkout_query. Используйте этот метод для ответа на такие pre-" +"checkout запросы. В случае успеха возвращается True." #: of telebot.TeleBot.answer_pre_checkout_query:6 msgid "" "The Bot API must receive an answer within 10 seconds after the pre-" "checkout query was sent." msgstr "" -"Bot API должно получить ответ в течение 10 секунд после отправки pre-checkout запроса." +"Bot API должно получить ответ в течение 10 секунд после отправки pre-" +"checkout запроса." #: of telebot.TeleBot.answer_pre_checkout_query:8 msgid "" @@ -785,11 +805,12 @@ msgid "" "different color or garment!\"). Telegram will display this message to the" " user." msgstr "" -"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать человек, объясняющее " -"причину, по которой бот не может обработать заказ (например " -"\"Извините, кто-то только что купил последнюю из наших прекрасных черных футболок с коротким рукавом " -"пока вы заполняли детали оплаты. Пожалуйста выберите другой цвет или фасон!\"). " -"Telegram покажет это сообщение пользователю." +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое " +"может прочитать человек, объясняющее причину, по которой бот не может " +"обработать заказ (например \"Извините, кто-то только что купил последнюю " +"из наших прекрасных черных футболок с коротким рукавом пока вы заполняли " +"детали оплаты. Пожалуйста выберите другой цвет или фасон!\"). Telegram " +"покажет это сообщение пользователю." #: of telebot.TeleBot.answer_shipping_query:1 msgid "Asks for an answer to a shipping question." @@ -809,15 +830,17 @@ msgid "" "if there are any problems (for example, if delivery to the specified " "address is not possible)" msgstr "" -"Задайте True если доставка по выбранному адресу возможна и False, если есть " -"какие-то проблемы (например, доставка по выбранному адресу не осуществляется)" +"Задайте True если доставка по выбранному адресу возможна и False, если " +"есть какие-то проблемы (например, доставка по выбранному адресу не " +"осуществляется)" #: of telebot.TeleBot.answer_shipping_query:11 msgid "" "Required if ok is True. A JSON-serialized array of available shipping " "options." msgstr "" -"Обязательный в случае, когда ok - True. Массив вариантов доставки в формате JSON." +"Обязательный в случае, когда ok - True. Массив вариантов доставки в " +"формате JSON." #: of telebot.TeleBot.answer_shipping_query:14 msgid "" @@ -826,10 +849,10 @@ msgid "" "delivery to your desired address is unavailable'). Telegram will display " "this message to the user." msgstr "" -"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое может прочитать " -"человек, объясняющее причину, по которой невозможно завершить заказ (например " -"\"Извините, доставка по запрошенному адресу недоступна\"). " -"Telegram покажет это сообщение пользователю." +"Обязательный в случае, когда ok - False. Сообщение об ошибке, которое " +"может прочитать человек, объясняющее причину, по которой невозможно " +"завершить заказ (например \"Извините, доставка по запрошенному адресу " +"недоступна\"). Telegram покажет это сообщение пользователю." #: of telebot.TeleBot.answer_web_app_query:1 msgid "" @@ -838,9 +861,10 @@ msgid "" " the query originated. On success, a SentWebAppMessage object is " "returned." msgstr "" -"Используйте этот метод, чтобы задать результат взаимодействия с Web App и " -"отправить соответствующее сообщение от лица пользователя в чат, из которого пришел запрос. " -"В случае успеха возвращается объект SentWebAppMessage." +"Используйте этот метод, чтобы задать результат взаимодействия с Web App и" +" отправить соответствующее сообщение от лица пользователя в чат, из " +"которого пришел запрос. В случае успеха возвращается объект " +"SentWebAppMessage." #: of telebot.TeleBot.answer_web_app_query:6 msgid "" @@ -868,8 +892,8 @@ msgid "" "administrator in the chat for this to work and must have the " "can_invite_users administrator right. Returns True on success." msgstr "" -"Используйте этот метод, чтобы одобрить запрос на вступление в чат. " -"Бот должен быть администратором чата и иметь права администратора " +"Используйте этот метод, чтобы одобрить запрос на вступление в чат. Бот " +"должен быть администратором чата и иметь права администратора " "can_invite_users. Возвращает True в случае успеха." #: of telebot.TeleBot.approve_chat_join_request:5 @@ -890,7 +914,8 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup (in the format @supergroupusername)" msgstr "" -"Уникальный id чата или username супергруппы (в формате @supergroupusername)" +"Уникальный id чата или username супергруппы (в формате " +"@supergroupusername)" #: of telebot.TeleBot.approve_chat_join_request:11 #: telebot.TeleBot.ban_chat_member:12 @@ -927,9 +952,10 @@ msgid "" " to the chat on their own using invite links, etc., unless unbanned " "first. Returns True on success." msgstr "" -"Используйте этот метод, чтобы заблокировать пользователя в группе, супергруппе или канале. В " -"случае супергрупп и каналов, пользователь не сможет вернуться в чат самостоятельно, используя " -"ссылки с приглашением и т.д., пока не будет разблокирован. Возвращает True в случае успеха." +"Используйте этот метод, чтобы заблокировать пользователя в группе, " +"супергруппе или канале. В случае супергрупп и каналов, пользователь не " +"сможет вернуться в чат самостоятельно, используя ссылки с приглашением и " +"т.д., пока не будет разблокирован. Возвращает True в случае успеха." #: of telebot.TeleBot.ban_chat_member:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" @@ -940,8 +966,8 @@ msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" -"Уникальный id группы или username супергруппы или канала " -"(в формате @channelusername)" +"Уникальный id группы или username супергруппы или канала (в формате " +"@channelusername)" #: of telebot.TeleBot.ban_chat_member:15 msgid "" @@ -949,9 +975,9 @@ msgid "" "more than 366 days or less than 30 seconds from the current time they are" " considered to be banned forever" msgstr "" -"Дата, когда пользователь будет разблокирован, в формате UNIX time. Если пользователь " -"заблокирован больше чем на 366 дней или меньше чем на 30 секунд, то он будет заблокирован " -"до ручной разблокировки" +"Дата, когда пользователь будет разблокирован, в формате UNIX time. Если " +"пользователь заблокирован больше чем на 366 дней или меньше чем на 30 " +"секунд, то он будет заблокирован до ручной разблокировки" #: of telebot.TeleBot.ban_chat_member:19 msgid "" @@ -961,12 +987,12 @@ msgid "" "supergroups and channels." msgstr "" "Bool: Передайте True, чтобы удалить все сообщения пользователя из чата. " -"Если False, пользователю будут доступны все сообщения в группе, отправленные до его блокировки. " -"Всегда True для супергрупп и каналов." +"Если False, пользователю будут доступны все сообщения в группе, " +"отправленные до его блокировки. Всегда True для супергрупп и каналов." #: of telebot.TeleBot.ban_chat_member:24 #: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 -#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:22 +#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:25 msgid "Returns True on success." msgstr "Возвращает True в случае успеха." @@ -978,11 +1004,11 @@ msgid "" "administrator in the supergroup or channel for this to work and must have" " the appropriate administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы заблокировать канал в супергруппе или канале. Владелец " -"канала не сможет отправлять сообщения и участвовать в прямых эфирах " -"от лица канала, пока канал не будет разблокирован. Бот должен быть " -"администратором супергруппы или канала и иметь соответствующие права администратора." -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы заблокировать канал в супергруппе или " +"канале. Владелец канала не сможет отправлять сообщения и участвовать в " +"прямых эфирах от лица канала, пока канал не будет разблокирован. Бот " +"должен быть администратором супергруппы или канала и иметь " +"соответствующие права администратора.Возвращает True в случае успеха." #: of telebot.TeleBot.ban_chat_sender_chat:8 msgid "" @@ -993,29 +1019,32 @@ msgstr "" "https://core.telegram.org/bots/api#banchatsenderchat" #: of telebot.TeleBot.ban_chat_sender_chat:10 -#: telebot.TeleBot.close_forum_topic:7 telebot.TeleBot.copy_message:5 +#: telebot.TeleBot.close_forum_topic:7 +#: telebot.TeleBot.close_general_forum_topic:7 telebot.TeleBot.copy_message:5 #: telebot.TeleBot.create_forum_topic:7 telebot.TeleBot.delete_forum_topic:7 #: telebot.TeleBot.delete_message:13 telebot.TeleBot.edit_forum_topic:7 +#: telebot.TeleBot.edit_general_forum_topic:7 #: telebot.TeleBot.edit_message_live_location:13 -#: telebot.TeleBot.forward_message:8 telebot.TeleBot.pin_chat_message:7 -#: telebot.TeleBot.reopen_forum_topic:7 telebot.TeleBot.send_animation:6 -#: telebot.TeleBot.send_audio:9 telebot.TeleBot.send_dice:5 -#: telebot.TeleBot.send_document:5 telebot.TeleBot.send_game:5 -#: telebot.TeleBot.send_location:5 telebot.TeleBot.send_media_group:6 -#: telebot.TeleBot.send_message:9 telebot.TeleBot.send_photo:5 -#: telebot.TeleBot.send_sticker:6 telebot.TeleBot.send_video:5 -#: telebot.TeleBot.send_video_note:6 telebot.TeleBot.send_voice:7 -#: telebot.TeleBot.set_chat_description:6 telebot.TeleBot.set_chat_title:9 +#: telebot.TeleBot.forward_message:8 telebot.TeleBot.hide_general_forum_topic:7 +#: telebot.TeleBot.pin_chat_message:7 telebot.TeleBot.reopen_forum_topic:7 +#: telebot.TeleBot.reopen_general_forum_topic:7 +#: telebot.TeleBot.send_animation:6 telebot.TeleBot.send_audio:9 +#: telebot.TeleBot.send_dice:5 telebot.TeleBot.send_document:5 +#: telebot.TeleBot.send_game:5 telebot.TeleBot.send_location:5 +#: telebot.TeleBot.send_media_group:6 telebot.TeleBot.send_message:9 +#: telebot.TeleBot.send_photo:5 telebot.TeleBot.send_sticker:6 +#: telebot.TeleBot.send_video:5 telebot.TeleBot.send_video_note:6 +#: telebot.TeleBot.send_voice:7 telebot.TeleBot.set_chat_description:6 +#: telebot.TeleBot.set_chat_title:9 #: telebot.TeleBot.stop_message_live_location:6 #: telebot.TeleBot.unban_chat_sender_chat:8 +#: telebot.TeleBot.unhide_general_forum_topic:7 #: telebot.TeleBot.unpin_all_forum_topic_messages:7 #: telebot.TeleBot.unpin_chat_message:7 msgid "" "Unique identifier for the target chat or username of the target channel " "(in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала " -"(в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.TeleBot.ban_chat_sender_chat:13 msgid "Unique identifier of the target sender chat" @@ -1026,8 +1055,8 @@ msgid "" "Handles new incoming callback query. As a parameter to the decorator " "function, it passes :class:`telebot.types.CallbackQuery` object." msgstr "" -"Обрабатывает новый callback запрос. В качестве параметра передаёт в декорируемую " -"функцию объект :class:`telebot.types.CallbackQuery`." +"Обрабатывает новый callback запрос. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." #: of telebot.TeleBot.callback_query_handler:4 #: telebot.TeleBot.channel_post_handler:10 @@ -1091,8 +1120,8 @@ msgid "" "etc. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" -"Обрабатывает новый пост любого типа в канале - текст, фото, стикер и т.д. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает новый пост любого типа в канале - текст, фото, стикер и т.д." +" В качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.Message`." #: of telebot.TeleBot.channel_post_handler:4 @@ -1124,9 +1153,9 @@ msgid "" "updates. As a parameter to the decorator function, it passes " ":class:`telebot.types.ChatJoinRequest` object." msgstr "" -"Обрабатывает запрос на вступление в чат. Бот должен иметь права администратора" -"can_invite_users в чате, чтобы получать такие апдейты. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает запрос на вступление в чат. Бот должен иметь права " +"администратораcan_invite_users в чате, чтобы получать такие апдейты. В " +"качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.ChatJoinRequest`." #: of telebot.TeleBot.chat_member_handler:1 @@ -1137,11 +1166,10 @@ msgid "" "the decorator function, it passes " ":class:`telebot.types.ChatMemberUpdated` object." msgstr "" -"Обрабатывает изменение статуса пользователя в чате. Бот должен быть администратором " -"чата и явно указать “chat_member“ в allowed_updates, чтобы получать такие апдейты. " -"В качестве параметра передаёт в декорируемую функцию объект " -":class:`telebot.types.ChatMemberUpdated`." - +"Обрабатывает изменение статуса пользователя в чате. Бот должен быть " +"администратором чата и явно указать “chat_member“ в allowed_updates, " +"чтобы получать такие апдейты. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.ChatMemberUpdated`." #: of telebot.TeleBot.chosen_inline_handler:1 msgid "" @@ -1151,10 +1179,11 @@ msgid "" "parameter to the decorator function, it passes " ":class:`telebot.types.ChosenInlineResult` object." msgstr "" -"Обрабатывает результат inline запроса, который был выбран пользователем и отправлен " -"собеседнику в чате. Пожалуйста ознакомьтесь с документацией по сбору фидбека " -"для получения таких апдейтов вашим ботом. В качестве параметра передаёт в " -"декорируемую функцию объект :class:`telebot.types.ChosenInlineResult`." +"Обрабатывает результат inline запроса, который был выбран пользователем и" +" отправлен собеседнику в чате. Пожалуйста ознакомьтесь с документацией по" +" сбору фидбека для получения таких апдейтов вашим ботом. В качестве " +"параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ChosenInlineResult`." #: of telebot.TeleBot.clear_reply_handlers:1 #: telebot.TeleBot.clear_reply_handlers_by_message_id:1 @@ -1162,8 +1191,8 @@ msgid "" "Clears all callback functions registered by register_for_reply() and " "register_for_reply_by_message_id()." msgstr "" -"Очищает список функций, зарегистрированных с помощью register_for_reply() и " -"register_for_reply_by_message_id()." +"Очищает список функций, зарегистрированных с помощью register_for_reply()" +" и register_for_reply_by_message_id()." #: of telebot.TeleBot.clear_reply_handlers:3 msgid "The message for which we want to clear reply handlers" @@ -1176,14 +1205,15 @@ msgstr "id сообщения, у которого нужно очистить #: of telebot.TeleBot.clear_step_handler:1 #: telebot.TeleBot.clear_step_handler_by_chat_id:1 msgid "Clears all callback functions registered by register_next_step_handler()." -msgstr "Очищает список функций, зарегистрированных с помощью register_next_step_handler()." +msgstr "" +"Очищает список функций, зарегистрированных с помощью " +"register_next_step_handler()." #: of telebot.TeleBot.clear_step_handler:3 msgid "" "The message for which we want to handle new message after that in same " "chat." -msgstr "" -"Сообщение, после которого нужно обработать новое сообщение в том же чате." +msgstr "Сообщение, после которого нужно обработать новое сообщение в том же чате." #: of telebot.TeleBot.clear_step_handler_by_chat_id:3 msgid "The chat for which we want to clear next step handlers" @@ -1197,11 +1227,11 @@ msgid "" "The method will return error 429 in the first 10 minutes after the bot is" " launched. Returns True on success." msgstr "" -"Используйте этот метод чтобы закрыть инстанс бота прежде чем перемещать его с одного " -"локального сервера на другой. Вы должны удалить вебхук перед вызовом этого метода, " -"чтобы убедиться. что бот не будет запущен повторно после перезапуска сервера. " -"Метод будет возвращать ошибку 429 в течение 10 минут после запуска бота. " -"Возвращает True в случае успеха." +"Используйте этот метод чтобы закрыть инстанс бота прежде чем перемещать " +"его с одного локального сервера на другой. Вы должны удалить вебхук перед" +" вызовом этого метода, чтобы убедиться. что бот не будет запущен повторно" +" после перезапуска сервера. Метод будет возвращать ошибку 429 в течение " +"10 минут после запуска бота. Возвращает True в случае успеха." #: of telebot.TeleBot.close:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#close" @@ -1214,9 +1244,10 @@ msgid "" "the can_manage_topics administrator rights, unless it is the creator of " "the topic. Returns True on success." msgstr "" -"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics, за исключением " -"случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. " +"Бот должен быть администратором чата и иметь права администратора " +"can_manage_topics, за исключением случаев, когда бот является создателем " +"топика. Возвращает True в случае успеха." #: of telebot.TeleBot.close_forum_topic:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" @@ -1226,6 +1257,25 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#cl msgid "Identifier of the topic to close" msgstr "id топика для закрытия" +#: of telebot.TeleBot.close_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to close the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы закрыть открытый топик в чате супергруппы. " +"Бот должен быть администратором чата и иметь права администратора " +"can_manage_topics, за исключением случаев, когда бот является создателем " +"топика. Возвращает True в случае успеха." + +#: of telebot.TeleBot.close_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#closegeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#closeforumtopic" + #: of telebot.TeleBot.copy_message:1 msgid "Use this method to copy messages of any kind." msgstr "Используйте этот метод для копирования любых сообщений." @@ -1239,8 +1289,8 @@ msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" msgstr "" -"Уникальный id чата, в который было отправлено исходное сообщение " -"(или username канала в формате @channelusername)" +"Уникальный id чата, в который было отправлено исходное сообщение (или " +"username канала в формате @channelusername)" #: of telebot.TeleBot.copy_message:10 telebot.TeleBot.forward_message:14 msgid "Message identifier in the chat specified in from_chat_id" @@ -1251,8 +1301,8 @@ msgid "" "New caption for media, 0-1024 characters after entities parsing. If not " "specified, the original caption is kept" msgstr "" -"Новая подпись для медиа, 0-1024 символа после форматирования. Если не задано, " -"используется исходная подпись" +"Новая подпись для медиа, 0-1024 символа после форматирования. Если не " +"задано, используется исходная подпись" #: of telebot.TeleBot.copy_message:16 msgid "Mode for parsing entities in the new caption." @@ -1263,8 +1313,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the new " "caption, which can be specified instead of parse_mode" msgstr "" -"Список отформатированных частей новой подписи в формате JSON, " -"можно использовать вместо parse_mode" +"Список отформатированных частей новой подписи в формате JSON, можно " +"использовать вместо parse_mode" #: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 #: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 @@ -1278,7 +1328,8 @@ msgid "" "Sends the message silently. Users will receive a notification with no " "sound." msgstr "" -"Отправить сообщение, при получении которого пользователи получат уведомление без звука." +"Отправить сообщение, при получении которого пользователи получат " +"уведомление без звука." #: of telebot.TeleBot.copy_message:25 telebot.TeleBot.send_animation:34 #: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:38 @@ -1314,9 +1365,8 @@ msgid "" "Pass True, if the message should be sent even if the specified replied-to" " message is not found" msgstr "" -"Передайте True, если сообщение нужно отправить даже в случае отсутствия сообщения, на " -"которое дан ответ" - +"Передайте True, если сообщение нужно отправить даже в случае отсутствия " +"сообщения, на которое дан ответ" #: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:40 #: telebot.TeleBot.send_contact:26 telebot.TeleBot.send_dice:18 @@ -1331,8 +1381,9 @@ msgid "" "keyboard, custom reply keyboard, instructions to remove reply keyboard or" " to force a reply from the user." msgstr "" -"Дополнительные элементы интерфейса. Inline клавиатура, текстовая клавиатура, " -"запрос на удаление текстовой клавиатуры или запрос на ответ от пользователя." +"Дополнительные элементы интерфейса. Inline клавиатура, текстовая " +"клавиатура, запрос на удаление текстовой клавиатуры или запрос на ответ " +"от пользователя." #: of telebot.TeleBot.copy_message:39 telebot.TeleBot.delete_message:19 #: telebot.TeleBot.edit_message_live_location:23 @@ -1356,25 +1407,25 @@ msgid "Identifier of a message thread, in which the message will be sent" msgstr "id топика, в который нужно отправить сообщение" #: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 -#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:60 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:63 #: telebot.TeleBot.send_audio:63 telebot.TeleBot.send_contact:44 #: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:56 #: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 #: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 -#: telebot.TeleBot.send_photo:45 telebot.TeleBot.send_poll:70 +#: telebot.TeleBot.send_photo:48 telebot.TeleBot.send_poll:70 #: telebot.TeleBot.send_sticker:39 telebot.TeleBot.send_venue:57 -#: telebot.TeleBot.send_video:61 telebot.TeleBot.send_video_note:48 +#: telebot.TeleBot.send_video:64 telebot.TeleBot.send_video_note:48 #: telebot.TeleBot.send_voice:49 msgid "On success, the sent Message is returned." msgstr "В случае успеха возвращает отправленное сообщение (Message)." #: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 -#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:61 +#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:64 #: telebot.TeleBot.send_audio:64 telebot.TeleBot.send_contact:45 #: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:57 #: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 -#: telebot.TeleBot.send_photo:46 telebot.TeleBot.send_sticker:40 -#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:62 +#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:40 +#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:65 #: telebot.TeleBot.send_video_note:49 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1387,10 +1438,11 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" -"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот " -"должен быть администратором чата и иметь соответствующие права администратора. " -"Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " -"ссылку-приглашение (ChatInviteLink)." +"Используйте этот метод, чтобы создать новую ссылку-приглашение в чат. Бот" +" должен быть администратором чата и иметь соответствующие права " +"администратора. Ссылка может быть аннулирована методом " +"revokeChatInviteLink. Возвращает новую ссылку-приглашение " +"(ChatInviteLink)." #: of telebot.TeleBot.create_chat_invite_link:6 msgid "" @@ -1407,8 +1459,7 @@ msgstr "" msgid "" "Id: Unique identifier for the target chat or username of the target " "channel (in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала (в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.TeleBot.create_chat_invite_link:12 #: telebot.TeleBot.edit_chat_invite_link:10 @@ -1453,17 +1504,15 @@ msgid "" "can_manage_topics administrator rights. Returns information about the " "created topic as a ForumTopic object." msgstr "" -"Используйте этот метод, чтобы создать топик в супергруппе. Бот должен быть " -"администратором чата и иметь права администратора can_manage_topics. " -"Возвращает информацию о созданном топике (ForumTopic)." +"Используйте этот метод, чтобы создать топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics." +" Возвращает информацию о созданном топике (ForumTopic)." #: of telebot.TeleBot.create_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#createforumtopic" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#createforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#createforumtopic" #: of telebot.TeleBot.create_forum_topic:10 msgid "Name of the topic, 1-128 characters" @@ -1482,15 +1531,14 @@ msgid "" "Custom emoji for the topic icon. Must be an emoji of type “tgs” and must " "be exactly 1 character long" msgstr "" -"Кастомный эмодзи для использования в качестве иконки топика. Должно быть “tgs” " -"эмодзи и быть ровно 1 символом" +"Кастомный эмодзи для использования в качестве иконки топика. Должно быть " +"“tgs” эмодзи и быть ровно 1 символом" #: of telebot.TeleBot.create_forum_topic:19 msgid "" "On success, information about the created topic is returned as a " "ForumTopic object." -msgstr "" -"В случае успеха возвращается информация о созданном топике (ForumTopic)." +msgstr "В случае успеха возвращается информация о созданном топике (ForumTopic)." #: of telebot.TeleBot.create_forum_topic:20 msgid ":class:`telebot.types.ForumTopic`" @@ -1501,8 +1549,8 @@ msgid "" "Use this method to create a link for an invoice. Returns the created " "invoice link as String on success." msgstr "" -"используйте этот метод, чтобы создать ссылку-инвойс. Возвращает созданную ссылку " -"в случае успеха (String)." +"используйте этот метод, чтобы создать ссылку-инвойс. Возвращает созданную" +" ссылку в случае успеха (String)." #: of telebot.TeleBot.create_invoice_link:4 msgid "" @@ -1559,9 +1607,10 @@ msgid "" " The suggested tip amounts must be positive, passed in a strictly " "increased order and must not exceed max_tip_amount." msgstr "" -"Массив предлагаемых вариантов чаевых в наименьших единицах выбранной валюты " -"в формате JSON. Можно задать не более 4 вариантов. Варианты чаевых должны быть " -"больше нуля, перечисленные в порядке строгого возрастания и не превышать max_tip_amount." +"Массив предлагаемых вариантов чаевых в наименьших единицах выбранной " +"валюты в формате JSON. Можно задать не более 4 вариантов. Варианты чаевых" +" должны быть больше нуля, перечисленные в порядке строгого возрастания и " +"не превышать max_tip_amount." #: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:77 msgid "" @@ -1569,9 +1618,9 @@ msgid "" "payment provider. A detailed description of required fields should be " "provided by the payment provider." msgstr "" -"Данные о инвойсе в формате JSON, которые будут переданы платежной системе. " -"Подробное описание обязательных полей должно быть предоставлено провайдером " -"платежной системы." +"Данные о инвойсе в формате JSON, которые будут переданы платежной " +"системе. Подробное описание обязательных полей должно быть предоставлено " +"провайдером платежной системы." #: of telebot.TeleBot.create_invoice_link:40 msgid "" @@ -1580,8 +1629,8 @@ msgid "" "what they are paying for." msgstr "" "URL изображения товара для инвойса. Может быть изображением товаров или " -"изображением инвойса. Людям больше нравится видеть фото товара, " -"за который они платят." +"изображением инвойса. Людям больше нравится видеть фото товара, за " +"который они платят." #: of telebot.TeleBot.create_invoice_link:44 telebot.TeleBot.send_invoice:37 msgid "Photo size in bytes" @@ -1597,11 +1646,15 @@ msgstr "Высота изображения" #: of telebot.TeleBot.create_invoice_link:53 telebot.TeleBot.send_invoice:46 msgid "Pass True, if you require the user's full name to complete the order" -msgstr "Передайте True, если для совершения заказа требуется полное имя пользователя" +msgstr "" +"Передайте True, если для совершения заказа требуется полное имя " +"пользователя" #: of telebot.TeleBot.create_invoice_link:56 telebot.TeleBot.send_invoice:49 msgid "Pass True, if you require the user's phone number to complete the order" -msgstr "Передайте True, если для совершения заказа требуется номер телефона пользователя" +msgstr "" +"Передайте True, если для совершения заказа требуется номер телефона " +"пользователя" #: of telebot.TeleBot.create_invoice_link:59 telebot.TeleBot.send_invoice:52 msgid "Pass True, if you require the user's email to complete the order" @@ -1611,12 +1664,13 @@ msgstr "Передайте True, если для совершения заказ msgid "" "Pass True, if you require the user's shipping address to complete the " "order" -msgstr "" -"Передайте True, если для совершения заказа требуется адрес доставки" +msgstr "Передайте True, если для совершения заказа требуется адрес доставки" #: of telebot.TeleBot.create_invoice_link:65 telebot.TeleBot.send_invoice:61 msgid "Pass True, if user's phone number should be sent to provider" -msgstr "Передайте True, если номер телефона пользователя нужно отправить платежной системе" +msgstr "" +"Передайте True, если номер телефона пользователя нужно отправить " +"платежной системе" #: of telebot.TeleBot.create_invoice_link:68 telebot.TeleBot.send_invoice:64 msgid "Pass True, if user's email address should be sent to provider" @@ -1640,9 +1694,9 @@ msgid "" "Use this method to create new sticker set owned by a user. The bot will " "be able to edit the created sticker set. Returns True on success." msgstr "" -"Используйте этот метод, чтобы создать новый стикерпак, владельцем которого " -"станет пользователь. Бот будет иметь возможность редактировать созданный стикерпак. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы создать новый стикерпак, владельцем " +"которого станет пользователь. Бот будет иметь возможность редактировать " +"созданный стикерпак. Возвращает True в случае успеха." #: of telebot.TeleBot.create_new_sticker_set:5 msgid "" @@ -1660,11 +1714,11 @@ msgid "" "in \"_by_\". is case insensitive. 1-64 " "characters." msgstr "" -"Короткое имя стикерпака для использования в ссылках вида t.me/addstickers/ " -"(например animals). Может содержать только латинские буквы, цифры и " -"нижние подчеркивания. Должно начинаться с буквы, не может содержать подряд " -"идущие нижние подчеркивания и должно заканчиваться на \"_by_\". " -" учитывает регистр. 1-64 символа." +"Короткое имя стикерпака для использования в ссылках вида " +"t.me/addstickers/ (например animals). Может содержать только латинские " +"буквы, цифры и нижние подчеркивания. Должно начинаться с буквы, не может " +"содержать подряд идущие нижние подчеркивания и должно заканчиваться на " +"\"_by_\". учитывает регистр. 1-64 символа." #: of telebot.TeleBot.create_new_sticker_set:15 msgid "Sticker set title, 1-64 characters" @@ -1675,8 +1729,8 @@ msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." msgstr "" -"Передайте True, если создаётся стикерпак масок. Устарело, начиная с " -"Bot API 6.2, используйте sticker_type." +"Передайте True, если создаётся стикерпак масок. Устарело, начиная с Bot " +"API 6.2, используйте sticker_type." #: of telebot.TeleBot.create_new_sticker_set:36 msgid "" @@ -1684,9 +1738,9 @@ msgid "" "emoji sticker sets can't be created via the Bot API at the moment. By " "default, a regular sticker set is created." msgstr "" -"Необязательный, тип стикерпака, передайте “regular” или “mask”. Стикерпаки " -"кастомных эмодзи пока что не могут быть созданы с помощью Bot API. По умолчанию " -"будет создан обычный стикерпак." +"Необязательный, тип стикерпака, передайте “regular” или “mask”. " +"Стикерпаки кастомных эмодзи пока что не могут быть созданы с помощью Bot " +"API. По умолчанию будет создан обычный стикерпак." #: of telebot.TeleBot.decline_chat_join_request:1 msgid "" @@ -1695,8 +1749,8 @@ msgid "" "can_invite_users administrator right. Returns True on success." msgstr "" "Используйте этот метод, чтобы отклонить запрос на вступление в чат. Бот " -"должен быть администратором чата и иметь права администратора can_invite_users. " -"Возвращает True в случае успеха." +"должен быть администратором чата и иметь права администратора " +"can_invite_users. Возвращает True в случае успеха." #: of telebot.TeleBot.decline_chat_join_request:5 msgid "" @@ -1715,10 +1769,10 @@ msgid "" " the ‘All Members Are Admins’ setting is off in the target group." msgstr "" "Используйте этот метод, чтобы удалить фото чата. Нельзя изменить фото в " -"приватных чатах. Бот должен быть администратором чата и иметь соответствующие " -"права администратора. Возвращает True в случае успеха. " -"Примечание: В обычных группах (не супергруппах), метод будет работать только " -"в случаях, когда настройка ‘All Members Are Admins’ выключена." +"приватных чатах. Бот должен быть администратором чата и иметь " +"соответствующие права администратора. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппах), метод будет работать " +"только в случаях, когда настройка ‘All Members Are Admins’ выключена." #: of telebot.TeleBot.delete_chat_photo:6 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletechatphoto" @@ -1729,8 +1783,7 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#de msgid "" "Int or Str: Unique identifier for the target chat or username of the " "target channel (in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала (в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.TeleBot.delete_chat_sticker_set:1 msgid "" @@ -1740,10 +1793,11 @@ msgid "" "returned in getChat requests to check if the bot can use this method. " "Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить стикерпак группы из супергруппы. Бот " -"должен быть администратором чата и иметь соответствующие права администратора. " -"Используйте поле can_set_sticker_set, возвращаемое методом getChat, чтобы " -"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." +"Используйте этот метод, чтобы удалить стикерпак группы из супергруппы. " +"Бот должен быть администратором чата и иметь соответствующие права " +"администратора. Используйте поле can_set_sticker_set, возвращаемое " +"методом getChat, чтобы проверить, что бот может использовать этот метод. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.delete_chat_sticker_set:5 msgid "" @@ -1761,17 +1815,15 @@ msgid "" "topic. Returns True on success." msgstr "" "Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics, " -"за исключением случае, когда бот является создателем топика. " -"Возвращает True в случае успеха." +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." #: of telebot.TeleBot.delete_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deleteforumtopic" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" #: of telebot.TeleBot.delete_forum_topic:10 msgid "Identifier of the topic to delete" @@ -1791,15 +1843,16 @@ msgid "" "delete any message there. Returns True on success." msgstr "" "Используйте этот метод, чтобы удалить сообщение, в том числе сервисное, " -"ограничения: - Сообщение может быть удалено только если оно было отправлено " -"менее 48 часов назад. - Dice-сообщение в приватном чате может быть удалено " -"только если оно было отправлено более 24 часов назад. - Боты могут удалять " -"свои сообщения в приватных чатах, группах и супергруппах. - Боты могут удалять " -"чужие сообщение в приватных чатах. - Боты с правами администратора can_post_messages " -"могут удалять сообщения в каналах. - Если бот является администратором группы, он " -"может удалить любое сообщение в ней. - Если бот имеет права администратора " -"can_delete_messages в супергруппе или канале, он может удалить любое сообщение в них. " -"Возвращает True в случае успеха." +"ограничения: - Сообщение может быть удалено только если оно было " +"отправлено менее 48 часов назад. - Dice-сообщение в приватном чате может " +"быть удалено только если оно было отправлено более 24 часов назад. - Боты" +" могут удалять свои сообщения в приватных чатах, группах и супергруппах. " +"- Боты могут удалять чужие сообщение в приватных чатах. - Боты с правами " +"администратора can_post_messages могут удалять сообщения в каналах. - " +"Если бот является администратором группы, он может удалить любое " +"сообщение в ней. - Если бот имеет права администратора " +"can_delete_messages в супергруппе или канале, он может удалить любое " +"сообщение в них. Возвращает True в случае успеха." #: of telebot.TeleBot.delete_message:11 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessage" @@ -1815,26 +1868,23 @@ msgid "" "scope and user language. After deletion, higher level commands will be " "shown to affected users. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить список команд бота для заданных поля " -"видимости и языка. После удаления, команды более широкого поля видимости " -"будут доступны пользователям, которых коснулись изменения. " +"Используйте этот метод, чтобы удалить список команд бота для заданных " +"поля видимости и языка. После удаления, команды более широкого поля " +"видимости будут доступны пользователям, которых коснулись изменения. " "Возвращает True в случае успеха." #: of telebot.TeleBot.delete_my_commands:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#deletemycommands" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#deletemycommands" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletemycommands" #: of telebot.TeleBot.delete_my_commands:7 telebot.TeleBot.get_my_commands:6 #: telebot.TeleBot.set_my_commands:8 msgid "" "The scope of users for which the commands are relevant. Defaults to " "BotCommandScopeDefault." -msgstr "" -"Область видимости команд. По умолчанию BotCommandScopeDefault." +msgstr "Область видимости команд. По умолчанию BotCommandScopeDefault." #: of telebot.TeleBot.delete_my_commands:11 telebot.TeleBot.get_my_commands:10 #: telebot.TeleBot.set_my_commands:12 @@ -1843,8 +1893,9 @@ msgid "" "to all users from the given scope, for whose language there are no " "dedicated commands" msgstr "" -"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения коснутся " -"команд для всех пользователей в заданном поле видимости, не имеющих команд на их языке" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" #: of telebot.TeleBot.delete_state:1 msgid "Delete the current state of a user." @@ -1855,8 +1906,8 @@ msgid "" "Use this method to delete a sticker from a set created by the bot. " "Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного ботом. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного " +"ботом. Возвращает True в случае успеха." #: of telebot.TeleBot.delete_sticker_from_set:3 msgid "" @@ -1876,8 +1927,8 @@ msgid "" "Use this method to remove webhook integration if you decide to switch " "back to getUpdates. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить вебхук, если вы решите перейти обратно на getUpdates. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы удалить вебхук, если вы решите перейти " +"обратно на getUpdates. Возвращает True в случае успеха." #: of telebot.TeleBot.delete_webhook:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#deletewebhook" @@ -1886,7 +1937,9 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#de #: of telebot.TeleBot.delete_webhook:6 telebot.TeleBot.run_webhooks:36 #: telebot.TeleBot.set_webhook:36 msgid "Pass True to drop all pending updates, defaults to None" -msgstr "Передайте True, чтобы удалить все предшествующие запуску бота апдейты, по умолчанию None" +msgstr "" +"Передайте True, чтобы удалить все предшествующие запуску бота апдейты, по" +" умолчанию None" #: of telebot.TeleBot.delete_webhook:9 telebot.TeleBot.run_webhooks:39 msgid "Request connection timeout, defaults to None" @@ -1895,7 +1948,9 @@ msgstr "Тайм-аут запроса, по умолчанию None" #: of telebot.TeleBot.disable_save_next_step_handlers:1 #: telebot.TeleBot.disable_save_reply_handlers:1 msgid "Disable saving next step handlers (by default saving disable)" -msgstr "Отключить сохранение next step хендлеров (по умолчанию сохранение отключено)" +msgstr "" +"Отключить сохранение next step хендлеров (по умолчанию сохранение " +"отключено)" #: of telebot.TeleBot.disable_save_next_step_handlers:3 msgid "" @@ -1904,9 +1959,10 @@ msgid "" "MemoryHandlerBackend is reassigned as a new next_step_backend backend " "instead of FileHandlerBackend." msgstr "" -"Эта функция оставлена для обратной совместимости, для отключения возможности " -"сохранения хендлеров в файл. В тех же целях MemoryHandlerBackend переопределен " -"как новый next_step_backend вместо FileHandlerBackend." +"Эта функция оставлена для обратной совместимости, для отключения " +"возможности сохранения хендлеров в файл. В тех же целях " +"MemoryHandlerBackend переопределен как новый next_step_backend вместо " +"FileHandlerBackend." #: of telebot.TeleBot.disable_save_reply_handlers:3 msgid "" @@ -1915,9 +1971,10 @@ msgid "" "MemoryHandlerBackend is reassigned as a new reply_backend backend instead" " of FileHandlerBackend." msgstr "" -"Эта функция оставлена для обратной совместимости, для отключения возможности " -"сохранения хендлеров в файл. В тех же целях MemoryHandlerBackend переопределен " -"как новый reply_backend вместо FileHandlerBackend." +"Эта функция оставлена для обратной совместимости, для отключения " +"возможности сохранения хендлеров в файл. В тех же целях " +"MemoryHandlerBackend переопределен как новый reply_backend вместо " +"FileHandlerBackend." #: of telebot.TeleBot.download_file:1 msgid "Downloads file." @@ -1942,8 +1999,8 @@ msgid "" "the appropriate admin rights." msgstr "" "Используйте этот метод, чтобы изменить неосновную ссылку-приглашение, " -"созданную ботом. Бот должен быть администратором чата и иметь соответствующие " -"права администратора." +"созданную ботом. Бот должен быть администратором чата и иметь " +"соответствующие права администратора." #: of telebot.TeleBot.edit_chat_invite_link:4 msgid "" @@ -1953,7 +2010,6 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#editchatinvitelink" - #: of telebot.TeleBot.edit_chat_invite_link:13 msgid "The invite link to edit" msgstr "Ссылка-приглашение для изменения" @@ -1965,9 +2021,10 @@ msgid "" "must have can_manage_topics administrator rights, unless it is the " "creator of the topic. Returns True on success." msgstr "" -"Используйте этот метод, чтобы изменить название и иконку топика в супергруппе. " -"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " -"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы изменить название и иконку топика в " +"супергруппе. Бот должен быть администратором чата и иметь права " +"администратора can_manage_topics, кроме случаев, когда бот является " +"создателем топика. Возвращает True в случае успеха." #: of telebot.TeleBot.edit_forum_topic:5 msgid "Telegram Documentation: https://core.telegram.org/bots/api#editforumtopic" @@ -1978,16 +2035,43 @@ msgid "Identifier of the topic to edit" msgstr "id топика для изменения" #: of telebot.TeleBot.edit_forum_topic:13 -msgid "New name of the topic, 1-128 characters" -msgstr "Новое название топика, 1-128 символов" +msgid "" +"Optional, New name of the topic, 1-128 characters. If not specififed or " +"empty, the current name of the topic will be kept" +msgstr "" -#: of telebot.TeleBot.edit_forum_topic:16 +#: of telebot.TeleBot.edit_forum_topic:17 msgid "" -"New custom emoji for the topic icon. Must be an emoji of type “tgs” and " -"must be exactly 1 character long" +"Optional, New unique identifier of the custom emoji shown as the topic " +"icon. Use getForumTopicIconStickers to get all allowed custom emoji " +"identifiers. Pass an empty string to remove the icon. If not specified, " +"the current icon will be kept" msgstr "" -"Новый кастомный эмодзи для иконки топика. Должно быть “tgs” эмодзи, " -"длиной ровно в 1 символ" + +#: of telebot.TeleBot.edit_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to edit the name of the 'General' topic in a forum " +"supergroup chat. The bot must be an administrator in the chat for this to" +" work and must have can_manage_topics administrator rights. Returns True " +"on success." +msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.TeleBot.edit_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#editgeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" + +#: of telebot.TeleBot.edit_general_forum_topic:10 +#, fuzzy +msgid "New topic name, 1-128 characters" +msgstr "Название товара, 1-32 символа" #: of telebot.TeleBot.edit_message_caption:1 msgid "Use this method to edit captions of messages." @@ -2010,8 +2094,8 @@ msgid "" "Required if inline_message_id is not specified. Unique identifier for the" " target chat or username of the target channel" msgstr "" -"Обязательный, если не указан inline_message_id. Уникальный id чата или username" -" канала" +"Обязательный, если не указан inline_message_id. Уникальный id чата или " +"username канала" #: of telebot.TeleBot.edit_message_caption:11 msgid "Required if inline_message_id is not specified." @@ -2021,8 +2105,7 @@ msgstr "Обязательный, если не указан inline_message_id." msgid "" "Required if inline_message_id is not specified. Identifier of the inline " "message." -msgstr "" -"Обязательный, если не указан inline_message_id. id inline сообщения." +msgstr "Обязательный, если не указан inline_message_id. id inline сообщения." #: of telebot.TeleBot.edit_message_caption:17 msgid "New caption of the message, 0-1024 characters after entities parsing" @@ -2051,8 +2134,8 @@ msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." msgstr "" -"В случае успеха, если изменённое сообщение отправлено ботом, возвращается новый объект Message, " -"иначе (inline сообщения) возвращается True." +"В случае успеха, если изменённое сообщение отправлено ботом, возвращается" +" новый объект Message, иначе (inline сообщения) возвращается True." #: of telebot.TeleBot.edit_message_caption:27 msgid ":obj:`types.Message` | :obj:`bool`" @@ -2063,8 +2146,8 @@ msgid "" "Use this method to edit live location messages. A location can be edited " "until its live_period expires or editing is explicitly" msgstr "" -"Используйте этот метод, чтобы изменить live местоположение в сообщении. Местоположение может быть " -"изменено пока не истечёт live_period или не" +"Используйте этот метод, чтобы изменить live местоположение в сообщении. " +"Местоположение может быть изменено пока не истечёт live_period или не" #: of telebot.TeleBot.edit_message_live_location:2 msgid "" @@ -2072,9 +2155,9 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" -"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " -"сообщение не является inline сообщением, возвращается новый объект Message, " -"иначе возвращается True." +"запрещено вызовом метода stopMessageLiveLocation. В случае успеха, если " +"измененное сообщение не является inline сообщением, возвращается новый " +"объект Message, иначе возвращается True." #: of telebot.TeleBot.edit_message_live_location:5 msgid "" @@ -2097,8 +2180,8 @@ msgid "" "Required if inline_message_id is not specified. Identifier of the message" " to edit" msgstr "" -"Обязательный, если не указан inline_message_id. id сообщения, которое нужно " -"изменить" +"Обязательный, если не указан inline_message_id. id сообщения, которое " +"нужно изменить" #: of telebot.TeleBot.edit_message_live_location:19 #: telebot.TeleBot.stop_message_live_location:15 @@ -2113,8 +2196,7 @@ msgstr "JSON-сериализованный объект новой inline кл msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message" -msgstr "" -"Обязательный, если не указаны chat_id и message_id. id inline сообщения" +msgstr "Обязательный, если не указаны chat_id и message_id. id inline сообщения" #: of telebot.TeleBot.edit_message_live_location:29 #: telebot.TeleBot.send_location:31 @@ -2126,24 +2208,24 @@ msgid "" "Direction in which the user is moving, in degrees. Must be between 1 and " "360 if specified." msgstr "" -"Направление, в котором пользователь двигается, в градусах. Если указано, должно " -"быть от 1 до 360." +"Направление, в котором пользователь двигается, в градусах. Если указано, " +"должно быть от 1 до 360." #: of telebot.TeleBot.edit_message_live_location:35 msgid "" "The maximum distance for proximity alerts about approaching another chat " "member, in meters. Must be between 1 and 100000 if specified." msgstr "" -"Максимальное расстояние для показа уведомлений о приближении других участников " -"чата, в метрах. Если указано, должно быть от 1 до 100000." +"Максимальное расстояние для показа уведомлений о приближении других " +"участников чата, в метрах. Если указано, должно быть от 1 до 100000." #: of telebot.TeleBot.edit_message_live_location:38 msgid "" "On success, if the edited message is not an inline message, the edited " "Message is returned, otherwise True is returned." msgstr "" -"В случае успеха, если измененное сообщение не является inline сообщением, возвращается " -"новый объект Message, иначе возвращается True." +"В случае успеха, если измененное сообщение не является inline сообщением," +" возвращается новый объект Message, иначе возвращается True." #: of telebot.TeleBot.edit_message_live_location:39 #: telebot.TeleBot.stop_message_live_location:23 @@ -2158,20 +2240,17 @@ msgid "" " arbitrarily. When inline message is edited, new file can't be uploaded. " "Use previously uploaded file via its file_id or specify a URL." msgstr "" -"Используйте этот метод, чтобы изменить гифку, аудио, документ, фото или видео " -"в сообщении. Если сообщение является частью альбома, оно может быть " -"изменено только на фото или видео. Иначе, тип сообщения может быть изменен" -" на любой. При изменении inline сообщения, нельзя загрузить новый файл. " -"используйте ранее загруженные файлы через file_id или укажите URL." +"Используйте этот метод, чтобы изменить гифку, аудио, документ, фото или " +"видео в сообщении. Если сообщение является частью альбома, оно может быть" +" изменено только на фото или видео. Иначе, тип сообщения может быть " +"изменен на любой. При изменении inline сообщения, нельзя загрузить новый " +"файл. используйте ранее загруженные файлы через file_id или укажите URL." #: of telebot.TeleBot.edit_message_media:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editmessagemedia" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#editmessagemedia" - +msgstr "Документация Telegram: https://core.telegram.org/bots/api#editmessagemedia" #: of telebot.TeleBot.edit_message_media:8 msgid "A JSON-serialized object for a new media content of the message" @@ -2738,6 +2817,25 @@ msgstr "" msgid ":class:`telebot.types.WebhookInfo`" msgstr "" +#: of telebot.TeleBot.hide_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to hide the 'General' topic in a forum supergroup chat. " +"The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.TeleBot.hide_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#hidegeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" + #: of telebot.TeleBot.infinity_polling:1 msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" @@ -3306,7 +3404,6 @@ msgid "Registers my chat member handler." msgstr "" #: of telebot.TeleBot.register_next_step_handler:1 -#: telebot.TeleBot.register_next_step_handler_by_chat_id:1 msgid "" "Registers a callback function to be notified when new message arrives " "after `message`." @@ -3335,10 +3432,17 @@ msgstr "" msgid "Args to pass in callback func" msgstr "" -#: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 -msgid "The chat for which we want to handle new message." +#: of telebot.TeleBot.register_next_step_handler_by_chat_id:1 +msgid "" +"Registers a callback function to be notified when new message arrives in " +"the given chat." msgstr "" +#: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 +#, fuzzy +msgid "The chat (chat ID) for which we want to handle new message." +msgstr "Сообщение, после которого нужно обработать новое сообщение в том же чате." + #: of telebot.TeleBot.register_poll_answer_handler:1 msgid "Registers poll answer handler." msgstr "" @@ -3377,6 +3481,25 @@ msgstr "" msgid "Identifier of the topic to reopen" msgstr "" +#: of telebot.TeleBot.reopen_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to reopen the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.TeleBot.reopen_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reopengeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#createforumtopic" + #: of telebot.TeleBot.reply_to:1 msgid "" "Convenience function for `send_message(message.chat.id, text, " @@ -3639,6 +3762,11 @@ msgstr "" msgid "Identifier of a message thread, in which the video will be sent" msgstr "" +#: of telebot.TeleBot.send_animation:60 +#, fuzzy +msgid "Pass True, if the animation should be sent as a spoiler" +msgstr "Передайте True, если email пользователя нужно отправить платежной системе" + #: of telebot.TeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -3741,6 +3869,13 @@ msgid "" "upload_video_note for video notes." msgstr "" +#: of telebot.TeleBot.send_chat_action:22 +#, fuzzy +msgid "" +"The thread identifier of a message from which the reply will be " +"sent(supergroups only)" +msgstr "id топика, в который нужно отправить сообщение" + #: of telebot.TeleBot.send_contact:1 msgid "" "Use this method to send phone contacts. On success, the sent Message is " @@ -4064,6 +4199,13 @@ msgstr "" msgid "Mode for parsing entities in the photo caption." msgstr "" +#: of telebot.TeleBot.send_photo:45 +#, fuzzy +msgid "Pass True, if the photo should be sent as a spoiler" +msgstr "" +"Передайте True, если номер телефона пользователя нужно отправить " +"платежной системе" + #: of telebot.TeleBot.send_poll:1 msgid "" "Use this method to send a native poll. On success, the sent Message is " @@ -4261,6 +4403,13 @@ msgstr "" msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "" +#: of telebot.TeleBot.send_video:61 +#, fuzzy +msgid "Pass True, if the video should be sent as a spoiler" +msgstr "" +"Передайте True, если номер телефона пользователя нужно отправить " +"платежной системе" + #: of telebot.TeleBot.send_video_note:1 msgid "" "As of v.4.0, Telegram clients support rounded square MPEG4 videos of up " @@ -4788,6 +4937,25 @@ msgstr "" msgid "Unique identifier of the target sender chat." msgstr "" +#: of telebot.TeleBot.unhide_general_forum_topic:1 +#, fuzzy +msgid "" +"Use this method to unhide the 'General' topic in a forum supergroup chat." +" The bot must be an administrator in the chat for this to work and must " +"have can_manage_topics administrator rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " +"быть администратором чата и иметь права администратора can_manage_topics," +" за исключением случае, когда бот является создателем топика. Возвращает " +"True в случае успеха." + +#: of telebot.TeleBot.unhide_general_forum_topic:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unhidegeneralforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" + #: of telebot.TeleBot.unpin_all_chat_messages:1 msgid "" "Use this method to unpin a all pinned messages in a supergroup chat. The " @@ -4878,7 +5046,7 @@ msgstr "" msgid ":class:`telebot.types.User`" msgstr "" -#: ../../sync_version/index.rst:17 +#: ../../source/sync_version/index.rst:17 msgid "custom_filters file" msgstr "" @@ -5017,7 +5185,7 @@ msgstr "" msgid "Filter to check whether message starts with some text." msgstr "" -#: ../../sync_version/index.rst:25 +#: ../../source/sync_version/index.rst:25 msgid "handler_backends file" msgstr "" @@ -5077,7 +5245,7 @@ msgstr "" msgid "Class representing common states." msgstr "" -#: ../../sync_version/index.rst:34 +#: ../../source/sync_version/index.rst:34 msgid "Extensions" msgstr "" @@ -5093,3 +5261,19 @@ msgid "" "manually by user." msgstr "" +#~ msgid "New name of the topic, 1-128 characters" +#~ msgstr "Новое название топика, 1-128 символов" + +#~ msgid "" +#~ "New custom emoji for the topic " +#~ "icon. Must be an emoji of type " +#~ "“tgs” and must be exactly 1 " +#~ "character long" +#~ msgstr "" +#~ "Новый кастомный эмодзи для иконки " +#~ "топика. Должно быть “tgs” эмодзи, длиной" +#~ " ровно в 1 символ" + +#~ msgid "The chat for which we want to handle new message." +#~ msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/types.po b/docs/source/locales/ru/LC_MESSAGES/types.po index e9417f378..db003a268 100644 --- a/docs/source/locales/ru/LC_MESSAGES/types.po +++ b/docs/source/locales/ru/LC_MESSAGES/types.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-01-02 19:24+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../types.rst:3 +#: ../../source/types.rst:3 msgid "Types of API" msgstr "" @@ -29,8 +29,10 @@ msgstr "" #: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 #: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 #: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 -#: telebot.types.ForumTopicReopened:1 telebot.types.Game:1 -#: telebot.types.GameHighScore:1 telebot.types.InlineQuery:1 +#: telebot.types.ForumTopicEdited:1 telebot.types.ForumTopicReopened:1 +#: telebot.types.Game:1 telebot.types.GameHighScore:1 +#: telebot.types.GeneralForumTopicHidden:1 +#: telebot.types.GeneralForumTopicUnhidden:1 telebot.types.InlineQuery:1 #: telebot.types.Invoice:1 telebot.types.Message:1 #: telebot.types.MessageAutoDeleteTimerChanged:1 telebot.types.MessageID:1 #: telebot.types.OrderInfo:1 telebot.types.PhotoSize:1 telebot.types.Poll:1 @@ -43,6 +45,7 @@ msgstr "" #: telebot.types.VideoChatParticipantsInvited:1 #: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 #: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 +#: telebot.types.WriteAccessAllowed:1 msgid "Bases: :py:class:`telebot.types.JsonDeserializable`" msgstr "" @@ -73,9 +76,10 @@ msgstr "" #: telebot.types.ChatPhoto telebot.types.ChosenInlineResult #: telebot.types.Contact telebot.types.Dice telebot.types.Document #: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic -#: telebot.types.ForumTopicCreated telebot.types.Game -#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton -#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add +#: telebot.types.ForumTopicCreated telebot.types.ForumTopicEdited +#: telebot.types.Game telebot.types.GameHighScore +#: telebot.types.InlineKeyboardButton telebot.types.InlineKeyboardMarkup +#: telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio #: telebot.types.InlineQueryResultCachedAudio @@ -245,7 +249,7 @@ msgstr "" #: telebot.types.BotCommandScopeChatAdministrators:12 #: telebot.types.BotCommandScopeChatMember:15 #: telebot.types.BotCommandScopeDefault:8 telebot.types.CallbackQuery:30 -#: telebot.types.Chat:101 telebot.types.ChatAdministratorRights:46 +#: telebot.types.Chat:109 telebot.types.ChatAdministratorRights:46 #: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:20 #: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:59 #: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 @@ -280,21 +284,21 @@ msgstr "" #: telebot.types.InputContactMessageContent:17 #: telebot.types.InputInvoiceMessageContent:75 #: telebot.types.InputLocationMessageContent:26 -#: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:40 -#: telebot.types.InputMediaDocument:35 telebot.types.InputMediaPhoto:24 +#: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:37 +#: telebot.types.InputMediaDocument:32 telebot.types.InputMediaPhoto:24 #: telebot.types.InputMediaVideo:43 telebot.types.InputTextMessageContent:19 #: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 #: telebot.types.KeyboardButton:25 telebot.types.KeyboardButtonPollType:9 #: telebot.types.LabeledPrice:13 telebot.types.Location:25 #: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 #: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 -#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:230 +#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:246 #: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 #: telebot.types.MessageID:8 telebot.types.OrderInfo:17 #: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:15 #: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 #: telebot.types.ProximityAlertTriggered:14 -#: telebot.types.ReplyKeyboardMarkup:42 telebot.types.ReplyKeyboardRemove:18 +#: telebot.types.ReplyKeyboardMarkup:49 telebot.types.ReplyKeyboardRemove:18 #: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 #: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 #: telebot.types.Sticker:49 telebot.types.StickerSet:30 @@ -892,23 +896,35 @@ msgstr "" #: of telebot.types.Chat:80 msgid "" +"Optional. :obj:`bool`, if the chat has enabled aggressive anti-spam " +"protection. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:84 +msgid "" +"Optional. :obj:`bool`, if the chat has enabled hidden members. Returned " +"only in getChat." +msgstr "" + +#: of telebot.types.Chat:88 +msgid "" "Optional. :obj:`bool`, if messages from the chat can't be forwarded to " "other chats. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:84 +#: of telebot.types.Chat:92 msgid "" "Optional. For supergroups, name of group sticker set. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:87 +#: of telebot.types.Chat:95 msgid "" "Optional. :obj:`bool`, if the bot can change the group sticker set. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:91 +#: of telebot.types.Chat:99 msgid "" "Optional. Unique identifier for the linked chat, i.e. the discussion " "group identifier for a channel and vice versa; for supergroups and " @@ -919,13 +935,13 @@ msgid "" "in getChat." msgstr "" -#: of telebot.types.Chat:97 +#: of telebot.types.Chat:105 msgid "" "Optional. For supergroups, the location to which the supergroup is " "connected. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:102 +#: of telebot.types.Chat:110 msgid ":class:`telebot.types.Chat`" msgstr "" @@ -1750,6 +1766,26 @@ msgstr "" msgid ":class:`telebot.types.ForumTopicCreated`" msgstr "" +#: of telebot.types.ForumTopicEdited:1 +msgid "This object represents a service message about an edited forum topic." +msgstr "" + +#: of telebot.types.ForumTopicEdited:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#forumtopicedited" +msgstr "" + +#: of telebot.types.ForumTopicEdited:5 +msgid "Optional, Name of the topic(if updated)" +msgstr "" + +#: of telebot.types.ForumTopicEdited:8 +msgid "" +"Optional. New identifier of the custom emoji shown as the topic icon, if " +"it was edited; an empty string if the icon was removed" +msgstr "" + #: of telebot.types.ForumTopicReopened:1 msgid "" "This object represents a service message about a forum topic reopened in " @@ -1840,6 +1876,30 @@ msgstr "" msgid ":class:`telebot.types.GameHighScore`" msgstr "" +#: of telebot.types.GeneralForumTopicHidden:1 +msgid "" +"This object represents a service message about General forum topic hidden" +" in the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.GeneralForumTopicHidden:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#generalforumtopichidden" +msgstr "" + +#: of telebot.types.GeneralForumTopicUnhidden:1 +msgid "" +"This object represents a service message about General forum topic " +"unhidden in the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.GeneralForumTopicUnhidden:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#generalforumtopicunhidden" +msgstr "" + #: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 #: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 msgid "" @@ -2163,7 +2223,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultAudio:5 -#: telebot.types.InlineQueryResultCachedAudio:5 telebot.types.InputMediaAudio:5 +#: telebot.types.InlineQueryResultCachedAudio:5 msgid "Type of the result, must be audio" msgstr "" @@ -2202,7 +2262,7 @@ msgstr "" #: of telebot.types.InlineQueryResultAudio:20 #: telebot.types.InlineQueryResultCachedAudio:17 -#: telebot.types.InputMediaAudio:23 +#: telebot.types.InputMediaAudio:20 msgid "" "Optional. Mode for parsing entities in the audio caption. See formatting " "options for more details." @@ -2221,9 +2281,9 @@ msgstr "" #: telebot.types.InlineQueryResultMpeg4Gif:39 #: telebot.types.InlineQueryResultPhoto:36 #: telebot.types.InlineQueryResultVideo:30 -#: telebot.types.InlineQueryResultVoice:24 telebot.types.InputMediaAnimation:27 -#: telebot.types.InputMediaAudio:27 telebot.types.InputMediaDocument:27 -#: telebot.types.InputMediaPhoto:20 telebot.types.InputMediaVideo:27 +#: telebot.types.InlineQueryResultVoice:24 telebot.types.InputMediaAnimation:24 +#: telebot.types.InputMediaAudio:24 telebot.types.InputMediaDocument:24 +#: telebot.types.InputMediaPhoto:17 telebot.types.InputMediaVideo:24 msgid "" "Optional. List of special entities that appear in the caption, which can " "be specified instead of parse_mode" @@ -2396,7 +2456,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultCachedDocument:5 -#: telebot.types.InlineQueryResultDocument:5 telebot.types.InputMediaDocument:5 +#: telebot.types.InlineQueryResultDocument:5 msgid "Type of the result, must be document" msgstr "" @@ -2413,7 +2473,7 @@ msgstr "" #: of telebot.types.InlineQueryResultCachedDocument:20 #: telebot.types.InlineQueryResultDocument:14 -#: telebot.types.InputMediaDocument:20 +#: telebot.types.InputMediaDocument:17 msgid "" "Optional. Caption of the document to be sent, 0-1024 characters after " "entities parsing" @@ -2421,7 +2481,7 @@ msgstr "" #: of telebot.types.InlineQueryResultCachedDocument:23 #: telebot.types.InlineQueryResultDocument:17 -#: telebot.types.InputMediaDocument:23 +#: telebot.types.InputMediaDocument:20 msgid "" "Optional. Mode for parsing entities in the document caption. See " "formatting options for more details." @@ -2548,7 +2608,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultCachedPhoto:5 -#: telebot.types.InlineQueryResultPhoto:5 telebot.types.InputMediaPhoto:5 +#: telebot.types.InlineQueryResultPhoto:5 msgid "Type of the result, must be photo" msgstr "" @@ -2557,14 +2617,14 @@ msgid "A valid file identifier of the photo" msgstr "" #: of telebot.types.InlineQueryResultCachedPhoto:20 -#: telebot.types.InlineQueryResultPhoto:29 telebot.types.InputMediaPhoto:13 +#: telebot.types.InlineQueryResultPhoto:29 telebot.types.InputMediaPhoto:10 msgid "" "Optional. Caption of the photo to be sent, 0-1024 characters after " "entities parsing" msgstr "" #: of telebot.types.InlineQueryResultCachedPhoto:23 -#: telebot.types.InlineQueryResultPhoto:32 telebot.types.InputMediaPhoto:16 +#: telebot.types.InlineQueryResultPhoto:32 telebot.types.InputMediaPhoto:13 msgid "" "Optional. Mode for parsing entities in the photo caption. See formatting " "options for more details." @@ -2624,7 +2684,7 @@ msgid "" msgstr "" #: of telebot.types.InlineQueryResultCachedVideo:5 -#: telebot.types.InlineQueryResultVideo:5 telebot.types.InputMediaVideo:5 +#: telebot.types.InlineQueryResultVideo:5 msgid "Type of the result, must be video" msgstr "" @@ -2633,14 +2693,14 @@ msgid "A valid file identifier for the video file" msgstr "" #: of telebot.types.InlineQueryResultCachedVideo:20 -#: telebot.types.InlineQueryResultVideo:23 telebot.types.InputMediaVideo:20 +#: telebot.types.InlineQueryResultVideo:23 telebot.types.InputMediaVideo:17 msgid "" "Optional. Caption of the video to be sent, 0-1024 characters after " "entities parsing" msgstr "" #: of telebot.types.InlineQueryResultCachedVideo:23 -#: telebot.types.InlineQueryResultVideo:26 telebot.types.InputMediaVideo:23 +#: telebot.types.InlineQueryResultVideo:26 telebot.types.InputMediaVideo:20 msgid "" "Optional. Mode for parsing entities in the video caption. See formatting " "options for more details." @@ -2916,17 +2976,17 @@ msgid "A valid URL for the MPEG4 file. File size must not exceed 1MB" msgstr "" #: of telebot.types.InlineQueryResultMpeg4Gif:14 -#: telebot.types.InlineQueryResultVideo:34 telebot.types.InputMediaVideo:31 +#: telebot.types.InlineQueryResultVideo:34 telebot.types.InputMediaVideo:28 msgid "Optional. Video width" msgstr "" #: of telebot.types.InlineQueryResultMpeg4Gif:17 -#: telebot.types.InlineQueryResultVideo:37 telebot.types.InputMediaVideo:34 +#: telebot.types.InlineQueryResultVideo:37 telebot.types.InputMediaVideo:31 msgid "Optional. Video height" msgstr "" #: of telebot.types.InlineQueryResultMpeg4Gif:20 -#: telebot.types.InlineQueryResultVideo:40 telebot.types.InputMediaVideo:37 +#: telebot.types.InlineQueryResultVideo:40 telebot.types.InputMediaVideo:34 msgid "Optional. Video duration in seconds" msgstr "" @@ -3357,13 +3417,9 @@ msgid "" "https://core.telegram.org/bots/api#inputmediaanimation" msgstr "" -#: of telebot.types.InputMediaAnimation:5 -msgid "Type of the result, must be animation" -msgstr "" - -#: of telebot.types.InputMediaAnimation:8 telebot.types.InputMediaAudio:8 -#: telebot.types.InputMediaDocument:8 telebot.types.InputMediaPhoto:8 -#: telebot.types.InputMediaVideo:8 +#: of telebot.types.InputMediaAnimation:5 telebot.types.InputMediaAudio:5 +#: telebot.types.InputMediaDocument:5 telebot.types.InputMediaPhoto:5 +#: telebot.types.InputMediaVideo:5 msgid "" "File to send. Pass a file_id to send a file that exists on the Telegram " "servers (recommended), pass an HTTP URL for Telegram to get a file from " @@ -3372,8 +3428,8 @@ msgid "" " on Sending Files »" msgstr "" -#: of telebot.types.InputMediaAnimation:13 telebot.types.InputMediaAudio:13 -#: telebot.types.InputMediaDocument:13 telebot.types.InputMediaVideo:13 +#: of telebot.types.InputMediaAnimation:10 telebot.types.InputMediaAudio:10 +#: telebot.types.InputMediaDocument:10 telebot.types.InputMediaVideo:10 msgid "" "Optional. Thumbnail of the file sent; can be ignored if thumbnail " "generation for the file is supported server-side. The thumbnail should be" @@ -3385,30 +3441,34 @@ msgid "" ". More information on Sending Files »" msgstr "" -#: of telebot.types.InputMediaAnimation:20 +#: of telebot.types.InputMediaAnimation:17 msgid "" "Optional. Caption of the animation to be sent, 0-1024 characters after " "entities parsing" msgstr "" -#: of telebot.types.InputMediaAnimation:23 +#: of telebot.types.InputMediaAnimation:20 msgid "" "Optional. Mode for parsing entities in the animation caption. See " "formatting options for more details." msgstr "" -#: of telebot.types.InputMediaAnimation:31 +#: of telebot.types.InputMediaAnimation:28 msgid "Optional. Animation width" msgstr "" -#: of telebot.types.InputMediaAnimation:34 +#: of telebot.types.InputMediaAnimation:31 msgid "Optional. Animation height" msgstr "" -#: of telebot.types.InputMediaAnimation:37 +#: of telebot.types.InputMediaAnimation:34 msgid "Optional. Animation duration in seconds" msgstr "" +#: of telebot.types.InputMediaAnimation:37 +msgid "Optional. True, if the uploaded animation is a spoiler" +msgstr "" + #: of telebot.types.InputMediaAnimation:41 msgid ":class:`telebot.types.InputMediaAnimation`" msgstr "" @@ -3421,25 +3481,25 @@ msgstr "" msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio" msgstr "" -#: of telebot.types.InputMediaAudio:20 +#: of telebot.types.InputMediaAudio:17 msgid "" "Optional. Caption of the audio to be sent, 0-1024 characters after " "entities parsing" msgstr "" -#: of telebot.types.InputMediaAudio:31 +#: of telebot.types.InputMediaAudio:28 msgid "Optional. Duration of the audio in seconds" msgstr "" -#: of telebot.types.InputMediaAudio:34 +#: of telebot.types.InputMediaAudio:31 msgid "Optional. Performer of the audio" msgstr "" -#: of telebot.types.InputMediaAudio:37 +#: of telebot.types.InputMediaAudio:34 msgid "Optional. Title of the audio" msgstr "" -#: of telebot.types.InputMediaAudio:41 +#: of telebot.types.InputMediaAudio:38 msgid ":class:`telebot.types.InputMediaAudio`" msgstr "" @@ -3453,14 +3513,14 @@ msgid "" "https://core.telegram.org/bots/api#inputmediadocument" msgstr "" -#: of telebot.types.InputMediaDocument:31 +#: of telebot.types.InputMediaDocument:28 msgid "" "Optional. Disables automatic server-side content type detection for files" " uploaded using multipart/form-data. Always True, if the document is sent" " as part of an album." msgstr "" -#: of telebot.types.InputMediaDocument:36 +#: of telebot.types.InputMediaDocument:33 msgid ":class:`telebot.types.InputMediaDocument`" msgstr "" @@ -3472,6 +3532,10 @@ msgstr "" msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto" msgstr "" +#: of telebot.types.InputMediaPhoto:21 +msgid "Optional. True, if the uploaded photo is a spoiler" +msgstr "" + #: of telebot.types.InputMediaPhoto:25 msgid ":class:`telebot.types.InputMediaPhoto`" msgstr "" @@ -3484,10 +3548,14 @@ msgstr "" msgid "Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo" msgstr "" -#: of telebot.types.InputMediaVideo:40 +#: of telebot.types.InputMediaVideo:37 msgid "Optional. Pass True, if the uploaded video is suitable for streaming" msgstr "" +#: of telebot.types.InputMediaVideo:40 +msgid "Optional. True, if the uploaded video is a spoiler" +msgstr "" + #: of telebot.types.InputMediaVideo:44 msgid ":class:`telebot.types.InputMediaVideo`" msgstr "" @@ -4118,63 +4186,67 @@ msgid "" msgstr "" #: of telebot.types.Message:115 -msgid "Optional. Message is a shared contact, information about the contact" +msgid "Optional. True, if the message media is covered by a spoiler animation" msgstr "" #: of telebot.types.Message:118 -msgid "Optional. Message is a dice with random value" +msgid "Optional. Message is a shared contact, information about the contact" msgstr "" #: of telebot.types.Message:121 +msgid "Optional. Message is a dice with random value" +msgstr "" + +#: of telebot.types.Message:124 msgid "" "Optional. Message is a game, information about the game. More about games" " »" msgstr "" -#: of telebot.types.Message:124 +#: of telebot.types.Message:127 msgid "Optional. Message is a native poll, information about the poll" msgstr "" -#: of telebot.types.Message:127 +#: of telebot.types.Message:130 msgid "" "Optional. Message is a venue, information about the venue. For backward " "compatibility, when this field is set, the location field will also be " "set" msgstr "" -#: of telebot.types.Message:131 +#: of telebot.types.Message:134 msgid "Optional. Message is a shared location, information about the location" msgstr "" -#: of telebot.types.Message:134 +#: of telebot.types.Message:137 msgid "" "Optional. New members that were added to the group or supergroup and " "information about them (the bot itself may be one of these members)" msgstr "" -#: of telebot.types.Message:138 +#: of telebot.types.Message:141 msgid "" "Optional. A member was removed from the group, information about them " "(this member may be the bot itself)" msgstr "" -#: of telebot.types.Message:142 +#: of telebot.types.Message:145 msgid "Optional. A chat title was changed to this value" msgstr "" -#: of telebot.types.Message:145 +#: of telebot.types.Message:148 msgid "Optional. A chat photo was change to this value" msgstr "" -#: of telebot.types.Message:148 +#: of telebot.types.Message:151 msgid "Optional. Service message: the chat photo was deleted" msgstr "" -#: of telebot.types.Message:151 +#: of telebot.types.Message:154 msgid "Optional. Service message: the group has been created" msgstr "" -#: of telebot.types.Message:154 +#: of telebot.types.Message:157 msgid "" "Optional. Service message: the supergroup has been created. This field " "can't be received in a message coming through updates, because bot can't " @@ -4183,7 +4255,7 @@ msgid "" " created supergroup." msgstr "" -#: of telebot.types.Message:159 +#: of telebot.types.Message:162 msgid "" "Optional. Service message: the channel has been created. This field can't" " be received in a message coming through updates, because bot can't be a " @@ -4191,11 +4263,11 @@ msgid "" "reply_to_message if someone replies to a very first message in a channel." msgstr "" -#: of telebot.types.Message:164 +#: of telebot.types.Message:167 msgid "Optional. Service message: auto-delete timer settings changed in the chat" msgstr "" -#: of telebot.types.Message:168 +#: of telebot.types.Message:171 msgid "" "Optional. The group has been migrated to a supergroup with the specified " "identifier. This number may have more than 32 significant bits and some " @@ -4204,7 +4276,7 @@ msgid "" " double-precision float type are safe for storing this identifier." msgstr "" -#: of telebot.types.Message:174 +#: of telebot.types.Message:177 msgid "" "Optional. The supergroup has been migrated from a group with the " "specified identifier. This number may have more than 32 significant bits " @@ -4214,80 +4286,98 @@ msgid "" "identifier." msgstr "" -#: of telebot.types.Message:180 +#: of telebot.types.Message:183 msgid "" "Optional. Specified message was pinned. Note that the Message object in " "this field will not contain further reply_to_message fields even if it is" " itself a reply." msgstr "" -#: of telebot.types.Message:184 +#: of telebot.types.Message:187 msgid "" "Optional. Message is an invoice for a payment, information about the " "invoice. More about payments »" msgstr "" -#: of telebot.types.Message:187 +#: of telebot.types.Message:190 msgid "" "Optional. Message is a service message about a successful payment, " "information about the payment. More about payments »" msgstr "" -#: of telebot.types.Message:191 +#: of telebot.types.Message:194 msgid "" "Optional. The domain name of the website on which the user has logged in." " More about Telegram Login »" msgstr "" -#: of telebot.types.Message:195 +#: of telebot.types.Message:198 +msgid "" +"Optional. Service message: the user allowed the bot added to the " +"attachment menu to write messages" +msgstr "" + +#: of telebot.types.Message:202 msgid "Optional. Telegram Passport data" msgstr "" -#: of telebot.types.Message:198 +#: of telebot.types.Message:205 msgid "" "Optional. Service message. A user in the chat triggered another user's " "proximity alert while sharing Live Location." msgstr "" -#: of telebot.types.Message:202 +#: of telebot.types.Message:209 msgid "Optional. Service message: forum topic created" msgstr "" -#: of telebot.types.Message:205 +#: of telebot.types.Message:212 +msgid "Optional. Service message: forum topic edited" +msgstr "" + +#: of telebot.types.Message:215 msgid "Optional. Service message: forum topic closed" msgstr "" -#: of telebot.types.Message:208 +#: of telebot.types.Message:218 msgid "Optional. Service message: forum topic reopened" msgstr "" -#: of telebot.types.Message:211 +#: of telebot.types.Message:221 +msgid "Optional. Service message: the 'General' forum topic hidden" +msgstr "" + +#: of telebot.types.Message:224 +msgid "Optional. Service message: the 'General' forum topic unhidden" +msgstr "" + +#: of telebot.types.Message:227 msgid "Optional. Service message: video chat scheduled" msgstr "" -#: of telebot.types.Message:214 +#: of telebot.types.Message:230 msgid "Optional. Service message: video chat started" msgstr "" -#: of telebot.types.Message:217 +#: of telebot.types.Message:233 msgid "Optional. Service message: video chat ended" msgstr "" -#: of telebot.types.Message:220 +#: of telebot.types.Message:236 msgid "Optional. Service message: new participants invited to a video chat" msgstr "" -#: of telebot.types.Message:223 +#: of telebot.types.Message:239 msgid "Optional. Service message: data sent by a Web App" msgstr "" -#: of telebot.types.Message:226 +#: of telebot.types.Message:242 msgid "" "Optional. Inline keyboard attached to the message. login_url buttons are " "represented as ordinary url buttons." msgstr "" -#: of telebot.types.Message:231 +#: of telebot.types.Message:247 msgid ":class:`telebot.types.Message`" msgstr "" @@ -4701,7 +4791,33 @@ msgid "" "the keyboard." msgstr "" -#: of telebot.types.ReplyKeyboardMarkup:43 +#: of telebot.types.ReplyKeyboardMarkup:42 +msgid "" +"Optional. Use this parameter if you want to show the keyboard to specific" +" users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message. Example: A user " +"requests to change the bot's language, bot replies to the request with a " +"keyboard to select the new language. Other users in the group don't see " +"the keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:42 +msgid "" +"Optional. Use this parameter if you want to show the keyboard to specific" +" users only. Targets: 1) users that are @mentioned in the text of the " +"Message object; 2) if the bot's message is a reply (has " +"reply_to_message_id), sender of the original message." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:46 +msgid "" +"Example: A user requests to change the bot's language, bot replies to the" +" request with a keyboard to select the new language. Other users in the " +"group don't see the keyboard." +msgstr "" + +#: of telebot.types.ReplyKeyboardMarkup:50 #: telebot.types.ReplyKeyboardMarkup.add:14 #: telebot.types.ReplyKeyboardMarkup.row:9 msgid ":class:`telebot.types.ReplyKeyboardMarkup`" @@ -5505,3 +5621,18 @@ msgstr "" msgid ":class:`telebot.types.WebhookInfo`" msgstr "" +#: of telebot.types.WriteAccessAllowed:1 +msgid "" +"This object represents a service message about a user allowed to post " +"messages in the chat. Currently holds no information." +msgstr "" + +#: of telebot.types.WriteAccessAllowed:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#writeaccessallowed" +msgstr "" + +#~ msgid "Type of the result, must be animation" +#~ msgstr "" + From bf38071e8f3f122d69771698ac41024abf82a276 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Tue, 3 Jan 2023 17:32:31 +0200 Subject: [PATCH 1229/1808] Some translations Up to lines sync_version.po:3691 and async_version.po:3609 --- .../locales/ru/LC_MESSAGES/async_version.po | 137 +++++++---- .../locales/ru/LC_MESSAGES/sync_version.po | 214 +++++++++++++----- 2 files changed, 253 insertions(+), 98 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index ab9d3cf23..91726b155 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -2198,6 +2198,8 @@ msgid "" "Optional, New name of the topic, 1-128 characters. If not specififed or " "empty, the current name of the topic will be kept" msgstr "" +"Необязательный, новое имя топика, 1-128 символов. Если не задано или пустое, " +"сохранится текущее имя топика" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:17 msgid "" @@ -2206,6 +2208,10 @@ msgid "" "identifiers. Pass an empty string to remove the icon. If not specified, " "the current icon will be kept" msgstr "" +"Необязательный, новый уникальный id кастомного эмодзи, используемого в " +"качестве иконки топика. Используйте getForumTopicIconStickers, чтобы " +"получить все доступные id кастомных эмодзи. Передайте пустую строку, " +"чтобы убрать иконку. Если не задан, сохранится текущая иконка топика" #: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:1 #, fuzzy @@ -3220,11 +3226,11 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.message_handler:5 msgid "Example:" -msgstr "" +msgstr "Пример:" #: of telebot.async_telebot.AsyncTeleBot.message_handler:7 msgid "Usage of message_handler" -msgstr "" +msgstr "Использование message_handler" #: of telebot.async_telebot.AsyncTeleBot.message_handler:40 msgid "" @@ -3232,13 +3238,16 @@ msgid "" "first parameter. It must return True if the command should handle the " "message." msgstr "" +"Необязательная lambda функция. Получает сообщение (объект Message) в качестве " +"первого параметра. Функция должна вернуть True если хендлер должен обработать " +"сообщение." #: of telebot.async_telebot.AsyncTeleBot.message_handler:52 #: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:23 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:13 msgid "decorated function" -msgstr "" +msgstr "декорируемая функция" #: of telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:1 msgid "" @@ -3247,6 +3256,10 @@ msgid "" "parameter to the decorator function, it passes " ":class:`telebot.types.ChatMemberUpdated` object." msgstr "" +"Обрабатывает изменения статуса бота. Для приватных чатов, этот апдейт " +"отправляется только когда бот был заблокирован или разблокирован " +"пользователем. В качестве параметра передаёт в декорируемую функцию " +"объект :class:`telebot.types.ChatMemberUpdated`." #: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:1 msgid "" @@ -3254,20 +3267,25 @@ msgid "" "administrator in the chat for this to work and must have the appropriate " "admin rights. Returns True on success." msgstr "" +"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот должен " +"быть администратором чата и иметь соответствующие права администратора. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#pinchatmessage" #: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:11 msgid "Identifier of a message to pin" -msgstr "" +msgstr "id сообщения, которое нужно закрепить" #: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:14 msgid "" "Pass True, if it is not necessary to send a notification to all group " "members about the new pinned message" msgstr "" +"Передайте True, если всем участникам группы необходимо отправить уведомление " +"о закреплённом сообщении" #: of telebot.async_telebot.AsyncTeleBot.poll_answer_handler:1 msgid "" @@ -3276,6 +3294,10 @@ msgid "" "bot itself. As a parameter to the decorator function, it passes " ":class:`telebot.types.PollAnswer` object." msgstr "" +"Обрабатывает изменения ответа пользователя в не анонимном опросе(когда " +"пользователь меняет выбор). Боты получают новые ответы только в опросах, " +"которые отправили сами. В качестве параметра передаёт в декорируемую функцию " +"объект :class:`telebot.types.PollAnswer`." #: of telebot.async_telebot.AsyncTeleBot.poll_handler:1 msgid "" @@ -3283,6 +3305,9 @@ msgid "" "polls and polls, which are sent by the bot As a parameter to the " "decorator function, it passes :class:`telebot.types.Poll` object." msgstr "" +"Обрабатывает изменения в состоянии опроса. Боты получают только апдейты " +"о завершенных опросах и опросах, которые отправили сами. В качестве " +"параметра передаёт в декорируемую функцию объект :class:`telebot.types.Poll`." #: of telebot.async_telebot.AsyncTeleBot.polling:1 msgid "" @@ -3290,36 +3315,43 @@ msgid "" "retrieve Updates automagically and notify listeners and message handlers " "accordingly." msgstr "" +"Запускает бота в режиме поллинга в основном цикле событий. Это позволяет " +"боту получать апдейты (Update) автоматически и вызывать соответствующие " +"листенеры и хендлеры." #: of telebot.async_telebot.AsyncTeleBot.polling:4 msgid "Warning: Do not call this function more than once!" -msgstr "" +msgstr "Предупреждение: Не вызывайте эту функцию более одного раза!" #: of telebot.async_telebot.AsyncTeleBot.polling:6 msgid "Always gets updates." -msgstr "" +msgstr "Всегда получает апдейты." #: of telebot.async_telebot.AsyncTeleBot.polling:10 msgid "" "Set non_stop=True if you want your bot to continue receiving updates if " "there is an error." msgstr "" +"Укажите non_stop=True, если хотите чтобы ваш бот продолжать получать апдейты " +"при возникновении ошибок." #: of telebot.async_telebot.AsyncTeleBot.polling:17 msgid "Do not stop polling when an ApiException occurs." -msgstr "" +msgstr "Не останавливать поллинг при возникновении ApiException." #: of telebot.async_telebot.AsyncTeleBot.polling:23 msgid "Delay between two update retrivals" -msgstr "" +msgstr "Задержка между получением апдейтов" #: of telebot.async_telebot.AsyncTeleBot.polling:42 msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." msgstr "" +"Устарело, используйте non_stop. " +"Старая опечатка, оставлено для обратной совместимости" #: of telebot.async_telebot.AsyncTeleBot.polling:45 msgid "Restart a file on file(s) change. Defaults to False." -msgstr "" +msgstr "Перезапускать при изменениях в файлах. По умолчанию False." #: of telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:1 msgid "" @@ -3327,16 +3359,21 @@ msgid "" "checkout. As a parameter to the decorator function, it passes " ":class:`telebot.types.PreCheckoutQuery` object." msgstr "" +"Новая pre-checkout query. Содержит полную информацию о заказе. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.PreCheckoutQuery`." #: of telebot.async_telebot.AsyncTeleBot.process_new_updates:1 msgid "" "Process new updates. Just pass list of updates - each update should be " "instance of Update object." msgstr "" +"Обрабатывает новые апдейты. Просто передайте список апдейтов(Update и " +"его наследники)." #: of telebot.async_telebot.AsyncTeleBot.process_new_updates:5 msgid "list of updates" -msgstr "" +msgstr "список апдейтов" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:1 msgid "" @@ -3345,50 +3382,60 @@ msgid "" "have the appropriate admin rights. Pass False for all boolean parameters " "to demote a user." msgstr "" +"Используйте этот метод, чтобы повысить или понизить пользователя в супергруппе " +"или канале. Бот должен быть администратором чатаи и иметь соответствующие права " +"администратора. Передайте False во все boolean параметры, чтобы понизить пользователя." #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#promotechatmember" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#promotechatmember" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:7 msgid "" "Unique identifier for the target chat or username of the target channel (" " in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:14 msgid "" "Pass True, if the administrator can change chat title, photo and other " "settings" msgstr "" +"Передайте True, если администратор может менять название чата, аватарку " +"и другие настройки" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:17 msgid "Pass True, if the administrator can create channel posts, channels only" -msgstr "" +msgstr "Передайте True, если администратор может создавать посты в канале, только для каналов" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:20 msgid "" "Pass True, if the administrator can edit messages of other users, " "channels only" msgstr "" +"Передайте True, если администратор может изменять сообщения других пользователей, " +"только для каналов" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:23 msgid "Pass True, if the administrator can delete messages of other users" -msgstr "" +msgstr "Передайте True, если администратор может удалять сообщения других пользователей" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:26 msgid "Pass True, if the administrator can invite new users to the chat" -msgstr "" +msgstr "Передайте True, если администратор может приглашать новых пользователей в чат" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:29 msgid "Pass True, if the administrator can restrict, ban or unban chat members" -msgstr "" +msgstr "Передайте True, если администратор может ограничивать, банить или разбанивать участников чата" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:32 msgid "Pass True, if the administrator can pin messages, supergroups only" -msgstr "" +msgstr "Передайте True, если администратор может закреплять сообщения, только для супергрупп" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:35 msgid "" @@ -3397,10 +3444,13 @@ msgid "" "directly or indirectly (promoted by administrators that were appointed by" " him)" msgstr "" +"Передайте True, если администратор может добавлять новых администраторов с " +"подмножеством его собственных прав администратора или понижать администраторов, " +"которых он повысил, напрямую или косвенно (администраторами, которых он назначил)" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:40 msgid "Pass True, if the administrator's presence in the chat is hidden" -msgstr "" +msgstr "Передайте True, если присутствие администратора в чате скрыто" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:43 msgid "" @@ -3409,26 +3459,35 @@ msgid "" "anonymous administrators in supergroups and ignore slow mode. Implied by " "any other administrator privilege" msgstr "" +"Передайте True, если администратор имеет доступ к логу событий чата, статистике " +"чата, статистике сообщений в каналах, видеть участников канала, видеть анонимных " +"администраторов в супергруппах и игнорировать медленный режим. Подразумевается " +"любым другим правом администратора" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:49 msgid "" "Pass True, if the administrator can manage voice chats For now, bots can " "use this privilege only for passing to other administrators." msgstr "" +"Передайте True, если администратор может управлять голосовыми чатами. На текущий " +"момент, боты могут использовать это право администратора только для передачи другим " +"администраторам." #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:53 msgid "Deprecated, use can_manage_video_chats." -msgstr "" +msgstr "Устарело, используйте can_manage_video_chats." #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:56 msgid "" "Pass True if the user is allowed to create, rename, close, and reopen " "forum topics, supergroups only" msgstr "" +"Передайте True, если пользователю разрешено создавать, переименовывать, закрывать, " +"и возобновлять топики, только для супергрупп" #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." -msgstr "" +msgstr "Регистрирует хендлер callback query." #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:3 @@ -3445,7 +3504,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:3 msgid "function to be called" -msgstr "" +msgstr "функция-хендлер" #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:18 @@ -3465,31 +3524,33 @@ msgid "" "True if you need to pass TeleBot instance to handler(useful for " "separating handlers into different files)" msgstr "" +"True, если вам нужно передать экземпляр класса TeleBot в хендлер(удобно для " +"разбиения кода на файлы)" #: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:1 msgid "Registers channel post message handler." -msgstr "" +msgstr "Регистрирует хендлер постов в каналах." #: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:9 msgid "list of commands" -msgstr "" +msgstr "список команд" #: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:12 msgid "Regular expression" -msgstr "" +msgstr "Регулярное выражение" #: of telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:1 msgid "Registers chat join request handler." -msgstr "" +msgstr "Регистрирует хендлер запросов на вступление в чат." #: of telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:1 msgid "Registers chat member handler." -msgstr "" +msgstr "Регистрирует хендлер смены состояний участников чата." #: of telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:14 msgid ":return:None" @@ -3497,55 +3558,55 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:1 msgid "Registers chosen inline handler." -msgstr "" +msgstr "Регистрирует хендлер выбора результата inline query." #: of telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:1 msgid "Registers edited channel post message handler." -msgstr "" +msgstr "Регистрирует хендлер изменения постов в каналах." #: of telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:1 msgid "Registers edited message handler." -msgstr "" +msgstr "Регистрирует хендлер изменения сообщений." #: of telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:18 msgid "True for private chat" -msgstr "" +msgstr "True для приватных чатов" #: of telebot.async_telebot.AsyncTeleBot.register_inline_handler:1 msgid "Registers inline handler." -msgstr "" +msgstr "Регистрирует хендлер inline query." #: of telebot.async_telebot.AsyncTeleBot.register_message_handler:1 msgid "Registers message handler." -msgstr "" +msgstr "Регистрирует хендлер сообщений." #: of telebot.async_telebot.AsyncTeleBot.register_message_handler:18 msgid "List of chat types" -msgstr "" +msgstr "Список видов чатов" #: of telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:1 msgid "Registers my chat member handler." -msgstr "" +msgstr "Регистрирует хендлер изменений статуса бота." #: of telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:1 msgid "Registers poll answer handler." -msgstr "" +msgstr "Регистрирует хендлер ответов в опросах." #: of telebot.async_telebot.AsyncTeleBot.register_poll_handler:1 msgid "Registers poll handler." -msgstr "" +msgstr "Регистрирует хендлер изменений состояния опросов." #: of telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:1 msgid "Registers pre-checkout request handler." -msgstr "" +msgstr "Регистрирует хендлер pre-checkout query." #: of telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:1 msgid "Registers shipping query handler." -msgstr "" +msgstr "Регистрирует хендлер shipping query." #: of telebot.async_telebot.AsyncTeleBot.remove_webhook:1 msgid "Alternative for delete_webhook but uses set_webhook" -msgstr "" +msgstr "Альтернатива delete_webhook, но использует set_webhook" #: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:1 msgid "" diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 16f933068..4844a8da0 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -671,7 +671,7 @@ msgid "" "query may be cached client-side. Telegram apps will support caching " "starting in version 3.14. Defaults to 0." msgstr "" -"Максимальная длительность хранения ответа на callback запрос " +"Максимальная длительность хранения ответа на callback query " "пользовательским клиентом в секундах. Приложения Telegram поддерживают " "хранение ответов начиная с версии 3.14, по умолчанию 0." @@ -680,7 +680,7 @@ msgid "" "Use this method to send answers to an inline query. On success, True is " "returned. No more than 50 results per query are allowed." msgstr "" -"Используйте этот метод для отправки ответов на inline запрос. В случае " +"Используйте этот метод для отправки ответов на inline query. В случае " "успеха возвращается True. Разрешено отправить не более 50 результатов на " "один запрос." @@ -698,14 +698,14 @@ msgstr "Уникальный id запроса для ответа" #: of telebot.TeleBot.answer_inline_query:9 msgid "Array of results for the inline query" -msgstr "Массив результатов для ответа на inline запрос" +msgstr "Массив результатов для ответа на inline query" #: of telebot.TeleBot.answer_inline_query:12 msgid "" "The maximum amount of time in seconds that the result of the inline query" " may be cached on the server." msgstr "" -"Максимальная длительность хранения результатов inline запроса на сервере " +"Максимальная длительность хранения результатов inline query на сервере " "в секундах." #: of telebot.TeleBot.answer_inline_query:16 @@ -777,7 +777,7 @@ msgid "" "checkout query was sent." msgstr "" "Bot API должно получить ответ в течение 10 секунд после отправки pre-" -"checkout запроса." +"checkout query." #: of telebot.TeleBot.answer_pre_checkout_query:8 msgid "" @@ -1055,7 +1055,7 @@ msgid "" "Handles new incoming callback query. As a parameter to the decorator " "function, it passes :class:`telebot.types.CallbackQuery` object." msgstr "" -"Обрабатывает новый callback запрос. В качестве параметра передаёт в " +"Обрабатывает новый callback query. В качестве параметра передаёт в " "декорируемую функцию объект :class:`telebot.types.CallbackQuery`." #: of telebot.TeleBot.callback_query_handler:4 @@ -1179,7 +1179,7 @@ msgid "" "parameter to the decorator function, it passes " ":class:`telebot.types.ChosenInlineResult` object." msgstr "" -"Обрабатывает результат inline запроса, который был выбран пользователем и" +"Обрабатывает результат inline query, который был выбран пользователем и" " отправлен собеседнику в чате. Пожалуйста ознакомьтесь с документацией по" " сбору фидбека для получения таких апдейтов вашим ботом. В качестве " "параметра передаёт в декорируемую функцию объект " @@ -2039,6 +2039,8 @@ msgid "" "Optional, New name of the topic, 1-128 characters. If not specififed or " "empty, the current name of the topic will be kept" msgstr "" +"Необязательный, новое имя топика, 1-128 символов. Если не задано или пустое, " +"сохранится текущее имя топика" #: of telebot.TeleBot.edit_forum_topic:17 msgid "" @@ -2047,6 +2049,10 @@ msgid "" "identifiers. Pass an empty string to remove the icon. If not specified, " "the current icon will be kept" msgstr "" +"Необязательный, новый уникальный id кастомного эмодзи, используемого в " +"качестве иконки топика. Используйте getForumTopicIconStickers, чтобы " +"получить все доступные id кастомных эмодзи. Передайте пустую строку, " +"чтобы убрать иконку. Если не задан, сохранится текущая иконка топика" #: of telebot.TeleBot.edit_general_forum_topic:1 #, fuzzy @@ -3107,11 +3113,11 @@ msgstr "" #: of telebot.TeleBot.message_handler:5 telebot.TeleBot.middleware_handler:7 #: telebot.TeleBot.register_middleware_handler:6 msgid "Example:" -msgstr "" +msgstr "Пример:" #: of telebot.TeleBot.message_handler:7 msgid "Usage of message_handler" -msgstr "" +msgstr "Использование message_handler" #: of telebot.TeleBot.message_handler:40 msgid "" @@ -3119,17 +3125,20 @@ msgid "" "first parameter. It must return True if the command should handle the " "message." msgstr "" +"Необязательная lambda функция. Получает сообщение (объект Message) в качестве " +"первого параметра. Функция должна вернуть True если хендлер должен обработать " +"сообщение." #: of telebot.TeleBot.message_handler:52 #: telebot.TeleBot.register_edited_channel_post_handler:23 #: telebot.TeleBot.register_inline_handler:14 #: telebot.TeleBot.register_pre_checkout_query_handler:13 msgid "decorated function" -msgstr "" +msgstr "декорируемая функция" #: of telebot.TeleBot.middleware_handler:1 msgid "Function-based middleware handler decorator." -msgstr "" +msgstr "Функция-декоратор для middleware хендлера." #: of telebot.TeleBot.middleware_handler:3 msgid "" @@ -3138,10 +3147,14 @@ msgid "" "and check type of the update inside the handler if more than one " "update_type is given" msgstr "" +"Этот декоратор может быть использован, чтобы декорировать функции, которые " +"будут использоваться в качестве middleware перед обработкой апдейтов, " +"будьте аккуратны и проверяйте вид апдейта внутри функции если возможны апдейты " +"разных видов" #: of telebot.TeleBot.middleware_handler:9 msgid "Usage of middleware_handler" -msgstr "" +msgstr "Использование middleware_handler" #: of telebot.TeleBot.middleware_handler:24 #: telebot.TeleBot.register_middleware_handler:15 @@ -3149,10 +3162,12 @@ msgid "" "Optional list of update types that can be passed into the middleware " "handler." msgstr "" +"Необязательный список видов апдейтов, которые будут обработаны этим " +"middleware хендлером." #: of telebot.TeleBot.middleware_handler:27 msgid "function" -msgstr "" +msgstr "функция" #: of telebot.TeleBot.my_chat_member_handler:1 msgid "" @@ -3161,6 +3176,10 @@ msgid "" "parameter to the decorator function, it passes " ":class:`telebot.types.ChatMemberUpdated` object." msgstr "" +"Обрабатывает изменения статуса бота. Для приватных чатов, этот апдейт " +"отправляется только когда бот был заблокирован или разблокирован " +"пользователем. В качестве параметра передаёт в декорируемую функцию " +"объект :class:`telebot.types.ChatMemberUpdated`." #: of telebot.TeleBot.pin_chat_message:1 msgid "" @@ -3168,20 +3187,25 @@ msgid "" "administrator in the chat for this to work and must have the appropriate " "admin rights. Returns True on success." msgstr "" +"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот должен " +"быть администратором чата и иметь соответствующие права администратора. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.pin_chat_message:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#pinchatmessage" #: of telebot.TeleBot.pin_chat_message:11 msgid "Identifier of a message to pin" -msgstr "" +msgstr "id сообщения, которое нужно закрепить" #: of telebot.TeleBot.pin_chat_message:14 msgid "" "Pass True, if it is not necessary to send a notification to all group " "members about the new pinned message" msgstr "" +"Передайте True, если всем участникам группы необходимо отправить уведомление " +"о закреплённом сообщении" #: of telebot.TeleBot.poll_answer_handler:1 msgid "" @@ -3190,6 +3214,10 @@ msgid "" "bot itself. As a parameter to the decorator function, it passes " ":class:`telebot.types.PollAnswer` object." msgstr "" +"Обрабатывает изменения ответа пользователя в не анонимном опросе(когда " +"пользователь меняет выбор). Боты получают новые ответы только в опросах, " +"которые отправили сами. В качестве параметра передаёт в декорируемую функцию " +"объект :class:`telebot.types.PollAnswer`." #: of telebot.TeleBot.poll_handler:1 msgid "" @@ -3197,6 +3225,9 @@ msgid "" "polls and polls, which are sent by the bot As a parameter to the " "decorator function, it passes :class:`telebot.types.Poll` object." msgstr "" +"Обрабатывает изменения в состоянии опроса. Боты получают только апдейты " +"о завершенных опросах и опросах, которые отправили сами. В качестве " +"параметра передаёт в декорируемую функцию объект :class:`telebot.types.Poll`." #: of telebot.TeleBot.polling:1 msgid "" @@ -3204,26 +3235,29 @@ msgid "" "__retrieve_updates function. This allows the bot to retrieve Updates " "automatically and notify listeners and message handlers accordingly." msgstr "" +"Эта функция создаёт новый Thread, который вызывает служебную фукнцию " +"__retrieve_updates. Это позволяет боту получать апдейты (Update) " +"автоматически и вызывать соответствующие листенеры и хендлеры." #: of telebot.TeleBot.polling:4 msgid "Warning: Do not call this function more than once!" -msgstr "" +msgstr "Предупреждение: Не вызывайте эту функцию более одного раза!" #: of telebot.TeleBot.polling:6 msgid "Always gets updates." -msgstr "" +msgstr "Всегда получает апдейты." #: of telebot.TeleBot.polling:8 msgid "Use :meth:`infinity_polling` instead." -msgstr "" +msgstr "Используйте :meth:`infinity_polling`." #: of telebot.TeleBot.polling:15 msgid "Delay between two update retrivals" -msgstr "" +msgstr "Задержка между получением апдейтов" #: of telebot.TeleBot.polling:18 msgid "Do not stop polling when an ApiException occurs." -msgstr "" +msgstr "Не останавливать поллинг при возникновении ApiException." #: of telebot.TeleBot.polling:34 msgid "" @@ -3236,6 +3270,14 @@ msgid "" "updates created before the call to the get_updates, so unwanted updates " "may be received for a short period of time." msgstr "" +"Список видов апдейтов, которые вы хотите получать. Например, укажите " +"[“message”, “edited_channel_post”, “callback_query”], чтобы получать " +"апдейты только этих видов. Полный список доступных видов апдейтов - " +"util.update_types. Укажите пустой список, чтобы получать все апдейты, " +"кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка. Пожалуйста учитывайте, что этот параметр не влияет " +"на апдейты, отправленные до вызова get_updates, поэтому нежелательные апдейты " +"могут быть получены в течение короткого периода времени." #: of telebot.TeleBot.polling:34 msgid "" @@ -3246,6 +3288,12 @@ msgid "" "types except chat_member (default). If not specified, the previous " "setting will be used." msgstr "" +"Список видов апдейтов, которые вы хотите получать. Например, укажите " +"[“message”, “edited_channel_post”, “callback_query”], чтобы получать " +"апдейты только этих видов. Полный список доступных видов апдейтов - " +"util.update_types. Укажите пустой список, чтобы получать все апдейты, " +"кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка." #: of telebot.TeleBot.polling:40 msgid "" @@ -3253,14 +3301,19 @@ msgid "" " call to the get_updates, so unwanted updates may be received for a short" " period of time." msgstr "" +"Пожалуйста учитывайте, что этот параметр не влияет " +"на апдейты, отправленные до вызова get_updates, поэтому нежелательные апдейты " +"могут быть получены в течение короткого периода времени." #: of telebot.TeleBot.polling:44 msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." msgstr "" +"Устарело, используйте non_stop. " +"Старая опечатка, оставлено для обратной совместимости" #: of telebot.TeleBot.polling:50 msgid "Path to watch for changes. Defaults to None" -msgstr "" +msgstr "Путь для мониторинга изменений. По умолчанию None" #: of telebot.TeleBot.pre_checkout_query_handler:1 msgid "" @@ -3268,20 +3321,25 @@ msgid "" "checkout. As a parameter to the decorator function, it passes " ":class:`telebot.types.PreCheckoutQuery` object." msgstr "" +"Новая pre-checkout query. Содержит полную информацию о заказе. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.PreCheckoutQuery`." #: of telebot.TeleBot.process_new_updates:1 msgid "" "Processes new updates. Just pass list of subclasses of Update to this " "method." msgstr "" +"Обрабатывает новые апдейты. Просто передайте список апдейтов(Update и " +"его наследники)." #: of telebot.TeleBot.process_new_updates:3 msgid "List of :class:`telebot.types.Update` objects." -msgstr "" +msgstr "Список объектов :class:`telebot.types.Update`." #: of telebot.TeleBot.process_new_updates msgid "return None" -msgstr "" +msgstr "возвращает None" #: of telebot.TeleBot.promote_chat_member:1 msgid "" @@ -3290,50 +3348,60 @@ msgid "" "have the appropriate admin rights. Pass False for all boolean parameters " "to demote a user." msgstr "" +"Используйте этот метод, чтобы повысить или понизить пользователя в супергруппе " +"или канале. Бот должен быть администратором чатаи и иметь соответствующие права " +"администратора. Передайте False во все boolean параметры, чтобы понизить пользователя." #: of telebot.TeleBot.promote_chat_member:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#promotechatmember" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#promotechatmember" #: of telebot.TeleBot.promote_chat_member:7 msgid "" "Unique identifier for the target chat or username of the target channel (" " in the format @channelusername)" msgstr "" +"Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.TeleBot.promote_chat_member:14 msgid "" "Pass True, if the administrator can change chat title, photo and other " "settings" msgstr "" +"Передайте True, если администратор может менять название чата, аватарку " +"и другие настройки" #: of telebot.TeleBot.promote_chat_member:17 msgid "Pass True, if the administrator can create channel posts, channels only" -msgstr "" +msgstr "Передайте True, если администратор может создавать посты в канале, только для каналов" #: of telebot.TeleBot.promote_chat_member:20 msgid "" "Pass True, if the administrator can edit messages of other users, " "channels only" msgstr "" +"Передайте True, если администратор может изменять сообщения других пользователей, " +"только для каналов" #: of telebot.TeleBot.promote_chat_member:23 msgid "Pass True, if the administrator can delete messages of other users" -msgstr "" +msgstr "Передайте True, если администратор может удалять сообщения других пользователей" #: of telebot.TeleBot.promote_chat_member:26 msgid "Pass True, if the administrator can invite new users to the chat" -msgstr "" +msgstr "Передайте True, если администратор может приглашать новых пользователей в чат" #: of telebot.TeleBot.promote_chat_member:29 msgid "Pass True, if the administrator can restrict, ban or unban chat members" -msgstr "" +msgstr "Передайте True, если администратор может ограничивать, банить или разбанивать участников чата" #: of telebot.TeleBot.promote_chat_member:32 msgid "Pass True, if the administrator can pin messages, supergroups only" -msgstr "" +msgstr "Передайте True, если администратор может закреплять сообщения, только для супергрупп" #: of telebot.TeleBot.promote_chat_member:35 msgid "" @@ -3342,10 +3410,13 @@ msgid "" "directly or indirectly (promoted by administrators that were appointed by" " him)" msgstr "" +"Передайте True, если администратор может добавлять новых администраторов с " +"подмножеством его собственных прав администратора или понижать администраторов, " +"которых он повысил, напрямую или косвенно (администраторами, которых он назначил)" #: of telebot.TeleBot.promote_chat_member:40 msgid "Pass True, if the administrator's presence in the chat is hidden" -msgstr "" +msgstr "Передайте True, если присутствие администратора в чате скрыто" #: of telebot.TeleBot.promote_chat_member:43 msgid "" @@ -3354,26 +3425,35 @@ msgid "" "anonymous administrators in supergroups and ignore slow mode. Implied by " "any other administrator privilege" msgstr "" +"Передайте True, если администратор имеет доступ к логу событий чата, статистике " +"чата, статистике сообщений в каналах, видеть участников канала, видеть анонимных " +"администраторов в супергруппах и игнорировать медленный режим. Подразумевается " +"любым другим правом администратора" #: of telebot.TeleBot.promote_chat_member:49 msgid "" "Pass True, if the administrator can manage voice chats For now, bots can " "use this privilege only for passing to other administrators." msgstr "" +"Передайте True, если администратор может управлять голосовыми чатами. На текущий " +"момент, боты могут использовать это право администратора только для передачи другим " +"администраторам." #: of telebot.TeleBot.promote_chat_member:53 msgid "Deprecated, use can_manage_video_chats." -msgstr "" +msgstr "Устарело, используйте can_manage_video_chats." #: of telebot.TeleBot.promote_chat_member:56 msgid "" "Pass True if the user is allowed to create, rename, close, and reopen " "forum topics, supergroups only" msgstr "" +"Передайте True, если пользователю разрешено создавать, переименовывать, закрывать, " +"и возобновлять топики, только для супергрупп" #: of telebot.TeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." -msgstr "" +msgstr "Регистрирует хендлер callback query." #: of telebot.TeleBot.register_callback_query_handler:3 #: telebot.TeleBot.register_channel_post_handler:3 @@ -3390,7 +3470,7 @@ msgstr "" #: telebot.TeleBot.register_pre_checkout_query_handler:3 #: telebot.TeleBot.register_shipping_query_handler:3 msgid "function to be called" -msgstr "" +msgstr "функция-хендлер" #: of telebot.TeleBot.register_callback_query_handler:9 #: telebot.TeleBot.register_channel_post_handler:18 @@ -3410,31 +3490,33 @@ msgid "" "True if you need to pass TeleBot instance to handler(useful for " "separating handlers into different files)" msgstr "" +"True, если вам нужно передать экземпляр класса TeleBot в хендлер(удобно для " +"разбиения кода на файлы)" #: of telebot.TeleBot.register_channel_post_handler:1 msgid "Registers channel post message handler." -msgstr "" +msgstr "Регистрирует хендлер постов в каналах." #: of telebot.TeleBot.register_channel_post_handler:9 #: telebot.TeleBot.register_edited_channel_post_handler:9 #: telebot.TeleBot.register_edited_message_handler:9 #: telebot.TeleBot.register_message_handler:9 msgid "list of commands" -msgstr "" +msgstr "список команд" #: of telebot.TeleBot.register_channel_post_handler:12 #: telebot.TeleBot.register_edited_channel_post_handler:12 #: telebot.TeleBot.register_edited_message_handler:12 msgid "Regular expression" -msgstr "" +msgstr "Регулярное выражение" #: of telebot.TeleBot.register_chat_join_request_handler:1 msgid "Registers chat join request handler." -msgstr "" +msgstr "Регистрирует хендлер запросов на вступление в чат." #: of telebot.TeleBot.register_chat_member_handler:1 msgid "Registers chat member handler." -msgstr "" +msgstr "Регистрирует хендлер смены состояний участников чата." #: of telebot.TeleBot.register_chat_member_handler:14 msgid ":return:None" @@ -3442,19 +3524,19 @@ msgstr "" #: of telebot.TeleBot.register_chosen_inline_handler:1 msgid "Registers chosen inline handler." -msgstr "" +msgstr "Регистрирует хендлер выбора результата inline query." #: of telebot.TeleBot.register_edited_channel_post_handler:1 msgid "Registers edited channel post message handler." -msgstr "" +msgstr "Регистрирует хендлер изменения постов в каналах." #: of telebot.TeleBot.register_edited_message_handler:1 msgid "Registers edited message handler." -msgstr "" +msgstr "Регистрирует хендлер изменения сообщений." #: of telebot.TeleBot.register_edited_message_handler:18 msgid "True for private chat" -msgstr "" +msgstr "True для приватных чатов" #: of telebot.TeleBot.register_for_reply:1 #: telebot.TeleBot.register_for_reply_by_message_id:1 @@ -3462,6 +3544,7 @@ msgid "" "Registers a callback function to be notified when a reply to `message` " "arrives." msgstr "" +"Регистрирует функцию для вызова при получении ответа на выбранное сообщение." #: of telebot.TeleBot.register_for_reply:3 #: telebot.TeleBot.register_for_reply_by_message_id:3 @@ -3469,10 +3552,12 @@ msgid "" "Warning: In case `callback` as lambda function, saving reply handlers " "will not work." msgstr "" +"Предупреждение: При использовании lambda функции в качестве `callback`, " +"сохранение reply хендлеров не будет работать." #: of telebot.TeleBot.register_for_reply:5 msgid "The message for which we are awaiting a reply." -msgstr "" +msgstr "Сообщение, ответ на которое нужно ждать." #: of telebot.TeleBot.register_for_reply:8 #: telebot.TeleBot.register_for_reply_by_message_id:8 @@ -3480,36 +3565,38 @@ msgid "" "The callback function to be called when a reply arrives. Must accept one " "`message` parameter, which will contain the replied message." msgstr "" +"Функция, которую нужно вызвать при получении ответа на сообщение. Должна принимать " +"параметр `message`, который будет содержать ответ на сообщение." #: of telebot.TeleBot.register_for_reply:12 #: telebot.TeleBot.register_for_reply_by_message_id:12 msgid "Optional arguments for the callback function." -msgstr "" +msgstr "Необязательные аргументы для вызываемой функции." #: of telebot.TeleBot.register_for_reply:13 #: telebot.TeleBot.register_for_reply_by_message_id:13 msgid "Optional keyword arguments for the callback function." -msgstr "" +msgstr "Необязательные именованные аргументы для вызываемой функции." #: of telebot.TeleBot.register_for_reply_by_message_id:5 msgid "The id of the message for which we are awaiting a reply." -msgstr "" +msgstr "id сообщения, ответ на которое нужно ждать." #: of telebot.TeleBot.register_inline_handler:1 msgid "Registers inline handler." -msgstr "" +msgstr "Регистрирует хендлер inline query." #: of telebot.TeleBot.register_message_handler:1 msgid "Registers message handler." -msgstr "" +msgstr "Регистрирует хендлер сообщений." #: of telebot.TeleBot.register_message_handler:18 msgid "List of chat types" -msgstr "" +msgstr "Список видов чатов" #: of telebot.TeleBot.register_middleware_handler:1 msgid "Adds function-based middleware handler." -msgstr "" +msgstr "Добавляет функцию-middleware." #: of telebot.TeleBot.register_middleware_handler:3 msgid "" @@ -3517,6 +3604,9 @@ msgid "" "middlewares are executed before handlers. But, be careful and check type " "of the update inside the handler if more than one update_type is given" msgstr "" +"Эта функция зарегистрирует вашу функцию-middleware. Middleware функции " +"исполняются до хендлеров. Будьте осторожны и проверяйте вид апдейта " +"внутри функции, если указано более одного update_type" #: of telebot.TeleBot.register_middleware_handler:8 msgid "bot = TeleBot('TOKEN')" @@ -3530,17 +3620,18 @@ msgstr "" #: of telebot.TeleBot.register_middleware_handler:12 msgid "Function that will be used as a middleware handler." -msgstr "" +msgstr "Функция, которая будет использована в качестве middleware." #: of telebot.TeleBot.register_my_chat_member_handler:1 msgid "Registers my chat member handler." -msgstr "" +msgstr "Регистрирует хендлер изменений статуса бота." #: of telebot.TeleBot.register_next_step_handler:1 msgid "" "Registers a callback function to be notified when new message arrives " "after `message`." msgstr "" +"Регистрирует функцию для вызова при получении нового сообщения после указанного." #: of telebot.TeleBot.register_next_step_handler:3 #: telebot.TeleBot.register_next_step_handler_by_chat_id:3 @@ -3548,53 +3639,56 @@ msgid "" "Warning: In case `callback` as lambda function, saving next step handlers" " will not work." msgstr "" +"Предупреждение: При использовании lambda функции в качестве `callback`, " +"сохранение next step хендлеров не будет работать." #: of telebot.TeleBot.register_next_step_handler:5 msgid "The message for which we want to handle new message in the same chat." -msgstr "" +msgstr "Сообщение, после которого нужно обработать следующее в том же чате." #: of telebot.TeleBot.register_next_step_handler:8 #: telebot.TeleBot.register_next_step_handler_by_chat_id:8 msgid "The callback function which next new message arrives." -msgstr "" +msgstr "Функция для вызова при получении нового сообщения." #: of telebot.TeleBot.register_next_step_handler:11 #: telebot.TeleBot.register_next_step_handler:13 #: telebot.TeleBot.register_next_step_handler_by_chat_id:11 #: telebot.TeleBot.register_next_step_handler_by_chat_id:13 msgid "Args to pass in callback func" -msgstr "" +msgstr "Аргументы для передачи в функцию" #: of telebot.TeleBot.register_next_step_handler_by_chat_id:1 msgid "" "Registers a callback function to be notified when new message arrives in " "the given chat." msgstr "" +"Регистрирует функцию для вызова при получении нового сообщения в заданном чате." #: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 #, fuzzy msgid "The chat (chat ID) for which we want to handle new message." -msgstr "Сообщение, после которого нужно обработать новое сообщение в том же чате." +msgstr "Чат (id чата), в котором нужно обработать новое сообщение." #: of telebot.TeleBot.register_poll_answer_handler:1 msgid "Registers poll answer handler." -msgstr "" +msgstr "Регистрирует хендлер ответов в опросах." #: of telebot.TeleBot.register_poll_handler:1 msgid "Registers poll handler." -msgstr "" +msgstr "Регистрирует хендлер изменений состояния опросов." #: of telebot.TeleBot.register_pre_checkout_query_handler:1 msgid "Registers pre-checkout request handler." -msgstr "" +msgstr "Регистрирует хендлер pre-checkout query." #: of telebot.TeleBot.register_shipping_query_handler:1 msgid "Registers shipping query handler." -msgstr "" +msgstr "Регистрирует хендлер shipping query." #: of telebot.TeleBot.remove_webhook:1 msgid "Deletes webhooks using set_webhook() function." -msgstr "" +msgstr "Удаляет вебхук, используя set_webhook()." #: of telebot.TeleBot.reopen_forum_topic:1 msgid "" From 490168f3f6d6bd62baf685da49bcbef56fbc4092 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Tue, 3 Jan 2023 19:43:36 +0200 Subject: [PATCH 1230/1808] Some translations Up to lines sync_version.po:4527 and async_version.po:4448 --- .../locales/ru/LC_MESSAGES/async_version.po | 295 +++++++++++++---- .../locales/ru/LC_MESSAGES/sync_version.po | 297 ++++++++++++++---- 2 files changed, 455 insertions(+), 137 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index 91726b155..7c3fd592f 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -3615,16 +3615,21 @@ msgid "" "the can_manage_topics administrator rights, unless it is the creator of " "the topic. Returns True on success." msgstr "" +"Используйте этот метод, чтобы возобновить закрытый топик в супергруппе с топиками. " +"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " +"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopenforumtopic" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#reopenforumtopic" #: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:10 msgid "Identifier of the topic to reopen" -msgstr "" +msgstr "id топика для возобновления" #: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:1 #, fuzzy @@ -3633,41 +3638,46 @@ msgid "" " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics," -" за исключением случае, когда бот является создателем топика. Возвращает " -"True в случае успеха." +"Используйте этот метод, чтобы возобновить топик 'General' в супергруппе с топиками. " +"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " +"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:5 #, fuzzy msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopengeneralforumtopic" -msgstr "Документация Telegram: https://core.telegram.org/bots/api#createforumtopic" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#reopengeneralforumtopic" #: of telebot.async_telebot.AsyncTeleBot.reply_to:1 msgid "" "Convenience function for `send_message(message.chat.id, text, " "reply_to_message_id=message.message_id, **kwargs)`" msgstr "" +"Удобная функция для замены `send_message(message.chat.id, text, " +"reply_to_message_id=message.message_id, **kwargs)`" #: of telebot.async_telebot.AsyncTeleBot.reply_to:3 msgid "Instance of :class:`telebot.types.Message`" -msgstr "" +msgstr "Экземпляр класса :class:`telebot.types.Message`" #: of telebot.async_telebot.AsyncTeleBot.reply_to:6 msgid "Text of the message." -msgstr "" +msgstr "Текст сообщения." #: of telebot.async_telebot.AsyncTeleBot.reply_to:9 msgid "" "Additional keyword arguments which are passed to " ":meth:`telebot.TeleBot.send_message`" msgstr "" +"Дополнительные именованные аргументы, передаваемые в " +":meth:`telebot.TeleBot.send_message`" #: of telebot.async_telebot.AsyncTeleBot.reset_data:1 msgid "Reset data for a user in chat." -msgstr "" +msgstr "Сбросить данные о пользователе в чате." #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:1 msgid "" @@ -3676,12 +3686,18 @@ msgid "" "appropriate admin rights. Pass True for all boolean parameters to lift " "restrictions from a user." msgstr "" +"Используйте этот метод, чтобы ограничить пользователя в супергруппе. Бот " +"должн быть администратором супергруппы и иметь соответствующие права " +"администратора. Передайте True во все boolean параметры, чтобы снять с " +"пользователя ограничения." #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#restrictchatmember" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#restrictchatmember" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 msgid "" @@ -3689,70 +3705,90 @@ msgid "" " restricted for more than 366 days or less than 30 seconds from the " "current time, they are considered to be restricted forever" msgstr "" +"Дата, когда ограничения будут сняты с пользователя, UNIX timestamp. Если " +"пользователь ограничен более чем на 366 дней или менее чем на 30 секунд с " +"текущего момента, он будет ограничен навсегда (пока ограничения не будут " +"сняты вручную)" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:19 msgid "" "Pass True, if the user can send text messages, contacts, locations and " "venues" msgstr "" +"Передайте True, если пользователь может отправлять текстовые сообщения, " +"контакты, местоположения и места" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:22 msgid "" "Pass True, if the user can send audios, documents, photos, videos, video " "notes and voice notes, implies can_send_messages" msgstr "" +"Передайте True, если пользователь может отправлять аудио, документы, фото, " +"видео, видео заметки (кружочки) и аудио заметки (голосовые), подразумевает " +"can_send_messages" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:26 msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" msgstr "" +"Передайте True, если пользователю разрешено отправлять опросы, подразумевает " +"can_send_messages" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:29 msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" +"Передайте True, если пользователь может отправлять гиф-ки, игры, стикеры и " +"использовать inline ботов, подразумевает can_send_media_messages" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 msgid "" "Pass True, if the user may add web page previews to their messages, " "implies can_send_media_messages" msgstr "" +"Передайте True, если пользователь может добавлять превью ссылок к своим сообщениям, " +"подразумевает can_send_media_messages" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:36 msgid "" "Pass True, if the user is allowed to change the chat title, photo and " "other settings. Ignored in public supergroups" msgstr "" +"Передайте True, если пользователю разрешено изменять название чата, аватарку и " +"другие настройки. Игнорируется в публичных супергруппах" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:40 msgid "" "Pass True, if the user is allowed to invite new users to the chat, " "implies can_invite_users" msgstr "" +"Передайте True, если пользователю разрешено приглашать пользователей в чат" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:44 msgid "" "Pass True, if the user is allowed to pin messages. Ignored in public " "supergroups" msgstr "" +"Передайте True, если пользователю разрешено закреплять сообщения. Игнорируется " +"в публичных супергруппах" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:47 #: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:14 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:19 msgid "True on success" -msgstr "" +msgstr "True в случае успеха" #: of telebot.async_telebot.AsyncTeleBot.retrieve_data:1 msgid "Returns context manager with data for a user in chat." -msgstr "" +msgstr "Возвращает контекстный менеджер с данными о пользователе в чате." #: of telebot.async_telebot.AsyncTeleBot.retrieve_data:6 msgid "Chat's unique identifier, defaults to user_id" -msgstr "" +msgstr "Уникальный id чата, по умолчанию user_id" #: of telebot.async_telebot.AsyncTeleBot.retrieve_data:9 msgid "Context manager with data for a user in chat" -msgstr "" +msgstr "Контекстный менеджер с данными о пользователе в чате." #: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:1 msgid "" @@ -3761,44 +3797,50 @@ msgid "" "must be an administrator in the chat for this to work and must have the " "appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы аннулировать ссылку-приглашение, созданную ботом. " +"Примечание: Если аннулируется главная ссылка-приглашение, автоматически " +"генерируется новая. Бот должен быть администратором чата и иметь соответствующие " +"права администратора." #: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#revokechatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#revokechatinvitelink" #: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:11 msgid "The invite link to revoke" -msgstr "" +msgstr "Ссылка-приглашение, которую нужно аннулировать" #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:1 msgid "This class sets webhooks and listens to a given url and port." -msgstr "" +msgstr "Этот класс устанавливает вебхуки и мониторит указанный URL и порт." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:3 msgid "IP address to listen to. Defaults to 0.0.0.0" -msgstr "" +msgstr "IP адрес для мониторинга. По умолчанию 0.0.0.0" #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:5 msgid "A port which will be used to listen to webhooks." -msgstr "" +msgstr "Порт, который будет использован для мониторинга вебхуков." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:6 msgid "Path to the webhook. Defaults to /token" -msgstr "" +msgstr "Путь к вебхуку. По умолчанию /token." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:7 msgid "Path to the certificate file." -msgstr "" +msgstr "Путь к файлу с SSL сертификатом." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:8 msgid "Path to the certificate key file." -msgstr "" +msgstr "Путь к файлу с приватным ключом SSL сертификата." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:9 msgid "Webhook URL." -msgstr "" +msgstr "URL вебхука." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:10 msgid "" @@ -3807,6 +3849,10 @@ msgid "" " load on your bot's server, and higher values to increase your bot's " "throughput." msgstr "" +"Максимально-допустимое количество одновременных HTTPS подключений к вебхуку " +"для доставки апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения, " +" чтобы уменьшить нагрузку на ваш сервер и большие значения для увеличения " +"пропускной способности вашего бота." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:11 msgid "" @@ -3817,24 +3863,32 @@ msgid "" "regardless of type (default). If not specified, the previous setting will" " be used." msgstr "" +"Список видов апдейтов, которые вы хотите получать, в формате JSON. " +"Например, укажите [“message”, “edited_channel_post”, “callback_query”], " +"чтобы получать апдейты только этих видов. Полный список доступных видов " +"апдейтов - util.update_types. Укажите пустой список, чтобы получать все " +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:12 msgid "" "The fixed IP address which will be used to send webhook requests instead " "of the IP address resolved through DNS" msgstr "" +"Фиксированный IP адрес, который будет использоваться для отправки запросов " +"к вебхуку вместо IP адреса, полученного через DNS" #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:13 msgid "Pass True to drop all pending updates" -msgstr "" +msgstr "Передайте True, чтобы проигнорировать все апдейты, полученные до запуска" #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:14 msgid "Integer. Request connection timeout" -msgstr "" +msgstr "Integer. Тайм-аут запроса на подключение." #: of telebot.async_telebot.AsyncTeleBot.run_webhooks:15 msgid "Secret token to be used to verify the webhook request." -msgstr "" +msgstr "Секретный токен для верификации запроса к вебхуку." #: of telebot.async_telebot.AsyncTeleBot.send_animation:1 msgid "" @@ -3843,10 +3897,14 @@ msgid "" "currently send animation files of up to 50 MB in size, this limit may be " "changed in the future." msgstr "" +"Используйте этот метод, чтобы отправить гиф-ку (GIF или H.264/MPEG-4 AVC видео " +"без звука). В случае успеха, возвращается отправленное сообщение (Message). На " +"текущий момент, боты могут отправлять гиф-ки весом до 50 MB, это ограничение " +"может измениться в будущем." #: of telebot.async_telebot.AsyncTeleBot.send_animation:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendanimation" #: of telebot.async_telebot.AsyncTeleBot.send_animation:9 msgid "" @@ -3855,18 +3913,22 @@ msgid "" "String for Telegram to get an animation from the Internet, or upload a " "new animation using multipart/form-data." msgstr "" +"Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гиф-ку, " +"которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить гиф-ку из интернета или загрузите новую гиф-ку " +"с помощью multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_animation:13 msgid "Duration of sent animation in seconds" -msgstr "" +msgstr "Длительность отправленной гиф-ки в секундах" #: of telebot.async_telebot.AsyncTeleBot.send_animation:16 msgid "Animation width" -msgstr "" +msgstr "Ширина гиф-ки" #: of telebot.async_telebot.AsyncTeleBot.send_animation:19 msgid "Animation height" -msgstr "" +msgstr "Высота гиф-ки" #: of telebot.async_telebot.AsyncTeleBot.send_animation:22 #: telebot.async_telebot.AsyncTeleBot.send_video:20 @@ -3880,16 +3942,25 @@ msgid "" "so you can pass “attach://” if the thumbnail was " "uploaded using multipart/form-data under ." msgstr "" +"Обложка отправленного файла; может быть проигнорирована, если генерация " +"обложки поддерживается на стороне сервера. Обложка должна быть картинкой " +"в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " +"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" +"data. Обложки не могут быть использованы повторно и могут быть загружены " +"только как новый файл, так что вы можете передать “attach://” " +"если обложка была загружена с помощью multipart/form-data под именем ." #: of telebot.async_telebot.AsyncTeleBot.send_animation:28 msgid "" "Animation caption (may also be used when resending animation by file_id)," " 0-1024 characters after entities parsing" msgstr "" +"Подпись к гиф-ке (может быть использована при повторной отправке гиф-ки по file_id), " +"0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_animation:31 msgid "Mode for parsing entities in the animation caption" -msgstr "" +msgstr "Режим форматирования подписи к гиф-ке" #: of telebot.async_telebot.AsyncTeleBot.send_animation:51 #: telebot.async_telebot.AsyncTeleBot.send_video:29 @@ -3897,16 +3968,18 @@ msgid "" "List of special entities that appear in the caption, which can be " "specified instead of parse_mode" msgstr "" +"Список отформатированных частей подписи, можно использовать вместо " +"parse_mode" #: of telebot.async_telebot.AsyncTeleBot.send_animation:57 #: telebot.async_telebot.AsyncTeleBot.send_video:58 msgid "Identifier of a message thread, in which the video will be sent" -msgstr "" +msgstr "id топика, в который будет отправлено видео" #: of telebot.async_telebot.AsyncTeleBot.send_animation:60 #, fuzzy msgid "Pass True, if the animation should be sent as a spoiler" -msgstr "Передайте True, если email пользователя нужно отправить платежной системе" +msgstr "Передайте True, если гиф-ку нужно отправить как спойлер" #: of telebot.async_telebot.AsyncTeleBot.send_audio:1 msgid "" @@ -3916,14 +3989,19 @@ msgid "" " audio files of up to 50 MB in size, this limit may be changed in the " "future." msgstr "" +"Используйте этот метод, чтобы отправить аудио, если вы хотите, чтобы клиенты " +"(приложения) Telegram проигрывали их в музыкальном проигрывателе. Ваше аудио " +"должно быть в формате .MP3 или .M4A. В случае успеха, возвращается отправленное " +"сообщение (Message). На текущий момент, боты могут отправлять аудио весом до 50 MB, " +"это ограничение может измениться в будущем." #: of telebot.async_telebot.AsyncTeleBot.send_audio:5 msgid "For sending voice messages, use the send_voice method instead." -msgstr "" +msgstr "Для отправки голосовых сообщений, используйте метод send_voice" #: of telebot.async_telebot.AsyncTeleBot.send_audio:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendaudio" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendaudio" #: of telebot.async_telebot.AsyncTeleBot.send_audio:12 msgid "" @@ -3933,28 +4011,34 @@ msgid "" "new one using multipart/form-data. Audio must be in the .MP3 or .M4A " "format." msgstr "" +"Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " +"multipart/form-data. Аудио должно быть в формате .MP3 или .M4A." #: of telebot.async_telebot.AsyncTeleBot.send_audio:17 msgid "Audio caption, 0-1024 characters after entities parsing" -msgstr "" +msgstr "Подпись к аудио, 0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_audio:20 msgid "Duration of the audio in seconds" -msgstr "" +msgstr "Длительность аудио в секундах" #: of telebot.async_telebot.AsyncTeleBot.send_audio:23 msgid "Performer" -msgstr "" +msgstr "Исполнитель" #: of telebot.async_telebot.AsyncTeleBot.send_audio:26 msgid "Track name" -msgstr "" +msgstr "Название трека" #: of telebot.async_telebot.AsyncTeleBot.send_audio:36 msgid "" "Mode for parsing entities in the audio caption. See formatting options " "for more details." msgstr "" +"Режим форматирования подписи к аудио. См. formatting options " +"для получения подробностей." #: of telebot.async_telebot.AsyncTeleBot.send_audio:45 msgid "" @@ -3966,6 +4050,13 @@ msgid "" "so you can pass “attach://” if the thumbnail was " "uploaded using multipart/form-data under " msgstr "" +"Обложка отправленного файла; может быть проигнорирована, если генерация " +"обложки поддерживается на стороне сервера. Обложка должна быть картинкой " +"в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " +"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" +"data. Обложки не могут быть использованы повторно и могут быть загружены " +"только как новый файл, так что вы можете передать “attach://” " +"если обложка была загружена с помощью multipart/form-data под именем ." #: of telebot.async_telebot.AsyncTeleBot.send_audio:51 #: telebot.async_telebot.AsyncTeleBot.send_document:35 @@ -3975,6 +4066,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the caption, " "which can be specified instead of parse_mode" msgstr "" +"Список отформатированных частей подписи в формате JSON, можно использовать вместо " +"parse_mode" #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:1 msgid "" @@ -3983,6 +4076,10 @@ msgid "" "(when a message arrives from your bot, Telegram clients clear its typing " "status). Returns True on success." msgstr "" +"Используйте этот метод, когда вам нужно показать пользователю, что бот " +"что-то делает. Статус устанавливается на 5 секеунд или менее (когда от " +"бота приходит сообщение, клиенты (приложения) Telegram убирают статус typing). " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:5 msgid "" @@ -3991,10 +4088,14 @@ msgid "" "image, please wait…”, the bot may use sendChatAction with action = " "upload_photo. The user will see a “sending photo” status for the bot." msgstr "" +"Пример: ImageBot-у требуется время, чтобы обработать запрос и загрузить " +"изображение. Вместо отправки текстового сообщения “Отправка изображения, " +"пожалуйста подождите…”, бот может использовать sendChatAction с параметром " +"action = upload_photo. Пользователь увидит статус бота “sending photo”." #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendchataction" #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:10 #: telebot.async_telebot.AsyncTeleBot.send_contact:5 @@ -4002,7 +4103,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_venue:5 #: telebot.async_telebot.AsyncTeleBot.stop_poll:5 msgid "Unique identifier for the target chat or username of the target channel" -msgstr "" +msgstr "Уникальный id чата или username канала" #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:13 msgid "" @@ -4013,37 +4114,45 @@ msgid "" "stickers, find_location for location data, record_video_note or " "upload_video_note for video notes." msgstr "" +"Тип действия. Выберите один, в зависимости от того, что получит " +"пользователь: typing для текстовых сообщений, upload_photo для фото, " +"record_video или upload_video для видео, record_voice или upload_voice для " +"голосовых сообщений, upload_document для файлов, choose_sticker для " +"стикеров, find_location для данных о местоположении, record_video_note или " +"upload_video_note для видео заметок (кружочков)." #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:22 #, fuzzy msgid "The thread to which the message will be sent(supergroups only)" -msgstr "id топика, в который нужно отправить сообщение" +msgstr "id топика, в который сообщение будет отправлено(только для супергрупп)" #: of telebot.async_telebot.AsyncTeleBot.send_contact:1 msgid "" "Use this method to send phone contacts. On success, the sent Message is " "returned." msgstr "" +"Используйте этот метод, чтобы отправить контакт. В случае успеха, возвращается " +"отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_contact:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendcontact" #: of telebot.async_telebot.AsyncTeleBot.send_contact:8 msgid "Contact's phone number" -msgstr "" +msgstr "Телефонный номер контакта" #: of telebot.async_telebot.AsyncTeleBot.send_contact:11 msgid "Contact's first name" -msgstr "" +msgstr "Имя контакта" #: of telebot.async_telebot.AsyncTeleBot.send_contact:14 msgid "Contact's last name" -msgstr "" +msgstr "Фамилия контакта" #: of telebot.async_telebot.AsyncTeleBot.send_contact:17 msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" -msgstr "" +msgstr "Дополнительные данные о контакте в формате vCard, 0-2048 байт" #: of telebot.async_telebot.AsyncTeleBot.send_contact:34 #: telebot.async_telebot.AsyncTeleBot.send_game:23 @@ -4052,21 +4161,25 @@ msgid "" "Pass True, if the message should be sent even if one of the specified " "replied-to messages is not found." msgstr "" +"Передайте True, если сообщение должно быть отправлено даже если одно из сообщений, " +"на которые нужно ответить, не найдено." #: of telebot.async_telebot.AsyncTeleBot.send_contact:41 #: telebot.async_telebot.AsyncTeleBot.send_venue:54 msgid "The thread to which the message will be sent" -msgstr "" +msgstr "Топик, в который сообщение будет отправлено" #: of telebot.async_telebot.AsyncTeleBot.send_dice:1 msgid "" "Use this method to send an animated emoji that will display a random " "value. On success, the sent Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить анимированный эмодзи, который покажет " +"случайное значение. В случае успеха, возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_dice:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#senddice" #: of telebot.async_telebot.AsyncTeleBot.send_dice:8 msgid "" @@ -4075,24 +4188,27 @@ msgid "" " and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " "to “🎲”" msgstr "" +"Эмодзи, на котором основана анимация. На текущий момент, должно быть одним из " +"“🎲”, “🎯”, “🏀”, “⚽”, “🎳”, или “🎰”. Значение может быть 1-6 для “🎲”, “🎯”" +" и “🎳”, 1-5 для “🏀” и “⚽”, и 1-64 для “🎰”. По умолчанию “🎲”" #: of telebot.async_telebot.AsyncTeleBot.send_dice:29 msgid "Protects the contents of the sent message from forwarding" -msgstr "" +msgstr "Защищает содержимое отправленного сообщения от пересылки" #: of telebot.async_telebot.AsyncTeleBot.send_dice:32 msgid "" "The identifier of a message thread, unique within the chat to which the " "message with the thread identifier belongs" -msgstr "" +msgstr "id топика, в который сообщение будет отправлено" #: of telebot.async_telebot.AsyncTeleBot.send_document:1 msgid "Use this method to send general files." -msgstr "" +msgstr "Используйте этот метод, чтобы отправить файл." #: of telebot.async_telebot.AsyncTeleBot.send_document:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#senddocument" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#senddocument" #: of telebot.async_telebot.AsyncTeleBot.send_document:8 msgid "" @@ -4101,16 +4217,22 @@ msgid "" "String for Telegram to get a file from the Internet, or upload a new one " "using multipart/form-data" msgstr "" +"(документ) Файл для отправки. Передайте file_id (String), чтобы отправить файл, " +"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить файл из интернета или загрузите новый с помощью " +"multipart/form-data" #: of telebot.async_telebot.AsyncTeleBot.send_document:15 msgid "" "Document caption (may also be used when resending documents by file_id), " "0-1024 characters after entities parsing" msgstr "" +"Подпись к файлу (может быть использована при повторной отправке файла по file_id), " +"0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_document:23 msgid "Mode for parsing entities in the document caption" -msgstr "" +msgstr "Режим форматирования частей подписи к файлу" #: of telebot.async_telebot.AsyncTeleBot.send_document:32 msgid "" @@ -4123,52 +4245,66 @@ msgid "" "“attach://” if the thumbnail was uploaded using " "multipart/form-data under " msgstr "" +"InputFile или String : Обложка отправленного файла; может быть проигнорирована, " +"если генерация обложки поддерживается на стороне сервера. Обложка должна " +"быть картинкой в формате JPEG и весить менее 200 kB. Ширина и высота обложки " +"не должны превышать 320. Игнорируется, если файл не загружен с помощью " +"multipart/form-data. Обложки не могут быть использованы повторно и могут быть " +"загружены только как новый файл, так что вы можете передать " +"“attach://” если обложка была загружена с помощью " +"multipart/form-data под именем ." #: of telebot.async_telebot.AsyncTeleBot.send_document:41 msgid "" "allows to define file name that will be visible in the Telegram instead " "of original file name" msgstr "" +"позволяет задать имя файла, которое будет показано в Telegram вместо настоящего" #: of telebot.async_telebot.AsyncTeleBot.send_document:44 msgid "" "Disables automatic server-side content type detection for files uploaded " "using multipart/form-data" msgstr "" +"Отключает автоматическое обнаружение типа файла на стороне сервера для файлов, " +"загруженных с помощью multipart/form-data" #: of telebot.async_telebot.AsyncTeleBot.send_document:47 #: telebot.async_telebot.AsyncTeleBot.send_sticker:33 #: telebot.async_telebot.AsyncTeleBot.send_video:55 msgid "function typo miss compatibility: do not use it" -msgstr "" +msgstr "опечатка: не используйте" #: of telebot.async_telebot.AsyncTeleBot.send_game:1 msgid "Used to send the game." -msgstr "" +msgstr "Используется для отправки игры." #: of telebot.async_telebot.AsyncTeleBot.send_game:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendgame" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendgame" #: of telebot.async_telebot.AsyncTeleBot.send_game:8 msgid "" "Short name of the game, serves as the unique identifier for the game. Set" " up your games via @BotFather." msgstr "" +"Короткое имя игры, служит в качестве уникального id игры. Настройте свои игры" +"через @BotFather." #: of telebot.async_telebot.AsyncTeleBot.send_game:20 msgid "Timeout in seconds for waiting for a response from the bot." -msgstr "" +msgstr "Тайм-аут в секундах, ожидание ответа от бота." #: of telebot.async_telebot.AsyncTeleBot.send_game:26 msgid "" "Pass True, if content of the message needs to be protected from being " "viewed by the bot." msgstr "" +"Передайте True, если содержимое сообщение должно быть защищено от просмотра ботом." #: of telebot.async_telebot.AsyncTeleBot.send_game:29 msgid "Identifier of the thread to which the message will be sent." -msgstr "" +msgstr "id топика, в которые будет сообщение будет отправлено." #: of telebot.async_telebot.AsyncTeleBot.send_game:33 #: telebot.async_telebot.AsyncTeleBot.send_invoice:102 @@ -4178,21 +4314,23 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.send_invoice:1 msgid "Sends invoice." -msgstr "" +msgstr "Отправляет инвойс." #: of telebot.async_telebot.AsyncTeleBot.send_invoice:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendinvoice" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendinvoice" #: of telebot.async_telebot.AsyncTeleBot.send_invoice:5 msgid "Unique identifier for the target private chat" -msgstr "" +msgstr "Уникальный id приватного чата" #: of telebot.async_telebot.AsyncTeleBot.send_invoice:29 msgid "" "Unique deep-linking parameter that can be used to generate this invoice " "when used as a start parameter" msgstr "" +"Уникальный deep-linking параметр, который может быть использован для " +"генерации этого инвойса при использовании в качестве параметра /start" #: of telebot.async_telebot.AsyncTeleBot.send_invoice:33 msgid "" @@ -4200,6 +4338,8 @@ msgid "" "a marketing image for a service. People like it better when they see what" " they are paying for." msgstr "" +"URL фото продукта. Может быть фото товаров или рекламным изображением сервиса. " +"Людям больше нравится, когда они видят, за что платят." #: of telebot.async_telebot.AsyncTeleBot.send_invoice:73 msgid "" @@ -4207,47 +4347,56 @@ msgid "" " price' button will be shown. If not empty, the first button must be a " "Pay button" msgstr "" +"JSON-сериализованный объект inline клавиатуры. Если пустой, будет показана " +"одна кнопка 'Pay total price'. Если не пустой, первая кнопка должна быть " +"кнопкой для оплаты" #: of telebot.async_telebot.AsyncTeleBot.send_invoice:81 #: telebot.async_telebot.AsyncTeleBot.set_webhook:39 msgid "Timeout of a request, defaults to None" -msgstr "" +msgstr "Тайм-аут запроса, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.send_invoice:98 msgid "" "The identifier of a message thread, in which the invoice message will be " "sent" -msgstr "" +msgstr "id топика, в который будет отправлен инвойс" #: of telebot.async_telebot.AsyncTeleBot.send_location:1 msgid "" "Use this method to send point on the map. On success, the sent Message is" " returned." msgstr "" +"Используйте этот метод, чтобы отправить точку на карте. В случае успеха, " +"возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_location:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendlocation" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendlocation" #: of telebot.async_telebot.AsyncTeleBot.send_location:8 msgid "Latitude of the location" -msgstr "" +msgstr "Широта" #: of telebot.async_telebot.AsyncTeleBot.send_location:11 msgid "Longitude of the location" -msgstr "" +msgstr "Долгота" #: of telebot.async_telebot.AsyncTeleBot.send_location:14 msgid "" "Period in seconds for which the location will be updated (see Live " "Locations, should be between 60 and 86400." msgstr "" +"Время в секундах, в течение которого местоположение будет обновляться " +"(см. Live Locations), должно быть между 60 и 86400." #: of telebot.async_telebot.AsyncTeleBot.send_location:34 msgid "" "For live locations, a direction in which the user is moving, in degrees. " "Must be between 1 and 360 if specified." msgstr "" +"Для live местоположений, направление, в котором пользователь двигается, в градусах. " +"Должно быть между 1 и 360, если указано." #: of telebot.async_telebot.AsyncTeleBot.send_location:37 msgid "" @@ -4255,6 +4404,9 @@ msgid "" "approaching another chat member, in meters. Must be between 1 and 100000 " "if specified." msgstr "" +"Для live местоположений, максимальное расстояние для уведомлений о " +"приближении другого участника чата, в метрах. Должно быть между 1 и 100000, " +"если указано." #: of telebot.async_telebot.AsyncTeleBot.send_media_group:1 msgid "" @@ -4263,30 +4415,37 @@ msgid "" " messages of the same type. On success, an array of Messages that were " "sent is returned." msgstr "" +"Используйте этот метод, чтобы отправить группу фото, видео, файлов или аудио " +"как альбом. Файлы и аудио могут быть сгруппированы в альбом только с сообщениями " +"того же типа. В случае успеха, возвращается массив отправленных сообщений (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_media_group:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendmediagroup" #: of telebot.async_telebot.AsyncTeleBot.send_media_group:9 msgid "" "A JSON-serialized array describing messages to be sent, must include 2-10" " items" msgstr "" +"JSON-сериализованный массив, описывающий сообщения для отправки, должен включать " +"от 2 до 10 элементов" #: of telebot.async_telebot.AsyncTeleBot.send_media_group:12 msgid "" "Sends the messages silently. Users will receive a notification with no " "sound." msgstr "" +"Отправить сообщение, при получении которого пользователя пользователи получат " +"уведомление без звука." #: of telebot.async_telebot.AsyncTeleBot.send_media_group:27 msgid "Identifier of a message thread, in which the messages will be sent" -msgstr "" +msgstr "id топика, в который будет отправлена группа медиа" #: of telebot.async_telebot.AsyncTeleBot.send_media_group:30 msgid "On success, an array of Messages that were sent is returned." -msgstr "" +msgstr "В случае успеха, возвращается массив отправленных сообщений (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_message:1 msgid "Use this method to send text messages." diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 4844a8da0..e5f7265d0 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -3697,16 +3697,21 @@ msgid "" "the can_manage_topics administrator rights, unless it is the creator of " "the topic. Returns True on success." msgstr "" +"Используйте этот метод, чтобы возобновить закрытый топик в супергруппе с топиками. " +"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " +"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.TeleBot.reopen_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopenforumtopic" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#reopenforumtopic" #: of telebot.TeleBot.reopen_forum_topic:10 msgid "Identifier of the topic to reopen" -msgstr "" +msgstr "id топика для возобновления" #: of telebot.TeleBot.reopen_general_forum_topic:1 #, fuzzy @@ -3715,41 +3720,46 @@ msgid "" " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics," -" за исключением случае, когда бот является создателем топика. Возвращает " -"True в случае успеха." +"Используйте этот метод, чтобы возобновить топик 'General' в супергруппе с топиками. " +"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " +"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.TeleBot.reopen_general_forum_topic:5 #, fuzzy msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopengeneralforumtopic" -msgstr "Документация Telegram: https://core.telegram.org/bots/api#createforumtopic" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#reopengeneralforumtopic" #: of telebot.TeleBot.reply_to:1 msgid "" "Convenience function for `send_message(message.chat.id, text, " "reply_to_message_id=message.message_id, **kwargs)`" msgstr "" +"Удобная функция для замены `send_message(message.chat.id, text, " +"reply_to_message_id=message.message_id, **kwargs)`" #: of telebot.TeleBot.reply_to:3 msgid "Instance of :class:`telebot.types.Message`" -msgstr "" +msgstr "Экземпляр класса :class:`telebot.types.Message`" #: of telebot.TeleBot.reply_to:6 msgid "Text of the message." -msgstr "" +msgstr "Текст сообщения." #: of telebot.TeleBot.reply_to:9 msgid "" "Additional keyword arguments which are passed to " ":meth:`telebot.TeleBot.send_message`" msgstr "" +"Дополнительные именованные аргументы, передаваемые в " +":meth:`telebot.TeleBot.send_message`" #: of telebot.TeleBot.reset_data:1 msgid "Reset data for a user in chat." -msgstr "" +msgstr "Сбросить данные о пользователе в чате." #: of telebot.TeleBot.restrict_chat_member:1 msgid "" @@ -3758,12 +3768,18 @@ msgid "" "appropriate admin rights. Pass True for all boolean parameters to lift " "restrictions from a user." msgstr "" +"Используйте этот метод, чтобы ограничить пользователя в супергруппе. Бот " +"должн быть администратором супергруппы и иметь соответствующие права " +"администратора. Передайте True во все boolean параметры, чтобы снять с " +"пользователя ограничения." #: of telebot.TeleBot.restrict_chat_member:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#restrictchatmember" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#restrictchatmember" #: of telebot.TeleBot.restrict_chat_member:14 msgid "" @@ -3771,69 +3787,89 @@ msgid "" " restricted for more than 366 days or less than 30 seconds from the " "current time, they are considered to be restricted forever" msgstr "" +"Дата, когда ограничения будут сняты с пользователя, UNIX timestamp. Если " +"пользователь ограничен более чем на 366 дней или менее чем на 30 секунд с " +"текущего момента, он будет ограничен навсегда (пока ограничения не будут " +"сняты вручную)" #: of telebot.TeleBot.restrict_chat_member:19 msgid "" "Pass True, if the user can send text messages, contacts, locations and " "venues" msgstr "" +"Передайте True, если пользователь может отправлять текстовые сообщения, " +"контакты, местоположения и места" #: of telebot.TeleBot.restrict_chat_member:22 msgid "" "Pass True, if the user can send audios, documents, photos, videos, video " "notes and voice notes, implies can_send_messages" msgstr "" +"Передайте True, если пользователь может отправлять аудио, документы, фото, " +"видео, видео заметки (кружочки) и голосовые сообщения, подразумевает " +"can_send_messages" #: of telebot.TeleBot.restrict_chat_member:26 msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" msgstr "" +"Передайте True, если пользователю разрешено отправлять опросы, подразумевает " +"can_send_messages" #: of telebot.TeleBot.restrict_chat_member:29 msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" +"Передайте True, если пользователь может отправлять гиф-ки, игры, стикеры и " +"использовать inline ботов, подразумевает can_send_media_messages" #: of telebot.TeleBot.restrict_chat_member:32 msgid "" "Pass True, if the user may add web page previews to their messages, " "implies can_send_media_messages" msgstr "" +"Передайте True, если пользователь может добавлять превью ссылок к своим сообщениям, " +"подразумевает can_send_media_messages" #: of telebot.TeleBot.restrict_chat_member:36 msgid "" "Pass True, if the user is allowed to change the chat title, photo and " "other settings. Ignored in public supergroups" msgstr "" +"Передайте True, если пользователю разрешено изменять название чата, аватарку и " +"другие настройки. Игнорируется в публичных супергруппах" #: of telebot.TeleBot.restrict_chat_member:40 msgid "" "Pass True, if the user is allowed to invite new users to the chat, " "implies can_invite_users" msgstr "" +"Передайте True, если пользователю разрешено приглашать пользователей в чат" #: of telebot.TeleBot.restrict_chat_member:44 msgid "" "Pass True, if the user is allowed to pin messages. Ignored in public " "supergroups" msgstr "" +"Передайте True, если пользователю разрешено закреплять сообщения. Игнорируется " +"в публичных супергруппах" #: of telebot.TeleBot.restrict_chat_member:47 #: telebot.TeleBot.set_chat_permissions:14 telebot.TeleBot.unban_chat_member:19 msgid "True on success" -msgstr "" +msgstr "True в случае успеха" #: of telebot.TeleBot.retrieve_data:1 msgid "Returns context manager with data for a user in chat." -msgstr "" +msgstr "Возвращает контекстный менеджер с данными о пользователе в чате." #: of telebot.TeleBot.retrieve_data:6 msgid "Chat's unique identifier, defaults to user_id" -msgstr "" +msgstr "Уникальный id чата, по умолчанию user_id" #: of telebot.TeleBot.retrieve_data:9 msgid "Context manager with data for a user in chat" -msgstr "" +msgstr "Контекстный менеджер с данными о пользователе в чате." #: of telebot.TeleBot.revoke_chat_invite_link:1 msgid "" @@ -3842,48 +3878,54 @@ msgid "" "must be an administrator in the chat for this to work and must have the " "appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы аннулировать ссылку-приглашение, созданную ботом. " +"Примечание: Если аннулируется главная ссылка-приглашение, автоматически " +"генерируется новая. Бот должен быть администратором чата и иметь соответствующие " +"права администратора." #: of telebot.TeleBot.revoke_chat_invite_link:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#revokechatinvitelink" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#revokechatinvitelink" #: of telebot.TeleBot.revoke_chat_invite_link:11 msgid "The invite link to revoke" -msgstr "" +msgstr "Ссылка-приглашение, которую нужно аннулировать" #: of telebot.TeleBot.run_webhooks:1 msgid "This class sets webhooks and listens to a given url and port." -msgstr "" +msgstr "Этот класс устанавливает вебхуки и мониторит указанный URL и порт." #: of telebot.TeleBot.run_webhooks:3 msgid "Requires fastapi, uvicorn, and latest version of starlette." -msgstr "" +msgstr "Требует fastapi, uvicorn и последнюю версию starlette." #: of telebot.TeleBot.run_webhooks:5 msgid "IP address to listen to, defaults to \"127.0.0.1\"" -msgstr "" +msgstr "IP адрес для мониторинга, по умолчанию \"127.0.0.1\"" #: of telebot.TeleBot.run_webhooks:8 msgid "A port which will be used to listen to webhooks., defaults to 443" -msgstr "" +msgstr "Порт, который будет использован для мониторинга вебхуков. По умолчанию 443" #: of telebot.TeleBot.run_webhooks:11 msgid "Path to the webhook. Defaults to /token, defaults to None" -msgstr "" +msgstr "Путь к вебхуку(по умолчанию /token). По умолчанию None" #: of telebot.TeleBot.run_webhooks:14 msgid "Path to the certificate file, defaults to None" -msgstr "" +msgstr "Путь к файлу с SSL сертификатом, по умолчанию None" #: of telebot.TeleBot.run_webhooks:17 msgid "Path to the certificate key file, defaults to None" -msgstr "" +msgstr "Путь к файлу с приватным ключом SSL сертификата, по умолчанию None" #: of telebot.TeleBot.run_webhooks:20 msgid "Webhook URL to be set, defaults to None" -msgstr "" +msgstr "URL вебхука, по умолчанию None" #: of telebot.TeleBot.run_webhooks:23 msgid "" @@ -3892,6 +3934,10 @@ msgid "" " load on your bot's server, and higher values to increase your bot's " "throughput., defaults to None" msgstr "" +"Максимально-допустимое количество одновременных HTTPS подключений к вебхуку " +"для доставки апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения, " +" чтобы уменьшить нагрузку на ваш сервер и большие значения для увеличения " +"пропускной способности вашего бота, по умолчанию None." #: of telebot.TeleBot.run_webhooks:28 msgid "" @@ -3902,20 +3948,28 @@ msgid "" "regardless of type (default). If not specified, the previous setting will" " be used. defaults to None" msgstr "" +"Список видов апдейтов, которые вы хотите получать, в формате JSON. " +"Например, укажите [“message”, “edited_channel_post”, “callback_query”], " +"чтобы получать апдейты только этих видов. Полный список доступных видов " +"апдейтов - util.update_types. Укажите пустой список, чтобы получать все " +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка. По умолчанию None" #: of telebot.TeleBot.run_webhooks:33 telebot.TeleBot.set_webhook:32 msgid "" "The fixed IP address which will be used to send webhook requests instead " "of the IP address resolved through DNS, defaults to None" msgstr "" +"Фиксированный IP адрес, который будет использоваться для отправки запросов " +"к вебхуку вместо IP адреса, полученного через DNS, по умолчанию None" #: of telebot.TeleBot.run_webhooks:42 msgid "Secret token to be used to verify the webhook request, defaults to None" -msgstr "" +msgstr "Секретный токен для верификации запроса к вебхуку, по умолчанию None" #: of telebot.TeleBot.run_webhooks:45 msgid "Length of a secret token, defaults to 20" -msgstr "" +msgstr "Длина секретного токена, по умолчанию 20" #: of telebot.TeleBot.run_webhooks telebot.custom_filters.TextFilter msgid "Raises" @@ -3923,7 +3977,7 @@ msgstr "" #: of telebot.TeleBot.run_webhooks:48 msgid "If necessary libraries were not installed." -msgstr "" +msgstr "Если необходимые библиотеки не были установлены." #: of telebot.TeleBot.send_animation:1 msgid "" @@ -3932,10 +3986,14 @@ msgid "" "currently send animation files of up to 50 MB in size, this limit may be " "changed in the future." msgstr "" +"Используйте этот метод, чтобы отправить гиф-ку (GIF или H.264/MPEG-4 AVC видео " +"без звука). В случае успеха, возвращается отправленное сообщение (Message). На " +"текущий момент, боты могут отправлять гиф-ки весом до 50 MB, это ограничение " +"может измениться в будущем." #: of telebot.TeleBot.send_animation:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendanimation" #: of telebot.TeleBot.send_animation:9 msgid "" @@ -3944,18 +4002,22 @@ msgid "" "String for Telegram to get an animation from the Internet, or upload a " "new animation using multipart/form-data." msgstr "" +"Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гиф-ку, " +"которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить гиф-ку из интернета или загрузите новую гиф-ку " +"с помощью multipart/form-data." #: of telebot.TeleBot.send_animation:13 msgid "Duration of sent animation in seconds" -msgstr "" +msgstr "Длительность отправленной гиф-ки в секундах" #: of telebot.TeleBot.send_animation:16 msgid "Animation width" -msgstr "" +msgstr "Ширина гиф-ки" #: of telebot.TeleBot.send_animation:19 msgid "Animation height" -msgstr "" +msgstr "Высота гиф-ки" #: of telebot.TeleBot.send_animation:22 telebot.TeleBot.send_video:20 #: telebot.TeleBot.send_video_note:33 @@ -3968,31 +4030,42 @@ msgid "" "so you can pass “attach://” if the thumbnail was " "uploaded using multipart/form-data under ." msgstr "" +"Обложка отправленного файла; может быть проигнорирована, если генерация " +"обложки поддерживается на стороне сервера. Обложка должна быть картинкой " +"в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " +"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" +"data. Обложки не могут быть использованы повторно и могут быть загружены " +"только как новый файл, так что вы можете передать “attach://” " +"если обложка была загружена с помощью multipart/form-data под именем ." #: of telebot.TeleBot.send_animation:28 msgid "" "Animation caption (may also be used when resending animation by file_id)," " 0-1024 characters after entities parsing" msgstr "" +"Подпись к гиф-ке (может быть использована при повторной отправке гиф-ки по file_id), " +"0-1024 символа после форматирования" #: of telebot.TeleBot.send_animation:31 msgid "Mode for parsing entities in the animation caption" -msgstr "" +msgstr "Режим форматирования подписи к гиф-ке" #: of telebot.TeleBot.send_animation:51 telebot.TeleBot.send_video:29 msgid "" "List of special entities that appear in the caption, which can be " "specified instead of parse_mode" msgstr "" +"Список отформатированных частей подписи, можно использовать вместо " +"parse_mode" #: of telebot.TeleBot.send_animation:57 telebot.TeleBot.send_video:58 msgid "Identifier of a message thread, in which the video will be sent" -msgstr "" +msgstr "id топика, в который будет отправлено видео" #: of telebot.TeleBot.send_animation:60 #, fuzzy msgid "Pass True, if the animation should be sent as a spoiler" -msgstr "Передайте True, если email пользователя нужно отправить платежной системе" +msgstr "Передайте True, если гиф-ку нужно отправить как спойлер" #: of telebot.TeleBot.send_audio:1 msgid "" @@ -4002,14 +4075,19 @@ msgid "" " audio files of up to 50 MB in size, this limit may be changed in the " "future." msgstr "" +"Используйте этот метод, чтобы отправить аудио, если вы хотите, чтобы клиенты " +"(приложения) Telegram проигрывали их в музыкальном проигрывателе. Ваше аудио " +"должно быть в формате .MP3 или .M4A. В случае успеха, возвращается отправленное " +"сообщение (Message). На текущий момент, боты могут отправлять аудио весом до 50 MB, " +"это ограничение может измениться в будущем." #: of telebot.TeleBot.send_audio:5 msgid "For sending voice messages, use the send_voice method instead." -msgstr "" +msgstr "Для отправки голосовых сообщений, используйте метод send_voice" #: of telebot.TeleBot.send_audio:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendaudio" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendaudio" #: of telebot.TeleBot.send_audio:12 msgid "" @@ -4019,28 +4097,34 @@ msgid "" "new one using multipart/form-data. Audio must be in the .MP3 or .M4A " "format." msgstr "" +"Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " +"multipart/form-data. Аудио должно быть в формате .MP3 или .M4A." #: of telebot.TeleBot.send_audio:17 msgid "Audio caption, 0-1024 characters after entities parsing" -msgstr "" +msgstr "Подпись к аудио, 0-1024 символа после форматирования" #: of telebot.TeleBot.send_audio:20 msgid "Duration of the audio in seconds" -msgstr "" +msgstr "Длительность аудио в секундах" #: of telebot.TeleBot.send_audio:23 msgid "Performer" -msgstr "" +msgstr "Исполнитель" #: of telebot.TeleBot.send_audio:26 msgid "Track name" -msgstr "" +msgstr "Название трека" #: of telebot.TeleBot.send_audio:36 msgid "" "Mode for parsing entities in the audio caption. See formatting options " "for more details." msgstr "" +"Режим форматирования подписи к аудио. См. formatting options " +"для получения подробностей." #: of telebot.TeleBot.send_audio:45 msgid "" @@ -4052,6 +4136,13 @@ msgid "" "so you can pass “attach://” if the thumbnail was " "uploaded using multipart/form-data under " msgstr "" +"Обложка отправленного файла; может быть проигнорирована, если генерация " +"обложки поддерживается на стороне сервера. Обложка должна быть картинкой " +"в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " +"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" +"data. Обложки не могут быть использованы повторно и могут быть загружены " +"только как новый файл, так что вы можете передать “attach://” " +"если обложка была загружена с помощью multipart/form-data под именем ." #: of telebot.TeleBot.send_audio:51 telebot.TeleBot.send_document:35 #: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:37 @@ -4059,6 +4150,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the caption, " "which can be specified instead of parse_mode" msgstr "" +"Список отформатированных частей подписи в формате JSON, можно использовать вместо " +"parse_mode" #: of telebot.TeleBot.send_chat_action:1 msgid "" @@ -4067,6 +4160,10 @@ msgid "" "(when a message arrives from your bot, Telegram clients clear its typing " "status). Returns True on success." msgstr "" +"Используйте этот метод, когда вам нужно показать пользователю, что бот " +"что-то делает. Статус устанавливается на 5 секеунд или менее (когда от " +"бота приходит сообщение, клиенты (приложения) Telegram убирают статус typing). " +"Возвращает True в случае успеха." #: of telebot.TeleBot.send_chat_action:5 msgid "" @@ -4075,16 +4172,20 @@ msgid "" "image, please wait…”, the bot may use sendChatAction with action = " "upload_photo. The user will see a “sending photo” status for the bot." msgstr "" +"Пример: ImageBot-у требуется время, чтобы обработать запрос и загрузить " +"изображение. Вместо отправки текстового сообщения “Отправка изображения, " +"пожалуйста подождите…”, бот может использовать sendChatAction с параметром " +"action = upload_photo. Пользователь увидит статус бота “sending photo”." #: of telebot.TeleBot.send_chat_action:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendchataction" #: of telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 #: telebot.TeleBot.send_poll:6 telebot.TeleBot.send_venue:5 #: telebot.TeleBot.stop_poll:5 msgid "Unique identifier for the target chat or username of the target channel" -msgstr "" +msgstr "Уникальный id чата или username канала" #: of telebot.TeleBot.send_chat_action:13 msgid "" @@ -4095,39 +4196,47 @@ msgid "" "stickers, find_location for location data, record_video_note or " "upload_video_note for video notes." msgstr "" +"Тип действия. Выберите один, в зависимости от того, что получит " +"пользователь: typing для текстовых сообщений, upload_photo для фото, " +"record_video или upload_video для видео, record_voice или upload_voice для " +"голосовых сообщений, upload_document для файлов, choose_sticker для " +"стикеров, find_location для данных о местоположении, record_video_note или " +"upload_video_note для видео заметок (кружочков)." #: of telebot.TeleBot.send_chat_action:22 #, fuzzy msgid "" "The thread identifier of a message from which the reply will be " "sent(supergroups only)" -msgstr "id топика, в который нужно отправить сообщение" +msgstr "id топика, на сообщение из которого нужно ответить(только для супергрупп)" #: of telebot.TeleBot.send_contact:1 msgid "" "Use this method to send phone contacts. On success, the sent Message is " "returned." msgstr "" +"Используйте этот метод, чтобы отправить контакт. В случае успеха, возвращается " +"отправленное сообщение (Message)." #: of telebot.TeleBot.send_contact:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendcontact" #: of telebot.TeleBot.send_contact:8 msgid "Contact's phone number" -msgstr "" +msgstr "Телефонный номер контакта" #: of telebot.TeleBot.send_contact:11 msgid "Contact's first name" -msgstr "" +msgstr "Имя контакта" #: of telebot.TeleBot.send_contact:14 msgid "Contact's last name" -msgstr "" +msgstr "Фамилия контакта" #: of telebot.TeleBot.send_contact:17 msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" -msgstr "" +msgstr "Дополнительные данные о контакте в формате vCard, 0-2048 байт" #: of telebot.TeleBot.send_contact:34 telebot.TeleBot.send_game:23 #: telebot.TeleBot.send_venue:41 @@ -4135,20 +4244,24 @@ msgid "" "Pass True, if the message should be sent even if one of the specified " "replied-to messages is not found." msgstr "" +"Передайте True, если сообщение должно быть отправлено даже если одно из сообщений, " +"на которые нужно ответить, не найдено." #: of telebot.TeleBot.send_contact:41 telebot.TeleBot.send_venue:54 msgid "The thread identifier of a message from which the reply will be sent" -msgstr "" +msgstr "id топика, на сообщение из которого нужно ответить" #: of telebot.TeleBot.send_dice:1 msgid "" "Use this method to send an animated emoji that will display a random " "value. On success, the sent Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить анимированный эмодзи, который покажет " +"случайное значение. В случае успеха, возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_dice:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#senddice" #: of telebot.TeleBot.send_dice:8 msgid "" @@ -4157,18 +4270,21 @@ msgid "" " and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " "to “🎲”" msgstr "" +"Эмодзи, на котором основана анимация. На текущий момент, должно быть одним из " +"“🎲”, “🎯”, “🏀”, “⚽”, “🎳”, или “🎰”. Значение может быть 1-6 для “🎲”, “🎯”" +" и “🎳”, 1-5 для “🏀” и “⚽”, и 1-64 для “🎰”. По умолчанию “🎲”" #: of telebot.TeleBot.send_dice:29 msgid "Protects the contents of the sent message from forwarding" -msgstr "" +msgstr "Защищает содержимое отправленного сообщения от пересылки" #: of telebot.TeleBot.send_document:1 msgid "Use this method to send general files." -msgstr "" +msgstr "Используйте этот метод, чтобы отправить файл." #: of telebot.TeleBot.send_document:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#senddocument" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#senddocument" #: of telebot.TeleBot.send_document:8 msgid "" @@ -4177,16 +4293,22 @@ msgid "" "String for Telegram to get a file from the Internet, or upload a new one " "using multipart/form-data" msgstr "" +"(документ) Файл для отправки. Передайте file_id (String), чтобы отправить файл, " +"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить файл из интернета или загрузите новый с помощью " +"multipart/form-data" #: of telebot.TeleBot.send_document:15 msgid "" "Document caption (may also be used when resending documents by file_id), " "0-1024 characters after entities parsing" msgstr "" +"Подпись к файлу (может быть использована при повторной отправке файла по file_id), " +"0-1024 символа после форматирования" #: of telebot.TeleBot.send_document:23 msgid "Mode for parsing entities in the document caption" -msgstr "" +msgstr "Режим форматирования частей подписи к файлу" #: of telebot.TeleBot.send_document:32 msgid "" @@ -4199,57 +4321,71 @@ msgid "" "“attach://” if the thumbnail was uploaded using " "multipart/form-data under " msgstr "" +"InputFile или String : Обложка отправленного файла; может быть проигнорирована, " +"если генерация обложки поддерживается на стороне сервера. Обложка должна " +"быть картинкой в формате JPEG и весить менее 200 kB. Ширина и высота обложки " +"не должны превышать 320. Игнорируется, если файл не загружен с помощью " +"multipart/form-data. Обложки не могут быть использованы повторно и могут быть " +"загружены только как новый файл, так что вы можете передать " +"“attach://” если обложка была загружена с помощью " +"multipart/form-data под именем ." #: of telebot.TeleBot.send_document:41 msgid "" "allows to define file name that will be visible in the Telegram instead " "of original file name" msgstr "" +"позволяет задать имя файла, которое будет показано в Telegram вместо настоящего" #: of telebot.TeleBot.send_document:44 msgid "" "Disables automatic server-side content type detection for files uploaded " "using multipart/form-data" msgstr "" +"Отключает автоматическое обнаружение типа файла на стороне сервера для файлов, " +"загруженных с помощью multipart/form-data" #: of telebot.TeleBot.send_document:47 telebot.TeleBot.send_sticker:33 #: telebot.TeleBot.send_video:55 msgid "function typo miss compatibility: do not use it" -msgstr "" +msgstr "опечатка: не используйте" #: of telebot.TeleBot.send_document:53 telebot.TeleBot.send_sticker:36 msgid "The thread to which the message will be sent" -msgstr "" +msgstr "Топик, в которой сообщение будет отправлено" #: of telebot.TeleBot.send_game:1 msgid "Used to send the game." -msgstr "" +msgstr "Используется для отправки игры." #: of telebot.TeleBot.send_game:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendgame" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendgame" #: of telebot.TeleBot.send_game:8 msgid "" "Short name of the game, serves as the unique identifier for the game. Set" " up your games via @BotFather." msgstr "" +"Короткое имя игры, служит в качестве уникального id игры. Настройте свои игры" +"через @BotFather." #: of telebot.TeleBot.send_game:20 msgid "Timeout in seconds for waiting for a response from the bot." -msgstr "" +msgstr "Тайм-аут в секундах, ожидание ответа от бота." #: of telebot.TeleBot.send_game:26 msgid "" "Pass True, if content of the message needs to be protected from being " "viewed by the bot." msgstr "" +"Передайте True, если содержимое сообщение должно быть защищено от просмотра ботом." #: of telebot.TeleBot.send_game:29 msgid "" "The identifier of a message thread, in which the game message will be " "sent." -msgstr "" +msgstr "id топика, в который будет отправлено сообщение с игрой." #: of telebot.TeleBot.send_game:33 telebot.TeleBot.send_invoice:102 #: telebot.TeleBot.send_poll:71 @@ -4258,21 +4394,23 @@ msgstr "" #: of telebot.TeleBot.send_invoice:1 msgid "Sends invoice." -msgstr "" +msgstr "Отправляет инвойс." #: of telebot.TeleBot.send_invoice:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendinvoice" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendinvoice" #: of telebot.TeleBot.send_invoice:5 msgid "Unique identifier for the target private chat" -msgstr "" +msgstr "Уникальный id приватного чата" #: of telebot.TeleBot.send_invoice:29 msgid "" "Unique deep-linking parameter that can be used to generate this invoice " "when used as a start parameter" msgstr "" +"Уникальный deep-linking параметр, который может быть использован для " +"генерации этого инвойса при использовании в качестве параметра /start" #: of telebot.TeleBot.send_invoice:33 msgid "" @@ -4280,6 +4418,8 @@ msgid "" "a marketing image for a service. People like it better when they see what" " they are paying for." msgstr "" +"URL фото продукта. Может быть фото товаров или рекламным изображением сервиса. " +"Людям больше нравится, когда они видят, за что платят." #: of telebot.TeleBot.send_invoice:73 msgid "" @@ -4287,46 +4427,55 @@ msgid "" " price' button will be shown. If not empty, the first button must be a " "Pay button" msgstr "" +"JSON-сериализованный объект inline клавиатуры. Если пустой, будет показана " +"одна кнопка 'Pay total price'. Если не пустой, первая кнопка должна быть " +"кнопкой для оплаты" #: of telebot.TeleBot.send_invoice:81 telebot.TeleBot.set_webhook:39 msgid "Timeout of a request, defaults to None" -msgstr "" +msgstr "Тайм-аут запроса, по умолчанию None" #: of telebot.TeleBot.send_invoice:98 msgid "" "The identifier of a message thread, in which the invoice message will be " "sent" -msgstr "" +msgstr "id топика, в который будет отправлен инвойс" #: of telebot.TeleBot.send_location:1 msgid "" "Use this method to send point on the map. On success, the sent Message is" " returned." msgstr "" +"Используйте этот метод, чтобы отправить точку на карте. В случае успеха, " +"возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_location:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendlocation" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendlocation" #: of telebot.TeleBot.send_location:8 msgid "Latitude of the location" -msgstr "" +msgstr "Широта" #: of telebot.TeleBot.send_location:11 msgid "Longitude of the location" -msgstr "" +msgstr "Долгота" #: of telebot.TeleBot.send_location:14 msgid "" "Period in seconds for which the location will be updated (see Live " "Locations, should be between 60 and 86400." msgstr "" +"Время в секундах, в течение которого местоположение будет обновляться " +"(см. Live Locations), должно быть между 60 и 86400." #: of telebot.TeleBot.send_location:34 msgid "" "For live locations, a direction in which the user is moving, in degrees. " "Must be between 1 and 360 if specified." msgstr "" +"Для live местоположений, направление, в котором пользователь двигается, в градусах. " +"Должно быть между 1 и 360, если указано." #: of telebot.TeleBot.send_location:37 msgid "" @@ -4334,6 +4483,9 @@ msgid "" "approaching another chat member, in meters. Must be between 1 and 100000 " "if specified." msgstr "" +"Для live местоположений, максимальное расстояние для уведомлений о " +"приближении другого участника чата, в метрах. Должно быть между 1 и 100000, " +"если указано." #: of telebot.TeleBot.send_media_group:1 msgid "" @@ -4342,30 +4494,37 @@ msgid "" " messages of the same type. On success, an array of Messages that were " "sent is returned." msgstr "" +"Используйте этот метод, чтобы отправить группу фото, видео, файлов или аудио " +"как альбом. Файлы и аудио могут быть сгруппированы в альбом только с сообщениями " +"того же типа. В случае успеха, возвращается массив отправленных сообщений (Message)." #: of telebot.TeleBot.send_media_group:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendmediagroup" #: of telebot.TeleBot.send_media_group:9 msgid "" "A JSON-serialized array describing messages to be sent, must include 2-10" " items" msgstr "" +"JSON-сериализованный массив, описывающий сообщения для отправки, должен включать " +"от 2 до 10 элементов" #: of telebot.TeleBot.send_media_group:12 msgid "" "Sends the messages silently. Users will receive a notification with no " "sound." msgstr "" +"Отправить сообщение, при получении которого пользователя пользователи получат " +"уведомление без звука." #: of telebot.TeleBot.send_media_group:27 msgid "Identifier of a message thread, in which the media group will be sent" -msgstr "" +msgstr "id топика, в который будет отправлена группа медиа" #: of telebot.TeleBot.send_media_group:30 msgid "On success, an array of Messages that were sent is returned." -msgstr "" +msgstr "В случае успеха, возвращается массив отправленных сообщений (Message)." #: of telebot.TeleBot.send_message:1 msgid "Use this method to send text messages." From 651db29cb28f36fa1a974748d1348240ded38bb6 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Tue, 3 Jan 2023 23:45:59 +0200 Subject: [PATCH 1231/1808] Fix typehints for stop_poll reply markup --- telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ef8ab7146..50ad5141d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4197,7 +4197,7 @@ def send_poll( def stop_poll( self, chat_id: Union[int, str], message_id: int, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Poll: """ Use this method to stop a poll which was sent by the bot. On success, the stopped Poll is returned. @@ -4210,7 +4210,7 @@ def stop_poll( :type message_id: :obj:`int` :param reply_markup: A JSON-serialized object for a new message markup. - :type reply_markup: :obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply` + :type reply_markup: :obj:`InlineKeyboardMarkup` :return: On success, the stopped Poll is returned. :rtype: :obj:`types.Poll` diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6e3ec9ad7..4700bc73f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5061,7 +5061,7 @@ async def send_poll( async def stop_poll( self, chat_id: Union[int, str], message_id: int, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Poll: + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Poll: """ Use this method to stop a poll which was sent by the bot. On success, the stopped Poll is returned. @@ -5074,7 +5074,7 @@ async def stop_poll( :type message_id: :obj:`int` :param reply_markup: A JSON-serialized object for a new message markup. - :type reply_markup: :obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply` + :type reply_markup: :obj:`InlineKeyboardMarkup` :return: On success, the stopped Poll is returned. :rtype: :obj:`types.Poll` From a0ba5ae9afd111e57482d8552b30b4221e44993d Mon Sep 17 00:00:00 2001 From: Cub11k Date: Wed, 4 Jan 2023 00:43:42 +0200 Subject: [PATCH 1232/1808] Finished translations on sync and async versions. Spelling fixes --- .../locales/ru/LC_MESSAGES/async_version.po | 524 +++++++++++----- .../locales/ru/LC_MESSAGES/sync_version.po | 557 +++++++++++++----- 2 files changed, 794 insertions(+), 287 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index 7c3fd592f..dad59ddbb 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -1278,7 +1278,7 @@ msgid "" ":class:`telebot.types.ChatJoinRequest` object." msgstr "" "Обрабатывает запрос на вступление в чат. Бот должен иметь права " -"администратораcan_invite_users в чате, чтобы получать такие апдейты. В " +"администратора can_invite_users в чате, чтобы получать такие апдейты. В " "качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.ChatJoinRequest`." @@ -3383,7 +3383,7 @@ msgid "" "to demote a user." msgstr "" "Используйте этот метод, чтобы повысить или понизить пользователя в супергруппе " -"или канале. Бот должен быть администратором чатаи и иметь соответствующие права " +"или канале. Бот должен быть администратором чата и иметь соответствующие права " "администратора. Передайте False во все boolean параметры, чтобы понизить пользователя." #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:5 @@ -3687,7 +3687,7 @@ msgid "" "restrictions from a user." msgstr "" "Используйте этот метод, чтобы ограничить пользователя в супергруппе. Бот " -"должн быть администратором супергруппы и иметь соответствующие права " +"должен быть администратором супергруппы и иметь соответствующие права " "администратора. Передайте True во все boolean параметры, чтобы снять с " "пользователя ограничения." @@ -3738,7 +3738,7 @@ msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" -"Передайте True, если пользователь может отправлять гиф-ки, игры, стикеры и " +"Передайте True, если пользователь может отправлять гифки, игры, стикеры и " "использовать inline ботов, подразумевает can_send_media_messages" #: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 @@ -3897,9 +3897,9 @@ msgid "" "currently send animation files of up to 50 MB in size, this limit may be " "changed in the future." msgstr "" -"Используйте этот метод, чтобы отправить гиф-ку (GIF или H.264/MPEG-4 AVC видео " +"Используйте этот метод, чтобы отправить гифку (GIF или H.264/MPEG-4 AVC видео " "без звука). В случае успеха, возвращается отправленное сообщение (Message). На " -"текущий момент, боты могут отправлять гиф-ки весом до 50 MB, это ограничение " +"текущий момент, боты могут отправлять гифки весом до 50 MB, это ограничение " "может измениться в будущем." #: of telebot.async_telebot.AsyncTeleBot.send_animation:4 @@ -3913,22 +3913,22 @@ msgid "" "String for Telegram to get an animation from the Internet, or upload a " "new animation using multipart/form-data." msgstr "" -"Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гиф-ку, " +"Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гифку, " "которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить гиф-ку из интернета или загрузите новую гиф-ку " +"(String), чтобы отправить гифку из интернета или загрузите новую гифку " "с помощью multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_animation:13 msgid "Duration of sent animation in seconds" -msgstr "Длительность отправленной гиф-ки в секундах" +msgstr "Длительность отправленной гифки в секундах" #: of telebot.async_telebot.AsyncTeleBot.send_animation:16 msgid "Animation width" -msgstr "Ширина гиф-ки" +msgstr "Ширина гифки" #: of telebot.async_telebot.AsyncTeleBot.send_animation:19 msgid "Animation height" -msgstr "Высота гиф-ки" +msgstr "Высота гифки" #: of telebot.async_telebot.AsyncTeleBot.send_animation:22 #: telebot.async_telebot.AsyncTeleBot.send_video:20 @@ -3955,12 +3955,12 @@ msgid "" "Animation caption (may also be used when resending animation by file_id)," " 0-1024 characters after entities parsing" msgstr "" -"Подпись к гиф-ке (может быть использована при повторной отправке гиф-ки по file_id), " +"Подпись к гифке (может быть использована при повторной отправке гифки по file_id), " "0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_animation:31 msgid "Mode for parsing entities in the animation caption" -msgstr "Режим форматирования подписи к гиф-ке" +msgstr "Режим форматирования подписи к гифке" #: of telebot.async_telebot.AsyncTeleBot.send_animation:51 #: telebot.async_telebot.AsyncTeleBot.send_video:29 @@ -3979,7 +3979,7 @@ msgstr "id топика, в который будет отправлено ви #: of telebot.async_telebot.AsyncTeleBot.send_animation:60 #, fuzzy msgid "Pass True, if the animation should be sent as a spoiler" -msgstr "Передайте True, если гиф-ку нужно отправить как спойлер" +msgstr "Передайте True, если гифку нужно отправить как спойлер" #: of telebot.async_telebot.AsyncTeleBot.send_audio:1 msgid "" @@ -4077,7 +4077,7 @@ msgid "" "status). Returns True on success." msgstr "" "Используйте этот метод, когда вам нужно показать пользователю, что бот " -"что-то делает. Статус устанавливается на 5 секеунд или менее (когда от " +"что-то делает. Статус устанавливается на 5 секунд или менее (когда от " "бота приходит сообщение, клиенты (приложения) Telegram убирают статус typing). " "Возвращает True в случае успеха." @@ -4449,7 +4449,7 @@ msgstr "В случае успеха, возвращается массив от #: of telebot.async_telebot.AsyncTeleBot.send_message:1 msgid "Use this method to send text messages." -msgstr "" +msgstr "Используйте этот метод, чтобы отправлять текстовые сообщения." #: of telebot.async_telebot.AsyncTeleBot.send_message:3 msgid "" @@ -4457,34 +4457,43 @@ msgid "" "otherwise you'll risk an HTTP 414 error. If you must send more than 4096 " "characters, use the `split_string` or `smart_split` function in util.py." msgstr "" +"Предупреждение: Не отправляйте больше 4096 символов в одном сообщении, " +"иначе вы рискуете получить ошибку HTTP 414. Если вам нужно отправить больше " +"4096 символов, используйте функцию `split_string` или `smart_split` из util.py." #: of telebot.async_telebot.AsyncTeleBot.send_message:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendmessage" #: of telebot.async_telebot.AsyncTeleBot.send_message:12 msgid "Text of the message to be sent" -msgstr "" +msgstr "Текст сообщения для отправки" #: of telebot.async_telebot.AsyncTeleBot.send_message:18 msgid "" "List of special entities that appear in message text, which can be " "specified instead of parse_mode" msgstr "" +"Список отформатированных частей в тексте сообщения, " +"можно использовать вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.send_message:27 msgid "" "If True, the message content will be hidden for all users except for the " "target user" msgstr "" +"Если True, содержимое сообщения будет скрыто от всех пользователей, кроме " +"заданного" #: of telebot.async_telebot.AsyncTeleBot.send_photo:1 msgid "Use this method to send photos. On success, the sent Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить фото. В случае успеха, возвращается " +"отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_photo:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendphoto" #: of telebot.async_telebot.AsyncTeleBot.send_photo:8 msgid "" @@ -4495,63 +4504,74 @@ msgid "" " width and height must not exceed 10000 in total. Width and height ratio " "must be at most 20." msgstr "" +"Фото для отправки. Передайте file_id (String), чтобы отправить фото, " +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить фото из интернета или загрузите новое с помощью " +"multipart/form-data. Фото должно весить не более 10 MB. Ширина и высота фото не " +"должны суммарно превышать 10000. Отношение ширины и высоты должно быть не более 20." #: of telebot.async_telebot.AsyncTeleBot.send_photo:13 msgid "" "Photo caption (may also be used when resending photos by file_id), 0-1024" " characters after entities parsing" msgstr "" +"Подпись к фото (может быть использована при повторной отправке файла по file_id), " +"0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_photo:16 msgid "Mode for parsing entities in the photo caption." -msgstr "" +msgstr "Режим форматирования подписи к фото." #: of telebot.async_telebot.AsyncTeleBot.send_photo:45 #, fuzzy msgid "Pass True, if the photo should be sent as a spoiler" -msgstr "" -"Передайте True, если номер телефона пользователя нужно отправить " -"платежной системе" +msgstr "Передайте True, если фото должно быть отправлено как спойлер" #: of telebot.async_telebot.AsyncTeleBot.send_poll:1 msgid "" "Use this method to send a native poll. On success, the sent Message is " "returned." msgstr "" +"Используйте этот метод, чтобы отправить опрос. В случае успеха, возвращается " +"отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_poll:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendpoll" #: of telebot.async_telebot.AsyncTeleBot.send_poll:9 msgid "Poll question, 1-300 characters" -msgstr "" +msgstr "Тема опроса, 1-300 символов" #: of telebot.async_telebot.AsyncTeleBot.send_poll:12 msgid "" "A JSON-serialized list of answer options, 2-10 strings 1-100 characters " "each" -msgstr "" +msgstr "JSON-сериализованный список вариантов ответа, 2-10 строк по 1-100 символов" #: of telebot.async_telebot.AsyncTeleBot.send_poll:15 msgid "True, if the poll needs to be anonymous, defaults to True" -msgstr "" +msgstr "True, если опрос должен быть анонимным, по умолчанию True" #: of telebot.async_telebot.AsyncTeleBot.send_poll:18 msgid "Poll type, “quiz” or “regular”, defaults to “regular”" -msgstr "" +msgstr "Вид опроса, “quiz” или “regular”, по умолчанию “regular”" #: of telebot.async_telebot.AsyncTeleBot.send_poll:21 msgid "" "True, if the poll allows multiple answers, ignored for polls in quiz " "mode, defaults to False" msgstr "" +"True, если опрос позволяет выбрать несколько вариантов ответа, игнорируется в " +"опросах вида “quiz”, по умолчанию False" #: of telebot.async_telebot.AsyncTeleBot.send_poll:24 msgid "" "0-based identifier of the correct answer option. Available only for polls" " in quiz mode, defaults to None" msgstr "" +"Индекс правильного варианта ответа, начиная с 0. Доступно только для опросов " +"вида “quiz”, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.send_poll:28 msgid "" @@ -4559,56 +4579,70 @@ msgid "" " lamp icon in a quiz-style poll, 0-200 characters with at most 2 line " "feeds after entities parsing" msgstr "" +"Текст, который будет показан при выборе неправильно варианта ответа или " +"нажатии на иконку лампочки в опросах вида “quiz”, 0-200 символов и не " +"более 2 строк после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_poll:32 msgid "" "Mode for parsing entities in the explanation. See formatting options for " "more details." msgstr "" +"Режим форматирования explanation. См. formatting options для получения " +"подробностей." #: of telebot.async_telebot.AsyncTeleBot.send_poll:35 msgid "" "Amount of time in seconds the poll will be active after creation, 5-600. " "Can't be used together with close_date." msgstr "" +"Время в секундах, в течение которого опрос будет активен, 5-600. " +"Нельзя использовать вместо с close_date." #: of telebot.async_telebot.AsyncTeleBot.send_poll:38 msgid "Point in time (Unix timestamp) when the poll will be automatically closed." -msgstr "" +msgstr "Время (UNIX timestamp), когда опрос будет автоматически завершен." #: of telebot.async_telebot.AsyncTeleBot.send_poll:41 msgid "" "Pass True, if the poll needs to be immediately closed. This can be useful" " for poll preview." msgstr "" +"Передайте True, если опрос должен быть завершен немедленно. Может быть полезно " +"для предпросмотра опроса." #: of telebot.async_telebot.AsyncTeleBot.send_poll:50 msgid "Pass True, if the poll allows multiple options to be voted simultaneously." -msgstr "" +msgstr "Передайте True, если опрос позволяет выбрать несколько вариантов одновременно." #: of telebot.async_telebot.AsyncTeleBot.send_poll:57 msgid "Timeout in seconds for waiting for a response from the user." -msgstr "" +msgstr "Тайм-аут в секундах, ожидание ответа от пользователя." #: of telebot.async_telebot.AsyncTeleBot.send_poll:60 msgid "" "A JSON-serialized list of special entities that appear in the " "explanation, which can be specified instead of parse_mode" msgstr "" +"JSON-сериализованный список отформатированных частей explanation, " +"можно использовать вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.send_poll:67 msgid "The identifier of a message thread, in which the poll will be sent" -msgstr "" +msgstr "id топика, в который будет отправлен опрос" #: of telebot.async_telebot.AsyncTeleBot.send_sticker:1 msgid "" "Use this method to send static .WEBP, animated .TGS, or video .WEBM " "stickers. On success, the sent Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить статичный .WEBP, анимированный .TGS, " +"или видео .WEBM стикер. В случае успеха возвращает отправленное " +"сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_sticker:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendsticker" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendsticker" #: of telebot.async_telebot.AsyncTeleBot.send_sticker:9 msgid "" @@ -4617,40 +4651,46 @@ msgid "" "Telegram to get a .webp file from the Internet, or upload a new one using" " multipart/form-data." msgstr "" +"Стикер для отправки. Передайте file_id (String), чтобы отправить файл, " +"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить .webp файл из интернета или загрузите новый с помощью " +"multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_sticker:21 msgid "to disable the notification" -msgstr "" +msgstr "отключить уведомление" #: of telebot.async_telebot.AsyncTeleBot.send_venue:1 msgid "" "Use this method to send information about a venue. On success, the sent " "Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить информацию о месте. В случае успеха " +"возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_venue:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvenue" #: of telebot.async_telebot.AsyncTeleBot.send_venue:8 msgid "Latitude of the venue" -msgstr "" +msgstr "Широта" #: of telebot.async_telebot.AsyncTeleBot.send_venue:11 msgid "Longitude of the venue" -msgstr "" +msgstr "Долгота" #: of telebot.async_telebot.AsyncTeleBot.send_venue:14 msgid "Name of the venue" -msgstr "" +msgstr "Название места" #: of telebot.async_telebot.AsyncTeleBot.send_venue:17 msgid "Address of the venue" -msgstr "" +msgstr "Адрес места" #: of telebot.async_telebot.AsyncTeleBot.send_venue:20 msgid "Foursquare identifier of the venue" -msgstr "" +msgstr "id места на Foursquare" #: of telebot.async_telebot.AsyncTeleBot.send_venue:23 msgid "" @@ -4658,24 +4698,29 @@ msgid "" "“arts_entertainment/default”, “arts_entertainment/aquarium” or " "“food/icecream”.)" msgstr "" +"Тип места на Foursquare, если известен. (Например, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” или " +"“food/icecream”.)" #: of telebot.async_telebot.AsyncTeleBot.send_venue:45 msgid "Google Places identifier of the venue" -msgstr "" +msgstr "id места на Google Places" #: of telebot.async_telebot.AsyncTeleBot.send_venue:48 msgid "Google Places type of the venue." -msgstr "" +msgstr "Тип места на Google Places." #: of telebot.async_telebot.AsyncTeleBot.send_video:1 msgid "" "Use this method to send video files, Telegram clients support mp4 videos " "(other formats may be sent as Document)." msgstr "" +"Используйте этот метод, чтобы отправить видео, клиенты (приложения) Telegram " +"поддерживают mp4 видео (другие форматы могут быть отправлены как Document)." #: of telebot.async_telebot.AsyncTeleBot.send_video:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvideo" #: of telebot.async_telebot.AsyncTeleBot.send_video:8 msgid "" @@ -4683,40 +4728,43 @@ msgid "" "that is already on the Telegram servers, or upload a new video file using" " multipart/form-data." msgstr "" +"Видео для отправки. Передайте file_id (String), чтобы отправить видео, " +"которое уже загружено на сервера Telegram или загрузите новое с помощью " +"multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_video:11 #: telebot.async_telebot.AsyncTeleBot.send_video_note:13 msgid "Duration of sent video in seconds" -msgstr "" +msgstr "Длительность отправленного видео в секундах" #: of telebot.async_telebot.AsyncTeleBot.send_video:14 msgid "Video width" -msgstr "" +msgstr "Ширина видео" #: of telebot.async_telebot.AsyncTeleBot.send_video:17 msgid "Video height" -msgstr "" +msgstr "Высота видео" #: of telebot.async_telebot.AsyncTeleBot.send_video:23 msgid "" "Video caption (may also be used when resending videos by file_id), 0-1024" " characters after entities parsing" msgstr "" +"Подпись к видео (может быть использована при повторной отправке файла по file_id), " +"0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_video:26 msgid "Mode for parsing entities in the video caption" -msgstr "" +msgstr "Режим форматирования подписи к видео" #: of telebot.async_telebot.AsyncTeleBot.send_video:32 msgid "Pass True, if the uploaded video is suitable for streaming" -msgstr "" +msgstr "Передайте True, если загруженное видео подходит для стриминга" #: of telebot.async_telebot.AsyncTeleBot.send_video:61 #, fuzzy msgid "Pass True, if the video should be sent as a spoiler" -msgstr "" -"Передайте True, если номер телефона пользователя нужно отправить " -"платежной системе" +msgstr "Передайте True, если видео должно быть отправлено как спойлер" #: of telebot.async_telebot.AsyncTeleBot.send_video_note:1 msgid "" @@ -4724,10 +4772,14 @@ msgid "" "to 1 minute long. Use this method to send video messages. On success, the" " sent Message is returned." msgstr "" +"Начиная с версии v.4.0, клиенты(приложения) Telegram поддерживают скругленные " +"квадратные MPEG4 видео длительностью до минуты. Используйте этот метод, чтобы " +"отправить видео заметку (кружочек). В случае успеха возвращается отправленное " +"сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_video_note:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvideonote" #: of telebot.async_telebot.AsyncTeleBot.send_video_note:9 msgid "" @@ -4736,14 +4788,18 @@ msgid "" "multipart/form-data. Sending video notes by a URL is currently " "unsupported" msgstr "" +"Видео заметка для отправки. Передайте file_id (String), чтобы отправить видео " +"заметку, которая уже загружена на сервера Telegram или загрузите новую с помощью " +"multipart/form-data. На текущий момент, отправка видео заметок по URL не " +"поддерживается" #: of telebot.async_telebot.AsyncTeleBot.send_video_note:16 msgid "Video width and height, i.e. diameter of the video message" -msgstr "" +msgstr "Ширина и высота видео (диаметр видео сообщения)" #: of telebot.async_telebot.AsyncTeleBot.send_video_note:45 msgid "Identifier of a message thread, in which the video note will be sent" -msgstr "" +msgstr "id топика, в который будет отправлена видео заметка" #: of telebot.async_telebot.AsyncTeleBot.send_voice:1 msgid "" @@ -4754,10 +4810,16 @@ msgid "" "Bots can currently send voice messages of up to 50 MB in size, this limit" " may be changed in the future." msgstr "" +"Используйте этот метод, чтобы отправить голосовое сообщение. Ваше аудио " +"должно быть в формате .OGG и закодировано с помощью OPUS (другие форматы " +"можно отправить как Audio или Document). В случае успеха возвращается " +"отправленное сообщение (Message). На текущий момент, боты могут отправлять " +"голосовые сообщения весом до 50 MB, это ограничение может быть изменено в " +"будущем." #: of telebot.async_telebot.AsyncTeleBot.send_voice:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvoice" #: of telebot.async_telebot.AsyncTeleBot.send_voice:10 msgid "" @@ -4766,38 +4828,50 @@ msgid "" "Telegram to get a file from the Internet, or upload a new one using " "multipart/form-data." msgstr "" +"Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " +"multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_voice:14 msgid "Voice message caption, 0-1024 characters after entities parsing" -msgstr "" +msgstr "Подпись к голосовому сообщению, 0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_voice:17 msgid "Duration of the voice message in seconds" -msgstr "" +msgstr "Длительность голосового сообщения в секундах" #: of telebot.async_telebot.AsyncTeleBot.send_voice:28 msgid "" "Mode for parsing entities in the voice message caption. See formatting " "options for more details." msgstr "" +"Режим форматирования подписи к голосовому сообщению. См. formatting options " +"для получения подробностей." #: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:1 msgid "" "Use this method to set a custom title for an administrator in a " "supergroup promoted by the bot. Returns True on success." msgstr "" +"Используйте этот метод, чтобы задать кастомное звание администратора " +"супергруппы, повышенного ботом. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatadministratorcustomtitle" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatadministratorcustomtitle" #: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:13 msgid "" "New custom title for the administrator; 0-16 characters, emoji are not " "allowed" msgstr "" +"Новое кастомное звание администратора; 0-16 символов, эмодзи не " +"разрешены" #: of telebot.async_telebot.AsyncTeleBot.set_chat_description:1 msgid "" @@ -4805,40 +4879,51 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы изменить описание супергруппы или канала. " +"Бот должен быть администратором чата и иметь соответствующие права " +"администратора." #: of telebot.async_telebot.AsyncTeleBot.set_chat_description:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatdescription" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatdescription" #: of telebot.async_telebot.AsyncTeleBot.set_chat_description:10 msgid "Str: New chat description, 0-255 characters" -msgstr "" +msgstr "Str: Новое описание чата, 0-255 символов" #: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:1 msgid "" "Use this method to change the bot's menu button in a private chat, or the" " default menu button. Returns True on success." msgstr "" +"Используйте этот метод, чтобы изменить кнопку меню в приватном чате или " +"кнопку меню по умолчанию. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatmenubutton" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatmenubutton" #: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:7 msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be changed." msgstr "" +"Уникальный id приватного чата. Если не указан, будет изменена кнопка меню " +"по умолчанию." #: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:11 msgid "" "A JSON-serialized object for the new bot's menu button. Defaults to " "MenuButtonDefault" -msgstr "" +msgstr "JSON-сериализованный объект новой кнопки меню. По умолчанию MenuButtonDefault" #: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:1 msgid "" @@ -4846,16 +4931,21 @@ msgid "" "must be an administrator in the group or a supergroup for this to work " "and must have the can_restrict_members admin rights." msgstr "" +"Используйте этот метод, чтобы задать права по умолчанию для всех участников чата. " +"Бот должен быть администратором группы или супергруппы и иметь права администратора " +"can_restrict_members." #: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatpermissions" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatpermissions" #: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:11 msgid "New default chat permissions" -msgstr "" +msgstr "Новые права по умолчанию" #: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:1 msgid "" @@ -4866,14 +4956,19 @@ msgid "" "only work if the ‘All Members Are Admins’ setting is off in the target " "group." msgstr "" +"Используйте этот метод, чтобы задать новую аватарку чата. В приватных чатах " +"аватарки менять нельзя. Бот должен быть администратором чата и иметь " +"соответствующие права администратора. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппы), этот метод будет работать " +"только если настройка ‘All Members Are Admins’ отключена." #: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setchatphoto" #: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:13 msgid "InputFile: New chat photo, uploaded using multipart/form-data" -msgstr "" +msgstr "InputFile: Новая аватарка чата, загруженная с помощью multipart/form-data" #: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:1 msgid "" @@ -4883,20 +4978,26 @@ msgid "" "optionally returned in getChat requests to check if the bot can use this " "method. Returns True on success." msgstr "" +"Используйте этот метод, чтобы задать стикерпак супергруппы. Бот должен быть " +"администратором чата и иметь соответствующие права администратора. " +"Используйте атрибут can_set_sticker_set, возвращаемые методом getChat, чтобы " +"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatstickerset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatstickerset" #: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:10 msgid "Name of the sticker set to be set as the group sticker set" -msgstr "" +msgstr "Имя стикерпака для установки в качестве стикерпака группы" #: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:13 msgid "StickerSet object" -msgstr "" +msgstr "Объект StickerSet" #: of telebot.async_telebot.AsyncTeleBot.set_chat_title:1 msgid "" @@ -4906,56 +5007,67 @@ msgid "" "success. Note: In regular groups (non-supergroups), this method will only" " work if the ‘All Members Are Admins’ setting is off in the target group." msgstr "" +"Используйте этот метод, чтобы изменить название чата. В приватных чатах " +"изменить название нельзя. Бот должен быть администратором чата и иметь " +"соответствующие права админа. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппы), этот метод будет работать " +"только если настройка ‘All Members Are Admins’ отключена." #: of telebot.async_telebot.AsyncTeleBot.set_chat_title:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setchattitle" #: of telebot.async_telebot.AsyncTeleBot.set_chat_title:13 msgid "New chat title, 1-255 characters" -msgstr "" +msgstr "Новое название чата, 1-255 символов" #: of telebot.async_telebot.AsyncTeleBot.set_game_score:1 msgid "Sets the value of points in the game to a specific user." -msgstr "" +msgstr "Задаёт количество очков пользователя в игре." #: of telebot.async_telebot.AsyncTeleBot.set_game_score:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#setgamescore" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setgamescore" #: of telebot.async_telebot.AsyncTeleBot.set_game_score:8 msgid "New score, must be non-negative" -msgstr "" +msgstr "Количество очков, должно быть неотрицательным" #: of telebot.async_telebot.AsyncTeleBot.set_game_score:11 msgid "" "Pass True, if the high score is allowed to decrease. This can be useful " "when fixing mistakes or banning cheaters" msgstr "" +"Передайте True, если количество очков могут быть уменьшено. Может быть полезно " +"при исправлении ошибок или бане читеров" #: of telebot.async_telebot.AsyncTeleBot.set_game_score:23 msgid "" "Pass True, if the game message should not be automatically edited to " "include the current scoreboard" msgstr "" +"Передайте True, если сообщение с игрой должно быть автоматически " +"отредактировано, чтобы отобразить новый результат" #: of telebot.async_telebot.AsyncTeleBot.set_game_score:26 msgid "" "On success, if the message was sent by the bot, returns the edited " "Message, otherwise returns True." msgstr "" +"В случае успеха, если сообщение было отправлено ботом, возвращает измененное " +"сообщение (Message), иначе возвращает True." #: of telebot.async_telebot.AsyncTeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить список команд бота." #: of telebot.async_telebot.AsyncTeleBot.set_my_commands:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#setmycommands" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setmycommands" #: of telebot.async_telebot.AsyncTeleBot.set_my_commands:5 msgid "List of BotCommand. At most 100 commands can be specified." -msgstr "" +msgstr "Список объектов BotCommand. Можно задать не более 100 команд." #: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:1 msgid "" @@ -4964,18 +5076,26 @@ msgid "" "rights will be suggested to users, but they are are free to modify the " "list before adding the bot. Returns True on success." msgstr "" +"Используйте этот метод, чтобы изменить права администратора по умолчанию, " +"запрашиваемые при добавлении бота в группу или канал в качестве администратора. " +"Эти права будут предложены пользователям, но пользователи могут изменить список " +"перед добавлением бота. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:7 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setmydefaultadministratorrights" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setmydefaultadministratorrights" #: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:9 msgid "" "A JSON-serialized object describing new default administrator rights. If " "not specified, the default administrator rights will be cleared." msgstr "" +"JSON-сериалиованный объект, описывающий новые права администратора по умолчанию. " +"Если не указан, права администратора по умолчанию будут сброшены." #: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:13 msgid "" @@ -4983,10 +5103,12 @@ msgid "" "channels. Otherwise, the default administrator rights of the bot for " "groups and supergroups will be changed." msgstr "" +"Передайте True, чтобы изменить права администратора по умолчанию в каналах. " +"Иначе, будут изменены права администратора по умолчанию для групп и супергрупп." #: of telebot.async_telebot.AsyncTeleBot.set_state:1 msgid "Sets a new state of a user." -msgstr "" +msgstr "Задаёт новое состояние (стейт) пользователя." #: of telebot.async_telebot.AsyncTeleBot.set_state:5 msgid "" @@ -4995,26 +5117,34 @@ msgid "" "user_id, this means that state will be set for the user in his private " "chat with a bot." msgstr "" +"Вы должны указать и user id и chat id, чтобы задать состояние (стейт) " +"пользователя в чате. Иначе, если вы укажете только user_id, chat_id будет " +"равен user_id, что означает смену состояния (стейта) пользователя в его " +"приватном чате с ботом." #: of telebot.async_telebot.AsyncTeleBot.set_state:12 msgid "new state. can be string, integer, or :class:`telebot.types.State`" -msgstr "" +msgstr "новое состояние (стейт). может быть строкой, числом или :class:`telebot.types.State`" #: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:1 msgid "" "Use this method to move a sticker in a set created by the bot to a " "specific position . Returns True on success." msgstr "" +"Используйте этот метод, чтобы передвинуть стикер в стикерпаке, созданном ботом, на заданную " +"позицию. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickerpositioninset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setstickerpositioninset" #: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:8 msgid "New sticker position in the set, zero-based" -msgstr "" +msgstr "Новая позиция стикера в стикерпаке, начиная с нуля" #: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:1 msgid "" @@ -5022,24 +5152,29 @@ msgid "" "thumbnails can be set for animated sticker sets only. Returns True on " "success." msgstr "" +"Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " +"обложки могут быть заданы только для анимированных стикерпаков. Возвращает " +"True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickersetthumb" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setstickersetthumb" #: of telebot.async_telebot.AsyncTeleBot.set_update_listener:1 msgid "Update listener is a function that gets any update." -msgstr "" +msgstr "Задаёт функцию-листенер, которая будет вызвана при получении нового апдейта." #: of telebot.async_telebot.AsyncTeleBot.set_update_listener:3 msgid "function that should get update." -msgstr "" +msgstr "Функция-листенер." #: of telebot.async_telebot.AsyncTeleBot.set_update_listener:6 msgid "Example on asynchronous update listeners." -msgstr "" +msgstr "Пример асинхронного листенера апдейтов." #: of telebot.async_telebot.AsyncTeleBot.set_webhook:1 msgid "" @@ -5049,6 +5184,11 @@ msgid "" "Update. In case of an unsuccessful request, we will give up after a " "reasonable amount of attempts. Returns True on success." msgstr "" +"Используйте этот метод, чтобы задать URL и получать входящие апдейты с " +"помощью вебхука. Как только у бота появляется апдейт, он будет отправлен с " +"помощью HTTPS POST запроса на заданный URL, содержащего JSON-сериализованный " +"Update. В случае неудачного запроса, отправка апдейта будет отменена после " +"разумного числа попыток. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_webhook:6 msgid "" @@ -5057,22 +5197,30 @@ msgid "" "request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the " "secret token as content." msgstr "" +"Если вы хотите удостовериться, что вебхук был задан вами, вы можете " +"задать секретный токен в параметре secret_token. Если указан, запрос " +"с апдейтом будет содержать хедер “X-Telegram-Bot-Api-Secret-Token” с " +"секретным токеном в качестве значения." #: of telebot.async_telebot.AsyncTeleBot.set_webhook:9 msgid "Telegram Documentation: https://core.telegram.org/bots/api#setwebhook" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setwebhook" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:11 msgid "" "HTTPS URL to send updates to. Use an empty string to remove webhook " "integration, defaults to None" msgstr "" +"HTTPS URL для отправки апдейтов. Используйте пустую строку, чтобы удалить " +"вебхук, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:14 msgid "" "Upload your public key certificate so that the root certificate in use " "can be checked, defaults to None" msgstr "" +"Загрузите публичный ключ вашего SSL сертификата, чтобы корневой сертификат мог быть " +"проверен, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:17 msgid "" @@ -5081,6 +5229,10 @@ msgid "" "limit the load on your bot's server, and higher values to increase your " "bot's throughput, defaults to None" msgstr "" +"Максимально-допустимое количество одновременных HTTPS соединений для доставки " +"апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения для уменьшения " +"нагрузки на ваш сервер и большие значения, чтобы увеличить пропускную способность " +"вашего бота, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 msgid "" @@ -5093,6 +5245,14 @@ msgid "" "updates created before the call to the setWebhook, so unwanted updates " "may be received for a short period of time. Defaults to None" msgstr "" +"Список видов апдейтов, которые вы хотите получать, в формате JSON. " +"Например, укажите [“message”, “edited_channel_post”, “callback_query”], " +"чтобы получать апдейты только этих видов. Полный список доступных видов " +"апдейтов - util.update_types. Укажите пустой список, чтобы получать все " +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка. Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " +"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " +"получены в течение короткого периода времени. По умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 msgid "" @@ -5103,6 +5263,12 @@ msgid "" "types except chat_member (default). If not specified, the previous " "setting will be used." msgstr "" +"Список видов апдейтов, которые вы хотите получать, в формате JSON. " +"Например, укажите [“message”, “edited_channel_post”, “callback_query”], " +"чтобы получать апдейты только этих видов. Полный список доступных видов " +"апдейтов - util.update_types. Укажите пустой список, чтобы получать все " +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка." #: of telebot.async_telebot.AsyncTeleBot.set_webhook:27 msgid "" @@ -5110,12 +5276,17 @@ msgid "" " call to the setWebhook, so unwanted updates may be received for a short " "period of time. Defaults to None" msgstr "" +"Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " +"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " +"получены в течение короткого периода времени. По умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:32 msgid "" "The fixed IP address which will be used to send webhook requests instead " "of the IP address resolved through DNS, defaults to None" msgstr "" +"Фиксированный IP адрес, который будет использоваться для отправки запросов к вебхуку" +"вместо IP адреса, полученного с через DNS, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:42 msgid "" @@ -5124,14 +5295,18 @@ msgid "" "0-9, _ and - are allowed. The header is useful to ensure that the request" " comes from a webhook set by you. Defaults to None" msgstr "" +"Секретный токен для отправки в хедере “X-Telegram-Bot-Api-Secret-Token” " +"в каждом запросе с апдейтом, 1-256 символов. Разрешены только символы A-Z, a-z, " +"0-9, _ и -. Хедер полезен для, того чтобы удостовериться, что запрос приходит" +"с вебхука, установленного вами. По умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:47 msgid ":obj:`bool` if the request was successful." -msgstr "" +msgstr ":obj:`bool` если запрос был успешным." #: of telebot.async_telebot.AsyncTeleBot.setup_middleware:1 msgid "Setup middleware." -msgstr "" +msgstr "Настраивает middleware" #: of telebot.async_telebot.AsyncTeleBot.setup_middleware:5 msgid "" @@ -5139,10 +5314,13 @@ msgid "" ":class:`telebot.asyncio_handler_backends.BaseMiddleware` section for " "more." msgstr "" +"Взгляните на секцию " +":class:`telebot.asyncio_handler_backends.BaseMiddleware` для получения " +"подробностей." #: of telebot.async_telebot.AsyncTeleBot.setup_middleware:7 msgid "Middleware-class." -msgstr "" +msgstr "Класс-Middleware." #: of telebot.async_telebot.AsyncTeleBot.shipping_query_handler:1 msgid "" @@ -5150,10 +5328,13 @@ msgid "" "price. As a parameter to the decorator function, it passes " ":class:`telebot.types.ShippingQuery` object." msgstr "" +"Обрабатывает shipping query. Только для инвойсов с гибкой ценой. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ShippingQuery`." #: of telebot.async_telebot.AsyncTeleBot.skip_updates:1 msgid "Skip existing updates. Only last update will remain on server." -msgstr "" +msgstr "Пропускает существующие апдейты. На сервере останется только последний апдейт." #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:1 msgid "" @@ -5161,52 +5342,65 @@ msgid "" "live_period expires. On success, if the message is not an inline message," " the edited Message is returned, otherwise True is returned." msgstr "" +"Используйте этот метод, чтобы остановить обновление live местоположения до " +"истечения live_period. В случае успеха, если сообщение не является inline сообщением," +"возвращается измененное сообщение (Message), иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#stopmessagelivelocation" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#stopmessagelivelocation" #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:9 msgid "" "Required if inline_message_id is not specified. Identifier of the message" " with live location to stop" msgstr "" +"Обязательный, если не указан inline_message_id. id сообщения live местоположением, " +"которое нужно остановить" #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:12 msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message with live location to stop" msgstr "" +"Обязательный, если не указаны chat_id и message_id. id inline сообщения с live " +"местоположением, которое нужно остановить" #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:22 msgid "" "On success, if the message is not an inline message, the edited Message " "is returned, otherwise True is returned." msgstr "" +"В случае успеха, если сообщение не является inline сообщением, возвращается " +"измененное сообщение (Message), иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.stop_poll:1 msgid "" "Use this method to stop a poll which was sent by the bot. On success, the" " stopped Poll is returned." msgstr "" +"Используйте этот метод, чтобы завершить опрос, отправленный ботом. В случае успеха " +"возвращается завершенный опрос (Poll)." #: of telebot.async_telebot.AsyncTeleBot.stop_poll:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#stoppoll" #: of telebot.async_telebot.AsyncTeleBot.stop_poll:8 msgid "Identifier of the original message with the poll" -msgstr "" +msgstr "id сообщения с опросом" #: of telebot.async_telebot.AsyncTeleBot.stop_poll:11 msgid "A JSON-serialized object for a new message markup." -msgstr "" +msgstr "JSON-сериализованный объект новой inline клавиатуры." #: of telebot.async_telebot.AsyncTeleBot.stop_poll:14 msgid "On success, the stopped Poll is returned." -msgstr "" +msgstr "В случае успеха возвращается завершенный опрос (Poll)." #: of telebot.async_telebot.AsyncTeleBot.stop_poll:15 msgid ":obj:`types.Poll`" @@ -5222,20 +5416,27 @@ msgid "" "the user is a member of the chat they will also be removed from the chat." " If you don't want this, use the parameter only_if_banned." msgstr "" +"Используйте этот метод, чтобы разбанить ранее кикнутого пользователя в " +"супергруппе или канале. Пользовать не вернется в группу или канал автоматически, " +"но сможет присоединиться с помощью ссылки и т.д. Бот должен быть администратором. " +"По умолчанию, этот метод гарантирует, что после вызова, пользователь не является " +"участником чата, но может присоединиться. Поэтому если пользовать является участником " +"чата, он будет кикнут, но не забанен. Если вы хотите изменить это поведение, " +"используйте параметр only_if_banned." #: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#unbanchatmember" #: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:9 msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @username)" -msgstr "" +msgstr "Уникальный id группы или username супергруппы или канала (в формате @username)" #: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:16 msgid "Do nothing if the user is not banned" -msgstr "" +msgstr "Ничего не делать, если пользователь не забанен" #: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:1 msgid "" @@ -5243,16 +5444,21 @@ msgid "" " or channel. The bot must be an administrator for this to work and must " "have the appropriate administrator rights. Returns True on success." msgstr "" +"Используйте этот метод, чтобы разбанить ране забаненный канал в супергруппе " +"или канала. Бот должен быть администратором и иметь соответствующие права " +"администратора. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unbanchatsenderchat" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unbanchatsenderchat" #: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:11 msgid "Unique identifier of the target sender chat." -msgstr "" +msgstr "Уникальный id чата." #: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:1 #, fuzzy @@ -5261,17 +5467,18 @@ msgid "" " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics," -" за исключением случае, когда бот является создателем топика. Возвращает " -"True в случае успеха." +"Используйте этот метод, чтобы сделать топик 'General' видимым в супергруппе " +"с топиками. Бот должен быть администратором чата и иметь права администратора " +"can_manage_topics. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:5 #, fuzzy msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unhidegeneralforumtopic" -msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unhidegeneralforumtopic" #: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:1 msgid "" @@ -5279,12 +5486,17 @@ msgid "" "bot must be an administrator in the chat for this to work and must have " "the appropriate admin rights. Returns True on success." msgstr "" +"Используйте этот метод, что открепить все закрепленные сообщения в супергруппе. " +"Бот должен быть администратором чата и иметь соответствующие права администратора. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinallchatmessages" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinallchatmessages" #: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:1 msgid "" @@ -5293,16 +5505,21 @@ msgid "" "have the can_pin_messages administrator right in the supergroup. Returns " "True on success." msgstr "" +"Используйте этот метод, что открепить все закрепленные сообщения в топике. " +"Бот должен быть администратором чата и иметь права администратора " +"can_pin_messages в супергруппе. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinallforumtopicmessages" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" #: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:10 msgid "Identifier of the topic" -msgstr "" +msgstr "id топика" #: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:1 msgid "" @@ -5310,16 +5527,21 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights. Returns True on success." msgstr "" +"Используйте этот метод, что открепить закрепленное сообщение в супергруппе. " +"Бот должен быть администратором чата и иметь соответствующие права администратора. " +"Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinchatmessage" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinchatmessage" #: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:11 msgid "Int: Identifier of a message to unpin" -msgstr "" +msgstr "Int: id сообщения, которое нужно открепить" #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:1 msgid "" @@ -5327,16 +5549,21 @@ msgid "" "createNewStickerSet and addStickerToSet methods (can be used multiple " "times). Returns the uploaded File on success." msgstr "" +"Используйте этот метод, чтобы загрузить .png стикер, чтобы позже " +"использовать в методах createNewStickerSet и addStickerToSet (может быть " +"использован несколько раз). Возвращает загруженный файл (File) в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#uploadstickerfile" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#uploadstickerfile" #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:6 msgid "User identifier of sticker set owner" -msgstr "" +msgstr "id пользователя, создавшего стикерпак" #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:9 msgid "" @@ -5344,27 +5571,29 @@ msgid "" "dimensions must not exceed 512px, and either width or height must be " "exactly 512px." msgstr "" +"PNG изображение стикера, должно весить не более 512 kB, измерения не " +"должны превышать 512px и либо ширина, либо высота должна быть ровно 512px." #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:13 msgid "On success, the sent file is returned." -msgstr "" +msgstr "В случае успеха возвращается отправленный файл." #: of telebot.async_telebot.ExceptionHandler:1 msgid "Class for handling exceptions while Polling" -msgstr "" +msgstr "Класс для обработки исключений во время поллинга" #: of telebot.async_telebot.Handler:1 msgid "Class for (next step|reply) handlers" -msgstr "" +msgstr "Класс для (next step|reply) хендлеров" #: ../../source/async_version/index.rst:22 msgid "Asyncio filters" -msgstr "" +msgstr "Файл asyncio_filters" #: of telebot.asyncio_filters.AdvancedCustomFilter:1 #: telebot.asyncio_filters.SimpleCustomFilter:1 msgid "Bases: :py:class:`abc.ABC`" -msgstr "" +msgstr "Базовые классы: :py:class:`abc.ABC`" #: of telebot.asyncio_filters.AdvancedCustomFilter:1 msgid "" @@ -5373,20 +5602,23 @@ msgid "" " - filter failed. message: Message class text: Filter value given in " "handler" msgstr "" +"Базовый класс Advanced Custom Filter. Создайте класс наследник с методом check(). " +"Принимает два параметра, возвращает bool: True - фильтр пройден, False - фильтр " +"не пройден. message: класс Message text: значение фильтра, полученное в хендлере" #: of telebot.asyncio_filters.AdvancedCustomFilter:7 #: telebot.asyncio_filters.SimpleCustomFilter:5 msgid "Child classes should have .key property." -msgstr "" +msgstr "Классы наследники должны иметь статический атрибут (property) .key" #: of telebot.asyncio_filters.AdvancedCustomFilter:9 msgid "Example on creating an advanced custom filter." -msgstr "" +msgstr "Пример создания advanced custom filter." #: of telebot.asyncio_filters.AdvancedCustomFilter.check:1 #: telebot.asyncio_filters.SimpleCustomFilter.check:1 msgid "Perform a check." -msgstr "" +msgstr "Выполнить проверку." #: of telebot.asyncio_filters.ChatFilter:1 #: telebot.asyncio_filters.LanguageFilter:1 @@ -5395,11 +5627,11 @@ msgstr "" #: telebot.asyncio_filters.TextMatchFilter:1 #: telebot.asyncio_filters.TextStartsFilter:1 msgid "Bases: :py:class:`telebot.asyncio_filters.AdvancedCustomFilter`" -msgstr "" +msgstr "Базовые классы: :py:class:`telebot.custom_filters.AdvancedCustomFilter`" #: of telebot.asyncio_filters.ChatFilter:1 msgid "Check whether chat_id corresponds to given chat_id." -msgstr "" +msgstr "Проверяет, является ли chat_id заданным." #: of telebot.asyncio_filters.ChatFilter:3 #: telebot.asyncio_filters.ForwardFilter:3 @@ -5412,34 +5644,34 @@ msgstr "" #: telebot.asyncio_filters.TextMatchFilter:3 #: telebot.asyncio_filters.TextStartsFilter:3 msgid "Example on using this filter:" -msgstr "" +msgstr "Пример использования этого фильтра:" #: of telebot.asyncio_filters.ForwardFilter:1 #: telebot.asyncio_filters.IsAdminFilter:1 #: telebot.asyncio_filters.IsDigitFilter:1 #: telebot.asyncio_filters.IsReplyFilter:1 msgid "Bases: :py:class:`telebot.asyncio_filters.SimpleCustomFilter`" -msgstr "" +msgstr "Базовые классы: :py:class:`telebot.asyncio_filters.SimpleCustomFilter`" #: of telebot.asyncio_filters.ForwardFilter:1 msgid "Check whether message was forwarded from channel or group." -msgstr "" +msgstr "Проверяет, является ли сообщение пересланным из канала или группы." #: of telebot.asyncio_filters.IsAdminFilter:1 msgid "Check whether the user is administrator / owner of the chat." -msgstr "" +msgstr "Проверяет, является ли пользователь администратором / владельцем чата." #: of telebot.asyncio_filters.IsDigitFilter:1 msgid "Filter to check whether the string is made up of only digits." -msgstr "" +msgstr "Фильтр для проверки, состоит ли строка только из цифр." #: of telebot.asyncio_filters.IsReplyFilter:1 msgid "Check whether message is a reply." -msgstr "" +msgstr "Проверяет, является ли сообщение ответом (reply)." #: of telebot.asyncio_filters.LanguageFilter:1 msgid "Check users language_code." -msgstr "" +msgstr "Проверяет language_code пользователя." #: of telebot.asyncio_filters.SimpleCustomFilter:1 msgid "" @@ -5447,47 +5679,53 @@ msgid "" "Accepts only message, returns bool value, that is compared with given in " "handler." msgstr "" +"Базовый класс Simple Custom Filter. Создайте класс наследник с методом check(). " +"Принимает только сообщение, возвращает bool, который сравнивается с заданным в хендлере." #: of telebot.asyncio_filters.SimpleCustomFilter:7 msgid "Example on creating a simple custom filter." -msgstr "" +msgstr "Пример создания simple custom filter." #: of telebot.asyncio_filters.StateFilter:1 msgid "Filter to check state." -msgstr "" +msgstr "Фильтр для проверки состояния (стейта)." #: of telebot.asyncio_filters.TextContainsFilter:1 msgid "Filter to check Text message. key: text" -msgstr "" +msgstr "Фильтр для проверки текста сообщения. key: text" #: of telebot.asyncio_filters.TextFilter:1 msgid "" "Advanced text filter to check (types.Message, types.CallbackQuery, " "types.InlineQuery, types.Poll)" msgstr "" +"Advanced текстовый фильтр для проверки (types.Message, types.CallbackQuery, " +"types.InlineQuery, types.Poll)" #: of telebot.asyncio_filters.TextFilter:3 msgid "" "example of usage is in " "examples/asynchronous_telebot/custom_filters/advanced_text_filter.py" msgstr "" +"пример использования в " +"examples/asynchronous_telebot/custom_filters/advanced_text_filter.py" #: of telebot.asyncio_filters.TextFilter:5 msgid "string, True if object's text is equal to passed string" -msgstr "" +msgstr "строка, True если текст объекта идентичен заданной строке" #: of telebot.asyncio_filters.TextFilter:8 msgid "list[str] or tuple[str], True if any string element of iterable is in text" -msgstr "" +msgstr "list[str] или tuple[str], True если хотя бы один из элементов есть в тексте" #: of telebot.asyncio_filters.TextFilter:11 #: telebot.asyncio_filters.TextFilter:14 msgid "string, True if object's text starts with passed string" -msgstr "" +msgstr "string, True если текст объекта начинается с заданной строки" #: of telebot.asyncio_filters.TextFilter:17 msgid "bool (default False), case insensitive" -msgstr "" +msgstr "bool (по умолчанию False), независимый от регистра" #: of telebot.asyncio_filters.TextFilter msgid "Raises" @@ -5495,29 +5733,31 @@ msgstr "" #: of telebot.asyncio_filters.TextFilter:20 msgid "if incorrect value for a parameter was supplied" -msgstr "" +msgstr "если было задано некорректное значение параметра" #: of telebot.asyncio_filters.TextMatchFilter:1 msgid "Filter to check Text message." -msgstr "" +msgstr "Фильтр для проверки текста сообщения." #: of telebot.asyncio_filters.TextStartsFilter:1 msgid "Filter to check whether message starts with some text." -msgstr "" +msgstr "Фильтр для проверки, начинается ли сообщение с заданного текста." #: ../../source/async_version/index.rst:30 msgid "Asyncio handler backends" -msgstr "" +msgstr "Файл asyncio_handler_backends" #: of telebot.asyncio_handler_backends:1 msgid "File with all middleware classes, states." -msgstr "" +msgstr "Файл со всеми классами middleware и states." #: of telebot.asyncio_handler_backends.BaseMiddleware:1 msgid "" "Base class for middleware. Your middlewares should be inherited from this" " class." msgstr "" +"Базовый класс для middleware. Ваши middleware должны быть унаследованы от " +"этого класса." #: of telebot.asyncio_handler_backends.BaseMiddleware:4 msgid "" @@ -5526,10 +5766,14 @@ msgid "" "message update, then you will have to create pre_process_message " "function, and so on. Same applies to post_process." msgstr "" +"Задайте update_sensitive=True если хотите получать разные апдейты в " +"разных функциях. Например, если вы хотите обрабатывать pre_process для " +"апдейтов вида message, вам нужно будет создать функцию pre_process_message " +"и т.д. Аналогично для post_process." #: of telebot.asyncio_handler_backends.BaseMiddleware:9 msgid "Example of class-based middlewares" -msgstr "" +msgstr "Пример класса middleware" #: of telebot.asyncio_handler_backends.CancelUpdate:1 msgid "" @@ -5537,16 +5781,21 @@ msgid "" "middleware to skip update. Update will skip handler and execution of " "post_process in middlewares." msgstr "" +"Класс для отмены апдейтов. Просто верните экземпляр этого класса в " +"middleware, чтобы пропустить апдейт. Апдейт пропустит хендлер и исполнение " +"post_process в middleware." #: of telebot.asyncio_handler_backends.ContinueHandling:1 msgid "" "Class for continue updates in handlers. Just return instance of this " "class in handlers to continue process." msgstr "" +"Класс для продолжения обработки апдейта в хендлерах. Просто верните экземпляр " +"этого класса в хендлерах, чтобы продолжить обработку." #: of telebot.asyncio_handler_backends.ContinueHandling:5 msgid "Example of using ContinueHandling" -msgstr "" +msgstr "Пример использования ContinueHandling" #: of telebot.asyncio_handler_backends.SkipHandler:1 msgid "" @@ -5554,32 +5803,37 @@ msgid "" "middleware to skip handler. Update will go to post_process, but will skip" " execution of handler." msgstr "" +"Класс для пропуска хендлеров. Просто верните экземпляр этого класса в " +"middleware, чтобы пропустить хендлер. Апдейт попадёт в post_process, но " +"пропустит исполнение хендлера." #: of telebot.asyncio_handler_backends.State:1 msgid "Class representing a state." -msgstr "" +msgstr "Класс, представляющий состояние (стейт)." #: of telebot.asyncio_handler_backends.StatesGroup:1 msgid "Class representing common states." -msgstr "" +msgstr "Класс, представляющий похожие состояния (стейты)." #: ../../source/async_version/index.rst:41 msgid "Extensions" -msgstr "" +msgstr "Расширения" #: of telebot.ext.aio.webhooks:1 msgid "This file is used by AsyncTeleBot.run_webhooks() function." -msgstr "" +msgstr "Этот файл используется функцией AsyncTeleBot.run_webhooks()." #: of telebot.ext.aio.webhooks:3 msgid "Fastapi and starlette(0.20.2+) libraries are required to run this script." -msgstr "" +msgstr "Для запуска этого скрипта требуются библиотеки Fastapi и starlette(0.20.2+)." #: of telebot.ext.aio.webhooks.AsyncWebhookListener.run_app:1 msgid "" "Run app with the given parameters to init. Not supposed to be used " "manually by user." msgstr "" +"Запустить приложение с заданными параметрами инициализации. Не предназначено " +"для использования пользователем." #~ msgid "New name of the topic, 1-128 characters" #~ msgstr "Новое название топика, 1-128 символов" diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index e5f7265d0..999ae43fd 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -1154,7 +1154,7 @@ msgid "" ":class:`telebot.types.ChatJoinRequest` object." msgstr "" "Обрабатывает запрос на вступление в чат. Бот должен иметь права " -"администратораcan_invite_users в чате, чтобы получать такие апдейты. В " +"администратора can_invite_users в чате, чтобы получать такие апдейты. В " "качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.ChatJoinRequest`." @@ -2140,7 +2140,7 @@ msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." msgstr "" -"В случае успеха, если изменённое сообщение отправлено ботом, возвращается" +"В случае успеха если изменённое сообщение отправлено ботом, возвращается" " новый объект Message, иначе (inline сообщения) возвращается True." #: of telebot.TeleBot.edit_message_caption:27 @@ -2161,7 +2161,7 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" -"отключено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " +"отключено вызовом метода stopMessageLiveLocation. В случае успеха если измененное " "сообщение не является inline сообщением, возвращается новый объект Message, " "иначе возвращается True." @@ -2230,7 +2230,7 @@ msgid "" "On success, if the edited message is not an inline message, the edited " "Message is returned, otherwise True is returned." msgstr "" -"В случае успеха, если измененное сообщение не является inline сообщением," +"В случае успеха если измененное сообщение не является inline сообщением," " возвращается новый объект Message, иначе возвращается True." #: of telebot.TeleBot.edit_message_live_location:39 @@ -2476,7 +2476,7 @@ msgid "" "all chat administrators except other bots." msgstr "" "Используйте этот метод, чтобы получить список администраторов чата. " -"В случае успеха, возвращает массив объектов ChatMember, содержащих информацию " +"В случае успеха возвращает массив объектов ChatMember, содержащих информацию " "обо всех администраторах чата, кроме других ботов." #: of telebot.TeleBot.get_chat_administrators:5 @@ -2600,7 +2600,7 @@ msgid "" msgstr "" "Используйте этот метод, чтобы получить базовую информацию о файле и подготовить " "его к скачиванию. На текущий момент, боты могут скачивать файлы весом до 20MB. " -"В случае успеха, возвращается объект File. Гарантируется, что ссылка на скачивание " +"В случае успеха возвращается объект File. Гарантируется, что ссылка на скачивание " "будет актуальна как минимум 1 час. Когда ссылка перестаёт быть актуальной, новая " "может быть снова запрошена с помощью get_file." @@ -2648,7 +2648,7 @@ msgstr "" #: of telebot.TeleBot.get_forum_topic_icon_stickers:6 msgid "On success, a list of StickerSet objects is returned." -msgstr "В случае успеха, возвращается список объектов StickerSet." +msgstr "В случае успеха возвращается список объектов StickerSet." #: of telebot.TeleBot.get_forum_topic_icon_stickers:7 msgid "List[:class:`telebot.types.StickerSet`]" @@ -2661,7 +2661,7 @@ msgid "" "success, returns an Array of GameHighScore objects." msgstr "" "Используйте этот метод, чтобы получить данные для таблицы рекордов. Вернёт " -"очки указанного пользователя и несколько соседних результатов. В случае успеха, " +"очки указанного пользователя и несколько соседних результатов. В случае успеха " "возвращает массив объектов GameHighScore." #: of telebot.TeleBot.get_game_high_scores:4 @@ -2691,7 +2691,7 @@ msgstr "id пользователя" #: of telebot.TeleBot.get_game_high_scores:22 msgid "On success, returns an Array of GameHighScore objects." -msgstr "В случае успеха, возвращает массив объектов GameHighScore." +msgstr "В случае успеха возвращает массив объектов GameHighScore." #: of telebot.TeleBot.get_me:1 msgid "" @@ -2781,7 +2781,7 @@ msgid "" "Use this method to get a sticker set. On success, a StickerSet object is " "returned." msgstr "" -"Используйте этот метод, чтобы получить стикерпак. В случае успеха, возвращается " +"Используйте этот метод, чтобы получить стикерпак. В случае успеха возвращается " "объект StickerSet." #: of telebot.TeleBot.get_sticker_set:3 @@ -2790,7 +2790,7 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#ge #: of telebot.TeleBot.get_sticker_set:8 msgid "On success, a StickerSet object is returned." -msgstr "В случае успеха, возвращается объект StickerSet." +msgstr "В случае успеха возвращается объект StickerSet." #: of telebot.TeleBot.get_sticker_set:9 telebot.TeleBot.set_chat_sticker_set:14 msgid ":class:`telebot.types.StickerSet`" @@ -2912,7 +2912,7 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#ge #: of telebot.TeleBot.get_webhook_info:9 msgid "On success, returns a WebhookInfo object." -msgstr "В случае успеха, возвращает объект WebhookInfo." +msgstr "В случае успеха возвращает объект WebhookInfo." #: of telebot.TeleBot.get_webhook_info:10 msgid ":class:`telebot.types.WebhookInfo`" @@ -3235,7 +3235,7 @@ msgid "" "__retrieve_updates function. This allows the bot to retrieve Updates " "automatically and notify listeners and message handlers accordingly." msgstr "" -"Эта функция создаёт новый Thread, который вызывает служебную фукнцию " +"Эта функция создаёт новый Thread, который вызывает служебную функцию " "__retrieve_updates. Это позволяет боту получать апдейты (Update) " "автоматически и вызывать соответствующие листенеры и хендлеры." @@ -3349,7 +3349,7 @@ msgid "" "to demote a user." msgstr "" "Используйте этот метод, чтобы повысить или понизить пользователя в супергруппе " -"или канале. Бот должен быть администратором чатаи и иметь соответствующие права " +"или канале. Бот должен быть администратором чата и иметь соответствующие права " "администратора. Передайте False во все boolean параметры, чтобы понизить пользователя." #: of telebot.TeleBot.promote_chat_member:5 @@ -3769,7 +3769,7 @@ msgid "" "restrictions from a user." msgstr "" "Используйте этот метод, чтобы ограничить пользователя в супергруппе. Бот " -"должн быть администратором супергруппы и иметь соответствующие права " +"должен быть администратором супергруппы и иметь соответствующие права " "администратора. Передайте True во все boolean параметры, чтобы снять с " "пользователя ограничения." @@ -3820,7 +3820,7 @@ msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" -"Передайте True, если пользователь может отправлять гиф-ки, игры, стикеры и " +"Передайте True, если пользователь может отправлять гифки, игры, стикеры и " "использовать inline ботов, подразумевает can_send_media_messages" #: of telebot.TeleBot.restrict_chat_member:32 @@ -3986,9 +3986,9 @@ msgid "" "currently send animation files of up to 50 MB in size, this limit may be " "changed in the future." msgstr "" -"Используйте этот метод, чтобы отправить гиф-ку (GIF или H.264/MPEG-4 AVC видео " -"без звука). В случае успеха, возвращается отправленное сообщение (Message). На " -"текущий момент, боты могут отправлять гиф-ки весом до 50 MB, это ограничение " +"Используйте этот метод, чтобы отправить гифку (GIF или H.264/MPEG-4 AVC видео " +"без звука). В случае успеха возвращается отправленное сообщение (Message). На " +"текущий момент, боты могут отправлять гифки весом до 50 MB, это ограничение " "может измениться в будущем." #: of telebot.TeleBot.send_animation:4 @@ -4002,22 +4002,22 @@ msgid "" "String for Telegram to get an animation from the Internet, or upload a " "new animation using multipart/form-data." msgstr "" -"Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гиф-ку, " +"Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гифку, " "которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить гиф-ку из интернета или загрузите новую гиф-ку " +"(String), чтобы отправить гифку из интернета или загрузите новую гифку " "с помощью multipart/form-data." #: of telebot.TeleBot.send_animation:13 msgid "Duration of sent animation in seconds" -msgstr "Длительность отправленной гиф-ки в секундах" +msgstr "Длительность отправленной гифки в секундах" #: of telebot.TeleBot.send_animation:16 msgid "Animation width" -msgstr "Ширина гиф-ки" +msgstr "Ширина гифки" #: of telebot.TeleBot.send_animation:19 msgid "Animation height" -msgstr "Высота гиф-ки" +msgstr "Высота гифки" #: of telebot.TeleBot.send_animation:22 telebot.TeleBot.send_video:20 #: telebot.TeleBot.send_video_note:33 @@ -4043,12 +4043,12 @@ msgid "" "Animation caption (may also be used when resending animation by file_id)," " 0-1024 characters after entities parsing" msgstr "" -"Подпись к гиф-ке (может быть использована при повторной отправке гиф-ки по file_id), " +"Подпись к гифке (может быть использована при повторной отправке гифки по file_id), " "0-1024 символа после форматирования" #: of telebot.TeleBot.send_animation:31 msgid "Mode for parsing entities in the animation caption" -msgstr "Режим форматирования подписи к гиф-ке" +msgstr "Режим форматирования подписи к гифке" #: of telebot.TeleBot.send_animation:51 telebot.TeleBot.send_video:29 msgid "" @@ -4065,7 +4065,7 @@ msgstr "id топика, в который будет отправлено ви #: of telebot.TeleBot.send_animation:60 #, fuzzy msgid "Pass True, if the animation should be sent as a spoiler" -msgstr "Передайте True, если гиф-ку нужно отправить как спойлер" +msgstr "Передайте True, если гифку нужно отправить как спойлер" #: of telebot.TeleBot.send_audio:1 msgid "" @@ -4077,7 +4077,7 @@ msgid "" msgstr "" "Используйте этот метод, чтобы отправить аудио, если вы хотите, чтобы клиенты " "(приложения) Telegram проигрывали их в музыкальном проигрывателе. Ваше аудио " -"должно быть в формате .MP3 или .M4A. В случае успеха, возвращается отправленное " +"должно быть в формате .MP3 или .M4A. В случае успеха возвращается отправленное " "сообщение (Message). На текущий момент, боты могут отправлять аудио весом до 50 MB, " "это ограничение может измениться в будущем." @@ -4161,7 +4161,7 @@ msgid "" "status). Returns True on success." msgstr "" "Используйте этот метод, когда вам нужно показать пользователю, что бот " -"что-то делает. Статус устанавливается на 5 секеунд или менее (когда от " +"что-то делает. Статус устанавливается на 5 секунд или менее (когда от " "бота приходит сообщение, клиенты (приложения) Telegram убирают статус typing). " "Возвращает True в случае успеха." @@ -4215,7 +4215,7 @@ msgid "" "Use this method to send phone contacts. On success, the sent Message is " "returned." msgstr "" -"Используйте этот метод, чтобы отправить контакт. В случае успеха, возвращается " +"Используйте этот метод, чтобы отправить контакт. В случае успеха возвращается " "отправленное сообщение (Message)." #: of telebot.TeleBot.send_contact:3 @@ -4257,7 +4257,7 @@ msgid "" "value. On success, the sent Message is returned." msgstr "" "Используйте этот метод, чтобы отправить анимированный эмодзи, который покажет " -"случайное значение. В случае успеха, возвращается отправленное сообщение (Message)." +"случайное значение. В случае успеха возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_dice:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" @@ -4446,7 +4446,7 @@ msgid "" "Use this method to send point on the map. On success, the sent Message is" " returned." msgstr "" -"Используйте этот метод, чтобы отправить точку на карте. В случае успеха, " +"Используйте этот метод, чтобы отправить точку на карте. В случае успеха " "возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_location:3 @@ -4496,7 +4496,7 @@ msgid "" msgstr "" "Используйте этот метод, чтобы отправить группу фото, видео, файлов или аудио " "как альбом. Файлы и аудио могут быть сгруппированы в альбом только с сообщениями " -"того же типа. В случае успеха, возвращается массив отправленных сообщений (Message)." +"того же типа. В случае успеха возвращается массив отправленных сообщений (Message)." #: of telebot.TeleBot.send_media_group:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" @@ -4524,11 +4524,11 @@ msgstr "id топика, в который будет отправлена гр #: of telebot.TeleBot.send_media_group:30 msgid "On success, an array of Messages that were sent is returned." -msgstr "В случае успеха, возвращается массив отправленных сообщений (Message)." +msgstr "В случае успеха возвращается массив отправленных сообщений (Message)." #: of telebot.TeleBot.send_message:1 msgid "Use this method to send text messages." -msgstr "" +msgstr "Используйте этот метод, чтобы отправлять текстовые сообщения." #: of telebot.TeleBot.send_message:3 msgid "" @@ -4536,34 +4536,43 @@ msgid "" "otherwise you'll risk an HTTP 414 error. If you must send more than 4096 " "characters, use the `split_string` or `smart_split` function in util.py." msgstr "" +"Предупреждение: Не отправляйте больше 4096 символов в одном сообщении, " +"иначе вы рискуете получить ошибку HTTP 414. Если вам нужно отправить больше " +"4096 символов, используйте функцию `split_string` или `smart_split` из util.py." #: of telebot.TeleBot.send_message:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendmessage" #: of telebot.TeleBot.send_message:12 msgid "Text of the message to be sent" -msgstr "" +msgstr "Текст сообщения для отправки" #: of telebot.TeleBot.send_message:18 msgid "" "List of special entities that appear in message text, which can be " "specified instead of parse_mode" msgstr "" +"Список отформатированных частей в тексте сообщения, " +"можно использовать вместо parse_mode" #: of telebot.TeleBot.send_message:27 msgid "" "If True, the message content will be hidden for all users except for the " "target user" msgstr "" +"Если True, содержимое сообщения будет скрыто от всех пользователей, кроме " +"заданного" #: of telebot.TeleBot.send_photo:1 msgid "Use this method to send photos. On success, the sent Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить фото. В случае успеха возвращается " +"отправленное сообщение (Message)." #: of telebot.TeleBot.send_photo:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendphoto" #: of telebot.TeleBot.send_photo:8 msgid "" @@ -4574,63 +4583,74 @@ msgid "" " width and height must not exceed 10000 in total. Width and height ratio " "must be at most 20." msgstr "" +"Фото для отправки. Передайте file_id (String), чтобы отправить фото, " +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить фото из интернета или загрузите новое с помощью " +"multipart/form-data. Фото должно весить не более 10 MB. Ширина и высота фото не " +"должны суммарно превышать 10000. Отношение ширины и высоты должно быть не более 20." #: of telebot.TeleBot.send_photo:13 msgid "" "Photo caption (may also be used when resending photos by file_id), 0-1024" " characters after entities parsing" msgstr "" +"Подпись к фото (может быть использована при повторной отправке файла по file_id), " +"0-1024 символа после форматирования" #: of telebot.TeleBot.send_photo:16 msgid "Mode for parsing entities in the photo caption." -msgstr "" +msgstr "Режим форматирования подписи к фото." #: of telebot.TeleBot.send_photo:45 #, fuzzy msgid "Pass True, if the photo should be sent as a spoiler" -msgstr "" -"Передайте True, если номер телефона пользователя нужно отправить " -"платежной системе" +msgstr "Передайте True, если фото должно быть отправлено как спойлер" #: of telebot.TeleBot.send_poll:1 msgid "" "Use this method to send a native poll. On success, the sent Message is " "returned." msgstr "" +"Используйте этот метод, чтобы отправить опрос. В случае успеха возвращается " +"отправленное сообщение (Message)." #: of telebot.TeleBot.send_poll:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendpoll" #: of telebot.TeleBot.send_poll:9 msgid "Poll question, 1-300 characters" -msgstr "" +msgstr "Тема опроса, 1-300 символов" #: of telebot.TeleBot.send_poll:12 msgid "" "A JSON-serialized list of answer options, 2-10 strings 1-100 characters " "each" -msgstr "" +msgstr "JSON-сериализованный список вариантов ответа, 2-10 строк по 1-100 символов" #: of telebot.TeleBot.send_poll:15 msgid "True, if the poll needs to be anonymous, defaults to True" -msgstr "" +msgstr "True, если опрос должен быть анонимным, по умолчанию True" #: of telebot.TeleBot.send_poll:18 msgid "Poll type, “quiz” or “regular”, defaults to “regular”" -msgstr "" +msgstr "Вид опроса, “quiz” или “regular”, по умолчанию “regular”" #: of telebot.TeleBot.send_poll:21 msgid "" "True, if the poll allows multiple answers, ignored for polls in quiz " "mode, defaults to False" msgstr "" +"True, если опрос позволяет выбрать несколько вариантов ответа, игнорируется в " +"опросах вида “quiz”, по умолчанию False" #: of telebot.TeleBot.send_poll:24 msgid "" "0-based identifier of the correct answer option. Available only for polls" " in quiz mode, defaults to None" msgstr "" +"Индекс правильного варианта ответа, начиная с 0. Доступно только для опросов " +"вида “quiz”, по умолчанию None" #: of telebot.TeleBot.send_poll:28 msgid "" @@ -4638,56 +4658,70 @@ msgid "" " lamp icon in a quiz-style poll, 0-200 characters with at most 2 line " "feeds after entities parsing" msgstr "" +"Текст, который будет показан при выборе неправильно варианта ответа или " +"нажатии на иконку лампочки в опросах вида “quiz”, 0-200 символов и не " +"более 2 строк после форматирования" #: of telebot.TeleBot.send_poll:32 msgid "" "Mode for parsing entities in the explanation. See formatting options for " "more details." msgstr "" +"Режим форматирования explanation. См. formatting options для получения " +"подробностей." #: of telebot.TeleBot.send_poll:35 msgid "" "Amount of time in seconds the poll will be active after creation, 5-600. " "Can't be used together with close_date." msgstr "" +"Время в секундах, в течение которого опрос будет активен, 5-600. " +"Нельзя использовать вместо с close_date." #: of telebot.TeleBot.send_poll:38 msgid "Point in time (Unix timestamp) when the poll will be automatically closed." -msgstr "" +msgstr "Время (UNIX timestamp), когда опрос будет автоматически завершен." #: of telebot.TeleBot.send_poll:41 msgid "" "Pass True, if the poll needs to be immediately closed. This can be useful" " for poll preview." msgstr "" +"Передайте True, если опрос должен быть завершен немедленно. Может быть полезно " +"для предпросмотра опроса." #: of telebot.TeleBot.send_poll:50 msgid "Pass True, if the poll allows multiple options to be voted simultaneously." -msgstr "" +msgstr "Передайте True, если опрос позволяет выбрать несколько вариантов одновременно." #: of telebot.TeleBot.send_poll:57 msgid "Timeout in seconds for waiting for a response from the user." -msgstr "" +msgstr "Тайм-аут в секундах, ожидание ответа от пользователя." #: of telebot.TeleBot.send_poll:60 msgid "" "A JSON-serialized list of special entities that appear in the " "explanation, which can be specified instead of parse_mode" msgstr "" +"JSON-сериализованный список отформатированных частей explanation, " +"можно использовать вместо parse_mode" #: of telebot.TeleBot.send_poll:67 msgid "The identifier of a message thread, in which the poll will be sent" -msgstr "" +msgstr "id топика, в который будет отправлен опрос" #: of telebot.TeleBot.send_sticker:1 msgid "" "Use this method to send static .WEBP, animated .TGS, or video .WEBM " "stickers. On success, the sent Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить статичный .WEBP, анимированный .TGS, " +"или видео .WEBM стикер. В случае успеха возвращает отправленное " +"сообщение (Message)." #: of telebot.TeleBot.send_sticker:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendsticker" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendsticker" #: of telebot.TeleBot.send_sticker:9 msgid "" @@ -4696,40 +4730,46 @@ msgid "" "Telegram to get a .webp file from the Internet, or upload a new one using" " multipart/form-data." msgstr "" +"Стикер для отправки. Передайте file_id (String), чтобы отправить файл, " +"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить .webp файл из интернета или загрузите новый с помощью " +"multipart/form-data." #: of telebot.TeleBot.send_sticker:21 msgid "to disable the notification" -msgstr "" +msgstr "отключить уведомление" #: of telebot.TeleBot.send_venue:1 msgid "" "Use this method to send information about a venue. On success, the sent " "Message is returned." msgstr "" +"Используйте этот метод, чтобы отправить информацию о месте. В случае успеха " +"возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_venue:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvenue" #: of telebot.TeleBot.send_venue:8 msgid "Latitude of the venue" -msgstr "" +msgstr "Широта" #: of telebot.TeleBot.send_venue:11 msgid "Longitude of the venue" -msgstr "" +msgstr "Долгота" #: of telebot.TeleBot.send_venue:14 msgid "Name of the venue" -msgstr "" +msgstr "Название места" #: of telebot.TeleBot.send_venue:17 msgid "Address of the venue" -msgstr "" +msgstr "Адрес места" #: of telebot.TeleBot.send_venue:20 msgid "Foursquare identifier of the venue" -msgstr "" +msgstr "id места на Foursquare" #: of telebot.TeleBot.send_venue:23 msgid "" @@ -4737,24 +4777,29 @@ msgid "" "“arts_entertainment/default”, “arts_entertainment/aquarium” or " "“food/icecream”.)" msgstr "" +"Тип места на Foursquare, если известен. (Например, " +"“arts_entertainment/default”, “arts_entertainment/aquarium” или " +"“food/icecream”.)" #: of telebot.TeleBot.send_venue:45 msgid "Google Places identifier of the venue" -msgstr "" +msgstr "id места на Google Places" #: of telebot.TeleBot.send_venue:48 msgid "Google Places type of the venue." -msgstr "" +msgstr "Тип места на Google Places." #: of telebot.TeleBot.send_video:1 msgid "" "Use this method to send video files, Telegram clients support mp4 videos " "(other formats may be sent as Document)." msgstr "" +"Используйте этот метод, чтобы отправить видео, клиенты (приложения) Telegram " +"поддерживают mp4 видео (другие форматы могут быть отправлены как Document)." #: of telebot.TeleBot.send_video:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvideo" #: of telebot.TeleBot.send_video:8 msgid "" @@ -4762,39 +4807,42 @@ msgid "" "that is already on the Telegram servers, or upload a new video file using" " multipart/form-data." msgstr "" +"Видео для отправки. Передайте file_id (String), чтобы отправить видео, " +"которое уже загружено на сервера Telegram или загрузите новое с помощью " +"multipart/form-data." #: of telebot.TeleBot.send_video:11 telebot.TeleBot.send_video_note:13 msgid "Duration of sent video in seconds" -msgstr "" +msgstr "Длительность отправленного видео в секундах" #: of telebot.TeleBot.send_video:14 msgid "Video width" -msgstr "" +msgstr "Ширина видео" #: of telebot.TeleBot.send_video:17 msgid "Video height" -msgstr "" +msgstr "Высота видео" #: of telebot.TeleBot.send_video:23 msgid "" "Video caption (may also be used when resending videos by file_id), 0-1024" " characters after entities parsing" msgstr "" +"Подпись к видео (может быть использована при повторной отправке файла по file_id), " +"0-1024 символа после форматирования" #: of telebot.TeleBot.send_video:26 msgid "Mode for parsing entities in the video caption" -msgstr "" +msgstr "Режим форматирования подписи к видео" #: of telebot.TeleBot.send_video:32 msgid "Pass True, if the uploaded video is suitable for streaming" -msgstr "" +msgstr "Передайте True, если загруженное видео подходит для стриминга" #: of telebot.TeleBot.send_video:61 #, fuzzy msgid "Pass True, if the video should be sent as a spoiler" -msgstr "" -"Передайте True, если номер телефона пользователя нужно отправить " -"платежной системе" +msgstr "Передайте True, если видео должно быть отправлено как спойлер" #: of telebot.TeleBot.send_video_note:1 msgid "" @@ -4802,10 +4850,14 @@ msgid "" "to 1 minute long. Use this method to send video messages. On success, the" " sent Message is returned." msgstr "" +"Начиная с версии v.4.0, клиенты(приложения) Telegram поддерживают скругленные " +"квадратные MPEG4 видео длительностью до минуты. Используйте этот метод, чтобы " +"отправить видео заметку (кружочек). В случае успеха возвращается отправленное " +"сообщение (Message)." #: of telebot.TeleBot.send_video_note:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvideonote" #: of telebot.TeleBot.send_video_note:9 msgid "" @@ -4814,14 +4866,18 @@ msgid "" "multipart/form-data. Sending video notes by a URL is currently " "unsupported" msgstr "" +"Видео заметка для отправки. Передайте file_id (String), чтобы отправить видео " +"заметку, которая уже загружена на сервера Telegram или загрузите новую с помощью " +"multipart/form-data. На текущий момент, отправка видео заметок по URL не " +"поддерживается" #: of telebot.TeleBot.send_video_note:16 msgid "Video width and height, i.e. diameter of the video message" -msgstr "" +msgstr "Ширина и высота видео (диаметр видео сообщения)" #: of telebot.TeleBot.send_video_note:45 msgid "Identifier of a message thread, in which the video note will be sent" -msgstr "" +msgstr "id топика, в который будет отправлена видео заметка" #: of telebot.TeleBot.send_voice:1 msgid "" @@ -4832,10 +4888,16 @@ msgid "" "Bots can currently send voice messages of up to 50 MB in size, this limit" " may be changed in the future." msgstr "" +"Используйте этот метод, чтобы отправить голосовое сообщение. Ваше аудио " +"должно быть в формате .OGG и закодировано с помощью OPUS (другие форматы " +"можно отправить как Audio или Document). В случае успеха возвращается " +"отправленное сообщение (Message). На текущий момент, боты могут отправлять " +"голосовые сообщения весом до 50 MB, это ограничение может быть изменено в " +"будущем." #: of telebot.TeleBot.send_voice:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvoice" #: of telebot.TeleBot.send_voice:10 msgid "" @@ -4844,38 +4906,50 @@ msgid "" "Telegram to get a file from the Internet, or upload a new one using " "multipart/form-data." msgstr "" +"Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " +"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " +"multipart/form-data." #: of telebot.TeleBot.send_voice:14 msgid "Voice message caption, 0-1024 characters after entities parsing" -msgstr "" +msgstr "Подпись к голосовому сообщению, 0-1024 символа после форматирования" #: of telebot.TeleBot.send_voice:17 msgid "Duration of the voice message in seconds" -msgstr "" +msgstr "Длительность голосового сообщения в секундах" #: of telebot.TeleBot.send_voice:28 msgid "" "Mode for parsing entities in the voice message caption. See formatting " "options for more details." msgstr "" +"Режим форматирования подписи к голосовому сообщению. См. formatting options " +"для получения подробностей." #: of telebot.TeleBot.set_chat_administrator_custom_title:1 msgid "" "Use this method to set a custom title for an administrator in a " "supergroup promoted by the bot. Returns True on success." msgstr "" +"Используйте этот метод, чтобы задать кастомное звание администратора " +"супергруппы, повышенного ботом. Возвращает True в случае успеха." #: of telebot.TeleBot.set_chat_administrator_custom_title:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatadministratorcustomtitle" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatadministratorcustomtitle" #: of telebot.TeleBot.set_chat_administrator_custom_title:13 msgid "" "New custom title for the administrator; 0-16 characters, emoji are not " "allowed" msgstr "" +"Новое кастомное звание администратора; 0-16 символов, эмодзи не " +"разрешены" #: of telebot.TeleBot.set_chat_description:1 msgid "" @@ -4883,40 +4957,51 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights." msgstr "" +"Используйте этот метод, чтобы изменить описание супергруппы или канала. " +"Бот должен быть администратором чата и иметь соответствующие права " +"администратора." #: of telebot.TeleBot.set_chat_description:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatdescription" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatdescription" #: of telebot.TeleBot.set_chat_description:10 msgid "Str: New chat description, 0-255 characters" -msgstr "" +msgstr "Str: Новое описание чата, 0-255 символов" #: of telebot.TeleBot.set_chat_menu_button:1 msgid "" "Use this method to change the bot's menu button in a private chat, or the" " default menu button. Returns True on success." msgstr "" +"Используйте этот метод, чтобы изменить кнопку меню в приватном чате или " +"кнопку меню по умолчанию. Возвращает True в случае успеха." #: of telebot.TeleBot.set_chat_menu_button:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatmenubutton" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatmenubutton" #: of telebot.TeleBot.set_chat_menu_button:7 msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be changed." msgstr "" +"Уникальный id приватного чата. Если не указан, будет изменена кнопка меню " +"по умолчанию." #: of telebot.TeleBot.set_chat_menu_button:11 msgid "" "A JSON-serialized object for the new bot's menu button. Defaults to " "MenuButtonDefault" -msgstr "" +msgstr "JSON-сериализованный объект новой кнопки меню. По умолчанию MenuButtonDefault" #: of telebot.TeleBot.set_chat_permissions:1 msgid "" @@ -4924,16 +5009,21 @@ msgid "" "must be an administrator in the group or a supergroup for this to work " "and must have the can_restrict_members admin rights." msgstr "" +"Используйте этот метод, чтобы задать права по умолчанию для всех участников чата. " +"Бот должен быть администратором группы или супергруппы и иметь права администратора " +"can_restrict_members." #: of telebot.TeleBot.set_chat_permissions:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatpermissions" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatpermissions" #: of telebot.TeleBot.set_chat_permissions:11 msgid "New default chat permissions" -msgstr "" +msgstr "Новые права по умолчанию" #: of telebot.TeleBot.set_chat_photo:1 msgid "" @@ -4944,14 +5034,19 @@ msgid "" "only work if the ‘All Members Are Admins’ setting is off in the target " "group." msgstr "" +"Используйте этот метод, чтобы задать новую аватарку чата. В приватных чатах " +"аватарки менять нельзя. Бот должен быть администратором чата и иметь " +"соответствующие права администратора. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппы), этот метод будет работать " +"только если настройка ‘All Members Are Admins’ отключена." #: of telebot.TeleBot.set_chat_photo:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setchatphoto" #: of telebot.TeleBot.set_chat_photo:13 msgid "InputFile: New chat photo, uploaded using multipart/form-data" -msgstr "" +msgstr "InputFile: Новая аватарка чата, загруженная с помощью multipart/form-data" #: of telebot.TeleBot.set_chat_sticker_set:1 msgid "" @@ -4961,20 +5056,26 @@ msgid "" "optionally returned in getChat requests to check if the bot can use this " "method. Returns True on success." msgstr "" +"Используйте этот метод, чтобы задать стикерпак супергруппы. Бот должен быть " +"администратором чата и иметь соответствующие права администратора. " +"Используйте атрибут can_set_sticker_set, возвращаемые методом getChat, чтобы " +"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." #: of telebot.TeleBot.set_chat_sticker_set:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setchatstickerset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatstickerset" #: of telebot.TeleBot.set_chat_sticker_set:10 msgid "Name of the sticker set to be set as the group sticker set" -msgstr "" +msgstr "Имя стикерпака для установки в качестве стикерпака группы" #: of telebot.TeleBot.set_chat_sticker_set:13 msgid "StickerSet object" -msgstr "" +msgstr "Объект StickerSet" #: of telebot.TeleBot.set_chat_title:1 msgid "" @@ -4984,56 +5085,67 @@ msgid "" "success. Note: In regular groups (non-supergroups), this method will only" " work if the ‘All Members Are Admins’ setting is off in the target group." msgstr "" +"Используйте этот метод, чтобы изменить название чата. В приватных чатах " +"изменить название нельзя. Бот должен быть администратором чата и иметь " +"соответствующие права админа. Возвращает True в случае успеха. " +"Примечание: В обычных группах (не супергруппы), этот метод будет работать " +"только если настройка ‘All Members Are Admins’ отключена." #: of telebot.TeleBot.set_chat_title:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setchattitle" #: of telebot.TeleBot.set_chat_title:13 msgid "New chat title, 1-255 characters" -msgstr "" +msgstr "Новое название чата, 1-255 символов" #: of telebot.TeleBot.set_game_score:1 msgid "Sets the value of points in the game to a specific user." -msgstr "" +msgstr "Задаёт количество очков пользователя в игре." #: of telebot.TeleBot.set_game_score:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#setgamescore" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setgamescore" #: of telebot.TeleBot.set_game_score:8 msgid "New score, must be non-negative" -msgstr "" +msgstr "Количество очков, должно быть неотрицательным" #: of telebot.TeleBot.set_game_score:11 msgid "" "Pass True, if the high score is allowed to decrease. This can be useful " "when fixing mistakes or banning cheaters" msgstr "" +"Передайте True, если количество очков могут быть уменьшено. Может быть полезно " +"при исправлении ошибок или бане читеров" #: of telebot.TeleBot.set_game_score:23 msgid "" "Pass True, if the game message should not be automatically edited to " "include the current scoreboard" msgstr "" +"Передайте True, если сообщение с игрой должно быть автоматически " +"отредактировано, чтобы отобразить новый результат" #: of telebot.TeleBot.set_game_score:26 msgid "" "On success, if the message was sent by the bot, returns the edited " "Message, otherwise returns True." msgstr "" +"В случае успеха, если сообщение было отправлено ботом, возвращает измененное " +"сообщение (Message), иначе возвращает True." #: of telebot.TeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." -msgstr "" +msgstr "Используйте этот метод, чтобы изменить список команд бота." #: of telebot.TeleBot.set_my_commands:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#setmycommands" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setmycommands" #: of telebot.TeleBot.set_my_commands:5 msgid "List of BotCommand. At most 100 commands can be specified." -msgstr "" +msgstr "Список объектов BotCommand. Можно задать не более 100 команд." #: of telebot.TeleBot.set_my_default_administrator_rights:1 msgid "" @@ -5042,18 +5154,26 @@ msgid "" "rights will be suggested to users, but they are are free to modify the " "list before adding the bot. Returns True on success." msgstr "" +"Используйте этот метод, чтобы изменить права администратора по умолчанию, " +"запрашиваемые при добавлении бота в группу или канал в качестве администратора. " +"Эти права будут предложены пользователям, но пользователи могут изменить список " +"перед добавлением бота. Возвращает True в случае успеха." #: of telebot.TeleBot.set_my_default_administrator_rights:7 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setmydefaultadministratorrights" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setmydefaultadministratorrights" #: of telebot.TeleBot.set_my_default_administrator_rights:9 msgid "" "A JSON-serialized object describing new default administrator rights. If " "not specified, the default administrator rights will be cleared." msgstr "" +"JSON-сериалиованный объект, описывающий новые права администратора по умолчанию. " +"Если не указан, права администратора по умолчанию будут сброшены." #: of telebot.TeleBot.set_my_default_administrator_rights:13 msgid "" @@ -5061,10 +5181,12 @@ msgid "" "channels. Otherwise, the default administrator rights of the bot for " "groups and supergroups will be changed." msgstr "" +"Передайте True, чтобы изменить права администратора по умолчанию в каналах. " +"Иначе, будут изменены права администратора по умолчанию для групп и супергрупп." #: of telebot.TeleBot.set_state:1 msgid "Sets a new state of a user." -msgstr "" +msgstr "Задаёт новое состояние (стейт) пользователя." #: of telebot.TeleBot.set_state:5 msgid "" @@ -5073,26 +5195,34 @@ msgid "" "user_id, this means that state will be set for the user in his private " "chat with a bot." msgstr "" +"Вы должны указать и user id и chat id, чтобы задать состояние (стейт) " +"пользователя в чате. Иначе, если вы укажете только user_id, chat_id будет " +"равен user_id, что означает смену состояния (стейта) пользователя в его " +"приватном чате с ботом." #: of telebot.TeleBot.set_state:12 msgid "new state. can be string, integer, or :class:`telebot.types.State`" -msgstr "" +msgstr "новое состояние (стейт). может быть строкой, числом или :class:`telebot.types.State`" #: of telebot.TeleBot.set_sticker_position_in_set:1 msgid "" "Use this method to move a sticker in a set created by the bot to a " "specific position . Returns True on success." msgstr "" +"Используйте этот метод, чтобы передвинуть стикер в стикерпаке, созданном ботом, на заданную " +"позицию. Возвращает True в случае успеха." #: of telebot.TeleBot.set_sticker_position_in_set:3 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickerpositioninset" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setstickerpositioninset" #: of telebot.TeleBot.set_sticker_position_in_set:8 msgid "New sticker position in the set, zero-based" -msgstr "" +msgstr "Новая позиция стикера в стикерпаке, начиная с нуля" #: of telebot.TeleBot.set_sticker_set_thumb:1 msgid "" @@ -5100,20 +5230,25 @@ msgid "" "thumbnails can be set for animated sticker sets only. Returns True on " "success." msgstr "" +"Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " +"обложки могут быть заданы только для анимированных стикерпаков. Возвращает " +"True в случае успеха." #: of telebot.TeleBot.set_sticker_set_thumb:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickersetthumb" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setstickersetthumb" #: of telebot.TeleBot.set_update_listener:1 msgid "Sets a listener function to be called when a new update is received." -msgstr "" +msgstr "Задаёт функцию-листенер, которая будет вызвана при получении нового апдейта." #: of telebot.TeleBot.set_update_listener:3 msgid "Listener function." -msgstr "" +msgstr "Функция-листенер." #: of telebot.TeleBot.set_webhook:1 msgid "" @@ -5123,6 +5258,11 @@ msgid "" "Update. In case of an unsuccessful request, we will give up after a " "reasonable amount of attempts. Returns True on success." msgstr "" +"Используйте этот метод, чтобы задать URL и получать входящие апдейты с " +"помощью вебхука. Как только у бота появляется апдейт, он будет отправлен с " +"помощью HTTPS POST запроса на заданный URL, содержащего JSON-сериализованный " +"Update. В случае неудачного запроса, отправка апдейта будет отменена после " +"разумного числа попыток. Возвращает True в случае успеха." #: of telebot.TeleBot.set_webhook:6 msgid "" @@ -5131,22 +5271,30 @@ msgid "" "request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the " "secret token as content." msgstr "" +"Если вы хотите удостовериться, что вебхук был задан вами, вы можете " +"задать секретный токен в параметре secret_token. Если указан, запрос " +"с апдейтом будет содержать хедер “X-Telegram-Bot-Api-Secret-Token” с " +"секретным токеном в качестве значения." #: of telebot.TeleBot.set_webhook:9 msgid "Telegram Documentation: https://core.telegram.org/bots/api#setwebhook" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#setwebhook" #: of telebot.TeleBot.set_webhook:11 msgid "" "HTTPS URL to send updates to. Use an empty string to remove webhook " "integration, defaults to None" msgstr "" +"HTTPS URL для отправки апдейтов. Используйте пустую строку, чтобы удалить " +"вебхук, по умолчанию None" #: of telebot.TeleBot.set_webhook:14 msgid "" "Upload your public key certificate so that the root certificate in use " "can be checked, defaults to None" msgstr "" +"Загрузите публичный ключ вашего SSL сертификата, чтобы корневой сертификат мог быть " +"проверен, по умолчанию None" #: of telebot.TeleBot.set_webhook:17 msgid "" @@ -5155,6 +5303,10 @@ msgid "" "limit the load on your bot's server, and higher values to increase your " "bot's throughput, defaults to None" msgstr "" +"Максимально-допустимое количество одновременных HTTPS соединений для доставки " +"апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения для уменьшения " +"нагрузки на ваш сервер и большие значения, чтобы увеличить пропускную способность " +"вашего бота, по умолчанию None" #: of telebot.TeleBot.set_webhook:22 msgid "" @@ -5167,6 +5319,14 @@ msgid "" "updates created before the call to the setWebhook, so unwanted updates " "may be received for a short period of time. Defaults to None" msgstr "" +"Список видов апдейтов, которые вы хотите получать, в формате JSON. " +"Например, укажите [“message”, “edited_channel_post”, “callback_query”], " +"чтобы получать апдейты только этих видов. Полный список доступных видов " +"апдейтов - util.update_types. Укажите пустой список, чтобы получать все " +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка. Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " +"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " +"получены в течение короткого периода времени. По умолчанию None" #: of telebot.TeleBot.set_webhook:22 msgid "" @@ -5177,6 +5337,12 @@ msgid "" "types except chat_member (default). If not specified, the previous " "setting will be used." msgstr "" +"Список видов апдейтов, которые вы хотите получать, в формате JSON. " +"Например, укажите [“message”, “edited_channel_post”, “callback_query”], " +"чтобы получать апдейты только этих видов. Полный список доступных видов " +"апдейтов - util.update_types. Укажите пустой список, чтобы получать все " +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " +"последняя настройка." #: of telebot.TeleBot.set_webhook:27 msgid "" @@ -5184,6 +5350,9 @@ msgid "" " call to the setWebhook, so unwanted updates may be received for a short " "period of time. Defaults to None" msgstr "" +"Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " +"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " +"получены в течение короткого периода времени. По умолчанию None" #: of telebot.TeleBot.set_webhook:42 msgid "" @@ -5192,18 +5361,22 @@ msgid "" "0-9, _ and - are allowed. The header is useful to ensure that the request" " comes from a webhook set by you. Defaults to None" msgstr "" +"Секретный токен для отправки в хедере “X-Telegram-Bot-Api-Secret-Token” " +"в каждом запросе с апдейтом, 1-256 символов. Разрешены только символы A-Z, a-z, " +"0-9, _ и -. Хедер полезен для, того чтобы удостовериться, что запрос приходит" +"с вебхука, установленного вами. По умолчанию None" #: of telebot.TeleBot.set_webhook:47 msgid ":obj:`bool` if the request was successful." -msgstr "" +msgstr ":obj:`bool` если запрос был успешным." #: of telebot.TeleBot.setup_middleware:1 msgid "Registers class-based middleware." -msgstr "" +msgstr "Регистрирует класс-middleware" #: of telebot.TeleBot.setup_middleware:3 msgid "Subclass of :class:`telebot.handler_backends.BaseMiddleware`" -msgstr "" +msgstr "Наследник класса :class:`telebot.handler_backends.BaseMiddleware`" #: of telebot.TeleBot.shipping_query_handler:1 msgid "" @@ -5211,10 +5384,13 @@ msgid "" "price. As a parameter to the decorator function, it passes " ":class:`telebot.types.ShippingQuery` object." msgstr "" +"Обрабатывает shipping query. Только для инвойсов с гибкой ценой. " +"В качестве параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.ShippingQuery`." #: of telebot.TeleBot.stop_bot:1 msgid "Stops bot by stopping polling and closing the worker pool." -msgstr "" +msgstr "Останавливает работу бота и закрывает рабочий пул." #: of telebot.TeleBot.stop_message_live_location:1 msgid "" @@ -5222,52 +5398,65 @@ msgid "" "live_period expires. On success, if the message is not an inline message," " the edited Message is returned, otherwise True is returned." msgstr "" +"Используйте этот метод, чтобы остановить обновление live местоположения до " +"истечения live_period. В случае успеха, если сообщение не является inline сообщением," +"возвращается измененное сообщение (Message), иначе возвращается True." #: of telebot.TeleBot.stop_message_live_location:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#stopmessagelivelocation" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#stopmessagelivelocation" #: of telebot.TeleBot.stop_message_live_location:9 msgid "" "Required if inline_message_id is not specified. Identifier of the message" " with live location to stop" msgstr "" +"Обязательный, если не указан inline_message_id. id сообщения live местоположением, " +"которое нужно остановить" #: of telebot.TeleBot.stop_message_live_location:12 msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message with live location to stop" msgstr "" +"Обязательный, если не указаны chat_id и message_id. id inline сообщения с live " +"местоположением, которое нужно остановить" #: of telebot.TeleBot.stop_message_live_location:22 msgid "" "On success, if the message is not an inline message, the edited Message " "is returned, otherwise True is returned." msgstr "" +"В случае успеха, если сообщение не является inline сообщением, возвращается " +"измененное сообщение (Message), иначе возвращается True." #: of telebot.TeleBot.stop_poll:1 msgid "" "Use this method to stop a poll which was sent by the bot. On success, the" " stopped Poll is returned." msgstr "" +"Используйте этот метод, чтобы завершить опрос, отправленный ботом. В случае успеха " +"возвращается завершенный опрос (Poll)." #: of telebot.TeleBot.stop_poll:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#stoppoll" #: of telebot.TeleBot.stop_poll:8 msgid "Identifier of the original message with the poll" -msgstr "" +msgstr "id сообщения с опросом" #: of telebot.TeleBot.stop_poll:11 msgid "A JSON-serialized object for a new message markup." -msgstr "" +msgstr "JSON-сериализованный объект новой inline клавиатуры." #: of telebot.TeleBot.stop_poll:14 msgid "On success, the stopped Poll is returned." -msgstr "" +msgstr "В случае успеха возвращается завершенный опрос (Poll)." #: of telebot.TeleBot.stop_poll:15 msgid ":obj:`types.Poll`" @@ -5275,11 +5464,11 @@ msgstr "" #: of telebot.TeleBot.stop_polling:1 msgid "Stops polling." -msgstr "" +msgstr "Останавливает поллинг." #: of telebot.TeleBot.stop_polling:3 msgid "Does not accept any arguments." -msgstr "" +msgstr "Не принимает никаких аргументов." #: of telebot.TeleBot.unban_chat_member:1 msgid "" @@ -5291,20 +5480,27 @@ msgid "" "the user is a member of the chat they will also be removed from the chat." " If you don't want this, use the parameter only_if_banned." msgstr "" +"Используйте этот метод, чтобы разбанить ранее кикнутого пользователя в " +"супергруппе или канале. Пользовать не вернется в группу или канал автоматически, " +"но сможет присоединиться с помощью ссылки и т.д. Бот должен быть администратором. " +"По умолчанию, этот метод гарантирует, что после вызова, пользователь не является " +"участником чата, но может присоединиться. Поэтому если пользовать является участником " +"чата, он будет кикнут, но не забанен. Если вы хотите изменить это поведение, " +"используйте параметр only_if_banned." #: of telebot.TeleBot.unban_chat_member:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#unbanchatmember" -msgstr "" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#unbanchatmember" #: of telebot.TeleBot.unban_chat_member:9 msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @username)" -msgstr "" +msgstr "Уникальный id группы или username супергруппы или канала (в формате @username)" #: of telebot.TeleBot.unban_chat_member:16 msgid "Do nothing if the user is not banned" -msgstr "" +msgstr "Ничего не делать, если пользователь не забанен" #: of telebot.TeleBot.unban_chat_sender_chat:1 msgid "" @@ -5312,16 +5508,21 @@ msgid "" " or channel. The bot must be an administrator for this to work and must " "have the appropriate administrator rights. Returns True on success." msgstr "" +"Используйте этот метод, чтобы разбанить ране забаненный канал в супергруппе " +"или канала. Бот должен быть администратором и иметь соответствующие права " +"администратора. Возвращает True в случае успеха." #: of telebot.TeleBot.unban_chat_sender_chat:6 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unbanchatsenderchat" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unbanchatsenderchat" #: of telebot.TeleBot.unban_chat_sender_chat:11 msgid "Unique identifier of the target sender chat." -msgstr "" +msgstr "Уникальный id чата." #: of telebot.TeleBot.unhide_general_forum_topic:1 #, fuzzy @@ -5330,17 +5531,18 @@ msgid "" " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы удалить топик в супергруппе. Бот должен " -"быть администратором чата и иметь права администратора can_manage_topics," -" за исключением случае, когда бот является создателем топика. Возвращает " -"True в случае успеха." +"Используйте этот метод, чтобы сделать топик 'General' видимым в супергруппе " +"с топиками. Бот должен быть администратором чата и иметь права администратора " +"can_manage_topics. Возвращает True в случае успеха." #: of telebot.TeleBot.unhide_general_forum_topic:5 #, fuzzy msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unhidegeneralforumtopic" -msgstr "Документация Telegram: https://core.telegram.org/bots/api#deleteforumtopic" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unhidegeneralforumtopic" #: of telebot.TeleBot.unpin_all_chat_messages:1 msgid "" @@ -5348,12 +5550,17 @@ msgid "" "bot must be an administrator in the chat for this to work and must have " "the appropriate admin rights. Returns True on success." msgstr "" +"Используйте этот метод, что открепить все закрепленные сообщения в супергруппе. " +"Бот должен быть администратором чата и иметь соответствующие права администратора. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.unpin_all_chat_messages:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinallchatmessages" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinallchatmessages" #: of telebot.TeleBot.unpin_all_forum_topic_messages:1 msgid "" @@ -5362,16 +5569,21 @@ msgid "" "have the can_pin_messages administrator right in the supergroup. Returns " "True on success." msgstr "" +"Используйте этот метод, что открепить все закрепленные сообщения в топике. " +"Бот должен быть администратором чата и иметь права администратора " +"can_pin_messages в супергруппе. Возвращает True в случае успеха." #: of telebot.TeleBot.unpin_all_forum_topic_messages:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinallforumtopicmessages" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" #: of telebot.TeleBot.unpin_all_forum_topic_messages:10 msgid "Identifier of the topic" -msgstr "" +msgstr "id топика" #: of telebot.TeleBot.unpin_chat_message:1 msgid "" @@ -5379,16 +5591,21 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights. Returns True on success." msgstr "" +"Используйте этот метод, что открепить закрепленное сообщение в супергруппе. " +"Бот должен быть администратором чата и иметь соответствующие права администратора. " +"Возвращает True в случае успеха." #: of telebot.TeleBot.unpin_chat_message:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinchatmessage" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinchatmessage" #: of telebot.TeleBot.unpin_chat_message:11 msgid "Int: Identifier of a message to unpin" -msgstr "" +msgstr "Int: id сообщения, которое нужно открепить" #: of telebot.TeleBot.upload_sticker_file:1 msgid "" @@ -5396,16 +5613,21 @@ msgid "" "createNewStickerSet and addStickerToSet methods (can be used multiple " "times). Returns the uploaded File on success." msgstr "" +"Используйте этот метод, чтобы загрузить .png стикер, чтобы позже " +"использовать в методах createNewStickerSet и addStickerToSet (может быть " +"использован несколько раз). Возвращает загруженный файл (File) в случае успеха." #: of telebot.TeleBot.upload_sticker_file:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#uploadstickerfile" msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#uploadstickerfile" #: of telebot.TeleBot.upload_sticker_file:6 msgid "User identifier of sticker set owner" -msgstr "" +msgstr "id пользователя, создавшего стикерпак" #: of telebot.TeleBot.upload_sticker_file:9 msgid "" @@ -5413,20 +5635,24 @@ msgid "" "dimensions must not exceed 512px, and either width or height must be " "exactly 512px." msgstr "" +"PNG изображение стикера, должно весить не более 512 kB, измерения не " +"должны превышать 512px и либо ширина, либо высота должна быть ровно 512px." #: of telebot.TeleBot.upload_sticker_file:13 msgid "On success, the sent file is returned." -msgstr "" +msgstr "В случае успеха возвращается отправленный файл." #: of telebot.TeleBot.user:1 msgid "" "The User object representing this bot. Equivalent to bot.get_me() but the" " result is cached so only one API call is needed." msgstr "" +"Объект User, описывающий бота. Эквивалент bot.get_me(), но результат " +"кэшируется, поэтому нужен всего один запрос к API." #: of telebot.TeleBot.user:4 msgid "Bot's info." -msgstr "" +msgstr "Информация о боте" #: of telebot.TeleBot.user:5 msgid ":class:`telebot.types.User`" @@ -5434,12 +5660,12 @@ msgstr "" #: ../../source/sync_version/index.rst:17 msgid "custom_filters file" -msgstr "" +msgstr "Файл custom_filters" #: of telebot.custom_filters.AdvancedCustomFilter:1 #: telebot.custom_filters.SimpleCustomFilter:1 msgid "Bases: :py:class:`abc.ABC`" -msgstr "" +msgstr "Базовые классы: :py:class:`abc.ABC`" #: of telebot.custom_filters.AdvancedCustomFilter:1 msgid "" @@ -5448,20 +5674,23 @@ msgid "" " - filter failed. message: Message class text: Filter value given in " "handler" msgstr "" +"Базовый класс Advanced Custom Filter. Создайте класс наследник с методом check(). " +"Принимает два параметра, возвращает bool: True - фильтр пройден, False - фильтр " +"не пройден. message: класс Message text: значение фильтра, полученное в хендлере" #: of telebot.custom_filters.AdvancedCustomFilter:7 #: telebot.custom_filters.SimpleCustomFilter:5 msgid "Child classes should have .key property." -msgstr "" +msgstr "Классы наследники должны иметь статический атрибут (property) .key" #: of telebot.custom_filters.AdvancedCustomFilter:9 msgid "Example on creating an advanced custom filter." -msgstr "" +msgstr "Пример создания advanced custom filter." #: of telebot.custom_filters.AdvancedCustomFilter.check:1 #: telebot.custom_filters.SimpleCustomFilter.check:1 msgid "Perform a check." -msgstr "" +msgstr "Выполнить проверку." #: of telebot.custom_filters.ChatFilter:1 #: telebot.custom_filters.LanguageFilter:1 telebot.custom_filters.StateFilter:1 @@ -5469,11 +5698,11 @@ msgstr "" #: telebot.custom_filters.TextMatchFilter:1 #: telebot.custom_filters.TextStartsFilter:1 msgid "Bases: :py:class:`telebot.custom_filters.AdvancedCustomFilter`" -msgstr "" +msgstr "Базовые классы: :py:class:`telebot.custom_filters.AdvancedCustomFilter`" #: of telebot.custom_filters.ChatFilter:1 msgid "Check whether chat_id corresponds to given chat_id." -msgstr "" +msgstr "Проверяет, является ли chat_id заданным." #: of telebot.custom_filters.ChatFilter:3 #: telebot.custom_filters.ForwardFilter:3 @@ -5485,34 +5714,34 @@ msgstr "" #: telebot.custom_filters.TextMatchFilter:3 #: telebot.custom_filters.TextStartsFilter:3 msgid "Example on using this filter:" -msgstr "" +msgstr "Пример использования этого фильтра:" #: of telebot.custom_filters.ForwardFilter:1 #: telebot.custom_filters.IsAdminFilter:1 #: telebot.custom_filters.IsDigitFilter:1 #: telebot.custom_filters.IsReplyFilter:1 msgid "Bases: :py:class:`telebot.custom_filters.SimpleCustomFilter`" -msgstr "" +msgstr "Базовые классы: :py:class:`telebot.custom_filters.SimpleCustomFilter`" #: of telebot.custom_filters.ForwardFilter:1 msgid "Check whether message was forwarded from channel or group." -msgstr "" +msgstr "Проверяет, является ли сообщение пересланным из канала или группы." #: of telebot.custom_filters.IsAdminFilter:1 msgid "Check whether the user is administrator / owner of the chat." -msgstr "" +msgstr "Проверяет, является ли пользователь администратором / владельцем чата." #: of telebot.custom_filters.IsDigitFilter:1 msgid "Filter to check whether the string is made up of only digits." -msgstr "" +msgstr "Фильтр для проверки, состоит ли строка только из цифр." #: of telebot.custom_filters.IsReplyFilter:1 msgid "Check whether message is a reply." -msgstr "" +msgstr "Проверяет, является ли сообщение ответом (reply)." #: of telebot.custom_filters.LanguageFilter:1 msgid "Check users language_code." -msgstr "" +msgstr "Проверяет language_code пользователя." #: of telebot.custom_filters.SimpleCustomFilter:1 msgid "" @@ -5520,66 +5749,72 @@ msgid "" "Accepts only message, returns bool value, that is compared with given in " "handler." msgstr "" +"Базовый класс Simple Custom Filter. Создайте класс наследник с методом check(). " +"Принимает только сообщение, возвращает bool, который сравнивается с заданным в хендлере." #: of telebot.custom_filters.SimpleCustomFilter:7 msgid "Example on creating a simple custom filter." -msgstr "" +msgstr "Пример создания simple custom filter." #: of telebot.custom_filters.StateFilter:1 msgid "Filter to check state." -msgstr "" +msgstr "Фильтр для проверки состояния (стейта)." #: of telebot.custom_filters.TextContainsFilter:1 msgid "Filter to check Text message. key: text" -msgstr "" +msgstr "Фильтр для проверки текста сообщения. key: text" #: of telebot.custom_filters.TextFilter:1 msgid "" "Advanced text filter to check (types.Message, types.CallbackQuery, " "types.InlineQuery, types.Poll)" msgstr "" +"Advanced текстовый фильтр для проверки (types.Message, types.CallbackQuery, " +"types.InlineQuery, types.Poll)" #: of telebot.custom_filters.TextFilter:3 msgid "example of usage is in examples/custom_filters/advanced_text_filter.py" -msgstr "" +msgstr "пример использования в examples/custom_filters/advanced_text_filter.py" #: of telebot.custom_filters.TextFilter:5 msgid "string, True if object's text is equal to passed string" -msgstr "" +msgstr "строка, True если текст объекта идентичен заданной строке" #: of telebot.custom_filters.TextFilter:8 msgid "list[str] or tuple[str], True if any string element of iterable is in text" -msgstr "" +msgstr "list[str] или tuple[str], True если хотя бы один из элементов есть в тексте" #: of telebot.custom_filters.TextFilter:11 telebot.custom_filters.TextFilter:14 msgid "string, True if object's text starts with passed string" -msgstr "" +msgstr "string, True если текст объекта начинается с заданной строки" #: of telebot.custom_filters.TextFilter:17 msgid "bool (default False), case insensitive" -msgstr "" +msgstr "bool (по умолчанию False), независимый от регистра" #: of telebot.custom_filters.TextFilter:20 msgid "if incorrect value for a parameter was supplied" -msgstr "" +msgstr "если было задано некорректное значение параметра" #: of telebot.custom_filters.TextMatchFilter:1 msgid "Filter to check Text message." -msgstr "" +msgstr "Фильтр для проверки текста сообщения." #: of telebot.custom_filters.TextStartsFilter:1 msgid "Filter to check whether message starts with some text." -msgstr "" +msgstr "Фильтр для проверки, начинается ли сообщение с заданного текста." #: ../../source/sync_version/index.rst:25 msgid "handler_backends file" -msgstr "" +msgstr "Файл handler_backends" #: of telebot.handler_backends.BaseMiddleware:1 msgid "" "Base class for middleware. Your middlewares should be inherited from this" " class." msgstr "" +"Базовый класс для middleware. Ваши middleware должны быть унаследованы от " +"этого класса." #: of telebot.handler_backends.BaseMiddleware:4 msgid "" @@ -5588,16 +5823,22 @@ msgid "" "message update, then you will have to create pre_process_message " "function, and so on. Same applies to post_process." msgstr "" +"Задайте update_sensitive=True если хотите получать разные апдейты в " +"разных функциях. Например, если вы хотите обрабатывать pre_process для " +"апдейтов вида message, вам нужно будет создать функцию pre_process_message " +"и т.д. Аналогично для post_process." #: of telebot.handler_backends.BaseMiddleware:10 msgid "" "If you want to use middleware, you have to set use_class_middlewares=True" " in your TeleBot instance." msgstr "" +"Если вы хотите использовать middleware, вам нужно задать use_class_middlewares=True " +"в экземпляре класса TeleBot." #: of telebot.handler_backends.BaseMiddleware:13 msgid "Example of class-based middlewares." -msgstr "" +msgstr "Пример класса middleware." #: of telebot.handler_backends.CancelUpdate:1 msgid "" @@ -5605,16 +5846,21 @@ msgid "" "middleware to skip update. Update will skip handler and execution of " "post_process in middlewares." msgstr "" +"Класс для отмены апдейтов. Просто верните экземпляр этого класса в " +"middleware, чтобы пропустить апдейт. Апдейт пропустит хендлер и исполнение " +"post_process в middleware." #: of telebot.handler_backends.ContinueHandling:1 msgid "" "Class for continue updates in handlers. Just return instance of this " "class in handlers to continue process." msgstr "" +"Класс для продолжения обработки апдейта в хендлерах. Просто верните экземпляр " +"этого класса в хендлерах, чтобы продолжить обработку." #: of telebot.handler_backends.ContinueHandling:5 msgid "Example of using ContinueHandling" -msgstr "" +msgstr "Пример использования ContinueHandling" #: of telebot.handler_backends.SkipHandler:1 msgid "" @@ -5622,30 +5868,37 @@ msgid "" "middleware to skip handler. Update will go to post_process, but will skip" " execution of handler." msgstr "" +"Класс для пропуска хендлеров. Просто верните экземпляр этого класса в " +"middleware, чтобы пропустить хендлер. Апдейт попадёт в post_process, но " +"пропустит исполнение хендлера." #: of telebot.handler_backends.State:1 msgid "Class representing a state." -msgstr "" +msgstr "Класс, представляющий состояние (стейт)." #: of telebot.handler_backends.StatesGroup:1 msgid "Class representing common states." -msgstr "" +msgstr "Класс, представляющий похожие состояния (стейты)." #: ../../source/sync_version/index.rst:34 msgid "Extensions" -msgstr "" +msgstr "Расширения" #: of telebot.ext.sync.webhooks:1 msgid "" "This file is used by TeleBot.run_webhooks() function. Fastapi is required" " to run this script." msgstr "" +"Этот файл используется функцией TeleBot.run_webhooks(). Для запуска этого " +"скрипта требуется Fastapi." #: of telebot.ext.sync.webhooks.SyncWebhookListener.run_app:1 msgid "" "Run app with the given parameters to init. Not supposed to be used " "manually by user." msgstr "" +"Запустить приложение с заданными параметрами инициализации. Не предназначено " +"для использования пользователем." #~ msgid "New name of the topic, 1-128 characters" #~ msgstr "Новое название топика, 1-128 символов" From b540a6c4d4840b7a3c41fef0bf78024c0ee28b97 Mon Sep 17 00:00:00 2001 From: Konstantin Ostashenko Date: Wed, 4 Jan 2023 15:33:18 +0200 Subject: [PATCH 1233/1808] Fix msgid for forward_message:5 in sync_version.po --- docs/source/locales/ru/LC_MESSAGES/sync_version.po | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 999ae43fd..867e496da 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -1316,8 +1316,7 @@ msgstr "" "Список отформатированных частей новой подписи в формате JSON, можно " "использовать вместо parse_mode" -#: of telebot.TeleBot.forward_message:5 -#: telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 +#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 #: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 #: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:26 #: telebot.TeleBot.send_game:11 telebot.TeleBot.send_invoice:67 @@ -2434,6 +2433,14 @@ msgstr "Используйте этот метод, чтобы переслат msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" msgstr "Документация Telegram: https://core.telegram.org/bots/api#forwardmessage" +#: of telebot.TeleBot.forward_message:5 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" +"Отправить сообщение, при получении которого пользователи получат " +"уведомление без звука" + #: of telebot.TeleBot.forward_message:17 msgid "Protects the contents of the forwarded message from forwarding and saving" msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" From 3cfa24f9c0db5a29379e0d5424dc7a2051cdc6d7 Mon Sep 17 00:00:00 2001 From: Konstantin Ostashenko Date: Wed, 4 Jan 2023 15:35:09 +0200 Subject: [PATCH 1234/1808] Fix msgid for forward_message:5 in async_version.po --- docs/source/locales/ru/LC_MESSAGES/async_version.po | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index dad59ddbb..a094d98b4 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -1417,8 +1417,7 @@ msgstr "" "Список отформатированных частей новой подписи в формате JSON, можно " "использовать вместо parse_mode" -#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 -#: telebot.async_telebot.AsyncTeleBot.copy_message:22 +#: of telebot.async_telebot.AsyncTeleBot.copy_message:22 #: telebot.async_telebot.AsyncTeleBot.send_animation:45 #: telebot.async_telebot.AsyncTeleBot.send_audio:39 #: telebot.async_telebot.AsyncTeleBot.send_contact:20 @@ -2569,6 +2568,14 @@ msgstr "Используйте этот метод, чтобы переслат msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" msgstr "Документация Telegram: https://core.telegram.org/bots/api#forwardmessage" +#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" +"Отправить сообщение, при получении которого пользователи получат " +"уведомление без звука" + #: of telebot.async_telebot.AsyncTeleBot.forward_message:17 msgid "Protects the contents of the forwarded message from forwarding and saving" msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" From bd1290592b1ce13984a5042aed79f733e09c016c Mon Sep 17 00:00:00 2001 From: coder2020official Date: Wed, 4 Jan 2023 18:07:29 +0400 Subject: [PATCH 1235/1808] Fix docs issues --- .../locales/ru/LC_MESSAGES/async_version.po | 35 ++++++++++--------- .../locales/ru/LC_MESSAGES/sync_version.po | 32 ++++++++--------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index a094d98b4..86d86bfb8 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -4,6 +4,7 @@ # Documentation package. # FIRST AUTHOR , 2022. # + #, fuzzy msgid "" msgstr "" @@ -222,7 +223,7 @@ msgid "Parameters" msgstr "" #: of telebot.async_telebot.AsyncTeleBot:24 -#, fuzzy + msgid "Token of a bot, obtained from @BotFather" msgstr "Токен бота, нужно получить от @BotFather" @@ -1296,7 +1297,7 @@ msgstr "" "декорируемую функцию объект :class:`telebot.types.ChatMemberUpdated`." #: of telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:1 -#, fuzzy + msgid "" "The result of an inline query that was chosen by a user and sent to their" " chat partner. Please see our documentation on the feedback collecting " @@ -1349,7 +1350,7 @@ msgid "Identifier of the topic to close" msgstr "id топика для закрытия" #: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to close the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -1361,7 +1362,7 @@ msgstr "" "топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#closegeneralforumtopic" @@ -2213,7 +2214,7 @@ msgstr "" "чтобы убрать иконку. Если не задан, сохранится текущая иконка топика" #: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to edit the name of the 'General' topic in a forum " "supergroup chat. The bot must be an administrator in the chat for this to" @@ -2226,14 +2227,14 @@ msgstr "" "True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editgeneralforumtopic" msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" #: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:10 -#, fuzzy + msgid "New topic name, 1-128 characters" msgstr "Название товара, 1-32 символа" @@ -3072,7 +3073,7 @@ msgid ":class:`telebot.types.WebhookInfo`" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to hide the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -3084,7 +3085,7 @@ msgstr "" "True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#hidegeneralforumtopic" @@ -3639,7 +3640,7 @@ msgid "Identifier of the topic to reopen" msgstr "id топика для возобновления" #: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to reopen the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " @@ -3650,7 +3651,7 @@ msgstr "" "кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopengeneralforumtopic" @@ -3984,7 +3985,7 @@ msgid "Identifier of a message thread, in which the video will be sent" msgstr "id топика, в который будет отправлено видео" #: of telebot.async_telebot.AsyncTeleBot.send_animation:60 -#, fuzzy + msgid "Pass True, if the animation should be sent as a spoiler" msgstr "Передайте True, если гифку нужно отправить как спойлер" @@ -4129,7 +4130,7 @@ msgstr "" "upload_video_note для видео заметок (кружочков)." #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:22 -#, fuzzy + msgid "The thread to which the message will be sent(supergroups only)" msgstr "id топика, в который сообщение будет отправлено(только для супергрупп)" @@ -4530,7 +4531,7 @@ msgid "Mode for parsing entities in the photo caption." msgstr "Режим форматирования подписи к фото." #: of telebot.async_telebot.AsyncTeleBot.send_photo:45 -#, fuzzy + msgid "Pass True, if the photo should be sent as a spoiler" msgstr "Передайте True, если фото должно быть отправлено как спойлер" @@ -4769,7 +4770,7 @@ msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "Передайте True, если загруженное видео подходит для стриминга" #: of telebot.async_telebot.AsyncTeleBot.send_video:61 -#, fuzzy + msgid "Pass True, if the video should be sent as a spoiler" msgstr "Передайте True, если видео должно быть отправлено как спойлер" @@ -5468,7 +5469,7 @@ msgid "Unique identifier of the target sender chat." msgstr "Уникальный id чата." #: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to unhide the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " @@ -5479,7 +5480,7 @@ msgstr "" "can_manage_topics. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unhidegeneralforumtopic" diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 867e496da..3858e743f 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -1258,7 +1258,7 @@ msgid "Identifier of the topic to close" msgstr "id топика для закрытия" #: of telebot.TeleBot.close_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to close the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -1270,7 +1270,7 @@ msgstr "" "топика. Возвращает True в случае успеха." #: of telebot.TeleBot.close_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#closegeneralforumtopic" @@ -2054,7 +2054,7 @@ msgstr "" "чтобы убрать иконку. Если не задан, сохранится текущая иконка топика" #: of telebot.TeleBot.edit_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to edit the name of the 'General' topic in a forum " "supergroup chat. The bot must be an administrator in the chat for this to" @@ -2067,14 +2067,14 @@ msgstr "" "True в случае успеха." #: of telebot.TeleBot.edit_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editgeneralforumtopic" msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" #: of telebot.TeleBot.edit_general_forum_topic:10 -#, fuzzy + msgid "New topic name, 1-128 characters" msgstr "Название товара, 1-32 символа" @@ -2926,7 +2926,7 @@ msgid ":class:`telebot.types.WebhookInfo`" msgstr "" #: of telebot.TeleBot.hide_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to hide the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -2938,7 +2938,7 @@ msgstr "" "True в случае успеха." #: of telebot.TeleBot.hide_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#hidegeneralforumtopic" @@ -3673,7 +3673,7 @@ msgstr "" "Регистрирует функцию для вызова при получении нового сообщения в заданном чате." #: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 -#, fuzzy + msgid "The chat (chat ID) for which we want to handle new message." msgstr "Чат (id чата), в котором нужно обработать новое сообщение." @@ -3721,7 +3721,7 @@ msgid "Identifier of the topic to reopen" msgstr "id топика для возобновления" #: of telebot.TeleBot.reopen_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to reopen the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " @@ -3732,7 +3732,7 @@ msgstr "" "кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." #: of telebot.TeleBot.reopen_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopengeneralforumtopic" @@ -4070,7 +4070,7 @@ msgid "Identifier of a message thread, in which the video will be sent" msgstr "id топика, в который будет отправлено видео" #: of telebot.TeleBot.send_animation:60 -#, fuzzy + msgid "Pass True, if the animation should be sent as a spoiler" msgstr "Передайте True, если гифку нужно отправить как спойлер" @@ -4211,7 +4211,7 @@ msgstr "" "upload_video_note для видео заметок (кружочков)." #: of telebot.TeleBot.send_chat_action:22 -#, fuzzy + msgid "" "The thread identifier of a message from which the reply will be " "sent(supergroups only)" @@ -4609,7 +4609,7 @@ msgid "Mode for parsing entities in the photo caption." msgstr "Режим форматирования подписи к фото." #: of telebot.TeleBot.send_photo:45 -#, fuzzy + msgid "Pass True, if the photo should be sent as a spoiler" msgstr "Передайте True, если фото должно быть отправлено как спойлер" @@ -4847,7 +4847,7 @@ msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "Передайте True, если загруженное видео подходит для стриминга" #: of telebot.TeleBot.send_video:61 -#, fuzzy + msgid "Pass True, if the video should be sent as a spoiler" msgstr "Передайте True, если видео должно быть отправлено как спойлер" @@ -5532,7 +5532,7 @@ msgid "Unique identifier of the target sender chat." msgstr "Уникальный id чата." #: of telebot.TeleBot.unhide_general_forum_topic:1 -#, fuzzy + msgid "" "Use this method to unhide the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " @@ -5543,7 +5543,7 @@ msgstr "" "can_manage_topics. Возвращает True в случае успеха." #: of telebot.TeleBot.unhide_general_forum_topic:5 -#, fuzzy + msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unhidegeneralforumtopic" From f201df32759b03e8342a116e90778efb701f151f Mon Sep 17 00:00:00 2001 From: Cub11k Date: Thu, 5 Jan 2023 16:03:14 +0200 Subject: [PATCH 1236/1808] Translated index.po to russian --- docs/source/locales/ru/LC_MESSAGES/index.po | 36 +++++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/index.po b/docs/source/locales/ru/LC_MESSAGES/index.po index 38a49544f..ae4cc1fa1 100644 --- a/docs/source/locales/ru/LC_MESSAGES/index.po +++ b/docs/source/locales/ru/LC_MESSAGES/index.po @@ -20,15 +20,15 @@ msgstr "" #: ../../index.rst:8 msgid "Welcome to pyTelegramBotAPI's documentation!" -msgstr "" +msgstr "Добро пожаловать в документацию pyTelegramBotAPI!" #: ../../index.rst:10 msgid "Official documentation of pyTelegramBotAPI" -msgstr "" +msgstr "Официальная документация pyTelegramBotAPI" #: ../../index.rst:10 msgid "ptba, pytba, pyTelegramBotAPI, documentation, guide" -msgstr "" +msgstr "ptba, pytba, pyTelegramBotAPI, документация, гайд" #: ../../index.rst:17 msgid "TeleBot" @@ -39,26 +39,32 @@ msgid "" "TeleBot is synchronous and asynchronous implementation of `Telegram Bot " "API `_." msgstr "" +"TeleBot это синхронная и асинхронная реализация `Telegram Bot " +"API `_." #: ../../index.rst:21 msgid "Chats" -msgstr "" +msgstr "Чаты" #: ../../index.rst:22 msgid "" "English chat: `Private chat " "`__" msgstr "" +"Англоязычный чат: `Private chat " +"`__" #: ../../index.rst:24 msgid "" "Russian chat: `@pytelegrambotapi_talks_ru " "`__" msgstr "" +"Русскоязычный чат: `@pytelegrambotapi_talks_ru " +"`__" #: ../../index.rst:26 msgid "News: `@pyTelegramBotAPI `__" -msgstr "" +msgstr "Новости: `@pyTelegramBotAPI `__" #: ../../index.rst:28 msgid "Pypi: `Pypi `__" @@ -69,42 +75,44 @@ msgid "" "Source: `Github repository " "`__" msgstr "" +"Исходники: `Github repository " +"`__" #: ../../index.rst:33 msgid "Some features:" -msgstr "" +msgstr "Некоторые особенности:" #: ../../index.rst:34 msgid "Easy to learn and use." -msgstr "" +msgstr "Простой в изучении и использовании." #: ../../index.rst:36 msgid "Easy to understand." -msgstr "" +msgstr "Простой в понимании." #: ../../index.rst:38 msgid "Both sync and async." -msgstr "" +msgstr "И синхронный, и асинхронный." #: ../../index.rst:40 msgid "Examples on features." -msgstr "" +msgstr "Примеры возможностей." #: ../../index.rst:42 msgid "States" -msgstr "" +msgstr "Состояния (стейты, FSM)" #: ../../index.rst:44 msgid "And more..." -msgstr "" +msgstr "И другое..." #: ../../index.rst:47 msgid "Content" -msgstr "" +msgstr "Содержимое" #: ../../index.rst:63 msgid "Indices and tables" -msgstr "" +msgstr "Ссылки" #: ../../index.rst:65 msgid ":ref:`genindex`" From b93ec5d0e0d7048c3499705ba54b21632e4ab2fc Mon Sep 17 00:00:00 2001 From: Cub11k Date: Thu, 5 Jan 2023 16:13:00 +0200 Subject: [PATCH 1237/1808] Translated calldata.po to russian --- .../source/locales/ru/LC_MESSAGES/calldata.po | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/calldata.po b/docs/source/locales/ru/LC_MESSAGES/calldata.po index 03484e874..434f04260 100644 --- a/docs/source/locales/ru/LC_MESSAGES/calldata.po +++ b/docs/source/locales/ru/LC_MESSAGES/calldata.po @@ -20,38 +20,40 @@ msgstr "" #: ../../calldata.rst:4 msgid "Callback data factory" -msgstr "" +msgstr "Фабрика callback data" #: ../../calldata.rst:6 msgid "Callback data factory in pyTelegramBotAPI" -msgstr "" +msgstr "Фабрика callback data в pyTelegramBotAPI" #: ../../calldata.rst:6 msgid "" "ptba, pytba, pyTelegramBotAPI, callbackdatafactory, guide, callbackdata, " "factory" msgstr "" +"ptba, pytba, pyTelegramBotAPI, callbackdatafactory, гайд, callbackdata, " +"фабрика" #: ../../calldata.rst:12 msgid "callback\\_data file" -msgstr "" +msgstr "Файл callback\\_data" #: of telebot.callback_data:1 msgid "Callback data factory's file." -msgstr "" +msgstr "Файл фабрики callback data." #: of telebot.callback_data.CallbackData:1 #: telebot.callback_data.CallbackDataFilter:1 msgid "Bases: :py:class:`object`" -msgstr "" +msgstr "Базовые классы: :py:class:`object`" #: of telebot.callback_data.CallbackData:1 msgid "Callback data factory This class will help you to work with CallbackQuery" -msgstr "" +msgstr "Фабрика Callback data. Этот класс поможет вам в работе с CallbackQuery" #: of telebot.callback_data.CallbackData.filter:1 msgid "Generate filter" -msgstr "" +msgstr "Сгенерировать фильтр" #: of telebot.callback_data.CallbackData.filter #: telebot.callback_data.CallbackData.new @@ -62,7 +64,7 @@ msgstr "" #: of telebot.callback_data.CallbackData.filter:3 msgid "specified named parameters will be checked with CallbackQuery.data" -msgstr "" +msgstr "заданные именованные параметры будут проверены в CallbackQuery.data" #: of telebot.callback_data.CallbackData.filter #: telebot.callback_data.CallbackData.new @@ -73,19 +75,19 @@ msgstr "" #: of telebot.callback_data.CallbackData.filter:4 msgid "CallbackDataFilter class" -msgstr "" +msgstr "Класс CallbackDataFilter" #: of telebot.callback_data.CallbackData.new:1 msgid "Generate callback data" -msgstr "" +msgstr "Сгенерировать callback data" #: of telebot.callback_data.CallbackData.new:3 msgid "positional parameters of CallbackData instance parts" -msgstr "" +msgstr "позиционные параметры экземпляра CallbackData" #: of telebot.callback_data.CallbackData.new:4 msgid "named parameters" -msgstr "" +msgstr "именованные параметры" #: of telebot.callback_data.CallbackData.new:5 msgid "str" @@ -93,25 +95,27 @@ msgstr "" #: of telebot.callback_data.CallbackData.parse:1 msgid "Parse data from the callback data" -msgstr "" +msgstr "Получить данные из callback data" #: of telebot.callback_data.CallbackData.parse:3 msgid "" "string, use to telebot.types.CallbackQuery to parse it from string to a " "dict" msgstr "" +"string, примените к telebot.types.CallbackQuery, чтобы преобразовать " +"callback_data из строки (str) в словарь (dict)" #: of telebot.callback_data.CallbackData.parse:4 msgid "dict parsed from callback data" -msgstr "" +msgstr "словарь (dict), полученный из callback data" #: of telebot.callback_data.CallbackDataFilter:1 msgid "Filter for CallbackData." -msgstr "" +msgstr "Фильтр для CallbackData." #: of telebot.callback_data.CallbackDataFilter.check:1 msgid "Checks if query.data appropriates to specified config" -msgstr "" +msgstr "Проверяет, соответствует ли query.data заданной конфигурации" #: of telebot.callback_data.CallbackDataFilter.check:3 msgid "telebot.types.CallbackQuery" @@ -119,9 +123,8 @@ msgstr "" #: of telebot.callback_data.CallbackDataFilter.check:6 msgid "True if query.data appropriates to specified config" -msgstr "" +msgstr "True, если query.data соответствует заданной конфигурации" #: of telebot.callback_data.CallbackDataFilter.check msgid "Return type" msgstr "" - From 8aee5372ee7be89de97b7db4398a554c1b59da20 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Thu, 5 Jan 2023 16:25:16 +0200 Subject: [PATCH 1238/1808] Update README.md - add link to ru docs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5eb412ce7..cfcc19545 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ ##

Supported Bot API version: 6.4!

Official documentation

+

Official ru documentation

## Contents From c298d95d0fea4d5f7d0fdd8733bb2343240d7cdf Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 6 Jan 2023 19:27:25 +0200 Subject: [PATCH 1239/1808] Move functions, required in types.py to _util.py Add __all__ to util.py for sphinx to generate docs properly --- telebot/_util.py | 85 +++++++++++++++++++++++++++++ telebot/types.py | 28 +++++----- telebot/util.py | 137 +++++++++++++---------------------------------- 3 files changed, 137 insertions(+), 113 deletions(-) create mode 100644 telebot/_util.py diff --git a/telebot/_util.py b/telebot/_util.py new file mode 100644 index 000000000..d4c1eb27c --- /dev/null +++ b/telebot/_util.py @@ -0,0 +1,85 @@ +import random +import string + +try: + # noinspection PyPackageRequirements + from PIL import Image + from io import BytesIO + + pil_imported = True +except: + pil_imported = False + + +def is_string(var) -> bool: + """ + Returns True if the given object is a string. + """ + return isinstance(var, str) + + +def is_dict(var) -> bool: + """ + Returns True if the given object is a dictionary. + + :param var: object to be checked + :type var: :obj:`object` + + :return: True if the given object is a dictionary. + :rtype: :obj:`bool` + """ + return isinstance(var, dict) + + +def is_bytes(var) -> bool: + """ + Returns True if the given object is a bytes object. + + :param var: object to be checked + :type var: :obj:`object` + + :return: True if the given object is a bytes object. + :rtype: :obj:`bool` + """ + return isinstance(var, bytes) + + +def is_pil_image(var) -> bool: + """ + Returns True if the given object is a PIL.Image.Image object. + + :param var: object to be checked + :type var: :obj:`object` + + :return: True if the given object is a PIL.Image.Image object. + :rtype: :obj:`bool` + """ + return pil_imported and isinstance(var, Image.Image) + + +def pil_image_to_file(image, extension='JPEG', quality='web_low'): + if pil_imported: + photoBuffer = BytesIO() + image.convert('RGB').save(photoBuffer, extension, quality=quality) + photoBuffer.seek(0) + + return photoBuffer + else: + raise RuntimeError('PIL module is not imported') + + +def chunks(lst, n): + """Yield successive n-sized chunks from lst.""" + # https://stackoverflow.com/a/312464/9935473 + for i in range(0, len(lst), n): + yield lst[i:i + n] + + +def generate_random_token() -> str: + """ + Generates a random token consisting of letters and digits, 16 characters long. + + :return: a random token + :rtype: :obj:`str` + """ + return ''.join(random.sample(string.ascii_letters, 16)) diff --git a/telebot/types.py b/telebot/types.py index 2938674bb..6ab520eec 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12,7 +12,7 @@ except ImportError: import json -from telebot import util +from telebot import _util DISABLE_KEYLEN_ERROR = False @@ -87,9 +87,9 @@ def check_json(json_type, dict_copy = True): :param dict_copy: if dict is passed and it is changed outside - should be True! :return: Dictionary parsed from json or original dict """ - if util.is_dict(json_type): + if _util.is_dict(json_type): return json_type.copy() if dict_copy else json_type - elif util.is_string(json_type): + elif _util.is_string(json_type): return json.loads(json_type) else: raise ValueError("json_type should be a json dict or string.") @@ -2156,12 +2156,12 @@ def add(self, *args, row_width=None): logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - for row in util.chunks(args, row_width): + for row in _util.chunks(args, row_width): button_array = [] for button in row: - if util.is_string(button): + if _util.is_string(button): button_array.append({'text': button}) - elif util.is_bytes(button): + elif _util.is_bytes(button): button_array.append({'text': button.decode('utf-8')}) else: button_array.append(button.to_dict()) @@ -2278,12 +2278,12 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) This object represents an inline keyboard that appears right next to the message it belongs to. .. note:: - It is recommended to use :meth:`telebot.util.quick_markup` instead. + It is recommended to use :meth:`telebot._util.quick_markup` instead. .. code-block:: python3 :caption: Example of a custom keyboard with buttons. - from telebot.util import quick_markup + from telebot._util.import quick_markup markup = quick_markup( {'text': 'Press me', 'callback_data': 'press'}, @@ -2345,7 +2345,7 @@ def add(self, *args, row_width=None): logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - for row in util.chunks(args, row_width): + for row in _util.chunks(args, row_width): button_array = [button for button in row] self.keyboard.append(button_array) @@ -5786,11 +5786,11 @@ def __init__(self, type, media, caption=None, parse_mode=None, caption_entities= self.parse_mode: Optional[str] = parse_mode self.caption_entities: Optional[List[MessageEntity]] = caption_entities - if util.is_string(self.media): + if _util.is_string(self.media): self._media_name = '' self._media_dic = self.media else: - self._media_name = util.generate_random_token() + self._media_name = _util.generate_random_token() self._media_dic = 'attach://{0}'.format(self._media_name) def to_json(self): @@ -5810,7 +5810,7 @@ def convert_input_media(self): """ :meta private: """ - if util.is_string(self.media): + if _util.is_string(self.media): return self.to_json(), None return self.to_json(), {self._media_name: self.media} @@ -5845,8 +5845,8 @@ class InputMediaPhoto(InputMedia): :rtype: :class:`telebot.types.InputMediaPhoto` """ def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, has_spoiler=None): - if util.is_pil_image(media): - media = util.pil_image_to_file(media) + if _util.is_pil_image(media): + media = _util.pil_image_to_file(media) super(InputMediaPhoto, self).__init__( type="photo", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) diff --git a/telebot/util.py b/telebot/util.py index e52ee83a5..4c4efa922 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -import random import re -import string import threading import traceback from typing import Any, Callable, List, Dict, Optional, Union @@ -14,21 +12,13 @@ import logging from telebot import types +from telebot._util import is_pil_image, is_dict, is_string, is_bytes, chunks, generate_random_token, pil_image_to_file try: import ujson as json except ImportError: import json -try: - # noinspection PyPackageRequirements - from PIL import Image - from io import BytesIO - - pil_imported = True -except: - pil_imported = False - MAX_MESSAGE_LENGTH = 4096 logger = logging.getLogger('TeleBot') @@ -44,7 +34,8 @@ #: Contains all service content types such as `User joined the group`. content_type_service = [ - 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created', + 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', + 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', 'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', 'video_chat_participants_invited', 'message_auto_delete_timer_changed', 'forum_topic_created', 'forum_topic_closed', @@ -129,6 +120,7 @@ class ThreadPool: """ :meta private: """ + def __init__(self, telebot, num_threads=2): self.telebot = telebot self.tasks = Queue.Queue() @@ -169,6 +161,7 @@ class AsyncTask: """ :meta private: """ + def __init__(self, target, *args, **kwargs): self.target = target self.args = args @@ -198,7 +191,8 @@ class CustomRequestResponse(): """ :meta private: """ - def __init__(self, json_text, status_code = 200, reason = ""): + + def __init__(self, json_text, status_code=200, reason=""): self.status_code = status_code self.text = json_text self.reason = reason @@ -211,6 +205,7 @@ def async_dec(): """ :meta private: """ + def decorator(fn): def wrapper(*args, **kwargs): return AsyncTask(fn, *args, **kwargs) @@ -220,63 +215,6 @@ def wrapper(*args, **kwargs): return decorator -def is_string(var) -> bool: - """ - Returns True if the given object is a string. - """ - return isinstance(var, str) - - -def is_dict(var) -> bool: - """ - Returns True if the given object is a dictionary. - - :param var: object to be checked - :type var: :obj:`object` - - :return: True if the given object is a dictionary. - :rtype: :obj:`bool` - """ - return isinstance(var, dict) - - -def is_bytes(var) -> bool: - """ - Returns True if the given object is a bytes object. - - :param var: object to be checked - :type var: :obj:`object` - - :return: True if the given object is a bytes object. - :rtype: :obj:`bool` - """ - return isinstance(var, bytes) - - -def is_pil_image(var) -> bool: - """ - Returns True if the given object is a PIL.Image.Image object. - - :param var: object to be checked - :type var: :obj:`object` - - :return: True if the given object is a PIL.Image.Image object. - :rtype: :obj:`bool` - """ - return pil_imported and isinstance(var, Image.Image) - - -def pil_image_to_file(image, extension='JPEG', quality='web_low'): - if pil_imported: - photoBuffer = BytesIO() - image.convert('RGB').save(photoBuffer, extension, quality=quality) - photoBuffer.seek(0) - - return photoBuffer - else: - raise RuntimeError('PIL module is not imported') - - def is_command(text: str) -> bool: r""" Checks if `text` is a command. Telegram chat commands start with the '/' character. @@ -353,7 +291,7 @@ def split_string(text: str, chars_per_string: int) -> List[str]: return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)] -def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]: +def smart_split(text: str, chars_per_string: int = MAX_MESSAGE_LENGTH) -> List[str]: r""" Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string. This is very useful for splitting one giant message into multiples. @@ -383,9 +321,12 @@ def _text_before_last(substr: str) -> str: part = text[:chars_per_string] - if "\n" in part: part = _text_before_last("\n") - elif ". " in part: part = _text_before_last(". ") - elif " " in part: part = _text_before_last(" ") + if "\n" in part: + part = _text_before_last("\n") + elif ". " in part: + part = _text_before_last(". ") + elif " " in part: + part = _text_before_last(" ") parts.append(part) text = text[len(part):] @@ -401,12 +342,12 @@ def escape(text: str) -> str: chars = {"&": "&", "<": "<", ">": ">"} if text is None: return None - for old, new in chars.items(): + for old, new in chars.items(): text = text.replace(old, new) return text -def user_link(user: types.User, include_id: bool=False) -> str: +def user_link(user: types.User, include_id: bool = False) -> str: """ Returns an HTML user link. This is useful for reports. Attention: Don't forget to set parse_mode to 'HTML'! @@ -433,10 +374,10 @@ def user_link(user: types.User, include_id: bool=False) -> str: """ name = escape(user.first_name) return (f"{name}" - + (f" (
{user.id}
)" if include_id else "")) + + (f" (
{user.id}
)" if include_id else "")) -def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup: +def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int = 2) -> types.InlineKeyboardMarkup: """ Returns a reply markup from a dict in this format: {'text': kwargs} This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)' @@ -551,24 +492,7 @@ def per_thread(key, construct_value, reset=False): return getattr(thread_local, key) -def chunks(lst, n): - """Yield successive n-sized chunks from lst.""" - # https://stackoverflow.com/a/312464/9935473 - for i in range(0, len(lst), n): - yield lst[i:i + n] - - -def generate_random_token() -> str: - """ - Generates a random token consisting of letters and digits, 16 characters long. - - :return: a random token - :rtype: :obj:`str` - """ - return ''.join(random.sample(string.ascii_letters, 16)) - - -def deprecated(warn: bool=True, alternative: Optional[Callable]=None, deprecation_text=None): +def deprecated(warn: bool = True, alternative: Optional[Callable] = None, deprecation_text=None): """ Use this decorator to mark functions as deprecated. When the function is used, an info (or warning if `warn` is True) is logged. @@ -586,6 +510,7 @@ def deprecated(warn: bool=True, alternative: Optional[Callable]=None, deprecatio :return: The decorated function """ + def decorator(function): def wrapper(*args, **kwargs): info = f"`{function.__name__}` is deprecated." @@ -598,7 +523,9 @@ def wrapper(*args, **kwargs): else: logger.warning(info) return function(*args, **kwargs) + return wrapper + return decorator @@ -661,8 +588,8 @@ def antiflood(function: Callable, *args, **kwargs): return function(*args, **kwargs) else: raise - - + + def parse_web_app_data(token: str, raw_init_data: str): """ Parses web app data. @@ -715,4 +642,16 @@ def validate_web_app_data(token: str, raw_init_data: str): return hmac.new(secret_key.digest(), data_check_string.encode(), sha256).hexdigest() == init_data_hash - + +__all__ = ( + "content_type_media", "content_type_service", "update_types", + "WorkerThread", "AsyncTask", "CustomRequestResponse", + "async_dec", "deprecated", + "is_bytes", "is_string", "is_dict", "is_pil_image", + "chunks", "generate_random_token", "pil_image_to_file", + "is_command", "extract_command", "extract_arguments", + "split_string", "smart_split", "escape", "user_link", "quick_markup", + "antiflood", "parse_web_app_data", "validate_web_app_data", + "or_set", "or_clear", "orify", "OrEvent", "per_thread", + "webhook_google_functions" +) From e6f8acadf422eb1f0fc522d51647965622d96609 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 6 Jan 2023 21:41:30 +0200 Subject: [PATCH 1240/1808] rename _util.py to service_utils.py --- telebot/{_util.py => service_utils.py} | 0 telebot/types.py | 28 +++++++++++++------------- telebot/util.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) rename telebot/{_util.py => service_utils.py} (100%) diff --git a/telebot/_util.py b/telebot/service_utils.py similarity index 100% rename from telebot/_util.py rename to telebot/service_utils.py diff --git a/telebot/types.py b/telebot/types.py index 6ab520eec..fa52e2aab 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12,7 +12,7 @@ except ImportError: import json -from telebot import _util +from telebot import service_utils DISABLE_KEYLEN_ERROR = False @@ -87,9 +87,9 @@ def check_json(json_type, dict_copy = True): :param dict_copy: if dict is passed and it is changed outside - should be True! :return: Dictionary parsed from json or original dict """ - if _util.is_dict(json_type): + if service_utils.is_dict(json_type): return json_type.copy() if dict_copy else json_type - elif _util.is_string(json_type): + elif service_utils.is_string(json_type): return json.loads(json_type) else: raise ValueError("json_type should be a json dict or string.") @@ -2156,12 +2156,12 @@ def add(self, *args, row_width=None): logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - for row in _util.chunks(args, row_width): + for row in service_utils.chunks(args, row_width): button_array = [] for button in row: - if _util.is_string(button): + if service_utils.is_string(button): button_array.append({'text': button}) - elif _util.is_bytes(button): + elif service_utils.is_bytes(button): button_array.append({'text': button.decode('utf-8')}) else: button_array.append(button.to_dict()) @@ -2278,12 +2278,12 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) This object represents an inline keyboard that appears right next to the message it belongs to. .. note:: - It is recommended to use :meth:`telebot._util.quick_markup` instead. + It is recommended to use :meth:`telebot.service_utils..quick_markup` instead. .. code-block:: python3 :caption: Example of a custom keyboard with buttons. - from telebot._util.import quick_markup + from telebot.service_utils..import quick_markup markup = quick_markup( {'text': 'Press me', 'callback_data': 'press'}, @@ -2345,7 +2345,7 @@ def add(self, *args, row_width=None): logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - for row in _util.chunks(args, row_width): + for row in service_utils.chunks(args, row_width): button_array = [button for button in row] self.keyboard.append(button_array) @@ -5786,11 +5786,11 @@ def __init__(self, type, media, caption=None, parse_mode=None, caption_entities= self.parse_mode: Optional[str] = parse_mode self.caption_entities: Optional[List[MessageEntity]] = caption_entities - if _util.is_string(self.media): + if service_utils.is_string(self.media): self._media_name = '' self._media_dic = self.media else: - self._media_name = _util.generate_random_token() + self._media_name = service_utils.generate_random_token() self._media_dic = 'attach://{0}'.format(self._media_name) def to_json(self): @@ -5810,7 +5810,7 @@ def convert_input_media(self): """ :meta private: """ - if _util.is_string(self.media): + if service_utils.is_string(self.media): return self.to_json(), None return self.to_json(), {self._media_name: self.media} @@ -5845,8 +5845,8 @@ class InputMediaPhoto(InputMedia): :rtype: :class:`telebot.types.InputMediaPhoto` """ def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, has_spoiler=None): - if _util.is_pil_image(media): - media = _util.pil_image_to_file(media) + if service_utils.is_pil_image(media): + media = service_utils.pil_image_to_file(media) super(InputMediaPhoto, self).__init__( type="photo", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) diff --git a/telebot/util.py b/telebot/util.py index 4c4efa922..3e76f1953 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -12,7 +12,7 @@ import logging from telebot import types -from telebot._util import is_pil_image, is_dict, is_string, is_bytes, chunks, generate_random_token, pil_image_to_file +from telebot.service_utils import is_pil_image, is_dict, is_string, is_bytes, chunks, generate_random_token, pil_image_to_file try: import ujson as json From c27f60b94b4df649bf6671be9139d8b1c3018bf4 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 6 Jan 2023 22:36:08 +0200 Subject: [PATCH 1241/1808] Make create_dir() method cross-platform instead of POSIX only. Fix for issue #1869 --- telebot/asyncio_storage/pickle_storage.py | 2 +- telebot/storage/pickle_storage.py | 2 +- telebot/util.py | 24 +++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index 49fe3bedd..cf446d85b 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -28,7 +28,7 @@ def create_dir(self): """ Create directory .save-handlers. """ - dirs = self.file_path.rsplit('/', maxsplit=1)[0] + dirs, filename = os.path.split(self.file_path) os.makedirs(dirs, exist_ok=True) if not os.path.isfile(self.file_path): with open(self.file_path,'wb') as file: diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index dfffcf830..68c9fbed5 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -34,7 +34,7 @@ def create_dir(self): """ Create directory .save-handlers. """ - dirs = self.file_path.rsplit('/', maxsplit=1)[0] + dirs, filename = os.path.split(self.file_path) os.makedirs(dirs, exist_ok=True) if not os.path.isfile(self.file_path): with open(self.file_path,'wb') as file: diff --git a/telebot/util.py b/telebot/util.py index 3e76f1953..a1bf7d430 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -643,15 +643,15 @@ def validate_web_app_data(token: str, raw_init_data: str): return hmac.new(secret_key.digest(), data_check_string.encode(), sha256).hexdigest() == init_data_hash -__all__ = ( - "content_type_media", "content_type_service", "update_types", - "WorkerThread", "AsyncTask", "CustomRequestResponse", - "async_dec", "deprecated", - "is_bytes", "is_string", "is_dict", "is_pil_image", - "chunks", "generate_random_token", "pil_image_to_file", - "is_command", "extract_command", "extract_arguments", - "split_string", "smart_split", "escape", "user_link", "quick_markup", - "antiflood", "parse_web_app_data", "validate_web_app_data", - "or_set", "or_clear", "orify", "OrEvent", "per_thread", - "webhook_google_functions" -) +# __all__ = ( +# "content_type_media", "content_type_service", "update_types", +# "WorkerThread", "AsyncTask", "CustomRequestResponse", +# "async_dec", "deprecated", +# "is_bytes", "is_string", "is_dict", "is_pil_image", +# "chunks", "generate_random_token", "pil_image_to_file", +# "is_command", "extract_command", "extract_arguments", +# "split_string", "smart_split", "escape", "user_link", "quick_markup", +# "antiflood", "parse_web_app_data", "validate_web_app_data", +# "or_set", "or_clear", "orify", "OrEvent", "per_thread", +# "webhook_google_functions" +# ) From 02ae2557014a5181845ec0ccb44412d5be015dea Mon Sep 17 00:00:00 2001 From: Konstantin Ostashenko Date: Fri, 6 Jan 2023 22:39:27 +0200 Subject: [PATCH 1242/1808] Revert changes in util.py --- telebot/util.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index a1bf7d430..3e76f1953 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -643,15 +643,15 @@ def validate_web_app_data(token: str, raw_init_data: str): return hmac.new(secret_key.digest(), data_check_string.encode(), sha256).hexdigest() == init_data_hash -# __all__ = ( -# "content_type_media", "content_type_service", "update_types", -# "WorkerThread", "AsyncTask", "CustomRequestResponse", -# "async_dec", "deprecated", -# "is_bytes", "is_string", "is_dict", "is_pil_image", -# "chunks", "generate_random_token", "pil_image_to_file", -# "is_command", "extract_command", "extract_arguments", -# "split_string", "smart_split", "escape", "user_link", "quick_markup", -# "antiflood", "parse_web_app_data", "validate_web_app_data", -# "or_set", "or_clear", "orify", "OrEvent", "per_thread", -# "webhook_google_functions" -# ) +__all__ = ( + "content_type_media", "content_type_service", "update_types", + "WorkerThread", "AsyncTask", "CustomRequestResponse", + "async_dec", "deprecated", + "is_bytes", "is_string", "is_dict", "is_pil_image", + "chunks", "generate_random_token", "pil_image_to_file", + "is_command", "extract_command", "extract_arguments", + "split_string", "smart_split", "escape", "user_link", "quick_markup", + "antiflood", "parse_web_app_data", "validate_web_app_data", + "or_set", "or_clear", "orify", "OrEvent", "per_thread", + "webhook_google_functions" +) From 8744402efc68e0a1d3c49e584aa55940c1fca8d7 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 8 Jan 2023 10:48:45 +0400 Subject: [PATCH 1243/1808] Removed built-in io module from try/except block --- telebot/service_utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/service_utils.py b/telebot/service_utils.py index d4c1eb27c..1a2b8710c 100644 --- a/telebot/service_utils.py +++ b/telebot/service_utils.py @@ -1,11 +1,10 @@ import random import string +from io import BytesIO try: # noinspection PyPackageRequirements from PIL import Image - from io import BytesIO - pil_imported = True except: pil_imported = False From 6b399ab8cd5acd1430f055dfe7f65be9edee0c4e Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 8 Jan 2023 10:49:27 +0400 Subject: [PATCH 1244/1808] Being specific with except block --- telebot/service_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/service_utils.py b/telebot/service_utils.py index 1a2b8710c..2e4390904 100644 --- a/telebot/service_utils.py +++ b/telebot/service_utils.py @@ -6,7 +6,7 @@ # noinspection PyPackageRequirements from PIL import Image pil_imported = True -except: +except ImportError: pil_imported = False From 93dcbbeb02fff263e2a14025feeb7e674ad3d0dd Mon Sep 17 00:00:00 2001 From: Cub11k Date: Tue, 10 Jan 2023 19:35:36 +0200 Subject: [PATCH 1245/1808] Create method get_media_file_id() Method is used to get file id of different types of media --- telebot/util.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 3e76f1953..bc1921fdd 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -643,6 +643,40 @@ def validate_web_app_data(token: str, raw_init_data: str): return hmac.new(secret_key.digest(), data_check_string.encode(), sha256).hexdigest() == init_data_hash +def get_media_file_id(message: types.Message, content_types: Optional[List[str]] = None): + """ + Use this method to get file id of message media if it's present. + + :param message: The message to get media from + :type message: :class:`telebot.types.Message` + + :param content_types: Types of media to look for, by default all types are considered. + :type content_types: :obj:`list[str]`, optional + + :return: file_id or :obj:`None` + :rtype: :obj:`str` | :obj:`None` + """ + if content_types is None: + content_types = content_type_media + + if "animation" in content_types and message.animation is not None: + return message.animation.file_id + elif "audio" in content_types and message.audio is not None: + return message.audio.file_id + elif "document" in content_types and message.document is not None: + return message.document.file_id + elif "photo" in content_types and message.photo is not None: + return message.photo[0].file_id + elif "sticker" in content_types and message.sticker is not None: + return message.sticker.file_id + elif "video" in content_types and message.video is not None: + return message.video.file_id + elif "video_note" in content_types and message.video_note is not None: + return message.video_note.file_id + elif "voice" in content_types and message.voice is not None: + return message.voice.file_id + + __all__ = ( "content_type_media", "content_type_service", "update_types", "WorkerThread", "AsyncTask", "CustomRequestResponse", @@ -651,7 +685,7 @@ def validate_web_app_data(token: str, raw_init_data: str): "chunks", "generate_random_token", "pil_image_to_file", "is_command", "extract_command", "extract_arguments", "split_string", "smart_split", "escape", "user_link", "quick_markup", - "antiflood", "parse_web_app_data", "validate_web_app_data", + "antiflood", "get_media_file_id", "parse_web_app_data", "validate_web_app_data", "or_set", "or_clear", "orify", "OrEvent", "per_thread", "webhook_google_functions" ) From bef29d9318f3a9ff20f313ddd53bbe39d225ecbb Mon Sep 17 00:00:00 2001 From: Ilya Krivoshein Date: Fri, 13 Jan 2023 19:14:10 +0700 Subject: [PATCH 1246/1808] Added a bot with public source code to the list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cfcc19545..84962bd82 100644 --- a/README.md +++ b/README.md @@ -888,5 +888,6 @@ Here are some examples of template: * [Feedback-bot](https://github.com/coder2020official/feedbackbot) A feedback bot for user-admin communication. Made on AsyncTeleBot, using [template](https://github.com/coder2020official/asynctelebot_template). * [TeleServ](https://github.com/ablakely/TeleServ) by [ablakely](https://github.com/ablakely) This is a Telegram to IRC bridge which links as an IRC server and makes Telegram users appear as native IRC users. * [Simple Store Bot](https://github.com/AntonGlyzin/myshopbot) by [Anton Glyzin](https://github.com/AntonGlyzin) This is a simple telegram-store with an admin panel. Designed according to a template. +* [Media Rating Bot](https://t.me/mediaratingbot) ([source](https://github.com/CommanderCRM/MediaRatingBot))by [CommanderCRM](https://github.com/CommanderCRM). This bot aggregates media (movies, TV series, etc.) ratings from IMDb, Rotten Tomatoes, Metacritic, TheMovieDB, FilmAffinity and also provides number of votes of said media on IMDb. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 1eda7cafd46fa50f76b7aad210edab1c979ad5de Mon Sep 17 00:00:00 2001 From: Cub11k Date: Sun, 15 Jan 2023 15:04:07 +0200 Subject: [PATCH 1247/1808] Fix type of attribute id of InlineQuery from int to str --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index fa52e2aab..730a0d4f3 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3261,7 +3261,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, id, from_user, query, offset, chat_type=None, location=None, **kwargs): - self.id: int = id + self.id: str = id self.from_user: User = from_user self.query: str = query self.offset: str = offset From 1797f076dc9fc44503783ae9b3eb63d372093fa7 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Mon, 16 Jan 2023 15:45:59 +0200 Subject: [PATCH 1248/1808] Remove redundant function --- telebot/util.py | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index bc1921fdd..3e76f1953 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -643,40 +643,6 @@ def validate_web_app_data(token: str, raw_init_data: str): return hmac.new(secret_key.digest(), data_check_string.encode(), sha256).hexdigest() == init_data_hash -def get_media_file_id(message: types.Message, content_types: Optional[List[str]] = None): - """ - Use this method to get file id of message media if it's present. - - :param message: The message to get media from - :type message: :class:`telebot.types.Message` - - :param content_types: Types of media to look for, by default all types are considered. - :type content_types: :obj:`list[str]`, optional - - :return: file_id or :obj:`None` - :rtype: :obj:`str` | :obj:`None` - """ - if content_types is None: - content_types = content_type_media - - if "animation" in content_types and message.animation is not None: - return message.animation.file_id - elif "audio" in content_types and message.audio is not None: - return message.audio.file_id - elif "document" in content_types and message.document is not None: - return message.document.file_id - elif "photo" in content_types and message.photo is not None: - return message.photo[0].file_id - elif "sticker" in content_types and message.sticker is not None: - return message.sticker.file_id - elif "video" in content_types and message.video is not None: - return message.video.file_id - elif "video_note" in content_types and message.video_note is not None: - return message.video_note.file_id - elif "voice" in content_types and message.voice is not None: - return message.voice.file_id - - __all__ = ( "content_type_media", "content_type_service", "update_types", "WorkerThread", "AsyncTask", "CustomRequestResponse", @@ -685,7 +651,7 @@ def get_media_file_id(message: types.Message, content_types: Optional[List[str]] "chunks", "generate_random_token", "pil_image_to_file", "is_command", "extract_command", "extract_arguments", "split_string", "smart_split", "escape", "user_link", "quick_markup", - "antiflood", "get_media_file_id", "parse_web_app_data", "validate_web_app_data", + "antiflood", "parse_web_app_data", "validate_web_app_data", "or_set", "or_clear", "orify", "OrEvent", "per_thread", "webhook_google_functions" ) From 5e0da40fcdf3b4234acb99957e255bf37e1d4026 Mon Sep 17 00:00:00 2001 From: Albert <63642425+iamnalinor@users.noreply.github.com> Date: Sat, 21 Jan 2023 17:00:36 +0400 Subject: [PATCH 1249/1808] Fix "invite link must be non-empty" error `bot.edit_chat_invite_link` method contained a mistake: `invite_link` and `name` were supposed to be vice-versa in `apihelper.edit_chat_invite_link(...)` call. This caused to be invite_link empty or contain invalid value, resulting to get `Bad Request: invite link must be non-empty` error. This also affected the async version. --- 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 50ad5141d..6f6b4524b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3224,7 +3224,7 @@ def edit_chat_invite_link( :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( - apihelper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) + apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request) ) def revoke_chat_invite_link( diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4700bc73f..8dbf55a68 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4084,7 +4084,7 @@ async def edit_chat_invite_link( :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( - await asyncio_helper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request) + await asyncio_helper.edit_chat_invite_link(self.token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request) ) async def revoke_chat_invite_link( From 409ff4960392477244b18b7d4c21846411651ec6 Mon Sep 17 00:00:00 2001 From: Oleg Belov Date: Thu, 26 Jan 2023 22:49:41 -0300 Subject: [PATCH 1250/1808] Update README.md Change _handeler() to _handler() in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84962bd82..7a2bbd81c 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ def test_callback(call): # <- passes a CallbackQuery type object to your functio #### Shipping Query Handler Handle shipping queries -`@bot.shipping_query_handeler() # <- passes a ShippingQuery type object to your function` +`@bot.shipping_query_handler() # <- passes a ShippingQuery type object to your function` #### Pre Checkout Query Handler Handle pre checkoupt queries From b9bedef73f05165202b4124a1c6e30f90ea1deaf Mon Sep 17 00:00:00 2001 From: Artem Lavrenov Date: Sat, 28 Jan 2023 12:26:25 +0300 Subject: [PATCH 1251/1808] Avoid raise RuntimeError(cannot join current thread) --- telebot/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 3e76f1953..4fa81a3ec 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -154,7 +154,8 @@ def close(self): for worker in self.workers: worker.stop() for worker in self.workers: - worker.join() + if worker != threading.current_thread(): + worker.join() class AsyncTask: From ad7e4bbaf7d2dd81bd02818f8c5a56238d1ec0e3 Mon Sep 17 00:00:00 2001 From: Muhammad Aadil Date: Sat, 28 Jan 2023 18:20:58 +0500 Subject: [PATCH 1252/1808] Added poll_example.py in the examples --- examples/poll_example.py | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 examples/poll_example.py diff --git a/examples/poll_example.py b/examples/poll_example.py new file mode 100644 index 000000000..4fee73fd2 --- /dev/null +++ b/examples/poll_example.py @@ -0,0 +1,45 @@ +#!/usr/bin/python + +# This is an example file to create polls and handle poll answers +import telebot + +API_TOKEN = "" + +bot = telebot.TeleBot(API_TOKEN) + + +DATA = {} + + +@bot.message_handler(commands=["poll"]) +def create_poll(message): + bot.send_message(message.chat.id, "English Article Test") + answer_options = ["a", "an", "the", "-"] + + # is_anonymous = False -- if you want to check the user answer like here + poll = bot.send_poll( + chat_id=message.chat.id, + question="We are going to '' park.", + options=answer_options, + is_anonymous=False, + ) + + # store the poll_id against user_id in the database + poll_id = poll.poll.id + DATA[message.chat.id] = poll_id + + +@bot.poll_answer_handler() +def handle_poll_answers(poll): + + if DATA.get(poll.user.id) == poll.poll_id: + # to check the correct answer + user_answer = poll.option_ids[0] + correct_answer = 2 # "the" is the correct answer + if user_answer == correct_answer: + bot.send_message(poll.user.id, "Good! You're right.") + else: + bot.send_message(poll.user.id, 'The correct answer is "the" .') + + +bot.infinity_polling() From f5eac56afa70bfae6c4aab0137ee7da2b07059b2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 31 Jan 2023 11:19:11 +0300 Subject: [PATCH 1253/1808] Async allowed_updates fix --- telebot/asyncio_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 437188566..219cc1d7a 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -243,8 +243,8 @@ async def get_updates(token, offset=None, limit=None, params['limit'] = limit if timeout: params['timeout'] = timeout - if allowed_updates: - params['allowed_updates'] = allowed_updates + if allowed_updates is not None: # Empty lists should pass + params['allowed_updates'] = json.dumps(allowed_updates) return await _process_request(token, method_name, params=params, request_timeout=request_timeout) async def _check_result(method_name, result): From 10a80e1cfa8ebac66693838c9c3b706fd7804f9e Mon Sep 17 00:00:00 2001 From: Muhammad Aadil Date: Sat, 4 Feb 2023 10:27:29 +0500 Subject: [PATCH 1254/1808] add only quiz type poll example to the poll_example.py --- examples/poll_example.py | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/examples/poll_example.py b/examples/poll_example.py index 4fee73fd2..fe1ac644f 100644 --- a/examples/poll_example.py +++ b/examples/poll_example.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# This is an example file to create polls and handle poll answers +# This is an example file to create quiz polls import telebot API_TOKEN = "" @@ -8,38 +8,20 @@ bot = telebot.TeleBot(API_TOKEN) -DATA = {} - - @bot.message_handler(commands=["poll"]) def create_poll(message): bot.send_message(message.chat.id, "English Article Test") answer_options = ["a", "an", "the", "-"] - # is_anonymous = False -- if you want to check the user answer like here - poll = bot.send_poll( + bot.send_poll( chat_id=message.chat.id, question="We are going to '' park.", options=answer_options, + type='quiz', + correct_option_id=2, is_anonymous=False, ) - # store the poll_id against user_id in the database - poll_id = poll.poll.id - DATA[message.chat.id] = poll_id - - -@bot.poll_answer_handler() -def handle_poll_answers(poll): - - if DATA.get(poll.user.id) == poll.poll_id: - # to check the correct answer - user_answer = poll.option_ids[0] - correct_answer = 2 # "the" is the correct answer - if user_answer == correct_answer: - bot.send_message(poll.user.id, "Good! You're right.") - else: - bot.send_message(poll.user.id, 'The correct answer is "the" .') bot.infinity_polling() From ed6d6cc03fb556d1509c45f725bf0179300b875f Mon Sep 17 00:00:00 2001 From: Muhammad Aadil Date: Sat, 4 Feb 2023 11:22:49 +0500 Subject: [PATCH 1255/1808] add poll answer handler to poll_example.py to show the example to send next poll or log user answers --- examples/poll_example.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/poll_example.py b/examples/poll_example.py index fe1ac644f..8a3e16b7a 100644 --- a/examples/poll_example.py +++ b/examples/poll_example.py @@ -17,11 +17,16 @@ def create_poll(message): chat_id=message.chat.id, question="We are going to '' park.", options=answer_options, - type='quiz', + type="quiz", correct_option_id=2, is_anonymous=False, ) +@bot.poll_answer_handler() +def handle_poll(poll): + # This handler can be used to log User answers and to send next poll + pass + bot.infinity_polling() From c39c050abfbd767b57bd2724750489a054a1a315 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Feb 2023 15:32:55 +0400 Subject: [PATCH 1256/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a2bbd81c..ffcc909bb 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.4! +##

Supported Bot API version: 6.5!

Official documentation

Official ru documentation

From 2e5fb10430f3306089b277d56a860600c5c8dd75 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 16:02:18 +0400 Subject: [PATCH 1257/1808] Added the class KeyboardButtonRequestUser and the field request_user to the class KeyboardButton. --- telebot/types.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 730a0d4f3..e8389ab7d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2245,17 +2245,22 @@ class KeyboardButton(Dictionaryable, JsonSerializable): will be able to send a “web_app_data” service message. Available in private chats only. :type web_app: :class:`telebot.types.WebAppInfo` + :param request_user: Optional. If specified, pressing the button will open a list of suitable users. Tapping on any user + will send their identifier to the bot in a “user_shared” service message. Available in private chats only. + :type request_user: :class:`telebot.types.KeyboardButtonRequestUser` + :return: Instance of the class :rtype: :class:`telebot.types.KeyboardButton` """ def __init__(self, text: str, request_contact: Optional[bool]=None, request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, - web_app: WebAppInfo=None): + web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUser]=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location self.request_poll: KeyboardButtonPollType = request_poll self.web_app: WebAppInfo = web_app + self.request_user: KeyboardButtonRequestUser = request_user def to_json(self): return json.dumps(self.to_dict()) @@ -2270,6 +2275,8 @@ def to_dict(self): json_dict['request_poll'] = self.request_poll.to_dict() if self.web_app is not None: json_dict['web_app'] = self.web_app.to_dict() + if self.request_user is not None: + json_dict['request_user'] = self.request_user.to_dict() return json_dict @@ -7012,5 +7019,41 @@ def __init__(self) -> None: pass +class KeyboardButtonRequestUser(Dictionaryable): + """ + This object defines the criteria used to request a suitable user. + The identifier of the selected user will be shared with the bot when the corresponding button is pressed. + + Telegram documentation: https://core.telegram.org/bots/api#keyboardbuttonrequestuser + + :param request_id: Signed 32-bit identifier of the request, which will be received back in the UserShared object. + Must be unique within the message + :type request_id: :obj:`int` + + :param user_is_bot: Optional. Pass True to request a bot, pass False to request a regular user. + If not specified, no additional restrictions are applied. + :type user_is_bot: :obj:`bool` + + :param user_is_premium: Optional. Pass True to request a premium user, pass False to request a non-premium user. + If not specified, no additional restrictions are applied. + :type user_is_premium: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.KeyboardButtonRequestUser` + + """ + + def __init__(self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None) -> None: + self.request_id: int = request_id + self.user_is_bot: Optional[bool] = user_is_bot + self.user_is_premium: Optional[bool] = user_is_premium + + def to_dict(self) -> dict: + data = {'request_id': self.request_id} + if self.user_is_bot is not None: + data['user_is_bot'] = self.user_is_bot + if self.user_is_premium is not None: + data['user_is_premium'] = self.user_is_premium + return data From 3e0d69f7f4d8b67253954d1988fe3132141e4176 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 4 Feb 2023 16:16:34 +0400 Subject: [PATCH 1258/1808] fixed checks x1 --- telebot/types.py | 77 ++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index e8389ab7d..c788901c5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2217,6 +2217,45 @@ def __init__(self, type=''): def to_dict(self): return {'type': self.type} + + + +class KeyboardButtonRequestUser(Dictionaryable): + """ + This object defines the criteria used to request a suitable user. + The identifier of the selected user will be shared with the bot when the corresponding button is pressed. + + Telegram documentation: https://core.telegram.org/bots/api#keyboardbuttonrequestuser + + :param request_id: Signed 32-bit identifier of the request, which will be received back in the UserShared object. + Must be unique within the message + :type request_id: :obj:`int` + + :param user_is_bot: Optional. Pass True to request a bot, pass False to request a regular user. + If not specified, no additional restrictions are applied. + :type user_is_bot: :obj:`bool` + + :param user_is_premium: Optional. Pass True to request a premium user, pass False to request a non-premium user. + If not specified, no additional restrictions are applied. + :type user_is_premium: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.KeyboardButtonRequestUser` + + """ + + def __init__(self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None) -> None: + self.request_id: int = request_id + self.user_is_bot: Optional[bool] = user_is_bot + self.user_is_premium: Optional[bool] = user_is_premium + + def to_dict(self) -> dict: + data = {'request_id': self.request_id} + if self.user_is_bot is not None: + data['user_is_bot'] = self.user_is_bot + if self.user_is_premium is not None: + data['user_is_premium'] = self.user_is_premium + return data class KeyboardButton(Dictionaryable, JsonSerializable): @@ -7019,41 +7058,3 @@ def __init__(self) -> None: pass -class KeyboardButtonRequestUser(Dictionaryable): - """ - This object defines the criteria used to request a suitable user. - The identifier of the selected user will be shared with the bot when the corresponding button is pressed. - - Telegram documentation: https://core.telegram.org/bots/api#keyboardbuttonrequestuser - - :param request_id: Signed 32-bit identifier of the request, which will be received back in the UserShared object. - Must be unique within the message - :type request_id: :obj:`int` - - :param user_is_bot: Optional. Pass True to request a bot, pass False to request a regular user. - If not specified, no additional restrictions are applied. - :type user_is_bot: :obj:`bool` - - :param user_is_premium: Optional. Pass True to request a premium user, pass False to request a non-premium user. - If not specified, no additional restrictions are applied. - :type user_is_premium: :obj:`bool` - - :return: Instance of the class - :rtype: :class:`telebot.types.KeyboardButtonRequestUser` - - """ - - def __init__(self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None) -> None: - self.request_id: int = request_id - self.user_is_bot: Optional[bool] = user_is_bot - self.user_is_premium: Optional[bool] = user_is_premium - - def to_dict(self) -> dict: - data = {'request_id': self.request_id} - if self.user_is_bot is not None: - data['user_is_bot'] = self.user_is_bot - if self.user_is_premium is not None: - data['user_is_premium'] = self.user_is_premium - return data - - From 4d7f5310fb7a25d60c47960dd8b34d6cbfca6283 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 16:24:05 +0400 Subject: [PATCH 1259/1808] Added the class KeyboardButtonRequestChat and the field request_chat to the class KeyboardButton. --- telebot/types.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index c788901c5..868413a48 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2258,6 +2258,78 @@ def to_dict(self) -> dict: return data +class KeyboardButtonRequestChat(Dictionaryable): + """ + This object defines the criteria used to request a suitable chat. The identifier of the selected chat will + be shared with the bot when the corresponding button is pressed. + + Telegram documentation: https://core.telegram.org/bots/api#keyboardbuttonrequestchat + + :param request_id: Signed 32-bit identifier of the request, which will be received back in the ChatShared object. + Must be unique within the message + :type request_id: :obj:`int` + + :param chat_is_channel: Pass True to request a channel chat, pass False to request a group or a supergroup chat. + :type chat_is_channel: :obj:`bool` + + :param chat_is_forum: Optional. Pass True to request a forum supergroup, pass False to request a non-forum chat. + If not specified, no additional restrictions are applied. + :type chat_is_forum: :obj:`bool` + + :param chat_has_username: Optional. Pass True to request a supergroup or a channel with a username, pass False to request a + chat without a username. If not specified, no additional restrictions are applied. + :type chat_has_username: :obj:`bool` + + :param chat_is_created: Optional. Pass True to request a chat owned by the user. Otherwise, no additional restrictions are applied. + :type chat_is_created: :obj:`bool` + + :param user_administrator_rights: Optional. A JSON-serialized object listing the required administrator rights of the user in the chat. + The rights must be a superset of bot_administrator_rights. If not specified, no additional restrictions are applied. + :type user_administrator_rights: :class:`telebot.types.ChatAdministratorRights` + + :param bot_administrator_rights: Optional. A JSON-serialized object listing the required administrator rights of the bot in the chat. + The rights must be a subset of user_administrator_rights. If not specified, no additional restrictions are applied. + :type bot_administrator_rights: :class:`telebot.types.ChatAdministratorRights` + + :param bot_is_member: Optional. Pass True to request a chat where the bot is a member. Otherwise, no additional restrictions are applied. + :type bot_is_member: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`telebot.types.KeyboardButtonRequestChat` + """ + + def __init__(self, request_id: int, chat_is_channel: bool, chat_is_forum: Optional[bool]=None, + chat_has_username: Optional[bool]=None, chat_is_created: Optional[bool]=None, + user_administrator_rights: Optional[ChatAdministratorRights]=None, + bot_administrator_rights: Optional[ChatAdministratorRights]=None, bot_is_member: Optional[bool]=None) -> None: + self.request_id: int = request_id + self.chat_is_channel: bool = chat_is_channel + self.chat_is_forum: Optional[bool] = chat_is_forum + self.chat_has_username: Optional[bool] = chat_has_username + self.chat_is_created: Optional[bool] = chat_is_created + self.user_administrator_rights: Optional[ChatAdministratorRights] = user_administrator_rights + self.bot_administrator_rights: Optional[ChatAdministratorRights] = bot_administrator_rights + self.bot_is_member: Optional[bool] = bot_is_member + + + def to_dict(self) -> dict: + data = {'request_id': self.request_id, 'chat_is_channel': self.chat_is_channel} + if self.chat_is_forum is not None: + data['chat_is_forum'] = self.chat_is_forum + if self.chat_has_username is not None: + data['chat_has_username'] = self.chat_has_username + if self.chat_is_created is not None: + data['chat_is_created'] = self.chat_is_created + if self.user_administrator_rights is not None: + data['user_administrator_rights'] = self.user_administrator_rights.to_dict() + if self.bot_administrator_rights is not None: + data['bot_administrator_rights'] = self.bot_administrator_rights.to_dict() + if self.bot_is_member is not None: + data['bot_is_member'] = self.bot_is_member + return data + + + class KeyboardButton(Dictionaryable, JsonSerializable): """ This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields web_app, request_contact, request_location, and request_poll are mutually exclusive. @@ -2288,18 +2360,25 @@ class KeyboardButton(Dictionaryable, JsonSerializable): will send their identifier to the bot in a “user_shared” service message. Available in private chats only. :type request_user: :class:`telebot.types.KeyboardButtonRequestUser` + :param request_chat: Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will + send its identifier to the bot in a “chat_shared” service message. Available in private chats only. + :type request_chat: :class:`telebot.types.KeyboardButtonRequestChat` + :return: Instance of the class :rtype: :class:`telebot.types.KeyboardButton` """ def __init__(self, text: str, request_contact: Optional[bool]=None, request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, - web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUser]=None): + web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUser]=None, + request_chat: Optional[KeyboardButtonRequestChat]=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location self.request_poll: KeyboardButtonPollType = request_poll self.web_app: WebAppInfo = web_app self.request_user: KeyboardButtonRequestUser = request_user + self.request_chat: KeyboardButtonRequestChat = request_chat + def to_json(self): return json.dumps(self.to_dict()) From a3891ff36395e6dff85865beaec4a8325a8405f2 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 16:29:48 +0400 Subject: [PATCH 1260/1808] Pep 0563 proposed change https://peps.python.org/pep-0563/ --- telebot/types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/types.py b/telebot/types.py index 868413a48..01c18aff2 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations from io import IOBase import logging From ae42d0b1fe6790080990483fc9572fa0b02b78fd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 16:54:43 +0400 Subject: [PATCH 1261/1808] Added usershared and user_shared --- telebot/types.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 01c18aff2..96abff4f0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -903,6 +903,9 @@ class Message(JsonDeserializable): the payment. More about payments » :type successful_payment: :class:`telebot.types.SuccessfulPayment` + :param user_shared: Optional. Service message: a user was shared with the bot + :type user_shared: :class:`telebot.types.UserShared` + :param connected_website: Optional. The domain name of the website on which the user has logged in. More about Telegram Login » :type connected_website: :obj:`str` @@ -1152,6 +1155,9 @@ def de_json(cls, json_string): if 'write_access_allowed' in obj: opts['write_access_allowed'] = WriteAccessAllowed.de_json(obj['write_access_allowed']) content_type = 'write_access_allowed' + if 'user_shared' in obj: + opts['user_shared'] = UserShared.de_json(obj['user_shared']) + content_type = 'user_shared' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1247,6 +1253,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.general_forum_topic_hidden: Optional[GeneralForumTopicHidden] = None self.general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = None self.write_access_allowed: Optional[WriteAccessAllowed] = None + self.user_shared: Optional[UserShared] = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -7138,3 +7145,34 @@ def __init__(self) -> None: pass +class UserShared(JsonDeserializable): + """ + This object contains information about the user whose identifier was shared with the bot using a + `telebot.types.KeyboardButtonRequestUser` button. + + Telegram documentation: https://core.telegram.org/bots/api#usershared + + :param request_id: identifier of the request + :type request_id: :obj:`int` + + :param user_id: Identifier of the shared user. This number may have more than 32 significant bits and some programming + languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit + integer or double-precision float type are safe for storing this identifier. The bot may not have access to the user + and could be unable to use this identifier, unless the user is already known to the bot by some other means. + :type user_id: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.UserShared` + """ + + @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, request_id: int, user_id: int) -> None: + self.request_id: int = request_id + self.user_id: int = user_id + + From 4000c9fb4825567b4c3eeeee77a0b2256ccc3292 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 16:58:48 +0400 Subject: [PATCH 1262/1808] Added chat_shared and chatshared --- telebot/types.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 96abff4f0..700436efc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -906,6 +906,9 @@ class Message(JsonDeserializable): :param user_shared: Optional. Service message: a user was shared with the bot :type user_shared: :class:`telebot.types.UserShared` + :param chat_shared: Optional. Service message: a chat was shared with the bot + :type chat_shared: :class:`telebot.types.ChatShared` + :param connected_website: Optional. The domain name of the website on which the user has logged in. More about Telegram Login » :type connected_website: :obj:`str` @@ -1158,6 +1161,9 @@ def de_json(cls, json_string): if 'user_shared' in obj: opts['user_shared'] = UserShared.de_json(obj['user_shared']) content_type = 'user_shared' + if 'chat_shared' in obj: + opts['chat_shared'] = ChatShared.de_json(obj['chat_shared']) + content_type = 'chat_shared' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1254,6 +1260,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = None self.write_access_allowed: Optional[WriteAccessAllowed] = None self.user_shared: Optional[UserShared] = None + self.chat_shared: Optional[ChatShared] = None for key in options: setattr(self, key, options[key]) self.json = json_string @@ -7175,4 +7182,36 @@ def __init__(self, request_id: int, user_id: int) -> None: self.request_id: int = request_id self.user_id: int = user_id + +class ChatShared(JsonDeserializable): + """ + This object contains information about the chat whose identifier was shared with the bot using a + `telebot.types.KeyboardButtonRequestChat` button. + + Telegram documentation: https://core.telegram.org/bots/api#Chatshared + + :param request_id: identifier of the request + :type request_id: :obj:`int` + + :param chat_id: Identifier of the shared chat. This number may have more than 32 significant bits and some programming + languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit + integer or double-precision float type are safe for storing this identifier. The bot may not have access to the chat + and could be unable to use this identifier, unless the chat is already known to the bot by some other means. + :type chat_id: :obj:`int` + + :return: Instance of the class + :rtype: :class:`telebot.types.ChatShared` + """ + + @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, request_id: int, chat_id: int) -> None: + self.request_id: int = request_id + self.chat_id: int = chat_id + + \ No newline at end of file From 9e68f76f5d5f31646444655784a2e42bc3c1525d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 18:57:06 +0400 Subject: [PATCH 1263/1808] Replaced the fields can_send_media_messages in the classes ChatMemberRestricted and ChatPermissions with separate fields can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and can_send_voice_notes for different media types. --- telebot/types.py | 98 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 700436efc..d4545360f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2806,10 +2806,14 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_restrict_members=None, can_promote_members=None, can_change_info=None, can_invite_users=None, can_pin_messages=None, is_member=None, - can_send_messages=None, can_send_media_messages=None, can_send_polls=None, + can_send_messages=None, can_send_audios=None, can_send_documents=None, + can_send_photos=None, can_send_videos=None, can_send_video_notes=None, + can_send_voice_notes=None, + can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, can_manage_chat=None, can_manage_video_chats=None, - until_date=None, can_manage_topics=None, **kwargs): + until_date=None, can_manage_topics=None, + **kwargs): self.user: User = user self.status: str = status self.custom_title: str = custom_title @@ -2825,7 +2829,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_pin_messages: bool = can_pin_messages self.is_member: bool = is_member self.can_send_messages: bool = can_send_messages - self.can_send_media_messages: bool = can_send_media_messages + #self.can_send_media_messages: bool = can_send_media_messages self.can_send_polls: bool = can_send_polls self.can_send_other_messages: bool = can_send_other_messages self.can_add_web_page_previews: bool = can_add_web_page_previews @@ -2834,6 +2838,13 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_manage_voice_chats: bool = self.can_manage_video_chats # deprecated, for backward compatibility self.until_date: int = until_date self.can_manage_topics: bool = can_manage_topics + self.can_send_audios: bool = can_send_audios + self.can_send_documents: bool = can_send_documents + self.can_send_photos: bool = can_send_photos + self.can_send_videos: bool = can_send_videos + self.can_send_video_notes: bool = can_send_video_notes + self.can_send_voice_notes: bool = can_send_voice_notes + class ChatMemberOwner(ChatMember): @@ -2974,9 +2985,23 @@ class ChatMemberRestricted(ChatMember): :param can_send_messages: True, if the user is allowed to send text messages, contacts, locations and venues :type can_send_messages: :obj:`bool` - :param can_send_media_messages: True, if the user is allowed to send audios, documents, photos, videos, video - notes and voice notes - :type can_send_media_messages: :obj:`bool` + :param can_send_audios: True, if the user is allowed to send audios + :type can_send_audios: :obj:`bool` + + :param can_send_documents: True, if the user is allowed to send documents + :type can_send_documents: :obj:`bool` + + :param can_send_photos: True, if the user is allowed to send photos + :type can_send_photos: :obj:`bool` + + :param can_send_videos: True, if the user is allowed to send videos + :type can_send_videos: :obj:`bool` + + :param can_send_video_notes: True, if the user is allowed to send video notes + :type can_send_video_notes: :obj:`bool` + + :param can_send_voice_notes: True, if the user is allowed to send voice notes + :type can_send_voice_notes: :obj:`bool` :param can_send_polls: True, if the user is allowed to send polls :type can_send_polls: :obj:`bool` @@ -3048,19 +3073,33 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): venues :type can_send_messages: :obj:`bool` - :param can_send_media_messages: Optional. True, if the user is allowed to send audios, documents, photos, videos, - video notes and voice notes, implies can_send_messages - :type can_send_media_messages: :obj:`bool` + :param can_send_audios: Optional. True, if the user is allowed to send audios + :type can_send_audios: :obj:`bool` + + :param can_send_documents: Optional. True, if the user is allowed to send documents + :type can_send_documents: :obj:`bool` + + :param can_send_photos: Optional. True, if the user is allowed to send photos + :type can_send_photos: :obj:`bool` + + :param can_send_videos: Optional. True, if the user is allowed to send videos + :type can_send_videos: :obj:`bool` + + :param can_send_video_notes: Optional. True, if the user is allowed to send video notes + :type can_send_video_notes: :obj:`bool` + + :param can_send_voice_notes: Optional. True, if the user is allowed to send voice notes + :type can_send_voice_notes: :obj:`bool` :param can_send_polls: Optional. True, if the user is allowed to send polls, implies can_send_messages :type can_send_polls: :obj:`bool` :param can_send_other_messages: Optional. True, if the user is allowed to send animations, games, stickers and use - inline bots, implies can_send_media_messages + inline bots :type can_send_other_messages: :obj:`bool` :param can_add_web_page_previews: Optional. True, if the user is allowed to add web page previews to their - messages, implies can_send_media_messages + messages :type can_add_web_page_previews: :obj:`bool` :param can_change_info: Optional. True, if the user is allowed to change the chat title, photo and other settings. @@ -3086,13 +3125,15 @@ def de_json(cls, json_string): obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) - def __init__(self, can_send_messages=None, can_send_media_messages=None, - can_send_polls=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, - can_manage_topics=None, **kwargs): + def __init__(self, can_send_messages=None, can_send_audios=None, + can_send_documents=None, can_send_photos=None, + can_send_videos=None, can_send_video_notes=None, + can_send_voice_notes=None, can_send_polls=None, can_send_other_messages=None, + can_add_web_page_previews=None, can_change_info=None, + can_invite_users=None, can_pin_messages=None, + can_manage_topics=None, **kwargs): self.can_send_messages: bool = can_send_messages - self.can_send_media_messages: bool = can_send_media_messages + #self.can_send_media_messages: bool = can_send_media_messages self.can_send_polls: bool = can_send_polls self.can_send_other_messages: bool = can_send_other_messages self.can_add_web_page_previews: bool = can_add_web_page_previews @@ -3100,6 +3141,13 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None, self.can_invite_users: bool = can_invite_users self.can_pin_messages: bool = can_pin_messages self.can_manage_topics: bool = can_manage_topics + self.can_send_audios: bool = can_send_audios + self.can_send_documents: bool = can_send_documents + self.can_send_photos: bool = can_send_photos + self.can_send_videos: bool = can_send_videos + self.can_send_video_notes: bool = can_send_video_notes + self.can_send_voice_notes: bool = can_send_voice_notes + def to_json(self): return json.dumps(self.to_dict()) @@ -3108,8 +3156,19 @@ def to_dict(self): json_dict = dict() if self.can_send_messages is not None: json_dict['can_send_messages'] = self.can_send_messages - if self.can_send_media_messages is not None: - json_dict['can_send_media_messages'] = self.can_send_media_messages + if self.can_send_audios is not None: + json_dict['can_send_audios'] = self.can_send_audios + + if self.can_send_documents is not None: + json_dict['can_send_documents'] = self.can_send_documents + if self.can_send_photos is not None: + json_dict['can_send_photos'] = self.can_send_photos + if self.can_send_videos is not None: + json_dict['can_send_videos'] = self.can_send_videos + if self.can_send_video_notes is not None: + json_dict['can_send_video_notes'] = self.can_send_video_notes + if self.can_send_voice_notes is not None: + json_dict['can_send_voice_notes'] = self.can_send_voice_notes if self.can_send_polls is not None: json_dict['can_send_polls'] = self.can_send_polls if self.can_send_other_messages is not None: @@ -7214,4 +7273,3 @@ def __init__(self, request_id: int, chat_id: int) -> None: self.request_id: int = request_id self.chat_id: int = chat_id - \ No newline at end of file From d0d03d0c097e1973e506db5d64d1cc895e22479c Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 19:59:49 +0400 Subject: [PATCH 1264/1808] Added use_independent_chat_permissions for restrictchatmember --- telebot/__init__.py | 11 +++++++++-- telebot/apihelper.py | 5 ++++- telebot/async_telebot.py | 11 +++++++++-- telebot/asyncio_helper.py | 5 ++++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6f6b4524b..095b12e2c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2917,7 +2917,8 @@ def restrict_chat_member( can_add_web_page_previews: Optional[bool]=None, can_change_info: Optional[bool]=None, can_invite_users: Optional[bool]=None, - can_pin_messages: Optional[bool]=None) -> bool: + can_pin_messages: Optional[bool]=None, + use_independent_chat_permissions: Optional[bool]=None) -> bool: """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -2965,6 +2966,12 @@ def restrict_chat_member( :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups :type can_pin_messages: :obj:`bool` + :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, + the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, + can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and + can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. + :type use_independent_chat_permissions: :obj:`bool` + :return: True on success :rtype: :obj:`bool` """ @@ -2973,7 +2980,7 @@ def restrict_chat_member( can_send_messages, can_send_media_messages, can_send_polls, can_send_other_messages, can_add_web_page_previews, can_change_info, - can_invite_users, can_pin_messages) + can_invite_users, can_pin_messages, use_independent_chat_permissions) def promote_chat_member( self, chat_id: Union[int, str], user_id: int, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1086f54e9..aa9e337fd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -972,7 +972,7 @@ def restrict_chat_member( can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None): + can_invite_users=None, can_pin_messages=None, use_independent_chat_permissions=None): method_url = 'restrictChatMember' permissions = {} if can_send_messages is not None: @@ -991,6 +991,9 @@ def restrict_chat_member( permissions['can_invite_users'] = can_invite_users if can_pin_messages is not None: permissions['can_pin_messages'] = can_pin_messages + if use_independent_chat_permissions is not None: + permissions['use_independent_chat_permissions'] = use_independent_chat_permissions + permissions_json = json.dumps(permissions) payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} if until_date is not None: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8dbf55a68..d976c7844 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3775,7 +3775,8 @@ async def restrict_chat_member( can_add_web_page_previews: Optional[bool]=None, can_change_info: Optional[bool]=None, can_invite_users: Optional[bool]=None, - can_pin_messages: Optional[bool]=None) -> bool: + can_pin_messages: Optional[bool]=None, + use_independent_chat_permissions: Optional[bool]=None) -> bool: """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have @@ -3823,6 +3824,12 @@ async def restrict_chat_member( :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups :type can_pin_messages: :obj:`bool` + :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, + the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, + can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and + can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. + :type use_independent_chat_permissions: :obj:`bool` + :return: True on success :rtype: :obj:`bool` """ @@ -3831,7 +3838,7 @@ async def restrict_chat_member( can_send_messages, can_send_media_messages, can_send_polls, can_send_other_messages, can_add_web_page_previews, can_change_info, - can_invite_users, can_pin_messages) + can_invite_users, can_pin_messages, use_independent_chat_permissions) async def promote_chat_member( self, chat_id: Union[int, str], user_id: int, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 219cc1d7a..df4b17cb5 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -964,7 +964,7 @@ async def restrict_chat_member( can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None): + can_invite_users=None, can_pin_messages=None, use_independent_chat_permissions=None): method_url = 'restrictChatMember' permissions = {} if can_send_messages is not None: @@ -983,6 +983,9 @@ async def restrict_chat_member( permissions['can_invite_users'] = can_invite_users if can_pin_messages is not None: permissions['can_pin_messages'] = can_pin_messages + if use_independent_chat_permissions is not None: + permissions['use_independent_chat_permissions'] = use_independent_chat_permissions + permissions_json = json.dumps(permissions) payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} if until_date is not None: From d1348606e33b005b47adf757f44a0cd9f6e48dd3 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 20:04:07 +0400 Subject: [PATCH 1265/1808] Added use_independent_chat_permissions to setchatpermissions --- telebot/__init__.py | 11 +++++++++-- telebot/apihelper.py | 6 ++++-- telebot/async_telebot.py | 11 +++++++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 095b12e2c..0d6e37b5f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3138,7 +3138,8 @@ def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union return apihelper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) def set_chat_permissions( - self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: + self, chat_id: Union[int, str], permissions: types.ChatPermissions, + use_independent_chat_permissions: Optional[bool]=None) -> bool: """ Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work @@ -3153,10 +3154,16 @@ def set_chat_permissions( :param permissions: New default chat permissions :type permissions: :class:`telebot.types..ChatPermissions` + :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, + the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, + can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and + can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. + :type use_independent_chat_permissions: :obj:`bool` + :return: True on success :rtype: :obj:`bool` """ - return apihelper.set_chat_permissions(self.token, chat_id, permissions) + return apihelper.set_chat_permissions(self.token, chat_id, permissions, use_independent_chat_permissions) def create_chat_invite_link( self, chat_id: Union[int, str], diff --git a/telebot/apihelper.py b/telebot/apihelper.py index aa9e337fd..82e1269da 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -993,7 +993,7 @@ def restrict_chat_member( permissions['can_pin_messages'] = can_pin_messages if use_independent_chat_permissions is not None: permissions['use_independent_chat_permissions'] = use_independent_chat_permissions - + permissions_json = json.dumps(permissions) payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} if until_date is not None: @@ -1059,12 +1059,14 @@ def unban_chat_sender_chat(token, chat_id, sender_chat_id): return _make_request(token, method_url, params=payload, method='post') -def set_chat_permissions(token, chat_id, permissions): +def set_chat_permissions(token, chat_id, permissions, use_independent_chat_permissions=None): method_url = 'setChatPermissions' payload = { 'chat_id': chat_id, 'permissions': permissions.to_json() } + if use_independent_chat_permissions is not None: + payload['use_independent_chat_permissions'] = use_independent_chat_permissions return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d976c7844..67d752907 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3998,7 +3998,8 @@ async def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: return await asyncio_helper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) async def set_chat_permissions( - self, chat_id: Union[int, str], permissions: types.ChatPermissions) -> bool: + self, chat_id: Union[int, str], permissions: types.ChatPermissions, + use_independent_chat_permissions: Optional[bool]=None) -> bool: """ Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work @@ -4013,10 +4014,16 @@ async def set_chat_permissions( :param permissions: New default chat permissions :type permissions: :class:`telebot.types..ChatPermissions` + :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, + the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, + can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and + can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. + :type use_independent_chat_permissions: :obj:`bool` + :return: True on success :rtype: :obj:`bool` """ - return await asyncio_helper.set_chat_permissions(self.token, chat_id, permissions) + return await asyncio_helper.set_chat_permissions(self.token, chat_id, permissions, use_independent_chat_permissions) async def create_chat_invite_link( self, chat_id: Union[int, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index df4b17cb5..ddad140ce 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1049,12 +1049,14 @@ async def unban_chat_sender_chat(token, chat_id, sender_chat_id): payload = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} return await _process_request(token, method_url, params=payload, method='post') -async def set_chat_permissions(token, chat_id, permissions): +async def set_chat_permissions(token, chat_id, permissions, use_independent_chat_permissions=None): method_url = 'setChatPermissions' payload = { 'chat_id': chat_id, 'permissions': permissions.to_json() } + if use_independent_chat_permissions is not None: + payload['use_independent_chat_permissions'] = use_independent_chat_permissions return await _process_request(token, method_url, params=payload, method='post') From 2094120ec78ddce8fa9c54f0b97bd4df7fc07ff8 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 20:07:01 +0400 Subject: [PATCH 1266/1808] Added user_chat_id to ChatJoinRequest; And, i corrected typehints --- telebot/types.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index d4545360f..082acf1e9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -289,9 +289,16 @@ class ChatJoinRequest(JsonDeserializable): :param chat: Chat to which the request was sent :type chat: :class:`telebot.types.Chat` - :param from: User that sent the join request + :param from_user: User that sent the join request :type from_user: :class:`telebot.types.User` + :param user_chat_id: Optional. Identifier of a private chat with the user who sent the join request. + This number may have more than 32 significant bits and some programming languages may have difficulty/silent + defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision + float type are safe for storing this identifier. The bot can use this identifier for 24 hours to send messages + until the join request is processed, assuming no other administrator contacted the user. + :type user_chat_id: :obj:`int` + :param date: Date the request was sent in Unix time :type date: :obj:`int` @@ -313,12 +320,13 @@ 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, bio=None, invite_link=None, **kwargs): - self.chat = chat - self.from_user = from_user - self.date = date - self.bio = bio - self.invite_link = invite_link + def __init__(self, chat, from_user, user_chat_id, date, bio=None, invite_link=None, **kwargs): + self.chat: Chat = chat + self.from_user: User = from_user + self.date: str = date + self.bio: str = bio + self.invite_link: ChatInviteLink = invite_link + self.user_chat_id: int = user_chat_id class WebhookInfo(JsonDeserializable): """ From a9b878107cef661ec7acca5abdfc79a5ef94c39d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 4 Feb 2023 22:24:26 +0400 Subject: [PATCH 1267/1808] Fix can_send_media_messages param, added warnings --- telebot/__init__.py | 31 ++++++++++++++++++++++++++----- telebot/apihelper.py | 30 +++++------------------------- telebot/async_telebot.py | 30 +++++++++++++++++++++++++----- telebot/asyncio_helper.py | 29 ++++------------------------- telebot/types.py | 16 +++++++++++++++- 5 files changed, 75 insertions(+), 61 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0d6e37b5f..70daddccc 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2918,6 +2918,7 @@ def restrict_chat_member( can_change_info: Optional[bool]=None, can_invite_users: Optional[bool]=None, can_pin_messages: Optional[bool]=None, + permissions: Optional[types.ChatPermissions]=None, use_independent_chat_permissions: Optional[bool]=None) -> bool: """ Use this method to restrict a user in a supergroup. @@ -2926,6 +2927,10 @@ def restrict_chat_member( Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember + .. warning:: + Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of + passing all boolean parameters. Those boolean parameters won't be supported soon, so please take it into consideration. + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2972,15 +2977,31 @@ def restrict_chat_member( can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. :type use_independent_chat_permissions: :obj:`bool` + :param permissions: Pass ChatPermissions object to set all permissions at once. Use this param instead of + passing all boolean parameters. + :type permissions: :class:`telebot.types.ChatPermissions` + :return: True on success :rtype: :obj:`bool` """ + + if permissions is None: + permissions = types.ChatPermissions( + can_send_messages=can_send_messages, + can_send_media_messages=can_send_media_messages, + can_send_polls=can_send_polls, + can_send_other_messages=can_send_other_messages, + can_add_web_page_previews=can_add_web_page_previews, + can_change_info=can_change_info, + can_invite_users=can_invite_users, + can_pin_messages=can_pin_messages + ) + logger.warning( + 'Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of ' + 'passing all boolean parameters. Those boolean parameters won\'t be supported soon, so please take it into consideration.' + ) return apihelper.restrict_chat_member( - self.token, chat_id, user_id, until_date, - can_send_messages, can_send_media_messages, - can_send_polls, can_send_other_messages, - can_add_web_page_previews, can_change_info, - can_invite_users, can_pin_messages, use_independent_chat_permissions) + self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) def promote_chat_member( self, chat_id: Union[int, str], user_id: int, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 82e1269da..2a95eb076 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -968,39 +968,19 @@ def unban_chat_member(token, chat_id, user_id, only_if_banned): def restrict_chat_member( - token, chat_id, user_id, until_date=None, - can_send_messages=None, can_send_media_messages=None, - can_send_polls=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, use_independent_chat_permissions=None): + token, chat_id, user_id, permissions, until_date=None, + use_independent_chat_permissions=None): method_url = 'restrictChatMember' - permissions = {} - if can_send_messages is not None: - permissions['can_send_messages'] = can_send_messages - if can_send_media_messages is not None: - permissions['can_send_media_messages'] = can_send_media_messages - if can_send_polls is not None: - permissions['can_send_polls'] = can_send_polls - if can_send_other_messages is not None: - permissions['can_send_other_messages'] = can_send_other_messages - if can_add_web_page_previews is not None: - permissions['can_add_web_page_previews'] = can_add_web_page_previews - if can_change_info is not None: - permissions['can_change_info'] = can_change_info - if can_invite_users is not None: - permissions['can_invite_users'] = can_invite_users - if can_pin_messages is not None: - permissions['can_pin_messages'] = can_pin_messages + payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions.to_json()} + if use_independent_chat_permissions is not None: permissions['use_independent_chat_permissions'] = use_independent_chat_permissions - - permissions_json = json.dumps(permissions) - payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} if until_date is not None: if isinstance(until_date, datetime): payload['until_date'] = until_date.timestamp() else: payload['until_date'] = until_date + return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 67d752907..ebfaca531 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3776,6 +3776,7 @@ async def restrict_chat_member( can_change_info: Optional[bool]=None, can_invite_users: Optional[bool]=None, can_pin_messages: Optional[bool]=None, + permissions: Optional[types.ChatPermissions]=None, use_independent_chat_permissions: Optional[bool]=None) -> bool: """ Use this method to restrict a user in a supergroup. @@ -3784,6 +3785,10 @@ async def restrict_chat_member( Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember + .. warning:: + Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of + passing all boolean parameters. Those boolean parameters won't be supported soon, so please take it into consideration. + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -3830,15 +3835,30 @@ async def restrict_chat_member( can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. :type use_independent_chat_permissions: :obj:`bool` + :param permissions: Pass ChatPermissions object to set all permissions at once. Use this parameter instead of + passing all boolean parameters to avoid backward compatibility problems in future. + :type permissions: :obj:`types.ChatPermissions` + :return: True on success :rtype: :obj:`bool` """ + if permissions is None: + permissions = types.ChatPermissions( + can_send_messages=can_send_messages, + can_send_media_messages=can_send_media_messages, + can_send_polls=can_send_polls, + can_send_other_messages=can_send_other_messages, + can_add_web_page_previews=can_add_web_page_previews, + can_change_info=can_change_info, + can_invite_users=can_invite_users, + can_pin_messages=can_pin_messages + ) + logger.warning( + 'Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of ' + 'passing all boolean parameters. Those boolean parameters won\'t be supported soon, so please take it into consideration.' + ) return await asyncio_helper.restrict_chat_member( - self.token, chat_id, user_id, until_date, - can_send_messages, can_send_media_messages, - can_send_polls, can_send_other_messages, - can_add_web_page_previews, can_change_info, - can_invite_users, can_pin_messages, use_independent_chat_permissions) + self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) async def promote_chat_member( self, chat_id: Union[int, str], user_id: int, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index ddad140ce..9fa7345dd 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -960,34 +960,13 @@ async def unban_chat_member(token, chat_id, user_id, only_if_banned): async def restrict_chat_member( - token, chat_id, user_id, until_date=None, - can_send_messages=None, can_send_media_messages=None, - can_send_polls=None, can_send_other_messages=None, - can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, use_independent_chat_permissions=None): + token, chat_id, user_id, permissions, until_date=None, + use_independent_chat_permissions=None): method_url = 'restrictChatMember' - permissions = {} - if can_send_messages is not None: - permissions['can_send_messages'] = can_send_messages - if can_send_media_messages is not None: - permissions['can_send_media_messages'] = can_send_media_messages - if can_send_polls is not None: - permissions['can_send_polls'] = can_send_polls - if can_send_other_messages is not None: - permissions['can_send_other_messages'] = can_send_other_messages - if can_add_web_page_previews is not None: - permissions['can_add_web_page_previews'] = can_add_web_page_previews - if can_change_info is not None: - permissions['can_change_info'] = can_change_info - if can_invite_users is not None: - permissions['can_invite_users'] = can_invite_users - if can_pin_messages is not None: - permissions['can_pin_messages'] = can_pin_messages + payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions.to_json()} + if use_independent_chat_permissions is not None: permissions['use_independent_chat_permissions'] = use_independent_chat_permissions - - permissions_json = json.dumps(permissions) - payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions_json} if until_date is not None: if isinstance(until_date, datetime): payload['until_date'] = until_date.timestamp() diff --git a/telebot/types.py b/telebot/types.py index 082acf1e9..b53706e47 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2418,6 +2418,8 @@ def to_dict(self): json_dict['web_app'] = self.web_app.to_dict() if self.request_user is not None: json_dict['request_user'] = self.request_user.to_dict() + if self.request_chat is not None: + json_dict['request_chat'] = self.request_chat.to_dict() return json_dict @@ -3124,6 +3126,10 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): value of can_pin_messages :type can_manage_topics: :obj:`bool` + :param can_send_media_messages: deprecated. True, if the user is allowed to send audios, documents, photos, videos, + video notes and voice notes + :type can_send_media_messages: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ChatPermissions` """ @@ -3133,7 +3139,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) - def __init__(self, can_send_messages=None, can_send_audios=None, + def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send_audios=None, can_send_documents=None, can_send_photos=None, can_send_videos=None, can_send_video_notes=None, can_send_voice_notes=None, can_send_polls=None, can_send_other_messages=None, @@ -3156,6 +3162,14 @@ def __init__(self, can_send_messages=None, can_send_audios=None, self.can_send_video_notes: bool = can_send_video_notes self.can_send_voice_notes: bool = can_send_voice_notes + if can_send_media_messages is not None: + self.can_send_audios = can_send_media_messages + self.can_send_documents = can_send_media_messages + self.can_send_photos = can_send_media_messages + self.can_send_videos = can_send_media_messages + self.can_send_video_notes = can_send_media_messages + self.can_send_voice_notes = can_send_media_messages + def to_json(self): return json.dumps(self.to_dict()) From 4179e502c38eaf4069d1638c79e03d1014a458b4 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 5 Feb 2023 11:13:31 +0400 Subject: [PATCH 1268/1808] Fix description --- telebot/__init__.py | 6 ++---- telebot/async_telebot.py | 6 ++---- telebot/types.py | 1 + 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 70daddccc..46669a695 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2928,8 +2928,7 @@ def restrict_chat_member( Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember .. warning:: - Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of - passing all boolean parameters. Those boolean parameters won't be supported soon, so please take it into consideration. + Individual parameters are deprecated and will be removed, use 'permissions' instead. :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) @@ -2997,8 +2996,7 @@ def restrict_chat_member( can_pin_messages=can_pin_messages ) logger.warning( - 'Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of ' - 'passing all boolean parameters. Those boolean parameters won\'t be supported soon, so please take it into consideration.' + "Individual parameters are deprecated and will be removed, use 'permissions' instead." ) return apihelper.restrict_chat_member( self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ebfaca531..d1fa77b9b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3786,8 +3786,7 @@ async def restrict_chat_member( Telegram documentation: https://core.telegram.org/bots/api#restrictchatmember .. warning:: - Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of - passing all boolean parameters. Those boolean parameters won't be supported soon, so please take it into consideration. + Individual parameters are deprecated and will be removed, use 'permissions' instead :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) @@ -3854,8 +3853,7 @@ async def restrict_chat_member( can_pin_messages=can_pin_messages ) logger.warning( - 'Please pass `telebot.types.ChatPermissions` object to `permissions` parameter instead of ' - 'passing all boolean parameters. Those boolean parameters won\'t be supported soon, so please take it into consideration.' + "Individual parameters are deprecated and will be removed, use 'permissions' instead." ) return await asyncio_helper.restrict_chat_member( self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) diff --git a/telebot/types.py b/telebot/types.py index b53706e47..cb4facb4a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3163,6 +3163,7 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send self.can_send_voice_notes: bool = can_send_voice_notes if can_send_media_messages is not None: + logger.warning("can_send_media_messages is deprecated. Use individual parameters like can_send_audios, can_send_documents, etc.") self.can_send_audios = can_send_media_messages self.can_send_documents = can_send_media_messages self.can_send_photos = can_send_media_messages From 3d2c5c959097090ff7bc676d432a2a45547bf084 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 5 Feb 2023 13:11:07 +0300 Subject: [PATCH 1269/1808] Bump version to 4.10.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index e4107399e..eafc78618 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.9.0' +release = '4.10.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 1e28db82b..ad97ea2e5 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.9.0' +__version__ = '4.10.0' From b4196f58915f24727ab1c841639b720320dfb473 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 9 Feb 2023 17:56:10 +0300 Subject: [PATCH 1270/1808] restrict_chat_member fix --- telebot/apihelper.py | 2 +- telebot/asyncio_helper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 2a95eb076..a555f35b4 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -974,7 +974,7 @@ def restrict_chat_member( payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions.to_json()} if use_independent_chat_permissions is not None: - permissions['use_independent_chat_permissions'] = use_independent_chat_permissions + payload['use_independent_chat_permissions'] = use_independent_chat_permissions if until_date is not None: if isinstance(until_date, datetime): payload['until_date'] = until_date.timestamp() diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 9fa7345dd..486749882 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -966,7 +966,7 @@ async def restrict_chat_member( payload = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions.to_json()} if use_independent_chat_permissions is not None: - permissions['use_independent_chat_permissions'] = use_independent_chat_permissions + payload['use_independent_chat_permissions'] = use_independent_chat_permissions if until_date is not None: if isinstance(until_date, datetime): payload['until_date'] = until_date.timestamp() From 8dc4e77287e81dca49386fc5fc6489bfd38b30dd Mon Sep 17 00:00:00 2001 From: coder2020official Date: Thu, 9 Feb 2023 19:27:05 +0400 Subject: [PATCH 1271/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 9fa7345dd..60a33ae8f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -184,7 +184,7 @@ async def download_file(token, file_path): session = await session_manager.get_session() async with session.get(url, proxy=proxy) as response: if response.status != 200: - raise ApiHTTPException('Download file', result) + raise ApiHTTPException('Download file', response) result = await response.read() return result @@ -247,7 +247,7 @@ async def get_updates(token, offset=None, limit=None, params['allowed_updates'] = json.dumps(allowed_updates) return await _process_request(token, method_name, params=params, request_timeout=request_timeout) -async def _check_result(method_name, result): +async def _check_result(method_name, result: aiohttp.ClientResponse): """ Checks whether `result` is a valid API response. A result is considered invalid if: @@ -263,7 +263,7 @@ async def _check_result(method_name, result): try: result_json = await result.json(encoding="utf-8") except: - if result.status_code != 200: + if result.status != 200: raise ApiHTTPException(method_name, result) else: raise ApiInvalidJSONException(method_name, result) @@ -1898,10 +1898,10 @@ class ApiHTTPException(ApiException): This class represents an Exception thrown when a call to the Telegram API server returns HTTP code that is not 200. """ - def __init__(self, function_name, result): + def __init__(self, function_name, result: aiohttp.ClientResponse): super(ApiHTTPException, self).__init__( "The server returned HTTP {0} {1}. Response body:\n[{2}]" \ - .format(result.status_code, result.reason, result), + .format(result.status, result.reason, result.request_info), function_name, result) From 4f2c89c4a8ecc1c0eae966f98941d6ec4eadc294 Mon Sep 17 00:00:00 2001 From: orocane <53696294+S1RANN@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:12:33 +0800 Subject: [PATCH 1272/1808] Add a function to extract contents of entities from messages --- telebot/util.py | 33 ++++++++++++++++++++++++++++++ tests/test_telebot.py | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/telebot/util.py b/telebot/util.py index 4fa81a3ec..326ad7427 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -274,6 +274,39 @@ def extract_arguments(text: str) -> str or None: result = regexp.match(text) return result.group(2) if is_command(text) else None +def extract_entity(text: str, e: types.MessageEntity) -> str: + """ + Returns the content of the entity. + + :param text: The text of the message the entity belongs to + :type text: :obj:`str` + + :param e: The entity to extract + :type e: :obj:`MessageEntity` + + :return: The content of the entity + :rtype: :obj:`str` + """ + offset = 0 + start = 0 + encoded_text = text.encode() + end = len(encoded_text) + i = 0 + + for byte in encoded_text: + if (byte & 0xc0) != 0x80: + if offset == e.offset: + start = i + elif offset - e.offset == e.length: + end = i + break + if byte >= 0xf0: + offset += 2 + else: + offset += 1 + i += 1 + + return encoded_text[start:end].decode() def split_string(text: str, chars_per_string: int) -> List[str]: """ diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 06ee681e9..0caff9e86 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -470,6 +470,53 @@ def test_antiflood(self): for i in range(0,200): util.antiflood(tb.send_message, CHAT_ID, text) assert i == 199 + + def test_extract_entity(self): + entities_map = {"https://core.telegram.org/api/entities": "https://core.telegram.org/api/entities", + "https://github.com/eternnoir/pyTelegramBotAPI": "https://github.com/eternnoir/pyTelegramBotAPI", + "*粗 bold text体*": "粗 bold text体", + "_斜体 italic text_": "斜体 italic text", + "[谷歌](http://www.google.com/)": "谷歌", + '`std::cout<<"test"<() { + Ok(number @ 0..=2) => break number, + _ => { + println!("invalid input!"); + option = String::new(); + continue; + } + }; +};```''': '''let number = loop { + println!("Pick a pattern from 0-2:"); + stdin.read_line(&mut option).unwrap(); + match option.lines().next().unwrap().parse::() { + Ok(number @ 0..=2) => break number, + _ => { + println!("invalid input!"); + option = String::new(); + continue; + } + }; +};''', + "@username": "@username", + "#hashtag索引标签": "#hashtag索引标签", + "do-not-reply@telegram.org": "do-not-reply@telegram.org", + "+12125550123": "+12125550123"} + entites = list(entities_map.keys()) + contents = list(entities_map.values()) + contents.sort() + text = '\n'.join(entites) + + bot = telebot.TeleBot(TOKEN) + message = bot.send_message(CHAT_ID, text, parse_mode="Markdown") + extracted_contents = [util.extract_entity( + message.text, e) for e in message.entities] + extracted_contents.sort() + assert contents == extracted_contents @staticmethod def create_text_message(text): From d12ea91e1278b8ad08bd1844a14b256b7a2e8a6b Mon Sep 17 00:00:00 2001 From: zeldpol Date: Fri, 17 Feb 2023 19:23:11 +0200 Subject: [PATCH 1273/1808] Fix .webm upload No need to pass file content as a header, it causes the http error "431 Request Header Fields Too Large". --- telebot/apihelper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a555f35b4..17c3e4180 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1612,8 +1612,6 @@ def create_new_sticker_set( payload[stype] = sticker if mask_position: payload['mask_position'] = mask_position.to_json() - if webm_sticker: - payload['webm_sticker'] = webm_sticker if sticker_type: payload['sticker_type'] = sticker_type return _make_request(token, method_url, params=payload, files=files, method='post') From 7a67d5f9f980eeb89315b075b11cee31582b1a6a Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sat, 18 Feb 2023 22:36:29 +0200 Subject: [PATCH 1274/1808] Fix .webm async upload --- telebot/asyncio_helper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 22ca554ef..6a92b8794 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1601,8 +1601,6 @@ async def create_new_sticker_set( payload[stype] = sticker if mask_position: payload['mask_position'] = mask_position.to_json() - if webm_sticker: - payload['webm_sticker'] = webm_sticker if sticker_type: payload['sticker_type'] = sticker_type return await _process_request(token, method_url, params=payload, files=files, method='post') From c9b6d3f8681d67969737f0e4eabffbb33c29ff89 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 22 Feb 2023 09:49:29 +0300 Subject: [PATCH 1275/1808] New content types added + typo fix --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- telebot/asyncio_helper.py | 4 ++-- telebot/util.py | 9 ++++----- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 46669a695..f8dd4886f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -191,7 +191,7 @@ def __init__( coloredlogs.install(logger=logger, level=logger.level) except ImportError: raise ImportError( - 'Install colorredlogs module to use colorful_logs option.' + 'Install coloredlogs module to use colorful_logs option.' ) # threading-related diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d1fa77b9b..a75398c7e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -139,7 +139,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ coloredlogs.install(logger=logger, level=logger.level) except ImportError: raise ImportError( - 'Install colorredlogs module to use colorful_logs option.' + 'Install coloredlogs module to use colorful_logs option.' ) # properties diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 6a92b8794..04b494f3a 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -173,14 +173,14 @@ async def get_file_url(token, file_id): if FILE_URL is None: return "https://api.telegram.org/file/bot{0}/{1}".format(token, (await get_file(token, file_id))['file_path']) else: - # noinspection PyUnresolvedReferences return FILE_URL.format(token, (await get_file(token, file_id))['file_path']) async def download_file(token, file_path): if FILE_URL is None: url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path) - else: url = FILE_URL.format(token, file_path) + else: + url = FILE_URL.format(token, file_path) session = await session_manager.get_session() async with session.get(url, proxy=proxy) as response: if response.status != 200: diff --git a/telebot/util.py b/telebot/util.py index 4fa81a3ec..bcb84281a 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -35,11 +35,10 @@ #: Contains all service content types such as `User joined the group`. content_type_service = [ 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', - 'group_chat_created', - 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', - 'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended', - 'video_chat_participants_invited', 'message_auto_delete_timer_changed', 'forum_topic_created', 'forum_topic_closed', - 'forum_topic_reopened', + 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', + 'migrate_from_chat_id', 'pinned_message', 'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', + 'video_chat_ended', 'video_chat_participants_invited', 'message_auto_delete_timer_changed', 'forum_topic_created', + 'forum_topic_closed', 'forum_topic_reopened', 'user_shared', 'chat_shared', ] #: All update types, should be used for allowed_updates parameter in polling. From 2bd81a5f5c716089b3cf8fb65fd6da249cb78e74 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 10 Mar 2023 15:21:07 +0400 Subject: [PATCH 1276/1808] I'm back: Bot API Update too :) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffcc909bb..e1c556176 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.5! +##

Supported Bot API version: 6.6!

Official documentation

Official ru documentation

From c84b771e5a41f1b0e7eed82f1f38dfe8e2830e61 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 10 Mar 2023 21:36:44 +0400 Subject: [PATCH 1277/1808] Added the ability to set different bot descriptions for different user languages using the method setMyDescription. --- telebot/__init__.py | 18 ++++++++++++++++++ telebot/apihelper.py | 9 +++++++++ telebot/async_telebot.py | 18 ++++++++++++++++++ telebot/asyncio_helper.py | 8 ++++++++ 4 files changed, 53 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index f8dd4886f..0dfaf2652 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3400,6 +3400,24 @@ def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, """ result = apihelper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + + def set_my_description(self, description: Optional[str]=None, language_code: Optional[str]=None): + """ + Use this method to change the bot's description, which is shown in + the chat with the bot if the chat is empty. + Returns True on success. + + :param description: New bot description; 0-512 characters. Pass an empty string to remove the dedicated description for the given language. + :type description: :obj:`str` + + :param language_code: A two-letter ISO 639-1 language code. If empty, the description will be applied to all users for + whose language there is no dedicated description. + :type language_code: :obj:`str` + + :return: True on success. + """ + + return apihelper.set_my_description(self.token, description, language_code) def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 17c3e4180..d566966c8 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1152,6 +1152,15 @@ def set_chat_title(token, chat_id, title): return _make_request(token, method_url, params=payload, method='post') +def set_my_description(token, description=None, language_code=None): + method_url = r'setMyDescription' + payload = {} + if description: + payload['description'] = description + if language_code: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload, method='post') + def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' payload = {} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a75398c7e..da1d4fe1f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4238,6 +4238,24 @@ async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ return await asyncio_helper.delete_chat_photo(self.token, chat_id) + async def set_my_description(self, description: Optional[str]=None, language_code: Optional[str]=None): + """ + Use this method to change the bot's description, which is shown in + the chat with the bot if the chat is empty. + Returns True on success. + + :param description: New bot description; 0-512 characters. Pass an empty string to remove the dedicated description for the given language. + :type description: :obj:`str` + + :param language_code: A two-letter ISO 639-1 language code. If empty, the description will be applied to all users for + whose language there is no dedicated description. + :type language_code: :obj:`str` + + :return: True on success. + """ + + return await asyncio_helper.set_my_description(self.token, description, language_code) + async def get_my_commands(self, scope: Optional[types.BotCommandScope], language_code: Optional[str]) -> List[types.BotCommand]: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 04b494f3a..8ac5501a8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1138,6 +1138,14 @@ async def set_chat_title(token, chat_id, title): payload = {'chat_id': chat_id, 'title': title} return await _process_request(token, method_url, params=payload, method='post') +async def set_my_description(token, description=None, language_code=None): + method_url = r'setMyDescription' + payload = {} + if description: + payload['description'] = description + if language_code: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload, method='post') async def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' From 65dcd671404c0e805dc39d9c61101cebc58aae44 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 15:35:00 +0400 Subject: [PATCH 1278/1808] Added the ability to get the current bot description in the given language as the class BotDescription using the method getMyDescription. --- telebot/__init__.py | 13 +++++++++++++ telebot/apihelper.py | 7 +++++++ telebot/async_telebot.py | 13 +++++++++++++ telebot/asyncio_helper.py | 8 ++++++++ telebot/types.py | 22 ++++++++++++++++++++++ 5 files changed, 63 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0dfaf2652..0e182735b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3418,6 +3418,19 @@ def set_my_description(self, description: Optional[str]=None, language_code: Opt """ return apihelper.set_my_description(self.token, description, language_code) + + def get_my_description(self, language_code: Optional[str]=None): + """ + Use this method to get the current bot description for the given user language. + Returns BotDescription on success. + + :param language_code: A two-letter ISO 639-1 language code or an empty string + :type language_code: :obj:`str` + + :return: :class:`telebot.types.BotDescription` + """ + + return types.BotDescription.de_json(apihelper.get_my_description(self.token, language_code)) def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d566966c8..4d5000e9d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1161,6 +1161,13 @@ def set_my_description(token, description=None, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') +def get_my_description(token, language_code=None): + method_url = r'getMyDescription' + payload = {} + if language_code: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload) + def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' payload = {} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index da1d4fe1f..142867afc 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4255,6 +4255,19 @@ async def set_my_description(self, description: Optional[str]=None, language_cod """ return await asyncio_helper.set_my_description(self.token, description, language_code) + + async def get_my_description(self, language_code: Optional[str]=None): + """ + Use this method to get the current bot description for the given user language. + Returns BotDescription on success. + + :param language_code: A two-letter ISO 639-1 language code or an empty string + :type language_code: :obj:`str` + + :return: :class:`telebot.types.BotDescription` + """ + result = await asyncio_helper.get_my_description(self.token, language_code) + return types.BotDescription.de_json(result) async def get_my_commands(self, scope: Optional[types.BotCommandScope], language_code: Optional[str]) -> List[types.BotCommand]: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 8ac5501a8..0b132e9c1 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1147,6 +1147,14 @@ async def set_my_description(token, description=None, language_code=None): payload['language_code'] = language_code return await _process_request(token, method_url, params=payload, method='post') +async def get_my_description(token, language_code=None): + method_url = r'getMyDescription' + payload = {} + if language_code: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload) + + async def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' payload = {} diff --git a/telebot/types.py b/telebot/types.py index cb4facb4a..7ae6b52f3 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7296,3 +7296,25 @@ def __init__(self, request_id: int, chat_id: int) -> None: self.request_id: int = request_id self.chat_id: int = chat_id + +class BotDescription(JsonDeserializable): + """ + This object represents a bot description. + + Telegram documentation: https://core.telegram.org/bots/api#botdescription + + :param description: Bot description + :type description: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotDescription` + """ + + @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, description: str) -> None: + self.description: str = description \ No newline at end of file From 9b81a29a6ad69bbd0328c4b77bd290d92834e5ed Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 15:40:54 +0400 Subject: [PATCH 1279/1808] Added the ability to set different bot short descriptions for different user languages using the method setMyShortDescription. --- telebot/__init__.py | 18 ++++++++++++++++++ telebot/apihelper.py | 10 ++++++++++ telebot/async_telebot.py | 18 ++++++++++++++++++ telebot/asyncio_helper.py | 8 ++++++++ 4 files changed, 54 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0e182735b..e91eb4d95 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3431,6 +3431,24 @@ def get_my_description(self, language_code: Optional[str]=None): """ return types.BotDescription.de_json(apihelper.get_my_description(self.token, language_code)) + + def set_my_short_description(self, short_description:Optional[str]=None, language_code:Optional[str]=None): + """ + Use this method to change the bot's short description, which is shown on the bot's profile page and + is sent together with the link when users share the bot. + Returns True on success. + + :param short_description: New short description for the bot; 0-120 characters. Pass an empty string to remove the dedicated short description for the given language. + :type short_description: :obj:`str` + + :param language_code: A two-letter ISO 639-1 language code. + If empty, the short description will be applied to all users for whose language there is no dedicated short description. + :type language_code: :obj:`str` + + :return: True on success. + """ + + return apihelper.set_my_short_description(self.token, short_description, language_code) def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4d5000e9d..3807f2419 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1160,6 +1160,7 @@ def set_my_description(token, description=None, language_code=None): if language_code: payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') + def get_my_description(token, language_code=None): method_url = r'getMyDescription' @@ -1168,6 +1169,15 @@ def get_my_description(token, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload) +def set_my_short_description(token, short_description=None, language_code=None): + method_url = r'setMyShortDescription' + payload = {} + if short_description: + payload['short_description'] = short_description + if language_code: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload, method='post') + def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' payload = {} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 142867afc..0e1c51e0e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4269,6 +4269,24 @@ async def get_my_description(self, language_code: Optional[str]=None): result = await asyncio_helper.get_my_description(self.token, language_code) return types.BotDescription.de_json(result) + async def set_my_short_description(self, short_description:Optional[str]=None, language_code:Optional[str]=None): + """ + Use this method to change the bot's short description, which is shown on the bot's profile page and + is sent together with the link when users share the bot. + Returns True on success. + + :param short_description: New short description for the bot; 0-120 characters. Pass an empty string to remove the dedicated short description for the given language. + :type short_description: :obj:`str` + + :param language_code: A two-letter ISO 639-1 language code. + If empty, the short description will be applied to all users for whose language there is no dedicated short description. + :type language_code: :obj:`str` + + :return: True on success. + """ + + return await asyncio_helper.set_my_short_description(self.token, short_description, language_code) + async def get_my_commands(self, scope: Optional[types.BotCommandScope], language_code: Optional[str]) -> List[types.BotCommand]: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0b132e9c1..2460dd513 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1154,6 +1154,14 @@ async def get_my_description(token, language_code=None): payload['language_code'] = language_code return await _process_request(token, method_url, params=payload) +async def set_my_short_description(token, short_description=None, language_code=None): + method_url = r'setMyShortDescription' + payload = {} + if short_description: + payload['short_description'] = short_description + if language_code: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload, method='post') async def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' From 09e4a2a4377f05feb5bec283f735a13d322827ce Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 15:46:35 +0400 Subject: [PATCH 1280/1808] Added the ability to get the current bot short description in the given language as the class BotShortDescription using the method getMyShortDescription. --- telebot/__init__.py | 13 +++++++++++++ telebot/apihelper.py | 7 +++++++ telebot/async_telebot.py | 13 +++++++++++++ telebot/asyncio_helper.py | 7 +++++++ telebot/types.py | 25 ++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e91eb4d95..cf79684a8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3449,6 +3449,19 @@ def set_my_short_description(self, short_description:Optional[str]=None, languag """ return apihelper.set_my_short_description(self.token, short_description, language_code) + + def get_my_short_description(self, language_code: Optional[str]=None): + """ + Use this method to get the current bot short description for the given user language. + Returns BotShortDescription on success. + + :param language_code: A two-letter ISO 639-1 language code or an empty string + :type language_code: :obj:`str` + + :return: :class:`telebot.types.BotShortDescription` + """ + + return types.BotShortDescription.de_json(apihelper.get_my_short_description(self.token, language_code)) def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3807f2419..955b65582 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1178,6 +1178,13 @@ def set_my_short_description(token, short_description=None, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') +def get_my_short_description(token, language_code=None): + method_url = r'getMyShortDescription' + payload = {} + if language_code: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload) + def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' payload = {} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0e1c51e0e..14b1f6c09 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4287,6 +4287,19 @@ async def set_my_short_description(self, short_description:Optional[str]=None, l return await asyncio_helper.set_my_short_description(self.token, short_description, language_code) + async def get_my_short_description(self, language_code: Optional[str]=None): + """ + Use this method to get the current bot short description for the given user language. + Returns BotShortDescription on success. + + :param language_code: A two-letter ISO 639-1 language code or an empty string + :type language_code: :obj:`str` + + :return: :class:`telebot.types.BotShortDescription` + """ + result = await asyncio_helper.get_my_short_description(self.token, language_code) + return types.BotShortDescription.de_json(result) + async def get_my_commands(self, scope: Optional[types.BotCommandScope], language_code: Optional[str]) -> List[types.BotCommand]: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 2460dd513..cbf7fdce8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1163,6 +1163,13 @@ async def set_my_short_description(token, short_description=None, language_code= payload['language_code'] = language_code return await _process_request(token, method_url, params=payload, method='post') +async def get_my_short_description(token, language_code=None): + method_url = r'getMyShortDescription' + payload = {} + if language_code: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload) + async def get_my_commands(token, scope=None, language_code=None): method_url = r'getMyCommands' payload = {} diff --git a/telebot/types.py b/telebot/types.py index 7ae6b52f3..bc6dc442d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7317,4 +7317,27 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, description: str) -> None: - self.description: str = description \ No newline at end of file + self.description: str = description + + +class BotShortDescription(JsonDeserializable): + """ + This object represents a bot short description. + + Telegram documentation: https://core.telegram.org/bots/api#botshortdescription + + :param short_description: Bot short description + :type short_description: :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.BotShortDescription` + """ + + @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, short_description: str) -> None: + self.short_description: str = short_description \ No newline at end of file From 54caf30f69bb17b9858b84f015079e6845eb47b6 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 15:50:09 +0400 Subject: [PATCH 1281/1808] Added the parameter emoji to the method sendSticker to specify an emoji for just uploaded stickers. --- telebot/__init__.py | 8 ++++++-- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 8 ++++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index cf79684a8..047c0610f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2099,7 +2099,8 @@ def send_sticker( allow_sending_without_reply: Optional[bool]=None, protect_content:Optional[bool]=None, data: Union[Any, str]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + emoji: Optional[str]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2139,6 +2140,9 @@ def send_sticker( :param message_thread_id: The thread to which the message will be sent :type message_thread_id: :obj:`int` + :param emoji: Emoji associated with the sticker; only for just uploaded stickers + :type emoji: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2156,7 +2160,7 @@ def send_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, - protect_content=protect_content, message_thread_id=message_thread_id)) + protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji)) def send_video( self, chat_id: Union[int, str], video: Union[Any, str], diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 955b65582..3b4f7093f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -896,7 +896,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, - protect_content = None, message_thread_id=None): + protect_content = None, message_thread_id=None, emoji=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -937,6 +937,8 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['disable_content_type_detection'] = disable_content_type_detection if message_thread_id: payload['message_thread_id'] = message_thread_id + if emoji: + payload['emoji'] = emoji return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 14b1f6c09..10ef46100 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2958,7 +2958,8 @@ async def send_sticker( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, data: Union[Any, str]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + emoji: Optional[str]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2998,6 +2999,9 @@ async def send_sticker( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param emoji: Emoji associated with the sticker; only for just uploaded stickers + :type emoji: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3016,7 +3020,7 @@ async def send_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, protect_content=protect_content, - message_thread_id=message_thread_id)) + message_thread_id=message_thread_id, emoji=emoji)) async def send_video( self, chat_id: Union[int, str], video: Union[Any, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index cbf7fdce8..f2a9d4b10 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -888,7 +888,7 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, - message_thread_id=None): + message_thread_id=None, emoji=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -929,6 +929,8 @@ async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, r payload['disable_content_type_detection'] = disable_content_type_detection if message_thread_id: payload['message_thread_id'] = message_thread_id + if emoji: + payload['emoji'] = emoji return await _process_request(token, method_url, params=payload, files=files, method='post') From f30457bd7509139c4416e01e5cc7bf4bbecbb7f0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 16:33:02 +0400 Subject: [PATCH 1282/1808] Added support for the creation of custom emoji sticker sets in createNewStickerSet. --- telebot/__init__.py | 3 +-- telebot/async_telebot.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 047c0610f..07ab9e109 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4629,8 +4629,7 @@ def create_new_sticker_set( use sticker_type instead. :type contains_masks: :obj:`bool` - :param sticker_type: Optional, Type of stickers in the set, pass “regular” or “mask”. Custom emoji sticker sets can't be created - via the Bot API at the moment. By default, a regular sticker set is created. + :param sticker_type: Type of stickers in the set, pass “regular”, “mask”, or “custom_emoji”. By default, a regular sticker set is created. :type sticker_type: :obj:`str` :param mask_position: A JSON-serialized object for position where the mask should be placed on faces diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 10ef46100..e3b73731d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5492,8 +5492,7 @@ async def create_new_sticker_set( use sticker_type instead. :type contains_masks: :obj:`bool` - :param sticker_type: Optional, Type of stickers in the set, pass “regular” or “mask”. Custom emoji sticker sets can't be created - via the Bot API at the moment. By default, a regular sticker set is created. + :param sticker_type: Type of stickers in the set, pass “regular”, “mask”, or “custom_emoji”. By default, a regular sticker set is created. :type sticker_type: :obj:`str` :param mask_position: A JSON-serialized object for position where the mask should be placed on faces From 8a858cac4eae8ded8927dbcbea30e24f730d5707 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 16:39:04 +0400 Subject: [PATCH 1283/1808] Added the parameter needs_repainting to the method createNewStickerSet to automatically change the color of emoji based on context (e.g., use text color in messages, accent color in statuses, etc.). --- telebot/__init__.py | 10 ++++++++-- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 10 ++++++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 07ab9e109..f4df131be 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4592,7 +4592,8 @@ def create_new_sticker_set( webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, sticker_type: Optional[str]=None, - mask_position: Optional[types.MaskPosition]=None) -> bool: + mask_position: Optional[types.MaskPosition]=None, + needs_repainting: Optional[bool]=None) -> bool: """ Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. @@ -4635,6 +4636,11 @@ def create_new_sticker_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` + :param needs_repainting: Pass True if stickers in the sticker set must be repainted to the color of text when used in messages, + the accent color if used as emoji status, white on chat photos, or another appropriate color based on context; + for custom emoji sticker sets only + :type needs_repainting: :obj:`bool` + :return: On success, True is returned. :rtype: :obj:`bool` """ @@ -4645,7 +4651,7 @@ def create_new_sticker_set( return apihelper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position, webm_sticker, sticker_type) + mask_position, webm_sticker, sticker_type, needs_repainting) def add_sticker_to_set( self, user_id: int, name: str, emojis: str, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3b4f7093f..2bf5f9a76 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1630,7 +1630,7 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position=None, webm_sticker=None, sticker_type=None): + mask_position=None, webm_sticker=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} if png_sticker: @@ -1649,6 +1649,8 @@ def create_new_sticker_set( payload['mask_position'] = mask_position.to_json() if sticker_type: payload['sticker_type'] = sticker_type + if needs_repainting is not None: + payload['needs_repainting'] = needs_repainting return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e3b73731d..f4669e8c5 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5455,7 +5455,8 @@ async def create_new_sticker_set( webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, sticker_type: Optional[str]=None, - mask_position: Optional[types.MaskPosition]=None) -> bool: + mask_position: Optional[types.MaskPosition]=None, + needs_repainting: Optional[bool]=None) -> bool: """ Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. @@ -5498,6 +5499,11 @@ async def create_new_sticker_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` + :param needs_repainting: Pass True if stickers in the sticker set must be repainted to the color of text when used in messages, + the accent color if used as emoji status, white on chat photos, or another appropriate color based on context; + for custom emoji sticker sets only + :type needs_repainting: :obj:`bool` + :return: On success, True is returned. :rtype: :obj:`bool` """ @@ -5508,7 +5514,7 @@ async def create_new_sticker_set( return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position, webm_sticker, sticker_type) + mask_position, webm_sticker, sticker_type, needs_repainting) async def add_sticker_to_set( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index f2a9d4b10..0f9919c7b 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1617,7 +1617,7 @@ async def upload_sticker_file(token, user_id, png_sticker): async def create_new_sticker_set( token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position=None, webm_sticker=None, sticker_type=None): + mask_position=None, webm_sticker=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} if png_sticker: @@ -1636,6 +1636,8 @@ async def create_new_sticker_set( payload['mask_position'] = mask_position.to_json() if sticker_type: payload['sticker_type'] = sticker_type + if needs_repainting is not None: + payload['needs_repainting'] = needs_repainting return await _process_request(token, method_url, params=payload, files=files, method='post') From c0185dad447890adf315029d6c00eccad1c61514 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 16:41:19 +0400 Subject: [PATCH 1284/1808] Added the field needs_repainting to the class Sticker. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index bc6dc442d..b7fca7092 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5904,6 +5904,11 @@ class Sticker(JsonDeserializable): :param custom_emoji_id: Optional. For custom emoji stickers, unique identifier of the custom emoji :type custom_emoji_id: :obj:`str` + :param needs_repainting: Optional. True, if the sticker must be repainted to a text color in messages, + the color of the Telegram Premium badge in emoji status, white color on chat photos, or another + appropriate color in other places + :type needs_repainting: :obj:`bool` + :param file_size: Optional. File size in bytes :type file_size: :obj:`int` @@ -5927,7 +5932,7 @@ def de_json(cls, json_string): def __init__(self, file_id, file_unique_id, type, width, height, is_animated, is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, - premium_animation=None, custom_emoji_id=None, **kwargs): + premium_animation=None, custom_emoji_id=None, needs_repainting=None ,**kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.type: str = type @@ -5942,6 +5947,7 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, self.file_size: int = file_size self.premium_animation: File = premium_animation self.custom_emoji_id: int = custom_emoji_id + self.needs_repainting: bool = needs_repainting From f527fc91f6b5e052b0ddf1a5ea6b48dfbb8542b3 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 18:18:07 +0400 Subject: [PATCH 1285/1808] Replaced the parameters png_sticker, tgs_sticker, webm_sticker, emojis and mask_position in the method addStickerToSet with the parameter sticker of the type InputSticker. --- telebot/__init__.py | 19 ++++++++++-- telebot/apihelper.py | 20 +++--------- telebot/async_telebot.py | 15 +++++++-- telebot/asyncio_helper.py | 23 +++----------- telebot/types.py | 64 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 42 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f4df131be..1fbc8328a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4653,12 +4653,14 @@ def create_new_sticker_set( self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker, sticker_type, needs_repainting) + def add_sticker_to_set( - self, user_id: int, name: str, emojis: str, + self, user_id: int, name: str, emojis: List[str]=None, png_sticker: Optional[Union[Any, str]]=None, tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, - mask_position: Optional[types.MaskPosition]=None) -> bool: + mask_position: Optional[types.MaskPosition]=None, + sticker: Optional[List[types.InputSticker]]=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. It's required to pass `png_sticker` or `tgs_sticker`. @@ -4666,6 +4668,9 @@ def add_sticker_to_set( Telegram documentation: https://core.telegram.org/bots/api#addstickertoset + .. note:: + **_sticker parameters are deprecated, use stickers instead + :param user_id: User identifier of created sticker set owner :type user_id: :obj:`int` @@ -4689,11 +4694,19 @@ def add_sticker_to_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` + :param sticker: A JSON-serialized list of 1-50 initial stickers to be added to the sticker set + :type sticker: :class:`telebot.types.InputSticker` + :return: On success, True is returned. :rtype: :obj:`bool` """ + # Replaced the parameters png_sticker, tgs_sticker, webm_sticker, emojis and mask_position + if sticker is None: + sticker = png_sticker or tgs_sticker or webm_sticker + sticker = types.InputSticker(sticker, emojis, mask_position) + return apihelper.add_sticker_to_set( - self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker) + self.token, user_id, name, sticker) def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 2bf5f9a76..82d45aaa6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1654,23 +1654,11 @@ def create_new_sticker_set( return _make_request(token, method_url, params=payload, files=files, method='post') -def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): +def add_sticker_to_set(token, user_id, name, sticker): method_url = 'addStickerToSet' - payload = {'user_id': user_id, 'name': name, 'emojis': emojis} - if png_sticker: - stype = 'png_sticker' - elif webm_sticker: - stype = 'webm_sticker' - else: - stype = 'tgs_sticker' - sticker = png_sticker or tgs_sticker or webm_sticker - files = None - if not util.is_string(sticker): - files = {stype: sticker} - else: - payload[stype] = sticker - if mask_position: - payload['mask_position'] = mask_position.to_json() + json_dict, files = sticker.convert_input_sticker() + payload = {'user_id': user_id, 'name': name, 'sticker': json_dict} + return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f4669e8c5..6bb8b9ac8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5518,11 +5518,12 @@ async def create_new_sticker_set( async def add_sticker_to_set( - self, user_id: int, name: str, emojis: str, + self, user_id: int, name: str, emojis: List[str]=None, png_sticker: Optional[Union[Any, str]]=None, tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, - mask_position: Optional[types.MaskPosition]=None) -> bool: + mask_position: Optional[types.MaskPosition]=None, + sticker: Optional[List[types.InputSticker]]=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. It's required to pass `png_sticker` or `tgs_sticker`. @@ -5553,11 +5554,19 @@ async def add_sticker_to_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` + :param sticker: A JSON-serialized list of 1-50 initial stickers to be added to the sticker set + :type sticker: :class:`telebot.types.InputSticker` + :return: On success, True is returned. :rtype: :obj:`bool` """ + # Replaced the parameters png_sticker, tgs_sticker, webm_sticker, emojis and mask_position + if sticker is None: + sticker = png_sticker or tgs_sticker or webm_sticker + sticker = types.InputSticker(sticker, emojis, mask_position) + return await asyncio_helper.add_sticker_to_set( - self.token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker) + self.token, user_id, name, sticker) async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0f9919c7b..850ab5ae0 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1641,27 +1641,12 @@ async def create_new_sticker_set( return await _process_request(token, method_url, params=payload, files=files, method='post') -async def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position, webm_sticker): +async def add_sticker_to_set(token, user_id, name, sticker): method_url = 'addStickerToSet' - payload = {'user_id': user_id, 'name': name, 'emojis': emojis} - if png_sticker: - stype = 'png_sticker' - elif webm_sticker: - stype = 'webm_sticker' - else: - stype = 'tgs_sticker' - files = None - sticker = png_sticker or tgs_sticker or webm_sticker - - if not util.is_string(sticker): - files = {stype: sticker} - else: - payload[stype] = sticker - if mask_position: - payload['mask_position'] = mask_position.to_json() + json_dict, files = sticker.convert_input_sticker() + payload = {'user_id': user_id, 'name': name, 'sticker': json_dict} + - if webm_sticker: - payload['webm_sticker'] = webm_sticker return await _process_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/types.py b/telebot/types.py index b7fca7092..d789da123 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7346,4 +7346,66 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, short_description: str) -> None: - self.short_description: str = short_description \ No newline at end of file + self.short_description: str = short_description + + +class InputSticker(Dictionaryable, JsonSerializable): + """ + This object describes a sticker to be added to a sticker set. + + :param sticker: The added sticker. Pass a file_id as a String to send a file that already exists on the Telegram servers, + 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. + Animated and video stickers can't be uploaded via HTTP URL. + :type sticker: :obj:`str` or :obj:`telebot.types.InputFile` + + :param emoji_list: One or more(up to 20) emoji(s) corresponding to the sticker + :type emoji_list: :obj:`list` of :obj:`str` + + :param mask_position: Optional. Position where the mask should be placed on faces. For “mask” stickers only. + :type mask_position: :class:`telebot.types.MaskPosition` + + :param keywords: Optional. List of 0-20 search keywords for the sticker with total length of up to 64 characters. + For “regular” and “custom_emoji” stickers only. + :type keywords: :obj:`list` of :obj:`str` + + :return: Instance of the class + :rtype: :class:`telebot.types.InputSticker` + """ + + def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], mask_position: Optional[MaskPosition]=None, keywords: Optional[List[str]]=None) -> None: + self.sticker: Union[str, InputFile] = sticker + self.emoji_list: List[str] = emoji_list + self.mask_position: Optional[MaskPosition] = mask_position + self.keywords: Optional[List[str]] = keywords + + if service_utils.is_string(self.sticker): + self._sticker_name = '' + self._sticker_dic = self.sticker + else: + self._sticker_name = service_utils.generate_random_token() + self._sticker_dic = 'attach://{0}'.format(self._sticker_name) + + def to_dict(self) -> dict: + json_dict = { + 'sticker': self._sticker_dic, + 'emojis': self.emoji_list + } + + if self.mask_position is not None: + json_dict['mask_position'] = self.mask_position.to_dict() + if self.keywords is not None: + json_dict['keywords'] = self.keywords + + return json_dict + + def to_json(self) -> str: + return json.dumps(self.to_dict()) + + def convert_input_sticker(self): + if service_utils.is_string(self.sticker): + return self.to_json(), None + + return self.to_json(), {self._sticker_name: self.sticker} + + + \ No newline at end of file From 19dcce0d5bb45d46365542658380bdf86793c4f9 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 21:59:22 +0400 Subject: [PATCH 1286/1808] Added support for the creation of sticker sets with multiple initial stickers in createNewStickerSet by replacing the parameters png_sticker, tgs_sticker, webm_sticker, emojis and mask_position with the parameters stickers and sticker_format. --- telebot/__init__.py | 33 ++++++++++++++++++++++++++++---- telebot/apihelper.py | 40 +++++++++++++++++++++------------------ telebot/async_telebot.py | 35 ++++++++++++++++++++++++++++++---- telebot/asyncio_helper.py | 39 ++++++++++++++++++++------------------ telebot/types.py | 2 +- 5 files changed, 104 insertions(+), 45 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1fbc8328a..ac923789b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4586,14 +4586,16 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> typ def create_new_sticker_set( self, user_id: int, name: str, title: str, - emojis: str, + emojis: Optional[List[str]]=None, png_sticker: Union[Any, str]=None, tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, sticker_type: Optional[str]=None, mask_position: Optional[types.MaskPosition]=None, - needs_repainting: Optional[bool]=None) -> bool: + needs_repainting: Optional[bool]=None, + stickers: List[types.InputSticker]=None, + sticker_format: Optional[str]=None) -> bool: """ Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. @@ -4601,6 +4603,9 @@ def create_new_sticker_set( Telegram documentation: https://core.telegram.org/bots/api#createnewstickerset + .. note:: + Fields *_sticker are deprecated, pass a list of stickers to stickers parameter instead. + :param user_id: User identifier of created sticker set owner :type user_id: :obj:`int` @@ -4641,17 +4646,37 @@ def create_new_sticker_set( for custom emoji sticker sets only :type needs_repainting: :obj:`bool` + :param stickers: List of stickers to be added to the set + :type stickers: :obj:`list` of :class:`telebot.types.InputSticker` + + :param sticker_format: Format of stickers in the set, must be one of “static”, “animated”, “video” + :type sticker_format: :obj:`str` + :return: On success, True is returned. :rtype: :obj:`bool` """ + if tgs_sticker: + sticker_format = 'animated' + elif webm_sticker: + sticker_format = 'video' + elif png_sticker: + sticker_format = 'static' + if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') if sticker_type is None: sticker_type = 'mask' if contains_masks else 'regular' + if stickers is None: + stickers = png_sticker or tgs_sticker or webm_sticker + if stickers is None: + raise ValueError('You must pass at least one sticker') + stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] + + + return apihelper.create_new_sticker_set( - self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position, webm_sticker, sticker_type, needs_repainting) + self.token, user_id, name, title, stickers, sticker_format, sticker_type, needs_repainting) def add_sticker_to_set( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 82d45aaa6..0ee636e57 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1629,28 +1629,32 @@ def upload_sticker_file(token, user_id, png_sticker): def create_new_sticker_set( - token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position=None, webm_sticker=None, sticker_type=None, needs_repainting=None): + token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' - payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} - if png_sticker: - stype = 'png_sticker' - elif webm_sticker: - stype = 'webm_sticker' - else: - stype = 'tgs_sticker' - sticker = png_sticker or tgs_sticker or webm_sticker - files = None - if not util.is_string(sticker): - files = {stype: sticker} - else: - payload[stype] = sticker - if mask_position: - payload['mask_position'] = mask_position.to_json() + payload = {'user_id': user_id, 'name': name, 'title': title} if sticker_type: payload['sticker_type'] = sticker_type - if needs_repainting is not None: + if needs_repainting: payload['needs_repainting'] = needs_repainting + if sticker_format: + payload['sticker_format'] = sticker_format + + files = {} + lst = [] + + for sticker in stickers: + json_dict, file = sticker.convert_input_sticker() + json_dict = sticker.to_dict() + + if file: + list_keys = list(file.keys()) + files[list_keys[0]] = file[list_keys[0]] + lst.append(json_dict) + + payload['stickers'] = json.dumps(lst) + + + return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6bb8b9ac8..eba6c4764 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5449,14 +5449,16 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) async def create_new_sticker_set( self, user_id: int, name: str, title: str, - emojis: str, + emojis: Optional[str]=None, png_sticker: Union[Any, str]=None, tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, sticker_type: Optional[str]=None, mask_position: Optional[types.MaskPosition]=None, - needs_repainting: Optional[bool]=None) -> bool: + needs_repainting: Optional[bool]=None, + stickers: List[types.InputSticker]=None, + sticker_format: Optional[str]=None) -> bool: """ Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. @@ -5464,6 +5466,9 @@ async def create_new_sticker_set( Telegram documentation: https://core.telegram.org/bots/api#createnewstickerset + .. note:: + Fields *_sticker are deprecated, pass a list of stickers to stickers parameter instead. + :param user_id: User identifier of created sticker set owner :type user_id: :obj:`int` @@ -5504,17 +5509,37 @@ async def create_new_sticker_set( for custom emoji sticker sets only :type needs_repainting: :obj:`bool` + :param stickers: List of stickers to be added to the set + :type stickers: :obj:`list` of :class:`telebot.types.InputSticker` + + :param sticker_format: Format of stickers in the set, must be one of “static”, “animated”, “video” + :type sticker_format: :obj:`str` + :return: On success, True is returned. :rtype: :obj:`bool` """ + if tgs_sticker: + sticker_format = 'animated' + elif webm_sticker: + sticker_format = 'video' + elif png_sticker: + sticker_format = 'static' + if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') if sticker_type is None: sticker_type = 'mask' if contains_masks else 'regular' + + if stickers is None: + stickers = png_sticker or tgs_sticker or webm_sticker + if stickers is None: + raise ValueError('You must pass at least one sticker') + stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] + + return await asyncio_helper.create_new_sticker_set( - self.token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position, webm_sticker, sticker_type, needs_repainting) + self.token, user_id, name, title, stickers, sticker_format, sticker_type, needs_repainting) async def add_sticker_to_set( @@ -5563,6 +5588,8 @@ async def add_sticker_to_set( # Replaced the parameters png_sticker, tgs_sticker, webm_sticker, emojis and mask_position if sticker is None: sticker = png_sticker or tgs_sticker or webm_sticker + if sticker is None: + raise ValueError('You must pass at least one sticker') sticker = types.InputSticker(sticker, emojis, mask_position) return await asyncio_helper.add_sticker_to_set( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 850ab5ae0..7c0a80e54 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1616,28 +1616,31 @@ async def upload_sticker_file(token, user_id, png_sticker): async def create_new_sticker_set( - token, user_id, name, title, emojis, png_sticker, tgs_sticker, - mask_position=None, webm_sticker=None, sticker_type=None, needs_repainting=None): + token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' - payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} - if png_sticker: - stype = 'png_sticker' - elif webm_sticker: - stype = 'webm_sticker' - else: - stype = 'tgs_sticker' - sticker = png_sticker or tgs_sticker or webm_sticker - files = None - if not util.is_string(sticker): - files = {stype: sticker} - else: - payload[stype] = sticker - if mask_position: - payload['mask_position'] = mask_position.to_json() + payload = {'user_id': user_id, 'name': name, 'title': title} if sticker_type: payload['sticker_type'] = sticker_type - if needs_repainting is not None: + if needs_repainting: payload['needs_repainting'] = needs_repainting + if sticker_format: + payload['sticker_format'] = sticker_format + + files = {} + lst = [] + + for sticker in stickers: + json_dict, file = sticker.convert_input_sticker() + json_dict = sticker.to_dict() + + if file: + list_keys = list(file.keys()) + files[list_keys[0]] = file[list_keys[0]] + lst.append(json_dict) + + payload['stickers'] = json.dumps(lst) + + return await _process_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/types.py b/telebot/types.py index d789da123..a5e056d10 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7388,7 +7388,7 @@ def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], mask_p def to_dict(self) -> dict: json_dict = { 'sticker': self._sticker_dic, - 'emojis': self.emoji_list + 'emoji_list': self.emoji_list } if self.mask_position is not None: From 73135d6012aa7f483e0e04a1fa8ed1160897d374 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 22:03:37 +0400 Subject: [PATCH 1287/1808] Added support for .WEBP files in createNewStickerSet and addStickerToSet. --- telebot/__init__.py | 6 ++++-- telebot/async_telebot.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ac923789b..90a2fcfda 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4687,8 +4687,10 @@ def add_sticker_to_set( mask_position: Optional[types.MaskPosition]=None, sticker: Optional[List[types.InputSticker]]=None) -> bool: """ - Use this method to add a new sticker to a set created by the bot. - It's required to pass `png_sticker` or `tgs_sticker`. + Use this method to add a new sticker to a set created by the bot. + The format of the added sticker must match the format of the other stickers in the set. + Emoji sticker sets can have up to 200 stickers. Animated and video sticker sets can have up to 50 stickers. + Static sticker sets can have up to 120 stickers. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#addstickertoset diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index eba6c4764..079a9a6aa 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5550,8 +5550,10 @@ async def add_sticker_to_set( mask_position: Optional[types.MaskPosition]=None, sticker: Optional[List[types.InputSticker]]=None) -> bool: """ - Use this method to add a new sticker to a set created by the bot. - It's required to pass `png_sticker` or `tgs_sticker`. + Use this method to add a new sticker to a set created by the bot. + The format of the added sticker must match the format of the other stickers in the set. + Emoji sticker sets can have up to 200 stickers. Animated and video sticker sets can have up to 50 stickers. + Static sticker sets can have up to 120 stickers. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#addstickertoset From ae44b0022ddb099b77197580e332c42e03dc4c47 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 22:15:31 +0400 Subject: [PATCH 1288/1808] Added support for .WEBP, .TGS, and .WEBM files in uploadStickerFile by replacing the parameter png_sticker in the method uploadStickerFile with the parameters sticker and sticker_format. --- telebot/__init__.py | 18 +++++++++++++++--- telebot/apihelper.py | 6 +++--- telebot/async_telebot.py | 18 +++++++++++++++--- telebot/asyncio_helper.py | 6 +++--- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 90a2fcfda..e3ea18fc7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4564,7 +4564,7 @@ def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.S result = apihelper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] - def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: + def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, sticker: Optional[types.InputFile]=None, sticker_format: Optional[str]=None) -> types.File: """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. @@ -4574,14 +4574,26 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> typ :param user_id: User identifier of sticker set owner :type user_id: :obj:`int` - :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, + :param png_sticker: DEPRECATED: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. :type png_sticker: :obj:`filelike object` + :param sticker: A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. + See https://core.telegram.org/stickers for technical requirements. More information on Sending Files » + :type sticker: :class:`telebot.types.InputFile` + + :param sticker_format: One of "static", "animated", "video". + :type sticker_format: :obj:`str` + :return: On success, the sent file is returned. :rtype: :class:`telebot.types.File` """ - result = apihelper.upload_sticker_file(self.token, user_id, png_sticker) + if png_sticker: + logger.warning("png_sticker is deprecated, use sticker instead", DeprecationWarning) + sticker = png_sticker + sticker_format = "static" + + result = apihelper.upload_sticker_file(self.token, user_id, sticker, sticker_format) return types.File.de_json(result) def create_new_sticker_set( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0ee636e57..9a24a0c20 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1621,10 +1621,10 @@ def get_custom_emoji_stickers(token, custom_emoji_ids): method_url = r'getCustomEmojiStickers' return _make_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) -def upload_sticker_file(token, user_id, png_sticker): +def upload_sticker_file(token, user_id, sticker, sticker_format): method_url = 'uploadStickerFile' - payload = {'user_id': user_id} - files = {'png_sticker': png_sticker} + payload = {'user_id': user_id, 'sticker_format': sticker_format} + files = {'sticker': sticker} return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 079a9a6aa..f7766d41e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5427,7 +5427,7 @@ async def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[t result = await asyncio_helper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] - async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) -> types.File: + async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, sticker: Optional[types.InputFile]=None, sticker_format: Optional[str]=None) -> types.File: """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. @@ -5437,14 +5437,26 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]) :param user_id: User identifier of sticker set owner :type user_id: :obj:`int` - :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, + :param png_sticker: DEPRECATED: PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. :type png_sticker: :obj:`filelike object` + :param sticker: A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. + See https://core.telegram.org/stickers for technical requirements. More information on Sending Files » + :type sticker: :class:`telebot.types.InputFile` + + :param sticker_format: One of "static", "animated", "video". + :type sticker_format: :obj:`str` + :return: On success, the sent file is returned. :rtype: :class:`telebot.types.File` """ - result = await asyncio_helper.upload_sticker_file(self.token, user_id, png_sticker) + if png_sticker: + logger.warning("png_sticker is deprecated, use sticker instead", DeprecationWarning) + sticker = png_sticker + sticker_format = "static" + + result = await asyncio_helper.upload_sticker_file(self.token, user_id, sticker, sticker_format) return types.File.de_json(result) async def create_new_sticker_set( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 7c0a80e54..d571c9394 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1608,10 +1608,10 @@ async def get_custom_emoji_stickers(token, custom_emoji_ids): method_url = r'getCustomEmojiStickers' return await _process_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) -async def upload_sticker_file(token, user_id, png_sticker): +async def upload_sticker_file(token, user_id, sticker, sticker_format): method_url = 'uploadStickerFile' - payload = {'user_id': user_id} - files = {'png_sticker': png_sticker} + payload = {'user_id': user_id, 'sticker_format': sticker_format} + files = {'sticker': sticker} return await _process_request(token, method_url, params=payload, files=files, method='post') From ac0b386625def078bb363bec1d1ab0225d105561 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 22:19:49 +0400 Subject: [PATCH 1289/1808] Added the method setCustomEmojiStickerSetThumbnail for editing the thumbnail of custom emoji sticker sets created by the bot. --- telebot/__init__.py | 17 +++++++++++++++++ telebot/apihelper.py | 7 +++++++ telebot/async_telebot.py | 16 ++++++++++++++++ telebot/asyncio_helper.py | 7 +++++++ 4 files changed, 47 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index e3ea18fc7..fd70d3ae5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4563,6 +4563,23 @@ def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.S """ result = apihelper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] + + + def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None): + """ + Use this method to set the thumbnail of a custom emoji sticker set. + Returns True on success. + + :param name: Sticker set name + :type name: :obj:`str` + + :param custom_emoji_id: Custom emoji identifier of a sticker from the sticker set; pass an empty string to drop the thumbnail and use the first sticker as the thumbnail. + :type custom_emoji_id: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, sticker: Optional[types.InputFile]=None, sticker_format: Optional[str]=None) -> types.File: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9a24a0c20..3f65fa6cc 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1628,6 +1628,13 @@ def upload_sticker_file(token, user_id, sticker, sticker_format): return _make_request(token, method_url, params=payload, files=files, method='post') +def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): + method_url = 'setCustomEmojiStickerSetThumbnail' + payload = {'name': name} + if custom_emoji_id: + payload['custom_emoji_id'] = custom_emoji_id + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f7766d41e..a98f64d85 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5458,6 +5458,22 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=N result = await asyncio_helper.upload_sticker_file(self.token, user_id, sticker, sticker_format) return types.File.de_json(result) + + async def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None): + """ + Use this method to set the thumbnail of a custom emoji sticker set. + Returns True on success. + + :param name: Sticker set name + :type name: :obj:`str` + + :param custom_emoji_id: Custom emoji identifier of a sticker from the sticker set; pass an empty string to drop the thumbnail and use the first sticker as the thumbnail. + :type custom_emoji_id: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) async def create_new_sticker_set( self, user_id: int, name: str, title: str, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index d571c9394..a5d08cf1a 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1615,6 +1615,13 @@ async def upload_sticker_file(token, user_id, sticker, sticker_format): return await _process_request(token, method_url, params=payload, files=files, method='post') +async def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): + method_url = 'setCustomEmojiStickerSetThumbnail' + payload = {'name': name} + if custom_emoji_id: + payload['custom_emoji_id'] = custom_emoji_id + return await _process_request(token, method_url, params=payload, method='post') + async def create_new_sticker_set( token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' From 385fc6a6daecba730773bf7419810a97b32e36b0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 22:24:34 +0400 Subject: [PATCH 1290/1808] Added the method setStickerSetTitle for editing the title of sticker sets created by the bot. --- telebot/__init__.py | 17 +++++++++++++++++ telebot/apihelper.py | 6 ++++++ telebot/async_telebot.py | 18 ++++++++++++++++++ telebot/asyncio_helper.py | 6 ++++++ 4 files changed, 47 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index fd70d3ae5..a2cfb317c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4580,6 +4580,23 @@ def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Opt :rtype: :obj:`bool` """ return apihelper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) + + def set_sticker_set_title(self, name: str, title: str): + """ + Use this method to set the title of a created sticker set. + Returns True on success. + + :param name: Sticker set name + :type name: :obj:`str` + + :param title: New sticker set title + :type title: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return apihelper.set_sticker_set_title(self.token, name, title) def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, sticker: Optional[types.InputFile]=None, sticker_format: Optional[str]=None) -> types.File: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3f65fa6cc..9a264b937 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1635,6 +1635,12 @@ def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): payload['custom_emoji_id'] = custom_emoji_id return _make_request(token, method_url, params=payload, method='post') + +def set_sticker_set_title(token, name, title): + method_url = 'setStickerSetTitle' + payload = {'name': name, 'title': title} + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a98f64d85..608d880db 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5474,6 +5474,24 @@ async def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_i :rtype: :obj:`bool` """ return await asyncio_helper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) + + async def set_sticker_set_title(self, name: str, title: str): + """ + Use this method to set the title of a created sticker set. + Returns True on success. + + :param name: Sticker set name + :type name: :obj:`str` + + :param title: New sticker set title + :type title: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return await asyncio_helper.set_sticker_set_title(self.token, name, title) + async def create_new_sticker_set( self, user_id: int, name: str, title: str, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index a5d08cf1a..2018757d8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1622,6 +1622,12 @@ async def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=No payload['custom_emoji_id'] = custom_emoji_id return await _process_request(token, method_url, params=payload, method='post') + +async def set_sticker_set_title(token, name, title): + method_url = 'setStickerSetTitle' + payload = {'name': name, 'title': title} + return await _process_request(token, method_url, params=payload, method='post') + async def create_new_sticker_set( token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' From db087427fc27d4f97c76f35c3cbd9eff5157ccd4 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 22:27:37 +0400 Subject: [PATCH 1291/1808] Added the method deleteStickerSet for complete deletion of a given sticker set that was created by the bot. --- telebot/__init__.py | 13 +++++++++++++ telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 13 +++++++++++++ telebot/asyncio_helper.py | 5 +++++ 4 files changed, 36 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index a2cfb317c..1f5288e9a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4597,6 +4597,19 @@ def set_sticker_set_title(self, name: str, title: str): """ return apihelper.set_sticker_set_title(self.token, name, title) + + def delete_sticker_set(self, name:str): + """ + Use this method to delete a sticker set. Returns True on success. + + :param name: Sticker set name + :type name: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return apihelper.delete_sticker_set(self.token, name) def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, sticker: Optional[types.InputFile]=None, sticker_format: Optional[str]=None) -> types.File: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9a264b937..987be4bda 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1641,6 +1641,11 @@ def set_sticker_set_title(token, name, title): payload = {'name': name, 'title': title} return _make_request(token, method_url, params=payload, method='post') +def delete_sticker_set(token, name): + method_url = 'deleteStickerSet' + payload = {'name': name} + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 608d880db..1576b7f83 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5492,6 +5492,19 @@ async def set_sticker_set_title(self, name: str, title: str): return await asyncio_helper.set_sticker_set_title(self.token, name, title) + async def delete_sticker_set(self, name:str): + """ + Use this method to delete a sticker set. Returns True on success. + + :param name: Sticker set name + :type name: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return await asyncio_helper.delete_sticker_set(self.token, name) + async def create_new_sticker_set( self, user_id: int, name: str, title: str, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 2018757d8..cc0cf5bd1 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1615,6 +1615,11 @@ async def upload_sticker_file(token, user_id, sticker, sticker_format): return await _process_request(token, method_url, params=payload, files=files, method='post') +async def delete_sticker_set(token, name): + method_url = 'deleteStickerSet' + payload = {'name': name} + return await _process_request(token, method_url, params=payload, method='post') + async def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): method_url = 'setCustomEmojiStickerSetThumbnail' payload = {'name': name} From de5a32e45ce9d3cc3aea485b4f04ed0c8fdd589f Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 22:53:33 +0400 Subject: [PATCH 1292/1808] Fixed custom_emoji_ids and added set_sticker_emoji_list, and and fixed some typehints --- telebot/__init__.py | 24 +++++++++++++++++++++--- telebot/apihelper.py | 7 ++++++- telebot/async_telebot.py | 24 +++++++++++++++++++++--- telebot/asyncio_helper.py | 6 +++++- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1f5288e9a..a1029644f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4565,7 +4565,7 @@ def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.S return [types.Sticker.de_json(sticker) for sticker in result] - def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None): + def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: """ Use this method to set the thumbnail of a custom emoji sticker set. Returns True on success. @@ -4581,7 +4581,7 @@ def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Opt """ return apihelper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) - def set_sticker_set_title(self, name: str, title: str): + def set_sticker_set_title(self, name: str, title: str) -> bool: """ Use this method to set the title of a created sticker set. Returns True on success. @@ -4598,7 +4598,7 @@ def set_sticker_set_title(self, name: str, title: str): return apihelper.set_sticker_set_title(self.token, name, title) - def delete_sticker_set(self, name:str): + def delete_sticker_set(self, name:str) -> bool: """ Use this method to delete a sticker set. Returns True on success. @@ -4610,6 +4610,24 @@ def delete_sticker_set(self, name:str): """ return apihelper.delete_sticker_set(self.token, name) + + def set_sticker_emoji_list(self, sticker: str, emoji_list: List[str]) -> bool: + """ + Use this method to set the emoji list of a custom emoji sticker set. + Returns True on success. + + :param sticker: Sticker identifier + :type sticker: :obj:`str` + + :param emoji_list: List of emoji + :type emoji_list: :obj:`list` of :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return apihelper.set_sticker_emoji_list(self.token, sticker, emoji_list) + def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, sticker: Optional[types.InputFile]=None, sticker_format: Optional[str]=None) -> types.File: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 987be4bda..86125327b 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1619,7 +1619,7 @@ def get_sticker_set(token, name): def get_custom_emoji_stickers(token, custom_emoji_ids): method_url = r'getCustomEmojiStickers' - return _make_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) + return _make_request(token, method_url, params={'custom_emoji_ids': json.dumps(custom_emoji_ids)}) def upload_sticker_file(token, user_id, sticker, sticker_format): method_url = 'uploadStickerFile' @@ -1646,6 +1646,11 @@ def delete_sticker_set(token, name): payload = {'name': name} return _make_request(token, method_url, params=payload, method='post') +def set_sticker_emoji_list(token, sticker, emoji_list): + method_url = 'setStickerEmojiList' + payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1576b7f83..b50e9875e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5459,7 +5459,7 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=N result = await asyncio_helper.upload_sticker_file(self.token, user_id, sticker, sticker_format) return types.File.de_json(result) - async def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None): + async def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: """ Use this method to set the thumbnail of a custom emoji sticker set. Returns True on success. @@ -5475,7 +5475,7 @@ async def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_i """ return await asyncio_helper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) - async def set_sticker_set_title(self, name: str, title: str): + async def set_sticker_set_title(self, name: str, title: str) -> bool: """ Use this method to set the title of a created sticker set. Returns True on success. @@ -5492,7 +5492,7 @@ async def set_sticker_set_title(self, name: str, title: str): return await asyncio_helper.set_sticker_set_title(self.token, name, title) - async def delete_sticker_set(self, name:str): + async def delete_sticker_set(self, name:str) -> bool: """ Use this method to delete a sticker set. Returns True on success. @@ -5504,6 +5504,24 @@ async def delete_sticker_set(self, name:str): """ return await asyncio_helper.delete_sticker_set(self.token, name) + + + async def set_sticker_emoji_list(self, name: str, emoji_list: List[str]) -> bool: + """ + Use this method to set the emoji list of a sticker set. + Returns True on success. + + :param name: Sticker set name + :type name: :obj:`str` + + :param emoji_list: List of emojis + :type emoji_list: :obj:`list` of :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return await asyncio_helper.set_sticker_emoji_list(self.token, name, emoji_list) async def create_new_sticker_set( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index cc0cf5bd1..6268fff0d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1606,7 +1606,7 @@ async def get_sticker_set(token, name): async def get_custom_emoji_stickers(token, custom_emoji_ids): method_url = r'getCustomEmojiStickers' - return await _process_request(token, method_url, params={'custom_emoji_ids': custom_emoji_ids}) + return await _process_request(token, method_url, params={'custom_emoji_ids': json.dumps(custom_emoji_ids)}) async def upload_sticker_file(token, user_id, sticker, sticker_format): method_url = 'uploadStickerFile' @@ -1614,6 +1614,10 @@ async def upload_sticker_file(token, user_id, sticker, sticker_format): files = {'sticker': sticker} return await _process_request(token, method_url, params=payload, files=files, method='post') +async def set_sticker_emoji_list(token, sticker, emoji_list): + method_url = 'setStickerEmojiList' + payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} + return await _process_request(token, method_url, params=payload, method='post') async def delete_sticker_set(token, name): method_url = 'deleteStickerSet' From 9fa5b91e587cf8a5722d7b2c660db0c9230d2918 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 22:58:41 +0400 Subject: [PATCH 1293/1808] Added the method setStickerKeywords for changing the search keywords assigned to a sticker. --- telebot/__init__.py | 17 +++++++++++++++++ telebot/apihelper.py | 8 ++++++++ telebot/async_telebot.py | 17 +++++++++++++++++ telebot/asyncio_helper.py | 8 ++++++++ 4 files changed, 50 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index a1029644f..0458cc303 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4564,6 +4564,23 @@ def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.S result = apihelper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] + def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: + """ + Use this method to change search keywords assigned to a regular or custom emoji sticker. + The sticker must belong to a sticker set created by the bot. + Returns True on success. + + :param sticker: File identifier of the sticker. + :type sticker: :obj:`str` + + :param keywords: A JSON-serialized list of 0-20 search keywords for the sticker with total length of up to 64 characters + :type keywords: :obj:`list` of :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.set_sticker_keywords(self.token, sticker, keywords) + def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 86125327b..c48df056d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1620,6 +1620,14 @@ def get_sticker_set(token, name): def get_custom_emoji_stickers(token, custom_emoji_ids): method_url = r'getCustomEmojiStickers' return _make_request(token, method_url, params={'custom_emoji_ids': json.dumps(custom_emoji_ids)}) + +def set_sticker_keywords(token, sticker, keywords=None): + method_url = 'setStickerKeywords' + payload = {'sticker': sticker} + if keywords: + payload['keywords'] = json.dumps(keywords) + return _make_request(token, method_url, params=payload, method='post') + def upload_sticker_file(token, user_id, sticker, sticker_format): method_url = 'uploadStickerFile' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index b50e9875e..4e8ecdc50 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5412,6 +5412,23 @@ async def get_sticker_set(self, name: str) -> types.StickerSet: """ result = await asyncio_helper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) + + async def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: + """ + Use this method to change search keywords assigned to a regular or custom emoji sticker. + The sticker must belong to a sticker set created by the bot. + Returns True on success. + + :param sticker: File identifier of the sticker. + :type sticker: :obj:`str` + + :param keywords: A JSON-serialized list of 0-20 search keywords for the sticker with total length of up to 64 characters + :type keywords: :obj:`list` of :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.set_sticker_keywords(self.token, sticker, keywords) async def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.Sticker]: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 6268fff0d..d6f3774da 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1608,6 +1608,14 @@ async def get_custom_emoji_stickers(token, custom_emoji_ids): method_url = r'getCustomEmojiStickers' return await _process_request(token, method_url, params={'custom_emoji_ids': json.dumps(custom_emoji_ids)}) +async def set_sticker_keywords(token, sticker, keywords=None): + method_url = 'setStickerKeywords' + payload = {'sticker': sticker} + if keywords: + payload['keywords'] = json.dumps(keywords) + + return await _process_request(token, method_url, params=payload, method='post') + async def upload_sticker_file(token, user_id, sticker, sticker_format): method_url = 'uploadStickerFile' payload = {'user_id': user_id, 'sticker_format': sticker_format} From 715aabaf4920fac92b99bee9cc9d698d9c25e646 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 23:06:57 +0400 Subject: [PATCH 1294/1808] Added the method setStickerMaskPosition for changing the mask position of a mask sticker. --- telebot/__init__.py | 17 +++++++++++++++++ telebot/apihelper.py | 7 +++++++ telebot/async_telebot.py | 17 +++++++++++++++++ telebot/asyncio_helper.py | 9 ++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0458cc303..0d2910f7c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4581,6 +4581,23 @@ def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: """ return apihelper.set_sticker_keywords(self.token, sticker, keywords) + def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosition=None) -> bool: + """ + Use this method to change the mask position of a mask sticker. + The sticker must belong to a sticker set that was created by the bot. + Returns True on success. + + :param sticker: File identifier of the sticker. + :type sticker: :obj:`str` + + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces. + :type mask_position: :class:`telebot.types.MaskPosition` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.set_sticker_mask_position(self.token, sticker, mask_position) + def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c48df056d..bdc2112d1 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1628,6 +1628,13 @@ def set_sticker_keywords(token, sticker, keywords=None): payload['keywords'] = json.dumps(keywords) return _make_request(token, method_url, params=payload, method='post') +def set_sticker_mask_position(token, sticker, mask_position=None): + method_url = 'setStickerMaskPosition' + payload = {'sticker': sticker} + if mask_position: + payload['mask_position'] = mask_position.to_json() + return _make_request(token, method_url, params=payload, method='post') + def upload_sticker_file(token, user_id, sticker, sticker_format): method_url = 'uploadStickerFile' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4e8ecdc50..e169d2ea9 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5429,6 +5429,23 @@ async def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> :rtype: :obj:`bool` """ return await asyncio_helper.set_sticker_keywords(self.token, sticker, keywords) + + async def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosition=None) -> bool: + """ + Use this method to change the mask position of a mask sticker. + The sticker must belong to a sticker set that was created by the bot. + Returns True on success. + + :param sticker: File identifier of the sticker. + :type sticker: :obj:`str` + + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces. + :type mask_position: :class:`telebot.types.MaskPosition` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.set_sticker_mask_position(self.token, sticker, mask_position) async def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.Sticker]: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index d6f3774da..23be083d9 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1613,7 +1613,14 @@ async def set_sticker_keywords(token, sticker, keywords=None): payload = {'sticker': sticker} if keywords: payload['keywords'] = json.dumps(keywords) - + + return await _process_request(token, method_url, params=payload, method='post') + +async def set_sticker_mask_position(token, sticker, mask_position=None): + method_url = 'setStickerMaskPosition' + payload = {'sticker': sticker} + if mask_position: + payload['mask_position'] = mask_position.to_json() return await _process_request(token, method_url, params=payload, method='post') async def upload_sticker_file(token, user_id, sticker, sticker_format): From 5c6b867582a9d5ad5007e56a89511d5c80404877 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 23:34:17 +0400 Subject: [PATCH 1295/1808] Renamed the field thumb in the classes Animation, Audio, Document, Sticker, Video, VideoNote, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaVideo, StickerSet to thumbnail. Renamed the parameter thumb in the methods sendAnimation, sendAudio, sendDocument, sendVideo, sendVideoNote to thumbnail. --- telebot/__init__.py | 74 ++++++++++++------- telebot/apihelper.py | 30 ++++---- telebot/async_telebot.py | 75 ++++++++++++------- telebot/asyncio_helper.py | 30 ++++---- telebot/types.py | 149 ++++++++++++++++++++------------------ 5 files changed, 209 insertions(+), 149 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0d2910f7c..6ac67a6a1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1837,11 +1837,12 @@ def send_audio( parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None,) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -1887,11 +1888,11 @@ def send_audio( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. + :param thumbnail: 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 - :type thumb: :obj:`str` + :type thumbnail: :obj:`str` :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -1913,10 +1914,14 @@ def send_audio( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if thumb is not None and thumbnail is None: + thumbnail = thumb + logger.warning('thumb is deprecated, use thumbnail instead') + return types.Message.de_json( apihelper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb, + reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) # TODO: Rewrite this method like in API. @@ -2003,13 +2008,14 @@ def send_document( parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: 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, disable_content_type_detection: Optional[bool]=None, data: Optional[Union[Any, str]]=None, - protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None) -> types.Message: + protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None,) -> types.Message: """ Use this method to send general files. @@ -2042,8 +2048,8 @@ def send_document( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :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 - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param thumbnail: 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 + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -2078,11 +2084,15 @@ def send_document( # function typo miss compatibility document = data + if thumb is not None and thumbnail is None: + thumbnail = thumb + logger.warning('thumb is deprecated, use thumbnail instead') + return types.Message.de_json( apihelper.send_data( self.token, chat_id, document, '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, + disable_notification = disable_notification, timeout = timeout, caption = caption, thumb = thumbnail, 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, protect_content = protect_content, message_thread_id = message_thread_id)) @@ -2167,7 +2177,7 @@ def send_video( duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, @@ -2180,7 +2190,8 @@ def send_video( timeout: Optional[int]=None, data: Optional[Union[Any, str]]=None, message_thread_id: Optional[int]=None, - has_spoiler: Optional[bool]=None) -> types.Message: + has_spoiler: Optional[bool]=None, + thumb: Optional[Union[Any, str]]=None,) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2201,8 +2212,8 @@ def send_video( :param height: Video height :type height: :obj:`int` - :param thumb: 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 . - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param thumbnail: 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 . + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -2257,10 +2268,14 @@ def send_video( # function typo miss compatibility video = data + if thumb is not None and thumbnail is None: + thumbnail = thumb + logger.warning('thumb is deprecated, use thumbnail instead') + return types.Message.de_json( apihelper.send_video( self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, - parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, + parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) def send_animation( @@ -2268,7 +2283,7 @@ def send_animation( duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, @@ -2279,7 +2294,8 @@ def send_animation( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - has_spoiler: Optional[bool]=None) -> types.Message: + has_spoiler: Optional[bool]=None, + thumb: Optional[Union[Any, str]]=None,) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2302,11 +2318,11 @@ def send_animation( :param height: Animation height :type height: :obj:`int` - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. + :param thumbnail: 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 . - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption: Animation caption (may also be used when resending animation by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -2351,10 +2367,13 @@ def send_animation( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if thumbnail is None and thumb is not None: + thumbnail = thumb + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( apihelper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb, + reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, allow_sending_without_reply, protect_content, width, height, message_thread_id, has_spoiler)) # TODO: Rewrite this method like in API. @@ -2366,10 +2385,11 @@ def send_video_note( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -2403,11 +2423,11 @@ def send_video_note( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. + :param thumbnail: 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 . - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param allow_sending_without_reply: 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` @@ -2425,10 +2445,14 @@ def send_video_note( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if thumbnail is None and thumb is not None: + thumbnail = thumb + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + return types.Message.de_json( apihelper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb, allow_sending_without_reply, protect_content, message_thread_id)) + disable_notification, timeout, thumbnail, allow_sending_without_reply, protect_content, message_thread_id)) def send_media_group( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index bdc2112d1..1f12c367d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -697,11 +697,11 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if width: payload['width'] = width if height: @@ -748,11 +748,11 @@ def send_animation( if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -832,11 +832,11 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: @@ -877,11 +877,11 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -922,11 +922,11 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e169d2ea9..41decc781 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2700,11 +2700,12 @@ async def send_audio( parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2750,11 +2751,11 @@ async def send_audio( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. + :param thumbnail: 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 - :type thumb: :obj:`str` + :type thumbnail: :obj:`str` :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -2776,10 +2777,14 @@ async def send_audio( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if thumb is not None and thumbnail is None: + thumbnail = thumb + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + return types.Message.de_json( await asyncio_helper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb, + reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) async def send_voice( @@ -2864,14 +2869,15 @@ async def send_document( parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: 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, disable_content_type_detection: Optional[bool]=None, data: Optional[Union[Any, str]]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send general files. @@ -2904,8 +2910,8 @@ async def send_document( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :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 - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param thumbnail: 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 + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -2940,11 +2946,15 @@ async def send_document( # function typo miss compatibility document = data + if thumb is not None and thumbnail is None: + thumbnail = thumb + logger.warning('thumb is deprecated, use thumbnail instead') + return types.Message.de_json( await asyncio_helper.send_data( self.token, chat_id, document, '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, + disable_notification = disable_notification, timeout = timeout, caption = caption, thumb = thumbnail, 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, protect_content = protect_content, message_thread_id = message_thread_id)) @@ -3027,7 +3037,7 @@ async def send_video( duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, @@ -3040,7 +3050,8 @@ async def send_video( timeout: Optional[int]=None, data: Optional[Union[Any, str]]=None, message_thread_id: Optional[int]=None, - has_spoiler: Optional[bool]=None) -> types.Message: + has_spoiler: Optional[bool]=None, + thumb: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -3061,8 +3072,8 @@ async def send_video( :param height: Video height :type height: :obj:`int` - :param thumb: 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 . - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param thumbnail: 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 . + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -3118,10 +3129,14 @@ async def send_video( logger.warning("send_sticker: data parameter is deprecated. Use video instead.") video = data + if thumb and not(thumbnail): + logger.warning("send_sticker: thumb parameter is deprecated. Use thumbnail instead.") + thumbnail = thumb + return types.Message.de_json( await asyncio_helper.send_video( self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, - parse_mode, supports_streaming, disable_notification, timeout, thumb, width, height, + parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) async def send_animation( @@ -3129,7 +3144,7 @@ async def send_animation( duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, @@ -3140,7 +3155,8 @@ async def send_animation( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - has_spoiler: Optional[bool]=None) -> types.Message: + has_spoiler: Optional[bool]=None, + thumb: Optional[Union[Any, str]]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -3163,11 +3179,11 @@ async def send_animation( :param height: Animation height :type height: :obj:`int` - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. + :param thumbnail: 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 . - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption: Animation caption (may also be used when resending animation by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -3212,10 +3228,14 @@ async def send_animation( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if thumb is not None and thumbnail is None: + thumbnail = thumb + logger.warning('thumb is deprecated, use thumbnail instead') + return types.Message.de_json( await asyncio_helper.send_animation( self.token, chat_id, animation, duration, caption, reply_to_message_id, - reply_markup, parse_mode, disable_notification, timeout, thumb, + reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, allow_sending_without_reply, width, height, protect_content, message_thread_id, has_spoiler)) async def send_video_note( @@ -3226,10 +3246,11 @@ async def send_video_note( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None, + thumbnail: Optional[Union[Any, str]]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + thumb: Optional[Union[Any, str]]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -3263,11 +3284,11 @@ async def send_video_note( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. + :param thumbnail: 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 . - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param allow_sending_without_reply: 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` @@ -3285,10 +3306,14 @@ async def send_video_note( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if thumb is not None and thumbnail is None: + thumbnail = thumb + logger.warning('thumb is deprecated, use thumbnail instead') + return types.Message.de_json( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumb, allow_sending_without_reply, protect_content, message_thread_id)) + disable_notification, timeout, thumbnail, allow_sending_without_reply, protect_content, message_thread_id)) async def send_media_group( self, chat_id: Union[int, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 23be083d9..dc24cf6e3 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -689,11 +689,11 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if width: payload['width'] = width if height: @@ -740,11 +740,11 @@ async def send_animation( if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -824,11 +824,11 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: @@ -869,11 +869,11 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -914,11 +914,11 @@ async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, r if thumb: if not util.is_string(thumb): if files: - files['thumb'] = thumb + files['thumbnail'] = thumb else: - files = {'thumb': thumb} + files = {'thumbnail': thumb} else: - payload['thumb'] = thumb + payload['thumbnail'] = thumb if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: diff --git a/telebot/types.py b/telebot/types.py index a5e056d10..c18ca321e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1552,8 +1552,8 @@ class Audio(JsonDeserializable): double-precision float type are safe for storing this value. :type file_size: :obj:`int` - :param thumb: Optional. Thumbnail of the album cover to which the music file belongs - :type thumb: :class:`telebot.types.PhotoSize` + :param thumbnail: Optional. Thumbnail of the album cover to which the music file belongs + :type thumbnail: :class:`telebot.types.PhotoSize` :return: Instance of the class :rtype: :class:`telebot.types.Audio` @@ -1562,14 +1562,14 @@ class Audio(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) + if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: + obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) else: - obj['thumb'] = None + obj['thumbnail'] = None return cls(**obj) def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, - file_size=None, thumb=None, **kwargs): + file_size=None, thumbnail=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.duration: int = duration @@ -1578,7 +1578,8 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size - self.thumb: PhotoSize = thumb + self.thumbnail: PhotoSize = thumbnail + self.thumb = thumbnail class Voice(JsonDeserializable): @@ -1635,8 +1636,8 @@ class Document(JsonDeserializable): bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` - :param thumb: Optional. Document thumbnail as defined by sender - :type thumb: :class:`telebot.types.PhotoSize` + :param thumbnail: Optional. Document thumbnail as defined by sender + :type thumbnail: :class:`telebot.types.PhotoSize` :param file_name: Optional. Original filename as defined by sender :type file_name: :obj:`str` @@ -1656,19 +1657,20 @@ class Document(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) + if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: + obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) else: - obj['thumb'] = None + obj['thumbnail'] = None return cls(**obj) - def __init__(self, file_id, file_unique_id, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + def __init__(self, file_id, file_unique_id, thumbnail=None, file_name=None, mime_type=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id - self.thumb: PhotoSize = thumb + self.thumbnail: PhotoSize = thumbnail self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size + self.thumb = thumbnail class Video(JsonDeserializable): @@ -1693,8 +1695,8 @@ class Video(JsonDeserializable): :param duration: Duration of the video in seconds as defined by sender :type duration: :obj:`int` - :param thumb: Optional. Video thumbnail - :type thumb: :class:`telebot.types.PhotoSize` + :param thumbnail: Optional. Video thumbnail + :type thumbnail: :class:`telebot.types.PhotoSize` :param file_name: Optional. Original filename as defined by sender :type file_name: :obj:`str` @@ -1714,20 +1716,21 @@ class Video(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) + if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: + obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) return cls(**obj) - def __init__(self, file_id, file_unique_id, width, height, duration, thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=None, file_name=None, mime_type=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.width: int = width self.height: int = height self.duration: int = duration - self.thumb: PhotoSize = thumb + self.thumbnail: PhotoSize = thumbnail self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size + self.thumb = thumbnail class VideoNote(JsonDeserializable): @@ -1749,8 +1752,8 @@ class VideoNote(JsonDeserializable): :param duration: Duration of the video in seconds as defined by sender :type duration: :obj:`int` - :param thumb: Optional. Video thumbnail - :type thumb: :class:`telebot.types.PhotoSize` + :param thumbnail: Optional. Video thumbnail + :type thumbnail: :class:`telebot.types.PhotoSize` :param file_size: Optional. File size in bytes :type file_size: :obj:`int` @@ -1762,17 +1765,18 @@ class VideoNote(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) + if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: + obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) return cls(**obj) - def __init__(self, file_id, file_unique_id, length, duration, thumb=None, file_size=None, **kwargs): + def __init__(self, file_id, file_unique_id, length, duration, thumbnail=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.length: int = length self.duration: int = duration - self.thumb: PhotoSize = thumb + self.thumbnail: PhotoSize = thumbnail self.file_size: int = file_size + self.thumb = thumbnail class Contact(JsonDeserializable): @@ -5389,8 +5393,8 @@ class Animation(JsonDeserializable): :param duration: Duration of the video in seconds as defined by sender :type duration: :obj:`int` - :param thumb: Optional. Animation thumbnail as defined by sender - :type thumb: :class:`telebot.types.PhotoSize` + :param thumbnail: Optional. Animation thumbnail as defined by sender + :type thumbnail: :class:`telebot.types.PhotoSize` :param file_name: Optional. Original animation filename as defined by sender :type file_name: :obj:`str` @@ -5410,20 +5414,21 @@ class Animation(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj["thumb"] = PhotoSize.de_json(obj['thumb']) + if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: + obj["thumbnail"] = PhotoSize.de_json(obj['thumbnail']) else: - obj['thumb'] = None + obj['thumbnail'] = None return cls(**obj) def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, - thumb=None, file_name=None, mime_type=None, file_size=None, **kwargs): + thumbnail=None, file_name=None, mime_type=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.width: int = width self.height: int = height self.duration: int = duration - self.thumb: PhotoSize = thumb + self.thumbnail: PhotoSize = thumbnail + self.thumb: PhotoSize = thumbnail self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size @@ -5819,8 +5824,8 @@ class StickerSet(JsonDeserializable): :param stickers: List of all set stickers :type stickers: :obj:`list` of :class:`telebot.types.Sticker` - :param thumb: Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format - :type thumb: :class:`telebot.types.PhotoSize` + :param thumbnail: Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format + :type thumbnail: :class:`telebot.types.PhotoSize` :return: Instance of the class :rtype: :class:`telebot.types.StickerSet` @@ -5833,20 +5838,21 @@ def de_json(cls, json_string): for s in obj['stickers']: stickers.append(Sticker.de_json(s)) obj['stickers'] = stickers - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) + if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: + obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) else: - obj['thumb'] = None + obj['thumbnail'] = None return cls(**obj) - def __init__(self, name, title, sticker_type, is_animated, is_video, stickers, thumb=None, **kwargs): + def __init__(self, name, title, sticker_type, is_animated, is_video, stickers, thumbnail=None, **kwargs): self.name: str = name self.title: str = title self.sticker_type: str = sticker_type self.is_animated: bool = is_animated self.is_video: bool = is_video self.stickers: List[Sticker] = stickers - self.thumb: PhotoSize = thumb + self.thumbnail: PhotoSize = thumbnail + self.thumb = thumbnail @property def contains_masks(self): @@ -5886,8 +5892,8 @@ class Sticker(JsonDeserializable): :param is_video: True, if the sticker is a video sticker :type is_video: :obj:`bool` - :param thumb: Optional. Sticker thumbnail in the .WEBP or .JPG format - :type thumb: :class:`telebot.types.PhotoSize` + :param thumbnail: Optional. Sticker thumbnail in the .WEBP or .JPG format + :type thumbnail: :class:`telebot.types.PhotoSize` :param emoji: Optional. Emoji associated with the sticker :type emoji: :obj:`str` @@ -5920,10 +5926,10 @@ class Sticker(JsonDeserializable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - if 'thumb' in obj and 'file_id' in obj['thumb']: - obj['thumb'] = PhotoSize.de_json(obj['thumb']) + if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: + obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) else: - obj['thumb'] = None + obj['thumbnail'] = None if 'mask_position' in obj: obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) if 'premium_animation' in obj: @@ -5931,7 +5937,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, file_id, file_unique_id, type, width, height, is_animated, - is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, + is_video, thumbnail=None, emoji=None, set_name=None, mask_position=None, file_size=None, premium_animation=None, custom_emoji_id=None, needs_repainting=None ,**kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id @@ -5940,7 +5946,7 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, self.height: int = height self.is_animated: bool = is_animated self.is_video: bool = is_video - self.thumb: PhotoSize = thumb + self.thumbnail: PhotoSize = thumbnail self.emoji: str = emoji self.set_name: str = set_name self.mask_position: MaskPosition = mask_position @@ -5948,6 +5954,7 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, self.premium_animation: File = premium_animation self.custom_emoji_id: int = custom_emoji_id self.needs_repainting: bool = needs_repainting + self.thumb = self.thumbnail @@ -6099,12 +6106,12 @@ class InputMediaVideo(InputMedia): multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` - :param thumb: Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported + :param thumbnail: Optional. 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 . More information on Sending Files » - :type thumb: InputFile or :obj:`str` + :type thumbnail: InputFile or :obj:`str` :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -6135,21 +6142,22 @@ class InputMediaVideo(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaVideo` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, width=None, height=None, duration=None, supports_streaming=None, has_spoiler=None): super(InputMediaVideo, self).__init__( type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumb = thumb + self.thumbnail = thumbnail self.width = width self.height = height self.duration = duration self.supports_streaming = supports_streaming self.has_spoiler: Optional[bool] = has_spoiler + self.thumb = thumbnail def to_dict(self): ret = super(InputMediaVideo, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb + if self.thumbnail: + ret['thumbnail'] = self.thumbnail if self.width: ret['width'] = self.width if self.height: @@ -6207,20 +6215,21 @@ class InputMediaAnimation(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAnimation` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, width=None, height=None, duration=None, has_spoiler=None): super(InputMediaAnimation, self).__init__( type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumb = thumb + self.thumbnail = thumbnail self.width = width self.height = height self.duration = duration self.has_spoiler: Optional[bool] = has_spoiler + self.thumb = thumbnail def to_dict(self): ret = super(InputMediaAnimation, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb + if self.thumbnail: + ret['thumbnail'] = self.thumbnail if self.width: ret['width'] = self.width if self.height: @@ -6243,12 +6252,12 @@ class InputMediaAudio(InputMedia): multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` - :param thumb: Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported + :param thumbnail: Optional. 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 . More information on Sending Files » - :type thumb: InputFile or :obj:`str` + :type thumbnail: InputFile or :obj:`str` :param caption: Optional. Caption of the audio to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -6273,19 +6282,20 @@ class InputMediaAudio(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAudio` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, duration=None, performer=None, title=None): super(InputMediaAudio, self).__init__( type="audio", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumb = thumb + self.thumbnail = thumbnail self.duration = duration self.performer = performer self.title = title + self.thumb = thumbnail def to_dict(self): ret = super(InputMediaAudio, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb + if self.thumbnail: + ret['thumbnail'] = self.thumbnail if self.duration: ret['duration'] = self.duration if self.performer: @@ -6306,12 +6316,12 @@ class InputMediaDocument(InputMedia): multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` - :param thumb: Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported + :param thumbnail: Optional. 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 . More information on Sending Files » - :type thumb: InputFile or :obj:`str` + :type thumbnail: InputFile or :obj:`str` :param caption: Optional. Caption of the document to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -6331,17 +6341,18 @@ class InputMediaDocument(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaDocument` """ - def __init__(self, media, thumb=None, caption=None, parse_mode=None, caption_entities=None, + def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, disable_content_type_detection=None): super(InputMediaDocument, self).__init__( type="document", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumb = thumb + self.thumbnail = thumbnail self.disable_content_type_detection = disable_content_type_detection + self.thumb = thumbnail def to_dict(self): ret = super(InputMediaDocument, self).to_dict() - if self.thumb: - ret['thumb'] = self.thumb + if self.thumbnail: + ret['thumbnail'] = self.thumbnail if self.disable_content_type_detection is not None: ret['disable_content_type_detection'] = self.disable_content_type_detection return ret From 3b4e6fed040e7d81ddf3a5395caab4b9a46ccefb Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 23:37:32 +0400 Subject: [PATCH 1296/1808] Renamed the method setStickerSetThumb to setStickerSetThumbnail and its parameter thumb to thumbnail. --- telebot/__init__.py | 4 +++- telebot/apihelper.py | 2 +- telebot/async_telebot.py | 4 +++- telebot/asyncio_helper.py | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6ac67a6a1..a79bbc26d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4537,7 +4537,7 @@ def answer_callback_query( """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - def set_sticker_set_thumb( + def set_sticker_set_thumbnail( self, name: str, user_id: int, thumb: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. @@ -4558,6 +4558,8 @@ def set_sticker_set_thumb( :rtype: :obj:`bool` """ return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) + + set_sticker_set_thumb = set_sticker_set_thumbnail def get_sticker_set(self, name: str) -> types.StickerSet: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1f12c367d..0a30fcbea 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -356,7 +356,7 @@ def get_chat_member_count(token, chat_id): def set_sticker_set_thumb(token, name, user_id, thumb): - method_url = r'setStickerSetThumb' + method_url = r'setStickerSetThumbnail' payload = {'name': name, 'user_id': user_id} files = {} if thumb: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 41decc781..63f2bddc9 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5401,7 +5401,7 @@ async def answer_callback_query( """ return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - async def set_sticker_set_thumb( + async def set_sticker_set_thumbnail( self, name: str, user_id: int, thumb: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. @@ -5422,6 +5422,8 @@ async def set_sticker_set_thumb( :rtype: :obj:`bool` """ return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) + + set_sticker_set_thumb = set_sticker_set_thumbnail async def get_sticker_set(self, name: str) -> types.StickerSet: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index dc24cf6e3..e00410a23 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -342,7 +342,7 @@ async def get_chat_member_count(token, chat_id): async def set_sticker_set_thumb(token, name, user_id, thumb): - method_url = r'setStickerSetThumb' + method_url = r'setStickerSetThumbnail' payload = {'name': name, 'user_id': user_id} files = {} if thumb: From 991679bedc778cfa11e3d0c48984a37133492b27 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 23:53:16 +0400 Subject: [PATCH 1297/1808] Renamed all necessary thumbs to thumbnails in types.py --- telebot/types.py | 265 +++++++++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 111 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index c18ca321e..cd7d00f79 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3994,27 +3994,32 @@ class InlineQueryResultArticle(InlineQueryResultBase): :param description: Optional. Short description of the result :type description: :obj:`str` - :param thumb_url: Optional. Url of the thumbnail for the result - :type thumb_url: :obj:`str` + :param thumbnail_url: Optional. Url of the thumbnail for the result + :type thumbnail_url: :obj:`str` - :param thumb_width: Optional. Thumbnail width - :type thumb_width: :obj:`int` + :param thumbnail_width: Optional. Thumbnail width + :type thumbnail_width: :obj:`int` - :param thumb_height: Optional. Thumbnail height - :type thumb_height: :obj:`int` + :param thumbnail_height: Optional. Thumbnail height + :type thumbnail_height: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultArticle` """ 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): + url=None, hide_url=None, description=None, thumbnail_url=None, thumbnail_width=None, thumbnail_height=None): 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 - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height + self.thumbnail_url = thumbnail_url + self.thumbnail_width = thumbnail_width + self.thumbnail_height = thumbnail_height + + # deprecateds + self.thumb_url = thumbnail_url + self.thumb_width = thumbnail_width + self.thumb_height = thumbnail_height def to_dict(self): json_dict = super().to_dict() @@ -4024,12 +4029,12 @@ def to_dict(self): json_dict['hide_url'] = self.hide_url if self.description: json_dict['description'] = self.description - if self.thumb_url: - json_dict['thumb_url'] = self.thumb_url + if self.thumbnail_url: + json_dict['thumbnail_url'] = self.thumbnail_url if self.thumb_width: - json_dict['thumb_width'] = self.thumb_width + json_dict['thumbnail_width'] = self.thumbnail_width if self.thumb_height: - json_dict['thumb_height'] = self.thumb_height + json_dict['thumbnail_height'] = self.thumbnail_height return json_dict @@ -4048,8 +4053,8 @@ class InlineQueryResultPhoto(InlineQueryResultBase): :param photo_url: A valid URL of the photo. Photo must be in JPEG format. Photo size must not exceed 5MB :type photo_url: :obj:`str` - :param thumb_url: URL of the thumbnail for the photo - :type thumb_url: :obj:`str` + :param thumbnail_url: URL of the thumbnail for the photo + :type thumbnail_url: :obj:`str` :param photo_width: Optional. Width of the photo :type photo_width: :obj:`int` @@ -4083,21 +4088,24 @@ class InlineQueryResultPhoto(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultPhoto` """ - def __init__(self, id, photo_url, thumb_url, photo_width=None, photo_height=None, title=None, + def __init__(self, id, photo_url, thumbnail_url, photo_width=None, photo_height=None, title=None, description=None, caption=None, caption_entities=None, parse_mode=None, reply_markup=None, input_message_content=None): 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.thumbnail_url = thumbnail_url self.photo_width = photo_width self.photo_height = photo_height self.description = description + # deprecateds + self.thumb_url = thumbnail_url + def to_dict(self): json_dict = super().to_dict() json_dict['photo_url'] = self.photo_url - json_dict['thumb_url'] = self.thumb_url + json_dict['thumbnail_url'] = self.thumbnail_url if self.photo_width: json_dict['photo_width'] = self.photo_width if self.photo_height: @@ -4131,12 +4139,12 @@ class InlineQueryResultGif(InlineQueryResultBase): :param gif_duration: Optional. Duration of the GIF in seconds :type gif_duration: :obj:`int` - :param thumb_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result - :type thumb_url: :obj:`str` + :param thumbnail_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result + :type thumbnail_url: :obj:`str` - :param thumb_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or + :param thumbnail_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg” - :type thumb_mime_type: :obj:`str` + :type thumbnail_mime_type: :obj:`str` :param title: Optional. Title for the result :type title: :obj:`str` @@ -4160,19 +4168,23 @@ class InlineQueryResultGif(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultGif` """ - def __init__(self, id, gif_url, thumb_url, gif_width=None, gif_height=None, + def __init__(self, id, gif_url, thumbnail_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, - thumb_mime_type=None): + thumbnail_mime_type=None): 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.thumbnail_url = thumbnail_url self.gif_duration = gif_duration - self.thumb_mime_type = thumb_mime_type + self.thumbnail_mime_type = thumbnail_mime_type + + # deprecateds + self.thumb_url = thumbnail_url + self.thumb_mime_type = thumbnail_mime_type def to_dict(self): json_dict = super().to_dict() @@ -4181,11 +4193,11 @@ def to_dict(self): json_dict['gif_width'] = self.gif_width if self.gif_height: json_dict['gif_height'] = self.gif_height - json_dict['thumb_url'] = self.thumb_url + json_dict['thumbnail_url'] = self.thumbnail_url if self.gif_duration: json_dict['gif_duration'] = self.gif_duration - if self.thumb_mime_type: - json_dict['thumb_mime_type'] = self.thumb_mime_type + if self.thumbnail_mime_type: + json_dict['thumbnail_mime_type'] = self.thumbnail_mime_type return json_dict @@ -4213,12 +4225,12 @@ class InlineQueryResultMpeg4Gif(InlineQueryResultBase): :param mpeg4_duration: Optional. Video duration in seconds :type mpeg4_duration: :obj:`int` - :param thumb_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result - :type thumb_url: :obj:`str` + :param thumbnail_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result + :type thumbnail_url: :obj:`str` - :param thumb_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or + :param thumbnail_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg” - :type thumb_mime_type: :obj:`str` + :type thumbnail_mime_type: :obj:`str` :param title: Optional. Title for the result :type title: :obj:`str` @@ -4242,19 +4254,23 @@ class InlineQueryResultMpeg4Gif(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultMpeg4Gif` """ - def __init__(self, id, mpeg4_url, thumb_url, mpeg4_width=None, mpeg4_height=None, + def __init__(self, id, mpeg4_url, thumbnail_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, - thumb_mime_type=None): + thumbnail_mime_type=None): 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.thumbnail_url = thumbnail_url self.mpeg4_duration = mpeg4_duration - self.thumb_mime_type = thumb_mime_type + self.thumbnail_mime_type = thumbnail_mime_type + + # deprecateds + self.thumb_url = thumbnail_url + self.thumb_mime_type = thumbnail_mime_type def to_dict(self): json_dict = super().to_dict() @@ -4263,11 +4279,11 @@ def to_dict(self): json_dict['mpeg4_width'] = self.mpeg4_width if self.mpeg4_height: json_dict['mpeg4_height'] = self.mpeg4_height - json_dict['thumb_url'] = self.thumb_url + json_dict['thumbnail_url'] = self.thumbnail_url if self.mpeg4_duration: json_dict['mpeg4_duration '] = self.mpeg4_duration - if self.thumb_mime_type: - json_dict['thumb_mime_type'] = self.thumb_mime_type + if self.thumbnail_mime_type: + json_dict['thumbnail_mime_type'] = self.thumbnail_mime_type return json_dict @@ -4289,8 +4305,8 @@ class InlineQueryResultVideo(InlineQueryResultBase): :param mime_type: MIME type of the content of the video URL, “text/html” or “video/mp4” :type mime_type: :obj:`str` - :param thumb_url: URL of the thumbnail (JPEG only) for the video - :type thumb_url: :obj:`str` + :param thumbnail_url: URL of the thumbnail (JPEG only) for the video + :type thumbnail_url: :obj:`str` :param title: Title for the result :type title: :obj:`str` @@ -4328,7 +4344,7 @@ class InlineQueryResultVideo(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultVideo` """ - def __init__(self, id, video_url, mime_type, thumb_url, + def __init__(self, id, video_url, mime_type, thumbnail_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): @@ -4337,17 +4353,20 @@ def __init__(self, id, video_url, mime_type, thumb_url, parse_mode = parse_mode, caption_entities = caption_entities) self.video_url = video_url self.mime_type = mime_type - self.thumb_url = thumb_url + self.thumbnail_url = thumbnail_url self.video_width = video_width self.video_height = video_height self.video_duration = video_duration self.description = description + # deprecated + self.thumb_url = thumbnail_url + 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 + json_dict['thumbnail_url'] = self.thumbnail_url if self.video_height: json_dict['video_height'] = self.video_height if self.video_duration: @@ -4519,30 +4538,36 @@ class InlineQueryResultDocument(InlineQueryResultBase): :param input_message_content: Optional. Content of the message to be sent instead of the file :type input_message_content: :class:`telebot.types.InputMessageContent` - :param thumb_url: Optional. URL of the thumbnail (JPEG only) for the file - :type thumb_url: :obj:`str` + :param thumbnail_url: Optional. URL of the thumbnail (JPEG only) for the file + :type thumbnail_url: :obj:`str` - :param thumb_width: Optional. Thumbnail width - :type thumb_width: :obj:`int` + :param thumbnail_width: Optional. Thumbnail width + :type thumbnail_width: :obj:`int` - :param thumb_height: Optional. Thumbnail height - :type thumb_height: :obj:`int` + :param thumbnail_height: Optional. Thumbnail height + :type thumbnail_height: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultDocument` """ 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): + thumbnail_url=None, thumbnail_width=None, thumbnail_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.description = description - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height + self.thumbnail_url = thumbnail_url + self.thumbnail_width = thumbnail_width + self.thumbnail_height = thumbnail_height + + # deprecated + self.thumb_url = thumbnail_url + self.thumb_width = thumbnail_width + self.thumb_height = thumbnail_height + def to_dict(self): json_dict = super().to_dict() @@ -4550,12 +4575,12 @@ def to_dict(self): json_dict['mime_type'] = self.mime_type if self.description: json_dict['description'] = self.description - 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.thumbnail_url: + json_dict['thumbnail_url'] = self.thumbnail_url + if self.thumbnail_width: + json_dict['thumbnail_width'] = self.thumbnail_width + if self.thumbnail_height: + json_dict['thumbnail_height'] = self.thumbnail_height return json_dict @@ -4601,20 +4626,20 @@ class InlineQueryResultLocation(InlineQueryResultBase): :param input_message_content: Optional. Content of the message to be sent instead of the location :type input_message_content: :class:`telebot.types.InputMessageContent` - :param thumb_url: Optional. Url of the thumbnail for the result - :type thumb_url: :obj:`str` + :param thumbnail_url: Optional. Url of the thumbnail for the result + :type thumbnail_url: :obj:`str` - :param thumb_width: Optional. Thumbnail width - :type thumb_width: :obj:`int` + :param thumbnail_width: Optional. Thumbnail width + :type thumbnail_width: :obj:`int` - :param thumb_height: Optional. Thumbnail height - :type thumb_height: :obj:`int` + :param thumbnail_height: Optional. Thumbnail height + :type thumbnail_height: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultLocation` """ 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, heading=None, proximity_alert_radius = None): + input_message_content=None, thumbnail_url=None, thumbnail_width=None, thumbnail_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 @@ -4623,9 +4648,14 @@ def __init__(self, id, title, latitude, longitude, horizontal_accuracy, live_per self.live_period = live_period 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 + self.thumbnail_url = thumbnail_url + self.thumbnail_width = thumbnail_width + self.thumbnail_height = thumbnail_height + + # deprecated + self.thumb_url = thumbnail_url + self.thumb_width = thumbnail_width + self.thumb_height = thumbnail_height def to_dict(self): json_dict = super().to_dict() @@ -4639,12 +4669,12 @@ def to_dict(self): 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.thumbnail_url: + json_dict['thumbnail_url'] = self.thumbnail_url + if self.thumbnail_width: + json_dict['thumbnail_width'] = self.thumbnail_width + if self.thumbnail_height: + json_dict['thumbnail_height'] = self.thumbnail_height return json_dict @@ -4691,21 +4721,21 @@ class InlineQueryResultVenue(InlineQueryResultBase): :param input_message_content: Optional. Content of the message to be sent instead of the venue :type input_message_content: :class:`telebot.types.InputMessageContent` - :param thumb_url: Optional. Url of the thumbnail for the result - :type thumb_url: :obj:`str` + :param thumbnail_url: Optional. Url of the thumbnail for the result + :type thumbnail_url: :obj:`str` - :param thumb_width: Optional. Thumbnail width - :type thumb_width: :obj:`int` + :param thumbnail_width: Optional. Thumbnail width + :type thumbnail_width: :obj:`int` - :param thumb_height: Optional. Thumbnail height - :type thumb_height: :obj:`int` + :param thumbnail_height: Optional. Thumbnail height + :type thumbnail_height: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultVenue` """ 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): + reply_markup=None, input_message_content=None, thumbnail_url=None, + thumbnail_width=None, thumbnail_height=None, google_place_id=None, google_place_type=None): super().__init__('venue', id, title = title, input_message_content = input_message_content, reply_markup = reply_markup) self.latitude = latitude @@ -4715,9 +4745,15 @@ def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, self.foursquare_type = foursquare_type 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.thumbnail_url = thumbnail_url + self.thumbnail_width = thumbnail_width + self.thumbnail_height = thumbnail_height + + # deprecated + self.thumb_url = thumbnail_url + self.thumb_width = thumbnail_width + self.thumb_height = thumbnail_height + def to_dict(self): json_dict = super().to_dict() @@ -4732,12 +4768,12 @@ def to_dict(self): json_dict['google_place_id'] = self.google_place_id if self.google_place_type: json_dict['google_place_type'] = self.google_place_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.thumbnail_url: + json_dict['thumbnail_url'] = self.thumbnail_url + if self.thumbnail_width: + json_dict['thumbnail_width'] = self.thumbnail_width + if self.thumbnail_height: + json_dict['thumbnail_height'] = self.thumbnail_height return json_dict @@ -4771,30 +4807,37 @@ class InlineQueryResultContact(InlineQueryResultBase): :param input_message_content: Optional. Content of the message to be sent instead of the contact :type input_message_content: :class:`telebot.types.InputMessageContent` - :param thumb_url: Optional. Url of the thumbnail for the result - :type thumb_url: :obj:`str` + :param thumbnail_url: Optional. Url of the thumbnail for the result + :type thumbnail_url: :obj:`str` - :param thumb_width: Optional. Thumbnail width - :type thumb_width: :obj:`int` + :param thumbnail_width: Optional. Thumbnail width + :type thumbnail_width: :obj:`int` - :param thumb_height: Optional. Thumbnail height - :type thumb_height: :obj:`int` + :param thumbnail_height: Optional. Thumbnail height + :type thumbnail_height: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultContact` """ 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): + thumbnail_url=None, thumbnail_width=None, thumbnail_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 + self.thumbnail_url = thumbnail_url + self.thumbnail_width = thumbnail_width + self.thumbnail_height = thumbnail_height + + # deprecated + self.thumb_url = thumbnail_url + self.thumb_width = thumbnail_width + self.thumb_height = thumbnail_height + + def to_dict(self): json_dict = super().to_dict() @@ -4804,12 +4847,12 @@ def to_dict(self): 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 + if self.thumbnail_url: + json_dict['thumbnail_url'] = self.thumbnail_url + if self.thumbnail_width: + json_dict['thumbnail_width'] = self.thumbnail_width + if self.thumbnail_height: + json_dict['thumbnail_height'] = self.thumbnail_height return json_dict From 9d2f7c02a4f597361f6ea5b05705b372c789339e Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 11 Mar 2023 23:54:57 +0400 Subject: [PATCH 1298/1808] Fixing tests attempt 1 --- tests/test_types.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index 4587a160e..c696f162c 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -67,9 +67,9 @@ def test_json_GroupChat(): def test_json_Document(): - json_string = r'{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_unique_id": "AgADJQEAAqfhOEY","file_size":446}' + json_string = r'{"file_name":"Text File","thumbnail":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_unique_id": "AgADJQEAAqfhOEY","file_size":446}' doc = types.Document.de_json(json_string) - assert doc.thumb is None + assert doc.thumbnail is None assert doc.file_name == 'Text File' @@ -83,23 +83,23 @@ def test_json_Message_Audio(): def test_json_Message_Sticker(): - json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"type": "regular", "width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "thumb": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 60, "height": 60}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' + json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"type": "regular", "width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "thumbnail": {"file_id": "AAMCBAADHQJOFL7mAAJUMF8Dj62hpmDhpRAYvkc8CtIqipolAAJ8AAPA-8cF9yxjgjkLS97A0D4iXQARtQAHbQADHy4AAhoE", "file_unique_id": "AQADwNA-Il0AAx8uAAI", "file_size": 7776, "width": 60, "height": 60}, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 - assert msg.sticker.thumb.height == 60 + assert msg.sticker.thumbnail.height == 60 assert msg.content_type == 'sticker' -def test_json_Message_Sticker_without_thumb(): +def test_json_Message_Sticker_without_thumbnail(): json_string = r'{"message_id": 21552, "from": {"id": 590740002, "is_bot": false, "first_name": "⚜️ Ƥυrуα ⚜️", "username": "Purya", "language_code": "en"}, "chat": {"id": -1001309982000, "title": "123", "type": "supergroup"}, "date": 1594068909, "sticker": {"type": "regular", "width": 368, "height": 368, "emoji": "🤖", "set_name": "ipuryapack", "is_animated": false, "is_video": true, "file_id": "CAACAgQAAx0CThS-5gACVDBfA4-toaZg4aUQGL5HWerSKoqaJQACArADwPvHBfcsY4I5C3feGgQ", "file_unique_id": "AgADfAADsPvHWQ", "file_size": 14602}}' msg = types.Message.de_json(json_string) assert msg.sticker.height == 368 - assert msg.sticker.thumb is None + assert msg.sticker.thumbnail is None assert msg.content_type == 'sticker' def test_json_Message_Document(): - json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":446}}' + json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumbnail":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_unique_id": "AQAD_QIfa3QAAyA4BgAB","file_size":446}}' msg = types.Message.de_json(json_string) assert msg.document.file_name == 'Text File' assert msg.content_type == 'document' @@ -113,11 +113,11 @@ def test_json_Message_Photo(): def test_json_Message_Video(): - json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_unique_id": "AQADTeisa3QAAz1nAAI","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_unique_id": "AgADbgEAAn8VSFY","file_size":260699}}' + json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumbnail":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_unique_id": "AQADTeisa3QAAz1nAAI","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_unique_id": "AgADbgEAAn8VSFY","file_size":260699}}' msg = types.Message.de_json(json_string) assert msg.video assert msg.video.duration == 3 - assert msg.video.thumb.width == 50 + assert msg.video.thumbnail.width == 50 assert msg.content_type == 'video' From c47c26d2b0ce1245bb2391d018d07d5529f709f3 Mon Sep 17 00:00:00 2001 From: Arash Nemat Zadeh Date: Tue, 14 Mar 2023 17:29:51 +0330 Subject: [PATCH 1299/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ffcc909bb..8c8368a7e 100644 --- a/README.md +++ b/README.md @@ -889,5 +889,6 @@ Here are some examples of template: * [TeleServ](https://github.com/ablakely/TeleServ) by [ablakely](https://github.com/ablakely) This is a Telegram to IRC bridge which links as an IRC server and makes Telegram users appear as native IRC users. * [Simple Store Bot](https://github.com/AntonGlyzin/myshopbot) by [Anton Glyzin](https://github.com/AntonGlyzin) This is a simple telegram-store with an admin panel. Designed according to a template. * [Media Rating Bot](https://t.me/mediaratingbot) ([source](https://github.com/CommanderCRM/MediaRatingBot))by [CommanderCRM](https://github.com/CommanderCRM). This bot aggregates media (movies, TV series, etc.) ratings from IMDb, Rotten Tomatoes, Metacritic, TheMovieDB, FilmAffinity and also provides number of votes of said media on IMDb. +* [Spot Seek Bot](https://t.me/SpotSeekBot) ([source](https://github.com/arashnm80/spot-seek-bot)) by [Arashnm80](https://github.com/arashnm80). This is a free & open source telegram bot for downloading tracks, albums or playlists from spotify. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 67a52b2e983fc5c60f465a7786abdf450a67b8bf Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 15 Mar 2023 05:46:03 +0400 Subject: [PATCH 1300/1808] Apply suggestions from code review --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0a30fcbea..a0bb59282 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1157,7 +1157,7 @@ def set_chat_title(token, chat_id, title): def set_my_description(token, description=None, language_code=None): method_url = r'setMyDescription' payload = {} - if description: + if description is not None: payload['description'] = description if language_code: payload['language_code'] = language_code From 535a14ca0c47f796e6c160a950eee4b362a696f1 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 15 Mar 2023 05:48:16 +0400 Subject: [PATCH 1301/1808] Update telebot/apihelper.py --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a0bb59282..1100ed7b8 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1159,7 +1159,7 @@ def set_my_description(token, description=None, language_code=None): payload = {} if description is not None: payload['description'] = description - if language_code: + if language_code is not None: payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') From 603a7cf9f2661df4c200c03764077d2bfa40228f Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 15 Mar 2023 05:48:41 +0400 Subject: [PATCH 1302/1808] Update telebot/asyncio_helper.py --- 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 e00410a23..6de797321 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1145,7 +1145,7 @@ async def set_my_description(token, description=None, language_code=None): payload = {} if description: payload['description'] = description - if language_code: + if language_code is not None: payload['language_code'] = language_code return await _process_request(token, method_url, params=payload, method='post') From 1e450ebd155bfdf8a264e795e397b3619b9dd7e6 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 19 Mar 2023 17:31:53 +0400 Subject: [PATCH 1303/1808] Bot API 6.6 review changes --- telebot/__init__.py | 45 ++++++++++++++++++++++++++++++++----- telebot/apihelper.py | 16 ++++++------- telebot/async_telebot.py | 47 +++++++++++++++++++++++++++++++++------ telebot/asyncio_helper.py | 16 ++++++------- telebot/types.py | 3 +++ 5 files changed, 96 insertions(+), 31 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a79bbc26d..0d36d7bde 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4559,7 +4559,29 @@ def set_sticker_set_thumbnail( """ return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) - set_sticker_set_thumb = set_sticker_set_thumbnail + def set_sticker_set_thumb( + self, name: str, user_id: int, thumb: Union[Any, str]=None): + """ + Use this method to set the thumbnail of a sticker set. + Animated thumbnails can be set for animated sticker sets only. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb + + :param name: Sticker set name + :type name: :obj:`str` + + :param user_id: User identifier + :type user_id: :obj:`int` + + :param thumb: + :type thumb: :obj:`filelike object` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + # deprecated + logger.warning('set_sticker_set_thumb is deprecated. Use set_sticker_set_thumbnail instead.') + return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) def get_sticker_set(self, name: str) -> types.StickerSet: """ @@ -4817,7 +4839,7 @@ def create_new_sticker_set( def add_sticker_to_set( - self, user_id: int, name: str, emojis: List[str]=None, + self, user_id: int, name: str, emojis: Union[List[str], str], png_sticker: Optional[Union[Any, str]]=None, tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, @@ -4833,7 +4855,7 @@ def add_sticker_to_set( Telegram documentation: https://core.telegram.org/bots/api#addstickertoset .. note:: - **_sticker parameters are deprecated, use stickers instead + **_sticker, mask_position, emojis parameters are deprecated, use stickers instead :param user_id: User identifier of created sticker set owner :type user_id: :obj:`int` @@ -4859,15 +4881,26 @@ def add_sticker_to_set( :type mask_position: :class:`telebot.types.MaskPosition` :param sticker: A JSON-serialized list of 1-50 initial stickers to be added to the sticker set - :type sticker: :class:`telebot.types.InputSticker` + :type sticker: :obj:`list` of :class:`telebot.types.InputSticker` :return: On success, True is returned. :rtype: :obj:`bool` """ + + # split emojis if string + if isinstance(emojis, str): + emojis = list(emojis) # Replaced the parameters png_sticker, tgs_sticker, webm_sticker, emojis and mask_position if sticker is None: - sticker = png_sticker or tgs_sticker or webm_sticker - sticker = types.InputSticker(sticker, emojis, mask_position) + old_sticker = png_sticker or tgs_sticker or webm_sticker + if old_sticker is not None: + logger.warning( + 'The parameters "png_sticker", "tgs_sticker", "webm_sticker", "emojis" and "mask_position" are deprecated, ' + 'use "sticker" instead' + ) + if not old_sticker: + raise ValueError('You must pass at least one sticker.') + sticker = types.InputSticker(old_sticker, emojis, mask_position) return apihelper.add_sticker_to_set( self.token, user_id, name, sticker) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0a30fcbea..6d59b84ee 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1157,9 +1157,9 @@ def set_chat_title(token, chat_id, title): def set_my_description(token, description=None, language_code=None): method_url = r'setMyDescription' payload = {} - if description: + if description is not None: payload['description'] = description - if language_code: + if language_code is not None: payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') @@ -1174,9 +1174,9 @@ def get_my_description(token, language_code=None): def set_my_short_description(token, short_description=None, language_code=None): method_url = r'setMyShortDescription' payload = {} - if short_description: + if short_description is not None: payload['short_description'] = short_description - if language_code: + if language_code is not None: payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') @@ -1646,7 +1646,7 @@ def upload_sticker_file(token, user_id, sticker, sticker_format): def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): method_url = 'setCustomEmojiStickerSetThumbnail' payload = {'name': name} - if custom_emoji_id: + if custom_emoji_id is not None: payload['custom_emoji_id'] = custom_emoji_id return _make_request(token, method_url, params=payload, method='post') @@ -1667,15 +1667,13 @@ def set_sticker_emoji_list(token, sticker, emoji_list): return _make_request(token, method_url, params=payload, method='post') def create_new_sticker_set( - token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): + token, user_id, name, title, stickers, sticker_format, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' - payload = {'user_id': user_id, 'name': name, 'title': title} + payload = {'user_id': user_id, 'name': name, 'title': title, 'sticker_format': sticker_format} if sticker_type: payload['sticker_type'] = sticker_type if needs_repainting: payload['needs_repainting'] = needs_repainting - if sticker_format: - payload['sticker_format'] = sticker_format files = {} lst = [] diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 63f2bddc9..f13d1a099 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5423,7 +5423,29 @@ async def set_sticker_set_thumbnail( """ return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) - set_sticker_set_thumb = set_sticker_set_thumbnail + async def set_sticker_set_thumb( + self, name: str, user_id: int, thumb: Union[Any, str]=None): + """ + Use this method to set the thumbnail of a sticker set. + Animated thumbnails can be set for animated sticker sets only. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb + + :param name: Sticker set name + :type name: :obj:`str` + + :param user_id: User identifier + :type user_id: :obj:`int` + + :param thumb: + :type thumb: :obj:`filelike object` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + # deprecated + logger.warning('set_sticker_set_thumb is deprecated, use set_sticker_set_thumbnail instead') + return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) async def get_sticker_set(self, name: str) -> types.StickerSet: """ @@ -5681,7 +5703,7 @@ async def create_new_sticker_set( async def add_sticker_to_set( - self, user_id: int, name: str, emojis: List[str]=None, + self, user_id: int, name: str, emojis: Union[List[str], str]=None, png_sticker: Optional[Union[Any, str]]=None, tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, @@ -5694,6 +5716,9 @@ async def add_sticker_to_set( Static sticker sets can have up to 120 stickers. Returns True on success. + .. note:: + **_sticker, mask_position, emojis parameters are deprecated, use stickers instead + Telegram documentation: https://core.telegram.org/bots/api#addstickertoset :param user_id: User identifier of created sticker set owner @@ -5720,17 +5745,25 @@ async def add_sticker_to_set( :type mask_position: :class:`telebot.types.MaskPosition` :param sticker: A JSON-serialized list of 1-50 initial stickers to be added to the sticker set - :type sticker: :class:`telebot.types.InputSticker` + :type sticker: :obj:`list` of :class:`telebot.types.InputSticker` :return: On success, True is returned. :rtype: :obj:`bool` """ + # split emojis if string + if isinstance(emojis, str): + emojis = list(emojis) # Replaced the parameters png_sticker, tgs_sticker, webm_sticker, emojis and mask_position if sticker is None: - sticker = png_sticker or tgs_sticker or webm_sticker - if sticker is None: - raise ValueError('You must pass at least one sticker') - sticker = types.InputSticker(sticker, emojis, mask_position) + old_sticker = png_sticker or tgs_sticker or webm_sticker + if old_sticker is not None: + logger.warning( + 'Parameters "png_sticker", "tgs_sticker", "webm_sticker", "emojis" and "mask_position" are deprecated, ' + 'use "sticker" instead' + ) + if not old_sticker: + raise ValueError('You must pass at least one sticker.') + sticker = types.InputSticker(old_sticker, emojis, mask_position) return await asyncio_helper.add_sticker_to_set( self.token, user_id, name, sticker) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index e00410a23..0494f2741 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1143,9 +1143,9 @@ async def set_chat_title(token, chat_id, title): async def set_my_description(token, description=None, language_code=None): method_url = r'setMyDescription' payload = {} - if description: + if description is not None: payload['description'] = description - if language_code: + if language_code is not None: payload['language_code'] = language_code return await _process_request(token, method_url, params=payload, method='post') @@ -1159,9 +1159,9 @@ async def get_my_description(token, language_code=None): async def set_my_short_description(token, short_description=None, language_code=None): method_url = r'setMyShortDescription' payload = {} - if short_description: + if short_description is not None: payload['short_description'] = short_description - if language_code: + if language_code is not None: payload['language_code'] = language_code return await _process_request(token, method_url, params=payload, method='post') @@ -1642,7 +1642,7 @@ async def delete_sticker_set(token, name): async def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): method_url = 'setCustomEmojiStickerSetThumbnail' payload = {'name': name} - if custom_emoji_id: + if custom_emoji_id is not None: payload['custom_emoji_id'] = custom_emoji_id return await _process_request(token, method_url, params=payload, method='post') @@ -1653,15 +1653,13 @@ async def set_sticker_set_title(token, name, title): return await _process_request(token, method_url, params=payload, method='post') async def create_new_sticker_set( - token, user_id, name, title, stickers, sticker_format=None, sticker_type=None, needs_repainting=None): + token, user_id, name, title, stickers, sticker_format, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' - payload = {'user_id': user_id, 'name': name, 'title': title} + payload = {'user_id': user_id, 'name': name, 'title': title, 'sticker_format': sticker_format} if sticker_type: payload['sticker_type'] = sticker_type if needs_repainting: payload['needs_repainting'] = needs_repainting - if sticker_format: - payload['sticker_format'] = sticker_format files = {} lst = [] diff --git a/telebot/types.py b/telebot/types.py index cd7d00f79..96d526e3c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7436,7 +7436,10 @@ def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], mask_p self._sticker_name = '' self._sticker_dic = self.sticker else: + # work like in inputmedia: convert_input_media self._sticker_name = service_utils.generate_random_token() + # uses attach://_sticker_name for sticker param. then, + # actual file is sent using files param of the request self._sticker_dic = 'attach://{0}'.format(self._sticker_name) def to_dict(self) -> dict: From d8569394b05ddb0a2d855ff4a347f5d6fec913ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=CC=B7=CD=A0=CD=80=CD=9D=CC=9A=CD=86=CD=81=CC=BD=CC=94?= =?UTF-8?q?=CC=88=CC=B1=CC=BA=CD=99=CD=93=CC=AA=CD=94=CC=B9=CD=89=CD=89?= =?UTF-8?q?=CC=AF=CC=96=CC=9Fo=CC=B6=CD=98=CD=92=CC=82=CC=92=CD=8C=CC=BE?= =?UTF-8?q?=CC=87=CD=9D=CD=97=CC=A5=CC=BB=CC=BB=CC=96=CC=AE=CD=9Ad=CC=B8?= =?UTF-8?q?=CD=86=CD=98=CD=9D=CD=84=CD=88=CC=AE=CC=A6=CD=88=CD=99=CC=A2?= =?UTF-8?q?=CC=A1=CC=96=CD=94=CC=A6=CC=AD=CC=A9=CC=B0=CD=8E=CD=89=CC=A3?= =?UTF-8?q?=CC=B0e=CC=B8=CD=86=CC=8E=CC=8F=CD=92=CD=9D=CD=80=CD=84=CC=9A?= =?UTF-8?q?=CD=9B=CD=81=CC=8D=CC=80=CD=80=CC=BF=CD=96B=CC=B5=CC=92=CD=8B?= =?UTF-8?q?=CC=89=CC=91=CC=90=CC=92=CC=85=CC=85=CC=8B=CC=A8=CC=AF=CC=B9?= =?UTF-8?q?=CC=9D=CD=9C=CD=99=CD=89=CC=B2=CC=9F=CC=B3=CC=9F=CD=8E=CC=AA?= =?UTF-8?q?=CC=AB=CC=AA=CC=A4y=CC=B5=CC=93=CC=95=CD=90=CC=8F=CC=9E=CD=9A?= =?UTF-8?q?=CD=95=CC=AD=CC=A4=CC=B1=CD=96=CC=9F=CD=85=CC=AB=CC=9CZ=CC=B8?= =?UTF-8?q?=CD=83=CC=90=CD=86=CD=8E=CC=AB=CC=96=CD=8D=CC=AAe=CC=B6=CC=BD?= =?UTF-8?q?=CC=94=CD=82=CC=89=CD=8A=CD=84=CD=88=CD=87n=CC=B6=CD=81=CC=84?= =?UTF-8?q?=CC=8A=CC=82=CC=8A=CC=9A=CC=8F=CC=84=CD=90=CD=90=CD=9D=CC=BF?= =?UTF-8?q?=CC=93=CC=81=CC=BD=CC=83=CC=84=CC=A3=CC=A7=CC=A3=CD=8E=CC=A4?= =?UTF-8?q?=CC=AF=CD=96=CD=89=CD=8D=CC=B3=CD=87=CD=88=CC=98=CC=B3=CC=97?= =?UTF-8?q?=CD=9A?= <1809483+codebyzen@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:39:24 +0300 Subject: [PATCH 1304/1808] Update README.md I added CalendarIT Telegram bot, can post acquainted with what is happening today, tomorrow or what happened 20 years ago to channel. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a61883665..00fc37222 100644 --- a/README.md +++ b/README.md @@ -890,5 +890,6 @@ Here are some examples of template: * [Simple Store Bot](https://github.com/AntonGlyzin/myshopbot) by [Anton Glyzin](https://github.com/AntonGlyzin) This is a simple telegram-store with an admin panel. Designed according to a template. * [Media Rating Bot](https://t.me/mediaratingbot) ([source](https://github.com/CommanderCRM/MediaRatingBot))by [CommanderCRM](https://github.com/CommanderCRM). This bot aggregates media (movies, TV series, etc.) ratings from IMDb, Rotten Tomatoes, Metacritic, TheMovieDB, FilmAffinity and also provides number of votes of said media on IMDb. * [Spot Seek Bot](https://t.me/SpotSeekBot) ([source](https://github.com/arashnm80/spot-seek-bot)) by [Arashnm80](https://github.com/arashnm80). This is a free & open source telegram bot for downloading tracks, albums or playlists from spotify. +* [CalendarIT Bot](https://t.me/calendarit_bot) ([source](https://github.com/codebyzen/CalendarIT_Telegram_Bot))by [CodeByZen](https://github.com/codebyzen). A simple, but extensible Python Telegram bot, can post acquainted with what is happening today, tomorrow or what happened 20 years ago to channel. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From bd69492ed4b48e95d07ba0c9f060c690f1bd60c9 Mon Sep 17 00:00:00 2001 From: Mike Lei Date: Fri, 24 Mar 2023 02:17:44 +0800 Subject: [PATCH 1305/1808] Fix documentation for `InlineKeyboardMarkup` and `quick_markup` --- telebot/types.py | 22 ++++++++++++++-------- telebot/util.py | 8 +++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 96d526e3c..901a74754 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2432,23 +2432,29 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) This object represents an inline keyboard that appears right next to the message it belongs to. .. note:: - It is recommended to use :meth:`telebot.service_utils..quick_markup` instead. + It is recommended to use :meth:`telebot.util.quick_markup` instead. .. code-block:: python3 :caption: Example of a custom keyboard with buttons. - from telebot.service_utils..import quick_markup + from telebot.util import quick_markup - markup = quick_markup( - {'text': 'Press me', 'callback_data': 'press'}, - {'text': 'Press me too', 'callback_data': 'press_too'} - ) + markup = quick_markup({ + 'Twitter': {'url': 'https://twitter.com'}, + 'Facebook': {'url': 'https://facebook.com'}, + 'Back': {'callback_data': 'whatever'} + }, row_width=2) + # returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook + # and a back button below Telegram Documentation: https://core.telegram.org/bots/api#inlinekeyboardmarkup - :param inline_keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of + :param keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of :class:`telebot.types.InlineKeyboardButton` objects - :type inline_keyboard: :obj:`list` of :obj:`list` of :class:`telebot.types.InlineKeyboardButton` + :type keyboard: :obj:`list` of :obj:`list` of :class:`telebot.types.InlineKeyboardButton` + + :param row_width: number of :class:`telebot.types.InlineKeyboardButton` objects on each row + :type row_width: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.InlineKeyboardMarkup` diff --git a/telebot/util.py b/telebot/util.py index 405daf57e..615b79fa3 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -420,11 +420,13 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int = 2) -> types .. code-block:: python3 :caption: Using quick_markup: - quick_markup({ + from telebot.util import quick_markup + + markup = quick_markup({ 'Twitter': {'url': 'https://twitter.com'}, 'Facebook': {'url': 'https://facebook.com'}, 'Back': {'callback_data': 'whatever'} - }, row_width=2): + }, row_width=2) # returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook # and a back button below @@ -443,7 +445,7 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int = 2) -> types :param values: a dict containing all buttons to create in this format: {text: kwargs} {str:} :type values: :obj:`dict` - :param row_width: int row width + :param row_width: number of :class:`telebot.types.InlineKeyboardButton` objects on each row :type row_width: :obj:`int` :return: InlineKeyboardMarkup From 7c7a063fb6a3c4355e706eb39f340618ce36b7f3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 14:38:02 +0300 Subject: [PATCH 1306/1808] Fix some code hints --- README.md | 1 - telebot/__init__.py | 13 +++++++++++-- telebot/apihelper.py | 3 ++- telebot/async_telebot.py | 24 +++++++++++++++--------- telebot/asyncio_helper.py | 9 ++++++--- 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a61883665..5792f5467 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI) [![Documentation Status](https://readthedocs.org/projects/pytba/badge/?version=latest)](https://pytba.readthedocs.io/en/latest/?badge=latest) -[![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI) [![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/) [![PyPi status](https://img.shields.io/pypi/status/pytelegrambotapi.svg?style=flat-square)](https://pypi.python.org/pypi/pytelegrambotapi) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0d36d7bde..43a462c1d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -451,7 +451,7 @@ def run_webhooks(self, drop_pending_updates: Optional[bool] = None, timeout: Optional[int]=None, secret_token: Optional[str]=None, - secret_token_length: Optional[int]=20,): + secret_token_length: Optional[int]=20): """ This class sets webhooks and listens to a given url and port. @@ -1906,6 +1906,8 @@ def send_audio( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2072,6 +2074,8 @@ def send_document( :param message_thread_id: The thread to which the message will be sent :type message_thread_id: :obj:`int` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2256,6 +2260,8 @@ def send_video( :param has_spoiler: Pass True, if the video should be sent as a spoiler :type has_spoiler: :obj:`bool` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2359,6 +2365,8 @@ def send_animation( :param has_spoiler: Pass True, if the animation should be sent as a spoiler :type has_spoiler: :obj:`bool` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2438,6 +2446,8 @@ def send_video_note( :param message_thread_id: Identifier of a message thread, in which the video note will be sent :type message_thread_id: :obj:`int` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -6740,7 +6750,6 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty if not process_handler: continue for i in inspect.signature(handler['function']).parameters: params.append(i) - result = None if len(params) == 1: result = handler['function'](message) elif "data" in params: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6d59b84ee..88b36f959 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1479,7 +1479,8 @@ def send_invoice( :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 protect_content: + :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 :return: """ method_url = r'sendInvoice' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f13d1a099..2e0af5131 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -209,8 +209,8 @@ async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=Non :param allowed_updates: Array of string. List the types of updates you want your bot to receive. :type allowed_updates: :obj:`list`, optional - :param long_polling_timeout: Timeout in seconds for long polling. - :type long_polling_timeout: :obj:`int`, optional + :param request_timeout: Timeout in seconds for request. + :type request_timeout: :obj:`int`, optional :return: An Array of Update objects is returned. :rtype: :obj:`list` of :class:`telebot.types.Update` @@ -511,7 +511,6 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up if not process_update: continue for i in signature(handler['function']).parameters: params.append(i) - result = None if len(params) == 1: result = await handler['function'](message) elif "data" in params: @@ -2072,6 +2071,8 @@ async def run_webhooks(self, :param drop_pending_updates: Pass True to drop all pending updates :param timeout: Integer. Request connection timeout :param secret_token: Secret token to be used to verify the webhook request. + :param secret_token_length: Length of a secret token, defaults to 20 + :param debug: Debug mode, defaults to False :return: """ @@ -2079,12 +2080,9 @@ async def run_webhooks(self, if not secret_token: secret_token = ''.join(random.choices(string.ascii_uppercase + string.digits, k=secret_token_length)) - if not url_path: url_path = self.token + '/' if url_path[-1] != '/': url_path += '/' - - protocol = "https" if certificate else "http" if not webhook_url: @@ -2093,8 +2091,6 @@ async def run_webhooks(self, if certificate and certificate_key: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(certificate, certificate_key) - else: - ssl_ctx = None # open certificate if it exists cert_file = open(certificate, 'rb') if certificate else None @@ -2769,6 +2765,8 @@ async def send_audio( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2934,6 +2932,8 @@ async def send_document( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3116,6 +3116,8 @@ async def send_video( :param has_spoiler: Pass True, if the video should be sent as a spoiler :type has_spoiler: :obj:`bool` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3220,6 +3222,8 @@ async def send_animation( :param has_spoiler: Pass True, if the animation should be sent as a spoiler :type has_spoiler: :obj:`bool` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3299,6 +3303,8 @@ async def send_video_note( :param message_thread_id: Identifier of a message thread, in which the video note will be sent :type message_thread_id: :obj:`int` + :param thumb: Deprecated. Use thumbnail instead + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -5609,7 +5615,7 @@ async def set_sticker_emoji_list(self, name: str, emoji_list: List[str]) -> bool async def create_new_sticker_set( self, user_id: int, name: str, title: str, - emojis: Optional[str]=None, + emojis: Optional[List[str]]=None, png_sticker: Union[Any, str]=None, tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0494f2741..49dc92deb 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -173,6 +173,7 @@ async def get_file_url(token, file_id): if FILE_URL is None: return "https://api.telegram.org/file/bot{0}/{1}".format(token, (await get_file(token, file_id))['file_path']) else: + # noinspection PyUnresolvedReferences return FILE_URL.format(token, (await get_file(token, file_id))['file_path']) @@ -180,6 +181,7 @@ async def download_file(token, file_path): if FILE_URL is None: url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path) else: + # noinspection PyUnresolvedReferences url = FILE_URL.format(token, file_path) session = await session_manager.get_session() async with session.get(url, proxy=proxy) as response: @@ -1433,8 +1435,8 @@ async def send_invoice( need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, protect_content=None, - message_thread_id=None): + timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, + protect_content=None, message_thread_id=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1466,7 +1468,8 @@ async def send_invoice( :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 protect_content: + :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 :return: """ method_url = r'sendInvoice' From e56c60ac00ebec7bf0d35a496191b1fdda8a9096 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 14:44:50 +0300 Subject: [PATCH 1307/1808] thumb deprecation typo and thumb->thumbnail param rename --- telebot/__init__.py | 8 ++--- telebot/apihelper.py | 62 ++++++++++++++++----------------- telebot/async_telebot.py | 8 ++--- telebot/asyncio_helper.py | 72 +++++++++++++++++++-------------------- 4 files changed, 75 insertions(+), 75 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 43a462c1d..dd1f8d2be 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1918,7 +1918,7 @@ def send_audio( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('thumb is deprecated, use thumbnail instead') + logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') return types.Message.de_json( apihelper.send_audio( @@ -2090,13 +2090,13 @@ def send_document( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('thumb is deprecated, use thumbnail instead') + logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') return types.Message.de_json( apihelper.send_data( self.token, chat_id, document, '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 = thumbnail, + disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, 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, protect_content = protect_content, message_thread_id = message_thread_id)) @@ -2276,7 +2276,7 @@ def send_video( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('thumb is deprecated, use thumbnail instead') + logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') return types.Message.de_json( apihelper.send_video( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 88b36f959..32e38bc59 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -668,8 +668,8 @@ def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=Non def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, - thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, + thumbnail=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None, has_spoiler=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} @@ -694,14 +694,14 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if width: payload['width'] = width if height: @@ -721,7 +721,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, + parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, width=None, height=None, message_thread_id=None, has_spoiler=None): method_url = r'sendAnimation' @@ -745,14 +745,14 @@ def send_animation( payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -806,7 +806,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None, + disable_notification=None, timeout=None, thumbnail=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} @@ -829,14 +829,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: @@ -847,7 +847,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} @@ -874,14 +874,14 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -894,7 +894,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, - disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, + disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content = None, message_thread_id=None, emoji=None): method_url = get_method_by_type(data_type) @@ -919,14 +919,14 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['timeout'] = timeout if caption: payload['caption'] = caption - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2e0af5131..cd952aefe 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2948,13 +2948,13 @@ async def send_document( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('thumb is deprecated, use thumbnail instead') + logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') return types.Message.de_json( await asyncio_helper.send_data( self.token, chat_id, document, '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 = thumbnail, + disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, 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, protect_content = protect_content, message_thread_id = message_thread_id)) @@ -3234,7 +3234,7 @@ async def send_animation( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('thumb is deprecated, use thumbnail instead') + logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') return types.Message.de_json( await asyncio_helper.send_animation( @@ -3314,7 +3314,7 @@ async def send_video_note( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('thumb is deprecated, use thumbnail instead') + logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') return types.Message.de_json( await asyncio_helper.send_video_note( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 49dc92deb..dc8e9b8df 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -662,9 +662,9 @@ async def send_chat_action(token, chat_id, action, timeout=None, message_thread_ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, - thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, - protect_content=None, message_thread_id=None, has_spoiler=None): + parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, + thumbnail=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, + protect_content=None, message_thread_id=None, has_spoiler=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -688,14 +688,14 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if width: payload['width'] = width if height: @@ -715,7 +715,7 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to async def send_animation( token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, - parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None, + parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, allow_sending_without_reply=None, width=None, height=None, protect_content=None, message_thread_id=None, has_spoiler=None): method_url = r'sendAnimation' @@ -739,14 +739,14 @@ async def send_animation( payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -800,8 +800,8 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_t async def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + disable_notification=None, timeout=None, thumbnail=None, allow_sending_without_reply=None, protect_content=None, + message_thread_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -823,14 +823,14 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if allow_sending_without_reply is not None: payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: @@ -841,8 +841,8 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, - reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, + caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -868,14 +868,14 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['disable_notification'] = disable_notification if timeout: payload['timeout'] = timeout - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: @@ -888,9 +888,9 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, - disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, - message_thread_id=None, emoji=None): + disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, + allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, + message_thread_id=None, emoji=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -913,14 +913,14 @@ async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, r payload['timeout'] = timeout if caption: payload['caption'] = caption - if thumb: - if not util.is_string(thumb): + if thumbnail: + if not util.is_string(thumbnail): if files: - files['thumbnail'] = thumb + files['thumbnail'] = thumbnail else: - files = {'thumbnail': thumb} + files = {'thumbnail': thumbnail} else: - payload['thumbnail'] = thumb + payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if allow_sending_without_reply is not None: From 6a9c25cf809ef069faa88e8bf81b8f1b851feca2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 14:56:31 +0300 Subject: [PATCH 1308/1808] Fix set_sticker_set_thumb and set_sticker_set_thumbnail --- telebot/__init__.py | 17 ++++++++--------- telebot/apihelper.py | 10 +++++----- telebot/async_telebot.py | 16 +++++++--------- telebot/asyncio_helper.py | 10 +++++----- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index dd1f8d2be..b0161bc26 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4547,8 +4547,7 @@ def answer_callback_query( """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - def set_sticker_set_thumbnail( - self, name: str, user_id: int, thumb: Union[Any, str]=None): + def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. @@ -4561,16 +4560,17 @@ def set_sticker_set_thumbnail( :param user_id: User identifier :type user_id: :obj:`int` - :param thumb: - :type thumb: :obj:`filelike object` + :param thumbnail: A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements for video sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. + :type thumbnail: :obj:`filelike object` :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) + + return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail) - def set_sticker_set_thumb( - self, name: str, user_id: int, thumb: Union[Any, str]=None): + @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") + def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. @@ -4590,8 +4590,7 @@ def set_sticker_set_thumb( :rtype: :obj:`bool` """ # deprecated - logger.warning('set_sticker_set_thumb is deprecated. Use set_sticker_set_thumbnail instead.') - return apihelper.set_sticker_set_thumb(self.token, name, user_id, thumb) + return self.set_sticker_set_thumbnail(name, user_id, thumb) def get_sticker_set(self, name: str) -> types.StickerSet: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 32e38bc59..b5eb0651c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -355,15 +355,15 @@ def get_chat_member_count(token, chat_id): return _make_request(token, method_url, params=payload) -def set_sticker_set_thumb(token, name, user_id, thumb): +def set_sticker_set_thumbnail(token, name, user_id, thumbnail): method_url = r'setStickerSetThumbnail' payload = {'name': name, 'user_id': user_id} files = {} - if thumb: - if not isinstance(thumb, str): - files['thumb'] = thumb + if thumbnail: + if not isinstance(thumbnail, str): + files['thumbnail'] = thumbnail else: - payload['thumb'] = thumb + payload['thumbnail'] = thumbnail return _make_request(token, method_url, params=payload, files=files or None) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index cd952aefe..247440cdb 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5407,8 +5407,7 @@ async def answer_callback_query( """ return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - async def set_sticker_set_thumbnail( - self, name: str, user_id: int, thumb: Union[Any, str]=None): + async def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. @@ -5421,16 +5420,16 @@ async def set_sticker_set_thumbnail( :param user_id: User identifier :type user_id: :obj:`int` - :param thumb: - :type thumb: :obj:`filelike object` + :param thumbnail: A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements for video sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. + :type thumbnail: :obj:`filelike object` :return: On success, True is returned. :rtype: :obj:`bool` """ - return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) + return await asyncio_helper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail) - async def set_sticker_set_thumb( - self, name: str, user_id: int, thumb: Union[Any, str]=None): + @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") + async def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. @@ -5450,8 +5449,7 @@ async def set_sticker_set_thumb( :rtype: :obj:`bool` """ # deprecated - logger.warning('set_sticker_set_thumb is deprecated, use set_sticker_set_thumbnail instead') - return await asyncio_helper.set_sticker_set_thumb(self.token, name, user_id, thumb) + return await self.set_sticker_set_thumbnail(name, user_id, thumb) async def get_sticker_set(self, name: str) -> types.StickerSet: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index dc8e9b8df..6f7ef0f84 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -343,15 +343,15 @@ async def get_chat_member_count(token, chat_id): return await _process_request(token, method_url, params=payload) -async def set_sticker_set_thumb(token, name, user_id, thumb): +async def set_sticker_set_thumbnail(token, name, user_id, thumbnail): method_url = r'setStickerSetThumbnail' payload = {'name': name, 'user_id': user_id} files = {} - if thumb: - if not isinstance(thumb, str): - files['thumb'] = thumb + if thumbnail: + if not isinstance(thumbnail, str): + files['thumbnail'] = thumbnail else: - payload['thumb'] = thumb + payload['thumbnail'] = thumbnail return await _process_request(token, method_url, params=payload, files=files or None) From b0740a920a4aad7ab1ea6ec8141fdeebba08eac4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 15:08:40 +0300 Subject: [PATCH 1309/1808] Set "thumb" as property in types --- telebot/types.py | 73 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 96d526e3c..68adeba2b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1578,8 +1578,12 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size - self.thumbnail: PhotoSize = thumbnail - self.thumb = thumbnail + self.thumbnail: PhotoSize = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail class Voice(JsonDeserializable): @@ -1670,7 +1674,11 @@ def __init__(self, file_id, file_unique_id, thumbnail=None, file_name=None, mime self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail class Video(JsonDeserializable): @@ -1730,7 +1738,11 @@ def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=N self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail class VideoNote(JsonDeserializable): @@ -1776,7 +1788,11 @@ def __init__(self, file_id, file_unique_id, length, duration, thumbnail=None, fi self.duration: int = duration self.thumbnail: PhotoSize = thumbnail self.file_size: int = file_size - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail class Contact(JsonDeserializable): @@ -5471,11 +5487,15 @@ def __init__(self, file_id, file_unique_id, width=None, height=None, duration=No self.height: int = height self.duration: int = duration self.thumbnail: PhotoSize = thumbnail - self.thumb: PhotoSize = thumbnail self.file_name: str = file_name self.mime_type: str = mime_type self.file_size: int = file_size + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail + class GameHighScore(JsonDeserializable): """ @@ -5895,7 +5915,11 @@ def __init__(self, name, title, sticker_type, is_animated, is_video, stickers, t self.is_video: bool = is_video self.stickers: List[Sticker] = stickers self.thumbnail: PhotoSize = thumbnail - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail @property def contains_masks(self): @@ -5997,8 +6021,11 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, self.premium_animation: File = premium_animation self.custom_emoji_id: int = custom_emoji_id self.needs_repainting: bool = needs_repainting - self.thumb = self.thumbnail - + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): @@ -6195,7 +6222,11 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption self.duration = duration self.supports_streaming = supports_streaming self.has_spoiler: Optional[bool] = has_spoiler - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail def to_dict(self): ret = super(InputMediaVideo, self).to_dict() @@ -6225,12 +6256,12 @@ class InputMediaAnimation(InputMedia): multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` - :param thumb: Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported + :param thumbnail: Optional. 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 . More information on Sending Files » - :type thumb: InputFile or :obj:`str` + :type thumbnail: InputFile or :obj:`str` :param caption: Optional. Caption of the animation to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -6267,7 +6298,11 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption self.height = height self.duration = duration self.has_spoiler: Optional[bool] = has_spoiler - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail def to_dict(self): ret = super(InputMediaAnimation, self).to_dict() @@ -6333,7 +6368,11 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption self.duration = duration self.performer = performer self.title = title - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail def to_dict(self): ret = super(InputMediaAudio, self).to_dict() @@ -6390,7 +6429,11 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption type="document", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumbnail = thumbnail self.disable_content_type_detection = disable_content_type_detection - self.thumb = thumbnail + + @property + def thumb(self): + logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + return self.thumbnail def to_dict(self): ret = super(InputMediaDocument, self).to_dict() From c9ef0d71f05cfb8d5eb3ea5d1d27b86c2317b39d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 15:17:29 +0300 Subject: [PATCH 1310/1808] Deprecation warnings equalisation --- telebot/__init__.py | 24 +++++++++++------------- telebot/async_telebot.py | 28 ++++++++++++---------------- telebot/types.py | 22 +++++++++++----------- 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b0161bc26..8aec5512e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1028,7 +1028,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :return: """ if none_stop is not None: - logger.warning("polling: none_stop parameter is deprecated. Use non_stop instead.") + logger.warning('The parameter "none_stop" is deprecated. Use "non_stop" instead.') non_stop = none_stop if skip_pending: @@ -1918,7 +1918,7 @@ def send_audio( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( apihelper.send_audio( @@ -2086,11 +2086,12 @@ def send_document( if data and not(document): # function typo miss compatibility + logger.warning('The parameter "data" is deprecated. Use "document" instead.') document = data if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( apihelper.send_data( @@ -2166,6 +2167,7 @@ def send_sticker( if data and not(sticker): # function typo miss compatibility + logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data return types.Message.de_json( @@ -2272,11 +2274,12 @@ def send_video( if data and not(video): # function typo miss compatibility + logger.warning('The parameter "data" is deprecated. Use "video" instead.') video = data if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( apihelper.send_video( @@ -3033,9 +3036,7 @@ def restrict_chat_member( can_invite_users=can_invite_users, can_pin_messages=can_pin_messages ) - logger.warning( - "Individual parameters are deprecated and will be removed, use 'permissions' instead." - ) + logger.warning('The parameters "can_..." are deprecated, use "permissions" instead.') return apihelper.restrict_chat_member( self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) @@ -3118,7 +3119,7 @@ def promote_chat_member( :rtype: :obj:`bool` """ if can_manage_voice_chats is not None: - logger.warning("promote_chat_member: can_manage_voice_chats parameter is deprecated. Use can_manage_video_chats instead.") + logger.warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') if can_manage_video_chats is None: can_manage_video_chats = can_manage_voice_chats @@ -4745,7 +4746,7 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s :rtype: :class:`telebot.types.File` """ if png_sticker: - logger.warning("png_sticker is deprecated, use sticker instead", DeprecationWarning) + logger.warning('The parameter "png_sticker" is deprecated. Use "sticker" instead.') sticker = png_sticker sticker_format = "static" @@ -4903,10 +4904,7 @@ def add_sticker_to_set( if sticker is None: old_sticker = png_sticker or tgs_sticker or webm_sticker if old_sticker is not None: - logger.warning( - 'The parameters "png_sticker", "tgs_sticker", "webm_sticker", "emojis" and "mask_position" are deprecated, ' - 'use "sticker" instead' - ) + logger.warning('The parameters "..._sticker", "emojis" and "mask_position" are deprecated, use "sticker" instead') if not old_sticker: raise ValueError('You must pass at least one sticker.') sticker = types.InputSticker(old_sticker, emojis, mask_position) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 247440cdb..04c4e6e28 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -294,7 +294,7 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= :return: """ if none_stop is not None: - logger.warning("polling: none_stop parameter is deprecated. Use non_stop instead.") + logger.warning('The parameter "none_stop" is deprecated. Use "non_stop" instead.') non_stop = none_stop if skip_pending: @@ -2944,11 +2944,12 @@ async def send_document( if data and not(document): # function typo miss compatibility + logger.warning('The parameter "data" is deprecated. Use "document" instead.') document = data if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( await asyncio_helper.send_data( @@ -3021,7 +3022,7 @@ async def send_sticker( if data and not(sticker): # function typo miss compatibility - logger.warning("send_sticker: data parameter is deprecated. Use sticker instead.") + logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data return types.Message.de_json( @@ -3128,11 +3129,11 @@ async def send_video( if data and not(video): # function typo miss compatibility - logger.warning("send_sticker: data parameter is deprecated. Use video instead.") + logger.warning('The parameter "data" is deprecated. Use "video" instead.') video = data if thumb and not(thumbnail): - logger.warning("send_sticker: thumb parameter is deprecated. Use thumbnail instead.") + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') thumbnail = thumb return types.Message.de_json( @@ -3234,7 +3235,7 @@ async def send_animation( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( await asyncio_helper.send_animation( @@ -3314,7 +3315,7 @@ async def send_video_note( if thumb is not None and thumbnail is None: thumbnail = thumb - logger.warning('"thumb" param is deprecated, use "thumbnail" param instead') + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( await asyncio_helper.send_video_note( @@ -3887,9 +3888,7 @@ async def restrict_chat_member( can_invite_users=can_invite_users, can_pin_messages=can_pin_messages ) - logger.warning( - "Individual parameters are deprecated and will be removed, use 'permissions' instead." - ) + logger.warning('The parameters "can_..." are deprecated, use "permissions" instead.') return await asyncio_helper.restrict_chat_member( self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) @@ -3973,7 +3972,7 @@ async def promote_chat_member( """ if can_manage_voice_chats is not None: - logger.warning("promote_chat_member: can_manage_voice_chats parameter is deprecated. Use can_manage_video_chats instead.") + logger.warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') if can_manage_video_chats is None: can_manage_video_chats = can_manage_voice_chats @@ -5539,7 +5538,7 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=N :rtype: :class:`telebot.types.File` """ if png_sticker: - logger.warning("png_sticker is deprecated, use sticker instead", DeprecationWarning) + logger.warning('The parameter "png_sticker" is deprecated. Use "sticker" instead.') sticker = png_sticker sticker_format = "static" @@ -5761,10 +5760,7 @@ async def add_sticker_to_set( if sticker is None: old_sticker = png_sticker or tgs_sticker or webm_sticker if old_sticker is not None: - logger.warning( - 'Parameters "png_sticker", "tgs_sticker", "webm_sticker", "emojis" and "mask_position" are deprecated, ' - 'use "sticker" instead' - ) + logger.warning('The parameters "..._sticker", "emojis" and "mask_position" are deprecated, use "sticker" instead') if not old_sticker: raise ValueError('You must pass at least one sticker.') sticker = types.InputSticker(old_sticker, emojis, mask_position) diff --git a/telebot/types.py b/telebot/types.py index 68adeba2b..4c68e652f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1582,7 +1582,7 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -1677,7 +1677,7 @@ def __init__(self, file_id, file_unique_id, thumbnail=None, file_name=None, mime @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -1741,7 +1741,7 @@ def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=N @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -1791,7 +1791,7 @@ def __init__(self, file_id, file_unique_id, length, duration, thumbnail=None, fi @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -5493,7 +5493,7 @@ def __init__(self, file_id, file_unique_id, width=None, height=None, duration=No @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -5918,7 +5918,7 @@ def __init__(self, name, title, sticker_type, is_animated, is_video, stickers, t @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @property @@ -6024,7 +6024,7 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6225,7 +6225,7 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): @@ -6301,7 +6301,7 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): @@ -6371,7 +6371,7 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): @@ -6432,7 +6432,7 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption @property def thumb(self): - logger.warning('The property "thumb" is deprecated, use "thumbnail" instead') + logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): From fe2e9a7a3022c8dc18a48a40950af8b16a0aec4c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 15:22:30 +0300 Subject: [PATCH 1311/1808] thumb_url etc. converted to properties --- telebot/types.py | 130 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 96 insertions(+), 34 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 4c68e652f..58fc07b31 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -4032,10 +4032,20 @@ def __init__(self, id, title, input_message_content, reply_markup=None, self.thumbnail_width = thumbnail_width self.thumbnail_height = thumbnail_height - # deprecateds - self.thumb_url = thumbnail_url - self.thumb_width = thumbnail_width - self.thumb_height = thumbnail_height + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url + + @property + def thumb_width(self): + logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + return self.thumbnail_width + + @property + def thumb_height(self): + logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + return self.thumbnail_height def to_dict(self): json_dict = super().to_dict() @@ -4047,9 +4057,9 @@ def to_dict(self): json_dict['description'] = self.description if self.thumbnail_url: json_dict['thumbnail_url'] = self.thumbnail_url - if self.thumb_width: + if self.thumbnail_width: json_dict['thumbnail_width'] = self.thumbnail_width - if self.thumb_height: + if self.thumbnail_height: json_dict['thumbnail_height'] = self.thumbnail_height return json_dict @@ -4115,8 +4125,10 @@ def __init__(self, id, photo_url, thumbnail_url, photo_width=None, photo_height= self.photo_height = photo_height self.description = description - # deprecateds - self.thumb_url = thumbnail_url + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url def to_dict(self): json_dict = super().to_dict() @@ -4198,9 +4210,15 @@ def __init__(self, id, gif_url, thumbnail_url, gif_width=None, gif_height=None, self.gif_duration = gif_duration self.thumbnail_mime_type = thumbnail_mime_type - # deprecateds - self.thumb_url = thumbnail_url - self.thumb_mime_type = thumbnail_mime_type + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url + + @property + def thumb_mime_type(self): + logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') + return self.thumbnail_mime_type def to_dict(self): json_dict = super().to_dict() @@ -4284,9 +4302,15 @@ def __init__(self, id, mpeg4_url, thumbnail_url, mpeg4_width=None, mpeg4_height= self.mpeg4_duration = mpeg4_duration self.thumbnail_mime_type = thumbnail_mime_type - # deprecateds - self.thumb_url = thumbnail_url - self.thumb_mime_type = thumbnail_mime_type + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url + + @property + def thumb_mime_type(self): + logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') + return self.thumbnail_mime_type def to_dict(self): json_dict = super().to_dict() @@ -4375,8 +4399,10 @@ def __init__(self, id, video_url, mime_type, thumbnail_url, self.video_duration = video_duration self.description = description - # deprecated - self.thumb_url = thumbnail_url + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url def to_dict(self): json_dict = super().to_dict() @@ -4579,11 +4605,20 @@ def __init__(self, id, title, document_url, mime_type, caption=None, caption_ent self.thumbnail_width = thumbnail_width self.thumbnail_height = thumbnail_height - # deprecated - self.thumb_url = thumbnail_url - self.thumb_width = thumbnail_width - self.thumb_height = thumbnail_height + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url + @property + def thumb_width(self): + logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + return self.thumbnail_width + + @property + def thumb_height(self): + logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + return self.thumbnail_height def to_dict(self): json_dict = super().to_dict() @@ -4668,10 +4703,20 @@ def __init__(self, id, title, latitude, longitude, horizontal_accuracy, live_per self.thumbnail_width = thumbnail_width self.thumbnail_height = thumbnail_height - # deprecated - self.thumb_url = thumbnail_url - self.thumb_width = thumbnail_width - self.thumb_height = thumbnail_height + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url + + @property + def thumb_width(self): + logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + return self.thumbnail_width + + @property + def thumb_height(self): + logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + return self.thumbnail_height def to_dict(self): json_dict = super().to_dict() @@ -4765,11 +4810,20 @@ def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, self.thumbnail_width = thumbnail_width self.thumbnail_height = thumbnail_height - # deprecated - self.thumb_url = thumbnail_url - self.thumb_width = thumbnail_width - self.thumb_height = thumbnail_height - + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url + + @property + def thumb_width(self): + logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + return self.thumbnail_width + + @property + def thumb_height(self): + logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + return self.thumbnail_height def to_dict(self): json_dict = super().to_dict() @@ -4848,12 +4902,20 @@ def __init__(self, id, phone_number, first_name, last_name=None, vcard=None, self.thumbnail_width = thumbnail_width self.thumbnail_height = thumbnail_height - # deprecated - self.thumb_url = thumbnail_url - self.thumb_width = thumbnail_width - self.thumb_height = thumbnail_height + @property + def thumb_url(self): + logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + return self.thumbnail_url + + @property + def thumb_width(self): + logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + return self.thumbnail_width - + @property + def thumb_height(self): + logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + return self.thumbnail_height def to_dict(self): json_dict = super().to_dict() From 14434b398e59b425731421248c2ef2a74f398f60 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:34:08 +0300 Subject: [PATCH 1312/1808] send_animation thumbnail hint Co-authored-by: _run --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8aec5512e..03910e446 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2369,7 +2369,7 @@ def send_animation( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From b0e64d828c7386583cdd0f900d176ba89e601244 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:36:10 +0300 Subject: [PATCH 1313/1808] send_video thumbnail hint Co-authored-by: _run --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 04c4e6e28..0dcb9e403 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2766,7 +2766,7 @@ async def send_audio( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From f3486b3730524be5aa6e63dca50c1ba5e4d840b7 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:36:36 +0300 Subject: [PATCH 1314/1808] send_document thumbnail Co-authored-by: _run --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0dcb9e403..fa97afa65 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2933,7 +2933,7 @@ async def send_document( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From 018b89cdc077d421668925f68c4d6b39ebbf750a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:37:03 +0300 Subject: [PATCH 1315/1808] send_document thumbnail Co-authored-by: _run --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fa97afa65..bc3f5d620 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3118,7 +3118,7 @@ async def send_video( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From 46100edd97ee5331cdf201b5464bf2fd4ed70132 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:37:22 +0300 Subject: [PATCH 1316/1808] send_video_note thumbnail hint Co-authored-by: _run --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index bc3f5d620..bf8581330 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3305,7 +3305,7 @@ async def send_video_note( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From 6b5c263ee8529536f6f0fba02fcf7cb81987c2b5 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:37:46 +0300 Subject: [PATCH 1317/1808] send_animtion thumbnail hint Co-authored-by: _run --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index bf8581330..64807adf3 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3224,7 +3224,7 @@ async def send_animation( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From b1c172c4211d6ca0774f48df939ff8d6ba87d2f9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:38:05 +0300 Subject: [PATCH 1318/1808] send_document thumbnail hint Co-authored-by: _run --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 03910e446..75b410af2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2075,7 +2075,7 @@ def send_document( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From 39360e0640999730796375db45089df12a67175b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Mar 2023 16:38:16 +0300 Subject: [PATCH 1319/1808] send_video thumbnail hint Co-authored-by: _run --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 75b410af2..27f4ca61c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2263,7 +2263,7 @@ def send_video( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead - +:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From 2e589ab6e15df530a97e0a74600f1a32229f8abd Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 26 Mar 2023 18:00:35 +0300 Subject: [PATCH 1320/1808] Thumb type spec --- telebot/__init__.py | 12 ++++++++---- telebot/async_telebot.py | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 27f4ca61c..6c953e1bf 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1892,7 +1892,7 @@ def send_audio( 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 - :type thumbnail: :obj:`str` + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -1907,6 +1907,7 @@ def send_audio( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2075,7 +2076,8 @@ def send_document( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2263,7 +2265,8 @@ def send_video( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2369,7 +2372,8 @@ def send_animation( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 64807adf3..ebf5e80d6 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2058,8 +2058,7 @@ async def run_webhooks(self, """ This class sets webhooks and listens to a given url and port. - :param listen: IP address to listen to. Defaults to - 0.0.0.0 + :param listen: IP address to listen to. Defaults to 0.0.0.0 :param port: A port which will be used to listen to webhooks. :param url_path: Path to the webhook. Defaults to /token :param certificate: Path to the certificate file. @@ -2751,7 +2750,7 @@ async def send_audio( 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 - :type thumbnail: :obj:`str` + :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -2766,7 +2765,8 @@ async def send_audio( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2933,7 +2933,8 @@ async def send_document( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3118,7 +3119,8 @@ async def send_video( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3224,7 +3226,8 @@ async def send_animation( :type has_spoiler: :obj:`bool` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3305,7 +3308,8 @@ async def send_video_note( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead -:type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From 3f07dc4ce809004dc23ee44faba488fec128ea27 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 26 Mar 2023 20:04:29 +0300 Subject: [PATCH 1321/1808] thumb Co-authored-by: _run --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c953e1bf..c385a295f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2454,7 +2454,7 @@ def send_video_note( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead - + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ From 351d021e01351394df6b4754becdd5218466aaee Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 3 Apr 2023 01:00:29 +0500 Subject: [PATCH 1322/1808] Update redis_storage.py --- telebot/storage/redis_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index a10494811..453d6ae33 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -174,7 +174,7 @@ def save(self, chat_id, user_id, data): user_id = str(user_id) if response: if user_id in response: - response[user_id]['data'] = dict(data, **response[user_id]['data']) + response[user_id]['data'] = data self.set_record(chat_id, response) return True From 7bf87a306a097f903b7785fe0281009ba9e2aaa4 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 3 Apr 2023 01:00:48 +0500 Subject: [PATCH 1323/1808] Update redis_storage.py --- telebot/asyncio_storage/redis_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 92842abb1..f9c3aeac3 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -170,6 +170,6 @@ async def save(self, chat_id, user_id, data): user_id = str(user_id) if response: if user_id in response: - response[user_id]['data'] = dict(data, **response[user_id]['data']) + response[user_id]['data'] = data await self.set_record(chat_id, response) return True From 8796168efbcc36add1de66593825bdd981dce832 Mon Sep 17 00:00:00 2001 From: Francisco Griman <103836660+fcoagz@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:27:15 -0400 Subject: [PATCH 1324/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a6ca03667..0b37e4acf 100644 --- a/README.md +++ b/README.md @@ -829,6 +829,7 @@ Here are some examples of template: ## Bots using this library +* [DownloadMusicBOT](https://github.com/fcoagz/DownloadMusicBOT) by *Francisco Griman* - It is a simple bot that downloads audio from YouTube videos on Telegram. * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. From c5689f383bec1078bdac40f42590c77ade3170d4 Mon Sep 17 00:00:00 2001 From: Francisco Griman <103836660+fcoagz@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:49:21 -0400 Subject: [PATCH 1325/1808] edit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b37e4acf..0f98be845 100644 --- a/README.md +++ b/README.md @@ -829,7 +829,6 @@ Here are some examples of template: ## Bots using this library -* [DownloadMusicBOT](https://github.com/fcoagz/DownloadMusicBOT) by *Francisco Griman* - It is a simple bot that downloads audio from YouTube videos on Telegram. * [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes * [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger* * [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you. @@ -891,5 +890,6 @@ Here are some examples of template: * [Media Rating Bot](https://t.me/mediaratingbot) ([source](https://github.com/CommanderCRM/MediaRatingBot))by [CommanderCRM](https://github.com/CommanderCRM). This bot aggregates media (movies, TV series, etc.) ratings from IMDb, Rotten Tomatoes, Metacritic, TheMovieDB, FilmAffinity and also provides number of votes of said media on IMDb. * [Spot Seek Bot](https://t.me/SpotSeekBot) ([source](https://github.com/arashnm80/spot-seek-bot)) by [Arashnm80](https://github.com/arashnm80). This is a free & open source telegram bot for downloading tracks, albums or playlists from spotify. * [CalendarIT Bot](https://t.me/calendarit_bot) ([source](https://github.com/codebyzen/CalendarIT_Telegram_Bot))by [CodeByZen](https://github.com/codebyzen). A simple, but extensible Python Telegram bot, can post acquainted with what is happening today, tomorrow or what happened 20 years ago to channel. +* [DownloadMusicBOT](https://github.com/fcoagz/DownloadMusicBOT) by *Francisco Griman* - It is a simple bot that downloads audio from YouTube videos on Telegram. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From d466da35428d297eb86d95694ff3c7a671482493 Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 14 Apr 2023 22:00:42 +0300 Subject: [PATCH 1326/1808] Add state_list to StatesGroup --- telebot/asyncio_handler_backends.py | 5 +++++ telebot/handler_backends.py | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 96e13ee53..99a7b1797 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -81,6 +81,11 @@ def __init_subclass__(cls) -> None: value.name = ':'.join((cls.__name__, name)) value.group = cls + @property + def state_list(self): + return [value for name, value in self.__dict__.items() + if not name.startswith('__') and not callable(value) and isinstance(value, State)] + class SkipHandler: """ diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index be2714c5c..fda699785 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -191,7 +191,12 @@ def __init_subclass__(cls) -> None: value.name = ':'.join((cls.__name__, name)) value.group = cls - + @property + def state_list(self): + return [value for name, value in self.__dict__.items() + if not name.startswith('__') and not callable(value) and isinstance(value, State)] + + class BaseMiddleware: """ Base class for middleware. From e4bddd91cb3a3ef849960977e63c0da33ab8539c Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 14 Apr 2023 22:11:08 +0300 Subject: [PATCH 1327/1808] Define state_list in __init_subclass__ --- telebot/asyncio_handler_backends.py | 7 ++++--- telebot/handler_backends.py | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 99a7b1797..a565023fc 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -74,17 +74,18 @@ class MyStates(StatesGroup): my_state = State() # returns my_state:State string. """ def __init_subclass__(cls) -> None: - + state_list = [] for name, value in cls.__dict__.items(): if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable value.name = ':'.join((cls.__name__, name)) value.group = cls + state_list.append(value) + cls._state_list = state_list @property def state_list(self): - return [value for name, value in self.__dict__.items() - if not name.startswith('__') and not callable(value) and isinstance(value, State)] + return self._state_list class SkipHandler: diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index fda699785..2e4f86eab 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -185,16 +185,18 @@ class MyStates(StatesGroup): my_state = State() # returns my_state:State string. """ def __init_subclass__(cls) -> None: + state_list = [] for name, value in cls.__dict__.items(): if not name.startswith('__') and not callable(value) and isinstance(value, State): # change value of that variable value.name = ':'.join((cls.__name__, name)) value.group = cls + state_list.append(value) + cls._state_list = state_list @property def state_list(self): - return [value for name, value in self.__dict__.items() - if not name.startswith('__') and not callable(value) and isinstance(value, State)] + return self._state_list class BaseMiddleware: From 1b2ed0e2f7c11d4447a13cf1091be09e8b8cd5ee Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 15 Apr 2023 22:39:14 +0300 Subject: [PATCH 1328/1808] Bump version to 4.11.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index eafc78618..62ffd29df 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.10.0' +release = '4.11.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index ad97ea2e5..6624f64e3 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.10.0' +__version__ = '4.11.0' From a7cafd1f2448aa1c1761ae44454cb09529f68d3b Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 21 Apr 2023 21:27:08 +0400 Subject: [PATCH 1329/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f98be845..aa8b32ec3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.6! +##

Supported Bot API version: 6.7!

Official documentation

Official ru documentation

From d1417e561676bbef7ebe536747a669e4aacc93c6 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 21 Apr 2023 22:14:16 +0400 Subject: [PATCH 1330/1808] Added the field web_app_name to the class WriteAccessAllowed. --- telebot/types.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 22d46c937..fed442c03 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7396,13 +7396,20 @@ class WriteAccessAllowed(JsonDeserializable): Currently holds no information. Telegram documentation: https://core.telegram.org/bots/api#writeaccessallowed + + :param web_app_name: Optional. Name of the Web App which was launched from a link + :type web_app_name: :obj:`str` """ @classmethod def de_json(cls, json_string): - return cls() + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + - def __init__(self) -> None: - pass + def __init__(self, web_app_name: str) -> None: + self.web_app_name: str = web_app_name + class UserShared(JsonDeserializable): From 966b45186906805a012a58c32600e077629b4589 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 21 Apr 2023 22:28:20 +0400 Subject: [PATCH 1331/1808] Added the field switch_inline_query_chosen_chat of the type SwitchInlineQueryChosenChat to the class InlineKeyboardButton, which allows bots to switch to inline mode in a chosen chat of the given type. --- telebot/types.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index fed442c03..f23d7fc5b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2592,6 +2592,10 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable) something from multiple options. :type switch_inline_query_current_chat: :obj:`str` + :param switch_inline_query_chosen_chat: Optional. If set, pressing the button will prompt the user to select one of their chats of the + specified type, open that chat and insert the bot's username and the specified inline query in the input field + :type switch_inline_query_chosen_chat: :class:`telebot.types.SwitchInlineQueryChosenChat` + :param callback_game: Optional. Description of the game that will be launched when the user presses the button. NOTE: This type of button must always be the first button in the first row. :type callback_game: :class:`telebot.types.CallbackGame` @@ -2611,17 +2615,20 @@ def de_json(cls, json_string): obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) if 'web_app' in obj: obj['web_app'] = WebAppInfo.de_json(obj.get('web_app')) + if 'switch_inline_query_chosen_chat' in obj: + obj['switch_inline_query_chosen_chat'] = SwitchInlineQueryChosenChat.de_json(obj.get('switch_inline_query_chosen_chat')) return cls(**obj) def __init__(self, text, url=None, callback_data=None, web_app=None, switch_inline_query=None, - switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): + switch_inline_query_current_chat=None, switch_inline_query_chosen_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): self.text: str = text self.url: str = url self.callback_data: str = callback_data self.web_app: WebAppInfo = web_app self.switch_inline_query: str = switch_inline_query self.switch_inline_query_current_chat: str = switch_inline_query_current_chat + self.switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat = switch_inline_query_chosen_chat self.callback_game = callback_game # Not Implemented self.pay: bool = pay self.login_url: LoginUrl = login_url @@ -2647,6 +2654,8 @@ def to_dict(self): json_dict['pay'] = self.pay if self.login_url is not None: json_dict['login_url'] = self.login_url.to_dict() + if self.switch_inline_query_chosen_chat is not None: + json_dict['switch_inline_query_chosen_chat'] = self.switch_inline_query_chosen_chat.to_dict() return json_dict @@ -7583,4 +7592,64 @@ def convert_input_sticker(self): return self.to_json(), {self._sticker_name: self.sticker} - \ No newline at end of file + +class SwitchInlineQueryChosenChat(JsonDeserializable, Dictionaryable, JsonSerializable): + """ + Represents an inline button that switches the current user to inline mode in a chosen chat, + with an optional default inline query. + + Telegram Documentation: https://core.telegram.org/bots/api#inlinekeyboardbutton + + :param query: Optional. The default inline query to be inserted in the input field. + If left empty, only the bot's username will be inserted + :type query: :obj:`str` + + :param allow_user_chats: Optional. True, if private chats with users can be chosen + :type allow_user_chats: :obj:`bool` + + :param allow_bot_chats: Optional. True, if private chats with bots can be chosen + :type allow_bot_chats: :obj:`bool` + + :param allow_group_chats: Optional. True, if group and supergroup chats can be chosen + :type allow_group_chats: :obj:`bool` + + :param allow_channel_chats: Optional. True, if channel chats can be chosen + :type allow_channel_chats: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`SwitchInlineQueryChosenChat` + """ + + @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, query=None, allow_user_chats=None, allow_bot_chats=None, allow_group_chats=None, + allow_channel_chats=None): + self.query: str = query + self.allow_user_chats: bool = allow_user_chats + self.allow_bot_chats: bool = allow_bot_chats + self.allow_group_chats: bool = allow_group_chats + self.allow_channel_chats: bool = allow_channel_chats + + def to_dict(self): + json_dict = {} + + if self.query is not None: + json_dict['query'] = self.query + if self.allow_user_chats is not None: + json_dict['allow_user_chats'] = self.allow_user_chats + if self.allow_bot_chats is not None: + json_dict['allow_bot_chats'] = self.allow_bot_chats + if self.allow_group_chats is not None: + json_dict['allow_group_chats'] = self.allow_group_chats + if self.allow_channel_chats is not None: + json_dict['allow_channel_chats'] = self.allow_channel_chats + + return json_dict + + def to_json(self): + return json.dumps(self.to_dict()) From d6f4987197d9450d10a4c823f72cb67bbd832cc8 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 21 Apr 2023 22:30:32 +0400 Subject: [PATCH 1332/1808] Added the field via_chat_folder_invite_link to the class ChatMemberUpdated. --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index f23d7fc5b..53b692d65 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -237,6 +237,9 @@ class ChatMemberUpdated(JsonDeserializable): link events only. :type invite_link: :class:`telebot.types.ChatInviteLink` + :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` + :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberUpdated` """ @@ -251,13 +254,15 @@ 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, **kwargs): + def __init__(self, chat, from_user, date, old_chat_member, new_chat_member, invite_link=None, via_chat_folder_invite_link=None, + **kwargs): self.chat: Chat = chat self.from_user: User = from_user self.date: int = date 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_chat_folder_invite_link: Optional[bool] = via_chat_folder_invite_link @property def difference(self) -> Dict[str, List]: From 77e19286280c312be6802583d66d03e14d3ee35c Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 22 Apr 2023 18:25:24 +0400 Subject: [PATCH 1333/1808] Added the ability to set different bot names for different user languages using the method setMyName. --- telebot/__init__.py | 26 ++++++++++++++++++++++++++ telebot/apihelper.py | 9 +++++++++ telebot/async_telebot.py | 18 ++++++++++++++++++ telebot/asyncio_helper.py | 10 ++++++++++ 4 files changed, 63 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index c385a295f..549a69de9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3443,6 +3443,24 @@ def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, """ result = apihelper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + + def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=None): + """ + Use this method to change the bot's name. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmyname + + :param name: Optional. New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language. + :type name: :obj:`str` + + :param language_code: Optional. A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose + language there is no dedicated name. + :type language_code: :obj:`str` + + :return: True on success. + """ + + return apihelper.set_my_name(self.token, name, language_code) def set_my_description(self, description: Optional[str]=None, language_code: Optional[str]=None): """ @@ -3450,6 +3468,8 @@ def set_my_description(self, description: Optional[str]=None, language_code: Opt the chat with the bot if the chat is empty. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setmydescription + :param description: New bot description; 0-512 characters. Pass an empty string to remove the dedicated description for the given language. :type description: :obj:`str` @@ -3467,6 +3487,8 @@ def get_my_description(self, language_code: Optional[str]=None): Use this method to get the current bot description for the given user language. Returns BotDescription on success. + Telegram documentation: https://core.telegram.org/bots/api#getmydescription + :param language_code: A two-letter ISO 639-1 language code or an empty string :type language_code: :obj:`str` @@ -3481,6 +3503,8 @@ def set_my_short_description(self, short_description:Optional[str]=None, languag is sent together with the link when users share the bot. Returns True on success. + Telegram documentation: https://core.telegram.org/bots/api#setmyshortdescription + :param short_description: New short description for the bot; 0-120 characters. Pass an empty string to remove the dedicated short description for the given language. :type short_description: :obj:`str` @@ -3498,6 +3522,8 @@ def get_my_short_description(self, language_code: Optional[str]=None): Use this method to get the current bot short description for the given user language. Returns BotShortDescription on success. + Telegram documentation: https://core.telegram.org/bots/api#getmyshortdescription + :param language_code: A two-letter ISO 639-1 language code or an empty string :type language_code: :obj:`str` diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b5eb0651c..8d2b1e72e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1196,6 +1196,15 @@ def get_my_commands(token, scope=None, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload) +def set_my_name(token, name=None, language_code=None): + method_url = r'setMyName' + payload = {} + if name is not None: + payload['name'] = name + if language_code is not None: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload, method='post') + def set_chat_menu_button(token, chat_id=None, menu_button=None): method_url = r'setChatMenuButton' payload = {} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ebf5e80d6..242e74c4b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4361,6 +4361,24 @@ async def get_my_commands(self, scope: Optional[types.BotCommandScope], result = await asyncio_helper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + async def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=None): + """ + Use this method to change the bot's name. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmyname + + :param name: Optional. New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language. + :type name: :obj:`str` + + :param language_code: Optional. A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose + language there is no dedicated name. + :type language_code: :obj:`str` + + :return: True on success. + """ + + return await asyncio_helper.set_my_name(self.token, name, language_code) + async def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 6f7ef0f84..804e51654 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1183,6 +1183,16 @@ async def get_my_commands(token, scope=None, language_code=None): payload['language_code'] = language_code return await _process_request(token, method_url, params=payload) + +async def set_my_name(token, name=None, language_code=None): + method_url = r'setMyName' + payload = {} + if name is not None: + payload['name'] = name + if language_code is not None: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload, method='post') + async def set_chat_menu_button(token, chat_id=None, menu_button=None): method_url = r'setChatMenuButton' payload = {} From 1d62adc26261da555c2c2b6e7cf9b588c4bec3d2 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 22 Apr 2023 18:30:46 +0400 Subject: [PATCH 1334/1808] Added the ability to get the current bot name in the given language as the class BotName using the method getMyName. --- telebot/__init__.py | 16 ++++++++++++++++ telebot/apihelper.py | 7 +++++++ telebot/async_telebot.py | 16 ++++++++++++++++ telebot/asyncio_helper.py | 7 +++++++ telebot/types.py | 24 ++++++++++++++++++++++++ 5 files changed, 70 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 549a69de9..c1aaec077 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3462,6 +3462,22 @@ def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=Non return apihelper.set_my_name(self.token, name, language_code) + def get_my_name(self, language_code: Optional[str]=None): + """ + Use this method to get the current bot name for the given user language. + Returns BotName on success. + + Telegram documentation: https://core.telegram.org/bots/api#getmyname + + :param language_code: Optional. A two-letter ISO 639-1 language code or an empty string + :type language_code: :obj:`str` + + :return: :class:`telebot.types.BotName` + """ + + result = apihelper.get_my_name(self.token, language_code) + return types.BotName.de_json(result) + def set_my_description(self, description: Optional[str]=None, language_code: Optional[str]=None): """ Use this method to change the bot's description, which is shown in diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8d2b1e72e..57102f900 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1205,6 +1205,13 @@ def set_my_name(token, name=None, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') +def get_my_name(token, language_code=None): + method_url = r'getMyName' + payload = {} + if language_code is not None: + payload['language_code'] = language_code + return _make_request(token, method_url, params=payload) + def set_chat_menu_button(token, chat_id=None, menu_button=None): method_url = r'setChatMenuButton' payload = {} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 242e74c4b..a9d2ac872 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4379,6 +4379,22 @@ async def set_my_name(self, name: Optional[str]=None, language_code: Optional[st return await asyncio_helper.set_my_name(self.token, name, language_code) + async def get_my_name(self, language_code: Optional[str]=None): + """ + Use this method to get the current bot name for the given user language. + Returns BotName on success. + + Telegram documentation: https://core.telegram.org/bots/api#getmyname + + :param language_code: Optional. A two-letter ISO 639-1 language code or an empty string + :type language_code: :obj:`str` + + :return: :class:`telebot.types.BotName` + """ + + result = await asyncio_helper.get_my_name(self.token, language_code) + return types.BotName.de_json(result) + async def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 804e51654..507aec020 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1193,6 +1193,13 @@ async def set_my_name(token, name=None, language_code=None): payload['language_code'] = language_code return await _process_request(token, method_url, params=payload, method='post') +async def get_my_name(token, language_code=None): + method_url = r'getMyName' + payload = {} + if language_code is not None: + payload['language_code'] = language_code + return await _process_request(token, method_url, params=payload) + async def set_chat_menu_button(token, chat_id=None, menu_button=None): method_url = r'setChatMenuButton' payload = {} diff --git a/telebot/types.py b/telebot/types.py index 53b692d65..8a4460ee3 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7658,3 +7658,27 @@ def to_dict(self): def to_json(self): return json.dumps(self.to_dict()) + + +class BotName(JsonDeserializable): + """ + This object represents a bot name. + + Telegram Documentation: https://core.telegram.org/bots/api#botname + + :param name: The bot name + :type name: :obj:`str` + + :return: Instance of the class + :rtype: :class:`BotName` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + return cls(**obj) + + def __init__(self, name: str): + self.name: str = name \ No newline at end of file From be69feb25284f0d7dba3d7e6a8d96c55e72ae0de Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 22 Apr 2023 20:38:05 +0400 Subject: [PATCH 1335/1808] * Added a test for message entity __html_text function. #1971 should be fixed and then todo can be done. --- tests/test_types.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_types.py b/tests/test_types.py index c696f162c..8c15396c9 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -271,5 +271,30 @@ def test_sent_web_app_message(): assert sent_web_app_message.inline_message_id == '29430' +def test_message_entity(): + # TODO: Add support for nesting entities + + + #sample_string_1 = r'{"update_id":934522126,"message":{"message_id":1374510,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682177590,"text":"b b b","entities":[{"offset":0,"length":2,"type":"bold"},{"offset":0,"length":1,"type":"italic"},{"offset":2,"length":2,"type":"bold"},{"offset":2,"length":1,"type":"italic"},{"offset":4,"length":1,"type":"bold"},{"offset":4,"length":1,"type":"italic"}]}}' + #update = types.Update.de_json(sample_string_1) + #message: types.Message = update.message + #assert message.html_text == "b b b" + + sample_string_2 = r'{"update_id":934522166,"message":{"message_id":1374526,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179716,"text":"b b b","entities":[{"offset":0,"length":1,"type":"bold"},{"offset":2,"length":1,"type":"bold"},{"offset":4,"length":1,"type":"italic"}]}}' + message_2 = types.Update.de_json(sample_string_2).message + assert message_2.html_text == "b b b" + + + + #sample_string_3 = r'{"update_id":934522172,"message":{"message_id":1374530,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179968,"text":"This is a bold text with a nested italic and bold text.","entities":[{"offset":10,"length":4,"type":"bold"},{"offset":27,"length":7,"type":"italic"},{"offset":34,"length":15,"type":"bold"},{"offset":34,"length":15,"type":"italic"}]}}' + #message_3 = types.Update.de_json(sample_string_3).message + #assert message_3.html_text == "This is a bold text with a nested italic and bold text." + + + assert True + + + + From 26575dc5e7f25f026f03371243babca7022ca5cb Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 22 Apr 2023 20:51:08 +0400 Subject: [PATCH 1336/1808] Added support for launching Web Apps from inline query results by replacing the parameters switch_pm_text and switch_pm_parameter of the method answerInlineQuery with the parameter button of type InlineQueryResultsButton. --- telebot/__init__.py | 12 +++++++-- telebot/apihelper.py | 8 +++--- telebot/async_telebot.py | 12 +++++++-- telebot/asyncio_helper.py | 10 ++++---- telebot/types.py | 51 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 78 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index c1aaec077..0579325c3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4521,7 +4521,8 @@ def answer_inline_query( is_personal: Optional[bool]=None, next_offset: Optional[str]=None, switch_pm_text: Optional[str]=None, - switch_pm_parameter: Optional[str]=None) -> bool: + switch_pm_parameter: Optional[str]=None, + button: Optional[types.InlineQueryResultsButton]=None) -> bool: """ Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. @@ -4557,11 +4558,18 @@ def answer_inline_query( :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button :type switch_pm_text: :obj:`str` + :param button: A JSON-serialized object describing a button to be shown above inline query results + :type button: :obj:`types.InlineQueryResultsButton` + :return: On success, True is returned. :rtype: :obj:`bool` """ + if not button and (switch_pm_text or switch_pm_parameter): + logger.warning("switch_pm_text and switch_pm_parameter are deprecated for answer_inline_query. Use button instead.") + button = types.InlineQueryResultsButton(text=switch_pm_text, start_parameter=switch_pm_parameter) + return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, - switch_pm_text, switch_pm_parameter) + button) def answer_callback_query( self, callback_query_id: int, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 57102f900..a07f1ef91 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1614,7 +1614,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None, def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, - switch_pm_text=None, switch_pm_parameter=None): + button=None): method_url = 'answerInlineQuery' payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)} if cache_time is not None: @@ -1623,10 +1623,8 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per payload['is_personal'] = is_personal if next_offset is not None: payload['next_offset'] = next_offset - if switch_pm_text: - payload['switch_pm_text'] = switch_pm_text - if switch_pm_parameter: - payload['switch_pm_parameter'] = switch_pm_parameter + if button is not None: + payload["button"] = button.to_json() return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a9d2ac872..1a86024d5 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5371,7 +5371,8 @@ async def answer_inline_query( is_personal: Optional[bool]=None, next_offset: Optional[str]=None, switch_pm_text: Optional[str]=None, - switch_pm_parameter: Optional[str]=None) -> bool: + switch_pm_parameter: Optional[str]=None, + button: Optional[types.InlineQueryResultsButton]=None) -> bool: """ Use this method to send answers to an inline query. On success, True is returned. No more than 50 results per query are allowed. @@ -5407,11 +5408,18 @@ async def answer_inline_query( :param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button :type switch_pm_text: :obj:`str` + :param button: A JSON-serialized object describing a button to be shown above inline query results + :type button: :obj:`types.InlineQueryResultsButton` + :return: On success, True is returned. :rtype: :obj:`bool` """ + + if not button and (switch_pm_text or switch_pm_parameter): + logger.warning("switch_pm_text and switch_pm_parameter are deprecated for answer_inline_query. Use button instead.") + button = types.InlineQueryResultsButton(text=switch_pm_text, start_parameter=switch_pm_parameter) return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, - switch_pm_text, switch_pm_parameter) + button) async def answer_callback_query( self, callback_query_id: int, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 507aec020..5a615b397 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1604,7 +1604,7 @@ async def answer_callback_query(token, callback_query_id, text=None, show_alert= async def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, - switch_pm_text=None, switch_pm_parameter=None): + button=None): method_url = 'answerInlineQuery' payload = {'inline_query_id': inline_query_id, 'results': await _convert_list_json_serializable(results)} if cache_time is not None: @@ -1613,10 +1613,10 @@ async def answer_inline_query(token, inline_query_id, results, cache_time=None, payload['is_personal'] = is_personal if next_offset is not None: payload['next_offset'] = next_offset - if switch_pm_text: - payload['switch_pm_text'] = switch_pm_text - if switch_pm_parameter: - payload['switch_pm_parameter'] = switch_pm_parameter + if button is not None: + payload["button"] = button.to_json() + + return await _process_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 8a4460ee3..9f5a50747 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7681,4 +7681,53 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, name: str): - self.name: str = name \ No newline at end of file + self.name: str = name + + +class InlineQueryResultsButton(JsonSerializable, Dictionaryable): + """ + This object represents a button to be shown above inline query results. + You must use exactly one of the optional fields. + + Telegram documentation: https://core.telegram.org/bots/api#inlinequeryresultsbutton + + :param text: Label text on the button + :type text: :obj:`str` + + :param web_app: Optional. Description of the Web App that will be launched when the user presses the button. + The Web App will be able to switch back to the inline mode using the method web_app_switch_inline_query inside the Web App. + :type web_app: :class:`telebot.types.WebAppInfo` + + :param start_parameter: Optional. Deep-linking parameter for the /start message sent to the bot when a user presses the button. + 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed. + Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search + results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing + any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs + the bot to return an OAuth link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat + where they wanted to use the bot's inline capabilities. + :type start_parameter: :obj:`str` + + :return: Instance of the class + :rtype: :class:`InlineQueryResultsButton` + """ + + def __init__(self, text: str, web_app: Optional[WebAppInfo]=None, start_parameter: Optional[str]=None) -> None: + self.text: str = text + self.web_app: Optional[WebAppInfo] = web_app + self.start_parameter: Optional[str] = start_parameter + + + def to_dict(self) -> dict: + json_dict = { + 'text': self.text + } + + if self.web_app is not None: + json_dict['web_app'] = self.web_app.to_dict() + if self.start_parameter is not None: + json_dict['start_parameter'] = self.start_parameter + + return json_dict + + def to_json(self) -> str: + return json.dumps(self.to_dict()) \ No newline at end of file From ecb5d9b4f68c315f53843b5f3900a314245b799a Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 22 Apr 2023 22:53:57 +0400 Subject: [PATCH 1337/1808] Added tests for __html_text, fixed the bug, added custom_emoji for entities --- telebot/types.py | 32 ++++++++++++++++++++++---------- tests/test_types.py | 19 +++++++++++-------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 9f5a50747..b793073fd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1313,6 +1313,7 @@ def __html_text(self, text, entities): "strikethrough": "{text}", "underline": "{text}", "spoiler": "{text}", + "custom_emoji": "{text}" } if hasattr(self, "custom_subs"): @@ -1321,7 +1322,7 @@ def __html_text(self, text, entities): utf16_text = text.encode("utf-16-le") html_text = "" - def func(upd_text, subst_type=None, url=None, user=None): + def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): upd_text = upd_text.decode("utf-16-le") if subst_type == "text_mention": subst_type = "text_link" @@ -1332,30 +1333,41 @@ def func(upd_text, subst_type=None, url=None, user=None): if not subst_type or not _subs.get(subst_type): return upd_text subs = _subs.get(subst_type) + if subst_type == "custom_emoji": + return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) return subs.format(text=upd_text, url=url) offset = 0 + start_index = 0 + end_index = 0 for entity in entities: if entity.offset > offset: + # when the offset is not 0: for example, a __b__ + # we need to add the text before the entity to the html_text html_text += func(utf16_text[offset * 2 : entity.offset * 2]) offset = entity.offset - html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + + new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user, entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string offset += entity.length + end_index = len(html_text) elif entity.offset == offset: - html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user) + new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user, entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string + end_index = len(html_text) offset += entity.length else: # Here we are processing nested entities. # We shouldn't update offset, because they are the same as entity before. # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, # And we don't change it). - entity_string = utf16_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - formatted_string = func(entity_string, entity.type, entity.url, entity.user) - entity_string_decoded = entity_string.decode("utf-16-le") - last_occurence = html_text.rfind(entity_string_decoded) - string_length = len(entity_string_decoded) - #html_text = html_text.replace(html_text[last_occurence:last_occurence+string_length], formatted_string) - html_text = html_text[:last_occurence] + formatted_string + html_text[last_occurence+string_length:] + entity_string = html_text[start_index : end_index].encode("utf-16-le") + formatted_string = func(entity_string, entity.type, entity.url, entity.user, entity.custom_emoji_id).replace("&", "&").replace("<", "<").replace(">",">") + html_text = html_text[:start_index] + formatted_string + html_text[end_index:] + end_index = len(html_text) + if offset * 2 < len(utf16_text): html_text += func(utf16_text[offset * 2:]) diff --git a/tests/test_types.py b/tests/test_types.py index 8c15396c9..138c36b8c 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -275,10 +275,10 @@ def test_message_entity(): # TODO: Add support for nesting entities - #sample_string_1 = r'{"update_id":934522126,"message":{"message_id":1374510,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682177590,"text":"b b b","entities":[{"offset":0,"length":2,"type":"bold"},{"offset":0,"length":1,"type":"italic"},{"offset":2,"length":2,"type":"bold"},{"offset":2,"length":1,"type":"italic"},{"offset":4,"length":1,"type":"bold"},{"offset":4,"length":1,"type":"italic"}]}}' - #update = types.Update.de_json(sample_string_1) - #message: types.Message = update.message - #assert message.html_text == "b b b" + sample_string_1 = r'{"update_id":934522126,"message":{"message_id":1374510,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682177590,"text":"b b b","entities":[{"offset":0,"length":2,"type":"bold"},{"offset":0,"length":1,"type":"italic"},{"offset":2,"length":2,"type":"bold"},{"offset":2,"length":1,"type":"italic"},{"offset":4,"length":1,"type":"bold"},{"offset":4,"length":1,"type":"italic"}]}}' + update = types.Update.de_json(sample_string_1) + message: types.Message = update.message + assert message.html_text == "b b b" sample_string_2 = r'{"update_id":934522166,"message":{"message_id":1374526,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179716,"text":"b b b","entities":[{"offset":0,"length":1,"type":"bold"},{"offset":2,"length":1,"type":"bold"},{"offset":4,"length":1,"type":"italic"}]}}' message_2 = types.Update.de_json(sample_string_2).message @@ -286,12 +286,15 @@ def test_message_entity(): - #sample_string_3 = r'{"update_id":934522172,"message":{"message_id":1374530,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179968,"text":"This is a bold text with a nested italic and bold text.","entities":[{"offset":10,"length":4,"type":"bold"},{"offset":27,"length":7,"type":"italic"},{"offset":34,"length":15,"type":"bold"},{"offset":34,"length":15,"type":"italic"}]}}' - #message_3 = types.Update.de_json(sample_string_3).message - #assert message_3.html_text == "This is a bold text with a nested italic and bold text." + sample_string_3 = r'{"update_id":934522172,"message":{"message_id":1374530,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179968,"text":"This is a bold text with a nested italic and bold text.","entities":[{"offset":10,"length":4,"type":"bold"},{"offset":27,"length":7,"type":"italic"},{"offset":34,"length":15,"type":"bold"},{"offset":34,"length":15,"type":"italic"}]}}' + message_3 = types.Update.de_json(sample_string_3).message + assert message_3.html_text == "This is a bold text with a nested italic and bold text." - assert True + sample_string_4 = r'{"update_id":934522437,"message":{"message_id":1374619,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682189507,"forward_from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"forward_date":1682189124,"text":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa😋😋","entities":[{"offset":0,"length":76,"type":"bold"},{"offset":0,"length":76,"type":"italic"},{"offset":0,"length":76,"type":"underline"},{"offset":0,"length":76,"type":"strikethrough"},{"offset":76,"length":2,"type":"custom_emoji","custom_emoji_id":"5456188142006575553"},{"offset":78,"length":2,"type":"custom_emoji","custom_emoji_id":"5456188142006575553"}]}}' + message_4 = types.Update.de_json(sample_string_4).message + assert message_4.html_text == 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa😋😋' + From 58d53e1a54470513004fc1927801d7cdb1c6dd1a Mon Sep 17 00:00:00 2001 From: AmirW Date: Fri, 28 Apr 2023 16:12:23 +0330 Subject: [PATCH 1338/1808] bring back the async func message_filter -fixes #1974 - related commits: c84896391eac80eb08b17d0636999499e921723e f69a2ba044983fbd7b072946df7512d0e2904cd6 --- telebot/async_telebot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1a86024d5..72e7ad4c0 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -16,7 +16,7 @@ from telebot.asyncio_storage import StateMemoryStorage, StatePickleStorage, StateStorageBase from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate, SkipHandler, State, ContinueHandling -from inspect import signature +from inspect import signature, iscoroutinefunction from telebot import util, types, asyncio_helper import asyncio @@ -836,6 +836,8 @@ async def _test_filter(self, message_filter, filter_value, message): elif message_filter == 'chat_types': return message.chat.type in filter_value elif message_filter == 'func': + if iscoroutinefunction(filter_value): + return await filter_value(message) return filter_value(message) elif self.custom_filters and message_filter in self.custom_filters: return await self._check_filter(message_filter,filter_value,message) From 14294d1aa33487a780eb7085ead63d1b07a44119 Mon Sep 17 00:00:00 2001 From: Alexey Isaev Date: Sat, 18 Mar 2023 14:43:23 +0300 Subject: [PATCH 1339/1808] redesigned the antiflood method for guaranteed message delivery --- telebot/util.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 405daf57e..3e9dc5c60 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -588,7 +588,7 @@ def webhook_google_functions(bot, request): return 'Bot ON' -def antiflood(function: Callable, *args, **kwargs): +def antiflood(function: Callable, *args, number_retries=5, **kwargs): """ Use this function inside loops in order to avoid getting TooManyRequests error. Example: @@ -602,6 +602,9 @@ def antiflood(function: Callable, *args, **kwargs): :param function: The function to call :type function: :obj:`Callable` + :param number_retries: Number of retries to send + :type function: :obj:int + :param args: The arguments to pass to the function :type args: :obj:`tuple` @@ -613,14 +616,16 @@ def antiflood(function: Callable, *args, **kwargs): from telebot.apihelper import ApiTelegramException from time import sleep - try: - return function(*args, **kwargs) - except ApiTelegramException as ex: - if ex.error_code == 429: - sleep(ex.result_json['parameters']['retry_after']) + for _ in range(number_retries - 1): + try: return function(*args, **kwargs) - else: - raise + except ApiTelegramException as ex: + if ex.error_code == 429: + sleep(ex.result_json['parameters']['retry_after']) + else: + raise + else: + return function(*args, **kwargs) def parse_web_app_data(token: str, raw_init_data: str): From 2dac17aa758c9f29836cee21c653f2bcfa3f6317 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 18 May 2023 18:07:09 +0300 Subject: [PATCH 1340/1808] Bump version to 4.12.0 --- docs/source/conf.py | 4 ++-- telebot/version.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 62ffd29df..f698fc698 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -18,11 +18,11 @@ # -- Project information ----------------------------------------------------- project = 'pyTelegramBotAPI Documentation' -copyright = '2022, coder2020official' +copyright = '2022-2023, coder2020official' author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.11.0' +release = '4.12.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 6624f64e3..b42b55928 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.11.0' +__version__ = '4.12.0' From eaf90cce7f7e0ca9ef0f76ac156c7094f6e7bfb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 21:33:40 +0000 Subject: [PATCH 1341/1808] Bump requests from 2.20.0 to 2.31.0 Bumps [requests](https://github.com/psf/requests) from 2.20.0 to 2.31.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.20.0...v2.31.0) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 516882869..fdbff1c63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest -requests==2.20.0 +requests==2.31.0 wheel==0.38.1 aiohttp>=3.8.0,<3.9.0 \ No newline at end of file From 5ea1abaadd5d92a917f83c903eb99e6b0c7637ee Mon Sep 17 00:00:00 2001 From: Artem Lukin <48987557+artemetra@users.noreply.github.com> Date: Sun, 18 Jun 2023 01:57:08 +0200 Subject: [PATCH 1342/1808] fix typo in docs --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0579325c3..77cdd0ded 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6598,7 +6598,7 @@ def register_chat_member_handler(self, callback: Callable, func: Optional[Callab Registers chat member handler. :param callback: function to be called - :type callback: :obj:`function`` + :type callback: :obj:`function` :param func: Function executed as a filter :type func: :obj:`function` From fb98df3dfe47432e8fc1824a60cf9737445f08f8 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 8 Jul 2023 22:57:13 +0500 Subject: [PATCH 1343/1808] Fixed deprecation warning for readthedocs.org --- .readthedocs.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..ac48ea5a0 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.9" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: docs/requirements.txt From af3a98057f90e0920adea593bc7926a8a8403ac6 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 8 Jul 2023 23:10:41 +0500 Subject: [PATCH 1344/1808] Updated all russian docstrings, currently without translation. --- .../locales/en/LC_MESSAGES/async_version.po | 527 ++++- .../locales/en/LC_MESSAGES/sync_version.po | 699 ++++++- docs/source/locales/en/LC_MESSAGES/types.po | 739 +++++-- docs/source/locales/en/LC_MESSAGES/util.po | 90 +- .../locales/ru/LC_MESSAGES/async_version.po | 1529 +++++++++----- .../locales/ru/LC_MESSAGES/sync_version.po | 1781 +++++++++++------ docs/source/locales/ru/LC_MESSAGES/types.po | 739 +++++-- docs/source/locales/ru/LC_MESSAGES/util.po | 161 +- 8 files changed, 4558 insertions(+), 1707 deletions(-) diff --git a/docs/source/locales/en/LC_MESSAGES/async_version.po b/docs/source/locales/en/LC_MESSAGES/async_version.po index 083d1096f..cce40349e 100644 --- a/docs/source/locales/en/LC_MESSAGES/async_version.po +++ b/docs/source/locales/en/LC_MESSAGES/async_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-02 19:24+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -105,6 +105,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_webhook #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link @@ -131,6 +132,9 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_game_high_scores #: telebot.async_telebot.AsyncTeleBot.get_my_commands #: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_my_description +#: telebot.async_telebot.AsyncTeleBot.get_my_name +#: telebot.async_telebot.AsyncTeleBot.get_my_short_description #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates @@ -196,12 +200,20 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_photo #: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_my_description +#: telebot.async_telebot.AsyncTeleBot.set_my_name +#: telebot.async_telebot.AsyncTeleBot.set_my_short_description #: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title #: telebot.async_telebot.AsyncTeleBot.set_update_listener #: telebot.async_telebot.AsyncTeleBot.set_webhook #: telebot.async_telebot.AsyncTeleBot.setup_middleware @@ -304,6 +316,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_webhook #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link @@ -329,6 +342,9 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_game_high_scores #: telebot.async_telebot.AsyncTeleBot.get_my_commands #: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_my_description +#: telebot.async_telebot.AsyncTeleBot.get_my_name +#: telebot.async_telebot.AsyncTeleBot.get_my_short_description #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates @@ -392,12 +408,20 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_photo #: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_my_description +#: telebot.async_telebot.AsyncTeleBot.set_my_name +#: telebot.async_telebot.AsyncTeleBot.set_my_short_description #: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title #: telebot.async_telebot.AsyncTeleBot.set_update_listener #: telebot.async_telebot.AsyncTeleBot.set_webhook #: telebot.async_telebot.AsyncTeleBot.setup_middleware @@ -477,32 +501,45 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:1 msgid "" -"Use this method to add a new sticker to a set created by the bot. It's " -"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +"Use this method to add a new sticker to a set created by the bot. The " +"format of the added sticker must match the format of the other stickers " +"in the set. Emoji sticker sets can have up to 200 stickers. Animated and " +"video sticker sets can have up to 50 stickers. Static sticker sets can " +"have up to 120 stickers. Returns True on success." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:5 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:8 +msgid "" +"**_sticker, mask_position, emojis parameters are deprecated, use stickers" +" instead" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:10 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:7 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:7 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:12 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:10 msgid "User identifier of created sticker set owner" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:10 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:15 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:3 #: telebot.async_telebot.AsyncTeleBot.get_sticker_set:5 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:6 +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:4 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:4 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:6 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:4 msgid "Sticker set name" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:13 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:18 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:18 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:21 msgid "One or more emoji corresponding to the sticker" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:16 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:21 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:24 msgid "" "PNG image with the sticker, must be up to 512 kilobytes in size, " "dimensions must not exceed 512px, and either width or height must be " @@ -512,36 +549,43 @@ msgid "" "/form-data." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:26 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:26 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 msgid "TGS animation with the sticker, uploaded using multipart/form-data." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:24 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:29 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:32 msgid "WebM animation with the sticker, uploaded using multipart/form-data." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:27 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:40 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:32 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:42 msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:30 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:35 +msgid "" +"A JSON-serialized list of 1-50 initial stickers to be added to the " +"sticker set" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:38 #: telebot.async_telebot.AsyncTeleBot.answer_callback_query:22 -#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:35 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:38 #: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:21 #: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:18 #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:13 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:43 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:56 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:13 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:6 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:22 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:11 #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:15 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:13 msgid "On success, True is returned." msgstr "" @@ -568,6 +612,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_message #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_webhook #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link @@ -627,11 +672,16 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_photo #: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title #: telebot.async_telebot.AsyncTeleBot.set_webhook #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location #: telebot.async_telebot.AsyncTeleBot.stop_poll @@ -644,9 +694,9 @@ msgstr "" msgid "Return type" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:31 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:39 #: telebot.async_telebot.AsyncTeleBot.answer_callback_query:23 -#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:36 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:39 #: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:22 #: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:19 #: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:15 @@ -654,7 +704,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:17 #: telebot.async_telebot.AsyncTeleBot.close:9 #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:14 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:44 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:57 #: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:15 #: telebot.async_telebot.AsyncTeleBot.delete_chat_photo:13 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:11 @@ -662,6 +712,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_message:23 #: telebot.async_telebot.AsyncTeleBot.delete_my_commands:17 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:7 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:7 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:13 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:23 #: telebot.async_telebot.AsyncTeleBot.leave_chat:8 @@ -669,18 +720,23 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 -#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:48 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:26 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:18 #: telebot.async_telebot.AsyncTeleBot.set_chat_description:14 #: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:15 -#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:21 #: telebot.async_telebot.AsyncTeleBot.set_chat_photo:16 #: telebot.async_telebot.AsyncTeleBot.set_chat_title:17 +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:11 #: telebot.async_telebot.AsyncTeleBot.set_my_commands:18 #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:18 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:11 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:12 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:12 #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:12 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:16 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:16 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:11 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:20 #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:12 @@ -795,6 +851,12 @@ msgid "" "switch button" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:35 +msgid "" +"A JSON-serialized object describing a button to be shown above inline " +"query results" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:1 msgid "" "Once the user has confirmed their payment and shipping details, the Bot " @@ -920,7 +982,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_chat_member:8 #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:6 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:11 -#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:11 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:10 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:13 msgid "Unique identifier of the target user" @@ -941,6 +1003,9 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_title:16 #: telebot.async_telebot.AsyncTeleBot.set_my_commands:17 #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:17 +#: telebot.async_telebot.AsyncTeleBot.set_my_description:12 +#: telebot.async_telebot.AsyncTeleBot.set_my_name:12 +#: telebot.async_telebot.AsyncTeleBot.set_my_short_description:12 #: telebot.async_telebot.AsyncTeleBot.set_webhook:46 #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:14 #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:11 @@ -961,7 +1026,7 @@ msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:8 -#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:7 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:10 msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" @@ -985,8 +1050,13 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 #: telebot.async_telebot.AsyncTeleBot.delete_message:22 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:6 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:25 +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:10 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:10 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:11 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:10 msgid "Returns True on success." msgstr "" @@ -1394,21 +1464,21 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.copy_message:45 #: telebot.async_telebot.AsyncTeleBot.forward_message:26 #: telebot.async_telebot.AsyncTeleBot.reply_to:11 -#: telebot.async_telebot.AsyncTeleBot.send_animation:63 -#: telebot.async_telebot.AsyncTeleBot.send_audio:63 +#: telebot.async_telebot.AsyncTeleBot.send_animation:66 +#: telebot.async_telebot.AsyncTeleBot.send_audio:66 #: telebot.async_telebot.AsyncTeleBot.send_contact:44 #: telebot.async_telebot.AsyncTeleBot.send_dice:35 -#: telebot.async_telebot.AsyncTeleBot.send_document:56 +#: telebot.async_telebot.AsyncTeleBot.send_document:59 #: telebot.async_telebot.AsyncTeleBot.send_game:32 #: telebot.async_telebot.AsyncTeleBot.send_invoice:101 #: telebot.async_telebot.AsyncTeleBot.send_location:49 #: telebot.async_telebot.AsyncTeleBot.send_message:46 #: telebot.async_telebot.AsyncTeleBot.send_photo:48 #: telebot.async_telebot.AsyncTeleBot.send_poll:70 -#: telebot.async_telebot.AsyncTeleBot.send_sticker:39 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:42 #: telebot.async_telebot.AsyncTeleBot.send_venue:57 -#: telebot.async_telebot.AsyncTeleBot.send_video:64 -#: telebot.async_telebot.AsyncTeleBot.send_video_note:48 +#: telebot.async_telebot.AsyncTeleBot.send_video:67 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:51 #: telebot.async_telebot.AsyncTeleBot.send_voice:49 msgid "On success, the sent Message is returned." msgstr "" @@ -1416,18 +1486,18 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.copy_message:46 #: telebot.async_telebot.AsyncTeleBot.forward_message:27 #: telebot.async_telebot.AsyncTeleBot.reply_to:12 -#: telebot.async_telebot.AsyncTeleBot.send_animation:64 -#: telebot.async_telebot.AsyncTeleBot.send_audio:64 +#: telebot.async_telebot.AsyncTeleBot.send_animation:67 +#: telebot.async_telebot.AsyncTeleBot.send_audio:67 #: telebot.async_telebot.AsyncTeleBot.send_contact:45 #: telebot.async_telebot.AsyncTeleBot.send_dice:36 -#: telebot.async_telebot.AsyncTeleBot.send_document:57 +#: telebot.async_telebot.AsyncTeleBot.send_document:60 #: telebot.async_telebot.AsyncTeleBot.send_location:50 #: telebot.async_telebot.AsyncTeleBot.send_message:47 #: telebot.async_telebot.AsyncTeleBot.send_photo:49 -#: telebot.async_telebot.AsyncTeleBot.send_sticker:40 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:43 #: telebot.async_telebot.AsyncTeleBot.send_venue:58 -#: telebot.async_telebot.AsyncTeleBot.send_video:65 -#: telebot.async_telebot.AsyncTeleBot.send_video_note:49 +#: telebot.async_telebot.AsyncTeleBot.send_video:68 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:52 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1680,7 +1750,13 @@ msgid "" "https://core.telegram.org/bots/api#createnewstickerset" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:10 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:8 +msgid "" +"Fields *_sticker are deprecated, pass a list of stickers to stickers " +"parameter instead." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:13 msgid "" "Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " "animals). Can contain only English letters, digits and underscores. Must " @@ -1689,21 +1765,38 @@ msgid "" "characters." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:15 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:18 msgid "Sticker set title, 1-64 characters" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:32 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:35 msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:36 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:39 msgid "" -"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " -"emoji sticker sets can't be created via the Bot API at the moment. By " -"default, a regular sticker set is created." +"Type of stickers in the set, pass “regular”, “mask”, or “custom_emoji”. " +"By default, a regular sticker set is created." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:45 +msgid "" +"Pass True if stickers in the sticker set must be repainted to the color " +"of text when used in messages, the accent color if used as emoji status, " +"white on chat photos, or another appropriate color based on context; for " +"custom emoji sticker sets only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:50 +msgid "List of stickers to be added to the set" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:53 +msgid "" +"Format of stickers in the set, must be one of “static”, “animated”, " +"“video”" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:1 @@ -1846,6 +1939,10 @@ msgstr "" msgid "File identifier of the sticker" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_set:1 +msgid "Use this method to delete a sticker set. Returns True on success." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:1 msgid "" "Use this method to remove webhook integration if you decide to switch " @@ -2396,7 +2493,7 @@ msgid "File identifier" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.get_file:12 -#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file:14 +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file:21 msgid ":class:`telebot.types.File`" msgstr "" @@ -2457,7 +2554,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:10 #: telebot.async_telebot.AsyncTeleBot.retrieve_data:3 #: telebot.async_telebot.AsyncTeleBot.set_game_score:5 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:9 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:9 msgid "User identifier" msgstr "" @@ -2518,6 +2615,49 @@ msgstr "" msgid ":class:`telebot.types.ChatAdministratorRights`" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.get_my_description:1 +msgid "" +"Use this method to get the current bot description for the given user " +"language. Returns BotDescription on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_description:4 +#: telebot.async_telebot.AsyncTeleBot.get_my_short_description:4 +msgid "A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_description:7 +msgid ":class:`telebot.types.BotDescription`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:1 +msgid "" +"Use this method to get the current bot name for the given user language. " +"Returns BotName on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmyname" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:6 +msgid "Optional. A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:9 +msgid ":class:`telebot.types.BotName`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_short_description:1 +msgid "" +"Use this method to get the current bot short description for the given " +"user language. Returns BotShortDescription on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_short_description:7 +msgid ":class:`telebot.types.BotShortDescription`" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.get_state:1 msgid "" "Gets current state of a user. Not recommended to use this method. But it " @@ -2589,7 +2729,7 @@ msgid "Array of string. List the types of updates you want your bot to receive." msgstr "" #: of telebot.async_telebot.AsyncTeleBot.get_updates:21 -msgid "Timeout in seconds for long polling." +msgid "Timeout in seconds for request." msgstr "" #: of telebot.async_telebot.AsyncTeleBot.get_updates:24 @@ -3178,61 +3318,85 @@ msgid "" "https://core.telegram.org/bots/api#restrictchatmember" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:8 +msgid "" +"Individual parameters are deprecated and will be removed, use " +"'permissions' instead" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:17 msgid "" "Date when restrictions will be lifted for the user, unix time. If user is" " restricted for more than 366 days or less than 30 seconds from the " "current time, they are considered to be restricted forever" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:19 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:22 msgid "" "Pass True, if the user can send text messages, contacts, locations and " "venues" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:22 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:25 msgid "" "Pass True, if the user can send audios, documents, photos, videos, video " "notes and voice notes, implies can_send_messages" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:26 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:29 msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:29 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:35 msgid "" "Pass True, if the user may add web page previews to their messages, " "implies can_send_media_messages" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:36 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:39 msgid "" "Pass True, if the user is allowed to change the chat title, photo and " "other settings. Ignored in public supergroups" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:40 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:43 msgid "" "Pass True, if the user is allowed to invite new users to the chat, " "implies can_invite_users" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:44 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:47 msgid "" "Pass True, if the user is allowed to pin messages. Ignored in public " "supergroups" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:47 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:50 #: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:14 +msgid "" +"Pass True if chat permissions are set independently. Otherwise, the " +"can_send_other_messages and can_add_web_page_previews permissions will " +"imply the can_send_messages, can_send_audios, can_send_documents, " +"can_send_photos, can_send_videos, can_send_video_notes, and " +"can_send_voice_notes permissions; the can_send_polls permission will " +"imply the can_send_messages permission." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:56 +msgid "" +"Pass ChatPermissions object to set all permissions at once. Use this " +"parameter instead of passing all boolean parameters to avoid backward " +"compatibility problems in future." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:60 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:20 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:19 msgid "True on success" msgstr "" @@ -3275,27 +3439,27 @@ msgstr "" msgid "IP address to listen to. Defaults to 0.0.0.0" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:5 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:4 msgid "A port which will be used to listen to webhooks." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:6 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:5 msgid "Path to the webhook. Defaults to /token" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:7 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:6 msgid "Path to the certificate file." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:8 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:7 msgid "Path to the certificate key file." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:9 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:8 msgid "Webhook URL." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:10 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:9 msgid "" "Maximum allowed number of simultaneous HTTPS connections to the webhook " "for update delivery, 1-100. Defaults to 40. Use lower values to limit the" @@ -3303,7 +3467,7 @@ msgid "" "throughput." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:11 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:10 msgid "" "A JSON-serialized list of the update types you want your bot to receive. " "For example, specify [“message”, “edited_channel_post”, “callback_query”]" @@ -3313,24 +3477,32 @@ msgid "" " be used." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:12 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:11 msgid "" "The fixed IP address which will be used to send webhook requests instead " "of the IP address resolved through DNS" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:13 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:12 msgid "Pass True to drop all pending updates" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:14 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:13 msgid "Integer. Request connection timeout" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:15 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:14 msgid "Secret token to be used to verify the webhook request." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:15 +msgid "Length of a secret token, defaults to 20" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:16 +msgid "Debug mode, defaults to False" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_animation:1 msgid "" "Use this method to send animation files (GIF or H.264/MPEG-4 AVC video " @@ -3402,6 +3574,14 @@ msgstr "" msgid "Pass True, if the animation should be sent as a spoiler" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_animation:63 +#: telebot.async_telebot.AsyncTeleBot.send_audio:63 +#: telebot.async_telebot.AsyncTeleBot.send_document:56 +#: telebot.async_telebot.AsyncTeleBot.send_video:64 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:48 +msgid "Deprecated. Use thumbnail instead" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -3953,6 +4133,10 @@ msgstr "" msgid "to disable the notification" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:39 +msgid "Emoji associated with the sticker; only for just uploaded stickers" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_venue:1 msgid "" "Use this method to send information about a venue. On success, the sent " @@ -4243,6 +4427,20 @@ msgstr "" msgid "New chat title, 1-255 characters" msgstr "" +#: of +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:1 +msgid "" +"Use this method to set the thumbnail of a custom emoji sticker set. " +"Returns True on success." +msgstr "" + +#: of +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:7 +msgid "" +"Custom emoji identifier of a sticker from the sticker set; pass an empty " +"string to drop the thumbnail and use the first sticker as the thumbnail." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.set_game_score:1 msgid "Sets the value of points in the game to a specific user." msgstr "" @@ -4312,6 +4510,65 @@ msgid "" "groups and supergroups will be changed." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.set_my_description:1 +msgid "" +"Use this method to change the bot's description, which is shown in the " +"chat with the bot if the chat is empty. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_description:5 +msgid "" +"New bot description; 0-512 characters. Pass an empty string to remove the" +" dedicated description for the given language." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_description:8 +msgid "" +"A two-letter ISO 639-1 language code. If empty, the description will be " +"applied to all users for whose language there is no dedicated " +"description." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:1 +msgid "Use this method to change the bot's name. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmyname" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:5 +msgid "" +"Optional. New bot name; 0-64 characters. Pass an empty string to remove " +"the dedicated name for the given language." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:8 +msgid "" +"Optional. A two-letter ISO 639-1 language code. If empty, the name will " +"be shown to all users for whose language there is no dedicated name." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_short_description:1 +msgid "" +"Use this method to change the bot's short description, which is shown on " +"the bot's profile page and is sent together with the link when users " +"share the bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_short_description:5 +msgid "" +"New short description for the bot; 0-120 characters. Pass an empty string" +" to remove the dedicated short description for the given language." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_short_description:8 +msgid "" +"A two-letter ISO 639-1 language code. If empty, the short description " +"will be applied to all users for whose language there is no dedicated " +"short description." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.set_state:1 msgid "Sets a new state of a user." msgstr "" @@ -4328,6 +4585,47 @@ msgstr "" msgid "new state. can be string, integer, or :class:`telebot.types.State`" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:1 +msgid "" +"Use this method to set the emoji list of a sticker set. Returns True on " +"success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:7 +msgid "List of emojis" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:1 +msgid "" +"Use this method to change search keywords assigned to a regular or custom" +" emoji sticker. The sticker must belong to a sticker set created by the " +"bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:5 +msgid "File identifier of the sticker." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:8 +msgid "" +"A JSON-serialized list of 0-20 search keywords for the sticker with total" +" length of up to 64 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:1 +msgid "" +"Use this method to change the mask position of a mask sticker. The " +"sticker must belong to a sticker set that was created by the bot. Returns" +" True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:8 +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:1 msgid "" "Use this method to move a sticker in a set created by the bot to a " @@ -4344,19 +4642,47 @@ msgstr "" msgid "New sticker position in the set, zero-based" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:1 +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:1 msgid "" "Use this method to set the thumbnail of a sticker set. Animated " "thumbnails can be set for animated sticker sets only. Returns True on " "success." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:4 +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickersetthumb" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:12 +msgid "" +"A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in " +"size and have a width and height of exactly 100px, or a .TGS animation " +"with a thumbnail up to 32 kilobytes in size (see " +"https://core.telegram.org/stickers#animated-sticker-requirements for " +"animated sticker technical requirements), or a WEBM video with the " +"thumbnail up to 32 kilobytes in size; see " +"https://core.telegram.org/stickers#video-sticker-requirements for video " +"sticker technical requirements. Pass a file_id as a String to send a file" +" that already exists on the Telegram servers, 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. More information on Sending Files ». Animated " +"and video sticker set thumbnails can't be uploaded via HTTP URL. If " +"omitted, then the thumbnail is dropped and the first sticker is used as " +"the thumbnail." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:1 +msgid "" +"Use this method to set the title of a created sticker set. Returns True " +"on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:7 +msgid "New sticker set title" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.set_update_listener:1 msgid "Update listener is a function that gets any update." msgstr "" @@ -4662,12 +4988,23 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:9 msgid "" -"PNG image with the sticker, must be up to 512 kilobytes in size, " -"dimensions must not exceed 512px, and either width or height must be " -"exactly 512px." +"DEPRECATED: PNG image with the sticker, must be up to 512 kilobytes in " +"size, dimensions must not exceed 512px, and either width or height must " +"be exactly 512px." msgstr "" #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:13 +msgid "" +"A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. See " +"https://core.telegram.org/stickers for technical requirements. More " +"information on Sending Files »" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:17 +msgid "One of \"static\", \"animated\", \"video\"." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:20 msgid "On success, the sent file is returned." msgstr "" @@ -4913,3 +5250,31 @@ msgstr "" #~ "character long" #~ msgstr "" +#~ msgid "" +#~ "Use this method to add a new " +#~ "sticker to a set created by the" +#~ " bot. It's required to pass " +#~ "`png_sticker` or `tgs_sticker`. Returns True" +#~ " on success." +#~ msgstr "" + +#~ msgid "" +#~ "Optional, Type of stickers in the " +#~ "set, pass “regular” or “mask”. Custom" +#~ " emoji sticker sets can't be created" +#~ " via the Bot API at the moment." +#~ " By default, a regular sticker set" +#~ " is created." +#~ msgstr "" + +#~ msgid "Timeout in seconds for long polling." +#~ msgstr "" + +#~ msgid "" +#~ "PNG image with the sticker, must " +#~ "be up to 512 kilobytes in size," +#~ " dimensions must not exceed 512px, " +#~ "and either width or height must be" +#~ " exactly 512px." +#~ msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/sync_version.po b/docs/source/locales/en/LC_MESSAGES/sync_version.po index 766e23cb1..13aa6b3cf 100644 --- a/docs/source/locales/en/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/en/LC_MESSAGES/sync_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-02 19:24+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -113,9 +113,9 @@ msgstr "" #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands #: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file -#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic -#: telebot.TeleBot.edit_general_forum_topic +#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook +#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link +#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_general_forum_topic #: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup @@ -132,16 +132,17 @@ msgstr "" #: telebot.TeleBot.get_file_url telebot.TeleBot.get_game_high_scores #: telebot.TeleBot.get_my_commands #: telebot.TeleBot.get_my_default_administrator_rights -#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set -#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.hide_general_forum_topic -#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler -#: telebot.TeleBot.leave_chat telebot.TeleBot.load_next_step_handlers -#: telebot.TeleBot.load_reply_handlers telebot.TeleBot.message_handler -#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler -#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler -#: telebot.TeleBot.poll_handler telebot.TeleBot.polling -#: telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name +#: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state +#: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates +#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info +#: telebot.TeleBot.hide_general_forum_topic telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers +#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler +#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler @@ -174,16 +175,21 @@ msgstr "" #: telebot.TeleBot.send_media_group telebot.TeleBot.send_message #: telebot.TeleBot.send_photo telebot.TeleBot.send_poll #: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note -#: telebot.TeleBot.send_voice +#: telebot.TeleBot.send_video telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights -#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set -#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_update_listener +#: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name +#: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state +#: telebot.TeleBot.set_sticker_emoji_list telebot.TeleBot.set_sticker_keywords +#: telebot.TeleBot.set_sticker_mask_position +#: telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumbnail +#: telebot.TeleBot.set_sticker_set_title telebot.TeleBot.set_update_listener #: telebot.TeleBot.set_webhook telebot.TeleBot.setup_middleware #: telebot.TeleBot.shipping_query_handler #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll @@ -320,9 +326,9 @@ msgstr "" #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands #: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file -#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic -#: telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook +#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link +#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text @@ -337,15 +343,16 @@ msgstr "" #: telebot.TeleBot.get_file_url telebot.TeleBot.get_forum_topic_icon_stickers #: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands #: telebot.TeleBot.get_my_default_administrator_rights -#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set -#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling -#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat -#: telebot.TeleBot.log_out telebot.TeleBot.message_handler -#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler -#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler -#: telebot.TeleBot.poll_handler telebot.TeleBot.polling -#: telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name +#: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state +#: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates +#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info +#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler +#: telebot.TeleBot.leave_chat telebot.TeleBot.log_out +#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler +#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler @@ -376,16 +383,21 @@ msgstr "" #: telebot.TeleBot.send_media_group telebot.TeleBot.send_message #: telebot.TeleBot.send_photo telebot.TeleBot.send_poll #: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note -#: telebot.TeleBot.send_voice +#: telebot.TeleBot.send_video telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights -#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set -#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name +#: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state +#: telebot.TeleBot.set_sticker_emoji_list telebot.TeleBot.set_sticker_keywords +#: telebot.TeleBot.set_sticker_mask_position +#: telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumbnail +#: telebot.TeleBot.set_sticker_set_title telebot.TeleBot.set_webhook #: telebot.TeleBot.setup_middleware telebot.TeleBot.shipping_query_handler #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat @@ -435,31 +447,43 @@ msgstr "" #: of telebot.TeleBot.add_sticker_to_set:1 msgid "" -"Use this method to add a new sticker to a set created by the bot. It's " -"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +"Use this method to add a new sticker to a set created by the bot. The " +"format of the added sticker must match the format of the other stickers " +"in the set. Emoji sticker sets can have up to 200 stickers. Animated and " +"video sticker sets can have up to 50 stickers. Static sticker sets can " +"have up to 120 stickers. Returns True on success." msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:5 +#: of telebot.TeleBot.add_sticker_to_set:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:7 -#: telebot.TeleBot.create_new_sticker_set:7 +#: of telebot.TeleBot.add_sticker_to_set:10 +msgid "" +"**_sticker, mask_position, emojis parameters are deprecated, use stickers" +" instead" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:12 +#: telebot.TeleBot.create_new_sticker_set:10 msgid "User identifier of created sticker set owner" msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:10 telebot.TeleBot.get_sticker_set:5 -#: telebot.TeleBot.set_sticker_set_thumb:6 +#: of telebot.TeleBot.add_sticker_to_set:15 +#: telebot.TeleBot.delete_sticker_set:3 telebot.TeleBot.get_sticker_set:5 +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:4 +#: telebot.TeleBot.set_sticker_set_thumbnail:6 +#: telebot.TeleBot.set_sticker_set_title:4 msgid "Sticker set name" msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:13 -#: telebot.TeleBot.create_new_sticker_set:18 +#: of telebot.TeleBot.add_sticker_to_set:18 +#: telebot.TeleBot.create_new_sticker_set:21 msgid "One or more emoji corresponding to the sticker" msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:16 -#: telebot.TeleBot.create_new_sticker_set:21 +#: of telebot.TeleBot.add_sticker_to_set:21 +#: telebot.TeleBot.create_new_sticker_set:24 msgid "" "PNG image with the sticker, must be up to 512 kilobytes in size, " "dimensions must not exceed 512px, and either width or height must be " @@ -469,35 +493,42 @@ msgid "" "/form-data." msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:21 -#: telebot.TeleBot.create_new_sticker_set:26 +#: of telebot.TeleBot.add_sticker_to_set:26 +#: telebot.TeleBot.create_new_sticker_set:29 msgid "TGS animation with the sticker, uploaded using multipart/form-data." msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:24 -#: telebot.TeleBot.create_new_sticker_set:29 +#: of telebot.TeleBot.add_sticker_to_set:29 +#: telebot.TeleBot.create_new_sticker_set:32 msgid "WebM animation with the sticker, uploaded using multipart/form-data." msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:27 -#: telebot.TeleBot.create_new_sticker_set:40 +#: of telebot.TeleBot.add_sticker_to_set:32 +#: telebot.TeleBot.create_new_sticker_set:42 msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:30 +#: of telebot.TeleBot.add_sticker_to_set:35 +msgid "" +"A JSON-serialized list of 1-50 initial stickers to be added to the " +"sticker set" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:38 #: telebot.TeleBot.answer_callback_query:22 -#: telebot.TeleBot.answer_inline_query:35 +#: telebot.TeleBot.answer_inline_query:38 #: telebot.TeleBot.answer_pre_checkout_query:21 #: telebot.TeleBot.answer_shipping_query:18 #: telebot.TeleBot.close_forum_topic:13 -#: telebot.TeleBot.create_new_sticker_set:43 +#: telebot.TeleBot.create_new_sticker_set:56 #: telebot.TeleBot.delete_forum_topic:13 #: telebot.TeleBot.delete_sticker_from_set:6 #: telebot.TeleBot.edit_forum_topic:22 telebot.TeleBot.reopen_forum_topic:13 +#: telebot.TeleBot.set_sticker_keywords:11 #: telebot.TeleBot.set_sticker_position_in_set:11 -#: telebot.TeleBot.set_sticker_set_thumb:15 +#: telebot.TeleBot.set_sticker_set_thumbnail:15 #: telebot.TeleBot.unpin_all_forum_topic_messages:13 msgid "On success, True is returned." msgstr "" @@ -514,9 +545,10 @@ msgstr "" #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands -#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_webhook -#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link -#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_sticker_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text telebot.TeleBot.export_chat_invite_link @@ -542,15 +574,18 @@ msgstr "" #: telebot.TeleBot.send_message telebot.TeleBot.send_photo #: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker #: telebot.TeleBot.send_venue telebot.TeleBot.send_video -#: telebot.TeleBot.send_video_note #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_sticker_emoji_list telebot.TeleBot.set_sticker_keywords +#: telebot.TeleBot.set_sticker_mask_position #: telebot.TeleBot.set_sticker_position_in_set -#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.set_sticker_set_thumbnail +#: telebot.TeleBot.set_sticker_set_title telebot.TeleBot.set_webhook #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat #: telebot.TeleBot.unpin_all_chat_messages @@ -560,34 +595,43 @@ msgstr "" msgid "Return type" msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:31 +#: of telebot.TeleBot.add_sticker_to_set:39 #: telebot.TeleBot.answer_callback_query:23 -#: telebot.TeleBot.answer_inline_query:36 +#: telebot.TeleBot.answer_inline_query:39 #: telebot.TeleBot.answer_pre_checkout_query:22 #: telebot.TeleBot.answer_shipping_query:19 #: telebot.TeleBot.approve_chat_join_request:15 #: telebot.TeleBot.ban_chat_member:25 telebot.TeleBot.ban_chat_sender_chat:17 #: telebot.TeleBot.close:9 telebot.TeleBot.close_forum_topic:14 -#: telebot.TeleBot.create_new_sticker_set:44 +#: telebot.TeleBot.create_new_sticker_set:57 #: telebot.TeleBot.decline_chat_join_request:15 #: telebot.TeleBot.delete_chat_photo:13 #: telebot.TeleBot.delete_chat_sticker_set:11 #: telebot.TeleBot.delete_forum_topic:14 telebot.TeleBot.delete_message:23 #: telebot.TeleBot.delete_my_commands:17 -#: telebot.TeleBot.delete_sticker_from_set:7 telebot.TeleBot.delete_webhook:13 +#: telebot.TeleBot.delete_sticker_from_set:7 +#: telebot.TeleBot.delete_sticker_set:7 telebot.TeleBot.delete_webhook:13 #: telebot.TeleBot.edit_forum_topic:23 telebot.TeleBot.leave_chat:8 #: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 #: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 #: telebot.TeleBot.reopen_forum_topic:14 -#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:26 +#: telebot.TeleBot.restrict_chat_member:61 telebot.TeleBot.send_chat_action:26 +#: telebot.TeleBot.send_video_note:28 telebot.TeleBot.send_video_note:40 +#: telebot.TeleBot.send_video_note:43 #: telebot.TeleBot.set_chat_administrator_custom_title:18 #: telebot.TeleBot.set_chat_description:14 #: telebot.TeleBot.set_chat_menu_button:15 -#: telebot.TeleBot.set_chat_permissions:15 telebot.TeleBot.set_chat_photo:16 -#: telebot.TeleBot.set_chat_title:17 telebot.TeleBot.set_my_commands:18 +#: telebot.TeleBot.set_chat_permissions:21 telebot.TeleBot.set_chat_photo:16 +#: telebot.TeleBot.set_chat_title:17 +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:11 +#: telebot.TeleBot.set_my_commands:18 #: telebot.TeleBot.set_my_default_administrator_rights:18 +#: telebot.TeleBot.set_sticker_emoji_list:11 +#: telebot.TeleBot.set_sticker_keywords:12 +#: telebot.TeleBot.set_sticker_mask_position:12 #: telebot.TeleBot.set_sticker_position_in_set:12 -#: telebot.TeleBot.set_sticker_set_thumb:16 +#: telebot.TeleBot.set_sticker_set_thumbnail:16 +#: telebot.TeleBot.set_sticker_set_title:11 #: telebot.TeleBot.unban_chat_member:20 #: telebot.TeleBot.unban_chat_sender_chat:15 #: telebot.TeleBot.unpin_all_chat_messages:12 @@ -702,6 +746,12 @@ msgid "" "switch button" msgstr "" +#: of telebot.TeleBot.answer_inline_query:35 +msgid "" +"A JSON-serialized object describing a button to be shown above inline " +"query results" +msgstr "" + #: of telebot.TeleBot.answer_pre_checkout_query:1 msgid "" "Once the user has confirmed their payment and shipping details, the Bot " @@ -825,7 +875,7 @@ msgstr "" #: telebot.TeleBot.decline_chat_join_request:11 #: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_profile_photos:6 #: telebot.TeleBot.promote_chat_member:11 -#: telebot.TeleBot.restrict_chat_member:11 +#: telebot.TeleBot.restrict_chat_member:14 #: telebot.TeleBot.set_chat_administrator_custom_title:10 #: telebot.TeleBot.unban_chat_member:13 msgid "Unique identifier of the target user" @@ -842,7 +892,9 @@ msgstr "" #: telebot.TeleBot.set_chat_menu_button:14 telebot.TeleBot.set_chat_photo:15 #: telebot.TeleBot.set_chat_title:16 telebot.TeleBot.set_my_commands:17 #: telebot.TeleBot.set_my_default_administrator_rights:17 -#: telebot.TeleBot.set_webhook:46 telebot.TeleBot.unban_chat_sender_chat:14 +#: telebot.TeleBot.set_my_description:14 telebot.TeleBot.set_my_name:12 +#: telebot.TeleBot.set_my_short_description:14 telebot.TeleBot.set_webhook:46 +#: telebot.TeleBot.unban_chat_sender_chat:14 #: telebot.TeleBot.unpin_all_chat_messages:11 #: telebot.TeleBot.unpin_chat_message:14 msgid "True on success." @@ -860,7 +912,7 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" msgstr "" -#: of telebot.TeleBot.ban_chat_member:8 telebot.TeleBot.restrict_chat_member:7 +#: of telebot.TeleBot.ban_chat_member:8 telebot.TeleBot.restrict_chat_member:10 msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" @@ -883,7 +935,12 @@ msgstr "" #: of telebot.TeleBot.ban_chat_member:24 #: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 -#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:25 +#: telebot.TeleBot.delete_sticker_set:6 telebot.TeleBot.delete_webhook:12 +#: telebot.TeleBot.send_chat_action:25 +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:10 +#: telebot.TeleBot.set_sticker_emoji_list:10 +#: telebot.TeleBot.set_sticker_mask_position:11 +#: telebot.TeleBot.set_sticker_set_title:10 msgid "Returns True on success." msgstr "" @@ -1241,26 +1298,24 @@ msgid "Identifier of a message thread, in which the message will be sent" msgstr "" #: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 -#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:63 -#: telebot.TeleBot.send_audio:63 telebot.TeleBot.send_contact:44 -#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:56 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:66 +#: telebot.TeleBot.send_audio:66 telebot.TeleBot.send_contact:44 +#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:59 #: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 #: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 #: telebot.TeleBot.send_photo:48 telebot.TeleBot.send_poll:70 -#: telebot.TeleBot.send_sticker:39 telebot.TeleBot.send_venue:57 -#: telebot.TeleBot.send_video:64 telebot.TeleBot.send_video_note:48 -#: telebot.TeleBot.send_voice:49 +#: telebot.TeleBot.send_sticker:42 telebot.TeleBot.send_venue:57 +#: telebot.TeleBot.send_video:67 telebot.TeleBot.send_voice:49 msgid "On success, the sent Message is returned." msgstr "" #: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 -#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:64 -#: telebot.TeleBot.send_audio:64 telebot.TeleBot.send_contact:45 -#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:57 +#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:67 +#: telebot.TeleBot.send_audio:67 telebot.TeleBot.send_contact:45 +#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:60 #: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 -#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:40 -#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:65 -#: telebot.TeleBot.send_video_note:49 +#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:43 +#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:68 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1493,7 +1548,13 @@ msgid "" "https://core.telegram.org/bots/api#createnewstickerset" msgstr "" -#: of telebot.TeleBot.create_new_sticker_set:10 +#: of telebot.TeleBot.create_new_sticker_set:8 +msgid "" +"Fields *_sticker are deprecated, pass a list of stickers to stickers " +"parameter instead." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:13 msgid "" "Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " "animals). Can contain only English letters, digits and underscores. Must " @@ -1502,21 +1563,38 @@ msgid "" "characters." msgstr "" -#: of telebot.TeleBot.create_new_sticker_set:15 +#: of telebot.TeleBot.create_new_sticker_set:18 msgid "Sticker set title, 1-64 characters" msgstr "" -#: of telebot.TeleBot.create_new_sticker_set:32 +#: of telebot.TeleBot.create_new_sticker_set:35 msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." msgstr "" -#: of telebot.TeleBot.create_new_sticker_set:36 +#: of telebot.TeleBot.create_new_sticker_set:39 +msgid "" +"Type of stickers in the set, pass “regular”, “mask”, or “custom_emoji”. " +"By default, a regular sticker set is created." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:45 msgid "" -"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " -"emoji sticker sets can't be created via the Bot API at the moment. By " -"default, a regular sticker set is created." +"Pass True if stickers in the sticker set must be repainted to the color " +"of text when used in messages, the accent color if used as emoji status, " +"white on chat photos, or another appropriate color based on context; for " +"custom emoji sticker sets only" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:50 +msgid "List of stickers to be added to the set" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:53 +msgid "" +"Format of stickers in the set, must be one of “static”, “animated”, " +"“video”" msgstr "" #: of telebot.TeleBot.decline_chat_join_request:1 @@ -1656,6 +1734,10 @@ msgstr "" msgid "File identifier of the sticker" msgstr "" +#: of telebot.TeleBot.delete_sticker_set:1 +msgid "Use this method to delete a sticker set. Returns True on success." +msgstr "" + #: of telebot.TeleBot.delete_webhook:1 msgid "" "Use this method to remove webhook integration if you decide to switch " @@ -2171,6 +2253,9 @@ msgid "Number of members in the chat." msgstr "" #: of telebot.TeleBot.get_chat_member_count:9 +#: telebot.TeleBot.send_video_note:14 telebot.TeleBot.send_video_note:17 +#: telebot.TeleBot.send_video_note:20 telebot.TeleBot.send_video_note:31 +#: telebot.TeleBot.send_video_note:46 msgid ":obj:`int`" msgstr "" @@ -2237,7 +2322,7 @@ msgstr "" msgid "File identifier" msgstr "" -#: of telebot.TeleBot.get_file:12 telebot.TeleBot.upload_sticker_file:14 +#: of telebot.TeleBot.get_file:12 telebot.TeleBot.upload_sticker_file:21 msgid ":class:`telebot.types.File`" msgstr "" @@ -2296,7 +2381,7 @@ msgid "" msgstr "" #: of telebot.TeleBot.get_game_high_scores:10 telebot.TeleBot.retrieve_data:3 -#: telebot.TeleBot.set_game_score:5 telebot.TeleBot.set_sticker_set_thumb:9 +#: telebot.TeleBot.set_game_score:5 telebot.TeleBot.set_sticker_set_thumbnail:9 msgid "User identifier" msgstr "" @@ -2360,6 +2445,61 @@ msgstr "" msgid ":class:`telebot.types.ChatAdministratorRights`" msgstr "" +#: of telebot.TeleBot.get_my_description:1 +msgid "" +"Use this method to get the current bot description for the given user " +"language. Returns BotDescription on success." +msgstr "" + +#: of telebot.TeleBot.get_my_description:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmydescription" +msgstr "" + +#: of telebot.TeleBot.get_my_description:6 +#: telebot.TeleBot.get_my_short_description:6 +msgid "A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.TeleBot.get_my_description:9 +msgid ":class:`telebot.types.BotDescription`" +msgstr "" + +#: of telebot.TeleBot.get_my_name:1 +msgid "" +"Use this method to get the current bot name for the given user language. " +"Returns BotName on success." +msgstr "" + +#: of telebot.TeleBot.get_my_name:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmyname" +msgstr "" + +#: of telebot.TeleBot.get_my_name:6 +msgid "Optional. A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.TeleBot.get_my_name:9 +msgid ":class:`telebot.types.BotName`" +msgstr "" + +#: of telebot.TeleBot.get_my_short_description:1 +msgid "" +"Use this method to get the current bot short description for the given " +"user language. Returns BotShortDescription on success." +msgstr "" + +#: of telebot.TeleBot.get_my_short_description:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmyshortdescription" +msgstr "" + +#: of telebot.TeleBot.get_my_short_description:9 +msgid ":class:`telebot.types.BotShortDescription`" +msgstr "" + #: of telebot.TeleBot.get_state:1 msgid "" "Gets current state of a user. Not recommended to use this method. But it " @@ -3201,61 +3341,84 @@ msgid "" "https://core.telegram.org/bots/api#restrictchatmember" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:14 +#: of telebot.TeleBot.restrict_chat_member:8 +msgid "" +"Individual parameters are deprecated and will be removed, use " +"'permissions' instead." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:17 msgid "" "Date when restrictions will be lifted for the user, unix time. If user is" " restricted for more than 366 days or less than 30 seconds from the " "current time, they are considered to be restricted forever" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:19 +#: of telebot.TeleBot.restrict_chat_member:22 msgid "" "Pass True, if the user can send text messages, contacts, locations and " "venues" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:22 +#: of telebot.TeleBot.restrict_chat_member:25 msgid "" "Pass True, if the user can send audios, documents, photos, videos, video " "notes and voice notes, implies can_send_messages" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:26 +#: of telebot.TeleBot.restrict_chat_member:29 msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:29 +#: of telebot.TeleBot.restrict_chat_member:32 msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:32 +#: of telebot.TeleBot.restrict_chat_member:35 msgid "" "Pass True, if the user may add web page previews to their messages, " "implies can_send_media_messages" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:36 +#: of telebot.TeleBot.restrict_chat_member:39 msgid "" "Pass True, if the user is allowed to change the chat title, photo and " "other settings. Ignored in public supergroups" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:40 +#: of telebot.TeleBot.restrict_chat_member:43 msgid "" "Pass True, if the user is allowed to invite new users to the chat, " "implies can_invite_users" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:44 +#: of telebot.TeleBot.restrict_chat_member:47 msgid "" "Pass True, if the user is allowed to pin messages. Ignored in public " "supergroups" msgstr "" -#: of telebot.TeleBot.restrict_chat_member:47 -#: telebot.TeleBot.set_chat_permissions:14 telebot.TeleBot.unban_chat_member:19 +#: of telebot.TeleBot.restrict_chat_member:50 +#: telebot.TeleBot.set_chat_permissions:14 +msgid "" +"Pass True if chat permissions are set independently. Otherwise, the " +"can_send_other_messages and can_add_web_page_previews permissions will " +"imply the can_send_messages, can_send_audios, can_send_documents, " +"can_send_photos, can_send_videos, can_send_video_notes, and " +"can_send_voice_notes permissions; the can_send_polls permission will " +"imply the can_send_messages permission." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:56 +msgid "" +"Pass ChatPermissions object to set all permissions at once. Use this " +"param instead of passing all boolean parameters." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:60 +#: telebot.TeleBot.set_chat_permissions:20 telebot.TeleBot.unban_chat_member:19 msgid "True on success" msgstr "" @@ -3429,6 +3592,12 @@ msgstr "" msgid "Pass True, if the animation should be sent as a spoiler" msgstr "" +#: of telebot.TeleBot.send_animation:63 telebot.TeleBot.send_audio:63 +#: telebot.TeleBot.send_document:56 telebot.TeleBot.send_video:64 +#: telebot.TeleBot.send_video_note:48 +msgid "Deprecated. Use thumbnail instead" +msgstr "" + #: of telebot.TeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -3973,6 +4142,10 @@ msgstr "" msgid "to disable the notification" msgstr "" +#: of telebot.TeleBot.send_sticker:39 +msgid "Emoji associated with the sticker; only for just uploaded stickers" +msgstr "" + #: of telebot.TeleBot.send_venue:1 msgid "" "Use this method to send information about a venue. On success, the sent " @@ -4076,6 +4249,22 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" msgstr "" +#: of telebot.TeleBot.send_video_note +msgid "param chat_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type chat_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note:7 +msgid ":obj:`int` or :obj:`str`" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param data" +msgstr "" + #: of telebot.TeleBot.send_video_note:9 msgid "" "Video note to send. Pass a file_id as String to send a video note that " @@ -4084,14 +4273,114 @@ msgid "" "unsupported" msgstr "" +#: of telebot.TeleBot.send_video_note +msgid "type data" +msgstr "" + +#: of telebot.TeleBot.send_video_note:11 telebot.TeleBot.send_video_note:37 +msgid ":obj:`str` or :class:`telebot.types.InputFile`" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param duration" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type duration" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param length" +msgstr "" + #: of telebot.TeleBot.send_video_note:16 msgid "Video width and height, i.e. diameter of the video message" msgstr "" +#: of telebot.TeleBot.send_video_note +msgid "type length" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param reply_to_message_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type reply_to_message_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param reply_markup" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type reply_markup" +msgstr "" + +#: of telebot.TeleBot.send_video_note:24 +msgid "" +":class:`telebot.types.InlineKeyboardMarkup` or " +":class:`telebot.types.ReplyKeyboardMarkup` or " +":class:`telebot.types.ReplyKeyboardRemove` or " +":class:`telebot.types.ForceReply`" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param disable_notification" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type disable_notification" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param timeout" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type timeout" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param thumbnail" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type thumbnail" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param allow_sending_without_reply" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type allow_sending_without_reply" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param protect_content" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type protect_content" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param message_thread_id" +msgstr "" + #: of telebot.TeleBot.send_video_note:45 msgid "Identifier of a message thread, in which the video note will be sent" msgstr "" +#: of telebot.TeleBot.send_video_note +msgid "type message_thread_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param thumb" +msgstr "" + #: of telebot.TeleBot.send_voice:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -4262,6 +4551,18 @@ msgstr "" msgid "New chat title, 1-255 characters" msgstr "" +#: of telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:1 +msgid "" +"Use this method to set the thumbnail of a custom emoji sticker set. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:7 +msgid "" +"Custom emoji identifier of a sticker from the sticker set; pass an empty " +"string to drop the thumbnail and use the first sticker as the thumbnail." +msgstr "" + #: of telebot.TeleBot.set_game_score:1 msgid "Sets the value of points in the game to a specific user." msgstr "" @@ -4331,6 +4632,77 @@ msgid "" "groups and supergroups will be changed." msgstr "" +#: of telebot.TeleBot.set_my_description:1 +msgid "" +"Use this method to change the bot's description, which is shown in the " +"chat with the bot if the chat is empty. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_my_description:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmydescription" +msgstr "" + +#: of telebot.TeleBot.set_my_description:7 +msgid "" +"New bot description; 0-512 characters. Pass an empty string to remove the" +" dedicated description for the given language." +msgstr "" + +#: of telebot.TeleBot.set_my_description:10 +msgid "" +"A two-letter ISO 639-1 language code. If empty, the description will be " +"applied to all users for whose language there is no dedicated " +"description." +msgstr "" + +#: of telebot.TeleBot.set_my_name:1 +msgid "Use this method to change the bot's name. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_my_name:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmyname" +msgstr "" + +#: of telebot.TeleBot.set_my_name:5 +msgid "" +"Optional. New bot name; 0-64 characters. Pass an empty string to remove " +"the dedicated name for the given language." +msgstr "" + +#: of telebot.TeleBot.set_my_name:8 +msgid "" +"Optional. A two-letter ISO 639-1 language code. If empty, the name will " +"be shown to all users for whose language there is no dedicated name." +msgstr "" + +#: of telebot.TeleBot.set_my_short_description:1 +msgid "" +"Use this method to change the bot's short description, which is shown on " +"the bot's profile page and is sent together with the link when users " +"share the bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_my_short_description:5 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmyshortdescription" +msgstr "" + +#: of telebot.TeleBot.set_my_short_description:7 +msgid "" +"New short description for the bot; 0-120 characters. Pass an empty string" +" to remove the dedicated short description for the given language." +msgstr "" + +#: of telebot.TeleBot.set_my_short_description:10 +msgid "" +"A two-letter ISO 639-1 language code. If empty, the short description " +"will be applied to all users for whose language there is no dedicated " +"short description." +msgstr "" + #: of telebot.TeleBot.set_state:1 msgid "Sets a new state of a user." msgstr "" @@ -4347,6 +4719,51 @@ msgstr "" msgid "new state. can be string, integer, or :class:`telebot.types.State`" msgstr "" +#: of telebot.TeleBot.set_sticker_emoji_list:1 +msgid "" +"Use this method to set the emoji list of a custom emoji sticker set. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_emoji_list:4 +msgid "Sticker identifier" +msgstr "" + +#: of telebot.TeleBot.set_sticker_emoji_list:7 +msgid "List of emoji" +msgstr "" + +#: of telebot.TeleBot.set_sticker_keywords:1 +msgid "" +"Use this method to change search keywords assigned to a regular or custom" +" emoji sticker. The sticker must belong to a sticker set created by the " +"bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_keywords:5 +#: telebot.TeleBot.set_sticker_mask_position:5 +msgid "File identifier of the sticker." +msgstr "" + +#: of telebot.TeleBot.set_sticker_keywords:8 +msgid "" +"A JSON-serialized list of 0-20 search keywords for the sticker with total" +" length of up to 64 characters" +msgstr "" + +#: of telebot.TeleBot.set_sticker_mask_position:1 +msgid "" +"Use this method to change the mask position of a mask sticker. The " +"sticker must belong to a sticker set that was created by the bot. Returns" +" True on success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_mask_position:8 +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces." +msgstr "" + #: of telebot.TeleBot.set_sticker_position_in_set:1 msgid "" "Use this method to move a sticker in a set created by the bot to a " @@ -4363,19 +4780,47 @@ msgstr "" msgid "New sticker position in the set, zero-based" msgstr "" -#: of telebot.TeleBot.set_sticker_set_thumb:1 +#: of telebot.TeleBot.set_sticker_set_thumbnail:1 msgid "" "Use this method to set the thumbnail of a sticker set. Animated " "thumbnails can be set for animated sticker sets only. Returns True on " "success." msgstr "" -#: of telebot.TeleBot.set_sticker_set_thumb:4 +#: of telebot.TeleBot.set_sticker_set_thumbnail:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickersetthumb" msgstr "" +#: of telebot.TeleBot.set_sticker_set_thumbnail:12 +msgid "" +"A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in " +"size and have a width and height of exactly 100px, or a .TGS animation " +"with a thumbnail up to 32 kilobytes in size (see " +"https://core.telegram.org/stickers#animated-sticker-requirements for " +"animated sticker technical requirements), or a WEBM video with the " +"thumbnail up to 32 kilobytes in size; see " +"https://core.telegram.org/stickers#video-sticker-requirements for video " +"sticker technical requirements. Pass a file_id as a String to send a file" +" that already exists on the Telegram servers, 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. More information on Sending Files ». Animated " +"and video sticker set thumbnails can't be uploaded via HTTP URL. If " +"omitted, then the thumbnail is dropped and the first sticker is used as " +"the thumbnail." +msgstr "" + +#: of telebot.TeleBot.set_sticker_set_title:1 +msgid "" +"Use this method to set the title of a created sticker set. Returns True " +"on success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_set_title:7 +msgid "New sticker set title" +msgstr "" + #: of telebot.TeleBot.set_update_listener:1 msgid "Sets a listener function to be called when a new update is received." msgstr "" @@ -4672,12 +5117,23 @@ msgstr "" #: of telebot.TeleBot.upload_sticker_file:9 msgid "" -"PNG image with the sticker, must be up to 512 kilobytes in size, " -"dimensions must not exceed 512px, and either width or height must be " -"exactly 512px." +"DEPRECATED: PNG image with the sticker, must be up to 512 kilobytes in " +"size, dimensions must not exceed 512px, and either width or height must " +"be exactly 512px." msgstr "" #: of telebot.TeleBot.upload_sticker_file:13 +msgid "" +"A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. See " +"https://core.telegram.org/stickers for technical requirements. More " +"information on Sending Files »" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:17 +msgid "One of \"static\", \"animated\", \"video\"." +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:20 msgid "On success, the sent file is returned." msgstr "" @@ -4923,3 +5379,28 @@ msgstr "" #~ msgid "The chat for which we want to handle new message." #~ msgstr "" +#~ msgid "" +#~ "Use this method to add a new " +#~ "sticker to a set created by the" +#~ " bot. It's required to pass " +#~ "`png_sticker` or `tgs_sticker`. Returns True" +#~ " on success." +#~ msgstr "" + +#~ msgid "" +#~ "Optional, Type of stickers in the " +#~ "set, pass “regular” or “mask”. Custom" +#~ " emoji sticker sets can't be created" +#~ " via the Bot API at the moment." +#~ " By default, a regular sticker set" +#~ " is created." +#~ msgstr "" + +#~ msgid "" +#~ "PNG image with the sticker, must " +#~ "be up to 512 kilobytes in size," +#~ " dimensions must not exceed 512px, " +#~ "and either width or height must be" +#~ " exactly 512px." +#~ msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/types.po b/docs/source/locales/en/LC_MESSAGES/types.po index db003a268..c846273fe 100644 --- a/docs/source/locales/en/LC_MESSAGES/types.po +++ b/docs/source/locales/en/LC_MESSAGES/types.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-02 19:24+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,9 +23,11 @@ msgid "Types of API" msgstr "" #: of telebot.types.Animation:1 telebot.types.Audio:1 -#: telebot.types.CallbackQuery:1 telebot.types.Chat:1 -#: telebot.types.ChatJoinRequest:1 telebot.types.ChatMember:1 -#: telebot.types.ChatMemberUpdated:1 telebot.types.ChatPhoto:1 +#: telebot.types.BotDescription:1 telebot.types.BotName:1 +#: telebot.types.BotShortDescription:1 telebot.types.CallbackQuery:1 +#: telebot.types.Chat:1 telebot.types.ChatJoinRequest:1 +#: telebot.types.ChatMember:1 telebot.types.ChatMemberUpdated:1 +#: telebot.types.ChatPhoto:1 telebot.types.ChatShared:1 #: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 #: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 #: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 @@ -41,8 +43,8 @@ msgstr "" #: telebot.types.ShippingQuery:1 telebot.types.Sticker:1 #: telebot.types.StickerSet:1 telebot.types.SuccessfulPayment:1 #: telebot.types.Update:1 telebot.types.UserProfilePhotos:1 -#: telebot.types.Venue:1 telebot.types.Video:1 telebot.types.VideoChatEnded:1 -#: telebot.types.VideoChatParticipantsInvited:1 +#: telebot.types.UserShared:1 telebot.types.Venue:1 telebot.types.Video:1 +#: telebot.types.VideoChatEnded:1 telebot.types.VideoChatParticipantsInvited:1 #: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 #: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 #: telebot.types.WriteAccessAllowed:1 @@ -66,14 +68,16 @@ msgstr "" #: telebot.types.BotCommandScopeChat #: telebot.types.BotCommandScopeChatAdministrators #: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault -#: telebot.types.CallbackQuery telebot.types.Chat -#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink -#: telebot.types.ChatJoinRequest telebot.types.ChatLocation -#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned -#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember -#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted -#: telebot.types.ChatMemberUpdated telebot.types.ChatPermissions -#: telebot.types.ChatPhoto telebot.types.ChosenInlineResult +#: telebot.types.BotDescription telebot.types.BotName +#: telebot.types.BotShortDescription telebot.types.CallbackQuery +#: telebot.types.Chat telebot.types.ChatAdministratorRights +#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest +#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator +#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft +#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner +#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChatShared telebot.types.ChosenInlineResult #: telebot.types.Contact telebot.types.Dice telebot.types.Document #: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic #: telebot.types.ForumTopicCreated telebot.types.ForumTopicEdited @@ -95,15 +99,17 @@ msgstr "" #: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation #: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto #: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo -#: telebot.types.InlineQueryResultVoice +#: telebot.types.InlineQueryResultVoice telebot.types.InlineQueryResultsButton #: telebot.types.InputContactMessageContent telebot.types.InputFile #: telebot.types.InputInvoiceMessageContent #: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation #: telebot.types.InputMediaAudio telebot.types.InputMediaDocument #: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo -#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent -#: telebot.types.Invoice telebot.types.KeyboardButton -#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.InputSticker telebot.types.InputTextMessageContent +#: telebot.types.InputVenueMessageContent telebot.types.Invoice +#: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType +#: telebot.types.KeyboardButtonRequestChat +#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice #: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message @@ -117,11 +123,13 @@ msgstr "" #: telebot.types.ShippingAddress telebot.types.ShippingOption #: telebot.types.ShippingOption.add_price telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet -#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User -#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat +#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos +#: telebot.types.UserShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +#: telebot.types.WriteAccessAllowed msgid "Parameters" msgstr "" @@ -183,14 +191,16 @@ msgstr "" #: telebot.types.BotCommandScopeChat #: telebot.types.BotCommandScopeChatAdministrators #: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault -#: telebot.types.CallbackQuery telebot.types.Chat -#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink -#: telebot.types.ChatJoinRequest telebot.types.ChatLocation -#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned -#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember -#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted -#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference -#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.BotDescription telebot.types.BotName +#: telebot.types.BotShortDescription telebot.types.CallbackQuery +#: telebot.types.Chat telebot.types.ChatAdministratorRights +#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest +#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator +#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft +#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner +#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated +#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChatShared #: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice #: telebot.types.Document telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game @@ -211,15 +221,17 @@ msgstr "" #: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation #: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto #: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo -#: telebot.types.InlineQueryResultVoice +#: telebot.types.InlineQueryResultVoice telebot.types.InlineQueryResultsButton #: telebot.types.InputContactMessageContent #: telebot.types.InputInvoiceMessageContent #: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation #: telebot.types.InputMediaAudio telebot.types.InputMediaDocument #: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo -#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent -#: telebot.types.Invoice telebot.types.KeyboardButton -#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.InputSticker telebot.types.InputTextMessageContent +#: telebot.types.InputVenueMessageContent telebot.types.Invoice +#: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType +#: telebot.types.KeyboardButtonRequestChat +#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice #: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message @@ -232,8 +244,9 @@ msgstr "" #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingOption.add_price #: telebot.types.ShippingQuery telebot.types.Sticker telebot.types.StickerSet -#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User -#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat +#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos +#: telebot.types.UserShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -248,20 +261,22 @@ msgstr "" #: telebot.types.BotCommandScopeChat:12 #: telebot.types.BotCommandScopeChatAdministrators:12 #: telebot.types.BotCommandScopeChatMember:15 -#: telebot.types.BotCommandScopeDefault:8 telebot.types.CallbackQuery:30 -#: telebot.types.Chat:109 telebot.types.ChatAdministratorRights:46 -#: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:20 -#: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:59 -#: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 -#: telebot.types.ChatMemberMember:11 telebot.types.ChatMemberOwner:17 -#: telebot.types.ChatMemberRestricted:47 telebot.types.ChatMemberUpdated:24 -#: telebot.types.ChatPermissions:38 telebot.types.ChatPhoto:21 +#: telebot.types.BotCommandScopeDefault:8 telebot.types.BotDescription:8 +#: telebot.types.BotName:8 telebot.types.BotShortDescription:8 +#: telebot.types.CallbackQuery:30 telebot.types.Chat:109 +#: telebot.types.ChatAdministratorRights:46 telebot.types.ChatInviteLink:34 +#: telebot.types.ChatJoinRequest:27 telebot.types.ChatLocation:11 +#: telebot.types.ChatMemberAdministrator:59 telebot.types.ChatMemberBanned:15 +#: telebot.types.ChatMemberLeft:11 telebot.types.ChatMemberMember:11 +#: telebot.types.ChatMemberOwner:17 telebot.types.ChatMemberRestricted:61 +#: telebot.types.ChatMemberUpdated:27 telebot.types.ChatPermissions:56 +#: telebot.types.ChatPhoto:21 telebot.types.ChatShared:15 #: telebot.types.ChosenInlineResult:21 telebot.types.Contact:22 #: telebot.types.Dice:11 telebot.types.Document:26 telebot.types.File:21 #: telebot.types.ForceReply:18 telebot.types.ForumTopic:17 #: telebot.types.ForumTopicCreated:14 telebot.types.Game:25 -#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:45 -#: telebot.types.InlineKeyboardMarkup:22 telebot.types.InlineQuery:26 +#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:49 +#: telebot.types.InlineKeyboardMarkup:28 telebot.types.InlineQuery:26 #: telebot.types.InlineQueryResultArticle:38 #: telebot.types.InlineQueryResultAudio:40 #: telebot.types.InlineQueryResultCachedAudio:31 @@ -281,31 +296,35 @@ msgstr "" #: telebot.types.InlineQueryResultVenue:51 #: telebot.types.InlineQueryResultVideo:53 #: telebot.types.InlineQueryResultVoice:37 +#: telebot.types.InlineQueryResultsButton:22 #: telebot.types.InputContactMessageContent:17 #: telebot.types.InputInvoiceMessageContent:75 #: telebot.types.InputLocationMessageContent:26 #: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:37 #: telebot.types.InputMediaDocument:32 telebot.types.InputMediaPhoto:24 -#: telebot.types.InputMediaVideo:43 telebot.types.InputTextMessageContent:19 +#: telebot.types.InputMediaVideo:43 telebot.types.InputSticker:18 +#: telebot.types.InputTextMessageContent:19 #: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 -#: telebot.types.KeyboardButton:25 telebot.types.KeyboardButtonPollType:9 -#: telebot.types.LabeledPrice:13 telebot.types.Location:25 -#: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 -#: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 -#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:246 -#: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 -#: telebot.types.MessageID:8 telebot.types.OrderInfo:17 -#: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:15 -#: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 -#: telebot.types.ProximityAlertTriggered:14 +#: telebot.types.KeyboardButton:33 telebot.types.KeyboardButtonPollType:9 +#: telebot.types.KeyboardButtonRequestChat:35 +#: telebot.types.KeyboardButtonRequestUser:18 telebot.types.LabeledPrice:13 +#: telebot.types.Location:25 telebot.types.LoginUrl:24 +#: telebot.types.MaskPosition:20 telebot.types.MenuButtonCommands:8 +#: telebot.types.MenuButtonDefault:8 telebot.types.MenuButtonWebApp:15 +#: telebot.types.Message:252 telebot.types.MessageAutoDeleteTimerChanged:8 +#: telebot.types.MessageEntity:32 telebot.types.MessageID:8 +#: telebot.types.OrderInfo:17 telebot.types.PhotoSize:21 telebot.types.Poll:47 +#: telebot.types.PollAnswer:15 telebot.types.PollOption:11 +#: telebot.types.PreCheckoutQuery:28 telebot.types.ProximityAlertTriggered:14 #: telebot.types.ReplyKeyboardMarkup:49 telebot.types.ReplyKeyboardRemove:18 #: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 #: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 -#: telebot.types.Sticker:49 telebot.types.StickerSet:30 -#: telebot.types.SuccessfulPayment:28 telebot.types.Update:61 +#: telebot.types.Sticker:54 telebot.types.StickerSet:30 +#: telebot.types.SuccessfulPayment:28 +#: telebot.types.SwitchInlineQueryChosenChat:22 telebot.types.Update:61 #: telebot.types.User:41 telebot.types.UserProfilePhotos:11 -#: telebot.types.Venue:27 telebot.types.Video:35 telebot.types.VideoChatEnded:8 -#: telebot.types.VideoChatParticipantsInvited:8 +#: telebot.types.UserShared:15 telebot.types.Venue:27 telebot.types.Video:35 +#: telebot.types.VideoChatEnded:8 telebot.types.VideoChatParticipantsInvited:8 #: telebot.types.VideoChatScheduled:9 telebot.types.VideoNote:24 #: telebot.types.Voice:23 telebot.types.WebAppData:12 #: telebot.types.WebAppInfo:8 telebot.types.WebhookInfo:37 @@ -320,14 +339,16 @@ msgstr "" #: telebot.types.BotCommandScopeChat #: telebot.types.BotCommandScopeChatAdministrators #: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault -#: telebot.types.CallbackQuery telebot.types.Chat -#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink -#: telebot.types.ChatJoinRequest telebot.types.ChatLocation -#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned -#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember -#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted -#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference -#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.BotDescription telebot.types.BotName +#: telebot.types.BotShortDescription telebot.types.CallbackQuery +#: telebot.types.Chat telebot.types.ChatAdministratorRights +#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest +#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator +#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft +#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner +#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated +#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChatShared #: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice #: telebot.types.Document telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game @@ -348,15 +369,17 @@ msgstr "" #: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation #: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto #: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo -#: telebot.types.InlineQueryResultVoice +#: telebot.types.InlineQueryResultVoice telebot.types.InlineQueryResultsButton #: telebot.types.InputContactMessageContent #: telebot.types.InputInvoiceMessageContent #: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation #: telebot.types.InputMediaAudio telebot.types.InputMediaDocument #: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo -#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent -#: telebot.types.Invoice telebot.types.KeyboardButton -#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.InputSticker telebot.types.InputTextMessageContent +#: telebot.types.InputVenueMessageContent telebot.types.Invoice +#: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType +#: telebot.types.KeyboardButtonRequestChat +#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice #: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message @@ -369,8 +392,9 @@ msgstr "" #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet -#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User -#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat +#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos +#: telebot.types.UserShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -703,6 +727,56 @@ msgstr "" msgid ":class:`telebot.types.BotCommandScopeDefault`" msgstr "" +#: of telebot.types.BotDescription:1 +msgid "This object represents a bot description." +msgstr "" + +#: of telebot.types.BotDescription:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#botdescription" +msgstr "" + +#: of telebot.types.BotDescription:5 +msgid "Bot description" +msgstr "" + +#: of telebot.types.BotDescription:9 +msgid ":class:`telebot.types.BotDescription`" +msgstr "" + +#: of telebot.types.BotName:1 +msgid "This object represents a bot name." +msgstr "" + +#: of telebot.types.BotName:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#botname" +msgstr "" + +#: of telebot.types.BotName:5 +msgid "The bot name" +msgstr "" + +#: of telebot.types.BotName:9 +msgid ":class:`BotName`" +msgstr "" + +#: of telebot.types.BotShortDescription:1 +msgid "This object represents a bot short description." +msgstr "" + +#: of telebot.types.BotShortDescription:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#botshortdescription" +msgstr "" + +#: of telebot.types.BotShortDescription:5 +msgid "Bot short description" +msgstr "" + +#: of telebot.types.BotShortDescription:9 +msgid ":class:`telebot.types.BotShortDescription`" +msgstr "" + #: of telebot.types.CallbackQuery:1 msgid "" "This object represents an incoming callback query from a callback button " @@ -1120,20 +1194,31 @@ msgid "User that sent the join request" msgstr "" #: of telebot.types.ChatJoinRequest:11 +msgid "" +"Optional. Identifier of a private chat with the user who sent the join " +"request. This number may have more than 32 significant bits and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it has at most 52 significant bits, so a 64-bit integer or " +"double-precision float type are safe for storing this identifier. The bot" +" can use this identifier for 24 hours to send messages until the join " +"request is processed, assuming no other administrator contacted the user." +msgstr "" + +#: of telebot.types.ChatJoinRequest:18 msgid "Date the request was sent in Unix time" msgstr "" -#: of telebot.types.ChatJoinRequest:14 +#: of telebot.types.ChatJoinRequest:21 msgid "Optional. Bio of the user." msgstr "" -#: of telebot.types.ChatJoinRequest:17 +#: of telebot.types.ChatJoinRequest:24 msgid "" "Optional. Chat invite link that was used by the user to send the join " "request" msgstr "" -#: of telebot.types.ChatJoinRequest:21 +#: of telebot.types.ChatJoinRequest:28 msgid ":class:`telebot.types.ChatJoinRequest`" msgstr "" @@ -1177,7 +1262,7 @@ msgstr "" msgid ":class:`telebot.types.ChatMemberMember`" msgstr "" -#: of telebot.types.ChatMember:7 telebot.types.ChatMemberRestricted:48 +#: of telebot.types.ChatMember:7 telebot.types.ChatMemberRestricted:62 msgid ":class:`telebot.types.ChatMemberRestricted`" msgstr "" @@ -1328,26 +1413,44 @@ msgid "" msgstr "" #: of telebot.types.ChatMemberRestricted:29 -msgid "" -"True, if the user is allowed to send audios, documents, photos, videos, " -"video notes and voice notes" +msgid "True, if the user is allowed to send audios" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:32 +msgid "True, if the user is allowed to send documents" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:35 +msgid "True, if the user is allowed to send photos" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:38 +msgid "True, if the user is allowed to send videos" msgstr "" -#: of telebot.types.ChatMemberRestricted:33 +#: of telebot.types.ChatMemberRestricted:41 +msgid "True, if the user is allowed to send video notes" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:44 +msgid "True, if the user is allowed to send voice notes" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:47 msgid "True, if the user is allowed to send polls" msgstr "" -#: of telebot.types.ChatMemberRestricted:36 +#: of telebot.types.ChatMemberRestricted:50 msgid "" "True, if the user is allowed to send animations, games, stickers and use " "inline bots" msgstr "" -#: of telebot.types.ChatMemberRestricted:40 +#: of telebot.types.ChatMemberRestricted:54 msgid "True, if the user is allowed to add web page previews to their messages" msgstr "" -#: of telebot.types.ChatMemberRestricted:43 +#: of telebot.types.ChatMemberRestricted:57 msgid "" "Date when restrictions will be lifted for this user; unix time. If 0, " "then the user is restricted forever" @@ -1389,7 +1492,11 @@ msgid "" "for joining by invite link events only." msgstr "" -#: of telebot.types.ChatMemberUpdated:25 +#: of telebot.types.ChatMemberUpdated:24 +msgid "Optional. True, if the user joined the chat via a chat folder invite link" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:28 msgid ":class:`telebot.types.ChatMemberUpdated`" msgstr "" @@ -1421,52 +1528,76 @@ msgid "" msgstr "" #: of telebot.types.ChatPermissions:9 -msgid "" -"Optional. True, if the user is allowed to send audios, documents, photos," -" videos, video notes and voice notes, implies can_send_messages" +msgid "Optional. True, if the user is allowed to send audios" msgstr "" -#: of telebot.types.ChatPermissions:13 +#: of telebot.types.ChatPermissions:12 +msgid "Optional. True, if the user is allowed to send documents" +msgstr "" + +#: of telebot.types.ChatPermissions:15 +msgid "Optional. True, if the user is allowed to send photos" +msgstr "" + +#: of telebot.types.ChatPermissions:18 +msgid "Optional. True, if the user is allowed to send videos" +msgstr "" + +#: of telebot.types.ChatPermissions:21 +msgid "Optional. True, if the user is allowed to send video notes" +msgstr "" + +#: of telebot.types.ChatPermissions:24 +msgid "Optional. True, if the user is allowed to send voice notes" +msgstr "" + +#: of telebot.types.ChatPermissions:27 msgid "" "Optional. True, if the user is allowed to send polls, implies " "can_send_messages" msgstr "" -#: of telebot.types.ChatPermissions:16 +#: of telebot.types.ChatPermissions:30 msgid "" "Optional. True, if the user is allowed to send animations, games, " -"stickers and use inline bots, implies can_send_media_messages" +"stickers and use inline bots" msgstr "" -#: of telebot.types.ChatPermissions:20 +#: of telebot.types.ChatPermissions:34 msgid "" "Optional. True, if the user is allowed to add web page previews to their " -"messages, implies can_send_media_messages" +"messages" msgstr "" -#: of telebot.types.ChatPermissions:24 +#: of telebot.types.ChatPermissions:38 msgid "" "Optional. True, if the user is allowed to change the chat title, photo " "and other settings. Ignored in public supergroups" msgstr "" -#: of telebot.types.ChatPermissions:28 +#: of telebot.types.ChatPermissions:42 msgid "Optional. True, if the user is allowed to invite new users to the chat" msgstr "" -#: of telebot.types.ChatPermissions:31 +#: of telebot.types.ChatPermissions:45 msgid "" "Optional. True, if the user is allowed to pin messages. Ignored in public" " supergroups" msgstr "" -#: of telebot.types.ChatPermissions:34 +#: of telebot.types.ChatPermissions:48 msgid "" "Optional. True, if the user is allowed to create forum topics. If omitted" " defaults to the value of can_pin_messages" msgstr "" -#: of telebot.types.ChatPermissions:39 +#: of telebot.types.ChatPermissions:52 +msgid "" +"deprecated. True, if the user is allowed to send audios, documents, " +"photos, videos, video notes and voice notes" +msgstr "" + +#: of telebot.types.ChatPermissions:57 msgid ":class:`telebot.types.ChatPermissions`" msgstr "" @@ -1508,6 +1639,36 @@ msgstr "" msgid ":class:`telebot.types.ChatPhoto`" msgstr "" +#: of telebot.types.ChatShared:1 +msgid "" +"This object contains information about the chat whose identifier was " +"shared with the bot using a `telebot.types.KeyboardButtonRequestChat` " +"button." +msgstr "" + +#: of telebot.types.ChatShared:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#Chatshared" +msgstr "" + +#: of telebot.types.ChatShared:6 telebot.types.UserShared:6 +msgid "identifier of the request" +msgstr "" + +#: of telebot.types.ChatShared:9 +msgid "" +"Identifier of the shared chat. This number may have more than 32 " +"significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier. The bot may not have access to the chat" +" and could be unable to use this identifier, unless the chat is already " +"known to the bot by some other means." +msgstr "" + +#: of telebot.types.ChatShared:16 +msgid ":class:`telebot.types.ChatShared`" +msgstr "" + #: of telebot.types.ChosenInlineResult:1 msgid "" "Represents a result of an inline query that was chosen by the user and " @@ -1915,12 +2076,14 @@ msgid "" msgstr "" #: of telebot.types.InlineKeyboardButton:3 +#: telebot.types.SwitchInlineQueryChosenChat:4 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#inlinekeyboardbutton" msgstr "" #: of telebot.types.InlineKeyboardButton:5 +#: telebot.types.InlineQueryResultsButton:6 msgid "Label text on the button" msgstr "" @@ -1974,19 +2137,26 @@ msgstr "" #: of telebot.types.InlineKeyboardButton:37 msgid "" +"Optional. If set, pressing the button will prompt the user to select one " +"of their chats of the specified type, open that chat and insert the bot's" +" username and the specified inline query in the input field" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:41 +msgid "" "Optional. Description of the game that will be launched when the user " "presses the button. NOTE: This type of button must always be the first " "button in the first row." msgstr "" -#: of telebot.types.InlineKeyboardButton:41 +#: of telebot.types.InlineKeyboardButton:45 msgid "" "Optional. Specify True, to send a Pay button. NOTE: This type of button " "must always be the first button in the first row and can only be used in " "invoice messages." msgstr "" -#: of telebot.types.InlineKeyboardButton:46 +#: of telebot.types.InlineKeyboardButton:50 msgid ":class:`telebot.types.InlineKeyboardButton`" msgstr "" @@ -2004,19 +2174,23 @@ msgstr "" msgid "Example of a custom keyboard with buttons." msgstr "" -#: of telebot.types.InlineKeyboardMarkup:16 +#: of telebot.types.InlineKeyboardMarkup:19 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#inlinekeyboardmarkup" msgstr "" -#: of telebot.types.InlineKeyboardMarkup:18 +#: of telebot.types.InlineKeyboardMarkup:21 msgid "" ":obj:`list` of button rows, each represented by an :obj:`list` of " ":class:`telebot.types.InlineKeyboardButton` objects" msgstr "" -#: of telebot.types.InlineKeyboardMarkup:23 +#: of telebot.types.InlineKeyboardMarkup:25 +msgid "number of :class:`telebot.types.InlineKeyboardButton` objects on each row" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:29 #: telebot.types.InlineKeyboardMarkup.add:17 #: telebot.types.InlineKeyboardMarkup.row:12 msgid ":class:`telebot.types.InlineKeyboardMarkup`" @@ -3165,12 +3339,58 @@ msgstr "" msgid ":class:`telebot.types.InlineQueryResultVoice`" msgstr "" +#: of telebot.types.InlineQueryResultsButton:1 telebot.types.LabeledPrice:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:1 +msgid "" +"This object represents a button to be shown above inline query results. " +"You must use exactly one of the optional fields." +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultsbutton" +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:9 +msgid "" +"Optional. Description of the Web App that will be launched when the user " +"presses the button. The Web App will be able to switch back to the inline" +" mode using the method web_app_switch_inline_query inside the Web App." +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:13 +msgid "" +"Optional. Deep-linking parameter for the /start message sent to the bot " +"when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ " +"and - are allowed. Example: An inline bot that sends YouTube videos can " +"ask the user to connect the bot to their YouTube account to adapt search " +"results accordingly. To do this, it displays a 'Connect your YouTube " +"account' button above the results, or even before showing any. The user " +"presses the button, switches to a private chat with the bot and, in doing" +" so, passes a start parameter that instructs the bot to return an OAuth " +"link. Once done, the bot can offer a switch_inline button so that the " +"user can easily return to the chat where they wanted to use the bot's " +"inline capabilities." +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:23 +msgid ":class:`InlineQueryResultsButton`" +msgstr "" + #: of telebot.types.InputContactMessageContent:1 #: telebot.types.InputInvoiceMessageContent:1 #: telebot.types.InputLocationMessageContent:1 #: telebot.types.InputTextMessageContent:1 #: telebot.types.InputVenueMessageContent:1 #: telebot.types.KeyboardButtonPollType:1 +#: telebot.types.KeyboardButtonRequestChat:1 +#: telebot.types.KeyboardButtonRequestUser:1 msgid "Bases: :py:class:`telebot.types.Dictionaryable`" msgstr "" @@ -3367,7 +3587,8 @@ msgstr "" msgid ":class:`telebot.types.InputLocationMessageContent`" msgstr "" -#: of telebot.types.InputMedia:1 telebot.types.KeyboardButton:1 +#: of telebot.types.InputMedia:1 telebot.types.InputSticker:1 +#: telebot.types.KeyboardButton:1 msgid "" "Bases: :py:class:`telebot.types.Dictionaryable`, " ":py:class:`telebot.types.JsonSerializable`" @@ -3560,6 +3781,39 @@ msgstr "" msgid ":class:`telebot.types.InputMediaVideo`" msgstr "" +#: of telebot.types.InputSticker:1 +msgid "This object describes a sticker to be added to a sticker set." +msgstr "" + +#: of telebot.types.InputSticker:3 +msgid "" +"The added sticker. Pass a file_id as a String to send a file that already" +" exists on the Telegram servers, 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. Animated and video stickers can't be uploaded via " +"HTTP URL." +msgstr "" + +#: of telebot.types.InputSticker:8 +msgid "One or more(up to 20) emoji(s) corresponding to the sticker" +msgstr "" + +#: of telebot.types.InputSticker:11 +msgid "" +"Optional. Position where the mask should be placed on faces. For “mask” " +"stickers only." +msgstr "" + +#: of telebot.types.InputSticker:14 +msgid "" +"Optional. List of 0-20 search keywords for the sticker with total length " +"of up to 64 characters. For “regular” and “custom_emoji” stickers only." +msgstr "" + +#: of telebot.types.InputSticker:19 +msgid ":class:`telebot.types.InputSticker`" +msgstr "" + #: of telebot.types.InputTextMessageContent:1 msgid "" "Represents the content of a text message to be sent as the result of an " @@ -3726,7 +3980,21 @@ msgid "" "service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:26 +#: of telebot.types.KeyboardButton:25 +msgid "" +"Optional. If specified, pressing the button will open a list of suitable " +"users. Tapping on any user will send their identifier to the bot in a " +"“user_shared” service message. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:29 +msgid "" +"Optional. If specified, pressing the button will open a list of suitable " +"chats. Tapping on a chat will send its identifier to the bot in a " +"“chat_shared” service message. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:34 msgid ":class:`telebot.types.KeyboardButton`" msgstr "" @@ -3754,10 +4022,111 @@ msgstr "" msgid ":class:`telebot.types.KeyboardButtonPollType`" msgstr "" -#: of telebot.types.LabeledPrice:1 +#: of telebot.types.KeyboardButtonRequestChat:1 msgid "" -"Bases: :py:class:`telebot.types.JsonSerializable`, " -":py:class:`telebot.types.Dictionaryable`" +"This object defines the criteria used to request a suitable chat. The " +"identifier of the selected chat will be shared with the bot when the " +"corresponding button is pressed." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#keyboardbuttonrequestchat" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:6 +msgid "" +"Signed 32-bit identifier of the request, which will be received back in " +"the ChatShared object. Must be unique within the message" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:10 +msgid "" +"Pass True to request a channel chat, pass False to request a group or a " +"supergroup chat." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:13 +msgid "" +"Optional. Pass True to request a forum supergroup, pass False to request " +"a non-forum chat. If not specified, no additional restrictions are " +"applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:17 +msgid "" +"Optional. Pass True to request a supergroup or a channel with a username," +" pass False to request a chat without a username. If not specified, no " +"additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:21 +msgid "" +"Optional. Pass True to request a chat owned by the user. Otherwise, no " +"additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:24 +msgid "" +"Optional. A JSON-serialized object listing the required administrator " +"rights of the user in the chat. The rights must be a superset of " +"bot_administrator_rights. If not specified, no additional restrictions " +"are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:28 +msgid "" +"Optional. A JSON-serialized object listing the required administrator " +"rights of the bot in the chat. The rights must be a subset of " +"user_administrator_rights. If not specified, no additional restrictions " +"are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:32 +msgid "" +"Optional. Pass True to request a chat where the bot is a member. " +"Otherwise, no additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:36 +msgid ":class:`telebot.types.KeyboardButtonRequestChat`" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:1 +msgid "" +"This object defines the criteria used to request a suitable user. The " +"identifier of the selected user will be shared with the bot when the " +"corresponding button is pressed." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#keyboardbuttonrequestuser" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:6 +msgid "" +"Signed 32-bit identifier of the request, which will be received back in " +"the UserShared object. Must be unique within the message" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:10 +msgid "" +"Optional. Pass True to request a bot, pass False to request a regular " +"user. If not specified, no additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:14 +msgid "" +"Optional. Pass True to request a premium user, pass False to request a " +"non-premium user. If not specified, no additional restrictions are " +"applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:19 +msgid ":class:`telebot.types.KeyboardButtonRequestUser`" msgstr "" #: of telebot.types.LabeledPrice:1 @@ -4306,78 +4675,86 @@ msgid "" msgstr "" #: of telebot.types.Message:194 +msgid "Optional. Service message: a user was shared with the bot" +msgstr "" + +#: of telebot.types.Message:197 +msgid "Optional. Service message: a chat was shared with the bot" +msgstr "" + +#: of telebot.types.Message:200 msgid "" "Optional. The domain name of the website on which the user has logged in." " More about Telegram Login »" msgstr "" -#: of telebot.types.Message:198 +#: of telebot.types.Message:204 msgid "" "Optional. Service message: the user allowed the bot added to the " "attachment menu to write messages" msgstr "" -#: of telebot.types.Message:202 +#: of telebot.types.Message:208 msgid "Optional. Telegram Passport data" msgstr "" -#: of telebot.types.Message:205 +#: of telebot.types.Message:211 msgid "" "Optional. Service message. A user in the chat triggered another user's " "proximity alert while sharing Live Location." msgstr "" -#: of telebot.types.Message:209 +#: of telebot.types.Message:215 msgid "Optional. Service message: forum topic created" msgstr "" -#: of telebot.types.Message:212 +#: of telebot.types.Message:218 msgid "Optional. Service message: forum topic edited" msgstr "" -#: of telebot.types.Message:215 +#: of telebot.types.Message:221 msgid "Optional. Service message: forum topic closed" msgstr "" -#: of telebot.types.Message:218 +#: of telebot.types.Message:224 msgid "Optional. Service message: forum topic reopened" msgstr "" -#: of telebot.types.Message:221 +#: of telebot.types.Message:227 msgid "Optional. Service message: the 'General' forum topic hidden" msgstr "" -#: of telebot.types.Message:224 +#: of telebot.types.Message:230 msgid "Optional. Service message: the 'General' forum topic unhidden" msgstr "" -#: of telebot.types.Message:227 +#: of telebot.types.Message:233 msgid "Optional. Service message: video chat scheduled" msgstr "" -#: of telebot.types.Message:230 +#: of telebot.types.Message:236 msgid "Optional. Service message: video chat started" msgstr "" -#: of telebot.types.Message:233 +#: of telebot.types.Message:239 msgid "Optional. Service message: video chat ended" msgstr "" -#: of telebot.types.Message:236 +#: of telebot.types.Message:242 msgid "Optional. Service message: new participants invited to a video chat" msgstr "" -#: of telebot.types.Message:239 +#: of telebot.types.Message:245 msgid "Optional. Service message: data sent by a Web App" msgstr "" -#: of telebot.types.Message:242 +#: of telebot.types.Message:248 msgid "" "Optional. Inline keyboard attached to the message. login_url buttons are " "represented as ordinary url buttons." msgstr "" -#: of telebot.types.Message:247 +#: of telebot.types.Message:253 msgid ":class:`telebot.types.Message`" msgstr "" @@ -4540,7 +4917,7 @@ msgstr "" msgid "Photo height" msgstr "" -#: of telebot.types.PhotoSize:18 telebot.types.Sticker:46 +#: of telebot.types.PhotoSize:18 telebot.types.Sticker:51 #: telebot.types.VideoNote:21 msgid "Optional. File size in bytes" msgstr "" @@ -5058,7 +5435,14 @@ msgstr "" msgid "Optional. For custom emoji stickers, unique identifier of the custom emoji" msgstr "" -#: of telebot.types.Sticker:50 +#: of telebot.types.Sticker:46 +msgid "" +"Optional. True, if the sticker must be repainted to a text color in " +"messages, the color of the Telegram Premium badge in emoji status, white " +"color on chat photos, or another appropriate color in other places" +msgstr "" + +#: of telebot.types.Sticker:55 msgid ":class:`telebot.types.Sticker`" msgstr "" @@ -5136,6 +5520,45 @@ msgstr "" msgid ":class:`telebot.types.SuccessfulPayment`" msgstr "" +#: of telebot.types.SwitchInlineQueryChosenChat:1 telebot.types.User:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:1 +msgid "" +"Represents an inline button that switches the current user to inline mode" +" in a chosen chat, with an optional default inline query." +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:6 +msgid "" +"Optional. The default inline query to be inserted in the input field. If " +"left empty, only the bot's username will be inserted" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:10 +msgid "Optional. True, if private chats with users can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:13 +msgid "Optional. True, if private chats with bots can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:16 +msgid "Optional. True, if group and supergroup chats can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:19 +msgid "Optional. True, if channel chats can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:23 +msgid ":class:`SwitchInlineQueryChosenChat`" +msgstr "" + #: of telebot.types.Update:1 msgid "" "This object represents an incoming update.At most one of the optional " @@ -5240,13 +5663,6 @@ msgstr "" msgid ":class:`telebot.types.Update`" msgstr "" -#: of telebot.types.User:1 -msgid "" -"Bases: :py:class:`telebot.types.JsonDeserializable`, " -":py:class:`telebot.types.Dictionaryable`, " -":py:class:`telebot.types.JsonSerializable`" -msgstr "" - #: of telebot.types.User:1 msgid "This object represents a Telegram user or bot." msgstr "" @@ -5348,6 +5764,32 @@ msgstr "" msgid ":class:`telebot.types.UserProfilePhotos`" msgstr "" +#: of telebot.types.UserShared:1 +msgid "" +"This object contains information about the user whose identifier was " +"shared with the bot using a `telebot.types.KeyboardButtonRequestUser` " +"button." +msgstr "" + +#: of telebot.types.UserShared:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#usershared" +msgstr "" + +#: of telebot.types.UserShared:9 +msgid "" +"Identifier of the shared user. This number may have more than 32 " +"significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier. The bot may not have access to the user" +" and could be unable to use this identifier, unless the user is already " +"known to the bot by some other means." +msgstr "" + +#: of telebot.types.UserShared:16 +msgid ":class:`telebot.types.UserShared`" +msgstr "" + #: of telebot.types.Venue:1 msgid "This object represents a venue." msgstr "" @@ -5633,6 +6075,37 @@ msgid "" "https://core.telegram.org/bots/api#writeaccessallowed" msgstr "" +#: of telebot.types.WriteAccessAllowed:6 +msgid "Optional. Name of the Web App which was launched from a link" +msgstr "" + #~ msgid "Type of the result, must be animation" #~ msgstr "" +#~ msgid "" +#~ "True, if the user is allowed to" +#~ " send audios, documents, photos, videos," +#~ " video notes and voice notes" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. True, if the user is " +#~ "allowed to send audios, documents, " +#~ "photos, videos, video notes and voice" +#~ " notes, implies can_send_messages" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. True, if the user is " +#~ "allowed to send animations, games, " +#~ "stickers and use inline bots, implies" +#~ " can_send_media_messages" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. True, if the user is " +#~ "allowed to add web page previews " +#~ "to their messages, implies " +#~ "can_send_media_messages" +#~ msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/util.po b/docs/source/locales/en/LC_MESSAGES/util.po index afba83905..0d5f4b500 100644 --- a/docs/source/locales/en/LC_MESSAGES/util.po +++ b/docs/source/locales/en/LC_MESSAGES/util.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,19 +18,19 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../util.rst:3 +#: ../../source/util.rst:3 msgid "Utils" msgstr "" -#: ../../util.rst:5 +#: ../../source/util.rst:5 msgid "Utils in pyTelegramBotAPI" msgstr "" -#: ../../util.rst:5 +#: ../../source/util.rst:5 msgid "ptba, pytba, pyTelegramBotAPI, utils, guide" msgstr "" -#: ../../util.rst:11 +#: ../../source/util.rst:11 msgid "util file" msgstr "" @@ -40,9 +40,10 @@ msgid "" "error. Example:" msgstr "" -#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments -#: telebot.util.extract_command telebot.util.is_bytes telebot.util.is_command -#: telebot.util.is_dict telebot.util.is_pil_image +#: of telebot.service_utils.is_bytes telebot.service_utils.is_dict +#: telebot.service_utils.is_pil_image telebot.util.antiflood +#: telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_command #: telebot.util.parse_web_app_data telebot.util.quick_markup #: telebot.util.smart_split telebot.util.split_string telebot.util.user_link #: telebot.util.validate_web_app_data telebot.util.webhook_google_functions @@ -54,28 +55,33 @@ msgid "The function to call" msgstr "" #: of telebot.util.antiflood:13 -msgid "The arguments to pass to the function" +msgid "Number of retries to send" msgstr "" #: of telebot.util.antiflood:16 +msgid "The arguments to pass to the function" +msgstr "" + +#: of telebot.util.antiflood:19 msgid "The keyword arguments to pass to the function" msgstr "" -#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments -#: telebot.util.extract_command telebot.util.generate_random_token -#: telebot.util.is_bytes telebot.util.is_command telebot.util.is_dict -#: telebot.util.is_pil_image telebot.util.parse_web_app_data -#: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string -#: telebot.util.user_link telebot.util.validate_web_app_data -#: telebot.util.webhook_google_functions +#: of telebot.service_utils.generate_random_token +#: telebot.service_utils.is_bytes telebot.service_utils.is_dict +#: telebot.service_utils.is_pil_image telebot.util.antiflood +#: telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_command +#: telebot.util.parse_web_app_data telebot.util.quick_markup +#: telebot.util.smart_split telebot.util.split_string telebot.util.user_link +#: telebot.util.validate_web_app_data telebot.util.webhook_google_functions msgid "Returns" msgstr "" -#: of telebot.util.antiflood:19 +#: of telebot.util.antiflood:22 msgid "None" msgstr "" -#: of telebot.util.chunks:1 +#: of telebot.service_utils.chunks:1 msgid "Yield successive n-sized chunks from lst." msgstr "" @@ -117,9 +123,10 @@ msgstr "" msgid "the arguments if `text` is a command (according to is_command), else None." msgstr "" -#: of telebot.util.extract_arguments telebot.util.extract_command -#: telebot.util.generate_random_token telebot.util.is_bytes -#: telebot.util.is_command telebot.util.is_dict telebot.util.is_pil_image +#: of telebot.service_utils.generate_random_token +#: telebot.service_utils.is_bytes telebot.service_utils.is_dict +#: telebot.service_utils.is_pil_image telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_command #: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string #: telebot.util.user_link msgid "Return type" @@ -143,35 +150,35 @@ msgstr "" msgid "the command if `text` is a command (according to is_command), else None." msgstr "" -#: of telebot.util.generate_random_token:1 +#: of telebot.service_utils.generate_random_token:1 msgid "" "Generates a random token consisting of letters and digits, 16 characters " "long." msgstr "" -#: of telebot.util.generate_random_token:3 +#: of telebot.service_utils.generate_random_token:3 msgid "a random token" msgstr "" -#: of telebot.util.generate_random_token:4 telebot.util.user_link:22 +#: of telebot.service_utils.generate_random_token:4 telebot.util.user_link:22 msgid ":obj:`str`" msgstr "" -#: of telebot.util.is_bytes:1 +#: of telebot.service_utils.is_bytes:1 msgid "Returns True if the given object is a bytes object." msgstr "" -#: of telebot.util.is_bytes:3 telebot.util.is_dict:3 -#: telebot.util.is_pil_image:3 +#: of telebot.service_utils.is_bytes:3 telebot.service_utils.is_dict:3 +#: telebot.service_utils.is_pil_image:3 msgid "object to be checked" msgstr "" -#: of telebot.util.is_bytes:6 +#: of telebot.service_utils.is_bytes:6 msgid "True if the given object is a bytes object." msgstr "" -#: of telebot.util.is_bytes:7 telebot.util.is_command:7 telebot.util.is_dict:7 -#: telebot.util.is_pil_image:7 +#: of telebot.service_utils.is_bytes:7 telebot.service_utils.is_dict:7 +#: telebot.service_utils.is_pil_image:7 telebot.util.is_command:7 msgid ":obj:`bool`" msgstr "" @@ -189,23 +196,23 @@ msgstr "" msgid "True if `text` is a command, else False." msgstr "" -#: of telebot.util.is_dict:1 +#: of telebot.service_utils.is_dict:1 msgid "Returns True if the given object is a dictionary." msgstr "" -#: of telebot.util.is_dict:6 +#: of telebot.service_utils.is_dict:6 msgid "True if the given object is a dictionary." msgstr "" -#: of telebot.util.is_pil_image:1 +#: of telebot.service_utils.is_pil_image:1 msgid "Returns True if the given object is a PIL.Image.Image object." msgstr "" -#: of telebot.util.is_pil_image:6 +#: of telebot.service_utils.is_pil_image:6 msgid "True if the given object is a PIL.Image.Image object." msgstr "" -#: of telebot.util.is_string:1 +#: of telebot.service_utils.is_string:1 msgid "Returns True if the given object is a string." msgstr "" @@ -240,21 +247,21 @@ msgstr "" msgid "Using quick_markup:" msgstr "" -#: of telebot.util.quick_markup:29 +#: of telebot.util.quick_markup:31 msgid "" "a dict containing all buttons to create in this format: {text: kwargs} " "{str:}" msgstr "" -#: of telebot.util.quick_markup:32 -msgid "int row width" +#: of telebot.util.quick_markup:34 +msgid "number of :class:`telebot.types.InlineKeyboardButton` objects on each row" msgstr "" -#: of telebot.util.quick_markup:35 +#: of telebot.util.quick_markup:37 msgid "InlineKeyboardMarkup" msgstr "" -#: of telebot.util.quick_markup:36 +#: of telebot.util.quick_markup:38 msgid ":obj:`types.InlineKeyboardMarkup`" msgstr "" @@ -343,3 +350,6 @@ msgstr "" msgid "The response object" msgstr "" +#~ msgid "int row width" +#~ msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index 86d86bfb8..8644f09fb 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -4,13 +4,12 @@ # Documentation package. # FIRST AUTHOR , 2022. # - #, fuzzy msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-02 19:24+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -108,6 +107,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_webhook #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link @@ -134,6 +134,9 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.get_game_high_scores #: telebot.async_telebot.AsyncTeleBot.get_my_commands #: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_my_description +#: telebot.async_telebot.AsyncTeleBot.get_my_name +#: telebot.async_telebot.AsyncTeleBot.get_my_short_description #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates @@ -199,12 +202,20 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.set_chat_photo #: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_my_description +#: telebot.async_telebot.AsyncTeleBot.set_my_name +#: telebot.async_telebot.AsyncTeleBot.set_my_short_description #: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title #: telebot.async_telebot.AsyncTeleBot.set_update_listener #: telebot.async_telebot.AsyncTeleBot.set_webhook #: telebot.async_telebot.AsyncTeleBot.setup_middleware @@ -223,7 +234,6 @@ msgid "Parameters" msgstr "" #: of telebot.async_telebot.AsyncTeleBot:24 - msgid "Token of a bot, obtained from @BotFather" msgstr "Токен бота, нужно получить от @BotFather" @@ -308,6 +318,7 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_webhook #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link @@ -333,6 +344,9 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.get_game_high_scores #: telebot.async_telebot.AsyncTeleBot.get_my_commands #: telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.get_my_description +#: telebot.async_telebot.AsyncTeleBot.get_my_name +#: telebot.async_telebot.AsyncTeleBot.get_my_short_description #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates @@ -396,12 +410,20 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.set_chat_photo #: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_my_description +#: telebot.async_telebot.AsyncTeleBot.set_my_name +#: telebot.async_telebot.AsyncTeleBot.set_my_short_description #: telebot.async_telebot.AsyncTeleBot.set_state +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title #: telebot.async_telebot.AsyncTeleBot.set_update_listener #: telebot.async_telebot.AsyncTeleBot.set_webhook #: telebot.async_telebot.AsyncTeleBot.setup_middleware @@ -481,36 +503,45 @@ msgstr "Данные для добавления" #: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:1 msgid "" -"Use this method to add a new sticker to a set created by the bot. It's " -"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +"Use this method to add a new sticker to a set created by the bot. The " +"format of the added sticker must match the format of the other stickers " +"in the set. Emoji sticker sets can have up to 200 stickers. Animated and " +"video sticker sets can have up to 50 stickers. Static sticker sets can " +"have up to 120 stickers. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:8 +msgid "" +"**_sticker, mask_position, emojis parameters are deprecated, use stickers" +" instead" msgstr "" -"Используйте этот метод, чтобы добавить новый стикер в стикерпак, " -"созданный ботом. Необходимо передать либо `png_sticker`, либо " -"`tgs_sticker`, либо `webm_sticker`. Возвращает True в случае успешного " -"добавления." -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:5 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:10 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" msgstr "Документация Telegram: https://core.telegram.org/bots/api#addstickertoset" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:7 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:7 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:12 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:10 msgid "User identifier of created sticker set owner" msgstr "id пользователя, создавшего стикерпак" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:10 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:15 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:3 #: telebot.async_telebot.AsyncTeleBot.get_sticker_set:5 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:6 +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:4 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:4 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:6 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:4 msgid "Sticker set name" msgstr "Имя стикерпака" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:13 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:18 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:18 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:21 msgid "One or more emoji corresponding to the sticker" msgstr "Один или несколько эмодзи, относящихся к стикеру" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:16 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:21 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:24 msgid "" "PNG image with the sticker, must be up to 512 kilobytes in size, " "dimensions must not exceed 512px, and either width or height must be " @@ -526,40 +557,47 @@ msgstr "" "Telegram скачал файл из интернета, или загрузите новый файл с помощью " "multipart/form-data." -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:21 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:26 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:26 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 msgid "TGS animation with the sticker, uploaded using multipart/form-data." msgstr "" "Анимированный стикер в формате TGS, загруженный с помощью multipart/form-" "data." -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:24 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:29 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:29 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:32 msgid "WebM animation with the sticker, uploaded using multipart/form-data." msgstr "" "Анимированный стикер в формате WebM, загруженный с помощью multipart" "/form-data." -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:27 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:40 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:32 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:42 msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" msgstr "Позиция для размещения маски на лицах в формате JSON" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:30 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:35 +msgid "" +"A JSON-serialized list of 1-50 initial stickers to be added to the " +"sticker set" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:38 #: telebot.async_telebot.AsyncTeleBot.answer_callback_query:22 -#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:35 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:38 #: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:21 #: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:18 #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:13 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:43 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:56 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:13 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:6 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:22 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:13 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:11 #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:15 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:13 msgid "On success, True is returned." msgstr "В случае успеха возвращается True." @@ -586,6 +624,7 @@ msgstr "В случае успеха возвращается True." #: telebot.async_telebot.AsyncTeleBot.delete_message #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_webhook #: telebot.async_telebot.AsyncTeleBot.download_file #: telebot.async_telebot.AsyncTeleBot.edit_chat_invite_link @@ -645,11 +684,16 @@ msgstr "В случае успеха возвращается True." #: telebot.async_telebot.AsyncTeleBot.set_chat_photo #: telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.set_chat_title +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title #: telebot.async_telebot.AsyncTeleBot.set_webhook #: telebot.async_telebot.AsyncTeleBot.stop_message_live_location #: telebot.async_telebot.AsyncTeleBot.stop_poll @@ -662,9 +706,9 @@ msgstr "В случае успеха возвращается True." msgid "Return type" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:31 +#: of telebot.async_telebot.AsyncTeleBot.add_sticker_to_set:39 #: telebot.async_telebot.AsyncTeleBot.answer_callback_query:23 -#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:36 +#: telebot.async_telebot.AsyncTeleBot.answer_inline_query:39 #: telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:22 #: telebot.async_telebot.AsyncTeleBot.answer_shipping_query:19 #: telebot.async_telebot.AsyncTeleBot.approve_chat_join_request:15 @@ -672,7 +716,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat:17 #: telebot.async_telebot.AsyncTeleBot.close:9 #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:14 -#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:44 +#: telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:57 #: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:15 #: telebot.async_telebot.AsyncTeleBot.delete_chat_photo:13 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:11 @@ -680,6 +724,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_message:23 #: telebot.async_telebot.AsyncTeleBot.delete_my_commands:17 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set:7 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:7 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:13 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:23 #: telebot.async_telebot.AsyncTeleBot.leave_chat:8 @@ -687,18 +732,23 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 -#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:48 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:26 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:18 #: telebot.async_telebot.AsyncTeleBot.set_chat_description:14 #: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:15 -#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:15 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:21 #: telebot.async_telebot.AsyncTeleBot.set_chat_photo:16 #: telebot.async_telebot.AsyncTeleBot.set_chat_title:17 +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:11 #: telebot.async_telebot.AsyncTeleBot.set_my_commands:18 #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:18 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:11 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:12 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:12 #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:12 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:16 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:16 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:11 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:20 #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:12 @@ -854,6 +904,13 @@ msgstr "" "Параметр для передачи боту вместе с сообщением /start, отправленному при " "нажатии кнопки переключения" +#: of telebot.async_telebot.AsyncTeleBot.answer_inline_query:35 +#, fuzzy +msgid "" +"A JSON-serialized object describing a button to be shown above inline " +"query results" +msgstr "Объект в формате JSON, описывающий сообщение, которое нужно отправить" + #: of telebot.async_telebot.AsyncTeleBot.answer_pre_checkout_query:1 msgid "" "Once the user has confirmed their payment and shipping details, the Bot " @@ -1019,7 +1076,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_chat_member:8 #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:6 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:11 -#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:11 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:10 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:13 msgid "Unique identifier of the target user" @@ -1040,6 +1097,9 @@ msgstr "Уникальный id сделавшего запрос пользов #: telebot.async_telebot.AsyncTeleBot.set_chat_title:16 #: telebot.async_telebot.AsyncTeleBot.set_my_commands:17 #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:17 +#: telebot.async_telebot.AsyncTeleBot.set_my_description:12 +#: telebot.async_telebot.AsyncTeleBot.set_my_name:12 +#: telebot.async_telebot.AsyncTeleBot.set_my_short_description:12 #: telebot.async_telebot.AsyncTeleBot.set_webhook:46 #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:14 #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:11 @@ -1064,7 +1124,7 @@ msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" msgstr "Документация Telegram: https://core.telegram.org/bots/api#banchatmember" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:8 -#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:7 +#: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:10 msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" @@ -1096,8 +1156,13 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 #: telebot.async_telebot.AsyncTeleBot.delete_message:22 +#: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:6 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:25 +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:10 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:10 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:11 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:10 msgid "Returns True on success." msgstr "Возвращает True в случае успеха." @@ -1297,7 +1362,6 @@ msgstr "" "декорируемую функцию объект :class:`telebot.types.ChatMemberUpdated`." #: of telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:1 - msgid "" "The result of an inline query that was chosen by a user and sent to their" " chat partner. Please see our documentation on the feedback collecting " @@ -1350,7 +1414,6 @@ msgid "Identifier of the topic to close" msgstr "id топика для закрытия" #: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:1 - msgid "" "Use this method to close the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -1362,7 +1425,6 @@ msgstr "" "топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#closegeneralforumtopic" @@ -1561,21 +1623,21 @@ msgstr "id топика, в который нужно отправить соо #: of telebot.async_telebot.AsyncTeleBot.copy_message:45 #: telebot.async_telebot.AsyncTeleBot.forward_message:26 #: telebot.async_telebot.AsyncTeleBot.reply_to:11 -#: telebot.async_telebot.AsyncTeleBot.send_animation:63 -#: telebot.async_telebot.AsyncTeleBot.send_audio:63 +#: telebot.async_telebot.AsyncTeleBot.send_animation:66 +#: telebot.async_telebot.AsyncTeleBot.send_audio:66 #: telebot.async_telebot.AsyncTeleBot.send_contact:44 #: telebot.async_telebot.AsyncTeleBot.send_dice:35 -#: telebot.async_telebot.AsyncTeleBot.send_document:56 +#: telebot.async_telebot.AsyncTeleBot.send_document:59 #: telebot.async_telebot.AsyncTeleBot.send_game:32 #: telebot.async_telebot.AsyncTeleBot.send_invoice:101 #: telebot.async_telebot.AsyncTeleBot.send_location:49 #: telebot.async_telebot.AsyncTeleBot.send_message:46 #: telebot.async_telebot.AsyncTeleBot.send_photo:48 #: telebot.async_telebot.AsyncTeleBot.send_poll:70 -#: telebot.async_telebot.AsyncTeleBot.send_sticker:39 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:42 #: telebot.async_telebot.AsyncTeleBot.send_venue:57 -#: telebot.async_telebot.AsyncTeleBot.send_video:64 -#: telebot.async_telebot.AsyncTeleBot.send_video_note:48 +#: telebot.async_telebot.AsyncTeleBot.send_video:67 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:51 #: telebot.async_telebot.AsyncTeleBot.send_voice:49 msgid "On success, the sent Message is returned." msgstr "В случае успеха возвращает отправленное сообщение (Message)." @@ -1583,18 +1645,18 @@ msgstr "В случае успеха возвращает отправленно #: of telebot.async_telebot.AsyncTeleBot.copy_message:46 #: telebot.async_telebot.AsyncTeleBot.forward_message:27 #: telebot.async_telebot.AsyncTeleBot.reply_to:12 -#: telebot.async_telebot.AsyncTeleBot.send_animation:64 -#: telebot.async_telebot.AsyncTeleBot.send_audio:64 +#: telebot.async_telebot.AsyncTeleBot.send_animation:67 +#: telebot.async_telebot.AsyncTeleBot.send_audio:67 #: telebot.async_telebot.AsyncTeleBot.send_contact:45 #: telebot.async_telebot.AsyncTeleBot.send_dice:36 -#: telebot.async_telebot.AsyncTeleBot.send_document:57 +#: telebot.async_telebot.AsyncTeleBot.send_document:60 #: telebot.async_telebot.AsyncTeleBot.send_location:50 #: telebot.async_telebot.AsyncTeleBot.send_message:47 #: telebot.async_telebot.AsyncTeleBot.send_photo:49 -#: telebot.async_telebot.AsyncTeleBot.send_sticker:40 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:43 #: telebot.async_telebot.AsyncTeleBot.send_venue:58 -#: telebot.async_telebot.AsyncTeleBot.send_video:65 -#: telebot.async_telebot.AsyncTeleBot.send_video_note:49 +#: telebot.async_telebot.AsyncTeleBot.send_video:68 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:52 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1606,10 +1668,11 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" -"Используйте этот метод, чтобы создать дополнительную ссылку-приглашение в чат. Бот " -"должен быть администратором чата и иметь соответствующие права администратора. " -"Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " -"ссылку-приглашение (ChatInviteLink)." +"Используйте этот метод, чтобы создать дополнительную ссылку-приглашение в" +" чат. Бот должен быть администратором чата и иметь соответствующие права " +"администратора. Ссылка может быть аннулирована методом " +"revokeChatInviteLink. Возвращает новую ссылку-приглашение " +"(ChatInviteLink)." #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:6 msgid "" @@ -1893,7 +1956,13 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#createnewstickerset" -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:10 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:8 +msgid "" +"Fields *_sticker are deprecated, pass a list of stickers to stickers " +"parameter instead." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:13 msgid "" "Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " "animals). Can contain only English letters, digits and underscores. Must " @@ -1907,11 +1976,11 @@ msgstr "" "содержать подряд идущие нижние подчеркивания и должно заканчиваться на " "\"_by_\". учитывает регистр. 1-64 символа." -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:15 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:18 msgid "Sticker set title, 1-64 characters" msgstr "Название стикерпака, 1-64 символа" -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:32 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:35 msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." @@ -1919,16 +1988,34 @@ msgstr "" "Передайте True, если создаётся стикерпак масок. Устарело, начиная с Bot " "API 6.2, используйте sticker_type." -#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:36 +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:39 +#, fuzzy msgid "" -"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " -"emoji sticker sets can't be created via the Bot API at the moment. By " -"default, a regular sticker set is created." +"Type of stickers in the set, pass “regular”, “mask”, or “custom_emoji”. " +"By default, a regular sticker set is created." msgstr "" "Необязательный, тип стикерпака, передайте “regular” или “mask”. " "Стикерпаки кастомных эмодзи пока что не могут быть созданы с помощью Bot " "API. По умолчанию будет создан обычный стикерпак." +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:45 +msgid "" +"Pass True if stickers in the sticker set must be repainted to the color " +"of text when used in messages, the accent color if used as emoji status, " +"white on chat photos, or another appropriate color based on context; for " +"custom emoji sticker sets only" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:50 +msgid "List of stickers to be added to the set" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.create_new_sticker_set:53 +msgid "" +"Format of stickers in the set, must be one of “static”, “animated”, " +"“video”" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:1 msgid "" "Use this method to decline a chat join request. The bot must be an " @@ -2112,6 +2199,13 @@ msgstr "" msgid "File identifier of the sticker" msgstr "id файла стикера" +#: of telebot.async_telebot.AsyncTeleBot.delete_sticker_set:1 +#, fuzzy +msgid "Use this method to delete a sticker set. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного " +"ботом. Возвращает True в случае успеха." + #: of telebot.async_telebot.AsyncTeleBot.delete_webhook:1 msgid "" "Use this method to remove webhook integration if you decide to switch " @@ -2198,8 +2292,8 @@ msgid "" "Optional, New name of the topic, 1-128 characters. If not specififed or " "empty, the current name of the topic will be kept" msgstr "" -"Необязательный, новое имя топика, 1-128 символов. Если не задано или пустое, " -"сохранится текущее имя топика" +"Необязательный, новое имя топика, 1-128 символов. Если не задано или " +"пустое, сохранится текущее имя топика" #: of telebot.async_telebot.AsyncTeleBot.edit_forum_topic:17 msgid "" @@ -2214,7 +2308,6 @@ msgstr "" "чтобы убрать иконку. Если не задан, сохранится текущая иконка топика" #: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:1 - msgid "" "Use this method to edit the name of the 'General' topic in a forum " "supergroup chat. The bot must be an administrator in the chat for this to" @@ -2227,14 +2320,12 @@ msgstr "" "True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editgeneralforumtopic" msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" #: of telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:10 - msgid "New topic name, 1-128 characters" msgstr "Название товара, 1-32 символа" @@ -2320,9 +2411,9 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" -"отключено вызовом метода stopMessageLiveLocation. В случае успеха, если измененное " -"сообщение не является inline сообщением, возвращается новый объект Message, " -"иначе возвращается True." +"отключено вызовом метода stopMessageLiveLocation. В случае успеха, если " +"измененное сообщение не является inline сообщением, возвращается новый " +"объект Message, иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.edit_message_live_location:5 msgid "" @@ -2432,8 +2523,8 @@ msgid "" " target chat or username of the target channel (in the format " "@channelusername)" msgstr "" -"Обязательный, если не указан inline_message_id. Уникальный id чата " -"или username канала (в формате @channelusername)" +"Обязательный, если не указан inline_message_id. Уникальный id чата или " +"username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:13 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:8 @@ -2443,8 +2534,7 @@ msgstr "" msgid "" "Required if inline_message_id is not specified. Identifier of the sent " "message" -msgstr "" -"Обязательный, если не указан inline_message_id. id отправленного сообщения" +msgstr "Обязательный, если не указан inline_message_id. id отправленного сообщения" #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:23 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:18 @@ -2487,8 +2577,8 @@ msgid "" "List of special entities that appear in the message text, which can be " "specified instead of parse_mode" msgstr "" -"Список отформатированных частей в тексте сообщения, " -"можно использовать вместо parse_mode" +"Список отформатированных частей в тексте сообщения, можно использовать " +"вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.edit_message_text:23 #: telebot.async_telebot.AsyncTeleBot.send_message:21 @@ -2501,13 +2591,15 @@ msgid "" "edited. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" -"Обрабатывает новую версию поста в канале, который доступен боту и был изменён. " -"В качестве параметра, передаёт в декорируемую функцию объект " +"Обрабатывает новую версию поста в канале, который доступен боту и был " +"изменён. В качестве параметра, передаёт в декорируемую функцию объект " ":class:`telebot.types.Message`." #: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:1 msgid "Handles new version of a message that is known to the bot and was edited." -msgstr "Обрабатывает новую версию сообщения, которое доступно боту и было изменено." +msgstr "" +"Обрабатывает новую версию сообщения, которое доступно боту и было " +"изменено." #: of telebot.async_telebot.AsyncTeleBot.edited_message_handler:3 msgid "" @@ -2532,8 +2624,9 @@ msgid "" ":class:`~telebot.asyncio_storage.StatePickleStorage` instance as " "state_storage to TeleBot class." msgstr "" -"Рекомендуется передавать экземпляр класса :class:`~telebot.storage.StatePickleStorage` " -"в качестве state_storage при инициализации класса TeleBot вместо использования этой функции." +"Рекомендуется передавать экземпляр класса " +":class:`~telebot.storage.StatePickleStorage` в качестве state_storage при" +" инициализации класса TeleBot вместо использования этой функции." #: of telebot.async_telebot.AsyncTeleBot.enable_saving_states:7 msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" @@ -2545,9 +2638,9 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights." msgstr "" -"Используйте этот метод, чтобы создать или заменить главную ссылку-приглашение " -"в супергруппу или канал, созданную ботом. Бот должен быть администратором чата " -"и иметь соответствующие права администратора." +"Используйте этот метод, чтобы создать или заменить главную " +"ссылку-приглашение в супергруппу или канал, созданную ботом. Бот должен " +"быть администратором чата и иметь соответствующие права администратора." #: of telebot.async_telebot.AsyncTeleBot.export_chat_invite_link:4 msgid "" @@ -2596,9 +2689,10 @@ msgid "" "name of the user for one-on-one conversations, current username of a " "user, group or channel, etc.). Returns a Chat object on success." msgstr "" -"Используйте этот метод, чтобы получить актуальную информацию о чате (текущее " -"имя пользователя для персональных диалогов, текущий username пользователя, " -"группы или канала и т.д.). В случае успеха возвращает объект Chat." +"Используйте этот метод, чтобы получить актуальную информацию о чате " +"(текущее имя пользователя для персональных диалогов, текущий username " +"пользователя, группы или канала и т.д.). В случае успеха возвращает " +"объект Chat." #: of telebot.async_telebot.AsyncTeleBot.get_chat:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" @@ -2612,8 +2706,8 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" -"Уникальный id чата или username супергруппы или канала " -"(в формате @channelusername)" +"Уникальный id чата или username супергруппы или канала (в формате " +"@channelusername)" #: of telebot.async_telebot.AsyncTeleBot.get_chat:9 msgid "Chat information" @@ -2629,9 +2723,9 @@ msgid "" "returns an Array of ChatMember objects that contains information about " "all chat administrators except other bots." msgstr "" -"Используйте этот метод, чтобы получить список администраторов чата. " -"В случае успеха, возвращает массив объектов ChatMember, содержащих информацию " -"обо всех администраторах чата, кроме других ботов." +"Используйте этот метод, чтобы получить список администраторов чата. В " +"случае успеха, возвращает массив объектов ChatMember, содержащих " +"информацию обо всех администраторах чата, кроме других ботов." #: of telebot.async_telebot.AsyncTeleBot.get_chat_administrators:5 msgid "" @@ -2654,8 +2748,8 @@ msgid "" "Use this method to get information about a member of a chat. Returns a " "ChatMember object on success." msgstr "" -"Используйте этот метод, чтобы получить информацию об участнике чата. Возвращает " -"объект ChatMember в случае успеха." +"Используйте этот метод, чтобы получить информацию об участнике чата. " +"Возвращает объект ChatMember в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_chat_member:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" @@ -2695,7 +2789,8 @@ msgid "" "private chat, or the default menu button. Returns MenuButton on success." msgstr "" "Используйте этот метод, чтобы получить текущее значение кнопки menu в " -"приватном чате, или кнопку menu по умолчанию. Возвращает MenuButton в случае успеха." +"приватном чате, или кнопку menu по умолчанию. Возвращает MenuButton в " +"случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:5 msgid "" @@ -2710,8 +2805,8 @@ msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be returned." msgstr "" -"Уникальный id приватного чата. Если не указан, будет возвращена " -"кнопка menu по умолчанию." +"Уникальный id приватного чата. Если не указан, будет возвращена кнопка " +"menu по умолчанию." #: of telebot.async_telebot.AsyncTeleBot.get_chat_menu_button:11 msgid "types.MenuButton" @@ -2726,15 +2821,14 @@ msgid "" "Use this method to get information about custom emoji stickers by their " "identifiers. Returns an Array of Sticker objects." msgstr "" -"Используйте этот метод, чтобы получить информацию о кастомных эмодзи по их " -"id. Возвращает массив объектов Sticker." +"Используйте этот метод, чтобы получить информацию о кастомных эмодзи по " +"их id. Возвращает массив объектов Sticker." #: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:4 msgid "" "List of custom emoji identifiers. At most 200 custom emoji identifiers " "can be specified." -msgstr "" -"Список id кастомных эмодзи. Можно указать не более 200 id." +msgstr "Список id кастомных эмодзи. Можно указать не более 200 id." #: of telebot.async_telebot.AsyncTeleBot.get_custom_emoji_stickers:7 msgid "Returns an Array of Sticker objects." @@ -2752,11 +2846,12 @@ msgid "" "link will be valid for at least 1 hour. When the link expires, a new one " "can be requested by calling get_file again." msgstr "" -"Используйте этот метод, чтобы получить базовую информацию о файле и подготовить " -"его к скачиванию. На текущий момент, боты могут скачивать файлы весом до 20MB. " -"В случае успеха, возвращается объект File. Гарантируется, что ссылка на скачивание " -"будет актуальна как минимум 1 час. Когда ссылка перестаёт быть актуальной, новая " -"может быть снова запрошена с помощью get_file." +"Используйте этот метод, чтобы получить базовую информацию о файле и " +"подготовить его к скачиванию. На текущий момент, боты могут скачивать " +"файлы весом до 20MB. В случае успеха, возвращается объект File. " +"Гарантируется, что ссылка на скачивание будет актуальна как минимум 1 " +"час. Когда ссылка перестаёт быть актуальной, новая может быть снова " +"запрошена с помощью get_file." #: of telebot.async_telebot.AsyncTeleBot.get_file:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" @@ -2767,7 +2862,7 @@ msgid "File identifier" msgstr "id файла" #: of telebot.async_telebot.AsyncTeleBot.get_file:12 -#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file:14 +#: telebot.async_telebot.AsyncTeleBot.upload_sticker_file:21 msgid ":class:`telebot.types.File`" msgstr "" @@ -2789,9 +2884,9 @@ msgid "" "forum topic icon by any user. Requires no parameters. Returns an Array of" " Sticker objects." msgstr "" -"Используйте этот метод, чтобы получить кастомные эмодзи, которые могут быть " -"использованы любыми пользователями в качестве иконок топиков. Не требует параметров. " -"Возвращает массив объектов Sticker." +"Используйте этот метод, чтобы получить кастомные эмодзи, которые могут " +"быть использованы любыми пользователями в качестве иконок топиков. Не " +"требует параметров. Возвращает массив объектов Sticker." #: of telebot.async_telebot.AsyncTeleBot.get_forum_topic_icon_stickers:4 msgid "" @@ -2815,9 +2910,9 @@ msgid "" "of the specified user and several of their neighbors in a game. On " "success, returns an Array of GameHighScore objects." msgstr "" -"Используйте этот метод, чтобы получить данные для таблицы рекордов. Вернёт " -"очки указанного пользователя и несколько соседних результатов. В случае успеха, " -"возвращает массив объектов GameHighScore." +"Используйте этот метод, чтобы получить данные для таблицы рекордов. " +"Вернёт очки указанного пользователя и несколько соседних результатов. В " +"случае успеха, возвращает массив объектов GameHighScore." #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:4 msgid "" @@ -2826,10 +2921,10 @@ msgid "" "users if the user and their neighbors are not among them. Please note " "that this behavior is subject to change." msgstr "" -"На текущий момент этот метод вернёт очки указанного пользователя и по два соседних " -"результата с каждой стороны. Также вернет результаты трёх лучших игроков, если " -"результат пользователя и соседние не являются тремя лучшими. Пожалуйста учитывайте, " -"что это поведение может быть изменено." +"На текущий момент этот метод вернёт очки указанного пользователя и по два" +" соседних результата с каждой стороны. Также вернет результаты трёх " +"лучших игроков, если результат пользователя и соседние не являются тремя " +"лучшими. Пожалуйста учитывайте, что это поведение может быть изменено." #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:8 msgid "" @@ -2842,7 +2937,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.get_game_high_scores:10 #: telebot.async_telebot.AsyncTeleBot.retrieve_data:3 #: telebot.async_telebot.AsyncTeleBot.set_game_score:5 -#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:9 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:9 msgid "User identifier" msgstr "id пользователя" @@ -2863,8 +2958,8 @@ msgid "" "Use this method to get the current list of the bot's commands. Returns " "List of BotCommand on success." msgstr "" -"Используйте этот метод, чтобы получить текущий список команд бота. Возвращает " -"список объектов BotCommand в случае успеха." +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_my_commands:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" @@ -2883,8 +2978,9 @@ msgid "" "Use this method to get the current default administrator rights of the " "bot. Returns ChatAdministratorRights on success." msgstr "" -"Используйте этот метод, чтобы получить текущие права администратора для бота по умолчанию. " -"Возвращает объект ChatAdministratorRights в случае успеха." +"Используйте этот метод, чтобы получить текущие права администратора для " +"бота по умолчанию. Возвращает объект ChatAdministratorRights в случае " +"успеха." #: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:4 msgid "" @@ -2900,9 +2996,9 @@ msgid "" " Otherwise, the default administrator rights of the bot for groups and " "supergroups will be returned." msgstr "" -"Передайте True, чтобы получить права администратора для бота по умолчанию в каналах. " -"Иначе, будут возвращены права администратора для бота по умолчанию в группах и " -"супергруппах." +"Передайте True, чтобы получить права администратора для бота по умолчанию" +" в каналах. Иначе, будут возвращены права администратора для бота по " +"умолчанию в группах и супергруппах." #: of telebot.async_telebot.AsyncTeleBot.get_my_default_administrator_rights:9 msgid "Returns ChatAdministratorRights on success." @@ -2912,6 +3008,61 @@ msgstr "Возвращает объект ChatAdministratorRights в случа msgid ":class:`telebot.types.ChatAdministratorRights`" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.get_my_description:1 +#, fuzzy +msgid "" +"Use this method to get the current bot description for the given user " +"language. Returns BotDescription on success." +msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.get_my_description:4 +#: telebot.async_telebot.AsyncTeleBot.get_my_short_description:4 +msgid "A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_description:7 +#, fuzzy +msgid ":class:`telebot.types.BotDescription`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:1 +#, fuzzy +msgid "" +"Use this method to get the current bot name for the given user language. " +"Returns BotName on success." +msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:4 +#, fuzzy +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmyname" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getme" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:6 +msgid "Optional. A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_name:9 +#, fuzzy +msgid ":class:`telebot.types.BotName`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + +#: of telebot.async_telebot.AsyncTeleBot.get_my_short_description:1 +#, fuzzy +msgid "" +"Use this method to get the current bot short description for the given " +"user language. Returns BotShortDescription on success." +msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.get_my_short_description:7 +msgid ":class:`telebot.types.BotShortDescription`" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.get_state:1 msgid "" "Gets current state of a user. Not recommended to use this method. But it " @@ -2933,8 +3084,8 @@ msgid "" "Use this method to get a sticker set. On success, a StickerSet object is " "returned." msgstr "" -"Используйте этот метод, чтобы получить стикерпак. В случае успеха, возвращается " -"объект StickerSet." +"Используйте этот метод, чтобы получить стикерпак. В случае успеха, " +"возвращается объект StickerSet." #: of telebot.async_telebot.AsyncTeleBot.get_sticker_set:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" @@ -2954,8 +3105,8 @@ msgid "" "Use this method to receive incoming updates using long polling (wiki). An" " Array of Update objects is returned." msgstr "" -"Используйте этот метод, чтобы получить новые апдейты с помощью long polling-а (wiki). " -"Возвращается массив объектов Update." +"Используйте этот метод, чтобы получить новые апдейты с помощью long " +"polling-а (wiki). Возвращается массив объектов Update." #: of telebot.async_telebot.AsyncTeleBot.get_updates:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" @@ -2974,17 +3125,17 @@ msgstr "" "id первого апдейта. Должен быть на единицу больше наибольшего id среди " "ранее полученных апдейтов. По умолчанию, возвращается список апдейтов, " "начиная с самого раннего неполученного. Апдейт считается полученным как " -"только вызван метод getUpdates со смещением больше, чем id этого апдейта. " -"Отрицательное смещение может быть указано для получения последних offset апдейтов. " -"Все предыдущие апдейты будут считаться полученными." +"только вызван метод getUpdates со смещением больше, чем id этого апдейта." +" Отрицательное смещение может быть указано для получения последних offset" +" апдейтов. Все предыдущие апдейты будут считаться полученными." #: of telebot.async_telebot.AsyncTeleBot.get_updates:12 msgid "" "Limits the number of updates to be retrieved. Values between 1-100 are " "accepted. Defaults to 100." msgstr "" -"Максимальное число апдейтов для получения. Допускаются значения от 1 до 100. " -"По умолчанию 100." +"Максимальное число апдейтов для получения. Допускаются значения от 1 до " +"100. По умолчанию 100." #: of telebot.async_telebot.AsyncTeleBot.get_updates:15 #: telebot.async_telebot.AsyncTeleBot.get_webhook_info:6 @@ -2997,8 +3148,9 @@ msgid "Array of string. List the types of updates you want your bot to receive." msgstr "Массив строк. Список видов апдейтов, которые вы хотите получать." #: of telebot.async_telebot.AsyncTeleBot.get_updates:21 -msgid "Timeout in seconds for long polling." -msgstr "Тайм-аут поллинга в секундах." +#, fuzzy +msgid "Timeout in seconds for request." +msgstr "Таймаут запроса в секундах." #: of telebot.async_telebot.AsyncTeleBot.get_updates:24 msgid "An Array of Update objects is returned." @@ -3056,9 +3208,9 @@ msgid "" " success, returns a WebhookInfo object. If the bot is using getUpdates, " "will return an object with the url field empty." msgstr "" -"Используйте этот метод, чтобы получить текущий статус вебхука. Не требует параметров. " -"В случае успеха возвращает объект WebhookInfo. Если бот использует getUpdates, " -"вернёт объект с пустым атрибутом url." +"Используйте этот метод, чтобы получить текущий статус вебхука. Не требует" +" параметров. В случае успеха возвращает объект WebhookInfo. Если бот " +"использует getUpdates, вернёт объект с пустым атрибутом url." #: of telebot.async_telebot.AsyncTeleBot.get_webhook_info:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" @@ -3073,7 +3225,6 @@ msgid ":class:`telebot.types.WebhookInfo`" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:1 - msgid "" "Use this method to hide the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -3085,7 +3236,6 @@ msgstr "" "True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#hidegeneralforumtopic" @@ -3096,8 +3246,8 @@ msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" " polling." msgstr "" -"Запустить поллинг в бесконечном цикле с обработкой исключений, чтобы избежать " -"непредвиденных остановок поллинга." +"Запустить поллинг в бесконечном цикле с обработкой исключений, чтобы " +"избежать непредвиденных остановок поллинга." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:4 #: telebot.async_telebot.AsyncTeleBot.polling:15 @@ -3123,8 +3273,8 @@ msgid "" "Custom logging level for infinity_polling logging. Use logger levels from" " logging as a value. None/NOTSET = no error logging" msgstr "" -"Кастомный уровень логирования для infinity_polling. Используйте уровни из " -"logging в качестве значений. None/NOTSET = не логировать ошибки." +"Кастомный уровень логирования для infinity_polling. Используйте уровни из" +" logging в качестве значений. None/NOTSET = не логировать ошибки." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:19 #: telebot.async_telebot.AsyncTeleBot.polling:32 @@ -3163,9 +3313,9 @@ msgid "" " call to the get_updates, so unwanted updates may be received for a short" " period of time." msgstr "" -"Пожалуйста учитывайте, что этот параметр не влияет на апдейты, отправленные " -"до вызова get_updates, поэтому нежелательные апдейты могут быть получены " -"в течение короткого периода времени." +"Пожалуйста учитывайте, что этот параметр не влияет на апдейты, " +"отправленные до вызова get_updates, поэтому нежелательные апдейты могут " +"быть получены в течение короткого периода времени." #: of telebot.async_telebot.AsyncTeleBot.infinity_polling:29 msgid "Restart a file on file(s) change. Defaults to False" @@ -3211,10 +3361,10 @@ msgid "" msgstr "" "Используйте этот метод, чтобы отключиться от облачного Bot API сервера " "перед локальным запуском бота. Вы ДОЛЖНЫ отключить бота перед тем, как " -"запускать его локально, иначе нет никаких гарантий, что бот будет получать " -"апдейты. После успешного вызова, вы можете тут же подключиться к локальному " -"серверу, но не сможете подключиться обратно к облачному Bot API серверу в " -"течение 10 минут. Возвращает True в случае успеха." +"запускать его локально, иначе нет никаких гарантий, что бот будет " +"получать апдейты. После успешного вызова, вы можете тут же подключиться к" +" локальному серверу, но не сможете подключиться обратно к облачному Bot " +"API серверу в течение 10 минут. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.log_out:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" @@ -3227,8 +3377,8 @@ msgid "" ":class:`telebot.types.Message` object. All message handlers are tested in" " the order they were added." msgstr "" -"Обрабатывает входящие сообщения всех видов - text, photo, sticker, и т.д. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает входящие сообщения всех видов - text, photo, sticker, и т.д." +" В качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.Message`. Все хендлеры сообщений проверяются в том " "порядке, в котором были добавлены." @@ -3246,9 +3396,9 @@ msgid "" "first parameter. It must return True if the command should handle the " "message." msgstr "" -"Необязательная lambda функция. Получает сообщение (объект Message) в качестве " -"первого параметра. Функция должна вернуть True если хендлер должен обработать " -"сообщение." +"Необязательная lambda функция. Получает сообщение (объект Message) в " +"качестве первого параметра. Функция должна вернуть True если хендлер " +"должен обработать сообщение." #: of telebot.async_telebot.AsyncTeleBot.message_handler:52 #: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler:23 @@ -3275,9 +3425,9 @@ msgid "" "administrator in the chat for this to work and must have the appropriate " "admin rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот должен " -"быть администратором чата и иметь соответствующие права администратора. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот " +"должен быть администратором чата и иметь соответствующие права " +"администратора. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.pin_chat_message:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" @@ -3292,8 +3442,8 @@ msgid "" "Pass True, if it is not necessary to send a notification to all group " "members about the new pinned message" msgstr "" -"Передайте True, если всем участникам группы необходимо отправить уведомление " -"о закреплённом сообщении" +"Передайте True, если всем участникам группы необходимо отправить " +"уведомление о закреплённом сообщении" #: of telebot.async_telebot.AsyncTeleBot.poll_answer_handler:1 msgid "" @@ -3304,8 +3454,8 @@ msgid "" msgstr "" "Обрабатывает изменения ответа пользователя в не анонимном опросе(когда " "пользователь меняет выбор). Боты получают новые ответы только в опросах, " -"которые отправили сами. В качестве параметра передаёт в декорируемую функцию " -"объект :class:`telebot.types.PollAnswer`." +"которые отправили сами. В качестве параметра передаёт в декорируемую " +"функцию объект :class:`telebot.types.PollAnswer`." #: of telebot.async_telebot.AsyncTeleBot.poll_handler:1 msgid "" @@ -3313,9 +3463,10 @@ msgid "" "polls and polls, which are sent by the bot As a parameter to the " "decorator function, it passes :class:`telebot.types.Poll` object." msgstr "" -"Обрабатывает изменения в состоянии опроса. Боты получают только апдейты " -"о завершенных опросах и опросах, которые отправили сами. В качестве " -"параметра передаёт в декорируемую функцию объект :class:`telebot.types.Poll`." +"Обрабатывает изменения в состоянии опроса. Боты получают только апдейты о" +" завершенных опросах и опросах, которые отправили сами. В качестве " +"параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.Poll`." #: of telebot.async_telebot.AsyncTeleBot.polling:1 msgid "" @@ -3340,8 +3491,8 @@ msgid "" "Set non_stop=True if you want your bot to continue receiving updates if " "there is an error." msgstr "" -"Укажите non_stop=True, если хотите чтобы ваш бот продолжать получать апдейты " -"при возникновении ошибок." +"Укажите non_stop=True, если хотите чтобы ваш бот продолжать получать " +"апдейты при возникновении ошибок." #: of telebot.async_telebot.AsyncTeleBot.polling:17 msgid "Do not stop polling when an ApiException occurs." @@ -3354,8 +3505,8 @@ msgstr "Задержка между получением апдейтов" #: of telebot.async_telebot.AsyncTeleBot.polling:42 msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." msgstr "" -"Устарело, используйте non_stop. " -"Старая опечатка, оставлено для обратной совместимости" +"Устарело, используйте non_stop. Старая опечатка, оставлено для обратной " +"совместимости" #: of telebot.async_telebot.AsyncTeleBot.polling:45 msgid "Restart a file on file(s) change. Defaults to False." @@ -3367,8 +3518,8 @@ msgid "" "checkout. As a parameter to the decorator function, it passes " ":class:`telebot.types.PreCheckoutQuery` object." msgstr "" -"Новая pre-checkout query. Содержит полную информацию о заказе. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Новая pre-checkout query. Содержит полную информацию о заказе. В качестве" +" параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.PreCheckoutQuery`." #: of telebot.async_telebot.AsyncTeleBot.process_new_updates:1 @@ -3376,8 +3527,8 @@ msgid "" "Process new updates. Just pass list of updates - each update should be " "instance of Update object." msgstr "" -"Обрабатывает новые апдейты. Просто передайте список апдейтов(Update и " -"его наследники)." +"Обрабатывает новые апдейты. Просто передайте список апдейтов(Update и его" +" наследники)." #: of telebot.async_telebot.AsyncTeleBot.process_new_updates:5 msgid "list of updates" @@ -3390,9 +3541,10 @@ msgid "" "have the appropriate admin rights. Pass False for all boolean parameters " "to demote a user." msgstr "" -"Используйте этот метод, чтобы повысить или понизить пользователя в супергруппе " -"или канале. Бот должен быть администратором чата и иметь соответствующие права " -"администратора. Передайте False во все boolean параметры, чтобы понизить пользователя." +"Используйте этот метод, чтобы повысить или понизить пользователя в " +"супергруппе или канале. Бот должен быть администратором чата и иметь " +"соответствующие права администратора. Передайте False во все boolean " +"параметры, чтобы понизить пользователя." #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:5 msgid "" @@ -3406,44 +3558,53 @@ msgstr "" msgid "" "Unique identifier for the target chat or username of the target channel (" " in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала (в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:14 msgid "" "Pass True, if the administrator can change chat title, photo and other " "settings" msgstr "" -"Передайте True, если администратор может менять название чата, аватарку " -"и другие настройки" +"Передайте True, если администратор может менять название чата, аватарку и" +" другие настройки" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:17 msgid "Pass True, if the administrator can create channel posts, channels only" -msgstr "Передайте True, если администратор может создавать посты в канале, только для каналов" +msgstr "" +"Передайте True, если администратор может создавать посты в канале, только" +" для каналов" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:20 msgid "" "Pass True, if the administrator can edit messages of other users, " "channels only" msgstr "" -"Передайте True, если администратор может изменять сообщения других пользователей, " -"только для каналов" +"Передайте True, если администратор может изменять сообщения других " +"пользователей, только для каналов" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:23 msgid "Pass True, if the administrator can delete messages of other users" -msgstr "Передайте True, если администратор может удалять сообщения других пользователей" +msgstr "" +"Передайте True, если администратор может удалять сообщения других " +"пользователей" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:26 msgid "Pass True, if the administrator can invite new users to the chat" -msgstr "Передайте True, если администратор может приглашать новых пользователей в чат" +msgstr "" +"Передайте True, если администратор может приглашать новых пользователей в" +" чат" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:29 msgid "Pass True, if the administrator can restrict, ban or unban chat members" -msgstr "Передайте True, если администратор может ограничивать, банить или разбанивать участников чата" +msgstr "" +"Передайте True, если администратор может ограничивать, банить или " +"разбанивать участников чата" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:32 msgid "Pass True, if the administrator can pin messages, supergroups only" -msgstr "Передайте True, если администратор может закреплять сообщения, только для супергрупп" +msgstr "" +"Передайте True, если администратор может закреплять сообщения, только для" +" супергрупп" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:35 msgid "" @@ -3452,9 +3613,10 @@ msgid "" "directly or indirectly (promoted by administrators that were appointed by" " him)" msgstr "" -"Передайте True, если администратор может добавлять новых администраторов с " -"подмножеством его собственных прав администратора или понижать администраторов, " -"которых он повысил, напрямую или косвенно (администраторами, которых он назначил)" +"Передайте True, если администратор может добавлять новых администраторов " +"с подмножеством его собственных прав администратора или понижать " +"администраторов, которых он повысил, напрямую или косвенно " +"(администраторами, которых он назначил)" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:40 msgid "Pass True, if the administrator's presence in the chat is hidden" @@ -3467,19 +3629,19 @@ msgid "" "anonymous administrators in supergroups and ignore slow mode. Implied by " "any other administrator privilege" msgstr "" -"Передайте True, если администратор имеет доступ к логу событий чата, статистике " -"чата, статистике сообщений в каналах, видеть участников канала, видеть анонимных " -"администраторов в супергруппах и игнорировать медленный режим. Подразумевается " -"любым другим правом администратора" +"Передайте True, если администратор имеет доступ к логу событий чата, " +"статистике чата, статистике сообщений в каналах, видеть участников " +"канала, видеть анонимных администраторов в супергруппах и игнорировать " +"медленный режим. Подразумевается любым другим правом администратора" #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:49 msgid "" "Pass True, if the administrator can manage voice chats For now, bots can " "use this privilege only for passing to other administrators." msgstr "" -"Передайте True, если администратор может управлять голосовыми чатами. На текущий " -"момент, боты могут использовать это право администратора только для передачи другим " -"администраторам." +"Передайте True, если администратор может управлять голосовыми чатами. На " +"текущий момент, боты могут использовать это право администратора только " +"для передачи другим администраторам." #: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:53 msgid "Deprecated, use can_manage_video_chats." @@ -3490,8 +3652,8 @@ msgid "" "Pass True if the user is allowed to create, rename, close, and reopen " "forum topics, supergroups only" msgstr "" -"Передайте True, если пользователю разрешено создавать, переименовывать, закрывать, " -"и возобновлять топики, только для супергрупп" +"Передайте True, если пользователю разрешено создавать, переименовывать, " +"закрывать, и возобновлять топики, только для супергрупп" #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." @@ -3532,8 +3694,8 @@ msgid "" "True if you need to pass TeleBot instance to handler(useful for " "separating handlers into different files)" msgstr "" -"True, если вам нужно передать экземпляр класса TeleBot в хендлер(удобно для " -"разбиения кода на файлы)" +"True, если вам нужно передать экземпляр класса TeleBot в хендлер(удобно " +"для разбиения кода на файлы)" #: of telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:1 msgid "Registers channel post message handler." @@ -3623,35 +3785,33 @@ msgid "" "the can_manage_topics administrator rights, unless it is the creator of " "the topic. Returns True on success." msgstr "" -"Используйте этот метод, чтобы возобновить закрытый топик в супергруппе с топиками. " -"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " -"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы возобновить закрытый топик в супергруппе с " +"топиками. Бот должен быть администратором чата и иметь права " +"администратора can_manage_topics, кроме случаев, когда бот является " +"создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopenforumtopic" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#reopenforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#reopenforumtopic" #: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:10 msgid "Identifier of the topic to reopen" msgstr "id топика для возобновления" #: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:1 - msgid "" "Use this method to reopen the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы возобновить топик 'General' в супергруппе с топиками. " -"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " -"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы возобновить топик 'General' в супергруппе с" +" топиками. Бот должен быть администратором чата и иметь права " +"администратора can_manage_topics, кроме случаев, когда бот является " +"создателем топика. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopengeneralforumtopic" @@ -3707,18 +3867,24 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#restrictchatmember" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:8 +msgid "" +"Individual parameters are deprecated and will be removed, use " +"'permissions' instead" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:17 msgid "" "Date when restrictions will be lifted for the user, unix time. If user is" " restricted for more than 366 days or less than 30 seconds from the " "current time, they are considered to be restricted forever" msgstr "" "Дата, когда ограничения будут сняты с пользователя, UNIX timestamp. Если " -"пользователь ограничен более чем на 366 дней или менее чем на 30 секунд с " -"текущего момента, он будет ограничен навсегда (пока ограничения не будут " -"сняты вручную)" +"пользователь ограничен более чем на 366 дней или менее чем на 30 секунд с" +" текущего момента, он будет ограничен навсегда (пока ограничения не будут" +" сняты вручную)" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:19 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:22 msgid "" "Pass True, if the user can send text messages, contacts, locations and " "venues" @@ -3726,62 +3892,79 @@ msgstr "" "Передайте True, если пользователь может отправлять текстовые сообщения, " "контакты, местоположения и места" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:22 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:25 msgid "" "Pass True, if the user can send audios, documents, photos, videos, video " "notes and voice notes, implies can_send_messages" msgstr "" -"Передайте True, если пользователь может отправлять аудио, документы, фото, " -"видео, видео заметки (кружочки) и аудио заметки (голосовые), подразумевает " -"can_send_messages" +"Передайте True, если пользователь может отправлять аудио, документы, " +"фото, видео, видео заметки (кружочки) и аудио заметки (голосовые), " +"подразумевает can_send_messages" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:26 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:29 msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" msgstr "" -"Передайте True, если пользователю разрешено отправлять опросы, подразумевает " -"can_send_messages" +"Передайте True, если пользователю разрешено отправлять опросы, " +"подразумевает can_send_messages" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:29 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" -"Передайте True, если пользователь может отправлять гифки, игры, стикеры и " -"использовать inline ботов, подразумевает can_send_media_messages" +"Передайте True, если пользователь может отправлять гифки, игры, стикеры и" +" использовать inline ботов, подразумевает can_send_media_messages" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:32 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:35 msgid "" "Pass True, if the user may add web page previews to their messages, " "implies can_send_media_messages" msgstr "" -"Передайте True, если пользователь может добавлять превью ссылок к своим сообщениям, " -"подразумевает can_send_media_messages" +"Передайте True, если пользователь может добавлять превью ссылок к своим " +"сообщениям, подразумевает can_send_media_messages" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:36 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:39 msgid "" "Pass True, if the user is allowed to change the chat title, photo and " "other settings. Ignored in public supergroups" msgstr "" -"Передайте True, если пользователю разрешено изменять название чата, аватарку и " -"другие настройки. Игнорируется в публичных супергруппах" +"Передайте True, если пользователю разрешено изменять название чата, " +"аватарку и другие настройки. Игнорируется в публичных супергруппах" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:40 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:43 msgid "" "Pass True, if the user is allowed to invite new users to the chat, " "implies can_invite_users" -msgstr "" -"Передайте True, если пользователю разрешено приглашать пользователей в чат" +msgstr "Передайте True, если пользователю разрешено приглашать пользователей в чат" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:44 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:47 msgid "" "Pass True, if the user is allowed to pin messages. Ignored in public " "supergroups" msgstr "" -"Передайте True, если пользователю разрешено закреплять сообщения. Игнорируется " -"в публичных супергруппах" +"Передайте True, если пользователю разрешено закреплять сообщения. " +"Игнорируется в публичных супергруппах" -#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:47 +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:50 #: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:14 +msgid "" +"Pass True if chat permissions are set independently. Otherwise, the " +"can_send_other_messages and can_add_web_page_previews permissions will " +"imply the can_send_messages, can_send_audios, can_send_documents, " +"can_send_photos, can_send_videos, can_send_video_notes, and " +"can_send_voice_notes permissions; the can_send_polls permission will " +"imply the can_send_messages permission." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:56 +msgid "" +"Pass ChatPermissions object to set all permissions at once. Use this " +"parameter instead of passing all boolean parameters to avoid backward " +"compatibility problems in future." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.restrict_chat_member:60 +#: telebot.async_telebot.AsyncTeleBot.set_chat_permissions:20 #: telebot.async_telebot.AsyncTeleBot.unban_chat_member:19 msgid "True on success" msgstr "True в случае успеха" @@ -3805,10 +3988,10 @@ msgid "" "must be an administrator in the chat for this to work and must have the " "appropriate admin rights." msgstr "" -"Используйте этот метод, чтобы аннулировать ссылку-приглашение, созданную ботом. " -"Примечание: Если аннулируется главная ссылка-приглашение, автоматически " -"генерируется новая. Бот должен быть администратором чата и иметь соответствующие " -"права администратора." +"Используйте этот метод, чтобы аннулировать ссылку-приглашение, созданную " +"ботом. Примечание: Если аннулируется главная ссылка-приглашение, " +"автоматически генерируется новая. Бот должен быть администратором чата и " +"иметь соответствующие права администратора." #: of telebot.async_telebot.AsyncTeleBot.revoke_chat_invite_link:5 msgid "" @@ -3830,39 +4013,39 @@ msgstr "Этот класс устанавливает вебхуки и мон msgid "IP address to listen to. Defaults to 0.0.0.0" msgstr "IP адрес для мониторинга. По умолчанию 0.0.0.0" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:5 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:4 msgid "A port which will be used to listen to webhooks." msgstr "Порт, который будет использован для мониторинга вебхуков." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:6 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:5 msgid "Path to the webhook. Defaults to /token" msgstr "Путь к вебхуку. По умолчанию /token." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:7 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:6 msgid "Path to the certificate file." msgstr "Путь к файлу с SSL сертификатом." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:8 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:7 msgid "Path to the certificate key file." msgstr "Путь к файлу с приватным ключом SSL сертификата." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:9 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:8 msgid "Webhook URL." msgstr "URL вебхука." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:10 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:9 msgid "" "Maximum allowed number of simultaneous HTTPS connections to the webhook " "for update delivery, 1-100. Defaults to 40. Use lower values to limit the" " load on your bot's server, and higher values to increase your bot's " "throughput." msgstr "" -"Максимально-допустимое количество одновременных HTTPS подключений к вебхуку " -"для доставки апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения, " -" чтобы уменьшить нагрузку на ваш сервер и большие значения для увеличения " -"пропускной способности вашего бота." +"Максимально-допустимое количество одновременных HTTPS подключений к " +"вебхуку для доставки апдейтов, 1-100. По умолчанию 40. Используйте " +"меньшие значения, чтобы уменьшить нагрузку на ваш сервер и большие " +"значения для увеличения пропускной способности вашего бота." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:11 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:10 msgid "" "A JSON-serialized list of the update types you want your bot to receive. " "For example, specify [“message”, “edited_channel_post”, “callback_query”]" @@ -3875,29 +4058,39 @@ msgstr "" "Например, укажите [“message”, “edited_channel_post”, “callback_query”], " "чтобы получать апдейты только этих видов. Полный список доступных видов " "апдейтов - util.update_types. Укажите пустой список, чтобы получать все " -"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " -"последняя настройка." +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет " +"использована последняя настройка." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:12 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:11 msgid "" "The fixed IP address which will be used to send webhook requests instead " "of the IP address resolved through DNS" msgstr "" -"Фиксированный IP адрес, который будет использоваться для отправки запросов " -"к вебхуку вместо IP адреса, полученного через DNS" +"Фиксированный IP адрес, который будет использоваться для отправки " +"запросов к вебхуку вместо IP адреса, полученного через DNS" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:13 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:12 msgid "Pass True to drop all pending updates" msgstr "Передайте True, чтобы проигнорировать все апдейты, полученные до запуска" -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:14 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:13 msgid "Integer. Request connection timeout" msgstr "Integer. Тайм-аут запроса на подключение." -#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:15 +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:14 msgid "Secret token to be used to verify the webhook request." msgstr "Секретный токен для верификации запроса к вебхуку." +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:15 +#, fuzzy +msgid "Length of a secret token, defaults to 20" +msgstr "Тайм-аут запроса, по умолчанию None" + +#: of telebot.async_telebot.AsyncTeleBot.run_webhooks:16 +#, fuzzy +msgid "Debug mode, defaults to False" +msgstr "Глобальный parse_mode, по умолчанию None" + #: of telebot.async_telebot.AsyncTeleBot.send_animation:1 msgid "" "Use this method to send animation files (GIF or H.264/MPEG-4 AVC video " @@ -3905,10 +4098,10 @@ msgid "" "currently send animation files of up to 50 MB in size, this limit may be " "changed in the future." msgstr "" -"Используйте этот метод, чтобы отправить гифку (GIF или H.264/MPEG-4 AVC видео " -"без звука). В случае успеха, возвращается отправленное сообщение (Message). На " -"текущий момент, боты могут отправлять гифки весом до 50 MB, это ограничение " -"может измениться в будущем." +"Используйте этот метод, чтобы отправить гифку (GIF или H.264/MPEG-4 AVC " +"видео без звука). В случае успеха, возвращается отправленное сообщение " +"(Message). На текущий момент, боты могут отправлять гифки весом до 50 MB," +" это ограничение может измениться в будущем." #: of telebot.async_telebot.AsyncTeleBot.send_animation:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" @@ -3922,9 +4115,9 @@ msgid "" "new animation using multipart/form-data." msgstr "" "Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гифку, " -"которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить гифку из интернета или загрузите новую гифку " -"с помощью multipart/form-data." +"которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить гифку из интернета или загрузите новую " +"гифку с помощью multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_animation:13 msgid "Duration of sent animation in seconds" @@ -3953,18 +4146,19 @@ msgstr "" "Обложка отправленного файла; может быть проигнорирована, если генерация " "обложки поддерживается на стороне сервера. Обложка должна быть картинкой " "в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " -"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" -"data. Обложки не могут быть использованы повторно и могут быть загружены " -"только как новый файл, так что вы можете передать “attach://” " -"если обложка была загружена с помощью multipart/form-data под именем ." +"превышать 320. Игнорируется, если файл не загружен с помощью multipart" +"/form-data. Обложки не могут быть использованы повторно и могут быть " +"загружены только как новый файл, так что вы можете передать " +"“attach://” если обложка была загружена с помощью " +"multipart/form-data под именем ." #: of telebot.async_telebot.AsyncTeleBot.send_animation:28 msgid "" "Animation caption (may also be used when resending animation by file_id)," " 0-1024 characters after entities parsing" msgstr "" -"Подпись к гифке (может быть использована при повторной отправке гифки по file_id), " -"0-1024 символа после форматирования" +"Подпись к гифке (может быть использована при повторной отправке гифки по " +"file_id), 0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_animation:31 msgid "Mode for parsing entities in the animation caption" @@ -3985,10 +4179,17 @@ msgid "Identifier of a message thread, in which the video will be sent" msgstr "id топика, в который будет отправлено видео" #: of telebot.async_telebot.AsyncTeleBot.send_animation:60 - msgid "Pass True, if the animation should be sent as a spoiler" msgstr "Передайте True, если гифку нужно отправить как спойлер" +#: of telebot.async_telebot.AsyncTeleBot.send_animation:63 +#: telebot.async_telebot.AsyncTeleBot.send_audio:63 +#: telebot.async_telebot.AsyncTeleBot.send_document:56 +#: telebot.async_telebot.AsyncTeleBot.send_video:64 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:48 +msgid "Deprecated. Use thumbnail instead" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -3997,11 +4198,12 @@ msgid "" " audio files of up to 50 MB in size, this limit may be changed in the " "future." msgstr "" -"Используйте этот метод, чтобы отправить аудио, если вы хотите, чтобы клиенты " -"(приложения) Telegram проигрывали их в музыкальном проигрывателе. Ваше аудио " -"должно быть в формате .MP3 или .M4A. В случае успеха, возвращается отправленное " -"сообщение (Message). На текущий момент, боты могут отправлять аудио весом до 50 MB, " -"это ограничение может измениться в будущем." +"Используйте этот метод, чтобы отправить аудио, если вы хотите, чтобы " +"клиенты (приложения) Telegram проигрывали их в музыкальном проигрывателе." +" Ваше аудио должно быть в формате .MP3 или .M4A. В случае успеха, " +"возвращается отправленное сообщение (Message). На текущий момент, боты " +"могут отправлять аудио весом до 50 MB, это ограничение может измениться в" +" будущем." #: of telebot.async_telebot.AsyncTeleBot.send_audio:5 msgid "For sending voice messages, use the send_voice method instead." @@ -4020,9 +4222,9 @@ msgid "" "format." msgstr "" "Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " -"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " -"multipart/form-data. Аудио должно быть в формате .MP3 или .M4A." +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить аудио из интернета или загрузите новое с " +"помощью multipart/form-data. Аудио должно быть в формате .MP3 или .M4A." #: of telebot.async_telebot.AsyncTeleBot.send_audio:17 msgid "Audio caption, 0-1024 characters after entities parsing" @@ -4045,8 +4247,8 @@ msgid "" "Mode for parsing entities in the audio caption. See formatting options " "for more details." msgstr "" -"Режим форматирования подписи к аудио. См. formatting options " -"для получения подробностей." +"Режим форматирования подписи к аудио. См. formatting options для " +"получения подробностей." #: of telebot.async_telebot.AsyncTeleBot.send_audio:45 msgid "" @@ -4061,10 +4263,11 @@ msgstr "" "Обложка отправленного файла; может быть проигнорирована, если генерация " "обложки поддерживается на стороне сервера. Обложка должна быть картинкой " "в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " -"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" -"data. Обложки не могут быть использованы повторно и могут быть загружены " -"только как новый файл, так что вы можете передать “attach://” " -"если обложка была загружена с помощью multipart/form-data под именем ." +"превышать 320. Игнорируется, если файл не загружен с помощью multipart" +"/form-data. Обложки не могут быть использованы повторно и могут быть " +"загружены только как новый файл, так что вы можете передать " +"“attach://” если обложка была загружена с помощью " +"multipart/form-data под именем ." #: of telebot.async_telebot.AsyncTeleBot.send_audio:51 #: telebot.async_telebot.AsyncTeleBot.send_document:35 @@ -4074,8 +4277,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the caption, " "which can be specified instead of parse_mode" msgstr "" -"Список отформатированных частей подписи в формате JSON, можно использовать вместо " -"parse_mode" +"Список отформатированных частей подписи в формате JSON, можно " +"использовать вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:1 msgid "" @@ -4086,8 +4289,8 @@ msgid "" msgstr "" "Используйте этот метод, когда вам нужно показать пользователю, что бот " "что-то делает. Статус устанавливается на 5 секунд или менее (когда от " -"бота приходит сообщение, клиенты (приложения) Telegram убирают статус typing). " -"Возвращает True в случае успеха." +"бота приходит сообщение, клиенты (приложения) Telegram убирают статус " +"typing). Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:5 msgid "" @@ -4098,8 +4301,9 @@ msgid "" msgstr "" "Пример: ImageBot-у требуется время, чтобы обработать запрос и загрузить " "изображение. Вместо отправки текстового сообщения “Отправка изображения, " -"пожалуйста подождите…”, бот может использовать sendChatAction с параметром " -"action = upload_photo. Пользователь увидит статус бота “sending photo”." +"пожалуйста подождите…”, бот может использовать sendChatAction с " +"параметром action = upload_photo. Пользователь увидит статус бота " +"“sending photo”." #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" @@ -4124,13 +4328,12 @@ msgid "" msgstr "" "Тип действия. Выберите один, в зависимости от того, что получит " "пользователь: typing для текстовых сообщений, upload_photo для фото, " -"record_video или upload_video для видео, record_voice или upload_voice для " -"голосовых сообщений, upload_document для файлов, choose_sticker для " -"стикеров, find_location для данных о местоположении, record_video_note или " -"upload_video_note для видео заметок (кружочков)." +"record_video или upload_video для видео, record_voice или upload_voice " +"для голосовых сообщений, upload_document для файлов, choose_sticker для " +"стикеров, find_location для данных о местоположении, record_video_note " +"или upload_video_note для видео заметок (кружочков)." #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:22 - msgid "The thread to which the message will be sent(supergroups only)" msgstr "id топика, в который сообщение будет отправлено(только для супергрупп)" @@ -4139,8 +4342,8 @@ msgid "" "Use this method to send phone contacts. On success, the sent Message is " "returned." msgstr "" -"Используйте этот метод, чтобы отправить контакт. В случае успеха, возвращается " -"отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить контакт. В случае успеха, " +"возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_contact:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" @@ -4169,8 +4372,8 @@ msgid "" "Pass True, if the message should be sent even if one of the specified " "replied-to messages is not found." msgstr "" -"Передайте True, если сообщение должно быть отправлено даже если одно из сообщений, " -"на которые нужно ответить, не найдено." +"Передайте True, если сообщение должно быть отправлено даже если одно из " +"сообщений, на которые нужно ответить, не найдено." #: of telebot.async_telebot.AsyncTeleBot.send_contact:41 #: telebot.async_telebot.AsyncTeleBot.send_venue:54 @@ -4182,8 +4385,9 @@ msgid "" "Use this method to send an animated emoji that will display a random " "value. On success, the sent Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить анимированный эмодзи, который покажет " -"случайное значение. В случае успеха, возвращается отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить анимированный эмодзи, который " +"покажет случайное значение. В случае успеха, возвращается отправленное " +"сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_dice:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" @@ -4196,9 +4400,9 @@ msgid "" " and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " "to “🎲”" msgstr "" -"Эмодзи, на котором основана анимация. На текущий момент, должно быть одним из " -"“🎲”, “🎯”, “🏀”, “⚽”, “🎳”, или “🎰”. Значение может быть 1-6 для “🎲”, “🎯”" -" и “🎳”, 1-5 для “🏀” и “⚽”, и 1-64 для “🎰”. По умолчанию “🎲”" +"Эмодзи, на котором основана анимация. На текущий момент, должно быть " +"одним из “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, или “🎰”. Значение может быть 1-6 для " +"“🎲”, “🎯” и “🎳”, 1-5 для “🏀” и “⚽”, и 1-64 для “🎰”. По умолчанию “🎲”" #: of telebot.async_telebot.AsyncTeleBot.send_dice:29 msgid "Protects the contents of the sent message from forwarding" @@ -4225,18 +4429,18 @@ msgid "" "String for Telegram to get a file from the Internet, or upload a new one " "using multipart/form-data" msgstr "" -"(документ) Файл для отправки. Передайте file_id (String), чтобы отправить файл, " -"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить файл из интернета или загрузите новый с помощью " -"multipart/form-data" +"(документ) Файл для отправки. Передайте file_id (String), чтобы отправить" +" файл, который уже загружен на сервера Telegram (рекомендуется), " +"передайте HTTP URL (String), чтобы отправить файл из интернета или " +"загрузите новый с помощью multipart/form-data" #: of telebot.async_telebot.AsyncTeleBot.send_document:15 msgid "" "Document caption (may also be used when resending documents by file_id), " "0-1024 characters after entities parsing" msgstr "" -"Подпись к файлу (может быть использована при повторной отправке файла по file_id), " -"0-1024 символа после форматирования" +"Подпись к файлу (может быть использована при повторной отправке файла по " +"file_id), 0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_document:23 msgid "Mode for parsing entities in the document caption" @@ -4253,29 +4457,30 @@ msgid "" "“attach://” if the thumbnail was uploaded using " "multipart/form-data under " msgstr "" -"InputFile или String : Обложка отправленного файла; может быть проигнорирована, " -"если генерация обложки поддерживается на стороне сервера. Обложка должна " -"быть картинкой в формате JPEG и весить менее 200 kB. Ширина и высота обложки " -"не должны превышать 320. Игнорируется, если файл не загружен с помощью " -"multipart/form-data. Обложки не могут быть использованы повторно и могут быть " -"загружены только как новый файл, так что вы можете передать " -"“attach://” если обложка была загружена с помощью " -"multipart/form-data под именем ." +"InputFile или String : Обложка отправленного файла; может быть " +"проигнорирована, если генерация обложки поддерживается на стороне " +"сервера. Обложка должна быть картинкой в формате JPEG и весить менее 200 " +"kB. Ширина и высота обложки не должны превышать 320. Игнорируется, если " +"файл не загружен с помощью multipart/form-data. Обложки не могут быть " +"использованы повторно и могут быть загружены только как новый файл, так " +"что вы можете передать “attach://” если обложка была " +"загружена с помощью multipart/form-data под именем ." #: of telebot.async_telebot.AsyncTeleBot.send_document:41 msgid "" "allows to define file name that will be visible in the Telegram instead " "of original file name" msgstr "" -"позволяет задать имя файла, которое будет показано в Telegram вместо настоящего" +"позволяет задать имя файла, которое будет показано в Telegram вместо " +"настоящего" #: of telebot.async_telebot.AsyncTeleBot.send_document:44 msgid "" "Disables automatic server-side content type detection for files uploaded " "using multipart/form-data" msgstr "" -"Отключает автоматическое обнаружение типа файла на стороне сервера для файлов, " -"загруженных с помощью multipart/form-data" +"Отключает автоматическое обнаружение типа файла на стороне сервера для " +"файлов, загруженных с помощью multipart/form-data" #: of telebot.async_telebot.AsyncTeleBot.send_document:47 #: telebot.async_telebot.AsyncTeleBot.send_sticker:33 @@ -4296,8 +4501,8 @@ msgid "" "Short name of the game, serves as the unique identifier for the game. Set" " up your games via @BotFather." msgstr "" -"Короткое имя игры, служит в качестве уникального id игры. Настройте свои игры" -"через @BotFather." +"Короткое имя игры, служит в качестве уникального id игры. Настройте свои " +"игрычерез @BotFather." #: of telebot.async_telebot.AsyncTeleBot.send_game:20 msgid "Timeout in seconds for waiting for a response from the bot." @@ -4308,7 +4513,8 @@ msgid "" "Pass True, if content of the message needs to be protected from being " "viewed by the bot." msgstr "" -"Передайте True, если содержимое сообщение должно быть защищено от просмотра ботом." +"Передайте True, если содержимое сообщение должно быть защищено от " +"просмотра ботом." #: of telebot.async_telebot.AsyncTeleBot.send_game:29 msgid "Identifier of the thread to which the message will be sent." @@ -4346,8 +4552,8 @@ msgid "" "a marketing image for a service. People like it better when they see what" " they are paying for." msgstr "" -"URL фото продукта. Может быть фото товаров или рекламным изображением сервиса. " -"Людям больше нравится, когда они видят, за что платят." +"URL фото продукта. Может быть фото товаров или рекламным изображением " +"сервиса. Людям больше нравится, когда они видят, за что платят." #: of telebot.async_telebot.AsyncTeleBot.send_invoice:73 msgid "" @@ -4355,9 +4561,9 @@ msgid "" " price' button will be shown. If not empty, the first button must be a " "Pay button" msgstr "" -"JSON-сериализованный объект inline клавиатуры. Если пустой, будет показана " -"одна кнопка 'Pay total price'. Если не пустой, первая кнопка должна быть " -"кнопкой для оплаты" +"JSON-сериализованный объект inline клавиатуры. Если пустой, будет " +"показана одна кнопка 'Pay total price'. Если не пустой, первая кнопка " +"должна быть кнопкой для оплаты" #: of telebot.async_telebot.AsyncTeleBot.send_invoice:81 #: telebot.async_telebot.AsyncTeleBot.set_webhook:39 @@ -4403,8 +4609,8 @@ msgid "" "For live locations, a direction in which the user is moving, in degrees. " "Must be between 1 and 360 if specified." msgstr "" -"Для live местоположений, направление, в котором пользователь двигается, в градусах. " -"Должно быть между 1 и 360, если указано." +"Для live местоположений, направление, в котором пользователь двигается, в" +" градусах. Должно быть между 1 и 360, если указано." #: of telebot.async_telebot.AsyncTeleBot.send_location:37 msgid "" @@ -4413,8 +4619,8 @@ msgid "" "if specified." msgstr "" "Для live местоположений, максимальное расстояние для уведомлений о " -"приближении другого участника чата, в метрах. Должно быть между 1 и 100000, " -"если указано." +"приближении другого участника чата, в метрах. Должно быть между 1 и " +"100000, если указано." #: of telebot.async_telebot.AsyncTeleBot.send_media_group:1 msgid "" @@ -4423,9 +4629,10 @@ msgid "" " messages of the same type. On success, an array of Messages that were " "sent is returned." msgstr "" -"Используйте этот метод, чтобы отправить группу фото, видео, файлов или аудио " -"как альбом. Файлы и аудио могут быть сгруппированы в альбом только с сообщениями " -"того же типа. В случае успеха, возвращается массив отправленных сообщений (Message)." +"Используйте этот метод, чтобы отправить группу фото, видео, файлов или " +"аудио как альбом. Файлы и аудио могут быть сгруппированы в альбом только " +"с сообщениями того же типа. В случае успеха, возвращается массив " +"отправленных сообщений (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_media_group:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" @@ -4436,16 +4643,16 @@ msgid "" "A JSON-serialized array describing messages to be sent, must include 2-10" " items" msgstr "" -"JSON-сериализованный массив, описывающий сообщения для отправки, должен включать " -"от 2 до 10 элементов" +"JSON-сериализованный массив, описывающий сообщения для отправки, должен " +"включать от 2 до 10 элементов" #: of telebot.async_telebot.AsyncTeleBot.send_media_group:12 msgid "" "Sends the messages silently. Users will receive a notification with no " "sound." msgstr "" -"Отправить сообщение, при получении которого пользователя пользователи получат " -"уведомление без звука." +"Отправить сообщение, при получении которого пользователя пользователи " +"получат уведомление без звука." #: of telebot.async_telebot.AsyncTeleBot.send_media_group:27 msgid "Identifier of a message thread, in which the messages will be sent" @@ -4466,8 +4673,9 @@ msgid "" "characters, use the `split_string` or `smart_split` function in util.py." msgstr "" "Предупреждение: Не отправляйте больше 4096 символов в одном сообщении, " -"иначе вы рискуете получить ошибку HTTP 414. Если вам нужно отправить больше " -"4096 символов, используйте функцию `split_string` или `smart_split` из util.py." +"иначе вы рискуете получить ошибку HTTP 414. Если вам нужно отправить " +"больше 4096 символов, используйте функцию `split_string` или " +"`smart_split` из util.py." #: of telebot.async_telebot.AsyncTeleBot.send_message:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" @@ -4482,22 +4690,22 @@ msgid "" "List of special entities that appear in message text, which can be " "specified instead of parse_mode" msgstr "" -"Список отформатированных частей в тексте сообщения, " -"можно использовать вместо parse_mode" +"Список отформатированных частей в тексте сообщения, можно использовать " +"вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.send_message:27 msgid "" "If True, the message content will be hidden for all users except for the " "target user" msgstr "" -"Если True, содержимое сообщения будет скрыто от всех пользователей, кроме " -"заданного" +"Если True, содержимое сообщения будет скрыто от всех пользователей, кроме" +" заданного" #: of telebot.async_telebot.AsyncTeleBot.send_photo:1 msgid "Use this method to send photos. On success, the sent Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить фото. В случае успеха, возвращается " -"отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить фото. В случае успеха, " +"возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_photo:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" @@ -4513,25 +4721,25 @@ msgid "" "must be at most 20." msgstr "" "Фото для отправки. Передайте file_id (String), чтобы отправить фото, " -"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить фото из интернета или загрузите новое с помощью " -"multipart/form-data. Фото должно весить не более 10 MB. Ширина и высота фото не " -"должны суммарно превышать 10000. Отношение ширины и высоты должно быть не более 20." +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить фото из интернета или загрузите новое с " +"помощью multipart/form-data. Фото должно весить не более 10 MB. Ширина и " +"высота фото не должны суммарно превышать 10000. Отношение ширины и высоты" +" должно быть не более 20." #: of telebot.async_telebot.AsyncTeleBot.send_photo:13 msgid "" "Photo caption (may also be used when resending photos by file_id), 0-1024" " characters after entities parsing" msgstr "" -"Подпись к фото (может быть использована при повторной отправке файла по file_id), " -"0-1024 символа после форматирования" +"Подпись к фото (может быть использована при повторной отправке файла по " +"file_id), 0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_photo:16 msgid "Mode for parsing entities in the photo caption." msgstr "Режим форматирования подписи к фото." #: of telebot.async_telebot.AsyncTeleBot.send_photo:45 - msgid "Pass True, if the photo should be sent as a spoiler" msgstr "Передайте True, если фото должно быть отправлено как спойлер" @@ -4540,8 +4748,8 @@ msgid "" "Use this method to send a native poll. On success, the sent Message is " "returned." msgstr "" -"Используйте этот метод, чтобы отправить опрос. В случае успеха, возвращается " -"отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить опрос. В случае успеха, " +"возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_poll:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" @@ -4570,16 +4778,16 @@ msgid "" "True, if the poll allows multiple answers, ignored for polls in quiz " "mode, defaults to False" msgstr "" -"True, если опрос позволяет выбрать несколько вариантов ответа, игнорируется в " -"опросах вида “quiz”, по умолчанию False" +"True, если опрос позволяет выбрать несколько вариантов ответа, " +"игнорируется в опросах вида “quiz”, по умолчанию False" #: of telebot.async_telebot.AsyncTeleBot.send_poll:24 msgid "" "0-based identifier of the correct answer option. Available only for polls" " in quiz mode, defaults to None" msgstr "" -"Индекс правильного варианта ответа, начиная с 0. Доступно только для опросов " -"вида “quiz”, по умолчанию None" +"Индекс правильного варианта ответа, начиная с 0. Доступно только для " +"опросов вида “quiz”, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.send_poll:28 msgid "" @@ -4604,8 +4812,8 @@ msgid "" "Amount of time in seconds the poll will be active after creation, 5-600. " "Can't be used together with close_date." msgstr "" -"Время в секундах, в течение которого опрос будет активен, 5-600. " -"Нельзя использовать вместо с close_date." +"Время в секундах, в течение которого опрос будет активен, 5-600. Нельзя " +"использовать вместо с close_date." #: of telebot.async_telebot.AsyncTeleBot.send_poll:38 msgid "Point in time (Unix timestamp) when the poll will be automatically closed." @@ -4616,12 +4824,14 @@ msgid "" "Pass True, if the poll needs to be immediately closed. This can be useful" " for poll preview." msgstr "" -"Передайте True, если опрос должен быть завершен немедленно. Может быть полезно " -"для предпросмотра опроса." +"Передайте True, если опрос должен быть завершен немедленно. Может быть " +"полезно для предпросмотра опроса." #: of telebot.async_telebot.AsyncTeleBot.send_poll:50 msgid "Pass True, if the poll allows multiple options to be voted simultaneously." -msgstr "Передайте True, если опрос позволяет выбрать несколько вариантов одновременно." +msgstr "" +"Передайте True, если опрос позволяет выбрать несколько вариантов " +"одновременно." #: of telebot.async_telebot.AsyncTeleBot.send_poll:57 msgid "Timeout in seconds for waiting for a response from the user." @@ -4632,8 +4842,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the " "explanation, which can be specified instead of parse_mode" msgstr "" -"JSON-сериализованный список отформатированных частей explanation, " -"можно использовать вместо parse_mode" +"JSON-сериализованный список отформатированных частей explanation, можно " +"использовать вместо parse_mode" #: of telebot.async_telebot.AsyncTeleBot.send_poll:67 msgid "The identifier of a message thread, in which the poll will be sent" @@ -4644,8 +4854,8 @@ msgid "" "Use this method to send static .WEBP, animated .TGS, or video .WEBM " "stickers. On success, the sent Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить статичный .WEBP, анимированный .TGS, " -"или видео .WEBM стикер. В случае успеха возвращает отправленное " +"Используйте этот метод, чтобы отправить статичный .WEBP, анимированный " +".TGS, или видео .WEBM стикер. В случае успеха возвращает отправленное " "сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_sticker:4 @@ -4660,21 +4870,25 @@ msgid "" " multipart/form-data." msgstr "" "Стикер для отправки. Передайте file_id (String), чтобы отправить файл, " -"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить .webp файл из интернета или загрузите новый с помощью " -"multipart/form-data." +"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP " +"URL (String), чтобы отправить .webp файл из интернета или загрузите новый" +" с помощью multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_sticker:21 msgid "to disable the notification" msgstr "отключить уведомление" +#: of telebot.async_telebot.AsyncTeleBot.send_sticker:39 +msgid "Emoji associated with the sticker; only for just uploaded stickers" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_venue:1 msgid "" "Use this method to send information about a venue. On success, the sent " "Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить информацию о месте. В случае успеха " -"возвращается отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить информацию о месте. В случае " +"успеха возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_venue:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" @@ -4723,8 +4937,9 @@ msgid "" "Use this method to send video files, Telegram clients support mp4 videos " "(other formats may be sent as Document)." msgstr "" -"Используйте этот метод, чтобы отправить видео, клиенты (приложения) Telegram " -"поддерживают mp4 видео (другие форматы могут быть отправлены как Document)." +"Используйте этот метод, чтобы отправить видео, клиенты (приложения) " +"Telegram поддерживают mp4 видео (другие форматы могут быть отправлены как" +" Document)." #: of telebot.async_telebot.AsyncTeleBot.send_video:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" @@ -4758,8 +4973,8 @@ msgid "" "Video caption (may also be used when resending videos by file_id), 0-1024" " characters after entities parsing" msgstr "" -"Подпись к видео (может быть использована при повторной отправке файла по file_id), " -"0-1024 символа после форматирования" +"Подпись к видео (может быть использована при повторной отправке файла по " +"file_id), 0-1024 символа после форматирования" #: of telebot.async_telebot.AsyncTeleBot.send_video:26 msgid "Mode for parsing entities in the video caption" @@ -4770,7 +4985,6 @@ msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "Передайте True, если загруженное видео подходит для стриминга" #: of telebot.async_telebot.AsyncTeleBot.send_video:61 - msgid "Pass True, if the video should be sent as a spoiler" msgstr "Передайте True, если видео должно быть отправлено как спойлер" @@ -4780,10 +4994,10 @@ msgid "" "to 1 minute long. Use this method to send video messages. On success, the" " sent Message is returned." msgstr "" -"Начиная с версии v.4.0, клиенты(приложения) Telegram поддерживают скругленные " -"квадратные MPEG4 видео длительностью до минуты. Используйте этот метод, чтобы " -"отправить видео заметку (кружочек). В случае успеха возвращается отправленное " -"сообщение (Message)." +"Начиная с версии v.4.0, клиенты(приложения) Telegram поддерживают " +"скругленные квадратные MPEG4 видео длительностью до минуты. Используйте " +"этот метод, чтобы отправить видео заметку (кружочек). В случае успеха " +"возвращается отправленное сообщение (Message)." #: of telebot.async_telebot.AsyncTeleBot.send_video_note:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" @@ -4796,10 +5010,10 @@ msgid "" "multipart/form-data. Sending video notes by a URL is currently " "unsupported" msgstr "" -"Видео заметка для отправки. Передайте file_id (String), чтобы отправить видео " -"заметку, которая уже загружена на сервера Telegram или загрузите новую с помощью " -"multipart/form-data. На текущий момент, отправка видео заметок по URL не " -"поддерживается" +"Видео заметка для отправки. Передайте file_id (String), чтобы отправить " +"видео заметку, которая уже загружена на сервера Telegram или загрузите " +"новую с помощью multipart/form-data. На текущий момент, отправка видео " +"заметок по URL не поддерживается" #: of telebot.async_telebot.AsyncTeleBot.send_video_note:16 msgid "Video width and height, i.e. diameter of the video message" @@ -4821,9 +5035,9 @@ msgstr "" "Используйте этот метод, чтобы отправить голосовое сообщение. Ваше аудио " "должно быть в формате .OGG и закодировано с помощью OPUS (другие форматы " "можно отправить как Audio или Document). В случае успеха возвращается " -"отправленное сообщение (Message). На текущий момент, боты могут отправлять " -"голосовые сообщения весом до 50 MB, это ограничение может быть изменено в " -"будущем." +"отправленное сообщение (Message). На текущий момент, боты могут " +"отправлять голосовые сообщения весом до 50 MB, это ограничение может быть" +" изменено в будущем." #: of telebot.async_telebot.AsyncTeleBot.send_voice:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" @@ -4837,9 +5051,9 @@ msgid "" "multipart/form-data." msgstr "" "Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " -"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " -"multipart/form-data." +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить аудио из интернета или загрузите новое с " +"помощью multipart/form-data." #: of telebot.async_telebot.AsyncTeleBot.send_voice:14 msgid "Voice message caption, 0-1024 characters after entities parsing" @@ -4854,8 +5068,8 @@ msgid "" "Mode for parsing entities in the voice message caption. See formatting " "options for more details." msgstr "" -"Режим форматирования подписи к голосовому сообщению. См. formatting options " -"для получения подробностей." +"Режим форматирования подписи к голосовому сообщению. См. formatting " +"options для получения подробностей." #: of telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:1 msgid "" @@ -4877,9 +5091,7 @@ msgstr "" msgid "" "New custom title for the administrator; 0-16 characters, emoji are not " "allowed" -msgstr "" -"Новое кастомное звание администратора; 0-16 символов, эмодзи не " -"разрешены" +msgstr "Новое кастомное звание администратора; 0-16 символов, эмодзи не разрешены" #: of telebot.async_telebot.AsyncTeleBot.set_chat_description:1 msgid "" @@ -4924,14 +5136,16 @@ msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be changed." msgstr "" -"Уникальный id приватного чата. Если не указан, будет изменена кнопка меню " -"по умолчанию." +"Уникальный id приватного чата. Если не указан, будет изменена кнопка меню" +" по умолчанию." #: of telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:11 msgid "" "A JSON-serialized object for the new bot's menu button. Defaults to " "MenuButtonDefault" -msgstr "JSON-сериализованный объект новой кнопки меню. По умолчанию MenuButtonDefault" +msgstr "" +"JSON-сериализованный объект новой кнопки меню. По умолчанию " +"MenuButtonDefault" #: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:1 msgid "" @@ -4939,9 +5153,9 @@ msgid "" "must be an administrator in the group or a supergroup for this to work " "and must have the can_restrict_members admin rights." msgstr "" -"Используйте этот метод, чтобы задать права по умолчанию для всех участников чата. " -"Бот должен быть администратором группы или супергруппы и иметь права администратора " -"can_restrict_members." +"Используйте этот метод, чтобы задать права по умолчанию для всех " +"участников чата. Бот должен быть администратором группы или супергруппы и" +" иметь права администратора can_restrict_members." #: of telebot.async_telebot.AsyncTeleBot.set_chat_permissions:5 msgid "" @@ -4964,11 +5178,11 @@ msgid "" "only work if the ‘All Members Are Admins’ setting is off in the target " "group." msgstr "" -"Используйте этот метод, чтобы задать новую аватарку чата. В приватных чатах " -"аватарки менять нельзя. Бот должен быть администратором чата и иметь " -"соответствующие права администратора. Возвращает True в случае успеха. " -"Примечание: В обычных группах (не супергруппы), этот метод будет работать " -"только если настройка ‘All Members Are Admins’ отключена." +"Используйте этот метод, чтобы задать новую аватарку чата. В приватных " +"чатах аватарки менять нельзя. Бот должен быть администратором чата и " +"иметь соответствующие права администратора. Возвращает True в случае " +"успеха. Примечание: В обычных группах (не супергруппы), этот метод будет " +"работать только если настройка ‘All Members Are Admins’ отключена." #: of telebot.async_telebot.AsyncTeleBot.set_chat_photo:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" @@ -4986,10 +5200,11 @@ msgid "" "optionally returned in getChat requests to check if the bot can use this " "method. Returns True on success." msgstr "" -"Используйте этот метод, чтобы задать стикерпак супергруппы. Бот должен быть " -"администратором чата и иметь соответствующие права администратора. " -"Используйте атрибут can_set_sticker_set, возвращаемые методом getChat, чтобы " -"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." +"Используйте этот метод, чтобы задать стикерпак супергруппы. Бот должен " +"быть администратором чата и иметь соответствующие права администратора. " +"Используйте атрибут can_set_sticker_set, возвращаемые методом getChat, " +"чтобы проверить, что бот может использовать этот метод. Возвращает True в" +" случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_chat_sticker_set:5 msgid "" @@ -5018,8 +5233,8 @@ msgstr "" "Используйте этот метод, чтобы изменить название чата. В приватных чатах " "изменить название нельзя. Бот должен быть администратором чата и иметь " "соответствующие права админа. Возвращает True в случае успеха. " -"Примечание: В обычных группах (не супергруппы), этот метод будет работать " -"только если настройка ‘All Members Are Admins’ отключена." +"Примечание: В обычных группах (не супергруппы), этот метод будет работать" +" только если настройка ‘All Members Are Admins’ отключена." #: of telebot.async_telebot.AsyncTeleBot.set_chat_title:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" @@ -5029,6 +5244,24 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#se msgid "New chat title, 1-255 characters" msgstr "Новое название чата, 1-255 символов" +#: of +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:1 +#, fuzzy +msgid "" +"Use this method to set the thumbnail of a custom emoji sticker set. " +"Returns True on success." +msgstr "" +"Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " +"обложки могут быть заданы только для анимированных стикерпаков. " +"Возвращает True в случае успеха." + +#: of +#: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:7 +msgid "" +"Custom emoji identifier of a sticker from the sticker set; pass an empty " +"string to drop the thumbnail and use the first sticker as the thumbnail." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.set_game_score:1 msgid "Sets the value of points in the game to a specific user." msgstr "Задаёт количество очков пользователя в игре." @@ -5046,8 +5279,8 @@ msgid "" "Pass True, if the high score is allowed to decrease. This can be useful " "when fixing mistakes or banning cheaters" msgstr "" -"Передайте True, если количество очков могут быть уменьшено. Может быть полезно " -"при исправлении ошибок или бане читеров" +"Передайте True, если количество очков могут быть уменьшено. Может быть " +"полезно при исправлении ошибок или бане читеров" #: of telebot.async_telebot.AsyncTeleBot.set_game_score:23 msgid "" @@ -5062,8 +5295,8 @@ msgid "" "On success, if the message was sent by the bot, returns the edited " "Message, otherwise returns True." msgstr "" -"В случае успеха, если сообщение было отправлено ботом, возвращает измененное " -"сообщение (Message), иначе возвращает True." +"В случае успеха, если сообщение было отправлено ботом, возвращает " +"измененное сообщение (Message), иначе возвращает True." #: of telebot.async_telebot.AsyncTeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." @@ -5084,10 +5317,11 @@ msgid "" "rights will be suggested to users, but they are are free to modify the " "list before adding the bot. Returns True on success." msgstr "" -"Используйте этот метод, чтобы изменить права администратора по умолчанию, " -"запрашиваемые при добавлении бота в группу или канал в качестве администратора. " -"Эти права будут предложены пользователям, но пользователи могут изменить список " -"перед добавлением бота. Возвращает True в случае успеха." +"Используйте этот метод, чтобы изменить права администратора по умолчанию," +" запрашиваемые при добавлении бота в группу или канал в качестве " +"администратора. Эти права будут предложены пользователям, но пользователи" +" могут изменить список перед добавлением бота. Возвращает True в случае " +"успеха." #: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:7 msgid "" @@ -5102,8 +5336,9 @@ msgid "" "A JSON-serialized object describing new default administrator rights. If " "not specified, the default administrator rights will be cleared." msgstr "" -"JSON-сериалиованный объект, описывающий новые права администратора по умолчанию. " -"Если не указан, права администратора по умолчанию будут сброшены." +"JSON-сериалиованный объект, описывающий новые права администратора по " +"умолчанию. Если не указан, права администратора по умолчанию будут " +"сброшены." #: of telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:13 msgid "" @@ -5111,8 +5346,87 @@ msgid "" "channels. Otherwise, the default administrator rights of the bot for " "groups and supergroups will be changed." msgstr "" -"Передайте True, чтобы изменить права администратора по умолчанию в каналах. " -"Иначе, будут изменены права администратора по умолчанию для групп и супергрупп." +"Передайте True, чтобы изменить права администратора по умолчанию в " +"каналах. Иначе, будут изменены права администратора по умолчанию для " +"групп и супергрупп." + +#: of telebot.async_telebot.AsyncTeleBot.set_my_description:1 +#, fuzzy +msgid "" +"Use this method to change the bot's description, which is shown in the " +"chat with the bot if the chat is empty. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы изменить кнопку меню в приватном чате или " +"кнопку меню по умолчанию. Возвращает True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.set_my_description:5 +msgid "" +"New bot description; 0-512 characters. Pass an empty string to remove the" +" dedicated description for the given language." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_description:8 +#, fuzzy +msgid "" +"A two-letter ISO 639-1 language code. If empty, the description will be " +"applied to all users for whose language there is no dedicated " +"description." +msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:1 +#, fuzzy +msgid "Use this method to change the bot's name. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы изменить кнопку меню в приватном чате или " +"кнопку меню по умолчанию. Возвращает True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:3 +#, fuzzy +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmyname" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendgame" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:5 +msgid "" +"Optional. New bot name; 0-64 characters. Pass an empty string to remove " +"the dedicated name for the given language." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_name:8 +#, fuzzy +msgid "" +"Optional. A two-letter ISO 639-1 language code. If empty, the name will " +"be shown to all users for whose language there is no dedicated name." +msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_short_description:1 +msgid "" +"Use this method to change the bot's short description, which is shown on " +"the bot's profile page and is sent together with the link when users " +"share the bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_short_description:5 +msgid "" +"New short description for the bot; 0-120 characters. Pass an empty string" +" to remove the dedicated short description for the given language." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_my_short_description:8 +#, fuzzy +msgid "" +"A two-letter ISO 639-1 language code. If empty, the short description " +"will be applied to all users for whose language there is no dedicated " +"short description." +msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" #: of telebot.async_telebot.AsyncTeleBot.set_state:1 msgid "Sets a new state of a user." @@ -5126,21 +5440,73 @@ msgid "" "chat with a bot." msgstr "" "Вы должны указать и user id и chat id, чтобы задать состояние (стейт) " -"пользователя в чате. Иначе, если вы укажете только user_id, chat_id будет " -"равен user_id, что означает смену состояния (стейта) пользователя в его " +"пользователя в чате. Иначе, если вы укажете только user_id, chat_id будет" +" равен user_id, что означает смену состояния (стейта) пользователя в его " "приватном чате с ботом." #: of telebot.async_telebot.AsyncTeleBot.set_state:12 msgid "new state. can be string, integer, or :class:`telebot.types.State`" -msgstr "новое состояние (стейт). может быть строкой, числом или :class:`telebot.types.State`" +msgstr "" +"новое состояние (стейт). может быть строкой, числом или " +":class:`telebot.types.State`" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:1 +#, fuzzy +msgid "" +"Use this method to set the emoji list of a sticker set. Returns True on " +"success." +msgstr "" +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного " +"ботом. Возвращает True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:7 +#, fuzzy +msgid "List of emojis" +msgstr "список апдейтов" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:1 +msgid "" +"Use this method to change search keywords assigned to a regular or custom" +" emoji sticker. The sticker must belong to a sticker set created by the " +"bot. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:5 +#: telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:5 +#, fuzzy +msgid "File identifier of the sticker." +msgstr "id файла стикера" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_keywords:8 +msgid "" +"A JSON-serialized list of 0-20 search keywords for the sticker with total" +" length of up to 64 characters" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:1 +#, fuzzy +msgid "" +"Use this method to change the mask position of a mask sticker. The " +"sticker must belong to a sticker set that was created by the bot. Returns" +" True on success." +msgstr "" +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного " +"ботом. Возвращает True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_mask_position:8 +#, fuzzy +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces." +msgstr "Позиция для размещения маски на лицах в формате JSON" #: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:1 msgid "" "Use this method to move a sticker in a set created by the bot to a " "specific position . Returns True on success." msgstr "" -"Используйте этот метод, чтобы передвинуть стикер в стикерпаке, созданном ботом, на заданную " -"позицию. Возвращает True в случае успеха." +"Используйте этот метод, чтобы передвинуть стикер в стикерпаке, созданном " +"ботом, на заданную позицию. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:3 msgid "" @@ -5154,17 +5520,17 @@ msgstr "" msgid "New sticker position in the set, zero-based" msgstr "Новая позиция стикера в стикерпаке, начиная с нуля" -#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:1 +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:1 msgid "" "Use this method to set the thumbnail of a sticker set. Animated " "thumbnails can be set for animated sticker sets only. Returns True on " "success." msgstr "" "Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " -"обложки могут быть заданы только для анимированных стикерпаков. Возвращает " -"True в случае успеха." +"обложки могут быть заданы только для анимированных стикерпаков. " +"Возвращает True в случае успеха." -#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumb:4 +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickersetthumb" @@ -5172,9 +5538,44 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#setstickersetthumb" +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:12 +msgid "" +"A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in " +"size and have a width and height of exactly 100px, or a .TGS animation " +"with a thumbnail up to 32 kilobytes in size (see " +"https://core.telegram.org/stickers#animated-sticker-requirements for " +"animated sticker technical requirements), or a WEBM video with the " +"thumbnail up to 32 kilobytes in size; see " +"https://core.telegram.org/stickers#video-sticker-requirements for video " +"sticker technical requirements. Pass a file_id as a String to send a file" +" that already exists on the Telegram servers, 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. More information on Sending Files ». Animated " +"and video sticker set thumbnails can't be uploaded via HTTP URL. If " +"omitted, then the thumbnail is dropped and the first sticker is used as " +"the thumbnail." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:1 +#, fuzzy +msgid "" +"Use this method to set the title of a created sticker set. Returns True " +"on success." +msgstr "" +"Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " +"обложки могут быть заданы только для анимированных стикерпаков. " +"Возвращает True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.set_sticker_set_title:7 +#, fuzzy +msgid "New sticker set title" +msgstr "Имя стикерпака" + #: of telebot.async_telebot.AsyncTeleBot.set_update_listener:1 msgid "Update listener is a function that gets any update." -msgstr "Задаёт функцию-листенер, которая будет вызвана при получении нового апдейта." +msgstr "" +"Задаёт функцию-листенер, которая будет вызвана при получении нового " +"апдейта." #: of telebot.async_telebot.AsyncTeleBot.set_update_listener:3 msgid "function that should get update." @@ -5193,10 +5594,11 @@ msgid "" "reasonable amount of attempts. Returns True on success." msgstr "" "Используйте этот метод, чтобы задать URL и получать входящие апдейты с " -"помощью вебхука. Как только у бота появляется апдейт, он будет отправлен с " -"помощью HTTPS POST запроса на заданный URL, содержащего JSON-сериализованный " -"Update. В случае неудачного запроса, отправка апдейта будет отменена после " -"разумного числа попыток. Возвращает True в случае успеха." +"помощью вебхука. Как только у бота появляется апдейт, он будет отправлен " +"с помощью HTTPS POST запроса на заданный URL, содержащего " +"JSON-сериализованный Update. В случае неудачного запроса, отправка " +"апдейта будет отменена после разумного числа попыток. Возвращает True в " +"случае успеха." #: of telebot.async_telebot.AsyncTeleBot.set_webhook:6 msgid "" @@ -5206,8 +5608,8 @@ msgid "" "secret token as content." msgstr "" "Если вы хотите удостовериться, что вебхук был задан вами, вы можете " -"задать секретный токен в параметре secret_token. Если указан, запрос " -"с апдейтом будет содержать хедер “X-Telegram-Bot-Api-Secret-Token” с " +"задать секретный токен в параметре secret_token. Если указан, запрос с " +"апдейтом будет содержать хедер “X-Telegram-Bot-Api-Secret-Token” с " "секретным токеном в качестве значения." #: of telebot.async_telebot.AsyncTeleBot.set_webhook:9 @@ -5219,16 +5621,16 @@ msgid "" "HTTPS URL to send updates to. Use an empty string to remove webhook " "integration, defaults to None" msgstr "" -"HTTPS URL для отправки апдейтов. Используйте пустую строку, чтобы удалить " -"вебхук, по умолчанию None" +"HTTPS URL для отправки апдейтов. Используйте пустую строку, чтобы удалить" +" вебхук, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:14 msgid "" "Upload your public key certificate so that the root certificate in use " "can be checked, defaults to None" msgstr "" -"Загрузите публичный ключ вашего SSL сертификата, чтобы корневой сертификат мог быть " -"проверен, по умолчанию None" +"Загрузите публичный ключ вашего SSL сертификата, чтобы корневой " +"сертификат мог быть проверен, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:17 msgid "" @@ -5237,10 +5639,10 @@ msgid "" "limit the load on your bot's server, and higher values to increase your " "bot's throughput, defaults to None" msgstr "" -"Максимально-допустимое количество одновременных HTTPS соединений для доставки " -"апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения для уменьшения " -"нагрузки на ваш сервер и большие значения, чтобы увеличить пропускную способность " -"вашего бота, по умолчанию None" +"Максимально-допустимое количество одновременных HTTPS соединений для " +"доставки апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения " +"для уменьшения нагрузки на ваш сервер и большие значения, чтобы увеличить" +" пропускную способность вашего бота, по умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 msgid "" @@ -5257,10 +5659,11 @@ msgstr "" "Например, укажите [“message”, “edited_channel_post”, “callback_query”], " "чтобы получать апдейты только этих видов. Полный список доступных видов " "апдейтов - util.update_types. Укажите пустой список, чтобы получать все " -"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " -"последняя настройка. Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " -"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " -"получены в течение короткого периода времени. По умолчанию None" +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет " +"использована последняя настройка. Пожалуйста учтите, чтобы этот параметр " +"не влияет на апдейты, отправленные до вызова setWebhooks, поэтому " +"нежелательные апдейты могут быть получены в течение короткого периода " +"времени. По умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:22 msgid "" @@ -5275,8 +5678,8 @@ msgstr "" "Например, укажите [“message”, “edited_channel_post”, “callback_query”], " "чтобы получать апдейты только этих видов. Полный список доступных видов " "апдейтов - util.update_types. Укажите пустой список, чтобы получать все " -"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " -"последняя настройка." +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет " +"использована последняя настройка." #: of telebot.async_telebot.AsyncTeleBot.set_webhook:27 msgid "" @@ -5284,17 +5687,18 @@ msgid "" " call to the setWebhook, so unwanted updates may be received for a short " "period of time. Defaults to None" msgstr "" -"Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " -"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " -"получены в течение короткого периода времени. По умолчанию None" +"Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, отправленные" +" до вызова setWebhooks, поэтому нежелательные апдейты могут быть получены" +" в течение короткого периода времени. По умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:32 msgid "" "The fixed IP address which will be used to send webhook requests instead " "of the IP address resolved through DNS, defaults to None" msgstr "" -"Фиксированный IP адрес, который будет использоваться для отправки запросов к вебхуку" -"вместо IP адреса, полученного с через DNS, по умолчанию None" +"Фиксированный IP адрес, который будет использоваться для отправки " +"запросов к вебхукувместо IP адреса, полученного с через DNS, по умолчанию" +" None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:42 msgid "" @@ -5303,10 +5707,10 @@ msgid "" "0-9, _ and - are allowed. The header is useful to ensure that the request" " comes from a webhook set by you. Defaults to None" msgstr "" -"Секретный токен для отправки в хедере “X-Telegram-Bot-Api-Secret-Token” " -"в каждом запросе с апдейтом, 1-256 символов. Разрешены только символы A-Z, a-z, " -"0-9, _ и -. Хедер полезен для, того чтобы удостовериться, что запрос приходит" -"с вебхука, установленного вами. По умолчанию None" +"Секретный токен для отправки в хедере “X-Telegram-Bot-Api-Secret-Token” в" +" каждом запросе с апдейтом, 1-256 символов. Разрешены только символы A-Z," +" a-z, 0-9, _ и -. Хедер полезен для, того чтобы удостовериться, что " +"запрос приходитс вебхука, установленного вами. По умолчанию None" #: of telebot.async_telebot.AsyncTeleBot.set_webhook:47 msgid ":obj:`bool` if the request was successful." @@ -5336,13 +5740,15 @@ msgid "" "price. As a parameter to the decorator function, it passes " ":class:`telebot.types.ShippingQuery` object." msgstr "" -"Обрабатывает shipping query. Только для инвойсов с гибкой ценой. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает shipping query. Только для инвойсов с гибкой ценой. В " +"качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.ShippingQuery`." #: of telebot.async_telebot.AsyncTeleBot.skip_updates:1 msgid "Skip existing updates. Only last update will remain on server." -msgstr "Пропускает существующие апдейты. На сервере останется только последний апдейт." +msgstr "" +"Пропускает существующие апдейты. На сервере останется только последний " +"апдейт." #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:1 msgid "" @@ -5350,9 +5756,10 @@ msgid "" "live_period expires. On success, if the message is not an inline message," " the edited Message is returned, otherwise True is returned." msgstr "" -"Используйте этот метод, чтобы остановить обновление live местоположения до " -"истечения live_period. В случае успеха, если сообщение не является inline сообщением," -"возвращается измененное сообщение (Message), иначе возвращается True." +"Используйте этот метод, чтобы остановить обновление live местоположения " +"до истечения live_period. В случае успеха, если сообщение не является " +"inline сообщением,возвращается измененное сообщение (Message), иначе " +"возвращается True." #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:4 msgid "" @@ -5367,32 +5774,32 @@ msgid "" "Required if inline_message_id is not specified. Identifier of the message" " with live location to stop" msgstr "" -"Обязательный, если не указан inline_message_id. id сообщения live местоположением, " -"которое нужно остановить" +"Обязательный, если не указан inline_message_id. id сообщения live " +"местоположением, которое нужно остановить" #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:12 msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message with live location to stop" msgstr "" -"Обязательный, если не указаны chat_id и message_id. id inline сообщения с live " -"местоположением, которое нужно остановить" +"Обязательный, если не указаны chat_id и message_id. id inline сообщения с" +" live местоположением, которое нужно остановить" #: of telebot.async_telebot.AsyncTeleBot.stop_message_live_location:22 msgid "" "On success, if the message is not an inline message, the edited Message " "is returned, otherwise True is returned." msgstr "" -"В случае успеха, если сообщение не является inline сообщением, возвращается " -"измененное сообщение (Message), иначе возвращается True." +"В случае успеха, если сообщение не является inline сообщением, " +"возвращается измененное сообщение (Message), иначе возвращается True." #: of telebot.async_telebot.AsyncTeleBot.stop_poll:1 msgid "" "Use this method to stop a poll which was sent by the bot. On success, the" " stopped Poll is returned." msgstr "" -"Используйте этот метод, чтобы завершить опрос, отправленный ботом. В случае успеха " -"возвращается завершенный опрос (Poll)." +"Используйте этот метод, чтобы завершить опрос, отправленный ботом. В " +"случае успеха возвращается завершенный опрос (Poll)." #: of telebot.async_telebot.AsyncTeleBot.stop_poll:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" @@ -5425,11 +5832,12 @@ msgid "" " If you don't want this, use the parameter only_if_banned." msgstr "" "Используйте этот метод, чтобы разбанить ранее кикнутого пользователя в " -"супергруппе или канале. Пользовать не вернется в группу или канал автоматически, " -"но сможет присоединиться с помощью ссылки и т.д. Бот должен быть администратором. " -"По умолчанию, этот метод гарантирует, что после вызова, пользователь не является " -"участником чата, но может присоединиться. Поэтому если пользовать является участником " -"чата, он будет кикнут, но не забанен. Если вы хотите изменить это поведение, " +"супергруппе или канале. Пользовать не вернется в группу или канал " +"автоматически, но сможет присоединиться с помощью ссылки и т.д. Бот " +"должен быть администратором. По умолчанию, этот метод гарантирует, что " +"после вызова, пользователь не является участником чата, но может " +"присоединиться. Поэтому если пользовать является участником чата, он " +"будет кикнут, но не забанен. Если вы хотите изменить это поведение, " "используйте параметр only_if_banned." #: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:7 @@ -5440,7 +5848,9 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#un msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @username)" -msgstr "Уникальный id группы или username супергруппы или канала (в формате @username)" +msgstr "" +"Уникальный id группы или username супергруппы или канала (в формате " +"@username)" #: of telebot.async_telebot.AsyncTeleBot.unban_chat_member:16 msgid "Do nothing if the user is not banned" @@ -5452,9 +5862,9 @@ msgid "" " or channel. The bot must be an administrator for this to work and must " "have the appropriate administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы разбанить ране забаненный канал в супергруппе " -"или канала. Бот должен быть администратором и иметь соответствующие права " -"администратора. Возвращает True в случае успеха." +"Используйте этот метод, чтобы разбанить ране забаненный канал в " +"супергруппе или канала. Бот должен быть администратором и иметь " +"соответствующие права администратора. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:6 msgid "" @@ -5469,18 +5879,16 @@ msgid "Unique identifier of the target sender chat." msgstr "Уникальный id чата." #: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:1 - msgid "" "Use this method to unhide the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы сделать топик 'General' видимым в супергруппе " -"с топиками. Бот должен быть администратором чата и иметь права администратора " -"can_manage_topics. Возвращает True в случае успеха." +"Используйте этот метод, чтобы сделать топик 'General' видимым в " +"супергруппе с топиками. Бот должен быть администратором чата и иметь " +"права администратора can_manage_topics. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unhidegeneralforumtopic" @@ -5494,9 +5902,9 @@ msgid "" "bot must be an administrator in the chat for this to work and must have " "the appropriate admin rights. Returns True on success." msgstr "" -"Используйте этот метод, что открепить все закрепленные сообщения в супергруппе. " -"Бот должен быть администратором чата и иметь соответствующие права администратора. " -"Возвращает True в случае успеха." +"Используйте этот метод, что открепить все закрепленные сообщения в " +"супергруппе. Бот должен быть администратором чата и иметь соответствующие" +" права администратора. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:5 msgid "" @@ -5513,9 +5921,9 @@ msgid "" "have the can_pin_messages administrator right in the supergroup. Returns " "True on success." msgstr "" -"Используйте этот метод, что открепить все закрепленные сообщения в топике. " -"Бот должен быть администратором чата и иметь права администратора " -"can_pin_messages в супергруппе. Возвращает True в случае успеха." +"Используйте этот метод, что открепить все закрепленные сообщения в " +"топике. Бот должен быть администратором чата и иметь права администратора" +" can_pin_messages в супергруппе. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:5 msgid "" @@ -5535,17 +5943,15 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights. Returns True on success." msgstr "" -"Используйте этот метод, что открепить закрепленное сообщение в супергруппе. " -"Бот должен быть администратором чата и иметь соответствующие права администратора. " -"Возвращает True в случае успеха." +"Используйте этот метод, что открепить закрепленное сообщение в " +"супергруппе. Бот должен быть администратором чата и иметь соответствующие" +" права администратора. Возвращает True в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinchatmessage" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#unpinchatmessage" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#unpinchatmessage" #: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:11 msgid "Int: Identifier of a message to unpin" @@ -5559,7 +5965,8 @@ msgid "" msgstr "" "Используйте этот метод, чтобы загрузить .png стикер, чтобы позже " "использовать в методах createNewStickerSet и addStickerToSet (может быть " -"использован несколько раз). Возвращает загруженный файл (File) в случае успеха." +"использован несколько раз). Возвращает загруженный файл (File) в случае " +"успеха." #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:4 msgid "" @@ -5574,15 +5981,28 @@ msgid "User identifier of sticker set owner" msgstr "id пользователя, создавшего стикерпак" #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:9 +#, fuzzy msgid "" -"PNG image with the sticker, must be up to 512 kilobytes in size, " -"dimensions must not exceed 512px, and either width or height must be " -"exactly 512px." +"DEPRECATED: PNG image with the sticker, must be up to 512 kilobytes in " +"size, dimensions must not exceed 512px, and either width or height must " +"be exactly 512px." msgstr "" "PNG изображение стикера, должно весить не более 512 kB, измерения не " -"должны превышать 512px и либо ширина, либо высота должна быть ровно 512px." +"должны превышать 512px и либо ширина, либо высота должна быть ровно " +"512px." #: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:13 +msgid "" +"A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. See " +"https://core.telegram.org/stickers for technical requirements. More " +"information on Sending Files »" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:17 +msgid "One of \"static\", \"animated\", \"video\"." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.upload_sticker_file:20 msgid "On success, the sent file is returned." msgstr "В случае успеха возвращается отправленный файл." @@ -5610,9 +6030,10 @@ msgid "" " - filter failed. message: Message class text: Filter value given in " "handler" msgstr "" -"Базовый класс Advanced Custom Filter. Создайте класс наследник с методом check(). " -"Принимает два параметра, возвращает bool: True - фильтр пройден, False - фильтр " -"не пройден. message: класс Message text: значение фильтра, полученное в хендлере" +"Базовый класс Advanced Custom Filter. Создайте класс наследник с методом " +"check(). Принимает два параметра, возвращает bool: True - фильтр пройден," +" False - фильтр не пройден. message: класс Message text: значение " +"фильтра, полученное в хендлере" #: of telebot.asyncio_filters.AdvancedCustomFilter:7 #: telebot.asyncio_filters.SimpleCustomFilter:5 @@ -5687,8 +6108,9 @@ msgid "" "Accepts only message, returns bool value, that is compared with given in " "handler." msgstr "" -"Базовый класс Simple Custom Filter. Создайте класс наследник с методом check(). " -"Принимает только сообщение, возвращает bool, который сравнивается с заданным в хендлере." +"Базовый класс Simple Custom Filter. Создайте класс наследник с методом " +"check(). Принимает только сообщение, возвращает bool, который " +"сравнивается с заданным в хендлере." #: of telebot.asyncio_filters.SimpleCustomFilter:7 msgid "Example on creating a simple custom filter." @@ -5707,8 +6129,8 @@ msgid "" "Advanced text filter to check (types.Message, types.CallbackQuery, " "types.InlineQuery, types.Poll)" msgstr "" -"Advanced текстовый фильтр для проверки (types.Message, types.CallbackQuery, " -"types.InlineQuery, types.Poll)" +"Advanced текстовый фильтр для проверки (types.Message, " +"types.CallbackQuery, types.InlineQuery, types.Poll)" #: of telebot.asyncio_filters.TextFilter:3 msgid "" @@ -5724,7 +6146,9 @@ msgstr "строка, True если текст объекта идентичен #: of telebot.asyncio_filters.TextFilter:8 msgid "list[str] or tuple[str], True if any string element of iterable is in text" -msgstr "list[str] или tuple[str], True если хотя бы один из элементов есть в тексте" +msgstr "" +"list[str] или tuple[str], True если хотя бы один из элементов есть в " +"тексте" #: of telebot.asyncio_filters.TextFilter:11 #: telebot.asyncio_filters.TextFilter:14 @@ -5764,8 +6188,8 @@ msgid "" "Base class for middleware. Your middlewares should be inherited from this" " class." msgstr "" -"Базовый класс для middleware. Ваши middleware должны быть унаследованы от " -"этого класса." +"Базовый класс для middleware. Ваши middleware должны быть унаследованы от" +" этого класса." #: of telebot.asyncio_handler_backends.BaseMiddleware:4 msgid "" @@ -5776,8 +6200,8 @@ msgid "" msgstr "" "Задайте update_sensitive=True если хотите получать разные апдейты в " "разных функциях. Например, если вы хотите обрабатывать pre_process для " -"апдейтов вида message, вам нужно будет создать функцию pre_process_message " -"и т.д. Аналогично для post_process." +"апдейтов вида message, вам нужно будет создать функцию " +"pre_process_message и т.д. Аналогично для post_process." #: of telebot.asyncio_handler_backends.BaseMiddleware:9 msgid "Example of class-based middlewares" @@ -5790,16 +6214,16 @@ msgid "" "post_process in middlewares." msgstr "" "Класс для отмены апдейтов. Просто верните экземпляр этого класса в " -"middleware, чтобы пропустить апдейт. Апдейт пропустит хендлер и исполнение " -"post_process в middleware." +"middleware, чтобы пропустить апдейт. Апдейт пропустит хендлер и " +"исполнение post_process в middleware." #: of telebot.asyncio_handler_backends.ContinueHandling:1 msgid "" "Class for continue updates in handlers. Just return instance of this " "class in handlers to continue process." msgstr "" -"Класс для продолжения обработки апдейта в хендлерах. Просто верните экземпляр " -"этого класса в хендлерах, чтобы продолжить обработку." +"Класс для продолжения обработки апдейта в хендлерах. Просто верните " +"экземпляр этого класса в хендлерах, чтобы продолжить обработку." #: of telebot.asyncio_handler_backends.ContinueHandling:5 msgid "Example of using ContinueHandling" @@ -5833,15 +6257,17 @@ msgstr "Этот файл используется функцией AsyncTeleBot #: of telebot.ext.aio.webhooks:3 msgid "Fastapi and starlette(0.20.2+) libraries are required to run this script." -msgstr "Для запуска этого скрипта требуются библиотеки Fastapi и starlette(0.20.2+)." +msgstr "" +"Для запуска этого скрипта требуются библиотеки Fastapi и " +"starlette(0.20.2+)." #: of telebot.ext.aio.webhooks.AsyncWebhookListener.run_app:1 msgid "" "Run app with the given parameters to init. Not supposed to be used " "manually by user." msgstr "" -"Запустить приложение с заданными параметрами инициализации. Не предназначено " -"для использования пользователем." +"Запустить приложение с заданными параметрами инициализации. Не " +"предназначено для использования пользователем." #~ msgid "New name of the topic, 1-128 characters" #~ msgstr "Новое название топика, 1-128 символов" @@ -5862,3 +6288,20 @@ msgstr "" #~ ":meth:`telebot.async_telebot.AsyncTeleBot.send_message`" #~ msgstr "" +#~ msgid "" +#~ "Use this method to add a new " +#~ "sticker to a set created by the" +#~ " bot. It's required to pass " +#~ "`png_sticker` or `tgs_sticker`. Returns True" +#~ " on success." +#~ msgstr "" +#~ "Используйте этот метод, чтобы добавить " +#~ "новый стикер в стикерпак, созданный " +#~ "ботом. Необходимо передать либо `png_sticker`," +#~ " либо `tgs_sticker`, либо `webm_sticker`. " +#~ "Возвращает True в случае успешного " +#~ "добавления." + +#~ msgid "Timeout in seconds for long polling." +#~ msgstr "Тайм-аут поллинга в секундах." + diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index 3858e743f..c9243db70 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-02 19:24+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -115,9 +115,9 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands #: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file -#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic -#: telebot.TeleBot.edit_general_forum_topic +#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook +#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link +#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_general_forum_topic #: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup @@ -134,16 +134,17 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.get_file_url telebot.TeleBot.get_game_high_scores #: telebot.TeleBot.get_my_commands #: telebot.TeleBot.get_my_default_administrator_rights -#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set -#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.hide_general_forum_topic -#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler -#: telebot.TeleBot.leave_chat telebot.TeleBot.load_next_step_handlers -#: telebot.TeleBot.load_reply_handlers telebot.TeleBot.message_handler -#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler -#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler -#: telebot.TeleBot.poll_handler telebot.TeleBot.polling -#: telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name +#: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state +#: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates +#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info +#: telebot.TeleBot.hide_general_forum_topic telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers +#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler +#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler @@ -176,16 +177,21 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.send_media_group telebot.TeleBot.send_message #: telebot.TeleBot.send_photo telebot.TeleBot.send_poll #: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note -#: telebot.TeleBot.send_voice +#: telebot.TeleBot.send_video telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights -#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set -#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_update_listener +#: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name +#: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state +#: telebot.TeleBot.set_sticker_emoji_list telebot.TeleBot.set_sticker_keywords +#: telebot.TeleBot.set_sticker_mask_position +#: telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumbnail +#: telebot.TeleBot.set_sticker_set_title telebot.TeleBot.set_update_listener #: telebot.TeleBot.set_webhook telebot.TeleBot.setup_middleware #: telebot.TeleBot.shipping_query_handler #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll @@ -322,9 +328,9 @@ msgstr "Данные для добавления" #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands #: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file -#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic -#: telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook +#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link +#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text @@ -339,15 +345,16 @@ msgstr "Данные для добавления" #: telebot.TeleBot.get_file_url telebot.TeleBot.get_forum_topic_icon_stickers #: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands #: telebot.TeleBot.get_my_default_administrator_rights -#: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set -#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling -#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat -#: telebot.TeleBot.log_out telebot.TeleBot.message_handler -#: telebot.TeleBot.middleware_handler telebot.TeleBot.my_chat_member_handler -#: telebot.TeleBot.pin_chat_message telebot.TeleBot.poll_answer_handler -#: telebot.TeleBot.poll_handler telebot.TeleBot.polling -#: telebot.TeleBot.pre_checkout_query_handler +#: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name +#: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state +#: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates +#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info +#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler +#: telebot.TeleBot.leave_chat telebot.TeleBot.log_out +#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler +#: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler @@ -378,16 +385,21 @@ msgstr "Данные для добавления" #: telebot.TeleBot.send_media_group telebot.TeleBot.send_message #: telebot.TeleBot.send_photo telebot.TeleBot.send_poll #: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note -#: telebot.TeleBot.send_voice +#: telebot.TeleBot.send_video telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights -#: telebot.TeleBot.set_state telebot.TeleBot.set_sticker_position_in_set -#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name +#: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state +#: telebot.TeleBot.set_sticker_emoji_list telebot.TeleBot.set_sticker_keywords +#: telebot.TeleBot.set_sticker_mask_position +#: telebot.TeleBot.set_sticker_position_in_set +#: telebot.TeleBot.set_sticker_set_thumbnail +#: telebot.TeleBot.set_sticker_set_title telebot.TeleBot.set_webhook #: telebot.TeleBot.setup_middleware telebot.TeleBot.shipping_query_handler #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat @@ -437,35 +449,43 @@ msgstr "" #: of telebot.TeleBot.add_sticker_to_set:1 msgid "" -"Use this method to add a new sticker to a set created by the bot. It's " -"required to pass `png_sticker` or `tgs_sticker`. Returns True on success." +"Use this method to add a new sticker to a set created by the bot. The " +"format of the added sticker must match the format of the other stickers " +"in the set. Emoji sticker sets can have up to 200 stickers. Animated and " +"video sticker sets can have up to 50 stickers. Static sticker sets can " +"have up to 120 stickers. Returns True on success." msgstr "" -"Используйте этот метод, чтобы добавить новый стикер в стикерпак, " -"созданный ботом. Необходимо передать либо `png_sticker`, либо " -"`tgs_sticker`, либо `webm_sticker`. Возвращает True в случае успешного " -"добавления." -#: of telebot.TeleBot.add_sticker_to_set:5 +#: of telebot.TeleBot.add_sticker_to_set:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#addstickertoset" msgstr "Документация Telegram: https://core.telegram.org/bots/api#addstickertoset" -#: of telebot.TeleBot.add_sticker_to_set:7 -#: telebot.TeleBot.create_new_sticker_set:7 +#: of telebot.TeleBot.add_sticker_to_set:10 +msgid "" +"**_sticker, mask_position, emojis parameters are deprecated, use stickers" +" instead" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:12 +#: telebot.TeleBot.create_new_sticker_set:10 msgid "User identifier of created sticker set owner" msgstr "id пользователя, создавшего стикерпак" -#: of telebot.TeleBot.add_sticker_to_set:10 telebot.TeleBot.get_sticker_set:5 -#: telebot.TeleBot.set_sticker_set_thumb:6 +#: of telebot.TeleBot.add_sticker_to_set:15 +#: telebot.TeleBot.delete_sticker_set:3 telebot.TeleBot.get_sticker_set:5 +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:4 +#: telebot.TeleBot.set_sticker_set_thumbnail:6 +#: telebot.TeleBot.set_sticker_set_title:4 msgid "Sticker set name" msgstr "Имя стикерпака" -#: of telebot.TeleBot.add_sticker_to_set:13 -#: telebot.TeleBot.create_new_sticker_set:18 +#: of telebot.TeleBot.add_sticker_to_set:18 +#: telebot.TeleBot.create_new_sticker_set:21 msgid "One or more emoji corresponding to the sticker" msgstr "Один или несколько эмодзи, относящихся к стикеру" -#: of telebot.TeleBot.add_sticker_to_set:16 -#: telebot.TeleBot.create_new_sticker_set:21 +#: of telebot.TeleBot.add_sticker_to_set:21 +#: telebot.TeleBot.create_new_sticker_set:24 msgid "" "PNG image with the sticker, must be up to 512 kilobytes in size, " "dimensions must not exceed 512px, and either width or height must be " @@ -481,39 +501,46 @@ msgstr "" "Telegram скачал файл из интернета, или загрузите новый файл с помощью " "multipart/form-data." -#: of telebot.TeleBot.add_sticker_to_set:21 -#: telebot.TeleBot.create_new_sticker_set:26 +#: of telebot.TeleBot.add_sticker_to_set:26 +#: telebot.TeleBot.create_new_sticker_set:29 msgid "TGS animation with the sticker, uploaded using multipart/form-data." msgstr "" "Анимированный стикер в формате TGS, загруженный с помощью multipart/form-" "data." -#: of telebot.TeleBot.add_sticker_to_set:24 -#: telebot.TeleBot.create_new_sticker_set:29 +#: of telebot.TeleBot.add_sticker_to_set:29 +#: telebot.TeleBot.create_new_sticker_set:32 msgid "WebM animation with the sticker, uploaded using multipart/form-data." msgstr "" "Анимированный стикер в формате WebM, загруженный с помощью multipart" "/form-data." -#: of telebot.TeleBot.add_sticker_to_set:27 -#: telebot.TeleBot.create_new_sticker_set:40 +#: of telebot.TeleBot.add_sticker_to_set:32 +#: telebot.TeleBot.create_new_sticker_set:42 msgid "" "A JSON-serialized object for position where the mask should be placed on " "faces" msgstr "Позиция для размещения маски на лицах в формате JSON" -#: of telebot.TeleBot.add_sticker_to_set:30 +#: of telebot.TeleBot.add_sticker_to_set:35 +msgid "" +"A JSON-serialized list of 1-50 initial stickers to be added to the " +"sticker set" +msgstr "" + +#: of telebot.TeleBot.add_sticker_to_set:38 #: telebot.TeleBot.answer_callback_query:22 -#: telebot.TeleBot.answer_inline_query:35 +#: telebot.TeleBot.answer_inline_query:38 #: telebot.TeleBot.answer_pre_checkout_query:21 #: telebot.TeleBot.answer_shipping_query:18 #: telebot.TeleBot.close_forum_topic:13 -#: telebot.TeleBot.create_new_sticker_set:43 +#: telebot.TeleBot.create_new_sticker_set:56 #: telebot.TeleBot.delete_forum_topic:13 #: telebot.TeleBot.delete_sticker_from_set:6 #: telebot.TeleBot.edit_forum_topic:22 telebot.TeleBot.reopen_forum_topic:13 +#: telebot.TeleBot.set_sticker_keywords:11 #: telebot.TeleBot.set_sticker_position_in_set:11 -#: telebot.TeleBot.set_sticker_set_thumb:15 +#: telebot.TeleBot.set_sticker_set_thumbnail:15 #: telebot.TeleBot.unpin_all_forum_topic_messages:13 msgid "On success, True is returned." msgstr "В случае успеха возвращается True." @@ -530,9 +557,10 @@ msgstr "В случае успеха возвращается True." #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands -#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_webhook -#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link -#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_sticker_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text telebot.TeleBot.export_chat_invite_link @@ -558,15 +586,18 @@ msgstr "В случае успеха возвращается True." #: telebot.TeleBot.send_message telebot.TeleBot.send_photo #: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker #: telebot.TeleBot.send_venue telebot.TeleBot.send_video -#: telebot.TeleBot.send_video_note #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights +#: telebot.TeleBot.set_sticker_emoji_list telebot.TeleBot.set_sticker_keywords +#: telebot.TeleBot.set_sticker_mask_position #: telebot.TeleBot.set_sticker_position_in_set -#: telebot.TeleBot.set_sticker_set_thumb telebot.TeleBot.set_webhook +#: telebot.TeleBot.set_sticker_set_thumbnail +#: telebot.TeleBot.set_sticker_set_title telebot.TeleBot.set_webhook #: telebot.TeleBot.stop_message_live_location telebot.TeleBot.stop_poll #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat #: telebot.TeleBot.unpin_all_chat_messages @@ -576,34 +607,43 @@ msgstr "В случае успеха возвращается True." msgid "Return type" msgstr "" -#: of telebot.TeleBot.add_sticker_to_set:31 +#: of telebot.TeleBot.add_sticker_to_set:39 #: telebot.TeleBot.answer_callback_query:23 -#: telebot.TeleBot.answer_inline_query:36 +#: telebot.TeleBot.answer_inline_query:39 #: telebot.TeleBot.answer_pre_checkout_query:22 #: telebot.TeleBot.answer_shipping_query:19 #: telebot.TeleBot.approve_chat_join_request:15 #: telebot.TeleBot.ban_chat_member:25 telebot.TeleBot.ban_chat_sender_chat:17 #: telebot.TeleBot.close:9 telebot.TeleBot.close_forum_topic:14 -#: telebot.TeleBot.create_new_sticker_set:44 +#: telebot.TeleBot.create_new_sticker_set:57 #: telebot.TeleBot.decline_chat_join_request:15 #: telebot.TeleBot.delete_chat_photo:13 #: telebot.TeleBot.delete_chat_sticker_set:11 #: telebot.TeleBot.delete_forum_topic:14 telebot.TeleBot.delete_message:23 #: telebot.TeleBot.delete_my_commands:17 -#: telebot.TeleBot.delete_sticker_from_set:7 telebot.TeleBot.delete_webhook:13 +#: telebot.TeleBot.delete_sticker_from_set:7 +#: telebot.TeleBot.delete_sticker_set:7 telebot.TeleBot.delete_webhook:13 #: telebot.TeleBot.edit_forum_topic:23 telebot.TeleBot.leave_chat:8 #: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 #: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 #: telebot.TeleBot.reopen_forum_topic:14 -#: telebot.TeleBot.restrict_chat_member:48 telebot.TeleBot.send_chat_action:26 +#: telebot.TeleBot.restrict_chat_member:61 telebot.TeleBot.send_chat_action:26 +#: telebot.TeleBot.send_video_note:28 telebot.TeleBot.send_video_note:40 +#: telebot.TeleBot.send_video_note:43 #: telebot.TeleBot.set_chat_administrator_custom_title:18 #: telebot.TeleBot.set_chat_description:14 #: telebot.TeleBot.set_chat_menu_button:15 -#: telebot.TeleBot.set_chat_permissions:15 telebot.TeleBot.set_chat_photo:16 -#: telebot.TeleBot.set_chat_title:17 telebot.TeleBot.set_my_commands:18 +#: telebot.TeleBot.set_chat_permissions:21 telebot.TeleBot.set_chat_photo:16 +#: telebot.TeleBot.set_chat_title:17 +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:11 +#: telebot.TeleBot.set_my_commands:18 #: telebot.TeleBot.set_my_default_administrator_rights:18 +#: telebot.TeleBot.set_sticker_emoji_list:11 +#: telebot.TeleBot.set_sticker_keywords:12 +#: telebot.TeleBot.set_sticker_mask_position:12 #: telebot.TeleBot.set_sticker_position_in_set:12 -#: telebot.TeleBot.set_sticker_set_thumb:16 +#: telebot.TeleBot.set_sticker_set_thumbnail:16 +#: telebot.TeleBot.set_sticker_set_title:11 #: telebot.TeleBot.unban_chat_member:20 #: telebot.TeleBot.unban_chat_sender_chat:15 #: telebot.TeleBot.unpin_all_chat_messages:12 @@ -705,8 +745,8 @@ msgid "" "The maximum amount of time in seconds that the result of the inline query" " may be cached on the server." msgstr "" -"Максимальная длительность хранения результатов inline query на сервере " -"в секундах." +"Максимальная длительность хранения результатов inline query на сервере в " +"секундах." #: of telebot.TeleBot.answer_inline_query:16 msgid "" @@ -759,6 +799,13 @@ msgstr "" "Параметр для передачи боту вместе с сообщением /start, отправленному при " "нажатии кнопки переключения" +#: of telebot.TeleBot.answer_inline_query:35 +#, fuzzy +msgid "" +"A JSON-serialized object describing a button to be shown above inline " +"query results" +msgstr "Объект в формате JSON, описывающий сообщение, которое нужно отправить" + #: of telebot.TeleBot.answer_pre_checkout_query:1 msgid "" "Once the user has confirmed their payment and shipping details, the Bot " @@ -922,7 +969,7 @@ msgstr "" #: telebot.TeleBot.decline_chat_join_request:11 #: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_profile_photos:6 #: telebot.TeleBot.promote_chat_member:11 -#: telebot.TeleBot.restrict_chat_member:11 +#: telebot.TeleBot.restrict_chat_member:14 #: telebot.TeleBot.set_chat_administrator_custom_title:10 #: telebot.TeleBot.unban_chat_member:13 msgid "Unique identifier of the target user" @@ -939,7 +986,9 @@ msgstr "Уникальный id сделавшего запрос пользов #: telebot.TeleBot.set_chat_menu_button:14 telebot.TeleBot.set_chat_photo:15 #: telebot.TeleBot.set_chat_title:16 telebot.TeleBot.set_my_commands:17 #: telebot.TeleBot.set_my_default_administrator_rights:17 -#: telebot.TeleBot.set_webhook:46 telebot.TeleBot.unban_chat_sender_chat:14 +#: telebot.TeleBot.set_my_description:14 telebot.TeleBot.set_my_name:12 +#: telebot.TeleBot.set_my_short_description:14 telebot.TeleBot.set_webhook:46 +#: telebot.TeleBot.unban_chat_sender_chat:14 #: telebot.TeleBot.unpin_all_chat_messages:11 #: telebot.TeleBot.unpin_chat_message:14 msgid "True on success." @@ -961,7 +1010,7 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#banchatmember" msgstr "Документация Telegram: https://core.telegram.org/bots/api#banchatmember" -#: of telebot.TeleBot.ban_chat_member:8 telebot.TeleBot.restrict_chat_member:7 +#: of telebot.TeleBot.ban_chat_member:8 telebot.TeleBot.restrict_chat_member:10 msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @channelusername)" @@ -992,7 +1041,12 @@ msgstr "" #: of telebot.TeleBot.ban_chat_member:24 #: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 -#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:25 +#: telebot.TeleBot.delete_sticker_set:6 telebot.TeleBot.delete_webhook:12 +#: telebot.TeleBot.send_chat_action:25 +#: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:10 +#: telebot.TeleBot.set_sticker_emoji_list:10 +#: telebot.TeleBot.set_sticker_mask_position:11 +#: telebot.TeleBot.set_sticker_set_title:10 msgid "Returns True on success." msgstr "Возвращает True в случае успеха." @@ -1179,9 +1233,9 @@ msgid "" "parameter to the decorator function, it passes " ":class:`telebot.types.ChosenInlineResult` object." msgstr "" -"Обрабатывает результат inline query, который был выбран пользователем и" -" отправлен собеседнику в чате. Пожалуйста ознакомьтесь с документацией по" -" сбору фидбека для получения таких апдейтов вашим ботом. В качестве " +"Обрабатывает результат inline query, который был выбран пользователем и " +"отправлен собеседнику в чате. Пожалуйста ознакомьтесь с документацией по " +"сбору фидбека для получения таких апдейтов вашим ботом. В качестве " "параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.ChosenInlineResult`." @@ -1258,7 +1312,6 @@ msgid "Identifier of the topic to close" msgstr "id топика для закрытия" #: of telebot.TeleBot.close_general_forum_topic:1 - msgid "" "Use this method to close the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -1270,7 +1323,6 @@ msgstr "" "топика. Возвращает True в случае успеха." #: of telebot.TeleBot.close_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#closegeneralforumtopic" @@ -1407,26 +1459,24 @@ msgid "Identifier of a message thread, in which the message will be sent" msgstr "id топика, в который нужно отправить сообщение" #: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 -#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:63 -#: telebot.TeleBot.send_audio:63 telebot.TeleBot.send_contact:44 -#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:56 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:66 +#: telebot.TeleBot.send_audio:66 telebot.TeleBot.send_contact:44 +#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:59 #: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 #: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 #: telebot.TeleBot.send_photo:48 telebot.TeleBot.send_poll:70 -#: telebot.TeleBot.send_sticker:39 telebot.TeleBot.send_venue:57 -#: telebot.TeleBot.send_video:64 telebot.TeleBot.send_video_note:48 -#: telebot.TeleBot.send_voice:49 +#: telebot.TeleBot.send_sticker:42 telebot.TeleBot.send_venue:57 +#: telebot.TeleBot.send_video:67 telebot.TeleBot.send_voice:49 msgid "On success, the sent Message is returned." msgstr "В случае успеха возвращает отправленное сообщение (Message)." #: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 -#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:64 -#: telebot.TeleBot.send_audio:64 telebot.TeleBot.send_contact:45 -#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:57 +#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:67 +#: telebot.TeleBot.send_audio:67 telebot.TeleBot.send_contact:45 +#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:60 #: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 -#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:40 -#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:65 -#: telebot.TeleBot.send_video_note:49 +#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:43 +#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:68 msgid ":class:`telebot.types.Message`" msgstr "" @@ -1438,10 +1488,11 @@ msgid "" "method revokeChatInviteLink. Returns the new invite link as " "ChatInviteLink object." msgstr "" -"Используйте этот метод, чтобы создать дополнительную ссылку-приглашение в чат. Бот " -"должен быть администратором чата и иметь соответствующие права администратора. " -"Ссылка может быть аннулирована методом revokeChatInviteLink. Возвращает новую " -"ссылку-приглашение (ChatInviteLink)." +"Используйте этот метод, чтобы создать дополнительную ссылку-приглашение в" +" чат. Бот должен быть администратором чата и иметь соответствующие права " +"администратора. Ссылка может быть аннулирована методом " +"revokeChatInviteLink. Возвращает новую ссылку-приглашение " +"(ChatInviteLink)." #: of telebot.TeleBot.create_chat_invite_link:6 msgid "" @@ -1705,7 +1756,13 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#createnewstickerset" -#: of telebot.TeleBot.create_new_sticker_set:10 +#: of telebot.TeleBot.create_new_sticker_set:8 +msgid "" +"Fields *_sticker are deprecated, pass a list of stickers to stickers " +"parameter instead." +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:13 msgid "" "Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., " "animals). Can contain only English letters, digits and underscores. Must " @@ -1719,11 +1776,11 @@ msgstr "" "содержать подряд идущие нижние подчеркивания и должно заканчиваться на " "\"_by_\". учитывает регистр. 1-64 символа." -#: of telebot.TeleBot.create_new_sticker_set:15 +#: of telebot.TeleBot.create_new_sticker_set:18 msgid "Sticker set title, 1-64 characters" msgstr "Название стикерпака, 1-64 символа" -#: of telebot.TeleBot.create_new_sticker_set:32 +#: of telebot.TeleBot.create_new_sticker_set:35 msgid "" "Pass True, if a set of mask stickers should be created. Deprecated since " "Bot API 6.2, use sticker_type instead." @@ -1731,16 +1788,34 @@ msgstr "" "Передайте True, если создаётся стикерпак масок. Устарело, начиная с Bot " "API 6.2, используйте sticker_type." -#: of telebot.TeleBot.create_new_sticker_set:36 +#: of telebot.TeleBot.create_new_sticker_set:39 +#, fuzzy msgid "" -"Optional, Type of stickers in the set, pass “regular” or “mask”. Custom " -"emoji sticker sets can't be created via the Bot API at the moment. By " -"default, a regular sticker set is created." +"Type of stickers in the set, pass “regular”, “mask”, or “custom_emoji”. " +"By default, a regular sticker set is created." msgstr "" "Необязательный, тип стикерпака, передайте “regular” или “mask”. " "Стикерпаки кастомных эмодзи пока что не могут быть созданы с помощью Bot " "API. По умолчанию будет создан обычный стикерпак." +#: of telebot.TeleBot.create_new_sticker_set:45 +msgid "" +"Pass True if stickers in the sticker set must be repainted to the color " +"of text when used in messages, the accent color if used as emoji status, " +"white on chat photos, or another appropriate color based on context; for " +"custom emoji sticker sets only" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:50 +msgid "List of stickers to be added to the set" +msgstr "" + +#: of telebot.TeleBot.create_new_sticker_set:53 +msgid "" +"Format of stickers in the set, must be one of “static”, “animated”, " +"“video”" +msgstr "" + #: of telebot.TeleBot.decline_chat_join_request:1 msgid "" "Use this method to decline a chat join request. The bot must be an " @@ -1921,6 +1996,13 @@ msgstr "" msgid "File identifier of the sticker" msgstr "id файла стикера" +#: of telebot.TeleBot.delete_sticker_set:1 +#, fuzzy +msgid "Use this method to delete a sticker set. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного " +"ботом. Возвращает True в случае успеха." + #: of telebot.TeleBot.delete_webhook:1 msgid "" "Use this method to remove webhook integration if you decide to switch " @@ -2038,8 +2120,8 @@ msgid "" "Optional, New name of the topic, 1-128 characters. If not specififed or " "empty, the current name of the topic will be kept" msgstr "" -"Необязательный, новое имя топика, 1-128 символов. Если не задано или пустое, " -"сохранится текущее имя топика" +"Необязательный, новое имя топика, 1-128 символов. Если не задано или " +"пустое, сохранится текущее имя топика" #: of telebot.TeleBot.edit_forum_topic:17 msgid "" @@ -2054,7 +2136,6 @@ msgstr "" "чтобы убрать иконку. Если не задан, сохранится текущая иконка топика" #: of telebot.TeleBot.edit_general_forum_topic:1 - msgid "" "Use this method to edit the name of the 'General' topic in a forum " "supergroup chat. The bot must be an administrator in the chat for this to" @@ -2067,14 +2148,12 @@ msgstr "" "True в случае успеха." #: of telebot.TeleBot.edit_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#editgeneralforumtopic" msgstr "Документация Telegram: https://core.telegram.org/bots/api#editforumtopic" #: of telebot.TeleBot.edit_general_forum_topic:10 - msgid "New topic name, 1-128 characters" msgstr "Название товара, 1-32 символа" @@ -2139,8 +2218,8 @@ msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." msgstr "" -"В случае успеха если изменённое сообщение отправлено ботом, возвращается" -" новый объект Message, иначе (inline сообщения) возвращается True." +"В случае успеха если изменённое сообщение отправлено ботом, возвращается " +"новый объект Message, иначе (inline сообщения) возвращается True." #: of telebot.TeleBot.edit_message_caption:27 msgid ":obj:`types.Message` | :obj:`bool`" @@ -2160,9 +2239,9 @@ msgid "" "message is not an inline message, the edited Message is returned, " "otherwise True is returned." msgstr "" -"отключено вызовом метода stopMessageLiveLocation. В случае успеха если измененное " -"сообщение не является inline сообщением, возвращается новый объект Message, " -"иначе возвращается True." +"отключено вызовом метода stopMessageLiveLocation. В случае успеха если " +"измененное сообщение не является inline сообщением, возвращается новый " +"объект Message, иначе возвращается True." #: of telebot.TeleBot.edit_message_live_location:5 msgid "" @@ -2229,8 +2308,8 @@ msgid "" "On success, if the edited message is not an inline message, the edited " "Message is returned, otherwise True is returned." msgstr "" -"В случае успеха если измененное сообщение не является inline сообщением," -" возвращается новый объект Message, иначе возвращается True." +"В случае успеха если измененное сообщение не является inline сообщением, " +"возвращается новый объект Message, иначе возвращается True." #: of telebot.TeleBot.edit_message_live_location:39 #: telebot.TeleBot.stop_message_live_location:23 @@ -2270,8 +2349,8 @@ msgid "" " target chat or username of the target channel (in the format " "@channelusername)" msgstr "" -"Обязательный, если не указан inline_message_id. Уникальный id чата " -"или username канала (в формате @channelusername)" +"Обязательный, если не указан inline_message_id. Уникальный id чата или " +"username канала (в формате @channelusername)" #: of telebot.TeleBot.edit_message_media:13 #: telebot.TeleBot.edit_message_reply_markup:8 @@ -2280,8 +2359,7 @@ msgstr "" msgid "" "Required if inline_message_id is not specified. Identifier of the sent " "message" -msgstr "" -"Обязательный, если не указан inline_message_id. id отправленного сообщения" +msgstr "Обязательный, если не указан inline_message_id. id отправленного сообщения" #: of telebot.TeleBot.edit_message_media:23 #: telebot.TeleBot.edit_message_reply_markup:18 @@ -2322,8 +2400,8 @@ msgid "" "List of special entities that appear in the message text, which can be " "specified instead of parse_mode" msgstr "" -"Список отформатированных частей в тексте сообщения, " -"можно использовать вместо parse_mode" +"Список отформатированных частей в тексте сообщения, можно использовать " +"вместо parse_mode" #: of telebot.TeleBot.edit_message_text:23 telebot.TeleBot.send_message:21 msgid "Disables link previews for links in this message" @@ -2335,8 +2413,8 @@ msgid "" "edited. As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" -"Обрабатывает новую версию поста в канале, который доступен боту и был изменён. " -"В качестве параметра, передаёт в декорируемую функцию объект " +"Обрабатывает новую версию поста в канале, который доступен боту и был " +"изменён. В качестве параметра, передаёт в декорируемую функцию объект " ":class:`telebot.types.Message`." #: of telebot.TeleBot.edited_message_handler:1 @@ -2345,8 +2423,8 @@ msgid "" " As a parameter to the decorator function, it passes " ":class:`telebot.types.Message` object." msgstr "" -"Обрабатывает новую версию сообщения, которое доступно боту и было изменено. " -"В качестве параметра, передаёт в декорируемую функцию объект " +"Обрабатывает новую версию сообщения, которое доступно боту и было " +"изменено. В качестве параметра, передаёт в декорируемую функцию объект " ":class:`telebot.types.Message`." #: of telebot.TeleBot.edited_message_handler:16 @@ -2356,7 +2434,9 @@ msgstr "список видов чатов" #: of telebot.TeleBot.enable_save_next_step_handlers:1 msgid "Enable saving next step handlers (by default saving disabled)" -msgstr "Разрешить сохранение next step хендлеров (по умолчанию сохранение отключено)" +msgstr "" +"Разрешить сохранение next step хендлеров (по умолчанию сохранение " +"отключено)" #: of telebot.TeleBot.enable_save_next_step_handlers:3 #: telebot.TeleBot.enable_save_reply_handlers:3 @@ -2366,9 +2446,10 @@ msgid "" "saving capability for handlers. And the same implementation is now " "available with FileHandlerBackend" msgstr "" -"Эта функция, целью которой было включить возможность сохранения файлов для обработчиков, " -"явно назначает FileHandlerBackend (вместо Saver) просто для сохранения обратной совместимости. " -"Та же реализация теперь доступна с FileHandlerBackend." +"Эта функция, целью которой было включить возможность сохранения файлов " +"для обработчиков, явно назначает FileHandlerBackend (вместо Saver) просто" +" для сохранения обратной совместимости. Та же реализация теперь доступна " +"с FileHandlerBackend." #: of telebot.TeleBot.enable_save_next_step_handlers:7 #: telebot.TeleBot.enable_save_reply_handlers:7 @@ -2396,8 +2477,9 @@ msgid "" "It is recommended to pass a :class:`~telebot.storage.StatePickleStorage` " "instance as state_storage to TeleBot class." msgstr "" -"Рекомендуется передавать экземпляр класса :class:`~telebot.storage.StatePickleStorage` " -"в качестве state_storage при инициализации класса TeleBot вместо использования этой функции." +"Рекомендуется передавать экземпляр класса " +":class:`~telebot.storage.StatePickleStorage` в качестве state_storage при" +" инициализации класса TeleBot вместо использования этой функции." #: of telebot.TeleBot.enable_saving_states:7 msgid "Filename of saving file, defaults to \"./.state-save/states.pkl\"" @@ -2409,9 +2491,9 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights." msgstr "" -"Используйте этот метод, чтобы создать или заменить главную ссылку-приглашение " -"в супергруппу или канал, созданную ботом. Бот должен быть администратором чата " -"и иметь соответствующие права администратора." +"Используйте этот метод, чтобы создать или заменить главную " +"ссылку-приглашение в супергруппу или канал, созданную ботом. Бот должен " +"быть администратором чата и иметь соответствующие права администратора." #: of telebot.TeleBot.export_chat_invite_link:4 msgid "" @@ -2451,9 +2533,10 @@ msgid "" "name of the user for one-on-one conversations, current username of a " "user, group or channel, etc.). Returns a Chat object on success." msgstr "" -"Используйте этот метод, чтобы получить актуальную информацию о чате (текущее " -"имя пользователя для персональных диалогов, текущий username пользователя, " -"группы или канала и т.д.). В случае успеха возвращает объект Chat." +"Используйте этот метод, чтобы получить актуальную информацию о чате " +"(текущее имя пользователя для персональных диалогов, текущий username " +"пользователя, группы или канала и т.д.). В случае успеха возвращает " +"объект Chat." #: of telebot.TeleBot.get_chat:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchat" @@ -2465,8 +2548,8 @@ msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" msgstr "" -"Уникальный id чата или username супергруппы или канала " -"(в формате @channelusername)" +"Уникальный id чата или username супергруппы или канала (в формате " +"@channelusername)" #: of telebot.TeleBot.get_chat:9 msgid "Chat information" @@ -2482,9 +2565,9 @@ msgid "" "returns an Array of ChatMember objects that contains information about " "all chat administrators except other bots." msgstr "" -"Используйте этот метод, чтобы получить список администраторов чата. " -"В случае успеха возвращает массив объектов ChatMember, содержащих информацию " -"обо всех администраторах чата, кроме других ботов." +"Используйте этот метод, чтобы получить список администраторов чата. В " +"случае успеха возвращает массив объектов ChatMember, содержащих " +"информацию обо всех администраторах чата, кроме других ботов." #: of telebot.TeleBot.get_chat_administrators:5 msgid "" @@ -2507,8 +2590,8 @@ msgid "" "Use this method to get information about a member of a chat. Returns a " "ChatMember object on success." msgstr "" -"Используйте этот метод, чтобы получить информацию об участнике чата. Возвращает " -"объект ChatMember в случае успеха." +"Используйте этот метод, чтобы получить информацию об участнике чата. " +"Возвращает объект ChatMember в случае успеха." #: of telebot.TeleBot.get_chat_member:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getchatmember" @@ -2539,6 +2622,9 @@ msgid "Number of members in the chat." msgstr "Количество участников чата." #: of telebot.TeleBot.get_chat_member_count:9 +#: telebot.TeleBot.send_video_note:14 telebot.TeleBot.send_video_note:17 +#: telebot.TeleBot.send_video_note:20 telebot.TeleBot.send_video_note:31 +#: telebot.TeleBot.send_video_note:46 msgid ":obj:`int`" msgstr "" @@ -2548,7 +2634,8 @@ msgid "" "private chat, or the default menu button. Returns MenuButton on success." msgstr "" "Используйте этот метод, чтобы получить текущее значение кнопки menu в " -"приватном чате, или кнопку menu по умолчанию. Возвращает MenuButton в случае успеха." +"приватном чате, или кнопку menu по умолчанию. Возвращает MenuButton в " +"случае успеха." #: of telebot.TeleBot.get_chat_menu_button:5 msgid "" @@ -2563,8 +2650,8 @@ msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be returned." msgstr "" -"Уникальный id приватного чата. Если не указан, будет возвращена " -"кнопка menu по умолчанию." +"Уникальный id приватного чата. Если не указан, будет возвращена кнопка " +"menu по умолчанию." #: of telebot.TeleBot.get_chat_menu_button:11 msgid "types.MenuButton" @@ -2579,15 +2666,14 @@ msgid "" "Use this method to get information about custom emoji stickers by their " "identifiers. Returns an Array of Sticker objects." msgstr "" -"Используйте этот метод, чтобы получить информацию о кастомных эмодзи по их " -"id. Возвращает массив объектов Sticker." +"Используйте этот метод, чтобы получить информацию о кастомных эмодзи по " +"их id. Возвращает массив объектов Sticker." #: of telebot.TeleBot.get_custom_emoji_stickers:4 msgid "" "List of custom emoji identifiers. At most 200 custom emoji identifiers " "can be specified." -msgstr "" -"Список id кастомных эмодзи. Можно указать не более 200 id." +msgstr "Список id кастомных эмодзи. Можно указать не более 200 id." #: of telebot.TeleBot.get_custom_emoji_stickers:7 msgid "Returns an Array of Sticker objects." @@ -2605,11 +2691,12 @@ msgid "" "link will be valid for at least 1 hour. When the link expires, a new one " "can be requested by calling get_file again." msgstr "" -"Используйте этот метод, чтобы получить базовую информацию о файле и подготовить " -"его к скачиванию. На текущий момент, боты могут скачивать файлы весом до 20MB. " -"В случае успеха возвращается объект File. Гарантируется, что ссылка на скачивание " -"будет актуальна как минимум 1 час. Когда ссылка перестаёт быть актуальной, новая " -"может быть снова запрошена с помощью get_file." +"Используйте этот метод, чтобы получить базовую информацию о файле и " +"подготовить его к скачиванию. На текущий момент, боты могут скачивать " +"файлы весом до 20MB. В случае успеха возвращается объект File. " +"Гарантируется, что ссылка на скачивание будет актуальна как минимум 1 " +"час. Когда ссылка перестаёт быть актуальной, новая может быть снова " +"запрошена с помощью get_file." #: of telebot.TeleBot.get_file:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#getfile" @@ -2619,7 +2706,7 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#ge msgid "File identifier" msgstr "id файла" -#: of telebot.TeleBot.get_file:12 telebot.TeleBot.upload_sticker_file:14 +#: of telebot.TeleBot.get_file:12 telebot.TeleBot.upload_sticker_file:21 msgid ":class:`telebot.types.File`" msgstr "" @@ -2641,9 +2728,9 @@ msgid "" "forum topic icon by any user. Requires no parameters. Returns an Array of" " Sticker objects." msgstr "" -"Используйте этот метод, чтобы получить кастомные эмодзи, которые могут быть " -"использованы любыми пользователями в качестве иконок топиков. Не требует параметров. " -"Возвращает массив объектов Sticker." +"Используйте этот метод, чтобы получить кастомные эмодзи, которые могут " +"быть использованы любыми пользователями в качестве иконок топиков. Не " +"требует параметров. Возвращает массив объектов Sticker." #: of telebot.TeleBot.get_forum_topic_icon_stickers:4 msgid "" @@ -2667,9 +2754,9 @@ msgid "" "of the specified user and several of their neighbors in a game. On " "success, returns an Array of GameHighScore objects." msgstr "" -"Используйте этот метод, чтобы получить данные для таблицы рекордов. Вернёт " -"очки указанного пользователя и несколько соседних результатов. В случае успеха " -"возвращает массив объектов GameHighScore." +"Используйте этот метод, чтобы получить данные для таблицы рекордов. " +"Вернёт очки указанного пользователя и несколько соседних результатов. В " +"случае успеха возвращает массив объектов GameHighScore." #: of telebot.TeleBot.get_game_high_scores:4 msgid "" @@ -2678,10 +2765,10 @@ msgid "" "users if the user and their neighbors are not among them. Please note " "that this behavior is subject to change." msgstr "" -"На текущий момент этот метод вернёт очки указанного пользователя и по два соседних " -"результата с каждой стороны. Также вернет результаты трёх лучших игроков, если " -"результат пользователя и соседние не являются тремя лучшими. Пожалуйста учитывайте, " -"что это поведение может быть изменено." +"На текущий момент этот метод вернёт очки указанного пользователя и по два" +" соседних результата с каждой стороны. Также вернет результаты трёх " +"лучших игроков, если результат пользователя и соседние не являются тремя " +"лучшими. Пожалуйста учитывайте, что это поведение может быть изменено." #: of telebot.TeleBot.get_game_high_scores:8 msgid "" @@ -2692,7 +2779,7 @@ msgstr "" "https://core.telegram.org/bots/api#getgamehighscores" #: of telebot.TeleBot.get_game_high_scores:10 telebot.TeleBot.retrieve_data:3 -#: telebot.TeleBot.set_game_score:5 telebot.TeleBot.set_sticker_set_thumb:9 +#: telebot.TeleBot.set_game_score:5 telebot.TeleBot.set_sticker_set_thumbnail:9 msgid "User identifier" msgstr "id пользователя" @@ -2718,8 +2805,8 @@ msgid "" "Use this method to get the current list of the bot's commands. Returns " "List of BotCommand on success." msgstr "" -"Используйте этот метод, чтобы получить текущий список команд бота. Возвращает " -"список объектов BotCommand в случае успеха." +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." #: of telebot.TeleBot.get_my_commands:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getmycommands" @@ -2738,8 +2825,9 @@ msgid "" "Use this method to get the current default administrator rights of the " "bot. Returns ChatAdministratorRights on success." msgstr "" -"Используйте этот метод, чтобы получить текущие права администратора для бота по умолчанию. " -"Возвращает объект ChatAdministratorRights в случае успеха." +"Используйте этот метод, чтобы получить текущие права администратора для " +"бота по умолчанию. Возвращает объект ChatAdministratorRights в случае " +"успеха." #: of telebot.TeleBot.get_my_default_administrator_rights:4 msgid "" @@ -2755,9 +2843,9 @@ msgid "" " Otherwise, the default administrator rights of the bot for groups and " "supergroups will be returned." msgstr "" -"Передайте True, чтобы получить права администратора для бота по умолчанию в каналах. " -"Иначе, будут возвращены права администратора для бота по умолчанию в группах и " -"супергруппах." +"Передайте True, чтобы получить права администратора для бота по умолчанию" +" в каналах. Иначе, будут возвращены права администратора для бота по " +"умолчанию в группах и супергруппах." #: of telebot.TeleBot.get_my_default_administrator_rights:9 msgid "Returns ChatAdministratorRights on success." @@ -2767,6 +2855,79 @@ msgstr "Возвращает объект ChatAdministratorRights в случа msgid ":class:`telebot.types.ChatAdministratorRights`" msgstr "" +#: of telebot.TeleBot.get_my_description:1 +#, fuzzy +msgid "" +"Use this method to get the current bot description for the given user " +"language. Returns BotDescription on success." +msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." + +#: of telebot.TeleBot.get_my_description:4 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmydescription" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatdescription" + +#: of telebot.TeleBot.get_my_description:6 +#: telebot.TeleBot.get_my_short_description:6 +msgid "A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.TeleBot.get_my_description:9 +#, fuzzy +msgid ":class:`telebot.types.BotDescription`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + +#: of telebot.TeleBot.get_my_name:1 +#, fuzzy +msgid "" +"Use this method to get the current bot name for the given user language. " +"Returns BotName on success." +msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." + +#: of telebot.TeleBot.get_my_name:4 +#, fuzzy +msgid "Telegram documentation: https://core.telegram.org/bots/api#getmyname" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getme" + +#: of telebot.TeleBot.get_my_name:6 +msgid "Optional. A two-letter ISO 639-1 language code or an empty string" +msgstr "" + +#: of telebot.TeleBot.get_my_name:9 +#, fuzzy +msgid ":class:`telebot.types.BotName`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + +#: of telebot.TeleBot.get_my_short_description:1 +#, fuzzy +msgid "" +"Use this method to get the current bot short description for the given " +"user language. Returns BotShortDescription on success." +msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." + +#: of telebot.TeleBot.get_my_short_description:4 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getmyshortdescription" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatdescription" + +#: of telebot.TeleBot.get_my_short_description:9 +msgid ":class:`telebot.types.BotShortDescription`" +msgstr "" + #: of telebot.TeleBot.get_state:1 msgid "" "Gets current state of a user. Not recommended to use this method. But it " @@ -2788,8 +2949,8 @@ msgid "" "Use this method to get a sticker set. On success, a StickerSet object is " "returned." msgstr "" -"Используйте этот метод, чтобы получить стикерпак. В случае успеха возвращается " -"объект StickerSet." +"Используйте этот метод, чтобы получить стикерпак. В случае успеха " +"возвращается объект StickerSet." #: of telebot.TeleBot.get_sticker_set:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getstickerset" @@ -2808,8 +2969,8 @@ msgid "" "Use this method to receive incoming updates using long polling (wiki). An" " Array of Update objects is returned." msgstr "" -"Используйте этот метод, чтобы получить новые апдейты с помощью long polling-а (wiki). " -"Возвращается массив объектов Update." +"Используйте этот метод, чтобы получить новые апдейты с помощью long " +"polling-а (wiki). Возвращается массив объектов Update." #: of telebot.TeleBot.get_updates:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#getupdates" @@ -2828,17 +2989,17 @@ msgstr "" "id первого апдейта. Должен быть на единицу больше наибольшего id среди " "ранее полученных апдейтов. По умолчанию, возвращается список апдейтов, " "начиная с самого раннего неполученного. Апдейт считается полученным как " -"только вызван метод getUpdates со смещением больше, чем id этого апдейта. " -"Отрицательное смещение может быть указано для получения последних offset апдейтов. " -"Все предыдущие апдейты будут считаться полученными." +"только вызван метод getUpdates со смещением больше, чем id этого апдейта." +" Отрицательное смещение может быть указано для получения последних offset" +" апдейтов. Все предыдущие апдейты будут считаться полученными." #: of telebot.TeleBot.get_updates:12 msgid "" "Limits the number of updates to be retrieved. Values between 1-100 are " "accepted. Defaults to 100." msgstr "" -"Максимальное число апдейтов для получения. Допускаются значения от 1 до 100. " -"По умолчанию 100." +"Максимальное число апдейтов для получения. Допускаются значения от 1 до " +"100. По умолчанию 100." #: of telebot.TeleBot.get_updates:15 telebot.TeleBot.get_webhook_info:6 #: telebot.TeleBot.polling:21 @@ -2909,9 +3070,9 @@ msgid "" " success, returns a WebhookInfo object. If the bot is using getUpdates, " "will return an object with the url field empty." msgstr "" -"Используйте этот метод, чтобы получить текущий статус вебхука. Не требует параметров. " -"В случае успеха возвращает объект WebhookInfo. Если бот использует getUpdates, " -"вернёт объект с пустым атрибутом url." +"Используйте этот метод, чтобы получить текущий статус вебхука. Не требует" +" параметров. В случае успеха возвращает объект WebhookInfo. Если бот " +"использует getUpdates, вернёт объект с пустым атрибутом url." #: of telebot.TeleBot.get_webhook_info:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#getwebhookinfo" @@ -2926,7 +3087,6 @@ msgid ":class:`telebot.types.WebhookInfo`" msgstr "" #: of telebot.TeleBot.hide_general_forum_topic:1 - msgid "" "Use this method to hide the 'General' topic in a forum supergroup chat. " "The bot must be an administrator in the chat for this to work and must " @@ -2938,7 +3098,6 @@ msgstr "" "True в случае успеха." #: of telebot.TeleBot.hide_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#hidegeneralforumtopic" @@ -2949,8 +3108,8 @@ msgid "" "Wrap polling with infinite loop and exception handling to avoid bot stops" " polling." msgstr "" -"Запустить поллинг в бесконечном цикле с обработкой исключений, чтобы избежать " -"непредвиденных остановок поллинга." +"Запустить поллинг в бесконечном цикле с обработкой исключений, чтобы " +"избежать непредвиденных остановок поллинга." #: of telebot.TeleBot.infinity_polling:5 telebot.TeleBot.polling:13 msgid "Install watchdog and psutil before using restart_on_change option." @@ -2974,8 +3133,9 @@ msgid "" "logging. Use logger levels from logging as a value. None/NOTSET = no " "error logging" msgstr "" -"Кастомный (отличающийся от логгера) уровень логирования для infinity_polling. " -"Используйте уровни из logging в качестве значений. None/NOTSET = не логировать ошибки." +"Кастомный (отличающийся от логгера) уровень логирования для " +"infinity_polling. Используйте уровни из logging в качестве значений. " +"None/NOTSET = не логировать ошибки." #: of telebot.TeleBot.infinity_polling:20 msgid "" @@ -2994,8 +3154,8 @@ msgstr "" "util.update_types. Укажите пустой список, чтобы получать все апдейты, " "кроме chat_member (по умолчанию). Если не задан, будет использована " "последняя настройка. Пожалуйста учитывайте, что этот параметр не влияет " -"на апдейты, отправленные до вызова get_updates, поэтому нежелательные апдейты " -"могут быть получены в течение короткого периода времени." +"на апдейты, отправленные до вызова get_updates, поэтому нежелательные " +"апдейты могут быть получены в течение короткого периода времени." #: of telebot.TeleBot.infinity_polling:29 telebot.TeleBot.polling:47 msgid "Restart a file on file(s) change. Defaults to False" @@ -3037,8 +3197,9 @@ msgid "" "FileHandlerBackend before entering this function" msgstr "" "Эта функция оставлена для обратной совместимости, для загрузки хендлеров " -"из файла с помощью FileHandlerBackend и рекомендуется к использованию только " -"если next_step_backend был определён как FileHandlerBackend до вызова этой функции" +"из файла с помощью FileHandlerBackend и рекомендуется к использованию " +"только если next_step_backend был определён как FileHandlerBackend до " +"вызова этой функции" #: of telebot.TeleBot.load_next_step_handlers:8 msgid "" @@ -3052,8 +3213,7 @@ msgstr "" msgid "" "If True is passed, after the loading file will be deleted, defaults to " "True" -msgstr "" -"Если передано True, файл будет удалён после загрузки, по умолчанию True" +msgstr "Если передано True, файл будет удалён после загрузки, по умолчанию True" #: of telebot.TeleBot.load_reply_handlers:1 msgid "Load reply handlers from save file" @@ -3067,8 +3227,9 @@ msgid "" "before entering this function" msgstr "" "Эта функция оставлена для обратной совместимости, для загрузки хендлеров " -"из файла с помощью FileHandlerBackend и рекомендуется к использованию только " -"если reply_backend был определён как FileHandlerBackend до вызова этой функции" +"из файла с помощью FileHandlerBackend и рекомендуется к использованию " +"только если reply_backend был определён как FileHandlerBackend до вызова " +"этой функции" #: of telebot.TeleBot.load_reply_handlers:7 msgid "" @@ -3082,8 +3243,7 @@ msgstr "" msgid "" "If True is passed, after the loading file will be deleted, defaults to " "True, defaults to True" -msgstr "" -"Если передано True, файл будет удалён после загрузки, по умолчанию True" +msgstr "Если передано True, файл будет удалён после загрузки, по умолчанию True" #: of telebot.TeleBot.log_out:1 msgid "" @@ -3096,10 +3256,10 @@ msgid "" msgstr "" "Используйте этот метод, чтобы отключиться от облачного Bot API сервера " "перед локальным запуском бота. Вы ДОЛЖНЫ отключить бота перед тем, как " -"запускать его локально, иначе нет никаких гарантий, что бот будет получать " -"апдейты. После успешного вызова, вы можете тут же подключиться к локальному " -"серверу, но не сможете подключиться обратно к облачному Bot API серверу в " -"течение 10 минут. Возвращает True в случае успеха." +"запускать его локально, иначе нет никаких гарантий, что бот будет " +"получать апдейты. После успешного вызова, вы можете тут же подключиться к" +" локальному серверу, но не сможете подключиться обратно к облачному Bot " +"API серверу в течение 10 минут. Возвращает True в случае успеха." #: of telebot.TeleBot.log_out:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#logout" @@ -3112,8 +3272,8 @@ msgid "" ":class:`telebot.types.Message` object. All message handlers are tested in" " the order they were added." msgstr "" -"Обрабатывает входящие сообщения всех видов - text, photo, sticker, и т.д. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает входящие сообщения всех видов - text, photo, sticker, и т.д." +" В качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.Message`. Все хендлеры сообщений проверяются в том " "порядке, в котором были добавлены." @@ -3132,9 +3292,9 @@ msgid "" "first parameter. It must return True if the command should handle the " "message." msgstr "" -"Необязательная lambda функция. Получает сообщение (объект Message) в качестве " -"первого параметра. Функция должна вернуть True если хендлер должен обработать " -"сообщение." +"Необязательная lambda функция. Получает сообщение (объект Message) в " +"качестве первого параметра. Функция должна вернуть True если хендлер " +"должен обработать сообщение." #: of telebot.TeleBot.message_handler:52 #: telebot.TeleBot.register_edited_channel_post_handler:23 @@ -3154,10 +3314,10 @@ msgid "" "and check type of the update inside the handler if more than one " "update_type is given" msgstr "" -"Этот декоратор может быть использован, чтобы декорировать функции, которые " -"будут использоваться в качестве middleware перед обработкой апдейтов, " -"будьте аккуратны и проверяйте вид апдейта внутри функции если возможны апдейты " -"разных видов" +"Этот декоратор может быть использован, чтобы декорировать функции, " +"которые будут использоваться в качестве middleware перед обработкой " +"апдейтов, будьте аккуратны и проверяйте вид апдейта внутри функции если " +"возможны апдейты разных видов" #: of telebot.TeleBot.middleware_handler:9 msgid "Usage of middleware_handler" @@ -3194,9 +3354,9 @@ msgid "" "administrator in the chat for this to work and must have the appropriate " "admin rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот должен " -"быть администратором чата и иметь соответствующие права администратора. " -"Возвращает True в случае успеха." +"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот " +"должен быть администратором чата и иметь соответствующие права " +"администратора. Возвращает True в случае успеха." #: of telebot.TeleBot.pin_chat_message:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#pinchatmessage" @@ -3211,8 +3371,8 @@ msgid "" "Pass True, if it is not necessary to send a notification to all group " "members about the new pinned message" msgstr "" -"Передайте True, если всем участникам группы необходимо отправить уведомление " -"о закреплённом сообщении" +"Передайте True, если всем участникам группы необходимо отправить " +"уведомление о закреплённом сообщении" #: of telebot.TeleBot.poll_answer_handler:1 msgid "" @@ -3223,8 +3383,8 @@ msgid "" msgstr "" "Обрабатывает изменения ответа пользователя в не анонимном опросе(когда " "пользователь меняет выбор). Боты получают новые ответы только в опросах, " -"которые отправили сами. В качестве параметра передаёт в декорируемую функцию " -"объект :class:`telebot.types.PollAnswer`." +"которые отправили сами. В качестве параметра передаёт в декорируемую " +"функцию объект :class:`telebot.types.PollAnswer`." #: of telebot.TeleBot.poll_handler:1 msgid "" @@ -3232,9 +3392,10 @@ msgid "" "polls and polls, which are sent by the bot As a parameter to the " "decorator function, it passes :class:`telebot.types.Poll` object." msgstr "" -"Обрабатывает изменения в состоянии опроса. Боты получают только апдейты " -"о завершенных опросах и опросах, которые отправили сами. В качестве " -"параметра передаёт в декорируемую функцию объект :class:`telebot.types.Poll`." +"Обрабатывает изменения в состоянии опроса. Боты получают только апдейты о" +" завершенных опросах и опросах, которые отправили сами. В качестве " +"параметра передаёт в декорируемую функцию объект " +":class:`telebot.types.Poll`." #: of telebot.TeleBot.polling:1 msgid "" @@ -3283,8 +3444,8 @@ msgstr "" "util.update_types. Укажите пустой список, чтобы получать все апдейты, " "кроме chat_member (по умолчанию). Если не задан, будет использована " "последняя настройка. Пожалуйста учитывайте, что этот параметр не влияет " -"на апдейты, отправленные до вызова get_updates, поэтому нежелательные апдейты " -"могут быть получены в течение короткого периода времени." +"на апдейты, отправленные до вызова get_updates, поэтому нежелательные " +"апдейты могут быть получены в течение короткого периода времени." #: of telebot.TeleBot.polling:34 msgid "" @@ -3308,15 +3469,15 @@ msgid "" " call to the get_updates, so unwanted updates may be received for a short" " period of time." msgstr "" -"Пожалуйста учитывайте, что этот параметр не влияет " -"на апдейты, отправленные до вызова get_updates, поэтому нежелательные апдейты " -"могут быть получены в течение короткого периода времени." +"Пожалуйста учитывайте, что этот параметр не влияет на апдейты, " +"отправленные до вызова get_updates, поэтому нежелательные апдейты могут " +"быть получены в течение короткого периода времени." #: of telebot.TeleBot.polling:44 msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." msgstr "" -"Устарело, используйте non_stop. " -"Старая опечатка, оставлено для обратной совместимости" +"Устарело, используйте non_stop. Старая опечатка, оставлено для обратной " +"совместимости" #: of telebot.TeleBot.polling:50 msgid "Path to watch for changes. Defaults to None" @@ -3328,8 +3489,8 @@ msgid "" "checkout. As a parameter to the decorator function, it passes " ":class:`telebot.types.PreCheckoutQuery` object." msgstr "" -"Новая pre-checkout query. Содержит полную информацию о заказе. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Новая pre-checkout query. Содержит полную информацию о заказе. В качестве" +" параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.PreCheckoutQuery`." #: of telebot.TeleBot.process_new_updates:1 @@ -3337,8 +3498,8 @@ msgid "" "Processes new updates. Just pass list of subclasses of Update to this " "method." msgstr "" -"Обрабатывает новые апдейты. Просто передайте список апдейтов(Update и " -"его наследники)." +"Обрабатывает новые апдейты. Просто передайте список апдейтов(Update и его" +" наследники)." #: of telebot.TeleBot.process_new_updates:3 msgid "List of :class:`telebot.types.Update` objects." @@ -3355,9 +3516,10 @@ msgid "" "have the appropriate admin rights. Pass False for all boolean parameters " "to demote a user." msgstr "" -"Используйте этот метод, чтобы повысить или понизить пользователя в супергруппе " -"или канале. Бот должен быть администратором чата и иметь соответствующие права " -"администратора. Передайте False во все boolean параметры, чтобы понизить пользователя." +"Используйте этот метод, чтобы повысить или понизить пользователя в " +"супергруппе или канале. Бот должен быть администратором чата и иметь " +"соответствующие права администратора. Передайте False во все boolean " +"параметры, чтобы понизить пользователя." #: of telebot.TeleBot.promote_chat_member:5 msgid "" @@ -3371,44 +3533,53 @@ msgstr "" msgid "" "Unique identifier for the target chat or username of the target channel (" " in the format @channelusername)" -msgstr "" -"Уникальный id чата или username канала (в формате @channelusername)" +msgstr "Уникальный id чата или username канала (в формате @channelusername)" #: of telebot.TeleBot.promote_chat_member:14 msgid "" "Pass True, if the administrator can change chat title, photo and other " "settings" msgstr "" -"Передайте True, если администратор может менять название чата, аватарку " -"и другие настройки" +"Передайте True, если администратор может менять название чата, аватарку и" +" другие настройки" #: of telebot.TeleBot.promote_chat_member:17 msgid "Pass True, if the administrator can create channel posts, channels only" -msgstr "Передайте True, если администратор может создавать посты в канале, только для каналов" +msgstr "" +"Передайте True, если администратор может создавать посты в канале, только" +" для каналов" #: of telebot.TeleBot.promote_chat_member:20 msgid "" "Pass True, if the administrator can edit messages of other users, " "channels only" msgstr "" -"Передайте True, если администратор может изменять сообщения других пользователей, " -"только для каналов" +"Передайте True, если администратор может изменять сообщения других " +"пользователей, только для каналов" #: of telebot.TeleBot.promote_chat_member:23 msgid "Pass True, if the administrator can delete messages of other users" -msgstr "Передайте True, если администратор может удалять сообщения других пользователей" +msgstr "" +"Передайте True, если администратор может удалять сообщения других " +"пользователей" #: of telebot.TeleBot.promote_chat_member:26 msgid "Pass True, if the administrator can invite new users to the chat" -msgstr "Передайте True, если администратор может приглашать новых пользователей в чат" +msgstr "" +"Передайте True, если администратор может приглашать новых пользователей в" +" чат" #: of telebot.TeleBot.promote_chat_member:29 msgid "Pass True, if the administrator can restrict, ban or unban chat members" -msgstr "Передайте True, если администратор может ограничивать, банить или разбанивать участников чата" +msgstr "" +"Передайте True, если администратор может ограничивать, банить или " +"разбанивать участников чата" #: of telebot.TeleBot.promote_chat_member:32 msgid "Pass True, if the administrator can pin messages, supergroups only" -msgstr "Передайте True, если администратор может закреплять сообщения, только для супергрупп" +msgstr "" +"Передайте True, если администратор может закреплять сообщения, только для" +" супергрупп" #: of telebot.TeleBot.promote_chat_member:35 msgid "" @@ -3417,9 +3588,10 @@ msgid "" "directly or indirectly (promoted by administrators that were appointed by" " him)" msgstr "" -"Передайте True, если администратор может добавлять новых администраторов с " -"подмножеством его собственных прав администратора или понижать администраторов, " -"которых он повысил, напрямую или косвенно (администраторами, которых он назначил)" +"Передайте True, если администратор может добавлять новых администраторов " +"с подмножеством его собственных прав администратора или понижать " +"администраторов, которых он повысил, напрямую или косвенно " +"(администраторами, которых он назначил)" #: of telebot.TeleBot.promote_chat_member:40 msgid "Pass True, if the administrator's presence in the chat is hidden" @@ -3432,19 +3604,19 @@ msgid "" "anonymous administrators in supergroups and ignore slow mode. Implied by " "any other administrator privilege" msgstr "" -"Передайте True, если администратор имеет доступ к логу событий чата, статистике " -"чата, статистике сообщений в каналах, видеть участников канала, видеть анонимных " -"администраторов в супергруппах и игнорировать медленный режим. Подразумевается " -"любым другим правом администратора" +"Передайте True, если администратор имеет доступ к логу событий чата, " +"статистике чата, статистике сообщений в каналах, видеть участников " +"канала, видеть анонимных администраторов в супергруппах и игнорировать " +"медленный режим. Подразумевается любым другим правом администратора" #: of telebot.TeleBot.promote_chat_member:49 msgid "" "Pass True, if the administrator can manage voice chats For now, bots can " "use this privilege only for passing to other administrators." msgstr "" -"Передайте True, если администратор может управлять голосовыми чатами. На текущий " -"момент, боты могут использовать это право администратора только для передачи другим " -"администраторам." +"Передайте True, если администратор может управлять голосовыми чатами. На " +"текущий момент, боты могут использовать это право администратора только " +"для передачи другим администраторам." #: of telebot.TeleBot.promote_chat_member:53 msgid "Deprecated, use can_manage_video_chats." @@ -3455,8 +3627,8 @@ msgid "" "Pass True if the user is allowed to create, rename, close, and reopen " "forum topics, supergroups only" msgstr "" -"Передайте True, если пользователю разрешено создавать, переименовывать, закрывать, " -"и возобновлять топики, только для супергрупп" +"Передайте True, если пользователю разрешено создавать, переименовывать, " +"закрывать, и возобновлять топики, только для супергрупп" #: of telebot.TeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." @@ -3497,8 +3669,8 @@ msgid "" "True if you need to pass TeleBot instance to handler(useful for " "separating handlers into different files)" msgstr "" -"True, если вам нужно передать экземпляр класса TeleBot в хендлер(удобно для " -"разбиения кода на файлы)" +"True, если вам нужно передать экземпляр класса TeleBot в хендлер(удобно " +"для разбиения кода на файлы)" #: of telebot.TeleBot.register_channel_post_handler:1 msgid "Registers channel post message handler." @@ -3551,7 +3723,8 @@ msgid "" "Registers a callback function to be notified when a reply to `message` " "arrives." msgstr "" -"Регистрирует функцию для вызова при получении ответа на выбранное сообщение." +"Регистрирует функцию для вызова при получении ответа на выбранное " +"сообщение." #: of telebot.TeleBot.register_for_reply:3 #: telebot.TeleBot.register_for_reply_by_message_id:3 @@ -3572,8 +3745,8 @@ msgid "" "The callback function to be called when a reply arrives. Must accept one " "`message` parameter, which will contain the replied message." msgstr "" -"Функция, которую нужно вызвать при получении ответа на сообщение. Должна принимать " -"параметр `message`, который будет содержать ответ на сообщение." +"Функция, которую нужно вызвать при получении ответа на сообщение. Должна " +"принимать параметр `message`, который будет содержать ответ на сообщение." #: of telebot.TeleBot.register_for_reply:12 #: telebot.TeleBot.register_for_reply_by_message_id:12 @@ -3638,7 +3811,8 @@ msgid "" "Registers a callback function to be notified when new message arrives " "after `message`." msgstr "" -"Регистрирует функцию для вызова при получении нового сообщения после указанного." +"Регистрирует функцию для вызова при получении нового сообщения после " +"указанного." #: of telebot.TeleBot.register_next_step_handler:3 #: telebot.TeleBot.register_next_step_handler_by_chat_id:3 @@ -3670,10 +3844,10 @@ msgid "" "Registers a callback function to be notified when new message arrives in " "the given chat." msgstr "" -"Регистрирует функцию для вызова при получении нового сообщения в заданном чате." +"Регистрирует функцию для вызова при получении нового сообщения в заданном" +" чате." #: of telebot.TeleBot.register_next_step_handler_by_chat_id:5 - msgid "The chat (chat ID) for which we want to handle new message." msgstr "Чат (id чата), в котором нужно обработать новое сообщение." @@ -3704,35 +3878,33 @@ msgid "" "the can_manage_topics administrator rights, unless it is the creator of " "the topic. Returns True on success." msgstr "" -"Используйте этот метод, чтобы возобновить закрытый топик в супергруппе с топиками. " -"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " -"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы возобновить закрытый топик в супергруппе с " +"топиками. Бот должен быть администратором чата и иметь права " +"администратора can_manage_topics, кроме случаев, когда бот является " +"создателем топика. Возвращает True в случае успеха." #: of telebot.TeleBot.reopen_forum_topic:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopenforumtopic" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#reopenforumtopic" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#reopenforumtopic" #: of telebot.TeleBot.reopen_forum_topic:10 msgid "Identifier of the topic to reopen" msgstr "id топика для возобновления" #: of telebot.TeleBot.reopen_general_forum_topic:1 - msgid "" "Use this method to reopen the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы возобновить топик 'General' в супергруппе с топиками. " -"Бот должен быть администратором чата и иметь права администратора can_manage_topics, " -"кроме случаев, когда бот является создателем топика. Возвращает True в случае успеха." +"Используйте этот метод, чтобы возобновить топик 'General' в супергруппе с" +" топиками. Бот должен быть администратором чата и иметь права " +"администратора can_manage_topics, кроме случаев, когда бот является " +"создателем топика. Возвращает True в случае успеха." #: of telebot.TeleBot.reopen_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#reopengeneralforumtopic" @@ -3788,18 +3960,24 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#restrictchatmember" -#: of telebot.TeleBot.restrict_chat_member:14 +#: of telebot.TeleBot.restrict_chat_member:8 +msgid "" +"Individual parameters are deprecated and will be removed, use " +"'permissions' instead." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:17 msgid "" "Date when restrictions will be lifted for the user, unix time. If user is" " restricted for more than 366 days or less than 30 seconds from the " "current time, they are considered to be restricted forever" msgstr "" "Дата, когда ограничения будут сняты с пользователя, UNIX timestamp. Если " -"пользователь ограничен более чем на 366 дней или менее чем на 30 секунд с " -"текущего момента, он будет ограничен навсегда (пока ограничения не будут " -"сняты вручную)" +"пользователь ограничен более чем на 366 дней или менее чем на 30 секунд с" +" текущего момента, он будет ограничен навсегда (пока ограничения не будут" +" сняты вручную)" -#: of telebot.TeleBot.restrict_chat_member:19 +#: of telebot.TeleBot.restrict_chat_member:22 msgid "" "Pass True, if the user can send text messages, contacts, locations and " "venues" @@ -3807,62 +3985,78 @@ msgstr "" "Передайте True, если пользователь может отправлять текстовые сообщения, " "контакты, местоположения и места" -#: of telebot.TeleBot.restrict_chat_member:22 +#: of telebot.TeleBot.restrict_chat_member:25 msgid "" "Pass True, if the user can send audios, documents, photos, videos, video " "notes and voice notes, implies can_send_messages" msgstr "" -"Передайте True, если пользователь может отправлять аудио, документы, фото, " -"видео, видео заметки (кружочки) и голосовые сообщения, подразумевает " -"can_send_messages" +"Передайте True, если пользователь может отправлять аудио, документы, " +"фото, видео, видео заметки (кружочки) и голосовые сообщения, " +"подразумевает can_send_messages" -#: of telebot.TeleBot.restrict_chat_member:26 +#: of telebot.TeleBot.restrict_chat_member:29 msgid "Pass True, if the user is allowed to send polls, implies can_send_messages" msgstr "" -"Передайте True, если пользователю разрешено отправлять опросы, подразумевает " -"can_send_messages" +"Передайте True, если пользователю разрешено отправлять опросы, " +"подразумевает can_send_messages" -#: of telebot.TeleBot.restrict_chat_member:29 +#: of telebot.TeleBot.restrict_chat_member:32 msgid "" "Pass True, if the user can send animations, games, stickers and use " "inline bots, implies can_send_media_messages" msgstr "" -"Передайте True, если пользователь может отправлять гифки, игры, стикеры и " -"использовать inline ботов, подразумевает can_send_media_messages" +"Передайте True, если пользователь может отправлять гифки, игры, стикеры и" +" использовать inline ботов, подразумевает can_send_media_messages" -#: of telebot.TeleBot.restrict_chat_member:32 +#: of telebot.TeleBot.restrict_chat_member:35 msgid "" "Pass True, if the user may add web page previews to their messages, " "implies can_send_media_messages" msgstr "" -"Передайте True, если пользователь может добавлять превью ссылок к своим сообщениям, " -"подразумевает can_send_media_messages" +"Передайте True, если пользователь может добавлять превью ссылок к своим " +"сообщениям, подразумевает can_send_media_messages" -#: of telebot.TeleBot.restrict_chat_member:36 +#: of telebot.TeleBot.restrict_chat_member:39 msgid "" "Pass True, if the user is allowed to change the chat title, photo and " "other settings. Ignored in public supergroups" msgstr "" -"Передайте True, если пользователю разрешено изменять название чата, аватарку и " -"другие настройки. Игнорируется в публичных супергруппах" +"Передайте True, если пользователю разрешено изменять название чата, " +"аватарку и другие настройки. Игнорируется в публичных супергруппах" -#: of telebot.TeleBot.restrict_chat_member:40 +#: of telebot.TeleBot.restrict_chat_member:43 msgid "" "Pass True, if the user is allowed to invite new users to the chat, " "implies can_invite_users" -msgstr "" -"Передайте True, если пользователю разрешено приглашать пользователей в чат" +msgstr "Передайте True, если пользователю разрешено приглашать пользователей в чат" -#: of telebot.TeleBot.restrict_chat_member:44 +#: of telebot.TeleBot.restrict_chat_member:47 msgid "" "Pass True, if the user is allowed to pin messages. Ignored in public " "supergroups" msgstr "" -"Передайте True, если пользователю разрешено закреплять сообщения. Игнорируется " -"в публичных супергруппах" +"Передайте True, если пользователю разрешено закреплять сообщения. " +"Игнорируется в публичных супергруппах" -#: of telebot.TeleBot.restrict_chat_member:47 -#: telebot.TeleBot.set_chat_permissions:14 telebot.TeleBot.unban_chat_member:19 +#: of telebot.TeleBot.restrict_chat_member:50 +#: telebot.TeleBot.set_chat_permissions:14 +msgid "" +"Pass True if chat permissions are set independently. Otherwise, the " +"can_send_other_messages and can_add_web_page_previews permissions will " +"imply the can_send_messages, can_send_audios, can_send_documents, " +"can_send_photos, can_send_videos, can_send_video_notes, and " +"can_send_voice_notes permissions; the can_send_polls permission will " +"imply the can_send_messages permission." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:56 +msgid "" +"Pass ChatPermissions object to set all permissions at once. Use this " +"param instead of passing all boolean parameters." +msgstr "" + +#: of telebot.TeleBot.restrict_chat_member:60 +#: telebot.TeleBot.set_chat_permissions:20 telebot.TeleBot.unban_chat_member:19 msgid "True on success" msgstr "True в случае успеха" @@ -3885,10 +4079,10 @@ msgid "" "must be an administrator in the chat for this to work and must have the " "appropriate admin rights." msgstr "" -"Используйте этот метод, чтобы аннулировать ссылку-приглашение, созданную ботом. " -"Примечание: Если аннулируется главная ссылка-приглашение, автоматически " -"генерируется новая. Бот должен быть администратором чата и иметь соответствующие " -"права администратора." +"Используйте этот метод, чтобы аннулировать ссылку-приглашение, созданную " +"ботом. Примечание: Если аннулируется главная ссылка-приглашение, " +"автоматически генерируется новая. Бот должен быть администратором чата и " +"иметь соответствующие права администратора." #: of telebot.TeleBot.revoke_chat_invite_link:5 msgid "" @@ -3941,10 +4135,11 @@ msgid "" " load on your bot's server, and higher values to increase your bot's " "throughput., defaults to None" msgstr "" -"Максимально-допустимое количество одновременных HTTPS подключений к вебхуку " -"для доставки апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения, " -" чтобы уменьшить нагрузку на ваш сервер и большие значения для увеличения " -"пропускной способности вашего бота, по умолчанию None." +"Максимально-допустимое количество одновременных HTTPS подключений к " +"вебхуку для доставки апдейтов, 1-100. По умолчанию 40. Используйте " +"меньшие значения, чтобы уменьшить нагрузку на ваш сервер и большие " +"значения для увеличения пропускной способности вашего бота, по умолчанию " +"None." #: of telebot.TeleBot.run_webhooks:28 msgid "" @@ -3959,16 +4154,17 @@ msgstr "" "Например, укажите [“message”, “edited_channel_post”, “callback_query”], " "чтобы получать апдейты только этих видов. Полный список доступных видов " "апдейтов - util.update_types. Укажите пустой список, чтобы получать все " -"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " -"последняя настройка. По умолчанию None" +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет " +"использована последняя настройка. По умолчанию None" #: of telebot.TeleBot.run_webhooks:33 telebot.TeleBot.set_webhook:32 msgid "" "The fixed IP address which will be used to send webhook requests instead " "of the IP address resolved through DNS, defaults to None" msgstr "" -"Фиксированный IP адрес, который будет использоваться для отправки запросов " -"к вебхуку вместо IP адреса, полученного через DNS, по умолчанию None" +"Фиксированный IP адрес, который будет использоваться для отправки " +"запросов к вебхуку вместо IP адреса, полученного через DNS, по умолчанию " +"None" #: of telebot.TeleBot.run_webhooks:42 msgid "Secret token to be used to verify the webhook request, defaults to None" @@ -3993,10 +4189,10 @@ msgid "" "currently send animation files of up to 50 MB in size, this limit may be " "changed in the future." msgstr "" -"Используйте этот метод, чтобы отправить гифку (GIF или H.264/MPEG-4 AVC видео " -"без звука). В случае успеха возвращается отправленное сообщение (Message). На " -"текущий момент, боты могут отправлять гифки весом до 50 MB, это ограничение " -"может измениться в будущем." +"Используйте этот метод, чтобы отправить гифку (GIF или H.264/MPEG-4 AVC " +"видео без звука). В случае успеха возвращается отправленное сообщение " +"(Message). На текущий момент, боты могут отправлять гифки весом до 50 MB," +" это ограничение может измениться в будущем." #: of telebot.TeleBot.send_animation:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendanimation" @@ -4010,9 +4206,9 @@ msgid "" "new animation using multipart/form-data." msgstr "" "Гиф-ка для отправки. Передайте file_id (String), чтобы отправить гифку, " -"которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить гифку из интернета или загрузите новую гифку " -"с помощью multipart/form-data." +"которая уже загружена на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить гифку из интернета или загрузите новую " +"гифку с помощью multipart/form-data." #: of telebot.TeleBot.send_animation:13 msgid "Duration of sent animation in seconds" @@ -4040,18 +4236,19 @@ msgstr "" "Обложка отправленного файла; может быть проигнорирована, если генерация " "обложки поддерживается на стороне сервера. Обложка должна быть картинкой " "в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " -"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" -"data. Обложки не могут быть использованы повторно и могут быть загружены " -"только как новый файл, так что вы можете передать “attach://” " -"если обложка была загружена с помощью multipart/form-data под именем ." +"превышать 320. Игнорируется, если файл не загружен с помощью multipart" +"/form-data. Обложки не могут быть использованы повторно и могут быть " +"загружены только как новый файл, так что вы можете передать " +"“attach://” если обложка была загружена с помощью " +"multipart/form-data под именем ." #: of telebot.TeleBot.send_animation:28 msgid "" "Animation caption (may also be used when resending animation by file_id)," " 0-1024 characters after entities parsing" msgstr "" -"Подпись к гифке (может быть использована при повторной отправке гифки по file_id), " -"0-1024 символа после форматирования" +"Подпись к гифке (может быть использована при повторной отправке гифки по " +"file_id), 0-1024 символа после форматирования" #: of telebot.TeleBot.send_animation:31 msgid "Mode for parsing entities in the animation caption" @@ -4070,10 +4267,15 @@ msgid "Identifier of a message thread, in which the video will be sent" msgstr "id топика, в который будет отправлено видео" #: of telebot.TeleBot.send_animation:60 - msgid "Pass True, if the animation should be sent as a spoiler" msgstr "Передайте True, если гифку нужно отправить как спойлер" +#: of telebot.TeleBot.send_animation:63 telebot.TeleBot.send_audio:63 +#: telebot.TeleBot.send_document:56 telebot.TeleBot.send_video:64 +#: telebot.TeleBot.send_video_note:48 +msgid "Deprecated. Use thumbnail instead" +msgstr "" + #: of telebot.TeleBot.send_audio:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -4082,11 +4284,12 @@ msgid "" " audio files of up to 50 MB in size, this limit may be changed in the " "future." msgstr "" -"Используйте этот метод, чтобы отправить аудио, если вы хотите, чтобы клиенты " -"(приложения) Telegram проигрывали их в музыкальном проигрывателе. Ваше аудио " -"должно быть в формате .MP3 или .M4A. В случае успеха возвращается отправленное " -"сообщение (Message). На текущий момент, боты могут отправлять аудио весом до 50 MB, " -"это ограничение может измениться в будущем." +"Используйте этот метод, чтобы отправить аудио, если вы хотите, чтобы " +"клиенты (приложения) Telegram проигрывали их в музыкальном проигрывателе." +" Ваше аудио должно быть в формате .MP3 или .M4A. В случае успеха " +"возвращается отправленное сообщение (Message). На текущий момент, боты " +"могут отправлять аудио весом до 50 MB, это ограничение может измениться в" +" будущем." #: of telebot.TeleBot.send_audio:5 msgid "For sending voice messages, use the send_voice method instead." @@ -4105,9 +4308,9 @@ msgid "" "format." msgstr "" "Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " -"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " -"multipart/form-data. Аудио должно быть в формате .MP3 или .M4A." +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить аудио из интернета или загрузите новое с " +"помощью multipart/form-data. Аудио должно быть в формате .MP3 или .M4A." #: of telebot.TeleBot.send_audio:17 msgid "Audio caption, 0-1024 characters after entities parsing" @@ -4130,8 +4333,8 @@ msgid "" "Mode for parsing entities in the audio caption. See formatting options " "for more details." msgstr "" -"Режим форматирования подписи к аудио. См. formatting options " -"для получения подробностей." +"Режим форматирования подписи к аудио. См. formatting options для " +"получения подробностей." #: of telebot.TeleBot.send_audio:45 msgid "" @@ -4146,10 +4349,11 @@ msgstr "" "Обложка отправленного файла; может быть проигнорирована, если генерация " "обложки поддерживается на стороне сервера. Обложка должна быть картинкой " "в формате JPEG и весить менее 200 kB. Ширина и высота обложки не должны " -"превышать 320. Игнорируется, если файл не загружен с помощью multipart/form-" -"data. Обложки не могут быть использованы повторно и могут быть загружены " -"только как новый файл, так что вы можете передать “attach://” " -"если обложка была загружена с помощью multipart/form-data под именем ." +"превышать 320. Игнорируется, если файл не загружен с помощью multipart" +"/form-data. Обложки не могут быть использованы повторно и могут быть " +"загружены только как новый файл, так что вы можете передать " +"“attach://” если обложка была загружена с помощью " +"multipart/form-data под именем ." #: of telebot.TeleBot.send_audio:51 telebot.TeleBot.send_document:35 #: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:37 @@ -4157,8 +4361,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the caption, " "which can be specified instead of parse_mode" msgstr "" -"Список отформатированных частей подписи в формате JSON, можно использовать вместо " -"parse_mode" +"Список отформатированных частей подписи в формате JSON, можно " +"использовать вместо parse_mode" #: of telebot.TeleBot.send_chat_action:1 msgid "" @@ -4169,8 +4373,8 @@ msgid "" msgstr "" "Используйте этот метод, когда вам нужно показать пользователю, что бот " "что-то делает. Статус устанавливается на 5 секунд или менее (когда от " -"бота приходит сообщение, клиенты (приложения) Telegram убирают статус typing). " -"Возвращает True в случае успеха." +"бота приходит сообщение, клиенты (приложения) Telegram убирают статус " +"typing). Возвращает True в случае успеха." #: of telebot.TeleBot.send_chat_action:5 msgid "" @@ -4181,8 +4385,9 @@ msgid "" msgstr "" "Пример: ImageBot-у требуется время, чтобы обработать запрос и загрузить " "изображение. Вместо отправки текстового сообщения “Отправка изображения, " -"пожалуйста подождите…”, бот может использовать sendChatAction с параметром " -"action = upload_photo. Пользователь увидит статус бота “sending photo”." +"пожалуйста подождите…”, бот может использовать sendChatAction с " +"параметром action = upload_photo. Пользователь увидит статус бота " +"“sending photo”." #: of telebot.TeleBot.send_chat_action:8 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" @@ -4205,13 +4410,12 @@ msgid "" msgstr "" "Тип действия. Выберите один, в зависимости от того, что получит " "пользователь: typing для текстовых сообщений, upload_photo для фото, " -"record_video или upload_video для видео, record_voice или upload_voice для " -"голосовых сообщений, upload_document для файлов, choose_sticker для " -"стикеров, find_location для данных о местоположении, record_video_note или " -"upload_video_note для видео заметок (кружочков)." +"record_video или upload_video для видео, record_voice или upload_voice " +"для голосовых сообщений, upload_document для файлов, choose_sticker для " +"стикеров, find_location для данных о местоположении, record_video_note " +"или upload_video_note для видео заметок (кружочков)." #: of telebot.TeleBot.send_chat_action:22 - msgid "" "The thread identifier of a message from which the reply will be " "sent(supergroups only)" @@ -4222,8 +4426,8 @@ msgid "" "Use this method to send phone contacts. On success, the sent Message is " "returned." msgstr "" -"Используйте этот метод, чтобы отправить контакт. В случае успеха возвращается " -"отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить контакт. В случае успеха " +"возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_contact:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendcontact" @@ -4251,8 +4455,8 @@ msgid "" "Pass True, if the message should be sent even if one of the specified " "replied-to messages is not found." msgstr "" -"Передайте True, если сообщение должно быть отправлено даже если одно из сообщений, " -"на которые нужно ответить, не найдено." +"Передайте True, если сообщение должно быть отправлено даже если одно из " +"сообщений, на которые нужно ответить, не найдено." #: of telebot.TeleBot.send_contact:41 telebot.TeleBot.send_venue:54 msgid "The thread identifier of a message from which the reply will be sent" @@ -4263,8 +4467,9 @@ msgid "" "Use this method to send an animated emoji that will display a random " "value. On success, the sent Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить анимированный эмодзи, который покажет " -"случайное значение. В случае успеха возвращается отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить анимированный эмодзи, который " +"покажет случайное значение. В случае успеха возвращается отправленное " +"сообщение (Message)." #: of telebot.TeleBot.send_dice:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#senddice" @@ -4277,9 +4482,9 @@ msgid "" " and “🎳”, values 1-5 for “🏀” and “⚽”, and values 1-64 for “🎰”. Defaults " "to “🎲”" msgstr "" -"Эмодзи, на котором основана анимация. На текущий момент, должно быть одним из " -"“🎲”, “🎯”, “🏀”, “⚽”, “🎳”, или “🎰”. Значение может быть 1-6 для “🎲”, “🎯”" -" и “🎳”, 1-5 для “🏀” и “⚽”, и 1-64 для “🎰”. По умолчанию “🎲”" +"Эмодзи, на котором основана анимация. На текущий момент, должно быть " +"одним из “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, или “🎰”. Значение может быть 1-6 для " +"“🎲”, “🎯” и “🎳”, 1-5 для “🏀” и “⚽”, и 1-64 для “🎰”. По умолчанию “🎲”" #: of telebot.TeleBot.send_dice:29 msgid "Protects the contents of the sent message from forwarding" @@ -4300,18 +4505,18 @@ msgid "" "String for Telegram to get a file from the Internet, or upload a new one " "using multipart/form-data" msgstr "" -"(документ) Файл для отправки. Передайте file_id (String), чтобы отправить файл, " -"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить файл из интернета или загрузите новый с помощью " -"multipart/form-data" +"(документ) Файл для отправки. Передайте file_id (String), чтобы отправить" +" файл, который уже загружен на сервера Telegram (рекомендуется), " +"передайте HTTP URL (String), чтобы отправить файл из интернета или " +"загрузите новый с помощью multipart/form-data" #: of telebot.TeleBot.send_document:15 msgid "" "Document caption (may also be used when resending documents by file_id), " "0-1024 characters after entities parsing" msgstr "" -"Подпись к файлу (может быть использована при повторной отправке файла по file_id), " -"0-1024 символа после форматирования" +"Подпись к файлу (может быть использована при повторной отправке файла по " +"file_id), 0-1024 символа после форматирования" #: of telebot.TeleBot.send_document:23 msgid "Mode for parsing entities in the document caption" @@ -4328,29 +4533,30 @@ msgid "" "“attach://” if the thumbnail was uploaded using " "multipart/form-data under " msgstr "" -"InputFile или String : Обложка отправленного файла; может быть проигнорирована, " -"если генерация обложки поддерживается на стороне сервера. Обложка должна " -"быть картинкой в формате JPEG и весить менее 200 kB. Ширина и высота обложки " -"не должны превышать 320. Игнорируется, если файл не загружен с помощью " -"multipart/form-data. Обложки не могут быть использованы повторно и могут быть " -"загружены только как новый файл, так что вы можете передать " -"“attach://” если обложка была загружена с помощью " -"multipart/form-data под именем ." +"InputFile или String : Обложка отправленного файла; может быть " +"проигнорирована, если генерация обложки поддерживается на стороне " +"сервера. Обложка должна быть картинкой в формате JPEG и весить менее 200 " +"kB. Ширина и высота обложки не должны превышать 320. Игнорируется, если " +"файл не загружен с помощью multipart/form-data. Обложки не могут быть " +"использованы повторно и могут быть загружены только как новый файл, так " +"что вы можете передать “attach://” если обложка была " +"загружена с помощью multipart/form-data под именем ." #: of telebot.TeleBot.send_document:41 msgid "" "allows to define file name that will be visible in the Telegram instead " "of original file name" msgstr "" -"позволяет задать имя файла, которое будет показано в Telegram вместо настоящего" +"позволяет задать имя файла, которое будет показано в Telegram вместо " +"настоящего" #: of telebot.TeleBot.send_document:44 msgid "" "Disables automatic server-side content type detection for files uploaded " "using multipart/form-data" msgstr "" -"Отключает автоматическое обнаружение типа файла на стороне сервера для файлов, " -"загруженных с помощью multipart/form-data" +"Отключает автоматическое обнаружение типа файла на стороне сервера для " +"файлов, загруженных с помощью multipart/form-data" #: of telebot.TeleBot.send_document:47 telebot.TeleBot.send_sticker:33 #: telebot.TeleBot.send_video:55 @@ -4374,8 +4580,8 @@ msgid "" "Short name of the game, serves as the unique identifier for the game. Set" " up your games via @BotFather." msgstr "" -"Короткое имя игры, служит в качестве уникального id игры. Настройте свои игры" -"через @BotFather." +"Короткое имя игры, служит в качестве уникального id игры. Настройте свои " +"игрычерез @BotFather." #: of telebot.TeleBot.send_game:20 msgid "Timeout in seconds for waiting for a response from the bot." @@ -4386,7 +4592,8 @@ msgid "" "Pass True, if content of the message needs to be protected from being " "viewed by the bot." msgstr "" -"Передайте True, если содержимое сообщение должно быть защищено от просмотра ботом." +"Передайте True, если содержимое сообщение должно быть защищено от " +"просмотра ботом." #: of telebot.TeleBot.send_game:29 msgid "" @@ -4425,8 +4632,8 @@ msgid "" "a marketing image for a service. People like it better when they see what" " they are paying for." msgstr "" -"URL фото продукта. Может быть фото товаров или рекламным изображением сервиса. " -"Людям больше нравится, когда они видят, за что платят." +"URL фото продукта. Может быть фото товаров или рекламным изображением " +"сервиса. Людям больше нравится, когда они видят, за что платят." #: of telebot.TeleBot.send_invoice:73 msgid "" @@ -4434,9 +4641,9 @@ msgid "" " price' button will be shown. If not empty, the first button must be a " "Pay button" msgstr "" -"JSON-сериализованный объект inline клавиатуры. Если пустой, будет показана " -"одна кнопка 'Pay total price'. Если не пустой, первая кнопка должна быть " -"кнопкой для оплаты" +"JSON-сериализованный объект inline клавиатуры. Если пустой, будет " +"показана одна кнопка 'Pay total price'. Если не пустой, первая кнопка " +"должна быть кнопкой для оплаты" #: of telebot.TeleBot.send_invoice:81 telebot.TeleBot.set_webhook:39 msgid "Timeout of a request, defaults to None" @@ -4481,8 +4688,8 @@ msgid "" "For live locations, a direction in which the user is moving, in degrees. " "Must be between 1 and 360 if specified." msgstr "" -"Для live местоположений, направление, в котором пользователь двигается, в градусах. " -"Должно быть между 1 и 360, если указано." +"Для live местоположений, направление, в котором пользователь двигается, в" +" градусах. Должно быть между 1 и 360, если указано." #: of telebot.TeleBot.send_location:37 msgid "" @@ -4491,8 +4698,8 @@ msgid "" "if specified." msgstr "" "Для live местоположений, максимальное расстояние для уведомлений о " -"приближении другого участника чата, в метрах. Должно быть между 1 и 100000, " -"если указано." +"приближении другого участника чата, в метрах. Должно быть между 1 и " +"100000, если указано." #: of telebot.TeleBot.send_media_group:1 msgid "" @@ -4501,9 +4708,10 @@ msgid "" " messages of the same type. On success, an array of Messages that were " "sent is returned." msgstr "" -"Используйте этот метод, чтобы отправить группу фото, видео, файлов или аудио " -"как альбом. Файлы и аудио могут быть сгруппированы в альбом только с сообщениями " -"того же типа. В случае успеха возвращается массив отправленных сообщений (Message)." +"Используйте этот метод, чтобы отправить группу фото, видео, файлов или " +"аудио как альбом. Файлы и аудио могут быть сгруппированы в альбом только " +"с сообщениями того же типа. В случае успеха возвращается массив " +"отправленных сообщений (Message)." #: of telebot.TeleBot.send_media_group:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup" @@ -4514,16 +4722,16 @@ msgid "" "A JSON-serialized array describing messages to be sent, must include 2-10" " items" msgstr "" -"JSON-сериализованный массив, описывающий сообщения для отправки, должен включать " -"от 2 до 10 элементов" +"JSON-сериализованный массив, описывающий сообщения для отправки, должен " +"включать от 2 до 10 элементов" #: of telebot.TeleBot.send_media_group:12 msgid "" "Sends the messages silently. Users will receive a notification with no " "sound." msgstr "" -"Отправить сообщение, при получении которого пользователя пользователи получат " -"уведомление без звука." +"Отправить сообщение, при получении которого пользователя пользователи " +"получат уведомление без звука." #: of telebot.TeleBot.send_media_group:27 msgid "Identifier of a message thread, in which the media group will be sent" @@ -4544,8 +4752,9 @@ msgid "" "characters, use the `split_string` or `smart_split` function in util.py." msgstr "" "Предупреждение: Не отправляйте больше 4096 символов в одном сообщении, " -"иначе вы рискуете получить ошибку HTTP 414. Если вам нужно отправить больше " -"4096 символов, используйте функцию `split_string` или `smart_split` из util.py." +"иначе вы рискуете получить ошибку HTTP 414. Если вам нужно отправить " +"больше 4096 символов, используйте функцию `split_string` или " +"`smart_split` из util.py." #: of telebot.TeleBot.send_message:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendmessage" @@ -4560,22 +4769,22 @@ msgid "" "List of special entities that appear in message text, which can be " "specified instead of parse_mode" msgstr "" -"Список отформатированных частей в тексте сообщения, " -"можно использовать вместо parse_mode" +"Список отформатированных частей в тексте сообщения, можно использовать " +"вместо parse_mode" #: of telebot.TeleBot.send_message:27 msgid "" "If True, the message content will be hidden for all users except for the " "target user" msgstr "" -"Если True, содержимое сообщения будет скрыто от всех пользователей, кроме " -"заданного" +"Если True, содержимое сообщения будет скрыто от всех пользователей, кроме" +" заданного" #: of telebot.TeleBot.send_photo:1 msgid "Use this method to send photos. On success, the sent Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить фото. В случае успеха возвращается " -"отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить фото. В случае успеха " +"возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_photo:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendphoto" @@ -4591,25 +4800,25 @@ msgid "" "must be at most 20." msgstr "" "Фото для отправки. Передайте file_id (String), чтобы отправить фото, " -"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить фото из интернета или загрузите новое с помощью " -"multipart/form-data. Фото должно весить не более 10 MB. Ширина и высота фото не " -"должны суммарно превышать 10000. Отношение ширины и высоты должно быть не более 20." +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить фото из интернета или загрузите новое с " +"помощью multipart/form-data. Фото должно весить не более 10 MB. Ширина и " +"высота фото не должны суммарно превышать 10000. Отношение ширины и высоты" +" должно быть не более 20." #: of telebot.TeleBot.send_photo:13 msgid "" "Photo caption (may also be used when resending photos by file_id), 0-1024" " characters after entities parsing" msgstr "" -"Подпись к фото (может быть использована при повторной отправке файла по file_id), " -"0-1024 символа после форматирования" +"Подпись к фото (может быть использована при повторной отправке файла по " +"file_id), 0-1024 символа после форматирования" #: of telebot.TeleBot.send_photo:16 msgid "Mode for parsing entities in the photo caption." msgstr "Режим форматирования подписи к фото." #: of telebot.TeleBot.send_photo:45 - msgid "Pass True, if the photo should be sent as a spoiler" msgstr "Передайте True, если фото должно быть отправлено как спойлер" @@ -4618,8 +4827,8 @@ msgid "" "Use this method to send a native poll. On success, the sent Message is " "returned." msgstr "" -"Используйте этот метод, чтобы отправить опрос. В случае успеха возвращается " -"отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить опрос. В случае успеха " +"возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_poll:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendpoll" @@ -4648,16 +4857,16 @@ msgid "" "True, if the poll allows multiple answers, ignored for polls in quiz " "mode, defaults to False" msgstr "" -"True, если опрос позволяет выбрать несколько вариантов ответа, игнорируется в " -"опросах вида “quiz”, по умолчанию False" +"True, если опрос позволяет выбрать несколько вариантов ответа, " +"игнорируется в опросах вида “quiz”, по умолчанию False" #: of telebot.TeleBot.send_poll:24 msgid "" "0-based identifier of the correct answer option. Available only for polls" " in quiz mode, defaults to None" msgstr "" -"Индекс правильного варианта ответа, начиная с 0. Доступно только для опросов " -"вида “quiz”, по умолчанию None" +"Индекс правильного варианта ответа, начиная с 0. Доступно только для " +"опросов вида “quiz”, по умолчанию None" #: of telebot.TeleBot.send_poll:28 msgid "" @@ -4682,8 +4891,8 @@ msgid "" "Amount of time in seconds the poll will be active after creation, 5-600. " "Can't be used together with close_date." msgstr "" -"Время в секундах, в течение которого опрос будет активен, 5-600. " -"Нельзя использовать вместо с close_date." +"Время в секундах, в течение которого опрос будет активен, 5-600. Нельзя " +"использовать вместо с close_date." #: of telebot.TeleBot.send_poll:38 msgid "Point in time (Unix timestamp) when the poll will be automatically closed." @@ -4694,12 +4903,14 @@ msgid "" "Pass True, if the poll needs to be immediately closed. This can be useful" " for poll preview." msgstr "" -"Передайте True, если опрос должен быть завершен немедленно. Может быть полезно " -"для предпросмотра опроса." +"Передайте True, если опрос должен быть завершен немедленно. Может быть " +"полезно для предпросмотра опроса." #: of telebot.TeleBot.send_poll:50 msgid "Pass True, if the poll allows multiple options to be voted simultaneously." -msgstr "Передайте True, если опрос позволяет выбрать несколько вариантов одновременно." +msgstr "" +"Передайте True, если опрос позволяет выбрать несколько вариантов " +"одновременно." #: of telebot.TeleBot.send_poll:57 msgid "Timeout in seconds for waiting for a response from the user." @@ -4710,8 +4921,8 @@ msgid "" "A JSON-serialized list of special entities that appear in the " "explanation, which can be specified instead of parse_mode" msgstr "" -"JSON-сериализованный список отформатированных частей explanation, " -"можно использовать вместо parse_mode" +"JSON-сериализованный список отформатированных частей explanation, можно " +"использовать вместо parse_mode" #: of telebot.TeleBot.send_poll:67 msgid "The identifier of a message thread, in which the poll will be sent" @@ -4722,8 +4933,8 @@ msgid "" "Use this method to send static .WEBP, animated .TGS, or video .WEBM " "stickers. On success, the sent Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить статичный .WEBP, анимированный .TGS, " -"или видео .WEBM стикер. В случае успеха возвращает отправленное " +"Используйте этот метод, чтобы отправить статичный .WEBP, анимированный " +".TGS, или видео .WEBM стикер. В случае успеха возвращает отправленное " "сообщение (Message)." #: of telebot.TeleBot.send_sticker:4 @@ -4738,21 +4949,25 @@ msgid "" " multipart/form-data." msgstr "" "Стикер для отправки. Передайте file_id (String), чтобы отправить файл, " -"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить .webp файл из интернета или загрузите новый с помощью " -"multipart/form-data." +"который уже загружен на сервера Telegram (рекомендуется), передайте HTTP " +"URL (String), чтобы отправить .webp файл из интернета или загрузите новый" +" с помощью multipart/form-data." #: of telebot.TeleBot.send_sticker:21 msgid "to disable the notification" msgstr "отключить уведомление" +#: of telebot.TeleBot.send_sticker:39 +msgid "Emoji associated with the sticker; only for just uploaded stickers" +msgstr "" + #: of telebot.TeleBot.send_venue:1 msgid "" "Use this method to send information about a venue. On success, the sent " "Message is returned." msgstr "" -"Используйте этот метод, чтобы отправить информацию о месте. В случае успеха " -"возвращается отправленное сообщение (Message)." +"Используйте этот метод, чтобы отправить информацию о месте. В случае " +"успеха возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_venue:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvenue" @@ -4801,8 +5016,9 @@ msgid "" "Use this method to send video files, Telegram clients support mp4 videos " "(other formats may be sent as Document)." msgstr "" -"Используйте этот метод, чтобы отправить видео, клиенты (приложения) Telegram " -"поддерживают mp4 видео (другие форматы могут быть отправлены как Document)." +"Используйте этот метод, чтобы отправить видео, клиенты (приложения) " +"Telegram поддерживают mp4 видео (другие форматы могут быть отправлены как" +" Document)." #: of telebot.TeleBot.send_video:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideo" @@ -4835,8 +5051,8 @@ msgid "" "Video caption (may also be used when resending videos by file_id), 0-1024" " characters after entities parsing" msgstr "" -"Подпись к видео (может быть использована при повторной отправке файла по file_id), " -"0-1024 символа после форматирования" +"Подпись к видео (может быть использована при повторной отправке файла по " +"file_id), 0-1024 символа после форматирования" #: of telebot.TeleBot.send_video:26 msgid "Mode for parsing entities in the video caption" @@ -4847,7 +5063,6 @@ msgid "Pass True, if the uploaded video is suitable for streaming" msgstr "Передайте True, если загруженное видео подходит для стриминга" #: of telebot.TeleBot.send_video:61 - msgid "Pass True, if the video should be sent as a spoiler" msgstr "Передайте True, если видео должно быть отправлено как спойлер" @@ -4857,15 +5072,31 @@ msgid "" "to 1 minute long. Use this method to send video messages. On success, the" " sent Message is returned." msgstr "" -"Начиная с версии v.4.0, клиенты(приложения) Telegram поддерживают скругленные " -"квадратные MPEG4 видео длительностью до минуты. Используйте этот метод, чтобы " -"отправить видео заметку (кружочек). В случае успеха возвращается отправленное " -"сообщение (Message)." +"Начиная с версии v.4.0, клиенты(приложения) Telegram поддерживают " +"скругленные квадратные MPEG4 видео длительностью до минуты. Используйте " +"этот метод, чтобы отправить видео заметку (кружочек). В случае успеха " +"возвращается отправленное сообщение (Message)." #: of telebot.TeleBot.send_video_note:4 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvideonote" +#: of telebot.TeleBot.send_video_note +msgid "param chat_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type chat_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note:7 +msgid ":obj:`int` or :obj:`str`" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param data" +msgstr "" + #: of telebot.TeleBot.send_video_note:9 msgid "" "Video note to send. Pass a file_id as String to send a video note that " @@ -4873,19 +5104,125 @@ msgid "" "multipart/form-data. Sending video notes by a URL is currently " "unsupported" msgstr "" -"Видео заметка для отправки. Передайте file_id (String), чтобы отправить видео " -"заметку, которая уже загружена на сервера Telegram или загрузите новую с помощью " -"multipart/form-data. На текущий момент, отправка видео заметок по URL не " -"поддерживается" +"Видео заметка для отправки. Передайте file_id (String), чтобы отправить " +"видео заметку, которая уже загружена на сервера Telegram или загрузите " +"новую с помощью multipart/form-data. На текущий момент, отправка видео " +"заметок по URL не поддерживается" + +#: of telebot.TeleBot.send_video_note +#, fuzzy +msgid "type data" +msgstr "тип" + +#: of telebot.TeleBot.send_video_note:11 telebot.TeleBot.send_video_note:37 +#, fuzzy +msgid ":obj:`str` or :class:`telebot.types.InputFile`" +msgstr "Список объектов :class:`telebot.types.Update`." + +#: of telebot.TeleBot.send_video_note +msgid "param duration" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type duration" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param length" +msgstr "" #: of telebot.TeleBot.send_video_note:16 msgid "Video width and height, i.e. diameter of the video message" msgstr "Ширина и высота видео (диаметр видео сообщения)" +#: of telebot.TeleBot.send_video_note +msgid "type length" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param reply_to_message_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type reply_to_message_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param reply_markup" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type reply_markup" +msgstr "" + +#: of telebot.TeleBot.send_video_note:24 +msgid "" +":class:`telebot.types.InlineKeyboardMarkup` or " +":class:`telebot.types.ReplyKeyboardMarkup` or " +":class:`telebot.types.ReplyKeyboardRemove` or " +":class:`telebot.types.ForceReply`" +msgstr "" + +#: of telebot.TeleBot.send_video_note +#, fuzzy +msgid "param disable_notification" +msgstr "отключить уведомление" + +#: of telebot.TeleBot.send_video_note +#, fuzzy +msgid "type disable_notification" +msgstr "отключить уведомление" + +#: of telebot.TeleBot.send_video_note +msgid "param timeout" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type timeout" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param thumbnail" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type thumbnail" +msgstr "" + +#: of telebot.TeleBot.send_video_note +#, fuzzy +msgid "param allow_sending_without_reply" +msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" + +#: of telebot.TeleBot.send_video_note +#, fuzzy +msgid "type allow_sending_without_reply" +msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" + +#: of telebot.TeleBot.send_video_note +msgid "param protect_content" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "type protect_content" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param message_thread_id" +msgstr "" + #: of telebot.TeleBot.send_video_note:45 msgid "Identifier of a message thread, in which the video note will be sent" msgstr "id топика, в который будет отправлена видео заметка" +#: of telebot.TeleBot.send_video_note +msgid "type message_thread_id" +msgstr "" + +#: of telebot.TeleBot.send_video_note +msgid "param thumb" +msgstr "" + #: of telebot.TeleBot.send_voice:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -4898,9 +5235,9 @@ msgstr "" "Используйте этот метод, чтобы отправить голосовое сообщение. Ваше аудио " "должно быть в формате .OGG и закодировано с помощью OPUS (другие форматы " "можно отправить как Audio или Document). В случае успеха возвращается " -"отправленное сообщение (Message). На текущий момент, боты могут отправлять " -"голосовые сообщения весом до 50 MB, это ограничение может быть изменено в " -"будущем." +"отправленное сообщение (Message). На текущий момент, боты могут " +"отправлять голосовые сообщения весом до 50 MB, это ограничение может быть" +" изменено в будущем." #: of telebot.TeleBot.send_voice:5 msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvoice" @@ -4914,9 +5251,9 @@ msgid "" "multipart/form-data." msgstr "" "Аудио для отправки. Передайте file_id (String), чтобы отправить аудио, " -"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP URL " -"(String), чтобы отправить аудио из интернета или загрузите новое с помощью " -"multipart/form-data." +"которое уже загружено на сервера Telegram (рекомендуется), передайте HTTP" +" URL (String), чтобы отправить аудио из интернета или загрузите новое с " +"помощью multipart/form-data." #: of telebot.TeleBot.send_voice:14 msgid "Voice message caption, 0-1024 characters after entities parsing" @@ -4931,8 +5268,8 @@ msgid "" "Mode for parsing entities in the voice message caption. See formatting " "options for more details." msgstr "" -"Режим форматирования подписи к голосовому сообщению. См. formatting options " -"для получения подробностей." +"Режим форматирования подписи к голосовому сообщению. См. formatting " +"options для получения подробностей." #: of telebot.TeleBot.set_chat_administrator_custom_title:1 msgid "" @@ -4954,9 +5291,7 @@ msgstr "" msgid "" "New custom title for the administrator; 0-16 characters, emoji are not " "allowed" -msgstr "" -"Новое кастомное звание администратора; 0-16 символов, эмодзи не " -"разрешены" +msgstr "Новое кастомное звание администратора; 0-16 символов, эмодзи не разрешены" #: of telebot.TeleBot.set_chat_description:1 msgid "" @@ -5001,14 +5336,16 @@ msgid "" "Unique identifier for the target private chat. If not specified, default " "bot's menu button will be changed." msgstr "" -"Уникальный id приватного чата. Если не указан, будет изменена кнопка меню " -"по умолчанию." +"Уникальный id приватного чата. Если не указан, будет изменена кнопка меню" +" по умолчанию." #: of telebot.TeleBot.set_chat_menu_button:11 msgid "" "A JSON-serialized object for the new bot's menu button. Defaults to " "MenuButtonDefault" -msgstr "JSON-сериализованный объект новой кнопки меню. По умолчанию MenuButtonDefault" +msgstr "" +"JSON-сериализованный объект новой кнопки меню. По умолчанию " +"MenuButtonDefault" #: of telebot.TeleBot.set_chat_permissions:1 msgid "" @@ -5016,9 +5353,9 @@ msgid "" "must be an administrator in the group or a supergroup for this to work " "and must have the can_restrict_members admin rights." msgstr "" -"Используйте этот метод, чтобы задать права по умолчанию для всех участников чата. " -"Бот должен быть администратором группы или супергруппы и иметь права администратора " -"can_restrict_members." +"Используйте этот метод, чтобы задать права по умолчанию для всех " +"участников чата. Бот должен быть администратором группы или супергруппы и" +" иметь права администратора can_restrict_members." #: of telebot.TeleBot.set_chat_permissions:5 msgid "" @@ -5041,11 +5378,11 @@ msgid "" "only work if the ‘All Members Are Admins’ setting is off in the target " "group." msgstr "" -"Используйте этот метод, чтобы задать новую аватарку чата. В приватных чатах " -"аватарки менять нельзя. Бот должен быть администратором чата и иметь " -"соответствующие права администратора. Возвращает True в случае успеха. " -"Примечание: В обычных группах (не супергруппы), этот метод будет работать " -"только если настройка ‘All Members Are Admins’ отключена." +"Используйте этот метод, чтобы задать новую аватарку чата. В приватных " +"чатах аватарки менять нельзя. Бот должен быть администратором чата и " +"иметь соответствующие права администратора. Возвращает True в случае " +"успеха. Примечание: В обычных группах (не супергруппы), этот метод будет " +"работать только если настройка ‘All Members Are Admins’ отключена." #: of telebot.TeleBot.set_chat_photo:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchatphoto" @@ -5063,10 +5400,11 @@ msgid "" "optionally returned in getChat requests to check if the bot can use this " "method. Returns True on success." msgstr "" -"Используйте этот метод, чтобы задать стикерпак супергруппы. Бот должен быть " -"администратором чата и иметь соответствующие права администратора. " -"Используйте атрибут can_set_sticker_set, возвращаемые методом getChat, чтобы " -"проверить, что бот может использовать этот метод. Возвращает True в случае успеха." +"Используйте этот метод, чтобы задать стикерпак супергруппы. Бот должен " +"быть администратором чата и иметь соответствующие права администратора. " +"Используйте атрибут can_set_sticker_set, возвращаемые методом getChat, " +"чтобы проверить, что бот может использовать этот метод. Возвращает True в" +" случае успеха." #: of telebot.TeleBot.set_chat_sticker_set:5 msgid "" @@ -5095,8 +5433,8 @@ msgstr "" "Используйте этот метод, чтобы изменить название чата. В приватных чатах " "изменить название нельзя. Бот должен быть администратором чата и иметь " "соответствующие права админа. Возвращает True в случае успеха. " -"Примечание: В обычных группах (не супергруппы), этот метод будет работать " -"только если настройка ‘All Members Are Admins’ отключена." +"Примечание: В обычных группах (не супергруппы), этот метод будет работать" +" только если настройка ‘All Members Are Admins’ отключена." #: of telebot.TeleBot.set_chat_title:7 msgid "Telegram documentation: https://core.telegram.org/bots/api#setchattitle" @@ -5106,6 +5444,22 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#se msgid "New chat title, 1-255 characters" msgstr "Новое название чата, 1-255 символов" +#: of telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:1 +#, fuzzy +msgid "" +"Use this method to set the thumbnail of a custom emoji sticker set. " +"Returns True on success." +msgstr "" +"Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " +"обложки могут быть заданы только для анимированных стикерпаков. " +"Возвращает True в случае успеха." + +#: of telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:7 +msgid "" +"Custom emoji identifier of a sticker from the sticker set; pass an empty " +"string to drop the thumbnail and use the first sticker as the thumbnail." +msgstr "" + #: of telebot.TeleBot.set_game_score:1 msgid "Sets the value of points in the game to a specific user." msgstr "Задаёт количество очков пользователя в игре." @@ -5123,8 +5477,8 @@ msgid "" "Pass True, if the high score is allowed to decrease. This can be useful " "when fixing mistakes or banning cheaters" msgstr "" -"Передайте True, если количество очков могут быть уменьшено. Может быть полезно " -"при исправлении ошибок или бане читеров" +"Передайте True, если количество очков могут быть уменьшено. Может быть " +"полезно при исправлении ошибок или бане читеров" #: of telebot.TeleBot.set_game_score:23 msgid "" @@ -5139,8 +5493,8 @@ msgid "" "On success, if the message was sent by the bot, returns the edited " "Message, otherwise returns True." msgstr "" -"В случае успеха, если сообщение было отправлено ботом, возвращает измененное " -"сообщение (Message), иначе возвращает True." +"В случае успеха, если сообщение было отправлено ботом, возвращает " +"измененное сообщение (Message), иначе возвращает True." #: of telebot.TeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." @@ -5161,10 +5515,11 @@ msgid "" "rights will be suggested to users, but they are are free to modify the " "list before adding the bot. Returns True on success." msgstr "" -"Используйте этот метод, чтобы изменить права администратора по умолчанию, " -"запрашиваемые при добавлении бота в группу или канал в качестве администратора. " -"Эти права будут предложены пользователям, но пользователи могут изменить список " -"перед добавлением бота. Возвращает True в случае успеха." +"Используйте этот метод, чтобы изменить права администратора по умолчанию," +" запрашиваемые при добавлении бота в группу или канал в качестве " +"администратора. Эти права будут предложены пользователям, но пользователи" +" могут изменить список перед добавлением бота. Возвращает True в случае " +"успеха." #: of telebot.TeleBot.set_my_default_administrator_rights:7 msgid "" @@ -5179,8 +5534,9 @@ msgid "" "A JSON-serialized object describing new default administrator rights. If " "not specified, the default administrator rights will be cleared." msgstr "" -"JSON-сериалиованный объект, описывающий новые права администратора по умолчанию. " -"Если не указан, права администратора по умолчанию будут сброшены." +"JSON-сериалиованный объект, описывающий новые права администратора по " +"умолчанию. Если не указан, права администратора по умолчанию будут " +"сброшены." #: of telebot.TeleBot.set_my_default_administrator_rights:13 msgid "" @@ -5188,8 +5544,105 @@ msgid "" "channels. Otherwise, the default administrator rights of the bot for " "groups and supergroups will be changed." msgstr "" -"Передайте True, чтобы изменить права администратора по умолчанию в каналах. " -"Иначе, будут изменены права администратора по умолчанию для групп и супергрупп." +"Передайте True, чтобы изменить права администратора по умолчанию в " +"каналах. Иначе, будут изменены права администратора по умолчанию для " +"групп и супергрупп." + +#: of telebot.TeleBot.set_my_description:1 +#, fuzzy +msgid "" +"Use this method to change the bot's description, which is shown in the " +"chat with the bot if the chat is empty. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы изменить кнопку меню в приватном чате или " +"кнопку меню по умолчанию. Возвращает True в случае успеха." + +#: of telebot.TeleBot.set_my_description:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmydescription" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatdescription" + +#: of telebot.TeleBot.set_my_description:7 +msgid "" +"New bot description; 0-512 characters. Pass an empty string to remove the" +" dedicated description for the given language." +msgstr "" + +#: of telebot.TeleBot.set_my_description:10 +#, fuzzy +msgid "" +"A two-letter ISO 639-1 language code. If empty, the description will be " +"applied to all users for whose language there is no dedicated " +"description." +msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" + +#: of telebot.TeleBot.set_my_name:1 +#, fuzzy +msgid "Use this method to change the bot's name. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы изменить кнопку меню в приватном чате или " +"кнопку меню по умолчанию. Возвращает True в случае успеха." + +#: of telebot.TeleBot.set_my_name:3 +#, fuzzy +msgid "Telegram documentation: https://core.telegram.org/bots/api#setmyname" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendgame" + +#: of telebot.TeleBot.set_my_name:5 +msgid "" +"Optional. New bot name; 0-64 characters. Pass an empty string to remove " +"the dedicated name for the given language." +msgstr "" + +#: of telebot.TeleBot.set_my_name:8 +#, fuzzy +msgid "" +"Optional. A two-letter ISO 639-1 language code. If empty, the name will " +"be shown to all users for whose language there is no dedicated name." +msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" + +#: of telebot.TeleBot.set_my_short_description:1 +msgid "" +"Use this method to change the bot's short description, which is shown on " +"the bot's profile page and is sent together with the link when users " +"share the bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_my_short_description:5 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmyshortdescription" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#setchatdescription" + +#: of telebot.TeleBot.set_my_short_description:7 +msgid "" +"New short description for the bot; 0-120 characters. Pass an empty string" +" to remove the dedicated short description for the given language." +msgstr "" + +#: of telebot.TeleBot.set_my_short_description:10 +#, fuzzy +msgid "" +"A two-letter ISO 639-1 language code. If empty, the short description " +"will be applied to all users for whose language there is no dedicated " +"short description." +msgstr "" +"Двухбуквенный языковой код в формате ISO 639-1. Если не задан, изменения " +"коснутся команд для всех пользователей в заданном поле видимости, не " +"имеющих команд на их языке" #: of telebot.TeleBot.set_state:1 msgid "Sets a new state of a user." @@ -5203,21 +5656,78 @@ msgid "" "chat with a bot." msgstr "" "Вы должны указать и user id и chat id, чтобы задать состояние (стейт) " -"пользователя в чате. Иначе, если вы укажете только user_id, chat_id будет " -"равен user_id, что означает смену состояния (стейта) пользователя в его " +"пользователя в чате. Иначе, если вы укажете только user_id, chat_id будет" +" равен user_id, что означает смену состояния (стейта) пользователя в его " "приватном чате с ботом." #: of telebot.TeleBot.set_state:12 msgid "new state. can be string, integer, or :class:`telebot.types.State`" -msgstr "новое состояние (стейт). может быть строкой, числом или :class:`telebot.types.State`" +msgstr "" +"новое состояние (стейт). может быть строкой, числом или " +":class:`telebot.types.State`" + +#: of telebot.TeleBot.set_sticker_emoji_list:1 +#, fuzzy +msgid "" +"Use this method to set the emoji list of a custom emoji sticker set. " +"Returns True on success." +msgstr "" +"Используйте этот метод, чтобы получить текущий список команд бота. " +"Возвращает список объектов BotCommand в случае успеха." + +#: of telebot.TeleBot.set_sticker_emoji_list:4 +#, fuzzy +msgid "Sticker identifier" +msgstr "id пользователя" + +#: of telebot.TeleBot.set_sticker_emoji_list:7 +#, fuzzy +msgid "List of emoji" +msgstr "список команд" + +#: of telebot.TeleBot.set_sticker_keywords:1 +msgid "" +"Use this method to change search keywords assigned to a regular or custom" +" emoji sticker. The sticker must belong to a sticker set created by the " +"bot. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_sticker_keywords:5 +#: telebot.TeleBot.set_sticker_mask_position:5 +#, fuzzy +msgid "File identifier of the sticker." +msgstr "id файла стикера" + +#: of telebot.TeleBot.set_sticker_keywords:8 +msgid "" +"A JSON-serialized list of 0-20 search keywords for the sticker with total" +" length of up to 64 characters" +msgstr "" + +#: of telebot.TeleBot.set_sticker_mask_position:1 +#, fuzzy +msgid "" +"Use this method to change the mask position of a mask sticker. The " +"sticker must belong to a sticker set that was created by the bot. Returns" +" True on success." +msgstr "" +"Используйте этот метод, чтобы удалить стикер из стикерпака, созданного " +"ботом. Возвращает True в случае успеха." + +#: of telebot.TeleBot.set_sticker_mask_position:8 +#, fuzzy +msgid "" +"A JSON-serialized object for position where the mask should be placed on " +"faces." +msgstr "Позиция для размещения маски на лицах в формате JSON" #: of telebot.TeleBot.set_sticker_position_in_set:1 msgid "" "Use this method to move a sticker in a set created by the bot to a " "specific position . Returns True on success." msgstr "" -"Используйте этот метод, чтобы передвинуть стикер в стикерпаке, созданном ботом, на заданную " -"позицию. Возвращает True в случае успеха." +"Используйте этот метод, чтобы передвинуть стикер в стикерпаке, созданном " +"ботом, на заданную позицию. Возвращает True в случае успеха." #: of telebot.TeleBot.set_sticker_position_in_set:3 msgid "" @@ -5231,17 +5741,17 @@ msgstr "" msgid "New sticker position in the set, zero-based" msgstr "Новая позиция стикера в стикерпаке, начиная с нуля" -#: of telebot.TeleBot.set_sticker_set_thumb:1 +#: of telebot.TeleBot.set_sticker_set_thumbnail:1 msgid "" "Use this method to set the thumbnail of a sticker set. Animated " "thumbnails can be set for animated sticker sets only. Returns True on " "success." msgstr "" "Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " -"обложки могут быть заданы только для анимированных стикерпаков. Возвращает " -"True в случае успеха." +"обложки могут быть заданы только для анимированных стикерпаков. " +"Возвращает True в случае успеха." -#: of telebot.TeleBot.set_sticker_set_thumb:4 +#: of telebot.TeleBot.set_sticker_set_thumbnail:4 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#setstickersetthumb" @@ -5249,9 +5759,44 @@ msgstr "" "Документация Telegram: " "https://core.telegram.org/bots/api#setstickersetthumb" +#: of telebot.TeleBot.set_sticker_set_thumbnail:12 +msgid "" +"A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in " +"size and have a width and height of exactly 100px, or a .TGS animation " +"with a thumbnail up to 32 kilobytes in size (see " +"https://core.telegram.org/stickers#animated-sticker-requirements for " +"animated sticker technical requirements), or a WEBM video with the " +"thumbnail up to 32 kilobytes in size; see " +"https://core.telegram.org/stickers#video-sticker-requirements for video " +"sticker technical requirements. Pass a file_id as a String to send a file" +" that already exists on the Telegram servers, 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. More information on Sending Files ». Animated " +"and video sticker set thumbnails can't be uploaded via HTTP URL. If " +"omitted, then the thumbnail is dropped and the first sticker is used as " +"the thumbnail." +msgstr "" + +#: of telebot.TeleBot.set_sticker_set_title:1 +#, fuzzy +msgid "" +"Use this method to set the title of a created sticker set. Returns True " +"on success." +msgstr "" +"Используйте этот метод, чтобы задать обложку стикерпака. Анимированные " +"обложки могут быть заданы только для анимированных стикерпаков. " +"Возвращает True в случае успеха." + +#: of telebot.TeleBot.set_sticker_set_title:7 +#, fuzzy +msgid "New sticker set title" +msgstr "Имя стикерпака" + #: of telebot.TeleBot.set_update_listener:1 msgid "Sets a listener function to be called when a new update is received." -msgstr "Задаёт функцию-листенер, которая будет вызвана при получении нового апдейта." +msgstr "" +"Задаёт функцию-листенер, которая будет вызвана при получении нового " +"апдейта." #: of telebot.TeleBot.set_update_listener:3 msgid "Listener function." @@ -5266,10 +5811,11 @@ msgid "" "reasonable amount of attempts. Returns True on success." msgstr "" "Используйте этот метод, чтобы задать URL и получать входящие апдейты с " -"помощью вебхука. Как только у бота появляется апдейт, он будет отправлен с " -"помощью HTTPS POST запроса на заданный URL, содержащего JSON-сериализованный " -"Update. В случае неудачного запроса, отправка апдейта будет отменена после " -"разумного числа попыток. Возвращает True в случае успеха." +"помощью вебхука. Как только у бота появляется апдейт, он будет отправлен " +"с помощью HTTPS POST запроса на заданный URL, содержащего " +"JSON-сериализованный Update. В случае неудачного запроса, отправка " +"апдейта будет отменена после разумного числа попыток. Возвращает True в " +"случае успеха." #: of telebot.TeleBot.set_webhook:6 msgid "" @@ -5279,8 +5825,8 @@ msgid "" "secret token as content." msgstr "" "Если вы хотите удостовериться, что вебхук был задан вами, вы можете " -"задать секретный токен в параметре secret_token. Если указан, запрос " -"с апдейтом будет содержать хедер “X-Telegram-Bot-Api-Secret-Token” с " +"задать секретный токен в параметре secret_token. Если указан, запрос с " +"апдейтом будет содержать хедер “X-Telegram-Bot-Api-Secret-Token” с " "секретным токеном в качестве значения." #: of telebot.TeleBot.set_webhook:9 @@ -5292,16 +5838,16 @@ msgid "" "HTTPS URL to send updates to. Use an empty string to remove webhook " "integration, defaults to None" msgstr "" -"HTTPS URL для отправки апдейтов. Используйте пустую строку, чтобы удалить " -"вебхук, по умолчанию None" +"HTTPS URL для отправки апдейтов. Используйте пустую строку, чтобы удалить" +" вебхук, по умолчанию None" #: of telebot.TeleBot.set_webhook:14 msgid "" "Upload your public key certificate so that the root certificate in use " "can be checked, defaults to None" msgstr "" -"Загрузите публичный ключ вашего SSL сертификата, чтобы корневой сертификат мог быть " -"проверен, по умолчанию None" +"Загрузите публичный ключ вашего SSL сертификата, чтобы корневой " +"сертификат мог быть проверен, по умолчанию None" #: of telebot.TeleBot.set_webhook:17 msgid "" @@ -5310,10 +5856,10 @@ msgid "" "limit the load on your bot's server, and higher values to increase your " "bot's throughput, defaults to None" msgstr "" -"Максимально-допустимое количество одновременных HTTPS соединений для доставки " -"апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения для уменьшения " -"нагрузки на ваш сервер и большие значения, чтобы увеличить пропускную способность " -"вашего бота, по умолчанию None" +"Максимально-допустимое количество одновременных HTTPS соединений для " +"доставки апдейтов, 1-100. По умолчанию 40. Используйте меньшие значения " +"для уменьшения нагрузки на ваш сервер и большие значения, чтобы увеличить" +" пропускную способность вашего бота, по умолчанию None" #: of telebot.TeleBot.set_webhook:22 msgid "" @@ -5330,10 +5876,11 @@ msgstr "" "Например, укажите [“message”, “edited_channel_post”, “callback_query”], " "чтобы получать апдейты только этих видов. Полный список доступных видов " "апдейтов - util.update_types. Укажите пустой список, чтобы получать все " -"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " -"последняя настройка. Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " -"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " -"получены в течение короткого периода времени. По умолчанию None" +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет " +"использована последняя настройка. Пожалуйста учтите, чтобы этот параметр " +"не влияет на апдейты, отправленные до вызова setWebhooks, поэтому " +"нежелательные апдейты могут быть получены в течение короткого периода " +"времени. По умолчанию None" #: of telebot.TeleBot.set_webhook:22 msgid "" @@ -5348,8 +5895,8 @@ msgstr "" "Например, укажите [“message”, “edited_channel_post”, “callback_query”], " "чтобы получать апдейты только этих видов. Полный список доступных видов " "апдейтов - util.update_types. Укажите пустой список, чтобы получать все " -"апдейты, кроме chat_member (по умолчанию). Если не задан, будет использована " -"последняя настройка." +"апдейты, кроме chat_member (по умолчанию). Если не задан, будет " +"использована последняя настройка." #: of telebot.TeleBot.set_webhook:27 msgid "" @@ -5357,9 +5904,9 @@ msgid "" " call to the setWebhook, so unwanted updates may be received for a short " "period of time. Defaults to None" msgstr "" -"Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, " -"отправленные до вызова setWebhooks, поэтому нежелательные апдейты могут быть " -"получены в течение короткого периода времени. По умолчанию None" +"Пожалуйста учтите, чтобы этот параметр не влияет на апдейты, отправленные" +" до вызова setWebhooks, поэтому нежелательные апдейты могут быть получены" +" в течение короткого периода времени. По умолчанию None" #: of telebot.TeleBot.set_webhook:42 msgid "" @@ -5368,10 +5915,10 @@ msgid "" "0-9, _ and - are allowed. The header is useful to ensure that the request" " comes from a webhook set by you. Defaults to None" msgstr "" -"Секретный токен для отправки в хедере “X-Telegram-Bot-Api-Secret-Token” " -"в каждом запросе с апдейтом, 1-256 символов. Разрешены только символы A-Z, a-z, " -"0-9, _ и -. Хедер полезен для, того чтобы удостовериться, что запрос приходит" -"с вебхука, установленного вами. По умолчанию None" +"Секретный токен для отправки в хедере “X-Telegram-Bot-Api-Secret-Token” в" +" каждом запросе с апдейтом, 1-256 символов. Разрешены только символы A-Z," +" a-z, 0-9, _ и -. Хедер полезен для, того чтобы удостовериться, что " +"запрос приходитс вебхука, установленного вами. По умолчанию None" #: of telebot.TeleBot.set_webhook:47 msgid ":obj:`bool` if the request was successful." @@ -5391,8 +5938,8 @@ msgid "" "price. As a parameter to the decorator function, it passes " ":class:`telebot.types.ShippingQuery` object." msgstr "" -"Обрабатывает shipping query. Только для инвойсов с гибкой ценой. " -"В качестве параметра передаёт в декорируемую функцию объект " +"Обрабатывает shipping query. Только для инвойсов с гибкой ценой. В " +"качестве параметра передаёт в декорируемую функцию объект " ":class:`telebot.types.ShippingQuery`." #: of telebot.TeleBot.stop_bot:1 @@ -5405,9 +5952,10 @@ msgid "" "live_period expires. On success, if the message is not an inline message," " the edited Message is returned, otherwise True is returned." msgstr "" -"Используйте этот метод, чтобы остановить обновление live местоположения до " -"истечения live_period. В случае успеха, если сообщение не является inline сообщением," -"возвращается измененное сообщение (Message), иначе возвращается True." +"Используйте этот метод, чтобы остановить обновление live местоположения " +"до истечения live_period. В случае успеха, если сообщение не является " +"inline сообщением,возвращается измененное сообщение (Message), иначе " +"возвращается True." #: of telebot.TeleBot.stop_message_live_location:4 msgid "" @@ -5422,32 +5970,32 @@ msgid "" "Required if inline_message_id is not specified. Identifier of the message" " with live location to stop" msgstr "" -"Обязательный, если не указан inline_message_id. id сообщения live местоположением, " -"которое нужно остановить" +"Обязательный, если не указан inline_message_id. id сообщения live " +"местоположением, которое нужно остановить" #: of telebot.TeleBot.stop_message_live_location:12 msgid "" "Required if chat_id and message_id are not specified. Identifier of the " "inline message with live location to stop" msgstr "" -"Обязательный, если не указаны chat_id и message_id. id inline сообщения с live " -"местоположением, которое нужно остановить" +"Обязательный, если не указаны chat_id и message_id. id inline сообщения с" +" live местоположением, которое нужно остановить" #: of telebot.TeleBot.stop_message_live_location:22 msgid "" "On success, if the message is not an inline message, the edited Message " "is returned, otherwise True is returned." msgstr "" -"В случае успеха, если сообщение не является inline сообщением, возвращается " -"измененное сообщение (Message), иначе возвращается True." +"В случае успеха, если сообщение не является inline сообщением, " +"возвращается измененное сообщение (Message), иначе возвращается True." #: of telebot.TeleBot.stop_poll:1 msgid "" "Use this method to stop a poll which was sent by the bot. On success, the" " stopped Poll is returned." msgstr "" -"Используйте этот метод, чтобы завершить опрос, отправленный ботом. В случае успеха " -"возвращается завершенный опрос (Poll)." +"Используйте этот метод, чтобы завершить опрос, отправленный ботом. В " +"случае успеха возвращается завершенный опрос (Poll)." #: of telebot.TeleBot.stop_poll:3 msgid "Telegram documentation: https://core.telegram.org/bots/api#stoppoll" @@ -5488,11 +6036,12 @@ msgid "" " If you don't want this, use the parameter only_if_banned." msgstr "" "Используйте этот метод, чтобы разбанить ранее кикнутого пользователя в " -"супергруппе или канале. Пользовать не вернется в группу или канал автоматически, " -"но сможет присоединиться с помощью ссылки и т.д. Бот должен быть администратором. " -"По умолчанию, этот метод гарантирует, что после вызова, пользователь не является " -"участником чата, но может присоединиться. Поэтому если пользовать является участником " -"чата, он будет кикнут, но не забанен. Если вы хотите изменить это поведение, " +"супергруппе или канале. Пользовать не вернется в группу или канал " +"автоматически, но сможет присоединиться с помощью ссылки и т.д. Бот " +"должен быть администратором. По умолчанию, этот метод гарантирует, что " +"после вызова, пользователь не является участником чата, но может " +"присоединиться. Поэтому если пользовать является участником чата, он " +"будет кикнут, но не забанен. Если вы хотите изменить это поведение, " "используйте параметр only_if_banned." #: of telebot.TeleBot.unban_chat_member:7 @@ -5503,7 +6052,9 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#un msgid "" "Unique identifier for the target group or username of the target " "supergroup or channel (in the format @username)" -msgstr "Уникальный id группы или username супергруппы или канала (в формате @username)" +msgstr "" +"Уникальный id группы или username супергруппы или канала (в формате " +"@username)" #: of telebot.TeleBot.unban_chat_member:16 msgid "Do nothing if the user is not banned" @@ -5515,9 +6066,9 @@ msgid "" " or channel. The bot must be an administrator for this to work and must " "have the appropriate administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы разбанить ране забаненный канал в супергруппе " -"или канала. Бот должен быть администратором и иметь соответствующие права " -"администратора. Возвращает True в случае успеха." +"Используйте этот метод, чтобы разбанить ране забаненный канал в " +"супергруппе или канала. Бот должен быть администратором и иметь " +"соответствующие права администратора. Возвращает True в случае успеха." #: of telebot.TeleBot.unban_chat_sender_chat:6 msgid "" @@ -5532,18 +6083,16 @@ msgid "Unique identifier of the target sender chat." msgstr "Уникальный id чата." #: of telebot.TeleBot.unhide_general_forum_topic:1 - msgid "" "Use this method to unhide the 'General' topic in a forum supergroup chat." " The bot must be an administrator in the chat for this to work and must " "have can_manage_topics administrator rights. Returns True on success." msgstr "" -"Используйте этот метод, чтобы сделать топик 'General' видимым в супергруппе " -"с топиками. Бот должен быть администратором чата и иметь права администратора " -"can_manage_topics. Возвращает True в случае успеха." +"Используйте этот метод, чтобы сделать топик 'General' видимым в " +"супергруппе с топиками. Бот должен быть администратором чата и иметь " +"права администратора can_manage_topics. Возвращает True в случае успеха." #: of telebot.TeleBot.unhide_general_forum_topic:5 - msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unhidegeneralforumtopic" @@ -5557,9 +6106,9 @@ msgid "" "bot must be an administrator in the chat for this to work and must have " "the appropriate admin rights. Returns True on success." msgstr "" -"Используйте этот метод, что открепить все закрепленные сообщения в супергруппе. " -"Бот должен быть администратором чата и иметь соответствующие права администратора. " -"Возвращает True в случае успеха." +"Используйте этот метод, что открепить все закрепленные сообщения в " +"супергруппе. Бот должен быть администратором чата и иметь соответствующие" +" права администратора. Возвращает True в случае успеха." #: of telebot.TeleBot.unpin_all_chat_messages:5 msgid "" @@ -5576,9 +6125,9 @@ msgid "" "have the can_pin_messages administrator right in the supergroup. Returns " "True on success." msgstr "" -"Используйте этот метод, что открепить все закрепленные сообщения в топике. " -"Бот должен быть администратором чата и иметь права администратора " -"can_pin_messages в супергруппе. Возвращает True в случае успеха." +"Используйте этот метод, что открепить все закрепленные сообщения в " +"топике. Бот должен быть администратором чата и иметь права администратора" +" can_pin_messages в супергруппе. Возвращает True в случае успеха." #: of telebot.TeleBot.unpin_all_forum_topic_messages:5 msgid "" @@ -5598,17 +6147,15 @@ msgid "" "The bot must be an administrator in the chat for this to work and must " "have the appropriate admin rights. Returns True on success." msgstr "" -"Используйте этот метод, что открепить закрепленное сообщение в супергруппе. " -"Бот должен быть администратором чата и иметь соответствующие права администратора. " -"Возвращает True в случае успеха." +"Используйте этот метод, что открепить закрепленное сообщение в " +"супергруппе. Бот должен быть администратором чата и иметь соответствующие" +" права администратора. Возвращает True в случае успеха." #: of telebot.TeleBot.unpin_chat_message:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#unpinchatmessage" -msgstr "" -"Документация Telegram: " -"https://core.telegram.org/bots/api#unpinchatmessage" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#unpinchatmessage" #: of telebot.TeleBot.unpin_chat_message:11 msgid "Int: Identifier of a message to unpin" @@ -5622,7 +6169,8 @@ msgid "" msgstr "" "Используйте этот метод, чтобы загрузить .png стикер, чтобы позже " "использовать в методах createNewStickerSet и addStickerToSet (может быть " -"использован несколько раз). Возвращает загруженный файл (File) в случае успеха." +"использован несколько раз). Возвращает загруженный файл (File) в случае " +"успеха." #: of telebot.TeleBot.upload_sticker_file:4 msgid "" @@ -5637,15 +6185,28 @@ msgid "User identifier of sticker set owner" msgstr "id пользователя, создавшего стикерпак" #: of telebot.TeleBot.upload_sticker_file:9 +#, fuzzy msgid "" -"PNG image with the sticker, must be up to 512 kilobytes in size, " -"dimensions must not exceed 512px, and either width or height must be " -"exactly 512px." +"DEPRECATED: PNG image with the sticker, must be up to 512 kilobytes in " +"size, dimensions must not exceed 512px, and either width or height must " +"be exactly 512px." msgstr "" "PNG изображение стикера, должно весить не более 512 kB, измерения не " -"должны превышать 512px и либо ширина, либо высота должна быть ровно 512px." +"должны превышать 512px и либо ширина, либо высота должна быть ровно " +"512px." #: of telebot.TeleBot.upload_sticker_file:13 +msgid "" +"A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. See " +"https://core.telegram.org/stickers for technical requirements. More " +"information on Sending Files »" +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:17 +msgid "One of \"static\", \"animated\", \"video\"." +msgstr "" + +#: of telebot.TeleBot.upload_sticker_file:20 msgid "On success, the sent file is returned." msgstr "В случае успеха возвращается отправленный файл." @@ -5681,9 +6242,10 @@ msgid "" " - filter failed. message: Message class text: Filter value given in " "handler" msgstr "" -"Базовый класс Advanced Custom Filter. Создайте класс наследник с методом check(). " -"Принимает два параметра, возвращает bool: True - фильтр пройден, False - фильтр " -"не пройден. message: класс Message text: значение фильтра, полученное в хендлере" +"Базовый класс Advanced Custom Filter. Создайте класс наследник с методом " +"check(). Принимает два параметра, возвращает bool: True - фильтр пройден," +" False - фильтр не пройден. message: класс Message text: значение " +"фильтра, полученное в хендлере" #: of telebot.custom_filters.AdvancedCustomFilter:7 #: telebot.custom_filters.SimpleCustomFilter:5 @@ -5756,8 +6318,9 @@ msgid "" "Accepts only message, returns bool value, that is compared with given in " "handler." msgstr "" -"Базовый класс Simple Custom Filter. Создайте класс наследник с методом check(). " -"Принимает только сообщение, возвращает bool, который сравнивается с заданным в хендлере." +"Базовый класс Simple Custom Filter. Создайте класс наследник с методом " +"check(). Принимает только сообщение, возвращает bool, который " +"сравнивается с заданным в хендлере." #: of telebot.custom_filters.SimpleCustomFilter:7 msgid "Example on creating a simple custom filter." @@ -5776,8 +6339,8 @@ msgid "" "Advanced text filter to check (types.Message, types.CallbackQuery, " "types.InlineQuery, types.Poll)" msgstr "" -"Advanced текстовый фильтр для проверки (types.Message, types.CallbackQuery, " -"types.InlineQuery, types.Poll)" +"Advanced текстовый фильтр для проверки (types.Message, " +"types.CallbackQuery, types.InlineQuery, types.Poll)" #: of telebot.custom_filters.TextFilter:3 msgid "example of usage is in examples/custom_filters/advanced_text_filter.py" @@ -5789,7 +6352,9 @@ msgstr "строка, True если текст объекта идентичен #: of telebot.custom_filters.TextFilter:8 msgid "list[str] or tuple[str], True if any string element of iterable is in text" -msgstr "list[str] или tuple[str], True если хотя бы один из элементов есть в тексте" +msgstr "" +"list[str] или tuple[str], True если хотя бы один из элементов есть в " +"тексте" #: of telebot.custom_filters.TextFilter:11 telebot.custom_filters.TextFilter:14 msgid "string, True if object's text starts with passed string" @@ -5820,8 +6385,8 @@ msgid "" "Base class for middleware. Your middlewares should be inherited from this" " class." msgstr "" -"Базовый класс для middleware. Ваши middleware должны быть унаследованы от " -"этого класса." +"Базовый класс для middleware. Ваши middleware должны быть унаследованы от" +" этого класса." #: of telebot.handler_backends.BaseMiddleware:4 msgid "" @@ -5832,16 +6397,16 @@ msgid "" msgstr "" "Задайте update_sensitive=True если хотите получать разные апдейты в " "разных функциях. Например, если вы хотите обрабатывать pre_process для " -"апдейтов вида message, вам нужно будет создать функцию pre_process_message " -"и т.д. Аналогично для post_process." +"апдейтов вида message, вам нужно будет создать функцию " +"pre_process_message и т.д. Аналогично для post_process." #: of telebot.handler_backends.BaseMiddleware:10 msgid "" "If you want to use middleware, you have to set use_class_middlewares=True" " in your TeleBot instance." msgstr "" -"Если вы хотите использовать middleware, вам нужно задать use_class_middlewares=True " -"в экземпляре класса TeleBot." +"Если вы хотите использовать middleware, вам нужно задать " +"use_class_middlewares=True в экземпляре класса TeleBot." #: of telebot.handler_backends.BaseMiddleware:13 msgid "Example of class-based middlewares." @@ -5854,16 +6419,16 @@ msgid "" "post_process in middlewares." msgstr "" "Класс для отмены апдейтов. Просто верните экземпляр этого класса в " -"middleware, чтобы пропустить апдейт. Апдейт пропустит хендлер и исполнение " -"post_process в middleware." +"middleware, чтобы пропустить апдейт. Апдейт пропустит хендлер и " +"исполнение post_process в middleware." #: of telebot.handler_backends.ContinueHandling:1 msgid "" "Class for continue updates in handlers. Just return instance of this " "class in handlers to continue process." msgstr "" -"Класс для продолжения обработки апдейта в хендлерах. Просто верните экземпляр " -"этого класса в хендлерах, чтобы продолжить обработку." +"Класс для продолжения обработки апдейта в хендлерах. Просто верните " +"экземпляр этого класса в хендлерах, чтобы продолжить обработку." #: of telebot.handler_backends.ContinueHandling:5 msgid "Example of using ContinueHandling" @@ -5896,16 +6461,16 @@ msgid "" "This file is used by TeleBot.run_webhooks() function. Fastapi is required" " to run this script." msgstr "" -"Этот файл используется функцией TeleBot.run_webhooks(). Для запуска этого " -"скрипта требуется Fastapi." +"Этот файл используется функцией TeleBot.run_webhooks(). Для запуска этого" +" скрипта требуется Fastapi." #: of telebot.ext.sync.webhooks.SyncWebhookListener.run_app:1 msgid "" "Run app with the given parameters to init. Not supposed to be used " "manually by user." msgstr "" -"Запустить приложение с заданными параметрами инициализации. Не предназначено " -"для использования пользователем." +"Запустить приложение с заданными параметрами инициализации. Не " +"предназначено для использования пользователем." #~ msgid "New name of the topic, 1-128 characters" #~ msgstr "Новое название топика, 1-128 символов" @@ -5923,3 +6488,17 @@ msgstr "" #~ msgid "The chat for which we want to handle new message." #~ msgstr "" +#~ msgid "" +#~ "Use this method to add a new " +#~ "sticker to a set created by the" +#~ " bot. It's required to pass " +#~ "`png_sticker` or `tgs_sticker`. Returns True" +#~ " on success." +#~ msgstr "" +#~ "Используйте этот метод, чтобы добавить " +#~ "новый стикер в стикерпак, созданный " +#~ "ботом. Необходимо передать либо `png_sticker`," +#~ " либо `tgs_sticker`, либо `webm_sticker`. " +#~ "Возвращает True в случае успешного " +#~ "добавления." + diff --git a/docs/source/locales/ru/LC_MESSAGES/types.po b/docs/source/locales/ru/LC_MESSAGES/types.po index db003a268..c846273fe 100644 --- a/docs/source/locales/ru/LC_MESSAGES/types.po +++ b/docs/source/locales/ru/LC_MESSAGES/types.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-02 19:24+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,9 +23,11 @@ msgid "Types of API" msgstr "" #: of telebot.types.Animation:1 telebot.types.Audio:1 -#: telebot.types.CallbackQuery:1 telebot.types.Chat:1 -#: telebot.types.ChatJoinRequest:1 telebot.types.ChatMember:1 -#: telebot.types.ChatMemberUpdated:1 telebot.types.ChatPhoto:1 +#: telebot.types.BotDescription:1 telebot.types.BotName:1 +#: telebot.types.BotShortDescription:1 telebot.types.CallbackQuery:1 +#: telebot.types.Chat:1 telebot.types.ChatJoinRequest:1 +#: telebot.types.ChatMember:1 telebot.types.ChatMemberUpdated:1 +#: telebot.types.ChatPhoto:1 telebot.types.ChatShared:1 #: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 #: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 #: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 @@ -41,8 +43,8 @@ msgstr "" #: telebot.types.ShippingQuery:1 telebot.types.Sticker:1 #: telebot.types.StickerSet:1 telebot.types.SuccessfulPayment:1 #: telebot.types.Update:1 telebot.types.UserProfilePhotos:1 -#: telebot.types.Venue:1 telebot.types.Video:1 telebot.types.VideoChatEnded:1 -#: telebot.types.VideoChatParticipantsInvited:1 +#: telebot.types.UserShared:1 telebot.types.Venue:1 telebot.types.Video:1 +#: telebot.types.VideoChatEnded:1 telebot.types.VideoChatParticipantsInvited:1 #: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 #: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 #: telebot.types.WriteAccessAllowed:1 @@ -66,14 +68,16 @@ msgstr "" #: telebot.types.BotCommandScopeChat #: telebot.types.BotCommandScopeChatAdministrators #: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault -#: telebot.types.CallbackQuery telebot.types.Chat -#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink -#: telebot.types.ChatJoinRequest telebot.types.ChatLocation -#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned -#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember -#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted -#: telebot.types.ChatMemberUpdated telebot.types.ChatPermissions -#: telebot.types.ChatPhoto telebot.types.ChosenInlineResult +#: telebot.types.BotDescription telebot.types.BotName +#: telebot.types.BotShortDescription telebot.types.CallbackQuery +#: telebot.types.Chat telebot.types.ChatAdministratorRights +#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest +#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator +#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft +#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner +#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChatShared telebot.types.ChosenInlineResult #: telebot.types.Contact telebot.types.Dice telebot.types.Document #: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic #: telebot.types.ForumTopicCreated telebot.types.ForumTopicEdited @@ -95,15 +99,17 @@ msgstr "" #: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation #: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto #: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo -#: telebot.types.InlineQueryResultVoice +#: telebot.types.InlineQueryResultVoice telebot.types.InlineQueryResultsButton #: telebot.types.InputContactMessageContent telebot.types.InputFile #: telebot.types.InputInvoiceMessageContent #: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation #: telebot.types.InputMediaAudio telebot.types.InputMediaDocument #: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo -#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent -#: telebot.types.Invoice telebot.types.KeyboardButton -#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.InputSticker telebot.types.InputTextMessageContent +#: telebot.types.InputVenueMessageContent telebot.types.Invoice +#: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType +#: telebot.types.KeyboardButtonRequestChat +#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice #: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message @@ -117,11 +123,13 @@ msgstr "" #: telebot.types.ShippingAddress telebot.types.ShippingOption #: telebot.types.ShippingOption.add_price telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet -#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User -#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat +#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos +#: telebot.types.UserShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo +#: telebot.types.WriteAccessAllowed msgid "Parameters" msgstr "" @@ -183,14 +191,16 @@ msgstr "" #: telebot.types.BotCommandScopeChat #: telebot.types.BotCommandScopeChatAdministrators #: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault -#: telebot.types.CallbackQuery telebot.types.Chat -#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink -#: telebot.types.ChatJoinRequest telebot.types.ChatLocation -#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned -#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember -#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted -#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference -#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.BotDescription telebot.types.BotName +#: telebot.types.BotShortDescription telebot.types.CallbackQuery +#: telebot.types.Chat telebot.types.ChatAdministratorRights +#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest +#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator +#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft +#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner +#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated +#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChatShared #: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice #: telebot.types.Document telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game @@ -211,15 +221,17 @@ msgstr "" #: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation #: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto #: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo -#: telebot.types.InlineQueryResultVoice +#: telebot.types.InlineQueryResultVoice telebot.types.InlineQueryResultsButton #: telebot.types.InputContactMessageContent #: telebot.types.InputInvoiceMessageContent #: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation #: telebot.types.InputMediaAudio telebot.types.InputMediaDocument #: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo -#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent -#: telebot.types.Invoice telebot.types.KeyboardButton -#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.InputSticker telebot.types.InputTextMessageContent +#: telebot.types.InputVenueMessageContent telebot.types.Invoice +#: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType +#: telebot.types.KeyboardButtonRequestChat +#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice #: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message @@ -232,8 +244,9 @@ msgstr "" #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingOption.add_price #: telebot.types.ShippingQuery telebot.types.Sticker telebot.types.StickerSet -#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User -#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat +#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos +#: telebot.types.UserShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -248,20 +261,22 @@ msgstr "" #: telebot.types.BotCommandScopeChat:12 #: telebot.types.BotCommandScopeChatAdministrators:12 #: telebot.types.BotCommandScopeChatMember:15 -#: telebot.types.BotCommandScopeDefault:8 telebot.types.CallbackQuery:30 -#: telebot.types.Chat:109 telebot.types.ChatAdministratorRights:46 -#: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:20 -#: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:59 -#: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 -#: telebot.types.ChatMemberMember:11 telebot.types.ChatMemberOwner:17 -#: telebot.types.ChatMemberRestricted:47 telebot.types.ChatMemberUpdated:24 -#: telebot.types.ChatPermissions:38 telebot.types.ChatPhoto:21 +#: telebot.types.BotCommandScopeDefault:8 telebot.types.BotDescription:8 +#: telebot.types.BotName:8 telebot.types.BotShortDescription:8 +#: telebot.types.CallbackQuery:30 telebot.types.Chat:109 +#: telebot.types.ChatAdministratorRights:46 telebot.types.ChatInviteLink:34 +#: telebot.types.ChatJoinRequest:27 telebot.types.ChatLocation:11 +#: telebot.types.ChatMemberAdministrator:59 telebot.types.ChatMemberBanned:15 +#: telebot.types.ChatMemberLeft:11 telebot.types.ChatMemberMember:11 +#: telebot.types.ChatMemberOwner:17 telebot.types.ChatMemberRestricted:61 +#: telebot.types.ChatMemberUpdated:27 telebot.types.ChatPermissions:56 +#: telebot.types.ChatPhoto:21 telebot.types.ChatShared:15 #: telebot.types.ChosenInlineResult:21 telebot.types.Contact:22 #: telebot.types.Dice:11 telebot.types.Document:26 telebot.types.File:21 #: telebot.types.ForceReply:18 telebot.types.ForumTopic:17 #: telebot.types.ForumTopicCreated:14 telebot.types.Game:25 -#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:45 -#: telebot.types.InlineKeyboardMarkup:22 telebot.types.InlineQuery:26 +#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:49 +#: telebot.types.InlineKeyboardMarkup:28 telebot.types.InlineQuery:26 #: telebot.types.InlineQueryResultArticle:38 #: telebot.types.InlineQueryResultAudio:40 #: telebot.types.InlineQueryResultCachedAudio:31 @@ -281,31 +296,35 @@ msgstr "" #: telebot.types.InlineQueryResultVenue:51 #: telebot.types.InlineQueryResultVideo:53 #: telebot.types.InlineQueryResultVoice:37 +#: telebot.types.InlineQueryResultsButton:22 #: telebot.types.InputContactMessageContent:17 #: telebot.types.InputInvoiceMessageContent:75 #: telebot.types.InputLocationMessageContent:26 #: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:37 #: telebot.types.InputMediaDocument:32 telebot.types.InputMediaPhoto:24 -#: telebot.types.InputMediaVideo:43 telebot.types.InputTextMessageContent:19 +#: telebot.types.InputMediaVideo:43 telebot.types.InputSticker:18 +#: telebot.types.InputTextMessageContent:19 #: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 -#: telebot.types.KeyboardButton:25 telebot.types.KeyboardButtonPollType:9 -#: telebot.types.LabeledPrice:13 telebot.types.Location:25 -#: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 -#: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 -#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:246 -#: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 -#: telebot.types.MessageID:8 telebot.types.OrderInfo:17 -#: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:15 -#: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 -#: telebot.types.ProximityAlertTriggered:14 +#: telebot.types.KeyboardButton:33 telebot.types.KeyboardButtonPollType:9 +#: telebot.types.KeyboardButtonRequestChat:35 +#: telebot.types.KeyboardButtonRequestUser:18 telebot.types.LabeledPrice:13 +#: telebot.types.Location:25 telebot.types.LoginUrl:24 +#: telebot.types.MaskPosition:20 telebot.types.MenuButtonCommands:8 +#: telebot.types.MenuButtonDefault:8 telebot.types.MenuButtonWebApp:15 +#: telebot.types.Message:252 telebot.types.MessageAutoDeleteTimerChanged:8 +#: telebot.types.MessageEntity:32 telebot.types.MessageID:8 +#: telebot.types.OrderInfo:17 telebot.types.PhotoSize:21 telebot.types.Poll:47 +#: telebot.types.PollAnswer:15 telebot.types.PollOption:11 +#: telebot.types.PreCheckoutQuery:28 telebot.types.ProximityAlertTriggered:14 #: telebot.types.ReplyKeyboardMarkup:49 telebot.types.ReplyKeyboardRemove:18 #: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 #: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 -#: telebot.types.Sticker:49 telebot.types.StickerSet:30 -#: telebot.types.SuccessfulPayment:28 telebot.types.Update:61 +#: telebot.types.Sticker:54 telebot.types.StickerSet:30 +#: telebot.types.SuccessfulPayment:28 +#: telebot.types.SwitchInlineQueryChosenChat:22 telebot.types.Update:61 #: telebot.types.User:41 telebot.types.UserProfilePhotos:11 -#: telebot.types.Venue:27 telebot.types.Video:35 telebot.types.VideoChatEnded:8 -#: telebot.types.VideoChatParticipantsInvited:8 +#: telebot.types.UserShared:15 telebot.types.Venue:27 telebot.types.Video:35 +#: telebot.types.VideoChatEnded:8 telebot.types.VideoChatParticipantsInvited:8 #: telebot.types.VideoChatScheduled:9 telebot.types.VideoNote:24 #: telebot.types.Voice:23 telebot.types.WebAppData:12 #: telebot.types.WebAppInfo:8 telebot.types.WebhookInfo:37 @@ -320,14 +339,16 @@ msgstr "" #: telebot.types.BotCommandScopeChat #: telebot.types.BotCommandScopeChatAdministrators #: telebot.types.BotCommandScopeChatMember telebot.types.BotCommandScopeDefault -#: telebot.types.CallbackQuery telebot.types.Chat -#: telebot.types.ChatAdministratorRights telebot.types.ChatInviteLink -#: telebot.types.ChatJoinRequest telebot.types.ChatLocation -#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned -#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember -#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted -#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference -#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.BotDescription telebot.types.BotName +#: telebot.types.BotShortDescription telebot.types.CallbackQuery +#: telebot.types.Chat telebot.types.ChatAdministratorRights +#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest +#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator +#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft +#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner +#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated +#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChatShared #: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice #: telebot.types.Document telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game @@ -348,15 +369,17 @@ msgstr "" #: telebot.types.InlineQueryResultGif telebot.types.InlineQueryResultLocation #: telebot.types.InlineQueryResultMpeg4Gif telebot.types.InlineQueryResultPhoto #: telebot.types.InlineQueryResultVenue telebot.types.InlineQueryResultVideo -#: telebot.types.InlineQueryResultVoice +#: telebot.types.InlineQueryResultVoice telebot.types.InlineQueryResultsButton #: telebot.types.InputContactMessageContent #: telebot.types.InputInvoiceMessageContent #: telebot.types.InputLocationMessageContent telebot.types.InputMediaAnimation #: telebot.types.InputMediaAudio telebot.types.InputMediaDocument #: telebot.types.InputMediaPhoto telebot.types.InputMediaVideo -#: telebot.types.InputTextMessageContent telebot.types.InputVenueMessageContent -#: telebot.types.Invoice telebot.types.KeyboardButton -#: telebot.types.KeyboardButtonPollType telebot.types.LabeledPrice +#: telebot.types.InputSticker telebot.types.InputTextMessageContent +#: telebot.types.InputVenueMessageContent telebot.types.Invoice +#: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType +#: telebot.types.KeyboardButtonRequestChat +#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice #: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message @@ -369,8 +392,9 @@ msgstr "" #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet -#: telebot.types.SuccessfulPayment telebot.types.Update telebot.types.User -#: telebot.types.UserProfilePhotos telebot.types.Venue telebot.types.Video +#: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat +#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos +#: telebot.types.UserShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -703,6 +727,56 @@ msgstr "" msgid ":class:`telebot.types.BotCommandScopeDefault`" msgstr "" +#: of telebot.types.BotDescription:1 +msgid "This object represents a bot description." +msgstr "" + +#: of telebot.types.BotDescription:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#botdescription" +msgstr "" + +#: of telebot.types.BotDescription:5 +msgid "Bot description" +msgstr "" + +#: of telebot.types.BotDescription:9 +msgid ":class:`telebot.types.BotDescription`" +msgstr "" + +#: of telebot.types.BotName:1 +msgid "This object represents a bot name." +msgstr "" + +#: of telebot.types.BotName:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#botname" +msgstr "" + +#: of telebot.types.BotName:5 +msgid "The bot name" +msgstr "" + +#: of telebot.types.BotName:9 +msgid ":class:`BotName`" +msgstr "" + +#: of telebot.types.BotShortDescription:1 +msgid "This object represents a bot short description." +msgstr "" + +#: of telebot.types.BotShortDescription:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#botshortdescription" +msgstr "" + +#: of telebot.types.BotShortDescription:5 +msgid "Bot short description" +msgstr "" + +#: of telebot.types.BotShortDescription:9 +msgid ":class:`telebot.types.BotShortDescription`" +msgstr "" + #: of telebot.types.CallbackQuery:1 msgid "" "This object represents an incoming callback query from a callback button " @@ -1120,20 +1194,31 @@ msgid "User that sent the join request" msgstr "" #: of telebot.types.ChatJoinRequest:11 +msgid "" +"Optional. Identifier of a private chat with the user who sent the join " +"request. This number may have more than 32 significant bits and some " +"programming languages may have difficulty/silent defects in interpreting " +"it. But it has at most 52 significant bits, so a 64-bit integer or " +"double-precision float type are safe for storing this identifier. The bot" +" can use this identifier for 24 hours to send messages until the join " +"request is processed, assuming no other administrator contacted the user." +msgstr "" + +#: of telebot.types.ChatJoinRequest:18 msgid "Date the request was sent in Unix time" msgstr "" -#: of telebot.types.ChatJoinRequest:14 +#: of telebot.types.ChatJoinRequest:21 msgid "Optional. Bio of the user." msgstr "" -#: of telebot.types.ChatJoinRequest:17 +#: of telebot.types.ChatJoinRequest:24 msgid "" "Optional. Chat invite link that was used by the user to send the join " "request" msgstr "" -#: of telebot.types.ChatJoinRequest:21 +#: of telebot.types.ChatJoinRequest:28 msgid ":class:`telebot.types.ChatJoinRequest`" msgstr "" @@ -1177,7 +1262,7 @@ msgstr "" msgid ":class:`telebot.types.ChatMemberMember`" msgstr "" -#: of telebot.types.ChatMember:7 telebot.types.ChatMemberRestricted:48 +#: of telebot.types.ChatMember:7 telebot.types.ChatMemberRestricted:62 msgid ":class:`telebot.types.ChatMemberRestricted`" msgstr "" @@ -1328,26 +1413,44 @@ msgid "" msgstr "" #: of telebot.types.ChatMemberRestricted:29 -msgid "" -"True, if the user is allowed to send audios, documents, photos, videos, " -"video notes and voice notes" +msgid "True, if the user is allowed to send audios" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:32 +msgid "True, if the user is allowed to send documents" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:35 +msgid "True, if the user is allowed to send photos" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:38 +msgid "True, if the user is allowed to send videos" msgstr "" -#: of telebot.types.ChatMemberRestricted:33 +#: of telebot.types.ChatMemberRestricted:41 +msgid "True, if the user is allowed to send video notes" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:44 +msgid "True, if the user is allowed to send voice notes" +msgstr "" + +#: of telebot.types.ChatMemberRestricted:47 msgid "True, if the user is allowed to send polls" msgstr "" -#: of telebot.types.ChatMemberRestricted:36 +#: of telebot.types.ChatMemberRestricted:50 msgid "" "True, if the user is allowed to send animations, games, stickers and use " "inline bots" msgstr "" -#: of telebot.types.ChatMemberRestricted:40 +#: of telebot.types.ChatMemberRestricted:54 msgid "True, if the user is allowed to add web page previews to their messages" msgstr "" -#: of telebot.types.ChatMemberRestricted:43 +#: of telebot.types.ChatMemberRestricted:57 msgid "" "Date when restrictions will be lifted for this user; unix time. If 0, " "then the user is restricted forever" @@ -1389,7 +1492,11 @@ msgid "" "for joining by invite link events only." msgstr "" -#: of telebot.types.ChatMemberUpdated:25 +#: of telebot.types.ChatMemberUpdated:24 +msgid "Optional. True, if the user joined the chat via a chat folder invite link" +msgstr "" + +#: of telebot.types.ChatMemberUpdated:28 msgid ":class:`telebot.types.ChatMemberUpdated`" msgstr "" @@ -1421,52 +1528,76 @@ msgid "" msgstr "" #: of telebot.types.ChatPermissions:9 -msgid "" -"Optional. True, if the user is allowed to send audios, documents, photos," -" videos, video notes and voice notes, implies can_send_messages" +msgid "Optional. True, if the user is allowed to send audios" msgstr "" -#: of telebot.types.ChatPermissions:13 +#: of telebot.types.ChatPermissions:12 +msgid "Optional. True, if the user is allowed to send documents" +msgstr "" + +#: of telebot.types.ChatPermissions:15 +msgid "Optional. True, if the user is allowed to send photos" +msgstr "" + +#: of telebot.types.ChatPermissions:18 +msgid "Optional. True, if the user is allowed to send videos" +msgstr "" + +#: of telebot.types.ChatPermissions:21 +msgid "Optional. True, if the user is allowed to send video notes" +msgstr "" + +#: of telebot.types.ChatPermissions:24 +msgid "Optional. True, if the user is allowed to send voice notes" +msgstr "" + +#: of telebot.types.ChatPermissions:27 msgid "" "Optional. True, if the user is allowed to send polls, implies " "can_send_messages" msgstr "" -#: of telebot.types.ChatPermissions:16 +#: of telebot.types.ChatPermissions:30 msgid "" "Optional. True, if the user is allowed to send animations, games, " -"stickers and use inline bots, implies can_send_media_messages" +"stickers and use inline bots" msgstr "" -#: of telebot.types.ChatPermissions:20 +#: of telebot.types.ChatPermissions:34 msgid "" "Optional. True, if the user is allowed to add web page previews to their " -"messages, implies can_send_media_messages" +"messages" msgstr "" -#: of telebot.types.ChatPermissions:24 +#: of telebot.types.ChatPermissions:38 msgid "" "Optional. True, if the user is allowed to change the chat title, photo " "and other settings. Ignored in public supergroups" msgstr "" -#: of telebot.types.ChatPermissions:28 +#: of telebot.types.ChatPermissions:42 msgid "Optional. True, if the user is allowed to invite new users to the chat" msgstr "" -#: of telebot.types.ChatPermissions:31 +#: of telebot.types.ChatPermissions:45 msgid "" "Optional. True, if the user is allowed to pin messages. Ignored in public" " supergroups" msgstr "" -#: of telebot.types.ChatPermissions:34 +#: of telebot.types.ChatPermissions:48 msgid "" "Optional. True, if the user is allowed to create forum topics. If omitted" " defaults to the value of can_pin_messages" msgstr "" -#: of telebot.types.ChatPermissions:39 +#: of telebot.types.ChatPermissions:52 +msgid "" +"deprecated. True, if the user is allowed to send audios, documents, " +"photos, videos, video notes and voice notes" +msgstr "" + +#: of telebot.types.ChatPermissions:57 msgid ":class:`telebot.types.ChatPermissions`" msgstr "" @@ -1508,6 +1639,36 @@ msgstr "" msgid ":class:`telebot.types.ChatPhoto`" msgstr "" +#: of telebot.types.ChatShared:1 +msgid "" +"This object contains information about the chat whose identifier was " +"shared with the bot using a `telebot.types.KeyboardButtonRequestChat` " +"button." +msgstr "" + +#: of telebot.types.ChatShared:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#Chatshared" +msgstr "" + +#: of telebot.types.ChatShared:6 telebot.types.UserShared:6 +msgid "identifier of the request" +msgstr "" + +#: of telebot.types.ChatShared:9 +msgid "" +"Identifier of the shared chat. This number may have more than 32 " +"significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier. The bot may not have access to the chat" +" and could be unable to use this identifier, unless the chat is already " +"known to the bot by some other means." +msgstr "" + +#: of telebot.types.ChatShared:16 +msgid ":class:`telebot.types.ChatShared`" +msgstr "" + #: of telebot.types.ChosenInlineResult:1 msgid "" "Represents a result of an inline query that was chosen by the user and " @@ -1915,12 +2076,14 @@ msgid "" msgstr "" #: of telebot.types.InlineKeyboardButton:3 +#: telebot.types.SwitchInlineQueryChosenChat:4 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#inlinekeyboardbutton" msgstr "" #: of telebot.types.InlineKeyboardButton:5 +#: telebot.types.InlineQueryResultsButton:6 msgid "Label text on the button" msgstr "" @@ -1974,19 +2137,26 @@ msgstr "" #: of telebot.types.InlineKeyboardButton:37 msgid "" +"Optional. If set, pressing the button will prompt the user to select one " +"of their chats of the specified type, open that chat and insert the bot's" +" username and the specified inline query in the input field" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:41 +msgid "" "Optional. Description of the game that will be launched when the user " "presses the button. NOTE: This type of button must always be the first " "button in the first row." msgstr "" -#: of telebot.types.InlineKeyboardButton:41 +#: of telebot.types.InlineKeyboardButton:45 msgid "" "Optional. Specify True, to send a Pay button. NOTE: This type of button " "must always be the first button in the first row and can only be used in " "invoice messages." msgstr "" -#: of telebot.types.InlineKeyboardButton:46 +#: of telebot.types.InlineKeyboardButton:50 msgid ":class:`telebot.types.InlineKeyboardButton`" msgstr "" @@ -2004,19 +2174,23 @@ msgstr "" msgid "Example of a custom keyboard with buttons." msgstr "" -#: of telebot.types.InlineKeyboardMarkup:16 +#: of telebot.types.InlineKeyboardMarkup:19 msgid "" "Telegram Documentation: " "https://core.telegram.org/bots/api#inlinekeyboardmarkup" msgstr "" -#: of telebot.types.InlineKeyboardMarkup:18 +#: of telebot.types.InlineKeyboardMarkup:21 msgid "" ":obj:`list` of button rows, each represented by an :obj:`list` of " ":class:`telebot.types.InlineKeyboardButton` objects" msgstr "" -#: of telebot.types.InlineKeyboardMarkup:23 +#: of telebot.types.InlineKeyboardMarkup:25 +msgid "number of :class:`telebot.types.InlineKeyboardButton` objects on each row" +msgstr "" + +#: of telebot.types.InlineKeyboardMarkup:29 #: telebot.types.InlineKeyboardMarkup.add:17 #: telebot.types.InlineKeyboardMarkup.row:12 msgid ":class:`telebot.types.InlineKeyboardMarkup`" @@ -3165,12 +3339,58 @@ msgstr "" msgid ":class:`telebot.types.InlineQueryResultVoice`" msgstr "" +#: of telebot.types.InlineQueryResultsButton:1 telebot.types.LabeledPrice:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.Dictionaryable`" +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:1 +msgid "" +"This object represents a button to be shown above inline query results. " +"You must use exactly one of the optional fields." +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#inlinequeryresultsbutton" +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:9 +msgid "" +"Optional. Description of the Web App that will be launched when the user " +"presses the button. The Web App will be able to switch back to the inline" +" mode using the method web_app_switch_inline_query inside the Web App." +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:13 +msgid "" +"Optional. Deep-linking parameter for the /start message sent to the bot " +"when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ " +"and - are allowed. Example: An inline bot that sends YouTube videos can " +"ask the user to connect the bot to their YouTube account to adapt search " +"results accordingly. To do this, it displays a 'Connect your YouTube " +"account' button above the results, or even before showing any. The user " +"presses the button, switches to a private chat with the bot and, in doing" +" so, passes a start parameter that instructs the bot to return an OAuth " +"link. Once done, the bot can offer a switch_inline button so that the " +"user can easily return to the chat where they wanted to use the bot's " +"inline capabilities." +msgstr "" + +#: of telebot.types.InlineQueryResultsButton:23 +msgid ":class:`InlineQueryResultsButton`" +msgstr "" + #: of telebot.types.InputContactMessageContent:1 #: telebot.types.InputInvoiceMessageContent:1 #: telebot.types.InputLocationMessageContent:1 #: telebot.types.InputTextMessageContent:1 #: telebot.types.InputVenueMessageContent:1 #: telebot.types.KeyboardButtonPollType:1 +#: telebot.types.KeyboardButtonRequestChat:1 +#: telebot.types.KeyboardButtonRequestUser:1 msgid "Bases: :py:class:`telebot.types.Dictionaryable`" msgstr "" @@ -3367,7 +3587,8 @@ msgstr "" msgid ":class:`telebot.types.InputLocationMessageContent`" msgstr "" -#: of telebot.types.InputMedia:1 telebot.types.KeyboardButton:1 +#: of telebot.types.InputMedia:1 telebot.types.InputSticker:1 +#: telebot.types.KeyboardButton:1 msgid "" "Bases: :py:class:`telebot.types.Dictionaryable`, " ":py:class:`telebot.types.JsonSerializable`" @@ -3560,6 +3781,39 @@ msgstr "" msgid ":class:`telebot.types.InputMediaVideo`" msgstr "" +#: of telebot.types.InputSticker:1 +msgid "This object describes a sticker to be added to a sticker set." +msgstr "" + +#: of telebot.types.InputSticker:3 +msgid "" +"The added sticker. Pass a file_id as a String to send a file that already" +" exists on the Telegram servers, 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. Animated and video stickers can't be uploaded via " +"HTTP URL." +msgstr "" + +#: of telebot.types.InputSticker:8 +msgid "One or more(up to 20) emoji(s) corresponding to the sticker" +msgstr "" + +#: of telebot.types.InputSticker:11 +msgid "" +"Optional. Position where the mask should be placed on faces. For “mask” " +"stickers only." +msgstr "" + +#: of telebot.types.InputSticker:14 +msgid "" +"Optional. List of 0-20 search keywords for the sticker with total length " +"of up to 64 characters. For “regular” and “custom_emoji” stickers only." +msgstr "" + +#: of telebot.types.InputSticker:19 +msgid ":class:`telebot.types.InputSticker`" +msgstr "" + #: of telebot.types.InputTextMessageContent:1 msgid "" "Represents the content of a text message to be sent as the result of an " @@ -3726,7 +3980,21 @@ msgid "" "service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:26 +#: of telebot.types.KeyboardButton:25 +msgid "" +"Optional. If specified, pressing the button will open a list of suitable " +"users. Tapping on any user will send their identifier to the bot in a " +"“user_shared” service message. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:29 +msgid "" +"Optional. If specified, pressing the button will open a list of suitable " +"chats. Tapping on a chat will send its identifier to the bot in a " +"“chat_shared” service message. Available in private chats only." +msgstr "" + +#: of telebot.types.KeyboardButton:34 msgid ":class:`telebot.types.KeyboardButton`" msgstr "" @@ -3754,10 +4022,111 @@ msgstr "" msgid ":class:`telebot.types.KeyboardButtonPollType`" msgstr "" -#: of telebot.types.LabeledPrice:1 +#: of telebot.types.KeyboardButtonRequestChat:1 msgid "" -"Bases: :py:class:`telebot.types.JsonSerializable`, " -":py:class:`telebot.types.Dictionaryable`" +"This object defines the criteria used to request a suitable chat. The " +"identifier of the selected chat will be shared with the bot when the " +"corresponding button is pressed." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#keyboardbuttonrequestchat" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:6 +msgid "" +"Signed 32-bit identifier of the request, which will be received back in " +"the ChatShared object. Must be unique within the message" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:10 +msgid "" +"Pass True to request a channel chat, pass False to request a group or a " +"supergroup chat." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:13 +msgid "" +"Optional. Pass True to request a forum supergroup, pass False to request " +"a non-forum chat. If not specified, no additional restrictions are " +"applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:17 +msgid "" +"Optional. Pass True to request a supergroup or a channel with a username," +" pass False to request a chat without a username. If not specified, no " +"additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:21 +msgid "" +"Optional. Pass True to request a chat owned by the user. Otherwise, no " +"additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:24 +msgid "" +"Optional. A JSON-serialized object listing the required administrator " +"rights of the user in the chat. The rights must be a superset of " +"bot_administrator_rights. If not specified, no additional restrictions " +"are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:28 +msgid "" +"Optional. A JSON-serialized object listing the required administrator " +"rights of the bot in the chat. The rights must be a subset of " +"user_administrator_rights. If not specified, no additional restrictions " +"are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:32 +msgid "" +"Optional. Pass True to request a chat where the bot is a member. " +"Otherwise, no additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestChat:36 +msgid ":class:`telebot.types.KeyboardButtonRequestChat`" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:1 +msgid "" +"This object defines the criteria used to request a suitable user. The " +"identifier of the selected user will be shared with the bot when the " +"corresponding button is pressed." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#keyboardbuttonrequestuser" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:6 +msgid "" +"Signed 32-bit identifier of the request, which will be received back in " +"the UserShared object. Must be unique within the message" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:10 +msgid "" +"Optional. Pass True to request a bot, pass False to request a regular " +"user. If not specified, no additional restrictions are applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:14 +msgid "" +"Optional. Pass True to request a premium user, pass False to request a " +"non-premium user. If not specified, no additional restrictions are " +"applied." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:19 +msgid ":class:`telebot.types.KeyboardButtonRequestUser`" msgstr "" #: of telebot.types.LabeledPrice:1 @@ -4306,78 +4675,86 @@ msgid "" msgstr "" #: of telebot.types.Message:194 +msgid "Optional. Service message: a user was shared with the bot" +msgstr "" + +#: of telebot.types.Message:197 +msgid "Optional. Service message: a chat was shared with the bot" +msgstr "" + +#: of telebot.types.Message:200 msgid "" "Optional. The domain name of the website on which the user has logged in." " More about Telegram Login »" msgstr "" -#: of telebot.types.Message:198 +#: of telebot.types.Message:204 msgid "" "Optional. Service message: the user allowed the bot added to the " "attachment menu to write messages" msgstr "" -#: of telebot.types.Message:202 +#: of telebot.types.Message:208 msgid "Optional. Telegram Passport data" msgstr "" -#: of telebot.types.Message:205 +#: of telebot.types.Message:211 msgid "" "Optional. Service message. A user in the chat triggered another user's " "proximity alert while sharing Live Location." msgstr "" -#: of telebot.types.Message:209 +#: of telebot.types.Message:215 msgid "Optional. Service message: forum topic created" msgstr "" -#: of telebot.types.Message:212 +#: of telebot.types.Message:218 msgid "Optional. Service message: forum topic edited" msgstr "" -#: of telebot.types.Message:215 +#: of telebot.types.Message:221 msgid "Optional. Service message: forum topic closed" msgstr "" -#: of telebot.types.Message:218 +#: of telebot.types.Message:224 msgid "Optional. Service message: forum topic reopened" msgstr "" -#: of telebot.types.Message:221 +#: of telebot.types.Message:227 msgid "Optional. Service message: the 'General' forum topic hidden" msgstr "" -#: of telebot.types.Message:224 +#: of telebot.types.Message:230 msgid "Optional. Service message: the 'General' forum topic unhidden" msgstr "" -#: of telebot.types.Message:227 +#: of telebot.types.Message:233 msgid "Optional. Service message: video chat scheduled" msgstr "" -#: of telebot.types.Message:230 +#: of telebot.types.Message:236 msgid "Optional. Service message: video chat started" msgstr "" -#: of telebot.types.Message:233 +#: of telebot.types.Message:239 msgid "Optional. Service message: video chat ended" msgstr "" -#: of telebot.types.Message:236 +#: of telebot.types.Message:242 msgid "Optional. Service message: new participants invited to a video chat" msgstr "" -#: of telebot.types.Message:239 +#: of telebot.types.Message:245 msgid "Optional. Service message: data sent by a Web App" msgstr "" -#: of telebot.types.Message:242 +#: of telebot.types.Message:248 msgid "" "Optional. Inline keyboard attached to the message. login_url buttons are " "represented as ordinary url buttons." msgstr "" -#: of telebot.types.Message:247 +#: of telebot.types.Message:253 msgid ":class:`telebot.types.Message`" msgstr "" @@ -4540,7 +4917,7 @@ msgstr "" msgid "Photo height" msgstr "" -#: of telebot.types.PhotoSize:18 telebot.types.Sticker:46 +#: of telebot.types.PhotoSize:18 telebot.types.Sticker:51 #: telebot.types.VideoNote:21 msgid "Optional. File size in bytes" msgstr "" @@ -5058,7 +5435,14 @@ msgstr "" msgid "Optional. For custom emoji stickers, unique identifier of the custom emoji" msgstr "" -#: of telebot.types.Sticker:50 +#: of telebot.types.Sticker:46 +msgid "" +"Optional. True, if the sticker must be repainted to a text color in " +"messages, the color of the Telegram Premium badge in emoji status, white " +"color on chat photos, or another appropriate color in other places" +msgstr "" + +#: of telebot.types.Sticker:55 msgid ":class:`telebot.types.Sticker`" msgstr "" @@ -5136,6 +5520,45 @@ msgstr "" msgid ":class:`telebot.types.SuccessfulPayment`" msgstr "" +#: of telebot.types.SwitchInlineQueryChosenChat:1 telebot.types.User:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:1 +msgid "" +"Represents an inline button that switches the current user to inline mode" +" in a chosen chat, with an optional default inline query." +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:6 +msgid "" +"Optional. The default inline query to be inserted in the input field. If " +"left empty, only the bot's username will be inserted" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:10 +msgid "Optional. True, if private chats with users can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:13 +msgid "Optional. True, if private chats with bots can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:16 +msgid "Optional. True, if group and supergroup chats can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:19 +msgid "Optional. True, if channel chats can be chosen" +msgstr "" + +#: of telebot.types.SwitchInlineQueryChosenChat:23 +msgid ":class:`SwitchInlineQueryChosenChat`" +msgstr "" + #: of telebot.types.Update:1 msgid "" "This object represents an incoming update.At most one of the optional " @@ -5240,13 +5663,6 @@ msgstr "" msgid ":class:`telebot.types.Update`" msgstr "" -#: of telebot.types.User:1 -msgid "" -"Bases: :py:class:`telebot.types.JsonDeserializable`, " -":py:class:`telebot.types.Dictionaryable`, " -":py:class:`telebot.types.JsonSerializable`" -msgstr "" - #: of telebot.types.User:1 msgid "This object represents a Telegram user or bot." msgstr "" @@ -5348,6 +5764,32 @@ msgstr "" msgid ":class:`telebot.types.UserProfilePhotos`" msgstr "" +#: of telebot.types.UserShared:1 +msgid "" +"This object contains information about the user whose identifier was " +"shared with the bot using a `telebot.types.KeyboardButtonRequestUser` " +"button." +msgstr "" + +#: of telebot.types.UserShared:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#usershared" +msgstr "" + +#: of telebot.types.UserShared:9 +msgid "" +"Identifier of the shared user. This number may have more than 32 " +"significant bits and some programming languages may have " +"difficulty/silent defects in interpreting it. But it has at most 52 " +"significant bits, so a 64-bit integer or double-precision float type are " +"safe for storing this identifier. The bot may not have access to the user" +" and could be unable to use this identifier, unless the user is already " +"known to the bot by some other means." +msgstr "" + +#: of telebot.types.UserShared:16 +msgid ":class:`telebot.types.UserShared`" +msgstr "" + #: of telebot.types.Venue:1 msgid "This object represents a venue." msgstr "" @@ -5633,6 +6075,37 @@ msgid "" "https://core.telegram.org/bots/api#writeaccessallowed" msgstr "" +#: of telebot.types.WriteAccessAllowed:6 +msgid "Optional. Name of the Web App which was launched from a link" +msgstr "" + #~ msgid "Type of the result, must be animation" #~ msgstr "" +#~ msgid "" +#~ "True, if the user is allowed to" +#~ " send audios, documents, photos, videos," +#~ " video notes and voice notes" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. True, if the user is " +#~ "allowed to send audios, documents, " +#~ "photos, videos, video notes and voice" +#~ " notes, implies can_send_messages" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. True, if the user is " +#~ "allowed to send animations, games, " +#~ "stickers and use inline bots, implies" +#~ " can_send_media_messages" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. True, if the user is " +#~ "allowed to add web page previews " +#~ "to their messages, implies " +#~ "can_send_media_messages" +#~ msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/util.po b/docs/source/locales/ru/LC_MESSAGES/util.po index 0f3c4957b..1ba54df52 100644 --- a/docs/source/locales/ru/LC_MESSAGES/util.po +++ b/docs/source/locales/ru/LC_MESSAGES/util.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-29 14:44+0400\n" +"POT-Creation-Date: 2023-07-08 23:07+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,19 +18,19 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: ../../util.rst:3 +#: ../../source/util.rst:3 msgid "Utils" msgstr "Утилиты" -#: ../../util.rst:5 +#: ../../source/util.rst:5 msgid "Utils in pyTelegramBotAPI" msgstr "Утилиты в pyTelegramBotAPI" -#: ../../util.rst:5 +#: ../../source/util.rst:5 msgid "ptba, pytba, pyTelegramBotAPI, utils, guide" msgstr "ptba, pytba, pyTelegramBotAPI, утилиты, гайд" -#: ../../util.rst:11 +#: ../../source/util.rst:11 msgid "util file" msgstr "Файл util" @@ -38,11 +38,14 @@ msgstr "Файл util" msgid "" "Use this function inside loops in order to avoid getting TooManyRequests " "error. Example:" -msgstr "Используйте эту функцию в циклах, чтобы избежать ошибки TooManyRequests. Пример:" +msgstr "" +"Используйте эту функцию в циклах, чтобы избежать ошибки TooManyRequests. " +"Пример:" -#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments -#: telebot.util.extract_command telebot.util.is_bytes telebot.util.is_command -#: telebot.util.is_dict telebot.util.is_pil_image +#: of telebot.service_utils.is_bytes telebot.service_utils.is_dict +#: telebot.service_utils.is_pil_image telebot.util.antiflood +#: telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_command #: telebot.util.parse_web_app_data telebot.util.quick_markup #: telebot.util.smart_split telebot.util.split_string telebot.util.user_link #: telebot.util.validate_web_app_data telebot.util.webhook_google_functions @@ -54,28 +57,33 @@ msgid "The function to call" msgstr "Вызываемая функция" #: of telebot.util.antiflood:13 +msgid "Number of retries to send" +msgstr "" + +#: of telebot.util.antiflood:16 msgid "The arguments to pass to the function" msgstr "Аргументы, для передачи в функцию" -#: of telebot.util.antiflood:16 +#: of telebot.util.antiflood:19 msgid "The keyword arguments to pass to the function" msgstr "Именованные аргументы для передачи в функцию" -#: of telebot.util.antiflood telebot.util.escape telebot.util.extract_arguments -#: telebot.util.extract_command telebot.util.generate_random_token -#: telebot.util.is_bytes telebot.util.is_command telebot.util.is_dict -#: telebot.util.is_pil_image telebot.util.parse_web_app_data -#: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string -#: telebot.util.user_link telebot.util.validate_web_app_data -#: telebot.util.webhook_google_functions +#: of telebot.service_utils.generate_random_token +#: telebot.service_utils.is_bytes telebot.service_utils.is_dict +#: telebot.service_utils.is_pil_image telebot.util.antiflood +#: telebot.util.escape telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_command +#: telebot.util.parse_web_app_data telebot.util.quick_markup +#: telebot.util.smart_split telebot.util.split_string telebot.util.user_link +#: telebot.util.validate_web_app_data telebot.util.webhook_google_functions msgid "Returns" msgstr "" -#: of telebot.util.antiflood:19 +#: of telebot.util.antiflood:22 msgid "None" msgstr "" -#: of telebot.util.chunks:1 +#: of telebot.service_utils.chunks:1 msgid "Yield successive n-sized chunks from lst." msgstr "Генерирует последовательные части списка, состоящие из n элементов." @@ -91,8 +99,9 @@ msgstr "Содержит все виды сервисных сообщений, msgid "" "Replaces the following chars in `text` ('&' with '&', '<' with '<'" " and '>' with '>')." -msgstr "Заменяет следующие символы в `text` ('&' на '&', '<' на '<'" -" и '>' на '>')." +msgstr "" +"Заменяет следующие символы в `text` ('&' на '&', '<' на '<' и '>' " +"на '>')." #: of telebot.util.escape:3 msgid "the text to escape" @@ -116,11 +125,14 @@ msgstr "Строка для извлечения аргументов коман #: of telebot.util.extract_arguments:13 msgid "the arguments if `text` is a command (according to is_command), else None." -msgstr "Аргументы, если `text` является командой (согласно is_command), в остальных случаях None." +msgstr "" +"Аргументы, если `text` является командой (согласно is_command), в " +"остальных случаях None." -#: of telebot.util.extract_arguments telebot.util.extract_command -#: telebot.util.generate_random_token telebot.util.is_bytes -#: telebot.util.is_command telebot.util.is_dict telebot.util.is_pil_image +#: of telebot.service_utils.generate_random_token +#: telebot.service_utils.is_bytes telebot.service_utils.is_dict +#: telebot.service_utils.is_pil_image telebot.util.extract_arguments +#: telebot.util.extract_command telebot.util.is_command #: telebot.util.quick_markup telebot.util.smart_split telebot.util.split_string #: telebot.util.user_link msgid "Return type" @@ -135,8 +147,9 @@ msgid "" "Extracts the command from `text` (minus the '/') if `text` is a command " "(see is_command). If `text` is not a command, this function returns None." msgstr "" -"Извлекает команду из `text` (исключает '/') если `text` является командой " -"(см. is_command). Если `text` не является командой, эта функция возвращает None." +"Извлекает команду из `text` (исключает '/') если `text` является командой" +" (см. is_command). Если `text` не является командой, эта функция " +"возвращает None." #: of telebot.util.extract_command:12 msgid "String to extract the command from" @@ -144,37 +157,41 @@ msgstr "Строка, из которой нужно извлечь команд #: of telebot.util.extract_command:15 msgid "the command if `text` is a command (according to is_command), else None." -msgstr "Команда, если `text` является командой (согласно is_command), в остальных случаях None." +msgstr "" +"Команда, если `text` является командой (согласно is_command), в остальных" +" случаях None." -#: of telebot.util.generate_random_token:1 +#: of telebot.service_utils.generate_random_token:1 msgid "" "Generates a random token consisting of letters and digits, 16 characters " "long." -msgstr "Генерирует рандомный токен, состоящий из латинских букв и цифр длиной 16 символов." +msgstr "" +"Генерирует рандомный токен, состоящий из латинских букв и цифр длиной 16 " +"символов." -#: of telebot.util.generate_random_token:3 +#: of telebot.service_utils.generate_random_token:3 msgid "a random token" msgstr "Сгенерированный токен" -#: of telebot.util.generate_random_token:4 telebot.util.user_link:22 +#: of telebot.service_utils.generate_random_token:4 telebot.util.user_link:22 msgid ":obj:`str`" msgstr "" -#: of telebot.util.is_bytes:1 +#: of telebot.service_utils.is_bytes:1 msgid "Returns True if the given object is a bytes object." msgstr "Возвращает True если полученный объект является bytes." -#: of telebot.util.is_bytes:3 telebot.util.is_dict:3 -#: telebot.util.is_pil_image:3 +#: of telebot.service_utils.is_bytes:3 telebot.service_utils.is_dict:3 +#: telebot.service_utils.is_pil_image:3 msgid "object to be checked" msgstr "Объект для проверки" -#: of telebot.util.is_bytes:6 +#: of telebot.service_utils.is_bytes:6 msgid "True if the given object is a bytes object." msgstr "True, если полученный объект является bytes." -#: of telebot.util.is_bytes:7 telebot.util.is_command:7 telebot.util.is_dict:7 -#: telebot.util.is_pil_image:7 +#: of telebot.service_utils.is_bytes:7 telebot.service_utils.is_dict:7 +#: telebot.service_utils.is_pil_image:7 telebot.util.is_command:7 msgid ":obj:`bool`" msgstr "" @@ -183,8 +200,8 @@ msgid "" "Checks if `text` is a command. Telegram chat commands start with the '/' " "character." msgstr "" -"Проверяет, является ли `text` командой. Команды в Telegram начинаются " -"с символа '/'." +"Проверяет, является ли `text` командой. Команды в Telegram начинаются с " +"символа '/'." #: of telebot.util.is_command:3 msgid "Text to check." @@ -194,23 +211,23 @@ msgstr "Текст для проверки." msgid "True if `text` is a command, else False." msgstr "True, если `text` является командой, иначе False." -#: of telebot.util.is_dict:1 +#: of telebot.service_utils.is_dict:1 msgid "Returns True if the given object is a dictionary." msgstr "Возвращает True, если полученный объект является словарём (dict)." -#: of telebot.util.is_dict:6 +#: of telebot.service_utils.is_dict:6 msgid "True if the given object is a dictionary." msgstr "True, если полученный объект является словарём (dict)." -#: of telebot.util.is_pil_image:1 +#: of telebot.service_utils.is_pil_image:1 msgid "Returns True if the given object is a PIL.Image.Image object." msgstr "Возвращает True, если полученный объект является PIL.Image.Image." -#: of telebot.util.is_pil_image:6 +#: of telebot.service_utils.is_pil_image:6 msgid "True if the given object is a PIL.Image.Image object." msgstr "True, если полученный объект является PIL.Image.Image." -#: of telebot.util.is_string:1 +#: of telebot.service_utils.is_string:1 msgid "Returns True if the given object is a string." msgstr "Возвращает True, если полученный объект является строкой (str)." @@ -236,9 +253,9 @@ msgid "" "is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2" " = InlineKeyboardButton(...)'" msgstr "" -"Возвращает reply markup из словаря следующего формата: {'text': kwargs}. Удобно " -"использовать вместо постоянного использования 'btn1 = InlineKeyboardButton(...)' 'btn2" -" = InlineKeyboardButton(...)'" +"Возвращает reply markup из словаря следующего формата: {'text': kwargs}. " +"Удобно использовать вместо постоянного использования 'btn1 = " +"InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)'" #: of telebot.util.quick_markup:4 telebot.util.user_link:5 msgid "Example:" @@ -248,7 +265,7 @@ msgstr "Пример:" msgid "Using quick_markup:" msgstr "Используя quick_markup:" -#: of telebot.util.quick_markup:29 +#: of telebot.util.quick_markup:31 msgid "" "a dict containing all buttons to create in this format: {text: kwargs} " "{str:}" @@ -256,15 +273,15 @@ msgstr "" "Словарь, содержащий все кнопки для создания reply markup в следующем " "формате: {text: kwargs} {str:}" -#: of telebot.util.quick_markup:32 -msgid "int row width" -msgstr "Количество кнопок в одной строке, int" +#: of telebot.util.quick_markup:34 +msgid "number of :class:`telebot.types.InlineKeyboardButton` objects on each row" +msgstr "" -#: of telebot.util.quick_markup:35 +#: of telebot.util.quick_markup:37 msgid "InlineKeyboardMarkup" msgstr "" -#: of telebot.util.quick_markup:36 +#: of telebot.util.quick_markup:38 msgid ":obj:`types.InlineKeyboardMarkup`" msgstr "" @@ -276,10 +293,11 @@ msgid "" " `chars_per_string` = 4096. Splits by '\\n', '. ' or ' ' in exactly this " "priority." msgstr "" -"Разбивает строку на несколько, каждая из которых будет не длиннее `characters_per_string`. " -"Удобно использовать для разбиения одного гигантского сообщения на несколько. " -"Если `chars_per_string` > 4096: `chars_per_string` = 4096. Разбивает строку по '\\n', '. ' или ' ' " -"именно в таком порядке." +"Разбивает строку на несколько, каждая из которых будет не длиннее " +"`characters_per_string`. Удобно использовать для разбиения одного " +"гигантского сообщения на несколько. Если `chars_per_string` > 4096: " +"`chars_per_string` = 4096. Разбивает строку по '\\n', '. ' или ' ' именно" +" в таком порядке." #: of telebot.util.smart_split:6 telebot.util.split_string:4 msgid "The text to split" @@ -287,7 +305,9 @@ msgstr "Текст для разбиения" #: of telebot.util.smart_split:9 msgid "The number of maximum characters per part the text is split to." -msgstr "Максимальное количество символов в части текста, на которые он будет разбит." +msgstr "" +"Максимальное количество символов в части текста, на которые он будет " +"разбит." #: of telebot.util.smart_split:12 telebot.util.split_string:10 msgid "The splitted text as a list of strings." @@ -303,8 +323,9 @@ msgid "" "`chars_per_string` characters per string. This is very useful for " "splitting one giant message into multiples." msgstr "" -"Разбивает одну строку на несколько, каждая из которых будет не длиннее `characters_per_string`. " -"Удобно использовать для разбиения одного гигантского сообщения на несколько." +"Разбивает одну строку на несколько, каждая из которых будет не длиннее " +"`characters_per_string`. Удобно использовать для разбиения одного " +"гигантского сообщения на несколько." #: of telebot.util.split_string:7 msgid "The number of characters per line the text is split into." @@ -312,15 +333,17 @@ msgstr "Количество символов в одной строке, на #: ../../docstring of telebot.util.update_types:1 msgid "All update types, should be used for allowed_updates parameter in polling." -msgstr "Все виды апдейтов, рекомендуется использовать в качестве параметра allowed_updates функции polling." +msgstr "" +"Все виды апдейтов, рекомендуется использовать в качестве параметра " +"allowed_updates функции polling." #: of telebot.util.user_link:1 msgid "" "Returns an HTML user link. This is useful for reports. Attention: Don't " "forget to set parse_mode to 'HTML'!" msgstr "" -"Возвращает HTML ссылку на пользователя. Удобно использовать для отчетов. Важно: Не " -"забудьте установить значение 'HTML' в parse_mode!" +"Возвращает HTML ссылку на пользователя. Удобно использовать для отчетов. " +"Важно: Не забудьте установить значение 'HTML' в parse_mode!" #: of telebot.util.user_link:11 msgid "" @@ -328,9 +351,10 @@ msgid "" "links, and etc.) This method is kept for backward compatibility, and it " "is recommended to use formatting.* for more options." msgstr "" -"Вы можете использовать formatting.* во всех остальных вариантах форматирования(bold, italic, " -"links, и прочее). Этот метод сохранён для обратной совместимости, рекомендуется " -"использовать formatting.* для большего количества вариантов." +"Вы можете использовать formatting.* во всех остальных вариантах " +"форматирования(bold, italic, links, и прочее). Этот метод сохранён для " +"обратной совместимости, рекомендуется использовать formatting.* для " +"большего количества вариантов." #: of telebot.util.user_link:15 msgid "the user (not the user_id)" @@ -364,3 +388,6 @@ msgstr "HTTP-запрос" msgid "The response object" msgstr "Объект, полученный в качестве ответа" +#~ msgid "int row width" +#~ msgstr "Количество кнопок в одной строке, int" + From 67e3774e8ef59af54b6b71ec69e5cc291bf218bc Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 8 Jul 2023 23:23:23 +0500 Subject: [PATCH 1345/1808] Fix issue with post_process in async not receiving the error --- telebot/async_telebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 72e7ad4c0..981ad25e5 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -536,6 +536,7 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up if not isinstance(result, ContinueHandling): break except Exception as e: + handler_error = e if self.exception_handler: self.exception_handler.handle(e) else: From 916569cdc5c51567ba7fd104f400ee5b8933cc1b Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 8 Jul 2023 23:42:47 +0500 Subject: [PATCH 1346/1808] Fixes #1944: uploading file from memory --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index b793073fd..7f5fc3035 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7250,7 +7250,7 @@ def _resolve_file(self, file): _file = open(file, 'rb') return _file, os.path.basename(_file.name) elif isinstance(file, IOBase): - return file, os.path.basename(file.name) + return file, service_utils.generate_random_token() elif isinstance(file, Path): _file = open(file, 'rb') return _file, os.path.basename(_file.name) @@ -7742,4 +7742,4 @@ def to_dict(self) -> dict: return json_dict def to_json(self) -> str: - return json.dumps(self.to_dict()) \ No newline at end of file + return json.dumps(self.to_dict()) From cb7f6a8c9933a540551868946d936fabf172865c Mon Sep 17 00:00:00 2001 From: Kourva <118578799+Kourva@users.noreply.github.com> Date: Mon, 10 Jul 2023 03:05:34 +0330 Subject: [PATCH 1347/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aa8b32ec3..cf6e75fa9 100644 --- a/README.md +++ b/README.md @@ -891,5 +891,6 @@ Here are some examples of template: * [Spot Seek Bot](https://t.me/SpotSeekBot) ([source](https://github.com/arashnm80/spot-seek-bot)) by [Arashnm80](https://github.com/arashnm80). This is a free & open source telegram bot for downloading tracks, albums or playlists from spotify. * [CalendarIT Bot](https://t.me/calendarit_bot) ([source](https://github.com/codebyzen/CalendarIT_Telegram_Bot))by [CodeByZen](https://github.com/codebyzen). A simple, but extensible Python Telegram bot, can post acquainted with what is happening today, tomorrow or what happened 20 years ago to channel. * [DownloadMusicBOT](https://github.com/fcoagz/DownloadMusicBOT) by *Francisco Griman* - It is a simple bot that downloads audio from YouTube videos on Telegram. +* [AwesomeChatGPTBot](https://github.com/Kourva/AwesomeChatGPTBot) - Simple ChatGTP-3.5 bot. It is FREE and can remember chat history for a while With pre-defined roles! **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From b18bcd494a6bdcae8ad95379344b3cfb2a36319b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 12 Jul 2023 19:48:22 +0300 Subject: [PATCH 1348/1808] Fix aioredis version check for regular redis --- telebot/asyncio_storage/redis_storage.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index f9c3aeac3..8d2519b71 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,10 +1,11 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext import json - redis_installed = True +is_actual_aioredis = False try: import aioredis + is_actual_aioredis = True except ImportError: try: from redis import asyncio as aioredis @@ -23,10 +24,10 @@ def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='tel if not redis_installed: raise ImportError('AioRedis is not installed. Install it via "pip install aioredis"') - - aioredis_version = tuple(map(int, aioredis.__version__.split(".")[0])) - if aioredis_version < (2,): - raise ImportError('Invalid aioredis version. Aioredis version should be >= 2.0.0') + if is_actual_aioredis: + aioredis_version = tuple(map(int, aioredis.__version__.split(".")[0])) + if aioredis_version < (2,): + raise ImportError('Invalid aioredis version. Aioredis version should be >= 2.0.0') self.redis = aioredis.Redis(host=host, port=port, db=db, password=password) self.prefix = prefix From 1946393c36d64511f691595e9625718049fafcca Mon Sep 17 00:00:00 2001 From: Artin GH <80193492+Artin-GH@users.noreply.github.com> Date: Fri, 14 Jul 2023 16:39:50 +0330 Subject: [PATCH 1349/1808] Fix backslash (\) issue in escape_markdown --- telebot/formatting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index ec421c617..32a09fe2d 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -61,8 +61,8 @@ def escape_markdown(content: str) -> str: :rtype: :obj:`str` """ - parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!\{\}])", r"\\\1", content) - reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!\{\}])", r"\1", parse) + parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!\{\}\\])", r"\\\1", content) + reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!\{\}\\])", r"\1", parse) return reparse From ebfbfb2dfc416e8c52ceeb03ea0545e3a1f9ea5c Mon Sep 17 00:00:00 2001 From: TheWCKD Date: Mon, 24 Jul 2023 18:38:22 +0300 Subject: [PATCH 1350/1808] added posibility to handle exception on RequestTimeout --- telebot/async_telebot.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 981ad25e5..b3f189e16 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -415,8 +415,16 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: except asyncio.CancelledError: return except asyncio_helper.RequestTimeout as e: - logger.error(str(e)) - if non_stop: + handled = False + if self.exception_handler: + self.exception_handler.handle(e) + handled = True + + if not handled: + logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) + logger.debug(traceback.format_exc()) + + if non_stop or handled: await asyncio.sleep(2) continue else: From a5cc2f589b49ee14b75a73309bc2f18b783b77ef Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 18 Aug 2023 22:00:14 +0500 Subject: [PATCH 1351/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf6e75fa9..c37a19eb4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.7! +##

Supported Bot API version: 6.8!

Official documentation

Official ru documentation

From b5112d8330a37af9534c999c842129c65e3972bf Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 18 Aug 2023 22:09:29 +0500 Subject: [PATCH 1352/1808] Support for stories: is_forwarded_story, story object --- telebot/types.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 7f5fc3035..3d884321e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -821,6 +821,9 @@ class Message(JsonDeserializable): :param sticker: Optional. Message is a sticker, information about the sticker :type sticker: :class:`telebot.types.Sticker` + :param story: Optional. Message is a forwarded story + :type story: :class:`telebot.types.Story` + :param video: Optional. Message is a video, information about the video :type video: :class:`telebot.types.Video` @@ -1177,6 +1180,9 @@ def de_json(cls, json_string): if 'chat_shared' in obj: opts['chat_shared'] = ChatShared.de_json(obj['chat_shared']) content_type = 'chat_shared' + if 'story' in obj: + opts['story'] = Story.de_json(obj['story']) + content_type = 'story' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1274,10 +1280,19 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.write_access_allowed: Optional[WriteAccessAllowed] = None self.user_shared: Optional[UserShared] = None self.chat_shared: Optional[ChatShared] = None + self.story: Optional[Story] = None for key in options: setattr(self, key, options[key]) self.json = json_string + @property + def is_forwarded_story(self): + """ + Returns True if the message is a forwarded story. + """ + # TODO: In future updates this propery might require changes. + return True if self.story is not None else False + def __html_text(self, text, entities): """ Author: @sviat9440 @@ -7743,3 +7758,18 @@ def to_dict(self) -> dict: def to_json(self) -> str: return json.dumps(self.to_dict()) + + +class Story(JsonDeserializable): + """ + This object represents a message about a forwarded story in the chat. + Currently holds no information. + """ + + @classmethod + def de_json(cls, json_string): + return cls() + + def __init__(self) -> None: + pass + From 35e765f70b2279bd4bea88e8d050d45719da54da Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 18 Aug 2023 22:14:44 +0500 Subject: [PATCH 1353/1808] Added the field voter_chat to the class PollAnswer, to contain channel chat voters in Polls. --- telebot/types.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 3d884321e..8fb32fa0b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6695,6 +6695,9 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): :param poll_id: Unique poll identifier :type poll_id: :obj:`str` + :param voter_chat: Optional. The chat that changed the answer to the poll, if the voter is anonymous + :type voter_chat: :class:`telebot.types.Chat` + :param user: The user, who changed the answer to the poll :type user: :class:`telebot.types.User` @@ -6710,12 +6713,16 @@ def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) + if 'voter_chat' in obj: + obj['voter_chat'] = Chat.de_json(obj['voter_chat']) return cls(**obj) - def __init__(self, poll_id, user, option_ids, **kwargs): + def __init__(self, poll_id, user, option_ids, voter_chat=None, **kwargs): self.poll_id: str = poll_id self.user: User = user self.option_ids: List[int] = option_ids + self.voter_chat: Optional[Chat] = voter_chat + def to_json(self): return json.dumps(self.to_dict()) From 1acb15f06f0e869218151c3d04a32274de1fd6cf Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 18 Aug 2023 22:19:14 +0500 Subject: [PATCH 1354/1808] Added the field emoji_status_expiration_date to the class Chat. --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 8fb32fa0b..6354c3ebc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -543,6 +543,10 @@ class Chat(JsonDeserializable): 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. + :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` @@ -638,7 +642,7 @@ def __init__(self, id, type, title=None, username=None, first_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, - has_hidden_members=None, has_aggressive_anti_spam_enabled=None, **kwargs): + has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -667,6 +671,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.emoji_status_custom_emoji_id: str = emoji_status_custom_emoji_id self.has_hidden_members: bool = has_hidden_members self.has_aggressive_anti_spam_enabled: bool = has_aggressive_anti_spam_enabled + self.emoji_status_expiration_date: int = emoji_status_expiration_date class MessageID(JsonDeserializable): From de568023f6af05e3c511de1f4c7cf4f343d7f152 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 18 Aug 2023 22:23:36 +0500 Subject: [PATCH 1355/1808] Added the method unpinAllGeneralForumTopicMessages. --- telebot/__init__.py | 18 ++++++++++++++++++ telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 18 ++++++++++++++++++ telebot/asyncio_helper.py | 5 +++++ 4 files changed, 46 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 77cdd0ded..e4377d2a0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4571,6 +4571,24 @@ def answer_inline_query( return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, button) + def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to clear the list of pinned messages in a General forum topic. + The bot must be an administrator in the chat for this to work and must have the + can_pin_messages administrator right in the supergroup. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages + + :param chat_id: Unique identifier for the target chat or username of chat + :type chat_id: :obj:`int` | :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + + return apihelper.unpin_all_general_forum_topic_messages(self.token, chat_id) + def answer_callback_query( self, callback_query_id: int, text: Optional[str]=None, show_alert: Optional[bool]=None, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a07f1ef91..cf8a03faf 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1585,6 +1585,11 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No return _make_request(token, method_url, params=payload) +def unpin_all_general_forum_topic_messages(token, chat_id): + method_url = 'unpinAllGeneralForumTopicMessages' + payload = {'chat_id': chat_id} + return _make_request(token, method_url, params=payload, method='post') + # InlineQuery def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None, cache_time=None): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 981ad25e5..cde4f0096 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5424,6 +5424,24 @@ async def answer_inline_query( return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, button) + async def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool: + """ + Use this method to clear the list of pinned messages in a General forum topic. + The bot must be an administrator in the chat for this to work and must have the + can_pin_messages administrator right in the supergroup. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages + + :param chat_id: Unique identifier for the target chat or username of chat + :type chat_id: :obj:`int` | :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + + return await asyncio_helper.unpin_all_general_forum_topic_messages(self.token, chat_id) + async def answer_callback_query( self, callback_query_id: int, text: Optional[str]=None, show_alert: Optional[bool]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 5a615b397..894b62be5 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1575,6 +1575,11 @@ async def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_mess return await _process_request(token, method_url, params=payload) +async def unpin_all_general_forum_topic_messages(token, chat_id): + method_url = 'unpinAllGeneralForumTopicMessages' + payload = {'chat_id': chat_id} + return await _process_request(token, method_url, params=payload, method='post') + # InlineQuery async def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None, cache_time=None): From 9fd6ef3845009eabc0768fb9de44205b5d5ad841 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sat, 19 Aug 2023 20:58:49 +0500 Subject: [PATCH 1356/1808] Done with the review --- telebot/types.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 6354c3ebc..a81fad42e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1290,13 +1290,6 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso setattr(self, key, options[key]) self.json = json_string - @property - def is_forwarded_story(self): - """ - Returns True if the message is a forwarded story. - """ - # TODO: In future updates this propery might require changes. - return True if self.story is not None else False def __html_text(self, text, entities): """ @@ -6703,7 +6696,7 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): :param voter_chat: Optional. The chat that changed the answer to the poll, if the voter is anonymous :type voter_chat: :class:`telebot.types.Chat` - :param user: The user, who changed the answer to the poll + :param user: Optional. The user, who changed the answer to the poll :type user: :class:`telebot.types.User` :param option_ids: 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted @@ -6717,14 +6710,15 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): def de_json(cls, json_string): if (json_string is None): return None obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) + if 'user' in obj: + obj['user'] = User.de_json(obj['user']) if 'voter_chat' in obj: obj['voter_chat'] = Chat.de_json(obj['voter_chat']) return cls(**obj) - def __init__(self, poll_id, user, option_ids, voter_chat=None, **kwargs): + def __init__(self, poll_id, option_ids, user=None, voter_chat=None, **kwargs): self.poll_id: str = poll_id - self.user: User = user + self.user: Optional[User] = user self.option_ids: List[int] = option_ids self.voter_chat: Optional[Chat] = voter_chat @@ -6733,9 +6727,17 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - return {'poll_id': self.poll_id, - 'user': self.user.to_dict(), - 'option_ids': self.option_ids} + # Left for backward compatibility, but with no support for voter_chat + json_dict = { + "poll_id": self.poll_id, + "option_ids": self.option_ids + } + if self.user: + json_dict["user"] = self.user.to_dict() + if self.voter_chat: + json_dict["voter_chat"] = self.voter_chat + return json_dict + class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): @@ -7780,7 +7782,10 @@ class Story(JsonDeserializable): @classmethod def de_json(cls, json_string): - return cls() + if json_string is None: + return None + obj = cls.check_json(json_string) + return cls(**obj) def __init__(self) -> None: pass From ca41526bcb4e8c810b5f3d486da5d24d9a596904 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 20 Aug 2023 09:06:56 +0300 Subject: [PATCH 1357/1808] Bump version to 4.13.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f698fc698..ed241d5ee 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.12.0' +release = '4.13.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index b42b55928..68dfd5046 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.12.0' +__version__ = '4.13.0' From 09f9fb49e536bea85248d94d3c14dcad361740f9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 20 Aug 2023 09:13:31 +0300 Subject: [PATCH 1358/1808] Removed python 3.7, added pypy 3.10 --- .github/workflows/setup_python.yml | 2 +- .readthedocs.yml | 4 ++-- .travis.yml | 1 - README.md | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index ac436e848..2a9787e25 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9'] + python-version: [ '3.8', '3.9', '3.10', '3.11', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 diff --git a/.readthedocs.yml b/.readthedocs.yml index 859c13484..0a15ad409 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -14,6 +14,6 @@ formats: all # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 + version: 3.11 install: - - requirements: doc_req.txt \ No newline at end of file + - requirements: doc_req.txt diff --git a/.travis.yml b/.travis.yml index d316070b1..8e6b02e5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "3.7" - "3.8" - "3.9" - "3.10" diff --git a/README.md b/README.md index c37a19eb4..477954916 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ ## Getting started -This API is tested with Python 3.7-3.11 and Pypy 3. +This API is tested with Python 3.8-3.11 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager): From 24ec1254a38ee523148f58c053487c210b14346e Mon Sep 17 00:00:00 2001 From: Cub11k Date: Fri, 8 Sep 2023 20:13:34 +0300 Subject: [PATCH 1359/1808] Add redis_url to StateRedisStorage to allow initialization by URL Useful for rediss:// and unix:// schemes --- telebot/asyncio_storage/redis_storage.py | 7 +++++-- telebot/storage/redis_storage.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 8d2519b71..84db253e5 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -20,7 +20,7 @@ class StateRedisStorage(StateStorageBase): To use it, just pass this class to: TeleBot(storage=StateRedisStorage()) """ - def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_'): + def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_', redis_url=None): if not redis_installed: raise ImportError('AioRedis is not installed. Install it via "pip install aioredis"') @@ -28,7 +28,10 @@ def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='tel aioredis_version = tuple(map(int, aioredis.__version__.split(".")[0])) if aioredis_version < (2,): raise ImportError('Invalid aioredis version. Aioredis version should be >= 2.0.0') - self.redis = aioredis.Redis(host=host, port=port, db=db, password=password) + if redis_url: + self.redis = aioredis.Redis.from_url(redis_url) + else: + self.redis = aioredis.Redis(host=host, port=port, db=db, password=password) self.prefix = prefix #self.con = Redis(connection_pool=self.redis) -> use this when necessary diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 453d6ae33..3fac57c46 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -15,9 +15,12 @@ class StateRedisStorage(StateStorageBase): To use it, just pass this class to: TeleBot(storage=StateRedisStorage()) """ - def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_'): + def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_', redis_url=None): super().__init__() - self.redis = ConnectionPool(host=host, port=port, db=db, password=password) + if redis_url: + self.redis = ConnectionPool.from_url(redis_url) + else: + self.redis = ConnectionPool(host=host, port=port, db=db, password=password) #self.con = Redis(connection_pool=self.redis) -> use this when necessary # # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} From b2d5bec9c0024d4fb5ff88c7a8630ffd73619402 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 22 Sep 2023 23:29:33 +0400 Subject: [PATCH 1360/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 477954916..4dc98ea5b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.8! +##

Supported Bot API version: 6.9!

Official documentation

Official ru documentation

From e904b9112be245d979a8117c122cf7cc075955c8 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 22 Sep 2023 23:36:52 +0400 Subject: [PATCH 1361/1808] Added the new administrator privileges can_post_stories, can_edit_stories and can_delete_stories to the classes ChatMemberAdministrator and ChatAdministratorRights. --- telebot/types.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index a81fad42e..6e49a0cb0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2888,6 +2888,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed can_send_other_messages=None, can_add_web_page_previews=None, can_manage_chat=None, can_manage_video_chats=None, until_date=None, can_manage_topics=None, + can_post_stories=None, can_edit_stories=None, can_delete_stories=None, **kwargs): self.user: User = user self.status: str = status @@ -2919,6 +2920,9 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_send_videos: bool = can_send_videos self.can_send_video_notes: bool = can_send_video_notes self.can_send_voice_notes: bool = can_send_voice_notes + self.can_post_stories: bool = can_post_stories + self.can_edit_stories: bool = can_edit_stories + self.can_delete_stories: bool = can_delete_stories @@ -3006,6 +3010,15 @@ class ChatMemberAdministrator(ChatMember): :param custom_title: Optional. Custom title for this user :type custom_title: :obj:`str` + :param can_post_stories: Optional. True, if the administrator can post channel stories + :type can_post_stories: :obj:`bool` + + :param can_edit_stories: Optional. True, if the administrator can edit stories + :type can_edit_stories: :obj:`bool` + + :param can_delete_stories: Optional. True, if the administrator can delete stories of other users + :type can_delete_stories: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberAdministrator` """ @@ -7179,6 +7192,15 @@ class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryab :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only :type can_manage_topics: :obj:`bool` + :param can_post_stories: Optional. True, if the administrator can post channel stories + :type can_post_stories: :obj:`bool` + + :param can_edit_stories: Optional. True, if the administrator can edit stories + :type can_edit_stories: :obj:`bool` + + :param can_delete_stories: Optional. True, if the administrator can delete stories of other users + :type can_delete_stories: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ChatAdministratorRights` """ @@ -7193,7 +7215,10 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, can_delete_messages: bool, can_manage_video_chats: bool, can_restrict_members: bool, can_promote_members: bool, can_change_info: bool, can_invite_users: bool, can_post_messages: bool=None, can_edit_messages: bool=None, - can_pin_messages: bool=None, can_manage_topics: bool=None) -> None: + can_pin_messages: bool=None, can_manage_topics: bool=None, + can_post_stories: bool=None, can_edit_stories: bool=None, + can_delete_stories: bool=None + ) -> None: self.is_anonymous: bool = is_anonymous self.can_manage_chat: bool = can_manage_chat @@ -7207,6 +7232,9 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, self.can_edit_messages: bool = can_edit_messages self.can_pin_messages: bool = can_pin_messages self.can_manage_topics: bool = can_manage_topics + self.can_post_stories: bool = can_post_stories + self.can_edit_stories: bool = can_edit_stories + self.can_delete_stories: bool = can_delete_stories def to_dict(self): json_dict = { @@ -7227,6 +7255,13 @@ def to_dict(self): json_dict['can_pin_messages'] = self.can_pin_messages if self.can_manage_topics is not None: json_dict['can_manage_topics'] = self.can_manage_topics + if self.can_post_stories is not None: + json_dict['can_post_stories'] = self.can_post_stories + if self.can_edit_stories is not None: + json_dict['can_edit_stories'] = self.can_edit_stories + if self.can_delete_stories is not None: + json_dict['can_delete_stories'] = self.can_delete_stories + return json_dict def to_json(self): From d787862f77de959e4c9b4b3058d7a63431a8be34 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 22 Sep 2023 23:42:12 +0400 Subject: [PATCH 1362/1808] Added the parameters can_post_stories, can_edit_stories and can_delete_stories to the method promoteChatMember. Currently, bots have no use for these privileges besides assigning them to other administrators. --- telebot/__init__.py | 17 +++++++++++++++-- telebot/apihelper.py | 9 ++++++++- telebot/async_telebot.py | 17 +++++++++++++++-- telebot/asyncio_helper.py | 9 ++++++++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e4377d2a0..3e0258a4c 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3058,7 +3058,10 @@ def promote_chat_member( can_manage_chat: Optional[bool]=None, can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None, - can_manage_topics: Optional[bool]=None) -> bool: + can_manage_topics: Optional[bool]=None, + can_post_stories: Optional[bool]=None, + can_edit_stories: Optional[bool]=None, + can_delete_stories: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -3119,6 +3122,15 @@ def promote_chat_member( and reopen forum topics, supergroups only :type can_manage_topics: :obj:`bool` + :param can_post_stories: Pass True if the administrator can create the channel's stories + :type can_post_stories: :obj:`bool` + + :param can_edit_stories: Pass True if the administrator can edit the channel's stories + :type can_edit_stories: :obj:`bool` + + :param can_delete_stories: Pass True if the administrator can delete the channel's stories + :type can_delete_stories: :obj:`bool` + :return: True on success. :rtype: :obj:`bool` """ @@ -3131,7 +3143,8 @@ def promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics) + is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics, + can_post_stories, can_edit_stories, can_delete_stories) def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index cf8a03faf..902d6e5ca 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -991,7 +991,8 @@ def promote_chat_member( can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None, - can_manage_topics=None): + can_manage_topics=None, can_post_stories=None, can_edit_stories=None, + can_delete_stories=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -1018,6 +1019,12 @@ def promote_chat_member( payload['can_manage_video_chats'] = can_manage_video_chats if can_manage_topics is not None: payload['can_manage_topics'] = can_manage_topics + if can_post_stories is not None: + payload['can_post_stories'] = can_post_stories + if can_edit_stories is not None: + payload['can_edit_stories'] = can_edit_stories + if can_delete_stories is not None: + payload['can_delete_stories'] = can_delete_stories return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 91d0709c6..8d833923f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3921,7 +3921,10 @@ async def promote_chat_member( can_manage_chat: Optional[bool]=None, can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None, - can_manage_topics: Optional[bool]=None) -> bool: + can_manage_topics: Optional[bool]=None, + can_post_stories: Optional[bool]=None, + can_edit_stories: Optional[bool]=None, + can_delete_stories: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -3982,6 +3985,15 @@ async def promote_chat_member( and reopen forum topics, supergroups only :type can_manage_topics: :obj:`bool` + :param can_post_stories: Pass True if the administrator can create the channel's stories + :type can_post_stories: :obj:`bool` + + :param can_edit_stories: Pass True if the administrator can edit the channel's stories + :type can_edit_stories: :obj:`bool` + + :param can_delete_stories: Pass True if the administrator can delete the channel's stories + :type can_delete_stories: :obj:`bool` + :return: True on success. :rtype: :obj:`bool` """ @@ -3995,7 +4007,8 @@ async def promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics) + is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics, + can_post_stories, can_edit_stories, can_delete_stories) async def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 894b62be5..526166eec 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -983,7 +983,8 @@ async def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, - is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None, can_manage_topics=None): + is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None, can_manage_topics=None, + can_post_stories=None, can_edit_stories=None, can_delete_stories=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -1010,6 +1011,12 @@ async def promote_chat_member( payload['can_manage_video_chats'] = can_manage_video_chats if can_manage_topics is not None: payload['can_manage_topics'] = can_manage_topics + if can_post_stories is not None: + payload['can_post_stories'] = can_post_stories + if can_edit_stories is not None: + payload['can_edit_stories'] = can_edit_stories + if can_delete_stories is not None: + payload['can_delete_stories'] = can_delete_stories return await _process_request(token, method_url, params=payload, method='post') From 1565bc05f733cb68287b39f5591d55bd042fae6d Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 22 Sep 2023 23:45:46 +0400 Subject: [PATCH 1363/1808] Added the fields from_request and from_attachment_menu to the class WriteAccessAllowed. --- telebot/types.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 6e49a0cb0..73efd5592 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7261,7 +7261,7 @@ def to_dict(self): json_dict['can_edit_stories'] = self.can_edit_stories if self.can_delete_stories is not None: json_dict['can_delete_stories'] = self.can_delete_stories - + return json_dict def to_json(self): @@ -7482,13 +7482,21 @@ def __init__(self, message_thread_id: int, name: str, icon_color: int, icon_cust class WriteAccessAllowed(JsonDeserializable): """ - This object represents a service message about a user allowed to post messages in the chat. - Currently holds no information. + This object represents a service message about a user allowing a bot to write + messages after adding it to the attachment menu, launching a Web App from a link, + or accepting an explicit request from a Web App sent by the method requestWriteAccess. Telegram documentation: https://core.telegram.org/bots/api#writeaccessallowed + :param from_request: Optional. True, if the access was granted after the user accepted an + explicit request from a Web App sent by the method requestWriteAccess + :type from_request: :obj:`bool` + :param web_app_name: Optional. Name of the Web App which was launched from a link :type web_app_name: :obj:`str` + + :param from_attachment_menu: Optional. True, if the access was granted when the bot was added to the attachment or side menu + :type from_attachment_menu: :obj:`bool` """ @classmethod def de_json(cls, json_string): @@ -7497,8 +7505,10 @@ def de_json(cls, json_string): return cls(**obj) - def __init__(self, web_app_name: str) -> None: + def __init__(self, from_request: Optional[bool]=None, web_app_name: Optional[str]=None, from_attachment_menu: Optional[bool]=None) -> None: self.web_app_name: str = web_app_name + self.from_request: bool = from_request + self.from_attachment_menu: bool = from_attachment_menu From 9d36e4c49f399e234236e3253a67dc4204fc17ae Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 24 Sep 2023 11:23:59 +0300 Subject: [PATCH 1364/1808] Bump version to 4.14.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ed241d5ee..499e7d3b5 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.13.0' +release = '4.14.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 68dfd5046..99a734c84 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.13.0' +__version__ = '4.14.0' From 94913a076a058821b2ba326739c1f0ba98e22a13 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 3 Oct 2023 12:20:54 +0300 Subject: [PATCH 1365/1808] Fix protect_content hint --- telebot/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3e0258a4c..cb145c664 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1517,7 +1517,7 @@ def send_message( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param protect_content: If True, the message content will be hidden for all users except for the target user + :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` :param reply_to_message_id: If the message is a reply, ID of the original message @@ -1733,7 +1733,7 @@ def send_dice( :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding + :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` :param message_thread_id: Identifier of a message thread, in which the message will be sent @@ -3942,7 +3942,7 @@ def send_game( :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified replied-to messages is not found. :type allow_sending_without_reply: :obj:`bool` - :param protect_content: Pass True, if content of the message needs to be protected from being viewed by the bot. + :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` :param message_thread_id: The identifier of a message thread, in which the game message will be sent. From c2869fcf5a2b1b4abad10f2168a4fdcf8f7ec476 Mon Sep 17 00:00:00 2001 From: alexsuhor <76397241+alexsuhor@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:38:26 +0500 Subject: [PATCH 1366/1808] Update README.md should have to functions -> should have two functions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dc98ea5b..f545ccb50 100644 --- a/README.md +++ b/README.md @@ -374,7 +374,7 @@ class Middleware(BaseMiddleware): if exception: # check for exception print(exception) ``` -Class-based middleware should have to functions: post and pre process. +Class-based middleware should have two functions: post and pre process. So, as you can see, class-based middlewares work before and after handler execution. For more, check out in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/middleware/class_based) From aacaf5b311024efcbf944dfe860baf12770dfbaf Mon Sep 17 00:00:00 2001 From: Arash Nemat Zadeh Date: Sat, 28 Oct 2023 21:12:04 +0330 Subject: [PATCH 1367/1808] adding QR-Code Generator bot to the list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f545ccb50..1f447e7e8 100644 --- a/README.md +++ b/README.md @@ -892,5 +892,6 @@ Here are some examples of template: * [CalendarIT Bot](https://t.me/calendarit_bot) ([source](https://github.com/codebyzen/CalendarIT_Telegram_Bot))by [CodeByZen](https://github.com/codebyzen). A simple, but extensible Python Telegram bot, can post acquainted with what is happening today, tomorrow or what happened 20 years ago to channel. * [DownloadMusicBOT](https://github.com/fcoagz/DownloadMusicBOT) by *Francisco Griman* - It is a simple bot that downloads audio from YouTube videos on Telegram. * [AwesomeChatGPTBot](https://github.com/Kourva/AwesomeChatGPTBot) - Simple ChatGTP-3.5 bot. It is FREE and can remember chat history for a while With pre-defined roles! +* [QR-Code For You Bot](https://t.me/qrcode_for_you_bot) ([source](https://github.com/arashnm80/qrcode-for-you-bot)) by [Arashnm80](https://github.com/arashnm80). Telegram qrcode generator bot created with pyhton and telebot. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From ce48dd9c16c5c3eb3b8d5458f3657ca36c97dd05 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 29 Oct 2023 17:45:11 +0300 Subject: [PATCH 1368/1808] Sending InputFile fix send_document with InputFile and visible_file_name fails to be sent. --- telebot/apihelper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 902d6e5ca..bfee49967 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -94,7 +94,9 @@ def _make_request(token, method_name, method='get', params=None, files=None): for key, value in files_copy.items(): if isinstance(value, types.InputFile): files[key] = value.file - + elif isinstance(value, tuple) and (len(value) == 2) and isinstance(value[1], types.InputFile): + files[key] = (value[0], value[1].file) + if files and format_header_param: fields.format_header_param = _no_encode(format_header_param) From 9c569e1d29121bfaeb500a04c08c5bfd2fe49d75 Mon Sep 17 00:00:00 2001 From: Arash Nemat Zadeh Date: Thu, 9 Nov 2023 00:27:04 +0330 Subject: [PATCH 1369/1808] add instagram downloader to bots list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1f447e7e8..0262e781d 100644 --- a/README.md +++ b/README.md @@ -893,5 +893,6 @@ Here are some examples of template: * [DownloadMusicBOT](https://github.com/fcoagz/DownloadMusicBOT) by *Francisco Griman* - It is a simple bot that downloads audio from YouTube videos on Telegram. * [AwesomeChatGPTBot](https://github.com/Kourva/AwesomeChatGPTBot) - Simple ChatGTP-3.5 bot. It is FREE and can remember chat history for a while With pre-defined roles! * [QR-Code For You Bot](https://t.me/qrcode_for_you_bot) ([source](https://github.com/arashnm80/qrcode-for-you-bot)) by [Arashnm80](https://github.com/arashnm80). Telegram qrcode generator bot created with pyhton and telebot. +* [Best Instagram Downloader Bot](https://t.me/Best_Instagram_Downloader_Bot) ([source](https://github.com/arashnm80/best-instagram-downloader)) by [Arashnm80](https://github.com/arashnm80). Free and open source telegram bot to download posts and reels from Instagram. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From a1bb69540cf8abc47e1f98d973ec3e65f0ea6bcd Mon Sep 17 00:00:00 2001 From: Alexander Osipov Date: Wed, 8 Nov 2023 16:40:14 +0300 Subject: [PATCH 1370/1808] Fix ExceptionHandler handle() method as async for async telebot issue https://github.com/eternnoir/pyTelegramBotAPI/issues/2070 --- .../asynchronous_telebot/exception_handler.py | 19 ++++------ telebot/__init__.py | 37 +++++++------------ telebot/async_telebot.py | 35 ++++++++---------- 3 files changed, 37 insertions(+), 54 deletions(-) diff --git a/examples/asynchronous_telebot/exception_handler.py b/examples/asynchronous_telebot/exception_handler.py index b37f94508..cbf1540a9 100644 --- a/examples/asynchronous_telebot/exception_handler.py +++ b/examples/asynchronous_telebot/exception_handler.py @@ -1,28 +1,25 @@ +import logging import telebot -from telebot.async_telebot import AsyncTeleBot - - -import logging +from telebot.async_telebot import AsyncTeleBot, ExceptionHandler logger = telebot.logger -telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. +telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console. -class ExceptionHandler(telebot.ExceptionHandler): - def handle(self, exception): - logger.error(exception) -bot = AsyncTeleBot('TOKEN',exception_handler=ExceptionHandler()) +class MyExceptionHandler(ExceptionHandler): + async def handle(self, exception): + logger.error(exception) +bot = AsyncTeleBot('TOKEN', exception_handler=MyExceptionHandler()) @bot.message_handler(commands=['photo']) async def photo_send(message: telebot.types.Message): await bot.send_message(message.chat.id, 'Hi, this is an example of exception handlers.') - raise Exception('test') # Exception goes to ExceptionHandler if it is set + raise Exception('test') # Exception goes to ExceptionHandler if it is set - import asyncio asyncio.run(bot.polling()) diff --git a/telebot/__init__.py b/telebot/__init__.py index cb145c664..7d06cf90d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1046,6 +1046,12 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F self.__non_threaded_polling(non_stop=non_stop, interval=interval, timeout=timeout, long_polling_timeout=long_polling_timeout, logger_level=logger_level, allowed_updates=allowed_updates) + def _handle_exception(self, exception: Exception) -> bool: + if self.exception_handler is None: + return False + + handled = self.exception_handler.handle(exception) + return handled def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, long_polling_timeout = None, logger_level=logging.ERROR, allowed_updates=None): @@ -1074,10 +1080,7 @@ def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, lon self.worker_pool.raise_exceptions() error_interval = 0.25 except apihelper.ApiException as e: - if self.exception_handler is not None: - handled = self.exception_handler.handle(e) - else: - handled = False + handled = self._handle_exception(e) if not handled: if logger_level and logger_level >= logging.ERROR: logger.error("Threaded polling exception: %s", str(e)) @@ -1107,10 +1110,7 @@ def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, lon self.__stop_polling.set() break except Exception as e: - if self.exception_handler is not None: - handled = self.exception_handler.handle(e) - else: - handled = False + handled = self._handle_exception(e) if not handled: polling_thread.stop() polling_thread.clear_exceptions() #* @@ -1144,11 +1144,7 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ self.__retrieve_updates(timeout, long_polling_timeout, allowed_updates=allowed_updates) error_interval = 0.25 except apihelper.ApiException as e: - if self.exception_handler is not None: - handled = self.exception_handler.handle(e) - else: - handled = False - + handled = self._handle_exception(e) if not handled: if logger_level and logger_level >= logging.ERROR: logger.error("Polling exception: %s", str(e)) @@ -1171,10 +1167,7 @@ def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_ self.__stop_polling.set() break except Exception as e: - if self.exception_handler is not None: - handled = self.exception_handler.handle(e) - else: - handled = False + handled = self._handle_exception(e) if not handled: raise e else: @@ -1190,10 +1183,7 @@ def _exec_task(self, task, *args, **kwargs): try: task(*args, **kwargs) except Exception as e: - if self.exception_handler is not None: - handled = self.exception_handler.handle(e) - else: - handled = False + handled = self._handle_exception(e) if not handled: raise e @@ -6858,9 +6848,8 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty break except Exception as e: handler_error = e - if self.exception_handler: - self.exception_handler.handle(e) - else: + handled = self._handle_exception(e) + if not handled: logger.error(str(e)) logger.debug("Exception traceback:\n%s", traceback.format_exc()) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8d833923f..cd71abeac 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -58,7 +58,7 @@ class ExceptionHandler: """ # noinspection PyMethodMayBeStatic,PyUnusedLocal - def handle(self, exception): + async def handle(self, exception): return False @@ -368,6 +368,16 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option if logger_level and logger_level >= logging.INFO: logger.error("Break infinity polling") + async def _handle_exception(self, exception: Exception) -> bool: + if self.exception_handler is None: + return False + + if iscoroutinefunction(self.exception_handler.handle): + handled = await self.exception_handler.handle(exception) + else: + handled = self.exception_handler.handle(exception) # noqa + return handled + async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, request_timeout: int=None, allowed_updates: Optional[List[str]]=None): """ @@ -415,11 +425,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: except asyncio.CancelledError: return except asyncio_helper.RequestTimeout as e: - handled = False - if self.exception_handler: - self.exception_handler.handle(e) - handled = True - + handled = await self._handle_exception(e) if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) logger.debug(traceback.format_exc()) @@ -430,11 +436,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: else: return except asyncio_helper.ApiException as e: - handled = False - if self.exception_handler: - self.exception_handler.handle(e) - handled = True - + handled = await self._handle_exception(e) if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) logger.debug(traceback.format_exc()) @@ -444,11 +446,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: else: break except Exception as e: - handled = False - if self.exception_handler: - self.exception_handler.handle(e) - handled = True - + handled = await self._handle_exception(e) if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) logger.debug(traceback.format_exc()) @@ -545,9 +543,8 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up break except Exception as e: handler_error = e - if self.exception_handler: - self.exception_handler.handle(e) - else: + handled = await self._handle_exception(e) + if not handled: logger.error(str(e)) logger.debug("Exception traceback:\n%s", traceback.format_exc()) From 62e195aae46c0df9f45ba4439323e5cfa0fea619 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 19 Nov 2023 11:23:12 +0300 Subject: [PATCH 1371/1808] Some code clearing --- telebot/__init__.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7d06cf90d..ac92a38c4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -175,14 +175,16 @@ def __init__( self.token = token self.skip_pending = skip_pending # backward compatibility self.last_update_id = last_update_id - - # propertys + + # properties self.suppress_middleware_excepions = suppress_middleware_excepions self.parse_mode = parse_mode self.disable_web_page_preview = disable_web_page_preview self.disable_notification = disable_notification self.protect_content = protect_content self.allow_sending_without_reply = allow_sending_without_reply + self.webhook_listener = None + self._user = None # logs-related if colorful_logs: @@ -269,7 +271,7 @@ def user(self) -> types.User: :return: Bot's info. :rtype: :class:`telebot.types.User` """ - if not hasattr(self, "_user"): + if not self._user: self._user = self.get_me() return self._user @@ -1055,7 +1057,7 @@ def _handle_exception(self, exception: Exception) -> bool: def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, long_polling_timeout = None, logger_level=logging.ERROR, allowed_updates=None): - if not(logger_level) or (logger_level < logging.INFO): + if (not logger_level) or (logger_level < logging.INFO): warning = "\n Warning: this message appearance will be changed. Set logger_level=logging.INFO to continue seeing it." else: warning = "" @@ -1130,7 +1132,7 @@ def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, lon def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None, logger_level=logging.ERROR, allowed_updates=None): - if not(logger_level) or (logger_level < logging.INFO): + if (not logger_level) or (logger_level < logging.INFO): warning = "\n Warning: this message appearance will be changed. Set logger_level=logging.INFO to continue seeing it." else: warning = "" @@ -2076,7 +2078,7 @@ def send_document( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if data and not(document): + if data and (not document): # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "document" instead.') document = data @@ -2157,7 +2159,7 @@ def send_sticker( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if data and not(sticker): + if data and (not sticker): # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data @@ -2265,7 +2267,7 @@ def send_video( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if data and not(video): + if data and (not video): # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "video" instead.') video = data @@ -6872,7 +6874,7 @@ def _notify_command_handlers(self, handlers, new_messages, update_type): :param update_type: handler/update type (Update fields) :return: """ - if not(handlers) and not(self.use_class_middlewares): + if (not handlers) and (not self.use_class_middlewares): return if self.use_class_middlewares: From 5fd60380db0de83049d35c66ce1794b988ed6471 Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Mon, 20 Nov 2023 21:44:50 +0400 Subject: [PATCH 1372/1808] Fix telebot.types.Message.html_text may contain unescaped HTML characters. --- telebot/types.py | 4 ++-- tests/test_types.py | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 73efd5592..221ba2fab 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1294,7 +1294,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso def __html_text(self, text, entities): """ Author: @sviat9440 - Updaters: @badiboy + Updaters: @badiboy, @EgorKhabarov Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" .. code-block:: python3 @@ -1314,7 +1314,7 @@ def __html_text(self, text, entities): """ if not entities: - return text + return text.replace("&", "&").replace("<", "<").replace(">", ">") _subs = { "bold": "{text}", diff --git a/tests/test_types.py b/tests/test_types.py index 138c36b8c..06bcae1ee 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -295,7 +295,15 @@ def test_message_entity(): message_4 = types.Update.de_json(sample_string_4).message assert message_4.html_text == 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa😋😋' - + + sample_string_5 = r'{"update_id":934522166,"message":{"message_id":1374526,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179716,"text":"b b i","entities":[{"offset":0,"length":1,"type":"bold"}]}}' + message_5 = types.Update.de_json(sample_string_5).message + assert message_5.html_text == "b <b>b</b> <i>i</i>" + + + sample_string_6 = r'{"update_id":934522166,"message":{"message_id":1374526,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179716,"text":"b i"}}' + message_6 = types.Update.de_json(sample_string_6).message + assert message_6.html_text == "<b>b</b> <i>i</i>" From 3f3d41159276ba08ff00b4d8e0d212525f15177b Mon Sep 17 00:00:00 2001 From: Ali Toosi Date: Mon, 4 Dec 2023 15:37:57 +1100 Subject: [PATCH 1373/1808] Add new content_types in docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0262e781d..639bfe00b 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)). The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings: -`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`, `web_app_data`. +`text`, `audio`, `document`, `animation`, `game`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `venue`, `dice`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`, `invoice`, `successful_payment`, `connected_website`, `poll`, `passport_data`, `proximity_alert_triggered`, `video_chat_scheduled`, `video_chat_started`, `video_chat_ended`, `video_chat_participants_invited`, `web_app_data`, `message_auto_delete_timer_changed`, `forum_topic_created`, `forum_topic_closed`, `forum_topic_reopened`, `forum_topic_edited`, `general_forum_topic_hidden`, `general_forum_topic_unhidden`, `write_access_allowed`, `user_shared`, `chat_shared`, `story`. You can use some types in one function. Example: From 0ecfb19721de4e5810a329bc13c6f63d13996d13 Mon Sep 17 00:00:00 2001 From: Matvey Aslandukov Date: Sat, 9 Dec 2023 12:49:26 +0200 Subject: [PATCH 1374/1808] Specified correct return type --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ac92a38c4..4a4324507 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1645,8 +1645,8 @@ def copy_message( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` - :return: On success, the sent Message is returned. - :rtype: :class:`telebot.types.Message` + :return: On success, the MessageId of the sent message is returned. + :rtype: :class:`telebot.types.MessageID` """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification parse_mode = self.parse_mode if (parse_mode is None) else parse_mode From 7a8559c314bbb2a5aa877a6796345bdfbcd9e386 Mon Sep 17 00:00:00 2001 From: Matvey Aslandukov Date: Sat, 9 Dec 2023 12:57:41 +0200 Subject: [PATCH 1375/1808] Specified correct return type in the async_telebot --- telebot/async_telebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index cd71abeac..92748a9fc 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2521,8 +2521,8 @@ async def copy_message( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` - :return: On success, the sent Message is returned. - :rtype: :class:`telebot.types.Message` + :return: On success, the MessageId of the sent message is returned. + :rtype: :class:`telebot.types.MessageID` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification From 3d4a5d5d3b7e5b5b5dd632f2bd4113aba5c9cc8b Mon Sep 17 00:00:00 2001 From: Simatwa Date: Mon, 18 Dec 2023 17:35:03 +0300 Subject: [PATCH 1376/1808] Add telegram-chatbots --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 639bfe00b..9d49ff2e7 100644 --- a/README.md +++ b/README.md @@ -894,5 +894,6 @@ Here are some examples of template: * [AwesomeChatGPTBot](https://github.com/Kourva/AwesomeChatGPTBot) - Simple ChatGTP-3.5 bot. It is FREE and can remember chat history for a while With pre-defined roles! * [QR-Code For You Bot](https://t.me/qrcode_for_you_bot) ([source](https://github.com/arashnm80/qrcode-for-you-bot)) by [Arashnm80](https://github.com/arashnm80). Telegram qrcode generator bot created with pyhton and telebot. * [Best Instagram Downloader Bot](https://t.me/Best_Instagram_Downloader_Bot) ([source](https://github.com/arashnm80/best-instagram-downloader)) by [Arashnm80](https://github.com/arashnm80). Free and open source telegram bot to download posts and reels from Instagram. +* [Personal bot for ChatGPT & Bard](https://github.com/Simatwa/pyTelegramBotAPI.git) by [Simatwa](https://github.com/Simatwa/telegram-chatbots). Chat with ChatGPT & Bard on the go. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 4f8866818e2cf967d22e24ae2297c062e4780dff Mon Sep 17 00:00:00 2001 From: anonymousdouble <112695649+anonymousdouble@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:15:22 +1100 Subject: [PATCH 1377/1808] Update test_telebot.py refactor with With statement to open file to make code more Pythonic --- tests/test_telebot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 0caff9e86..280a19f7c 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -175,10 +175,10 @@ def test_send_video(self): assert ret_msg.message_id def test_send_video_dis_noti(self): - file_data = open('./test_data/test_video.mp4', 'rb') - tb = telebot.TeleBot(TOKEN) - ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True) - assert ret_msg.message_id + with open('./test_data/test_video.mp4', 'rb') as file_data: + tb = telebot.TeleBot(TOKEN) + ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True) + assert ret_msg.message_id def test_send_video_more_params(self): file_data = open('./test_data/test_video.mp4', 'rb') From 3145269043955c5ed30193d29d513b1096ba0d00 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 20 Dec 2023 00:45:48 +0300 Subject: [PATCH 1378/1808] Bump version --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 499e7d3b5..321ff4966 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.14.0' +release = '4.14.1' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 99a734c84..d06b2f7a7 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.14.0' +__version__ = '4.14.1' From 430e3273c86d3c0be48d0276b1f159507e89660b Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 18:25:43 +0500 Subject: [PATCH 1379/1808] Start of bot API Update - 7.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d49ff2e7..289e38d7a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 6.9! +##

Supported Bot API version: 7.0!

Official documentation

Official ru documentation

From 134f45b508aed2ab047a5204a8b0a4071b15b541 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 19:02:45 +0500 Subject: [PATCH 1380/1808] Added the classes ReactionTypeEmoji and ReactionTypeCustomEmoji representing different types of reaction. --- telebot/types.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 73efd5592..8c095ef40 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7835,3 +7835,90 @@ def de_json(cls, json_string): def __init__(self) -> None: pass +# base class +class ReactionType(JsonDeserializable, Dictionaryable, JsonSerializable): + """ + This object represents a reaction type. + + Telegram documentation: https://core.telegram.org/bots/api#reactiontype + + :param type: Type of the reaction + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`ReactionType` + """ + + @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: str) -> None: + self.type: str = type + + def to_dict(self) -> dict: + json_dict = { + 'type': self.type + } + + return json_dict + + def to_json(self) -> str: + return json.dumps(self.to_dict()) + + +class ReactionTypeEmoji(ReactionType): + """ + This object represents an emoji reaction type. + + Telegram documentation: https://core.telegram.org/bots/api#reactiontypeemoji + + :param type: Type of the reaction, must be emoji + :type type: :obj:`str` + + :param emoji: Reaction emoji. List is available on the API doc. + :type emoji: :obj:`str` + + :return: Instance of the class + :rtype: :class:`ReactionTypeEmoji` + """ + + def __init__(self, emoji: str) -> None: + super().__init__('emoji') + self.emoji: str = emoji + + def to_dict(self) -> dict: + json_dict = super().to_dict() + json_dict['emoji'] = self.emoji + + return json_dict + + +class ReactionTypeCustomEmoji(ReactionType): + """ + This object represents a custom emoji reaction type. + + Telegram documentation: https://core.telegram.org/bots/api#reactiontypecustomemoji + + :param type: Type of the reaction, must be custom_emoji + :type type: :obj:`str` + + :param custom_emoji: Identifier of the custom emoji + :type custom_emoji: :obj:`str` + + :return: Instance of the class + :rtype: :class:`ReactionTypeCustomEmoji` + """ + + def __init__(self, custom_emoji: str) -> None: + super().__init__('custom_emoji') + self.custom_emoji: str = custom_emoji + + def to_dict(self) -> dict: + json_dict = super().to_dict() + json_dict['custom_emoji'] = self.custom_emoji + + return json_dict \ No newline at end of file From 974395ec9bc15a643afeafa3fca9ff14762df675 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 19:23:47 +0500 Subject: [PATCH 1381/1808] New handler: message_reaction_handler(untested) --- telebot/__init__.py | 66 ++++++++++++++++++++++++++++++++++++++++ telebot/async_telebot.py | 66 ++++++++++++++++++++++++++++++++++++++++ telebot/types.py | 59 +++++++++++++++++++++++++++++++++-- telebot/util.py | 2 +- 4 files changed, 190 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4a4324507..10cce6761 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -217,6 +217,7 @@ def __init__( self.edited_message_handlers = [] self.channel_post_handlers = [] self.edited_channel_post_handlers = [] + self.message_reaction_handlers = [] self.inline_handlers = [] self.chosen_inline_handlers = [] self.callback_query_handlers = [] @@ -670,6 +671,7 @@ def process_new_updates(self, updates: List[types.Update]): new_edited_messages = None new_channel_posts = None new_edited_channel_posts = None + new_message_reactions = None new_inline_queries = None new_chosen_inline_results = None new_callback_queries = None @@ -737,6 +739,9 @@ def process_new_updates(self, updates: List[types.Update]): if update.chat_join_request: if new_chat_join_request is None: new_chat_join_request = [] new_chat_join_request.append(update.chat_join_request) + if update.message_reactions: + if new_message_reactions is None: new_message_reactions = [] + new_message_reactions.append(update.message_reaction) if new_messages: self.process_new_messages(new_messages) @@ -766,6 +771,8 @@ def process_new_updates(self, updates: List[types.Update]): self.process_new_chat_member(new_chat_members) if new_chat_join_request: self.process_new_chat_join_request(new_chat_join_request) + if new_message_reactions: + self.process_new_message_reaction(new_message_reactions) def process_new_messages(self, new_messages): """ @@ -794,6 +801,12 @@ def process_new_edited_channel_posts(self, edited_channel_post): """ self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') + def process_new_message_reaction(self, message_reactions): + """ + :meta private: + """ + self._notify_command_handlers(self.message_reaction_handlers, message_reactions, 'message_reaction') + def process_new_inline_query(self, new_inline_queries): """ :meta private: @@ -6174,6 +6187,59 @@ def register_edited_channel_post_handler(self, callback: Callable, content_types **kwargs) self.add_edited_channel_post_handler(handler_dict) + + def message_reaction_handler(self, func, **kwargs): + """ + Handles new incoming message reaction. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_message_reaction_handler(handler_dict) + return handler + + return decorator + + def add_message_reaction_handler(self, handler_dict): + """ + Adds message reaction handler + Note that you should use register_message_reaction_handler to add message_reaction_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.message_reaction_handlers.append(handler_dict) + + def register_message_reaction_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers message reaction handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_message_reaction_handler(handler_dict) + + def inline_handler(self, func, **kwargs): """ Handles new incoming inline query. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 92748a9fc..2c1ff2f3d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -159,6 +159,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.edited_message_handlers = [] self.channel_post_handlers = [] self.edited_channel_post_handlers = [] + self.message_reaction_handlers = [] self.inline_handlers = [] self.chosen_inline_handlers = [] self.callback_query_handlers = [] @@ -576,6 +577,7 @@ async def process_new_updates(self, updates: List[types.Update]): new_edited_messages = None new_channel_posts = None new_edited_channel_posts = None + new_message_reactions = None new_inline_queries = None new_chosen_inline_results = None new_callback_queries = None @@ -630,6 +632,9 @@ async def process_new_updates(self, updates: List[types.Update]): if update.chat_join_request: if chat_join_request is None: chat_join_request = [] chat_join_request.append(update.chat_join_request) + if update.message_reaction: + if new_message_reactions is None: new_message_reactions = [] + new_message_reactions.append(update.message_reaction) if new_messages: await self.process_new_messages(new_messages) @@ -659,6 +664,8 @@ async def process_new_updates(self, updates: List[types.Update]): await self.process_new_chat_member(new_chat_members) if chat_join_request: await self.process_chat_join_request(chat_join_request) + if new_message_reactions: + await self.process_new_message_reaction(new_message_reactions) async def process_new_messages(self, new_messages): """ @@ -685,6 +692,12 @@ async def process_new_edited_channel_posts(self, edited_channel_post): """ await self._process_updates(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') + async def process_new_message_reaction(self, message_reaction): + """ + :meta private: + """ + await self._process_updates(self.message_reaction_handlers, message_reaction, 'message_reaction') + async def process_new_inline_query(self, new_inline_queries): """ :meta private: @@ -1346,6 +1359,59 @@ def register_edited_channel_post_handler(self, callback: Callable[[Any], Awaitab **kwargs) self.add_edited_channel_post_handler(handler_dict) + def messsage_reaction_handler(self, func, **kwargs): + """ + Handles new incoming message reaction. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_message_reaction_handler(handler_dict) + return handler + + return decorator + + def add_message_reaction_handler(self, handler_dict): + """ + Adds message reaction handler. + Note that you should use register_message_reaction_handler to add message_reaction_handler. + + :meta private: + + :param handler_dict: + :return: + """ + self.message_reaction_handlers.append(handler_dict) + + def register_message_reaction_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers message reaction handler. + + :param callback: function to be called + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_message_reaction_handler(handler_dict) + + def inline_handler(self, func, **kwargs): """ Handles new incoming inline query. diff --git a/telebot/types.py b/telebot/types.py index 8c095ef40..976643e92 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -127,6 +127,10 @@ class Update(JsonDeserializable): :param edited_channel_post: Optional. New version of a channel post that is known to the bot and was edited :type edited_channel_post: :class:`telebot.types.Message` + :param message_reaction: Optional. A reaction to a message was changed by a user. The bot must be an administrator in the chat + and must explicitly specify "message_reaction" in the list of allowed_updates to receive these updates. The update isn't received for reactions set by bots. + :type message_reaction: :class:`telebot.types.MessageReactionUpdated` + :param inline_query: Optional. New incoming inline query :type inline_query: :class:`telebot.types.InlineQuery` @@ -188,13 +192,14 @@ def de_json(cls, json_string): my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member')) chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) + message_reaction = MessageReactionUpdated.de_json(obj.get('message_reaction')) 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) 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): + my_chat_member, chat_member, chat_join_request, message_reaction): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -210,6 +215,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.my_chat_member = my_chat_member self.chat_member = chat_member self.chat_join_request = chat_join_request + self.message_reaction = message_reaction class ChatMemberUpdated(JsonDeserializable): @@ -7921,4 +7927,53 @@ def to_dict(self) -> dict: json_dict = super().to_dict() json_dict['custom_emoji'] = self.custom_emoji - return json_dict \ No newline at end of file + return json_dict + + +class MessageReactionUpdated(JsonDeserializable): + """ + This object represents a service message about a change in the list of the current user's reactions to a message. + + Telegram documentation: https://core.telegram.org/bots/api#messagereactionupdated + + :param chat: The chat containing the message the user reacted to + :type chat: :class:`telebot.types.Chat` + + :param message_id: Unique identifier of the message inside the chat + :type message_id: :obj:`int` + + :param user: Optional. The user that changed the reaction, if the user isn't anonymous + :type user: :class:`telebot.types.User` + + :param actor_chat: Optional. The chat on behalf of which the reaction was changed, if the user is anonymous + :type actor_chat: :class:`telebot.types.Chat` + + :param date: Date of the change in Unix time + :type date: :obj:`int` + + :param old_reaction: Previous list of reaction types that were set by the user + :type old_reaction: :obj:`list` of :class:`ReactionType` + + :param new_reaction: New list of reaction types that have been set by the user + :type new_reaction: :obj:`list` of :class:`ReactionType` + + :return: Instance of the class + :rtype: :class:`MessageReactionUpdated` + """ + + @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, chat: Chat, message_id: int, date: int, old_reaction: List[ReactionType], new_reaction: List[ReactionType], + user: Optional[User]=None, actor_chat: Optional[Chat]=None) -> None: + self.chat: Chat = chat + self.message_id: int = message_id + self.user: Optional[User] = user + self.actor_chat: Optional[Chat] = actor_chat + self.date: int = date + self.old_reaction: List[ReactionType] = old_reaction + self.new_reaction: List[ReactionType] = new_reaction \ No newline at end of file diff --git a/telebot/util.py b/telebot/util.py index 88fb4db96..1c0d06c5a 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -45,7 +45,7 @@ update_types = [ "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", + "chat_join_request", "message_reaction" ] From 8fee13649079014242826bb11144fbc51ab31f7d Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 19:28:47 +0500 Subject: [PATCH 1382/1808] Fix tests for the new handler --- tests/test_handler_backends.py | 6 ++++-- tests/test_telebot.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index 21cf8f9b2..52b84d352 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -65,9 +65,10 @@ def update_type(message): my_chat_member = None chat_member = None chat_join_request = None + message_reaction = None return types.Update(1001234038283, 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) + my_chat_member, chat_member, chat_join_request, message_reaction) @pytest.fixture() @@ -85,9 +86,10 @@ def reply_to_message_update_type(reply_to_message): my_chat_member = None chat_member = None chat_join_request = None + message_reaction = None return types.Update(1001234038284, reply_to_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) + poll, poll_answer, my_chat_member, chat_member, chat_join_request, message_reaction) def next_handler(message): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 280a19f7c..4b7b78a61 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -542,9 +542,10 @@ def create_message_update(text): my_chat_member = None chat_member = None chat_join_request = None + message_reaction = None return types.Update(-1001234038283, 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) + my_chat_member, chat_member, chat_join_request, message_reaction) def test_is_string_unicode(self): s1 = u'string' From 97e0d4e180e36408af5aab24f1c1da68c279541e Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 19:36:52 +0500 Subject: [PATCH 1383/1808] Fix tests #2 --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 976643e92..a1257e792 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -195,7 +195,7 @@ def de_json(cls, json_string): message_reaction = MessageReactionUpdated.de_json(obj.get('message_reaction')) 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) + my_chat_member, chat_member, chat_join_request, message_reaction) 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, From 2fb2e30eeaf83febe6af0dceb8cb11e274e90436 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 19:38:11 +0500 Subject: [PATCH 1384/1808] Fix minor issue for tests #3 --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 10cce6761..f9c1c7d52 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -739,7 +739,7 @@ def process_new_updates(self, updates: List[types.Update]): if update.chat_join_request: if new_chat_join_request is None: new_chat_join_request = [] new_chat_join_request.append(update.chat_join_request) - if update.message_reactions: + if update.message_reaction: if new_message_reactions is None: new_message_reactions = [] new_message_reactions.append(update.message_reaction) From 854c68530eb47ef72e03fdea04be4803d6536400 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 19:53:39 +0500 Subject: [PATCH 1385/1808] Added updates about reaction changes on a message with anonymous reactions, represented by the class MessageReactionCountUpdated and the field message_reaction_count in the class Update. The bot must explicitly allow the update to receive it. --- telebot/__init__.py | 63 ++++++++++++++++++++++++++++++++ telebot/async_telebot.py | 66 ++++++++++++++++++++++++++++++++++ telebot/types.py | 78 ++++++++++++++++++++++++++++++++++++++-- telebot/util.py | 2 +- 4 files changed, 205 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f9c1c7d52..366ad70d1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -218,6 +218,7 @@ def __init__( self.channel_post_handlers = [] self.edited_channel_post_handlers = [] self.message_reaction_handlers = [] + self.message_reaction_count_handlers = [] self.inline_handlers = [] self.chosen_inline_handlers = [] self.callback_query_handlers = [] @@ -672,6 +673,7 @@ def process_new_updates(self, updates: List[types.Update]): new_channel_posts = None new_edited_channel_posts = None new_message_reactions = None + message_reaction_counts = None new_inline_queries = None new_chosen_inline_results = None new_callback_queries = None @@ -742,6 +744,9 @@ def process_new_updates(self, updates: List[types.Update]): if update.message_reaction: if new_message_reactions is None: new_message_reactions = [] new_message_reactions.append(update.message_reaction) + if update.message_reaction_count: + if message_reaction_counts is None: message_reaction_counts = [] + message_reaction_counts.append(update.message_reaction_count) if new_messages: self.process_new_messages(new_messages) @@ -773,6 +778,8 @@ def process_new_updates(self, updates: List[types.Update]): self.process_new_chat_join_request(new_chat_join_request) if new_message_reactions: self.process_new_message_reaction(new_message_reactions) + if message_reaction_counts: + self.process_new_message_reaction_count(message_reaction_counts) def process_new_messages(self, new_messages): """ @@ -806,6 +813,12 @@ def process_new_message_reaction(self, message_reactions): :meta private: """ self._notify_command_handlers(self.message_reaction_handlers, message_reactions, 'message_reaction') + + def process_new_message_reaction_count(self, message_reaction_counts): + """ + :meta private: + """ + self._notify_command_handlers(self.message_reaction_count_handlers, message_reaction_counts, 'message_reaction_count') def process_new_inline_query(self, new_inline_queries): """ @@ -6239,6 +6252,56 @@ def register_message_reaction_handler(self, callback: Callable, func: Callable, handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_message_reaction_handler(handler_dict) + def message_reaction_count_handler(self, func, **kwargs): + """ + Handles new incoming message reaction count. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_message_reaction_count_handler(handler_dict) + return handler + + return decorator + + def add_message_reaction_count_handler(self, handler_dict): + """ + Adds message reaction count handler + Note that you should use register_message_reaction_count_handler to add message_reaction_count_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.message_reaction_count_handlers.append(handler_dict) + + def register_message_reaction_count_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers message reaction count handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_message_reaction_count_handler(handler_dict) def inline_handler(self, func, **kwargs): """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2c1ff2f3d..a6fdf5cd6 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -160,6 +160,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.channel_post_handlers = [] self.edited_channel_post_handlers = [] self.message_reaction_handlers = [] + self.message_reaction_count_handlers = [] self.inline_handlers = [] self.chosen_inline_handlers = [] self.callback_query_handlers = [] @@ -578,6 +579,7 @@ async def process_new_updates(self, updates: List[types.Update]): new_channel_posts = None new_edited_channel_posts = None new_message_reactions = None + mew_message_reaction_count_handlers = None new_inline_queries = None new_chosen_inline_results = None new_callback_queries = None @@ -635,6 +637,10 @@ async def process_new_updates(self, updates: List[types.Update]): if update.message_reaction: if new_message_reactions is None: new_message_reactions = [] new_message_reactions.append(update.message_reaction) + if update.message_reaction_count: + if new_message_reaction_count_handlers is None: new_message_reaction_count_handlers = [] + new_message_reaction_count_handlers.append(update.message_reaction_count) + if new_messages: await self.process_new_messages(new_messages) @@ -666,6 +672,8 @@ async def process_new_updates(self, updates: List[types.Update]): await self.process_chat_join_request(chat_join_request) if new_message_reactions: await self.process_new_message_reaction(new_message_reactions) + if new_message_reaction_count_handlers: + await self.process_new_message_reaction_count(new_message_reaction_count_handlers) async def process_new_messages(self, new_messages): """ @@ -698,6 +706,12 @@ async def process_new_message_reaction(self, message_reaction): """ await self._process_updates(self.message_reaction_handlers, message_reaction, 'message_reaction') + async def process_new_message_reaction_count(self, message_reaction_count): + """ + :meta private: + """ + await self._process_updates(self.message_reaction_count_handlers, message_reaction_count, 'message_reaction_count') + async def process_new_inline_query(self, new_inline_queries): """ :meta private: @@ -1411,6 +1425,58 @@ def register_message_reaction_handler(self, callback: Callable[[Any], Awaitable] handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_message_reaction_handler(handler_dict) + def message_reaction_count_handler(self, func, **kwargs): + """ + Handles new incoming message reaction count. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: + """ + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_message_reaction_count_handler(handler_dict) + return handler + + return decorator + + def add_message_reaction_count_handler(self, handler_dict): + """ + Adds message reaction count handler + Note that you should use register_message_reaction_count_handler to add message_reaction_count_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.message_reaction_count_handlers.append(handler_dict) + + def register_message_reaction_count_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers message reaction count handler. + + :param callback: function to be called + :type callback: :obj:`Awaitable` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_message_reaction_count_handler(handler_dict) + def inline_handler(self, func, **kwargs): """ diff --git a/telebot/types.py b/telebot/types.py index a1257e792..72f2558a2 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -131,6 +131,10 @@ class Update(JsonDeserializable): and must explicitly specify "message_reaction" in the list of allowed_updates to receive these updates. The update isn't received for reactions set by bots. :type message_reaction: :class:`telebot.types.MessageReactionUpdated` + :param message_reaction_count: Optional. Reactions to a message with anonymous reactions were changed. The bot must be an administrator in the chat and must explicitly specify + "message_reaction_count" in the list of allowed_updates to receive these updates. + :type message_reaction_count: :class:`telebot.types.MessageReactionCountUpdated` + :param inline_query: Optional. New incoming inline query :type inline_query: :class:`telebot.types.InlineQuery` @@ -193,13 +197,14 @@ def de_json(cls, json_string): chat_member = ChatMemberUpdated.de_json(obj.get('chat_member')) chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) message_reaction = MessageReactionUpdated.de_json(obj.get('message_reaction')) + message_reaction_count = MessageReactionCountUpdated.de_json(obj.get('message_reaction_count')) 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) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count) 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): + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -216,6 +221,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.chat_member = chat_member self.chat_join_request = chat_join_request self.message_reaction = message_reaction + self.message_reaction_count = message_reaction_count class ChatMemberUpdated(JsonDeserializable): @@ -7976,4 +7982,70 @@ def __init__(self, chat: Chat, message_id: int, date: int, old_reaction: List[Re self.actor_chat: Optional[Chat] = actor_chat self.date: int = date self.old_reaction: List[ReactionType] = old_reaction - self.new_reaction: List[ReactionType] = new_reaction \ No newline at end of file + self.new_reaction: List[ReactionType] = new_reaction + + + +class MessageReactionCountUpdated(JsonDeserializable): + """ + This object represents a service message about a change in the list of the current user's reactions to a message. + + Telegram documentation: https://core.telegram.org/bots/api#messagereactioncountupdated + + :param chat: The chat containing the message + :type chat: :class:`telebot.types.Chat` + + :param message_id: Unique message identifier inside the chat + :type message_id: :obj:`int` + + :param date: Date of the change in Unix time + :type date: :obj:`int` + + :param reactions: List of reactions that are present on the message + :type reactions: :obj:`list` of :class:`ReactionCount` + + :return: Instance of the class + :rtype: :class:`MessageReactionCountUpdated` + """ + + @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, chat: Chat, message_id: int, date: int, reactions: List[ReactionCount]) -> None: + self.chat: Chat = chat + self.message_id: int = message_id + self.date: int = date + self.reactions: List[ReactionCount] = reactions + + +class ReactionCount(JsonDeserializable): + """ + This object represents a reaction added to a message along with the number of times it was added. + + Telegram documentation: https://core.telegram.org/bots/api#reactioncount + + :param type: Type of the reaction + :type type: :class:`ReactionType` + + :param total_count: Number of times the reaction was added + :type total_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`ReactionCount` + """ + + @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: ReactionType, total_count: int) -> None: + self.type: ReactionType = type + self.total_count: int = total_count + diff --git a/telebot/util.py b/telebot/util.py index 1c0d06c5a..174239d35 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -45,7 +45,7 @@ update_types = [ "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" + "chat_join_request", "message_reaction", "message_reaction_count" ] From 96b4ced7788b8f397cf7586cd0404d3e1a136bc1 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 19:54:51 +0500 Subject: [PATCH 1386/1808] Fix tests for the new handler --- tests/test_handler_backends.py | 6 ++++-- tests/test_telebot.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index 52b84d352..ef22e8520 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -66,9 +66,10 @@ def update_type(message): chat_member = None chat_join_request = None message_reaction = None + message_reaction_count = None return types.Update(1001234038283, 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) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count) @pytest.fixture() @@ -87,9 +88,10 @@ def reply_to_message_update_type(reply_to_message): chat_member = None chat_join_request = None message_reaction = None + message_reaction_count = None return types.Update(1001234038284, reply_to_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) + poll, poll_answer, my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count) def next_handler(message): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 4b7b78a61..c1f08f8f0 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -543,9 +543,10 @@ def create_message_update(text): chat_member = None chat_join_request = None message_reaction = None + message_reaction_count = None return types.Update(-1001234038283, 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) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count) def test_is_string_unicode(self): s1 = u'string' From b6a0d6caa56db1fba085c18a9b7a2a7c7d7407a5 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 21:14:36 +0500 Subject: [PATCH 1387/1808] Added the method setMessageReaction that allows bots to react to messages. --- telebot/__init__.py | 23 +++++++++++++++++++++++ telebot/apihelper.py | 10 ++++++++++ telebot/async_telebot.py | 24 ++++++++++++++++++++++++ telebot/asyncio_helper.py | 9 +++++++++ 4 files changed, 66 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 366ad70d1..23a95eea4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1329,6 +1329,29 @@ def close(self) -> bool: :return: :obj:`bool` """ return apihelper.close(self.token) + + def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reaction: Optional[List[types.ReactionType]]=None, is_big: Optional[bool]=None) -> bool: + """ + Use this method to set a reaction to a message in a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmessagereaction + + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of the message to set reaction to + :type message_id: :obj:`int` + + :param reaction: New list of reaction types to set on the message. Currently, as non-premium users, bots can set up to one reaction per message. + A custom emoji reaction can be used if it is either already present on the message or explicitly allowed by chat administrators. + :type reaction: :obj:`list` of :class:`telebot.types.ReactionType` + + :param is_big: Pass True to set the reaction with a big animation + :type is_big: :obj:`bool` + + :return: :obj:`bool` + """ + return apihelper.set_message_reaction(self.token, chat_id, message_id, reaction, is_big) def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index bfee49967..8ae65814f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -332,6 +332,16 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None): payload['limit'] = limit return _make_request(token, method_url, params=payload) +def set_message_reaction(token, chat_id, message_id, reaction=None, is_big=None): + method_url = r'setMessageReaction' + payload = {'chat_id': chat_id, 'message_id': message_id} + if reaction: + payload['reaction'] = reaction + if is_big is not None: + payload['is_big'] = is_big + return _make_request(token, method_url, params=payload) + + def get_chat(token, chat_id): method_url = r'getChat' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a6fdf5cd6..0f630ca5d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2297,6 +2297,30 @@ async def get_webhook_info(self, timeout: Optional[int]=None) -> types.WebhookIn result = await asyncio_helper.get_webhook_info(self.token, timeout) return types.WebhookInfo.de_json(result) + async def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reaction: Optional[List[types.ReactionType]]=None, is_big: Optional[bool]=None) -> bool: + """ + Use this method to set a reaction to a message in a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmessagereaction + + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of the message to set reaction to + :type message_id: :obj:`int` + + :param reaction: New list of reaction types to set on the message. Currently, as non-premium users, bots can set up to one reaction per message. + A custom emoji reaction can be used if it is either already present on the message or explicitly allowed by chat administrators. + :type reaction: :obj:`list` of :class:`telebot.types.ReactionType` + + :param is_big: Pass True to set the reaction with a big animation + :type is_big: :obj:`bool` + + :return: :obj:`bool` + """ + result = await asyncio_helper.set_message_reaction(self.token, chat_id, message_id, reaction, is_big) + return result + async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 526166eec..91538abc4 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -318,6 +318,15 @@ async def get_user_profile_photos(token, user_id, offset=None, limit=None): payload['limit'] = limit return await _process_request(token, method_url, params=payload) +async def set_message_reaction(token, chat_id, message_id, reaction=None, is_big=None): + method_url = r'setMessageReaction' + payload = {'chat_id': chat_id, 'message_id': message_id} + if reaction: + payload['reaction'] = reaction + if is_big is not None: + payload['is_big'] = is_big + return await _process_request(token, method_url, params=payload) + async def get_chat(token, chat_id): method_url = r'getChat' From 75764586ce97087d3c56ace1060e6489424707f9 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 21:20:15 +0500 Subject: [PATCH 1388/1808] Added the field available_reactions to the class Chat. --- telebot/types.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 72f2558a2..0f0e5f00e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -551,6 +551,10 @@ class Chat(JsonDeserializable): Returned only in getChat. :type active_usernames: :obj:`list` of :obj:`str` + :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 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` @@ -644,6 +648,8 @@ def de_json(cls, json_string): obj['permissions'] = ChatPermissions.de_json(obj['permissions']) if 'location' in obj: obj['location'] = ChatLocation.de_json(obj['location']) + if 'available_reactions' in obj: + obj['available_reactions'] = [ReactionType(reaction) for reaction in obj['available_reactions']] return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -654,7 +660,8 @@ def __init__(self, id, type, title=None, username=None, first_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, - has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, **kwargs): + has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, + available_reactions=None,**kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -684,6 +691,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.has_hidden_members: bool = has_hidden_members self.has_aggressive_anti_spam_enabled: bool = has_aggressive_anti_spam_enabled self.emoji_status_expiration_date: int = emoji_status_expiration_date + self.available_reactions: List[ReactionType] = available_reactions class MessageID(JsonDeserializable): From ad816f2c3fd8f9cd70c6e4dd5b08585566104a78 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 21:42:30 +0500 Subject: [PATCH 1389/1808] Added the class ExternalReplyInfo and the field external_reply of type ExternalReplyInfo to the class Message, containing information about a message that is replied to by the current message, but can be from another chat or forum topic. --- telebot/types.py | 511 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 0f0e5f00e..700911f50 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8057,3 +8057,514 @@ def __init__(self, type: ReactionType, total_count: int) -> None: self.type: ReactionType = type self.total_count: int = total_count + + +class ExternalReplyInfo(JsonDeserializable): + """ + This object contains information about a message that is being replied to, + which may come from another chat or forum topic. + + Telegram documentation: https://core.telegram.org/bots/api#externalreplyinfo + + :param origin: Origin of the message replied to by the given message + :type origin: :class:`MessageOrigin` + + :param chat: Optional. Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + :type chat: :class:`Chat` + + :param message_id: Optional. Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + :type message_id: :obj:`int` + + :param link_preview_options: Optional. Options used for link preview generation for the original message, if it is a text message + :type link_preview_options: :class:`LinkPreviewOptions` + + :param animation: Optional. Message is an animation, information about the animation + :type animation: :class:`Animation` + + :param audio: Optional. Message is an audio file, information about the file + :type audio: :class:`Audio` + + :param document: Optional. Message is a general file, information about the file + :type document: :class:`Document` + + :param photo: Optional. Message is a photo, available sizes of the photo + :type photo: :obj:`list` of :class:`PhotoSize` + + :param sticker: Optional. Message is a sticker, information about the sticker + :type sticker: :class:`Sticker` + + :param story: Optional. Message is a forwarded story + :type story: :class:`Story` + + :param video: Optional. Message is a video, information about the video + :type video: :class:`Video` + + :param video_note: Optional. Message is a video note, information about the video message + :type video_note: :class:`VideoNote` + + :param voice: Optional. Message is a voice message, information about the file + :type voice: :class:`Voice` + + :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation + :type has_media_spoiler: :obj:`bool` + + :param contact: Optional. Message is a shared contact, information about the contact + :type contact: :class:`Contact` + + :param dice: Optional. Message is a dice with random value + :type dice: :class:`Dice` + + :param game: Optional. Message is a game, information about the game. More about games » + :type game: :class:`Game` + + :param giveaway: Optional. Message is a scheduled giveaway, information about the giveaway + :type giveaway: :class:`Giveaway` + + :param giveaway_winners: Optional. A giveaway with public winners was completed + :type giveaway_winners: :class:`GiveawayWinners` + + :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » + :type invoice: :class:`Invoice` + + :param location: Optional. Message is a shared location, information about the location + :type location: :class:`Location` + + :param poll: Optional. Message is a native poll, information about the poll + :type poll: :class:`Poll` + + :param venue: Optional. Message is a venue, information about the venue + :type venue: :class:`Venue` + + :return: Instance of the class + :rtype: :class:`ExternalReplyInfo` + """ + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + origin = obj.get('origin') + if origin is not None: + origin = MessageOrigin.de_json(origin) + + chat = obj.get('chat') + if chat is not None: + chat = Chat.de_json(chat) + + message_id = obj.get('message_id') + if message_id is not None: + message_id = int(message_id) + + link_preview_options = obj.get('link_preview_options') + if link_preview_options is not None: + link_preview_options = LinkPreviewOptions.de_json(link_preview_options) + + animation = obj.get('animation') + if animation is not None: + animation = Animation.de_json(animation) + + audio = obj.get('audio') + if audio is not None: + audio = Audio.de_json(audio) + + document = obj.get('document') + if document is not None: + document = Document.de_json(document) + + photo = obj.get('photo') + if photo is not None: + photo = [PhotoSize.de_json(photo[i]) for i in range(len(photo))] + + sticker = obj.get('sticker') + if sticker is not None: + sticker = Sticker.de_json(sticker) + + story = obj.get('story') + if story is not None: + story = Story.de_json(story) + + video = obj.get('video') + if video is not None: + video = Video.de_json(video) + + video_note = obj.get('video_note') + if video_note is not None: + video_note = VideoNote.de_json(video_note) + + voice = obj.get('voice') + if voice is not None: + voice = Voice.de_json(voice) + + has_media_spoiler = obj.get('has_media_spoiler') + if has_media_spoiler is not None: + has_media_spoiler = bool(has_media_spoiler) + + contact = obj.get('contact') + if contact is not None: + contact = Contact.de_json(contact) + + dice = obj.get('dice') + if dice is not None: + dice = Dice.de_json(dice) + + game = obj.get('game') + if game is not None: + game = Game.de_json(game) + + giveaway = obj.get('giveaway') + if giveaway is not None: + giveaway = Giveaway.de_json(giveaway) + + giveaway_winners = obj.get('giveaway_winners') + if giveaway_winners is not None: + giveaway_winners = GiveawayWinners.de_json(giveaway_winners) + + invoice = obj.get('invoice') + if invoice is not None: + invoice = Invoice.de_json(invoice) + + location = obj.get('location') + if location is not None: + location = Location.de_json(location) + + poll = obj.get('poll') + if poll is not None: + poll = Poll.de_json(poll) + + venue = obj.get('venue') + if venue is not None: + venue = Venue.de_json(venue) + + return cls(origin=origin, chat=chat, message_id=message_id, link_preview_options=link_preview_options, + animation=animation, audio=audio, document=document, photo=photo, sticker=sticker, story=story, + video=video, video_note=video_note, voice=voice, has_media_spoiler=has_media_spoiler, + contact=contact, dice=dice, game=game, giveaway=giveaway, giveaway_winners=giveaway_winners, + invoice=invoice, location=location, poll=poll, venue=venue) + + def __init__(self, origin: MessageOrigin, chat: Optional[Chat]=None, message_id: Optional[int]=None, + link_preview_options: Optional[LinkPreviewOptions]=None, animation: Optional[Animation]=None, + audio: Optional[Audio]=None, document: Optional[Document]=None, photo: Optional[List[PhotoSize]]=None, + sticker: Optional[Sticker]=None, story: Optional[Story]=None, video: Optional[Video]=None, + video_note: Optional[VideoNote]=None, voice: Optional[Voice]=None, + has_media_spoiler: Optional[bool]=None, contact: Optional[Contact]=None, + dice: Optional[Dice]=None, game: Optional[Game]=None, giveaway: Optional[Giveaway]=None, + giveaway_winners: Optional[GiveawayWinners]=None, invoice: Optional[Invoice]=None, + location: Optional[Location]=None, poll: Optional[Poll]=None, + venue: Optional[Venue]=None) -> None: + self.origin: MessageOrigin = origin + + self.chat: Optional[Chat] = chat + self.message_id: Optional[int] = message_id + self.link_preview_options: Optional[LinkPreviewOptions] = link_preview_options + self.animation: Optional[Animation] = animation + self.audio: Optional[Audio] = audio + self.document: Optional[Document] = document + self.photo: Optional[List[PhotoSize]] = photo + self.sticker: Optional[Sticker] = sticker + self.story: Optional[Story] = story + self.video: Optional[Video] = video + self.video_note: Optional[VideoNote] = video_note + self.voice: Optional[Voice] = voice + self.has_media_spoiler: Optional[bool] = has_media_spoiler + self.contact: Optional[Contact] = contact + self.dice: Optional[Dice] = dice + self.game: Optional[Game] = game + self.giveaway: Optional[Giveaway] = giveaway + self.giveaway_winners: Optional[GiveawayWinners] = giveaway_winners + self.invoice: Optional[Invoice] = invoice + self.location: Optional[Location] = location + self.poll: Optional[Poll] = poll + self.venue: Optional[Venue] = venue + +class MessageOrigin(JsonDeserializable): + """ + This object describes the origin of a message. + + Telegram documentation: https://core.telegram.org/bots/api#messageorigin + + :param type: Type of the message origin + :type type: :obj:`str` + + :param date: Date the message was sent originally in Unix time + :type date: :obj:`int` + + :param sender_user: User that sent the message originally (for MessageOriginUser) + :type sender_user: :class:`User` + + :param sender_user_name: Name of the user that sent the message originally (for MessageOriginHiddenUser) + :type sender_user_name: :obj:`str` + + :param sender_chat: Chat that sent the message originally (for MessageOriginChat) + :type sender_chat: :class:`Chat` + + :param author_signature: Optional. Author signature for certain cases + :type author_signature: :obj:`str` + + :return: Instance of the class + :rtype: :class:`MessageOrigin` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + message_type = obj.get('type') + if message_type == 'user': + sender_user = User.de_json(obj.get('sender_user')) + return MessageOriginUser(date=obj.get('date'), sender_user=sender_user) + elif message_type == 'hidden_user': + return MessageOriginHiddenUser(date=obj.get('date'), sender_user_name=obj.get('sender_user_name')) + elif message_type == 'chat': + sender_chat = Chat.de_json(obj.get('sender_chat')) + return MessageOriginChat(date=obj.get('date'), sender_chat=sender_chat, author_signature=obj.get('author_signature')) + elif message_type == 'channel': + chat = Chat.de_json(obj.get('chat')) + return MessageOriginChannel(date=obj.get('date'), chat=chat, message_id=obj.get('message_id'), author_signature=obj.get('author_signature')) + + def __init__(self, type: str, date: int) -> None: + self.type: str = type + self.date: int = date + + +class MessageOriginUser(MessageOrigin): + """ + The message was originally sent by a known user. + + :param sender_user: User that sent the message originally + :type sender_user: :class:`User` + """ + + def __init__(self, date: int, sender_user: Optional[User] = None) -> None: + super().__init__('user', date) + self.sender_user: Optional[User] = sender_user + + +class MessageOriginHiddenUser(MessageOrigin): + """ + The message was originally sent by an unknown user. + + :param sender_user_name: Name of the user that sent the message originally + :type sender_user_name: :obj:`str` + """ + + def __init__(self, date: int, sender_user_name: Optional[str] = None) -> None: + super().__init__('hidden_user', date) + self.sender_user_name: Optional[str] = sender_user_name + + +class MessageOriginChat(MessageOrigin): + """ + The message was originally sent on behalf of a chat to a group chat. + + :param sender_chat: Chat that sent the message originally + :type sender_chat: :class:`Chat` + + :param author_signature: Optional. For messages originally sent by an anonymous chat administrator, original message author signature + :type author_signature: :obj:`str` + """ + + def __init__(self, date: int, sender_chat: Optional[Chat] = None, author_signature: Optional[str] = None) -> None: + super().__init__('chat', date) + self.sender_chat: Optional[Chat] = sender_chat + self.author_signature: Optional[str] = author_signature + + +class MessageOriginChannel(MessageOrigin): + """ + The message was originally sent to a channel chat. + + :param chat: Channel chat to which the message was originally sent + :type chat: :class:`Chat` + + :param message_id: Unique message identifier inside the chat + :type message_id: :obj:`int` + + :param author_signature: Optional. Signature of the original post author + :type author_signature: :obj:`str` + """ + + def __init__(self, date: int, chat: Optional[Chat] = None, message_id: Optional[int] = None, author_signature: Optional[str] = None) -> None: + super().__init__('channel', date) + self.chat: Optional[Chat] = chat + self.message_id: Optional[int] = message_id + self.author_signature: Optional[str] = author_signature + + +class LinkPreviewOptions(JsonDeserializable): + """ + Describes the options used for link preview generation. + + Telegram documentation: https://core.telegram.org/bots/api#linkpreviewoptions + + :param is_disabled: Optional. True, if the link preview is disabled + :type is_disabled: :obj:`bool` + + :param url: Optional. URL to use for the link preview. If empty, then the first URL found in the message text will be used + :type url: :obj:`str` + + :param prefer_small_media: Optional. True, if the media in the link preview is supposed to be shrunk; ignored if the URL isn't explicitly specified or media size change isn't supported for the preview + :type prefer_small_media: :obj:`bool` + + :param prefer_large_media: Optional. True, if the media in the link preview is supposed to be enlarged; ignored if the URL isn't explicitly specified or media size change isn't supported for the preview + :type prefer_large_media: :obj:`bool` + + :param show_above_text: Optional. True, if the link preview must be shown above the message text; otherwise, the link preview will be shown below the message text + :type show_above_text: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`LinkPreviewOptions` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + return cls(is_disabled=obj.get('is_disabled'), url=obj.get('url'), prefer_small_media=obj.get('prefer_small_media'), + prefer_large_media=obj.get('prefer_large_media'), show_above_text=obj.get('show_above_text')) + + + def __init__(self, is_disabled: Optional[bool] = None, url: Optional[str] = None, + prefer_small_media: Optional[bool] = None, prefer_large_media: Optional[bool] = None, + show_above_text: Optional[bool] = None) -> None: + self.is_disabled: Optional[bool] = is_disabled + self.url: Optional[str] = url + self.prefer_small_media: Optional[bool] = prefer_small_media + self.prefer_large_media: Optional[bool] = prefer_large_media + self.show_above_text: Optional[bool] = show_above_text + + +class Giveaway(JsonDeserializable): + """ + This object represents a message about a scheduled giveaway. + + Telegram documentation: https://core.telegram.org/bots/api#giveaway + + :param chats: The list of chats which the user must join to participate in the giveaway + :type chats: :obj:`list` of :class:`Chat` + + :param winners_selection_date: Point in time (Unix timestamp) when winners of the giveaway will be selected + :type winners_selection_date: :obj:`int` + + :param winner_count: The number of users which are supposed to be selected as winners of the giveaway + :type winner_count: :obj:`int` + + :param only_new_members: Optional. True, if only users who join the chats after the giveaway started should be eligible to win + :type only_new_members: :obj:`bool` + + :param has_public_winners: Optional. True, if the list of giveaway winners will be visible to everyone + :type has_public_winners: :obj:`bool` + + :param prize_description: Optional. Description of additional giveaway prize + :type prize_description: :obj:`str` + + :param country_codes: Optional. A list of two-letter ISO 3166-1 alpha-2 country codes indicating the countries from which eligible users for the giveaway must come. If empty, then all users can participate in the giveaway. + :type country_codes: :obj:`list` of :obj:`str` + + :param premium_subscription_month_count: Optional. The number of months the Telegram Premium subscription won from the giveaway will be active for + :type premium_subscription_month_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`Giveaway` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + chats = [Chat.de_json(chat) for chat in obj.get('chats', [])] + + return cls(**obj, chats=chats) + + def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: int, + only_new_members: Optional[bool] = None, has_public_winners: Optional[bool] = None, + prize_description: Optional[str] = None, country_codes: Optional[List[str]] = None, + premium_subscription_month_count: Optional[int] = None) -> None: + self.chats: List[Chat] = chats + self.winners_selection_date: int = winners_selection_date + self.winner_count: int = winner_count + self.only_new_members: Optional[bool] = only_new_members + self.has_public_winners: Optional[bool] = has_public_winners + self.prize_description: Optional[str] = prize_description + self.country_codes: Optional[List[str]] = country_codes or [] + self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count + +class GiveawayWinners(JsonDeserializable): + """ + This object represents a message about the completion of a giveaway with public winners. + + Telegram documentation: https://core.telegram.org/bots/api#giveawaywinners + + :param chat: The chat that created the giveaway + :type chat: :class:`Chat` + + :param giveaway_message_id: Identifier of the messsage with the giveaway in the chat + :type giveaway_message_id: :obj:`int` + + :param winners_selection_date: Point in time (Unix timestamp) when winners of the giveaway were selected + :type winners_selection_date: :obj:`int` + + :param winner_count: Total number of winners in the giveaway + :type winner_count: :obj:`int` + + :param winners: List of up to 100 winners of the giveaway + :type winners: :obj:`list` of :class:`User` + + :param additional_chat_count: Optional. The number of other chats the user had to join in order to be eligible for the giveaway + :type additional_chat_count: :obj:`int` + + :param premium_subscription_month_count: Optional. The number of months the Telegram Premium subscription won from the giveaway will be active for + :type premium_subscription_month_count: :obj:`int` + + :param unclaimed_prize_count: Optional. Number of undistributed prizes + :type unclaimed_prize_count: :obj:`int` + + :param only_new_members: Optional. True, if only users who had joined the chats after the giveaway started were eligible to win + :type only_new_members: :obj:`bool` + + :param was_refunded: Optional. True, if the giveaway was canceled because the payment for it was refunded + :type was_refunded: :obj:`bool` + + :param prize_description: Optional. Description of additional giveaway prize + :type prize_description: :obj:`str` + + :return: Instance of the class + :rtype: :class:`GiveawayWinners` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + obj['chat'] = Chat.de_json(obj.get('chat')) + obj['winners'] = [User.de_json(user) for user in obj.get('winners', [])] + + return cls(**obj) + + def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: int, winner_count: int, + winners: List[User], additional_chat_count: Optional[int] = None, + premium_subscription_month_count: Optional[int] = None, unclaimed_prize_count: Optional[int] = None, + only_new_members: Optional[bool] = None, was_refunded: Optional[bool] = None, + prize_description: Optional[str] = None) -> None: + self.chat: Chat = chat + self.giveaway_message_id: int = giveaway_message_id + self.winners_selection_date: int = winners_selection_date + self.winner_count: int = winner_count + self.winners: List[User] = winners + self.additional_chat_count: Optional[int] = additional_chat_count + self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count + self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count + self.only_new_members: Optional[bool] = only_new_members + self.was_refunded: Optional[bool] = was_refunded + self.prize_description: Optional[str] = prize_description + + + \ No newline at end of file From 5e018918c67ad0765dc505be11575d8ac289aa40 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 21:52:22 +0500 Subject: [PATCH 1390/1808] Added TextQuote and fixed extrernal_reply (forgot in previous commit); CHECK THE COMMIT! --- telebot/types.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 700911f50..bdb1d290d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -807,6 +807,12 @@ class Message(JsonDeserializable): will not contain further reply_to_message fields even if it itself is a reply. :type reply_to_message: :class:`telebot.types.Message` + :param external_reply: Optional. Information about the message that is being replied to, which may come from another chat or forum topic + :type external_reply: :class:`telebot.types.ExternalReplyInfo` + + :param quote: Optional. For replies that quote part of the original message, the quoted part of the message + :type quote: :class:`telebot.types.TextQuote` + :param via_bot: Optional. Bot through which the message was sent :type via_bot: :class:`telebot.types.User` @@ -1208,6 +1214,13 @@ def de_json(cls, json_string): if 'story' in obj: opts['story'] = Story.de_json(obj['story']) content_type = 'story' + if 'external_reply' in obj: + opts['external_reply'] = ExternalReplyInfo.de_json(obj['external_reply']) + content_type = 'text' # @Badiboy not sure about content_types in here, please check + if 'quote' in obj: + opts['quote'] = TextQuote.de_json(obj['quote']) + content_type = 'text' # Here too, check the content types + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1306,6 +1319,9 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.user_shared: Optional[UserShared] = None self.chat_shared: Optional[ChatShared] = None self.story: Optional[Story] = None + self.external_reply: Optional[ExternalReplyInfo] = None + self.quote: Optional[TextQuote] = None + for key in options: setattr(self, key, options[key]) self.json = json_string @@ -8567,4 +8583,42 @@ def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: self.prize_description: Optional[str] = prize_description - \ No newline at end of file + +class TextQuote(JsonDeserializable): + """ + This object contains information about the quoted part of a message that is replied to by the given message. + + Telegram documentation: https://core.telegram.org/bots/api#textquote + + :param text: Text of the quoted part of a message that is replied to by the given message + :type text: :obj:`str` + + :param entities: Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes. + :type entities: :obj:`list` of :class:`MessageEntity` + + :param position: Approximate quote position in the original message in UTF-16 code units as specified by the sender + :type position: :obj:`int` + + :param is_manual: Optional. True, if the quote was chosen manually by the message sender. Otherwise, the quote was added automatically by the server. + :type is_manual: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`TextQuote` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + obj['entities'] = [MessageEntity.de_json(entity) for entity in obj.get('entities', [])] + + return cls(**obj) + + def __init__(self, text: str, entities: Optional[List[MessageEntity]] = None, + position: Optional[int] = None, is_manual: Optional[bool] = None) -> None: + self.text: str = text + self.entities: Optional[List[MessageEntity]] = entities + self.position: Optional[int] = position + self.is_manual: Optional[bool] = is_manual From 2604655ea434a844617398b03c66d244f08bd150 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 22:51:53 +0500 Subject: [PATCH 1391/1808] ReplyParameters support for sync, including backward compatibility --- telebot/__init__.py | 363 ++++++++++++++++++++++++++++++++++++------- telebot/apihelper.py | 201 +++++++++++------------- telebot/types.py | 80 ++++++++++ 3 files changed, 474 insertions(+), 170 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 23a95eea4..f8f60f987 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1530,7 +1530,8 @@ def send_message( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send text messages. @@ -1577,6 +1578,9 @@ def send_message( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1586,11 +1590,21 @@ def send_message( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_message( - self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, + self.token, chat_id, text, disable_web_page_preview, reply_markup, parse_mode, disable_notification, timeout, - entities, allow_sending_without_reply, protect_content=protect_content, message_thread_id=message_thread_id)) + entities, protect_content=protect_content, message_thread_id=message_thread_id)) def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -1648,7 +1662,8 @@ def copy_message( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.MessageID: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.MessageID: """ Use this method to copy messages of any kind. @@ -1693,6 +1708,9 @@ def copy_message( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Additional parameters for replies to messages + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -1702,10 +1720,20 @@ def copy_message( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, - disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, - timeout, protect_content, message_thread_id)) + disable_notification, reply_markup, + timeout, protect_content, message_thread_id, reply_parameters)) def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -1744,7 +1772,8 @@ def send_dice( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -1780,6 +1809,9 @@ def send_dice( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Additional parameters for replies to messages + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1787,10 +1819,20 @@ def send_dice( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_dice( - self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout, allow_sending_without_reply, protect_content, message_thread_id) + self.token, chat_id, emoji, disable_notification, + reply_markup, timeout, protect_content, message_thread_id, reply_parameters) ) @@ -1862,11 +1904,21 @@ def send_photo( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_photo( - self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, + self.token, chat_id, photo, caption, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) + protect_content, message_thread_id, has_spoiler, reply_parameters)) # TODO: Rewrite this method like in API. def send_audio( @@ -1883,7 +1935,8 @@ def send_audio( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None,) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -1950,6 +2003,9 @@ def send_audio( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1958,15 +2014,25 @@ def send_audio( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + if thumb is not None and thumbnail is None: thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( apihelper.send_audio( - self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, + self.token, chat_id, audio, caption, duration, performer, title, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) + caption_entities, protect_content, message_thread_id, reply_parameters)) # TODO: Rewrite this method like in API. def send_voice( @@ -1980,7 +2046,8 @@ def send_voice( caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=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). @@ -2030,6 +2097,9 @@ def send_voice( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2037,11 +2107,21 @@ def send_voice( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_voice( - self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, + self.token, chat_id, voice, caption, duration, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content, message_thread_id)) + protect_content, message_thread_id, reply_parameters)) # TODO: Rewrite this method like in API. def send_document( @@ -2059,7 +2139,8 @@ def send_document( disable_content_type_detection: Optional[bool]=None, data: Optional[Union[Any, str]]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None,) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send general files. @@ -2119,6 +2200,9 @@ def send_document( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2127,6 +2211,16 @@ def send_document( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + if data and (not document): # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "document" instead.') @@ -2139,11 +2233,10 @@ def send_document( return types.Message.de_json( apihelper.send_data( self.token, chat_id, document, 'document', - reply_to_message_id = reply_to_message_id, reply_markup = reply_markup, parse_mode = parse_mode, + reply_parameters=reply_parameters, reply_markup = reply_markup, parse_mode = parse_mode, disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, - 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, - protect_content = protect_content, message_thread_id = message_thread_id)) + caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, + visible_file_name = visible_file_name, protect_content = protect_content, message_thread_id = message_thread_id)) # TODO: Rewrite this method like in API. @@ -2158,7 +2251,8 @@ def send_sticker( protect_content:Optional[bool]=None, data: Union[Any, str]=None, message_thread_id: Optional[int]=None, - emoji: Optional[str]=None) -> types.Message: + emoji: Optional[str]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2201,6 +2295,9 @@ def send_sticker( :param emoji: Emoji associated with the sticker; only for just uploaded stickers :type emoji: :obj:`str` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2213,13 +2310,22 @@ def send_sticker( logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_data( - self.token, chat_id, sticker, 'sticker', - reply_to_message_id=reply_to_message_id, reply_markup=reply_markup, + self.token, chat_id, sticker, 'sticker', reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji)) + protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, + reply_parameters=reply_parameters)) def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -2240,7 +2346,8 @@ def send_video( data: Optional[Union[Any, str]]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, - thumb: Optional[Union[Any, str]]=None,) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2308,6 +2415,9 @@ def send_video( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param reply_parameters: Reply parameters + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2325,11 +2435,21 @@ def send_video( thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_video( - self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, + self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, - caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) + caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters)) def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -2348,7 +2468,8 @@ def send_animation( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, - thumb: Optional[Union[Any, str]]=None,) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2415,6 +2536,9 @@ def send_animation( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2423,14 +2547,24 @@ def send_animation( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + if thumbnail is None and thumb is not None: thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( apihelper.send_animation( - self.token, chat_id, animation, duration, caption, reply_to_message_id, + self.token, chat_id, animation, duration, caption, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, allow_sending_without_reply, protect_content, width, height, message_thread_id, has_spoiler)) + caption_entities, protect_content, width, height, message_thread_id, has_spoiler, reply_parameters)) # TODO: Rewrite this method like in API. def send_video_note( @@ -2445,7 +2579,8 @@ def send_video_note( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -2495,7 +2630,11 @@ def send_video_note( :type message_thread_id: :obj:`int` :param thumb: Deprecated. Use thumbnail instead - :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2507,10 +2646,20 @@ def send_video_note( thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_video_note( - self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumbnail, allow_sending_without_reply, protect_content, message_thread_id)) + self.token, chat_id, data, duration, length, reply_markup, + disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters)) def send_media_group( @@ -2523,7 +2672,8 @@ def send_media_group( reply_to_message_id: Optional[int]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> List[types.Message]: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -2554,6 +2704,9 @@ def send_media_group( :param message_thread_id: Identifier of a message thread, in which the media group will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -2561,9 +2714,19 @@ def send_media_group( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + result = apihelper.send_media_group( - self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, - allow_sending_without_reply, protect_content, message_thread_id) + self.token, chat_id, media, disable_notification, timeout, + protect_content, message_thread_id, reply_parameters) return [types.Message.de_json(msg) for msg in result] # TODO: Rewrite this method like in API. @@ -2580,7 +2743,8 @@ def send_location( proximity_alert_radius: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -2630,6 +2794,9 @@ def send_location( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2637,12 +2804,22 @@ def send_location( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_location( self.token, chat_id, latitude, longitude, live_period, - reply_to_message_id, reply_markup, disable_notification, timeout, + reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - allow_sending_without_reply, protect_content, message_thread_id)) + protect_content, message_thread_id, reply_parameters)) def edit_message_live_location( self, latitude: float, longitude: float, @@ -2751,7 +2928,8 @@ def send_venue( google_place_id: Optional[str]=None, google_place_type: Optional[str]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -2809,6 +2987,9 @@ def send_venue( :param message_thread_id: The thread identifier of a message from which the reply will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2816,11 +2997,21 @@ def send_venue( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, - disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, google_place_id, google_place_type, protect_content, message_thread_id)) + disable_notification, reply_markup, timeout, + google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters)) # TODO: Rewrite this method like in API. @@ -2833,7 +3024,8 @@ def send_contact( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None) -> types.Message: + protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -2878,6 +3070,9 @@ def send_contact( :param message_thread_id: The thread identifier of a message from which the reply will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2885,11 +3080,21 @@ def send_contact( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, - disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content, message_thread_id)) + disable_notification, reply_markup, timeout, + protect_content, message_thread_id, reply_parameters)) def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool: @@ -3956,7 +4161,8 @@ def send_game( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Used to send the game. @@ -3989,6 +4195,9 @@ def send_game( :param message_thread_id: The identifier of a message thread, in which the game message will be sent. :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters + :type reply_parameters: :obj:`ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -3996,10 +4205,20 @@ def send_game( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + result = apihelper.send_game( self.token, chat_id, game_short_name, disable_notification, - reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content, message_thread_id) + reply_markup, timeout, + protect_content, message_thread_id, reply_parameters) return types.Message.de_json(result) def set_game_score( @@ -4097,7 +4316,8 @@ def send_invoice( max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Sends invoice. @@ -4199,6 +4419,9 @@ def send_invoice( :param message_thread_id: The identifier of a message thread, in which the invoice message will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Required if the message is a reply. Additional interface options. + :type reply_parameters: :obj:`types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4206,13 +4429,23 @@ def send_invoice( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + result = apihelper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, - reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, - max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id) + reply_markup, provider_data, timeout, + max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters) return types.Message.de_json(result) def create_invoice_link(self, @@ -4337,7 +4570,8 @@ def send_poll( timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -4408,6 +4642,9 @@ def send_poll( :param message_thread_id: The identifier of a message thread, in which the poll will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: reply parameters. + :type reply_parameters: :obj:`ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4417,6 +4654,16 @@ def send_poll( if isinstance(question, types.Poll): raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") + + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) explanation_parse_mode = self.parse_mode if (explanation_parse_mode is None) else explanation_parse_mode @@ -4426,8 +4673,8 @@ def send_poll( question, options, is_anonymous, type, allows_multiple_answers, correct_option_id, explanation, explanation_parse_mode, open_period, close_date, is_closed, - disable_notification, reply_to_message_id, allow_sending_without_reply, - reply_markup, timeout, explanation_entities, protect_content, message_thread_id)) + disable_notification,reply_markup, timeout, explanation_entities, protect_content, message_thread_id, + reply_parameters)) def stop_poll( self, chat_id: Union[int, str], message_id: int, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8ae65814f..f41d92091 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -237,16 +237,14 @@ def download_file(token, file_path): def send_message( token, chat_id, text, - disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, + disable_web_page_preview=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + entities=None, protect_content=None, + message_thread_id=None, reply_parameters=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if disable_web_page_preview is not None: payload['disable_web_page_preview'] = disable_web_page_preview - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -257,12 +255,13 @@ def send_message( payload['timeout'] = timeout if entities: payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, method='post') @@ -414,8 +413,8 @@ def forward_message( def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, - disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, protect_content=None, message_thread_id=None): + disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, + reply_parameters=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -426,12 +425,9 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['caption_entities'] = _convert_entites(caption_entities) if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id - if reply_markup is not None: - payload['reply_markup'] = _convert_markup(reply_markup) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) if timeout: payload['timeout'] = timeout if protect_content is not None: @@ -443,22 +439,21 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m def send_dice( token, chat_id, - emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + emoji=None, disable_notification=None, + reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: payload['emoji'] = emoji if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -468,10 +463,10 @@ def send_dice( def send_photo( token, chat_id, photo, - caption=None, reply_to_message_id=None, reply_markup=None, + caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None, has_spoiler=None): + caption_entities=None, protect_content=None, + message_thread_id=None, has_spoiler=None,, reply_parameters=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -483,8 +478,6 @@ def send_photo( files = {'photo': photo} if caption: payload['caption'] = caption - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -495,32 +488,32 @@ def send_photo( payload['timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id is not None: payload['message_thread_id'] = message_thread_id if has_spoiler is not None: payload['has_spoiler'] = has_spoiler + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') def send_media_group( token, chat_id, media, - disable_notification=None, reply_to_message_id=None, - timeout=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + disable_notification=None, + timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id is not None: @@ -533,11 +526,11 @@ def send_media_group( def send_location( token, chat_id, latitude, longitude, - live_period=None, reply_to_message_id=None, + live_period=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + proximity_alert_radius=None, protect_content=None, + message_thread_id=None, reply_parameters=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -548,10 +541,6 @@ def send_location( payload['heading'] = heading if proximity_alert_radius: payload['proximity_alert_radius'] = proximity_alert_radius - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if disable_notification is not None: @@ -562,6 +551,9 @@ def send_location( payload['protect_content'] = protect_content if message_thread_id is not None: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -611,9 +603,8 @@ def stop_message_live_location( def send_venue( token, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, google_place_id=None, - google_place_type=None, protect_content=None, message_thread_id=None): + reply_markup=None, timeout=None, google_place_id=None, + google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -622,14 +613,10 @@ def send_venue( payload['foursquare_type'] = foursquare_type if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if google_place_id: payload['google_place_id'] = google_place_id if google_place_type: @@ -643,8 +630,8 @@ def send_venue( def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + disable_notification=None, reply_markup=None, timeout=None, + protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -653,18 +640,17 @@ def send_contact( payload['vcard'] = vcard if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id is not None: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -679,10 +665,10 @@ def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=Non return _make_request(token, method_url, params=payload) -def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, +def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, - thumbnail=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None, has_spoiler=None): + thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, + message_thread_id=None, has_spoiler=None, reply_parameters=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -694,8 +680,6 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['duration'] = duration if caption: payload['caption'] = caption - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -720,21 +704,22 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa payload['height'] = height if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id if has_spoiler is not None: payload['has_spoiler'] = has_spoiler + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') def send_animation( - token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, + token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, - allow_sending_without_reply=None, protect_content=None, width=None, height=None, message_thread_id=None, + protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None has_spoiler=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} @@ -747,8 +732,6 @@ def send_animation( payload['duration'] = duration if caption: payload['caption'] = caption - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -767,8 +750,9 @@ def send_animation( payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if width: @@ -782,9 +766,9 @@ def send_animation( return _make_request(token, method_url, params=payload, files=files, method='post') -def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, +def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -796,8 +780,6 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['caption'] = caption if duration: payload['duration'] = duration - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -808,18 +790,19 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess payload['timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') -def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumbnail=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): +def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, + disable_notification=None, timeout=None, thumbnail=None, protect_content=None, + message_thread_id=None, reply_parameters=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -833,8 +816,6 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m payload['length'] = length else: payload['length'] = 639 # seems like it is MAX length size - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if disable_notification is not None: @@ -849,8 +830,9 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m files = {'thumbnail': thumbnail} else: payload['thumbnail'] = thumbnail - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -858,9 +840,9 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m return _make_request(token, method_url, params=payload, files=files, method='post') -def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, +def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -876,8 +858,6 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['performer'] = performer if title: payload['title'] = title - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -896,19 +876,21 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + return _make_request(token, method_url, params=payload, files=files, method='post') -def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, +def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, - protect_content = None, message_thread_id=None, emoji=None): + disable_content_type_detection=None, visible_file_name=None, + protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -919,8 +901,6 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m files = {data_type: file_data} else: payload[data_type] = data - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode and data_type == 'document': @@ -941,8 +921,9 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: @@ -1399,20 +1380,19 @@ def delete_message(token, chat_id, message_id, timeout=None): def send_game( token, chat_id, game_short_name, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + disable_notification=None, reply_markup=None, timeout=None, + protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -1480,9 +1460,9 @@ def send_invoice( start_parameter = None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None): + disable_notification=None, reply_markup=None, provider_data=None, + timeout=None, max_tip_amount=None, suggested_tip_amounts=None, + protect_content=None, message_thread_id=None, reply_parameters=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1548,16 +1528,12 @@ def send_invoice( payload['is_flexible'] = is_flexible if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if provider_data: payload['provider_data'] = provider_data if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if max_tip_amount is not None: payload['max_tip_amount'] = max_tip_amount if suggested_tip_amounts is not None: @@ -1566,6 +1542,9 @@ def send_invoice( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -1804,8 +1783,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_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None): + disable_notification=False, + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1836,10 +1815,8 @@ def send_poll( if disable_notification: payload['disable_notification'] = disable_notification - if reply_to_message_id is not None: - payload['reply_to_message_id'] = reply_to_message_id - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + payload['reply_parameters'] = reply_parameters.to_json() if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: diff --git a/telebot/types.py b/telebot/types.py index bdb1d290d..4c53c3664 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8622,3 +8622,83 @@ def __init__(self, text: str, entities: Optional[List[MessageEntity]] = None, self.entities: Optional[List[MessageEntity]] = entities self.position: Optional[int] = position self.is_manual: Optional[bool] = is_manual + +class ReplyParameters(JsonDeserializable, Dictionaryable, JsonSerializable): + """ + Describes reply parameters for the message that is being sent. + + Telegram documentation: https://core.telegram.org/bots/api#replyparameters + + :param message_id: Identifier of the message that will be replied to in the current chat, or in the chat chat_id if it is specified + :type message_id: :obj:`int` + + :param chat_id: Optional. If the message to be replied to is from a different chat, unique identifier for the chat or username of the channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param allow_sending_without_reply: Optional. Pass True if the message should be sent even if the specified message to be replied to is not found; can be used only for replies in the same chat and forum topic. + :type allow_sending_without_reply: :obj:`bool` + + :param quote: Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. The message will fail to send if the quote isn't found in the original message. + :type quote: :obj:`str` + + :param quote_parse_mode: Optional. Mode for parsing entities in the quote. See formatting options for more details. + :type quote_parse_mode: :obj:`str` + + :param quote_entities: Optional. A JSON-serialized list of special entities that appear in the quote. It can be specified instead of quote_parse_mode. + :type quote_entities: :obj:`list` of :class:`MessageEntity` + + :param quote_position: Optional. Position of the quote in the original message in UTF-16 code units + :type quote_position: :obj:`int` + + :return: Instance of the class + :rtype: :class:`ReplyParameters` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + obj['quote_entities'] = [MessageEntity.de_json(entity) for entity in obj.get('quote_entities', [])] + + return cls(**obj) + + + def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, + allow_sending_without_reply: Optional[bool] = None, quote: Optional[str] = None, + quote_parse_mode: Optional[str] = None, quote_entities: Optional[List[MessageEntity]] = None, + quote_position: Optional[int] = None) -> None: + self.message_id: int = message_id + self.chat_id: Optional[Union[int, str]] = chat_id + self.allow_sending_without_reply: Optional[bool] = allow_sending_without_reply + self.quote: Optional[str] = quote + self.quote_parse_mode: Optional[str] = quote_parse_mode + self.quote_entities: Optional[List[MessageEntity]] = quote_entities + self.quote_position: Optional[int] = quote_position + + + def to_dict(self) -> dict: + json_dict = { + 'message_id': self.message_id + } + + if self.chat_id is not None: + json_dict['chat_id'] = self.chat_id + if self.allow_sending_without_reply is not None: + json_dict['allow_sending_without_reply'] = self.allow_sending_without_reply + if self.quote is not None: + json_dict['quote'] = self.quote + if self.quote_parse_mode is not None: + json_dict['quote_parse_mode'] = self.quote_parse_mode + if self.quote_entities is not None: + json_dict['quote_entities'] = [entity.to_dict() for entity in self.quote_entities] + if self.quote_position is not None: + json_dict['quote_position'] = self.quote_position + + return json_dict + + def to_json(self) -> str: + return json.dumps(self.to_dict()) + + \ No newline at end of file From 6211366e186a3614e8888289abb360abf24ededb Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 22:53:22 +0500 Subject: [PATCH 1392/1808] Fix the typo and therefore tests --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f41d92091..6d9201098 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -466,7 +466,7 @@ def send_photo( caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None,, reply_parameters=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None From 3b5d2d789e847b1dcfe0e9950e14eb463ee55d19 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 22:55:19 +0500 Subject: [PATCH 1393/1808] Fix tests attempt 2 --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 6d9201098..c44ac49e3 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -719,7 +719,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, - protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None + protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None, has_spoiler=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} From c04ae539612e5bf8b6a54f0d0ec64b50bd65af30 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 23:18:39 +0500 Subject: [PATCH 1394/1808] Added reply_parameters to sync --- telebot/async_telebot.py | 363 ++++++++++++++++++++++++++++++++------ telebot/asyncio_helper.py | 188 ++++++++------------ 2 files changed, 384 insertions(+), 167 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0f630ca5d..7546f2a84 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2514,7 +2514,8 @@ async def send_message( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send text messages. @@ -2561,6 +2562,9 @@ async def send_message( :param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for forum supergroups only :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2570,11 +2574,21 @@ async def send_message( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_message( - self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, + self.token, chat_id, text, disable_web_page_preview, reply_markup, parse_mode, disable_notification, timeout, - entities, allow_sending_without_reply, protect_content, message_thread_id)) + entities, protect_content, message_thread_id, reply_parameters)) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -2631,7 +2645,8 @@ async def copy_message( allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.MessageID: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.MessageID: """ Use this method to copy messages of any kind. @@ -2677,6 +2692,9 @@ async def copy_message( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` """ @@ -2685,10 +2703,20 @@ async def copy_message( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, - disable_notification, reply_to_message_id, allow_sending_without_reply, reply_markup, - timeout, protect_content, message_thread_id)) + disable_notification, reply_markup, + timeout, protect_content, message_thread_id, reply_parameters)) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -2727,7 +2755,8 @@ async def send_dice( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -2762,6 +2791,9 @@ async def send_dice( :param message_thread_id: The identifier of a message thread, unique within the chat to which the message with the thread identifier belongs :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2770,10 +2802,20 @@ async def send_dice( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_dice( - self.token, chat_id, emoji, disable_notification, reply_to_message_id, - reply_markup, timeout, allow_sending_without_reply, protect_content, message_thread_id) + self.token, chat_id, emoji, disable_notification, + reply_markup, timeout, protect_content, message_thread_id, reply_parameters) ) async def send_photo( @@ -2787,7 +2829,8 @@ async def send_photo( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - has_spoiler: Optional[bool]=None) -> types.Message: + has_spoiler: Optional[bool]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2836,6 +2879,9 @@ async def send_photo( :param has_spoiler: Pass True, if the photo should be sent as a spoiler :type has_spoiler: :obj:`bool` + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2844,11 +2890,21 @@ async def send_photo( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_photo( - self.token, chat_id, photo, caption, reply_to_message_id, reply_markup, + self.token, chat_id, photo, caption, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) + protect_content, message_thread_id, has_spoiler, reply_parameters)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -2864,7 +2920,8 @@ async def send_audio( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2930,6 +2987,9 @@ async def send_audio( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2943,11 +3003,21 @@ async def send_audio( thumbnail = thumb logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_audio( - self.token, chat_id, audio, caption, duration, performer, title, reply_to_message_id, + self.token, chat_id, audio, caption, duration, performer, title, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, allow_sending_without_reply, protect_content, message_thread_id)) + caption_entities, protect_content, message_thread_id, reply_parameters)) async def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -2960,7 +3030,8 @@ async def send_voice( caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=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). @@ -3009,6 +3080,9 @@ async def send_voice( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. """ @@ -3017,11 +3091,21 @@ async def send_voice( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_voice( - self.token, chat_id, voice, caption, duration, reply_to_message_id, reply_markup, + self.token, chat_id, voice, caption, duration, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - allow_sending_without_reply, protect_content, message_thread_id)) + protect_content, message_thread_id, reply_parameters)) async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], @@ -3039,7 +3123,8 @@ async def send_document( data: Optional[Union[Any, str]]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send general files. @@ -3098,6 +3183,9 @@ async def send_document( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3116,14 +3204,24 @@ async def send_document( thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_data( self.token, chat_id, document, 'document', - reply_to_message_id = reply_to_message_id, reply_markup = reply_markup, parse_mode = parse_mode, + reply_markup = reply_markup, parse_mode = parse_mode, disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, - caption_entities = caption_entities, allow_sending_without_reply = allow_sending_without_reply, + caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content, - message_thread_id = message_thread_id)) + message_thread_id = message_thread_id, reply_parameters=reply_parameters)) async def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], @@ -3135,7 +3233,8 @@ async def send_sticker( protect_content: Optional[bool]=None, data: Union[Any, str]=None, message_thread_id: Optional[int]=None, - emoji: Optional[str]=None) -> types.Message: + emoji: Optional[str]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -3177,6 +3276,9 @@ async def send_sticker( :param emoji: Emoji associated with the sticker; only for just uploaded stickers :type emoji: :obj:`str` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3190,13 +3292,23 @@ async def send_sticker( logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_data( self.token, chat_id, sticker, 'sticker', - reply_to_message_id=reply_to_message_id, reply_markup=reply_markup, + reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, - allow_sending_without_reply=allow_sending_without_reply, protect_content=protect_content, - message_thread_id=message_thread_id, emoji=emoji)) + protect_content=protect_content, + message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters)) async def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -3217,7 +3329,8 @@ async def send_video( data: Optional[Union[Any, str]]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, - thumb: Optional[Union[Any, str]]=None) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -3284,6 +3397,9 @@ async def send_video( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3293,6 +3409,16 @@ async def send_video( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + if data and not(video): # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "video" instead.') @@ -3304,9 +3430,9 @@ async def send_video( return types.Message.de_json( await asyncio_helper.send_video( - self.token, chat_id, video, duration, caption, reply_to_message_id, reply_markup, + self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, - caption_entities, allow_sending_without_reply, protect_content, message_thread_id, has_spoiler)) + caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -3325,7 +3451,8 @@ async def send_animation( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, - thumb: Optional[Union[Any, str]]=None) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -3391,6 +3518,9 @@ async def send_animation( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3400,15 +3530,25 @@ async def send_animation( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + if thumb is not None and thumbnail is None: thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( await asyncio_helper.send_animation( - self.token, chat_id, animation, duration, caption, reply_to_message_id, + self.token, chat_id, animation, duration, caption, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, allow_sending_without_reply, width, height, protect_content, message_thread_id, has_spoiler)) + caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -3422,7 +3562,8 @@ async def send_video_note( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - thumb: Optional[Union[Any, str]]=None) -> types.Message: + thumb: Optional[Union[Any, str]]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -3473,6 +3614,9 @@ async def send_video_note( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3481,14 +3625,24 @@ async def send_video_note( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + if thumb is not None and thumbnail is None: thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( await asyncio_helper.send_video_note( - self.token, chat_id, data, duration, length, reply_to_message_id, reply_markup, - disable_notification, timeout, thumbnail, allow_sending_without_reply, protect_content, message_thread_id)) + self.token, chat_id, data, duration, length, reply_markup, + disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters)) async def send_media_group( self, chat_id: Union[int, str], @@ -3500,7 +3654,8 @@ async def send_media_group( reply_to_message_id: Optional[int]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> List[types.Message]: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -3530,6 +3685,9 @@ async def send_media_group( :param message_thread_id: Identifier of a message thread, in which the messages will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] @@ -3538,9 +3696,18 @@ async def send_media_group( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + result = await asyncio_helper.send_media_group( - self.token, chat_id, media, disable_notification, reply_to_message_id, timeout, - allow_sending_without_reply, protect_content, message_thread_id) + self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -3556,7 +3723,8 @@ async def send_location( proximity_alert_radius: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -3605,6 +3773,9 @@ async def send_location( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3613,12 +3784,22 @@ async def send_location( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_location( self.token, chat_id, latitude, longitude, live_period, - reply_to_message_id, reply_markup, disable_notification, timeout, + reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - allow_sending_without_reply, protect_content, message_thread_id)) + protect_content, message_thread_id, reply_parameters)) async def edit_message_live_location( self, latitude: float, longitude: float, @@ -3726,7 +3907,8 @@ async def send_venue( google_place_id: Optional[str]=None, google_place_type: Optional[str]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -3783,6 +3965,9 @@ async def send_venue( :param message_thread_id: The thread to which the message will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3791,11 +3976,21 @@ async def send_venue( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, - disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, google_place_id, google_place_type, protect_content, message_thread_id) + disable_notification, reply_markup, timeout, + google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters) ) async def send_contact( @@ -3808,7 +4003,8 @@ async def send_contact( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -3852,6 +4048,9 @@ async def send_contact( :param message_thread_id: The thread to which the message will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3860,11 +4059,21 @@ async def send_contact( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + return types.Message.de_json( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, - disable_notification, reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content, message_thread_id) + disable_notification, reply_markup, timeout, + protect_content, message_thread_id, reply_parameters) ) async def send_chat_action( @@ -4931,7 +5140,8 @@ async def send_game( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Used to send the game. @@ -4963,6 +5173,9 @@ async def send_game( :param message_thread_id: Identifier of the thread to which the message will be sent. :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` @@ -4971,10 +5184,20 @@ async def send_game( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, - reply_to_message_id, reply_markup, timeout, - allow_sending_without_reply, protect_content, message_thread_id) + reply_markup, timeout, + protect_content, message_thread_id, reply_parameters) return types.Message.de_json(result) async def set_game_score( @@ -5071,7 +5294,8 @@ async def send_invoice( max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Sends invoice. @@ -5172,6 +5396,9 @@ async def send_invoice( :param message_thread_id: The identifier of a message thread, in which the invoice message will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` @@ -5180,13 +5407,23 @@ async def send_invoice( protect_content = self.protect_content if (protect_content is None) else protect_content allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + result = await asyncio_helper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, currency, prices, start_parameter, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, - reply_to_message_id, reply_markup, provider_data, timeout, allow_sending_without_reply, - max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id) + reply_markup, provider_data, timeout, + max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters) return types.Message.de_json(result) @@ -5311,7 +5548,8 @@ async def send_poll( timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -5381,6 +5619,9 @@ async def send_poll( :param message_thread_id: The identifier of a message thread, in which the poll will be sent :type message_thread_id: :obj:`int` + + :param reply_parameters: Reply parameters. + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` @@ -5390,6 +5631,16 @@ async def send_poll( allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply explanation_parse_mode = self.parse_mode if (explanation_parse_mode is None) else explanation_parse_mode + if allow_sending_without_reply or reply_to_message_id: + # show a deprecation warning + logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + allow_sending_without_reply=allow_sending_without_reply, + message_id=reply_to_message_id + ) + if isinstance(question, types.Poll): raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") @@ -5399,8 +5650,8 @@ async def send_poll( question, options, is_anonymous, type, allows_multiple_answers, correct_option_id, explanation, explanation_parse_mode, open_period, close_date, is_closed, - disable_notification, reply_to_message_id, allow_sending_without_reply, - reply_markup, timeout, explanation_entities, protect_content, message_thread_id)) + disable_notification, + reply_markup, timeout, explanation_entities, protect_content, message_thread_id, reply_parameters)) 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 91538abc4..3cfd3fed8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -278,16 +278,14 @@ async def _check_result(method_name, result: aiohttp.ClientResponse): async def send_message( token, chat_id, text, - disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None, + disable_web_page_preview=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + entities=None, protect_content=None, + message_thread_id=None, reply_parameters=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} if disable_web_page_preview is not None: params['disable_web_page_preview'] = disable_web_page_preview - if reply_to_message_id: - params['reply_to_message_id'] = reply_to_message_id if reply_markup: params['reply_markup'] = await _convert_markup(reply_markup) if parse_mode: @@ -298,8 +296,8 @@ async def send_message( params['timeout'] = timeout if entities: params['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) - if allow_sending_without_reply is not None: - params['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + params['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: params['protect_content'] = protect_content if message_thread_id: @@ -406,8 +404,8 @@ async def forward_message( async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, - disable_notification=None, reply_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, protect_content=None, message_thread_id=None): + disable_notification=None, + reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -418,12 +416,10 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['caption_entities'] = await _convert_entites(caption_entities) if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup is not None: payload['reply_markup'] = await _convert_markup(reply_markup) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if timeout: payload['timeout'] = timeout if protect_content is not None: @@ -435,36 +431,34 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p async def send_dice( token, chat_id, - emoji=None, disable_notification=None, reply_to_message_id=None, - reply_markup=None, timeout=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): + emoji=None, disable_notification=None, + reply_markup=None, timeout=None, protect_content=None, + message_thread_id=None,reply_parameters=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: payload['emoji'] = emoji if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return await _process_request(token, method_url, params=payload) async def send_photo( token, chat_id, photo, - caption=None, reply_to_message_id=None, reply_markup=None, + caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None, has_spoiler=None): + caption_entities=None, protect_content=None, + message_thread_id=None, has_spoiler=None,reply_parameters=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -476,8 +470,6 @@ async def send_photo( files = {'photo': photo} if caption: payload['caption'] = caption - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if parse_mode: @@ -488,8 +480,8 @@ async def send_photo( payload['timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -501,23 +493,21 @@ async def send_photo( async def send_media_group( token, chat_id, media, - disable_notification=None, reply_to_message_id=None, - timeout=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + disable_notification=None, + timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendMediaGroup' media_json, files = await convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return await _process_request( token, method_url, params=payload, method='post' if files else 'get', @@ -526,10 +516,10 @@ async def send_media_group( async def send_location( token, chat_id, latitude, longitude, - live_period=None, reply_to_message_id=None, + live_period=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -540,10 +530,8 @@ async def send_location( payload['heading'] = heading if proximity_alert_radius: payload['proximity_alert_radius'] = proximity_alert_radius - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if disable_notification is not None: @@ -603,9 +591,9 @@ async def stop_message_live_location( async def send_venue( token, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, disable_notification=None, - reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, google_place_id=None, - google_place_type=None, protect_content=None, message_thread_id=None): + reply_markup=None, timeout=None, + google_place_id=None, + google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -614,14 +602,12 @@ async def send_venue( payload['foursquare_type'] = foursquare_type if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if google_place_id: payload['google_place_id'] = google_place_id if google_place_type: @@ -635,8 +621,8 @@ async def send_venue( async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + disable_notification=None, reply_markup=None, timeout=None, + protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -645,14 +631,12 @@ async def send_contact( payload['vcard'] = vcard if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -670,10 +654,10 @@ async def send_chat_action(token, chat_id, action, timeout=None, message_thread_ return await _process_request(token, method_url, params=payload) -async def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, +async def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, - thumbnail=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None, - protect_content=None, message_thread_id=None, has_spoiler=None): + thumbnail=None, width=None, height=None, caption_entities=None, + protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -685,8 +669,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to payload['duration'] = duration if caption: payload['caption'] = caption - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if parse_mode: @@ -711,8 +695,6 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to payload['height'] = height if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -723,10 +705,10 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_to async def send_animation( - token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None, + token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, - allow_sending_without_reply=None, width=None, height=None, protect_content=None, message_thread_id=None, - has_spoiler=None): + width=None, height=None, protect_content=None, message_thread_id=None, + has_spoiler=None,reply_parameters=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -738,8 +720,8 @@ async def send_animation( payload['duration'] = duration if caption: payload['caption'] = caption - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if parse_mode: @@ -758,8 +740,6 @@ async def send_animation( payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if width: payload['width'] = width if height: @@ -773,9 +753,9 @@ async def send_animation( return await _process_request(token, method_url, params=payload, files=files, method='post') -async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None, +async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -787,8 +767,8 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_t payload['caption'] = caption if duration: payload['duration'] = duration - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if parse_mode: @@ -799,8 +779,6 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_t payload['timeout'] = timeout if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -808,9 +786,9 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_t return await _process_request(token, method_url, params=payload, files=files, method='post') -async def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None, - disable_notification=None, timeout=None, thumbnail=None, allow_sending_without_reply=None, protect_content=None, - message_thread_id=None): +async def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, + disable_notification=None, timeout=None, thumbnail=None, protect_content=None, + message_thread_id=None,reply_parameters=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -824,8 +802,8 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl payload['length'] = length else: payload['length'] = 639 # seems like it is MAX length size - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if disable_notification is not None: @@ -840,8 +818,6 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl files = {'thumbnail': thumbnail} else: payload['thumbnail'] = thumbnail - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -849,9 +825,9 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, repl return await _process_request(token, method_url, params=payload, files=files, method='post') -async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None, +async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, - caption_entities=None, allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -867,8 +843,8 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['performer'] = performer if title: payload['title'] = title - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if parse_mode: @@ -887,8 +863,6 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -896,10 +870,10 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform return await _process_request(token, method_url, params=payload, files=files, method='post') -async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None, +async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, - allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, - message_thread_id=None, emoji=None): + disable_content_type_detection=None, visible_file_name=None, protect_content=None, + message_thread_id=None, emoji=None,reply_parameters=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -910,8 +884,8 @@ async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, r files = {data_type: file_data} else: payload[data_type] = data - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if parse_mode and data_type == 'document': @@ -932,8 +906,6 @@ async def send_data(token, chat_id, data, data_type, reply_to_message_id=None, r payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: @@ -1386,20 +1358,18 @@ async def delete_message(token, chat_id, message_id, timeout=None): async def send_game( token, chat_id, game_short_name, - disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None, - allow_sending_without_reply=None, protect_content=None, message_thread_id=None): + disable_notification=None, reply_markup=None, timeout=None, + protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -1467,9 +1437,9 @@ async def send_invoice( start_parameter = None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, - disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None, - timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None): + disable_notification=None, reply_markup=None, provider_data=None, + timeout=None, max_tip_amount=None, suggested_tip_amounts=None, + protect_content=None, message_thread_id=None,reply_parameters=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1535,16 +1505,14 @@ async def send_invoice( payload['is_flexible'] = is_flexible if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_to_message_id: - payload['reply_to_message_id'] = reply_to_message_id + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) if provider_data: payload['provider_data'] = provider_data if timeout: payload['timeout'] = timeout - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply if max_tip_amount is not None: payload['max_tip_amount'] = max_tip_amount if suggested_tip_amounts is not None: @@ -1788,8 +1756,8 @@ async 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_to_message_id=None, allow_sending_without_reply=None, - reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None): + disable_notification=False, + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1820,10 +1788,8 @@ async def send_poll( if disable_notification: payload['disable_notification'] = disable_notification - if reply_to_message_id is not None: - payload['reply_to_message_id'] = reply_to_message_id - if allow_sending_without_reply is not None: - payload['allow_sending_without_reply'] = allow_sending_without_reply + if reply_parameters is not None: + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup is not None: payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: From 545cedb8e20662650f0f1ba9be4b725f375af3f8 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 23:24:33 +0500 Subject: [PATCH 1395/1808] Fix some issues with the previous commit --- telebot/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f8f60f987..3b1f9c91d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1604,7 +1604,7 @@ def send_message( apihelper.send_message( self.token, chat_id, text, disable_web_page_preview, reply_markup, parse_mode, disable_notification, timeout, - entities, protect_content=protect_content, message_thread_id=message_thread_id)) + entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters)) def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -1847,7 +1847,8 @@ def send_photo( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - has_spoiler: Optional[bool]=None) -> types.Message: + has_spoiler: Optional[bool]=None, + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -1895,6 +1896,9 @@ def send_photo( :param has_spoiler: Pass True, if the photo should be sent as a spoiler :type has_spoiler: :obj:`bool` + + :param reply_parameters: Additional parameters for replies to messages + :type reply_parameters: :class:`telebot.types.ReplyParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -1936,7 +1940,7 @@ def send_audio( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[bool]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, From b8ef19d67e3541e0a755907551067ab38a45f9bd Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 23:33:19 +0500 Subject: [PATCH 1396/1808] Added support for linkpreviewoptions --- telebot/__init__.py | 37 ++++++++++++++++++++++++++++++++----- telebot/apihelper.py | 14 +++++++------- telebot/async_telebot.py | 35 ++++++++++++++++++++++++++++++----- telebot/asyncio_helper.py | 14 +++++++------- telebot/types.py | 21 +++++++++++++++++++-- 5 files changed, 95 insertions(+), 26 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3b1f9c91d..4e223581f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1531,7 +1531,8 @@ def send_message( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + link_preview_options : Optional[types.ReplyParameters]=None) -> types.Message: """ Use this method to send text messages. @@ -1581,6 +1582,9 @@ def send_message( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param link_preview_options: Link preview options. + :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1600,11 +1604,20 @@ def send_message( message_id=reply_to_message_id ) + if disable_web_page_preview: + # show a deprecation warning + logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") + + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + disable_web_page_preview=disable_web_page_preview + ) + return types.Message.de_json( apihelper.send_message( - self.token, chat_id, text, disable_web_page_preview, + self.token, chat_id, text, reply_markup, parse_mode, disable_notification, timeout, - entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters)) + entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, link_preview_options=link_preview_options)) def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -4050,7 +4063,8 @@ def edit_message_text( parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + link_preview_options : Optional[types.LinkPreviewOptions]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -4080,14 +4094,27 @@ def edit_message_text( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`InlineKeyboardMarkup` + :param link_preview_options: A JSON-serialized object for options used to automatically generate previews for links. + :type link_preview_options: :obj:`LinkPreviewOptions` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview + if disable_web_page_preview: + # show a deprecation warning + logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") + + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + disable_web_page_preview=disable_web_page_preview + ) + + result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - entities, disable_web_page_preview, reply_markup) + entities, reply_markup, link_preview_options) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c44ac49e3..869f5b52c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -237,14 +237,14 @@ def download_file(token, file_path): def send_message( token, chat_id, text, - disable_web_page_preview=None, reply_markup=None, + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, - message_thread_id=None, reply_parameters=None): + message_thread_id=None, reply_parameters=None, link_preview_options=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} - if disable_web_page_preview is not None: - payload['disable_web_page_preview'] = disable_web_page_preview + if link_preview_options is not None: + payload['link_preview'] = link_preview_options.to_json() if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -1300,7 +1300,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, - entities = None, disable_web_page_preview=None, reply_markup=None): + entities = None, reply_markup=None, link_preview_options=None): method_url = r'editMessageText' payload = {'text': text} if chat_id: @@ -1313,10 +1313,10 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message 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: payload['reply_markup'] = _convert_markup(reply_markup) + if link_preview_options is not None: + payload['link_preview'] = link_preview_options.to_json() return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7546f2a84..955dcfd33 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2515,7 +2515,8 @@ async def send_message( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + link_preview_options: Optional[types.LinkPreviewOptions]=None) -> types.Message: """ Use this method to send text messages. @@ -2565,6 +2566,9 @@ async def send_message( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param link_preview_options: Options for previewing links. + :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2583,12 +2587,20 @@ async def send_message( allow_sending_without_reply=allow_sending_without_reply, message_id=reply_to_message_id ) + if disable_web_page_preview: + # show a deprecation warning + logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") + + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + disable_web_page_preview=disable_web_page_preview + ) return types.Message.de_json( await asyncio_helper.send_message( - self.token, chat_id, text, disable_web_page_preview, + self.token, chat_id, text, reply_markup, parse_mode, disable_notification, timeout, - entities, protect_content, message_thread_id, reply_parameters)) + entities, protect_content, message_thread_id, reply_parameters, link_preview_options)) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -5025,7 +5037,8 @@ async def edit_message_text( parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + link_preview_options: Optional[types.LinkPreviewOptions]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -5055,14 +5068,26 @@ async def edit_message_text( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`InlineKeyboardMarkup` + :param link_preview_options: A JSON-serialized object for options used to automatically generate Telegram link previews for messages. + :type link_preview_options: :obj:`LinkPreviewOptions` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview + if disable_web_page_preview: + # show a deprecation warning + logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") + + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + disable_web_page_preview=disable_web_page_preview + ) + result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - entities, disable_web_page_preview, reply_markup) + entities, reply_markup, link_preview_options) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3cfd3fed8..2aeebff32 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -278,14 +278,14 @@ async def _check_result(method_name, result: aiohttp.ClientResponse): async def send_message( token, chat_id, text, - disable_web_page_preview=None, reply_markup=None, + reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, - message_thread_id=None, reply_parameters=None): + message_thread_id=None, reply_parameters=None, link_preview_options=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} - if disable_web_page_preview is not None: - params['disable_web_page_preview'] = disable_web_page_preview + if link_preview_options is not None: + params['link_preview_options'] = json.dumps(link_preview_options.to_dict()) if reply_markup: params['reply_markup'] = await _convert_markup(reply_markup) if parse_mode: @@ -1278,7 +1278,7 @@ async def unpin_all_chat_messages(token, chat_id): # Updating messages async def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, - entities = None, disable_web_page_preview=None, reply_markup=None): + entities = None, reply_markup=None, link_preview_options=None): method_url = r'editMessageText' payload = {'text': text} if chat_id: @@ -1291,10 +1291,10 @@ async def edit_message_text(token, text, chat_id=None, message_id=None, inline_m 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: payload['reply_markup'] = await _convert_markup(reply_markup) + if link_preview_options is not None: + payload['link_preview_options'] = link_preview_options.to_json() return await _process_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 4c53c3664..31365b01d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8409,7 +8409,7 @@ def __init__(self, date: int, chat: Optional[Chat] = None, message_id: Optional[ self.author_signature: Optional[str] = author_signature -class LinkPreviewOptions(JsonDeserializable): +class LinkPreviewOptions(JsonDeserializable, Dictionaryable, JsonSerializable): """ Describes the options used for link preview generation. @@ -8453,6 +8453,24 @@ def __init__(self, is_disabled: Optional[bool] = None, url: Optional[str] = None self.prefer_large_media: Optional[bool] = prefer_large_media self.show_above_text: Optional[bool] = show_above_text + def to_dict(self) -> dict: + json_dict = {} + + if self.is_disabled is not None: + json_dict['is_disabled'] = self.is_disabled + if self.url is not None: + json_dict['url'] = self.url + if self.prefer_small_media is not None: + json_dict['prefer_small_media'] = self.prefer_small_media + if self.prefer_large_media is not None: + json_dict['prefer_large_media'] = self.prefer_large_media + if self.show_above_text is not None: + json_dict['show_above_text'] = self.show_above_text + + + def to_json(self) -> str: + return json.dumps(self.to_dict()) + class Giveaway(JsonDeserializable): """ @@ -8701,4 +8719,3 @@ def to_dict(self) -> dict: def to_json(self) -> str: return json.dumps(self.to_dict()) - \ No newline at end of file From 0485c31ef4202f2828c037ddb448eb6a92696436 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 23:37:39 +0500 Subject: [PATCH 1397/1808] Replaced the field disable_web_page_preview with link_preview_options in the class InputTextMessageContent. --- telebot/types.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 31365b01d..c76b8d049 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3628,11 +3628,16 @@ class InputTextMessageContent(Dictionaryable): :return: Instance of the class :rtype: :class:`telebot.types.InputTextMessageContent` """ - def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None): + def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None, link_preview_options=None): self.message_text: str = message_text self.parse_mode: str = parse_mode self.entities: List[MessageEntity] = entities - self.disable_web_page_preview: bool = disable_web_page_preview + link_preview_options: LinkPreviewOptions = link_preview_options + if disable_web_page_preview is not None and link_preview_options is None: + # deprecated + self.link_preview_options: LinkPreviewOptions = LinkPreviewOptions(disable_web_page_preview) + + def to_dict(self): json_dict = {'message_text': self.message_text} @@ -3640,8 +3645,8 @@ def to_dict(self): json_dict['parse_mode'] = self.parse_mode if self.entities: json_dict['entities'] = MessageEntity.to_list_of_dicts(self.entities) - if self.disable_web_page_preview is not None: - json_dict['disable_web_page_preview'] = self.disable_web_page_preview + if self.link_preview_options: + json_dict['link_preview_options'] = self.link_preview_options.to_dict() return json_dict From a26117416b3cd8a40ae92c7ad8fbfd5e4dde781e Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 23:39:35 +0500 Subject: [PATCH 1398/1808] Added the field link_preview_options to the class Message with information about the link preview options used to send the message. --- telebot/types.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index c76b8d049..26cd50dca 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -836,6 +836,10 @@ class Message(JsonDeserializable): appear in the text :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param link_preview_options: Optional. Options used for link preview generation for the message, + if it is a text message and link preview options were changed + :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :param animation: Optional. Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set :type animation: :class:`telebot.types.Animation` @@ -1221,6 +1225,10 @@ def de_json(cls, json_string): opts['quote'] = TextQuote.de_json(obj['quote']) content_type = 'text' # Here too, check the content types + if 'link_preview_options' in obj: + opts['link_preview_options'] = LinkPreviewOptions.de_json(obj['link_preview_options']) + + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1321,6 +1329,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.story: Optional[Story] = None self.external_reply: Optional[ExternalReplyInfo] = None self.quote: Optional[TextQuote] = None + self.LinkPreviewOptions: Optional[LinkPreviewOptions] = None for key in options: setattr(self, key, options[key]) From c406e6c972a9afbb2fcc1676687a44a8ad1bfa20 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 23:52:14 +0500 Subject: [PATCH 1399/1808] Added Copy,Delete,Forward Messages to sync and async --- telebot/__init__.py | 91 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 46 ++++++++++++++++++++ telebot/async_telebot.py | 90 ++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 49 +++++++++++++++++++++ 4 files changed, 276 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4e223581f..daad4a88a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1776,6 +1776,97 @@ def delete_message(self, chat_id: Union[int, str], message_id: int, :rtype: :obj:`bool` """ return apihelper.delete_message(self.token, chat_id, message_id, timeout) + + def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): + """ + Use this method to delete multiple messages in a chat. + The number of messages to be deleted must not exceed 100. + If the chat is a private chat, the user must be an administrator of the chat for this to work and must have the appropriate admin rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletemessages + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_ids: Identifiers of the messages to be deleted + :type message_ids: :obj:`list` of :obj:`int` + + :return: Returns True on success. + + """ + return apihelper.delete_messages(self.token, chat_id, message_ids) + + def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, + message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: + """ + Use this method to forward messages of any kind. + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + + :param message_ids: Message identifiers in the chat specified in from_chat_id + :type message_ids: :obj:`list` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` + + :param message_thread_id: Identifier of a message thread, in which the messages will be sent + :type message_thread_id: :obj:`int` + + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.MessageID` + """ + + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + + return types.MessageID.de_json( + apihelper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id)) + + def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], + disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, + protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: + """ + Use this method to copy messages of any kind. + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + + :param message_ids: Message identifiers in the chat specified in from_chat_id + :type message_ids: :obj:`list` of :obj:`int` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` + + :param message_thread_id: Identifier of a message thread, in which the messages will be sent + :type message_thread_id: :obj:`int` + + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :type protect_content: :obj:`bool` + + :param remove_caption: Pass True to copy the messages without their captions + :type remove_caption: :obj:`bool` + + :return: On success, an array of MessageId of the sent messages is returned. + :rtype: :obj:`list` of :class:`telebot.types.MessageID` + """ + disable_notification = self.disable_notification if disable_notification is None else disable_notification + protect_content = self.protect_content if protect_content is None else protect_content + + return [types.MessageID.de_json(message_id) for message_id in + apihelper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, + protect_content, message_thread_id, remove_caption)] + def send_dice( self, chat_id: Union[int, str], diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 869f5b52c..88a4e697a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1904,6 +1904,52 @@ def unhide_general_forum_topic(token, chat_id): payload = {'chat_id': chat_id} return _make_request(token, method_url, params=payload) +def delete_messages(token, chat_id, message_ids): + method_url = 'deleteMessages' + payload = { + 'chat_id': chat_id, + 'message_ids': message_ids + } + return _make_request(token, method_url, params=payload) + +def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, + message_thread_id=None, protect_content=None): + method_url = 'forwardMessages' + payload = { + 'chat_id': chat_id, + 'from_chat_id': from_chat_id, + 'message_ids': message_ids, + } + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id + if protect_content is not None: + payload['protect_content'] = protect_content + + result = _make_request(token, method_url, params=payload) + return result + +def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, + message_thread_id=None, protect_content=None, remove_caption=None): + method_url = 'copyMessages' + payload = { + 'chat_id': chat_id, + 'from_chat_id': from_chat_id, + 'message_ids': message_ids, + } + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id + if protect_content is not None: + payload['protect_content'] = protect_content + if remove_caption is not None: + payload['remove_caption'] = remove_caption + + result = _make_request(token, method_url, params=payload) + return result + def _convert_list_json_serializable(results): ret = '' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 955dcfd33..4adc7b424 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2758,6 +2758,96 @@ async def delete_message(self, chat_id: Union[int, str], message_id: int, :rtype: :obj:`bool` """ return await asyncio_helper.delete_message(self.token, chat_id, message_id, timeout) + + async def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): + """ + Use this method to delete multiple messages in a chat. + The number of messages to be deleted must not exceed 100. + If the chat is a private chat, the user must be an administrator of the chat for this to work and must have the appropriate admin rights. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletemessages + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_ids: Identifiers of the messages to be deleted + :type message_ids: :obj:`list` of :obj:`int` + + :return: Returns True on success. + + """ + return await asyncio_helper.delete_messages(self.token, chat_id, message_ids) + + async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, + message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: + """ + Use this method to forward messages of any kind. + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + + :param message_ids: Message identifiers in the chat specified in from_chat_id + :type message_ids: :obj:`list` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` + + :param message_thread_id: Identifier of a message thread, in which the messages will be sent + :type message_thread_id: :obj:`int` + + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :type protect_content: :obj:`bool` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.MessageID` + """ + + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + result = await asyncio_helper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id) + return types.MessageID.de_json( + result) + + async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], + disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, + protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: + """ + Use this method to copy messages of any kind. + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` + + :param message_ids: Message identifiers in the chat specified in from_chat_id + :type message_ids: :obj:`list` of :obj:`int` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` + + :param message_thread_id: Identifier of a message thread, in which the messages will be sent + :type message_thread_id: :obj:`int` + + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :type protect_content: :obj:`bool` + + :param remove_caption: Pass True to copy the messages without their captions + :type remove_caption: :obj:`bool` + + :return: On success, an array of MessageId of the sent messages is returned. + :rtype: :obj:`list` of :class:`telebot.types.MessageID` + """ + disable_notification = self.disable_notification if disable_notification is None else disable_notification + protect_content = self.protect_content if protect_content is None else protect_content + result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, + protect_content, message_thread_id, remove_caption) + return [types.MessageID.de_json(message_id) for message_id in + result] async def send_dice( self, chat_id: Union[int, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 2aeebff32..ae119078e 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1871,6 +1871,53 @@ async def unhide_general_forum_topic(token, chat_id): payload = {'chat_id': chat_id} return await _process_request(token, method_url, params=payload) +async def delete_messages(token, chat_id, message_ids): + method_url = 'deleteMessages' + payload = { + 'chat_id': chat_id, + 'message_ids': message_ids + } + return await _process_request(token, method_url, params=payload) + +async def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, + message_thread_id=None, protect_content=None): + method_url = 'forwardMessages' + payload = { + 'chat_id': chat_id, + 'from_chat_id': from_chat_id, + 'message_ids': message_ids, + } + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id + if protect_content is not None: + payload['protect_content'] = protect_content + + result = await _process_request(token, method_url, params=payload) + return result + +async def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, + message_thread_id=None, protect_content=None, remove_caption=None): + method_url = 'copyMessages' + payload = { + 'chat_id': chat_id, + 'from_chat_id': from_chat_id, + 'message_ids': message_ids, + } + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id + if protect_content is not None: + payload['protect_content'] = protect_content + if remove_caption is not None: + payload['remove_caption'] = remove_caption + + result = await _process_request(token, method_url, params=payload) + return result + + async def _convert_list_json_serializable(results): ret = '' for r in results: @@ -1999,3 +2046,5 @@ class RequestTimeout(Exception): This class represents a request timeout. """ pass + + From 254387d9850c1bbf51b84b9c65f6f5058662bc86 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 29 Dec 2023 23:58:52 +0500 Subject: [PATCH 1400/1808] Renamed the class KeyboardButtonRequestUser to KeyboardButtonRequestUsers and added the field max_quantity to it. Renamed the field request_user in the class KeyboardButton to request_users. The old name will still work for backward compatibility. --- telebot/types.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 26cd50dca..13a4333b8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2337,13 +2337,12 @@ def to_dict(self): return {'type': self.type} - -class KeyboardButtonRequestUser(Dictionaryable): +class KeyboardButtonRequestUsers(Dictionaryable): """ This object defines the criteria used to request a suitable user. The identifier of the selected user will be shared with the bot when the corresponding button is pressed. - Telegram documentation: https://core.telegram.org/bots/api#keyboardbuttonrequestuser + Telegram documentation: https://core.telegram.org/bots/api#keyboardbuttonrequestusers :param request_id: Signed 32-bit identifier of the request, which will be received back in the UserShared object. Must be unique within the message @@ -2357,15 +2356,19 @@ class KeyboardButtonRequestUser(Dictionaryable): If not specified, no additional restrictions are applied. :type user_is_premium: :obj:`bool` + :param max_quantity: Optional. The maximum number of users to be selected; 1-10. Defaults to 1. + :type max_quantity: :obj:`int` + :return: Instance of the class - :rtype: :class:`telebot.types.KeyboardButtonRequestUser` + :rtype: :class:`telebot.types.KeyboardButtonRequestUsers` """ - def __init__(self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None) -> None: + def __init__(self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, max_quantity: Optional[int]=None) -> None: self.request_id: int = request_id self.user_is_bot: Optional[bool] = user_is_bot self.user_is_premium: Optional[bool] = user_is_premium + self.max_quantity: Optional[int] = max_quantity def to_dict(self) -> dict: data = {'request_id': self.request_id} @@ -2373,8 +2376,11 @@ def to_dict(self) -> dict: data['user_is_bot'] = self.user_is_bot if self.user_is_premium is not None: data['user_is_premium'] = self.user_is_premium + if self.max_quantity is not None: + data['max_quantity'] = self.max_quantity return data +KeyboardButtonRequestUser = KeyboardButtonRequestUsers class KeyboardButtonRequestChat(Dictionaryable): """ @@ -2476,7 +2482,7 @@ class KeyboardButton(Dictionaryable, JsonSerializable): :param request_user: Optional. If specified, pressing the button will open a list of suitable users. Tapping on any user will send their identifier to the bot in a “user_shared” service message. Available in private chats only. - :type request_user: :class:`telebot.types.KeyboardButtonRequestUser` + :type request_user: :class:`telebot.types.KeyboardButtonRequestUsers` :param request_chat: Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a “chat_shared” service message. Available in private chats only. @@ -2487,15 +2493,18 @@ class KeyboardButton(Dictionaryable, JsonSerializable): """ def __init__(self, text: str, request_contact: Optional[bool]=None, request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, - web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUser]=None, - request_chat: Optional[KeyboardButtonRequestChat]=None): + web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUsers]=None, + request_chat: Optional[KeyboardButtonRequestChat]=None, request_users: Optional[KeyboardButtonRequestUsers]=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location self.request_poll: KeyboardButtonPollType = request_poll self.web_app: WebAppInfo = web_app - self.request_user: KeyboardButtonRequestUser = request_user + self.request_user: KeyboardButtonRequestUsers = request_user self.request_chat: KeyboardButtonRequestChat = request_chat + self.request_users: KeyboardButtonRequestUsers = request_users + if request_user is not None: + self.request_users = request_user def to_json(self): @@ -7565,7 +7574,7 @@ def __init__(self, from_request: Optional[bool]=None, web_app_name: Optional[str class UserShared(JsonDeserializable): """ This object contains information about the user whose identifier was shared with the bot using a - `telebot.types.KeyboardButtonRequestUser` button. + `telebot.types.KeyboardButtonRequestUsers` button. Telegram documentation: https://core.telegram.org/bots/api#usershared From 350f7fea32188f5a4378a8e8b9ecc5b32bda3dd9 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 00:03:14 +0500 Subject: [PATCH 1401/1808] Added the class UsersShared. Replaced the field user_shared in the class Message with the field users_shared. --- telebot/types.py | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 13a4333b8..f68045d23 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -954,8 +954,8 @@ class Message(JsonDeserializable): the payment. More about payments » :type successful_payment: :class:`telebot.types.SuccessfulPayment` - :param user_shared: Optional. Service message: a user was shared with the bot - :type user_shared: :class:`telebot.types.UserShared` + :param users_shared: Optional. Service message: a user was shared with the bot + :type users_shared: :class:`telebot.types.UserShared` :param chat_shared: Optional. Service message: a chat was shared with the bot :type chat_shared: :class:`telebot.types.ChatShared` @@ -1209,9 +1209,9 @@ def de_json(cls, json_string): if 'write_access_allowed' in obj: opts['write_access_allowed'] = WriteAccessAllowed.de_json(obj['write_access_allowed']) content_type = 'write_access_allowed' - if 'user_shared' in obj: - opts['user_shared'] = UserShared.de_json(obj['user_shared']) - content_type = 'user_shared' + if 'users_shared' in obj: + opts['users_shared'] = UserShared.de_json(obj['users_shared']) + content_type = 'users_shared' # COMPATIBILITY BROKEN! if 'chat_shared' in obj: opts['chat_shared'] = ChatShared.de_json(obj['chat_shared']) content_type = 'chat_shared' @@ -1324,7 +1324,8 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.general_forum_topic_hidden: Optional[GeneralForumTopicHidden] = None self.general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = None self.write_access_allowed: Optional[WriteAccessAllowed] = None - self.user_shared: Optional[UserShared] = None + self.users_shared: Optional[UserShared] = None + self.user_shared: Optional[UserShared] = self.users_shared self.chat_shared: Optional[ChatShared] = None self.story: Optional[Story] = None self.external_reply: Optional[ExternalReplyInfo] = None @@ -8742,3 +8743,35 @@ def to_dict(self) -> dict: def to_json(self) -> str: return json.dumps(self.to_dict()) +class UsersShared(JsonDeserializable): + """ + This object contains information about the users whose identifiers were shared with the bot + using a KeyboardButtonRequestUsers button. + + Telegram documentation: https://core.telegram.org/bots/api#usersshared + + :param request_id: Identifier of the request + :type request_id: :obj:`int` + + :param user_ids: Identifiers of the shared users. These numbers may have more than 32 significant bits + and some programming languages may have difficulty/silent defects in interpreting them. + But they have at most 52 significant bits, so 64-bit integers or double-precision float + types are safe for storing these identifiers. The bot may not have access to the users and + could be unable to use these identifiers unless the users are already known to the bot by + some other means. + :type user_ids: :obj:`list` of :obj:`int` + + :return: Instance of the class + :rtype: :class:`UsersShared` + """ + + def __init__(self, request_id, user_ids): + self.request_id = request_id + self.user_ids = user_ids + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + return cls(**obj) \ No newline at end of file From e1c92237a69e758f028fc0a59e2c548ccca12925 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 12:42:51 +0500 Subject: [PATCH 1402/1808] Chat Boosts full support for handlers; --- telebot/__init__.py | 126 ++++++++++++++++ telebot/async_telebot.py | 123 ++++++++++++++++ telebot/types.py | 259 ++++++++++++++++++++++++++++++++- tests/test_handler_backends.py | 8 +- tests/test_telebot.py | 4 +- 5 files changed, 514 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index daad4a88a..223d679f5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -229,6 +229,8 @@ def __init__( self.my_chat_member_handlers = [] self.chat_member_handlers = [] self.chat_join_request_handlers = [] + self.chat_boost_handlers = [] + self.removed_chat_boost_handlers = [] self.custom_filters = {} self.state_handlers = [] @@ -684,6 +686,8 @@ def process_new_updates(self, updates: List[types.Update]): new_my_chat_members = None new_chat_members = None new_chat_join_request = None + removed_chat_boosts = None + chat_boosts = None for update in updates: if apihelper.ENABLE_MIDDLEWARE and not self.use_class_middlewares: @@ -747,6 +751,12 @@ def process_new_updates(self, updates: List[types.Update]): if update.message_reaction_count: if message_reaction_counts is None: message_reaction_counts = [] message_reaction_counts.append(update.message_reaction_count) + if update.chat_boost: + if chat_boosts is None: chat_boosts = [] + chat_boosts.append(update.chat_boost) + if update.removed_chat_boost: + if removed_chat_boosts is None: removed_chat_boosts = [] + removed_chat_boosts.append(update.removed_chat_boost) if new_messages: self.process_new_messages(new_messages) @@ -780,6 +790,10 @@ def process_new_updates(self, updates: List[types.Update]): self.process_new_message_reaction(new_message_reactions) if message_reaction_counts: self.process_new_message_reaction_count(message_reaction_counts) + if chat_boosts: + self.process_new_chat_boost(chat_boosts) + if removed_chat_boosts: + self.process_new_removed_chat_boost(removed_chat_boosts) def process_new_messages(self, new_messages): """ @@ -880,6 +894,19 @@ def process_new_chat_join_request(self, chat_join_request): """ self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') + def process_new_chat_boost(self, chat_boosts): + """ + :meta private: + """ + self._notify_command_handlers(self.chat_boost_handlers, chat_boosts, 'chat_boost') + + def process_new_removed_chat_boost(self, removed_chat_boosts): + """ + :meta private: + """ + self._notify_command_handlers(self.removed_chat_boost_handlers, removed_chat_boosts, 'removed_chat_boost') + + def process_middlewares(self, update): """ :meta private: @@ -7209,6 +7236,105 @@ def register_chat_join_request_handler(self, callback: Callable, func: Optional[ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_join_request_handler(handler_dict) + def chat_boost_handler(self, func=None, **kwargs): + """ + Handles new incoming chat boost state. + it passes :class:`telebot.types.ChatBoostUpdated` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_boost_handler(handler_dict) + return handler + + return decorator + + def add_chat_boost_handler(self, handler_dict): + """ + Adds a chat_boost handler. + Note that you should use register_chat_boost_handler to add chat_boost_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.chat_boost_handlers.append(handler_dict) + + def register_chat_boost_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + """ + Registers chat boost handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_chat_boost_handler(handler_dict) + + def removed_chat_boost_handler(self, func=None, **kwargs): + """ + Handles new incoming chat boost state. + it passes :class:`telebot.types.ChatBoostRemoved` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_removed_chat_boost_handler(handler_dict) + return handler + + return decorator + + def add_removed_chat_boost_handler(self, handler_dict): + """ + Adds a removed_chat_boost handler. + Note that you should use register_removed_chat_boost_handler to add removed_chat_boost_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.removed_chat_boost_handlers.append(handler_dict) + + def register_removed_chat_boost_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + """ + Registers removed chat boost handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_removed_chat_boost_handler(handler_dict) + + def _test_message_handler(self, message_handler, message): """ Test message handler diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4adc7b424..34cd56051 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -171,6 +171,8 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.my_chat_member_handlers = [] self.chat_member_handlers = [] self.chat_join_request_handlers = [] + self.removed_chat_boost_handlers = [] + self.chat_boost_handlers = [] self.custom_filters = {} self.state_handlers = [] self.middlewares = [] @@ -590,6 +592,8 @@ async def process_new_updates(self, updates: List[types.Update]): new_my_chat_members = None new_chat_members = None chat_join_request = None + removed_chat_boost_handlers = None + chat_boost_handlers = None for update in updates: logger.debug('Processing updates: {0}'.format(update)) if update.message: @@ -640,6 +644,12 @@ async def process_new_updates(self, updates: List[types.Update]): if update.message_reaction_count: if new_message_reaction_count_handlers is None: new_message_reaction_count_handlers = [] new_message_reaction_count_handlers.append(update.message_reaction_count) + if update.chat_boost: + if chat_boost_handlers is None: chat_boost_handlers = [] + chat_boost_handlers.append(update.chat_boost) + if update.removed_chat_boost: + if removed_chat_boost_handlers is None: removed_chat_boost_handlers = [] + removed_chat_boost_handlers.append(update.removed_chat_boost) if new_messages: @@ -674,6 +684,8 @@ async def process_new_updates(self, updates: List[types.Update]): await self.process_new_message_reaction(new_message_reactions) if new_message_reaction_count_handlers: await self.process_new_message_reaction_count(new_message_reaction_count_handlers) + if chat_boost_handlers: + await self.process_new_chat_boost(chat_boost_handlers) async def process_new_messages(self, new_messages): """ @@ -772,6 +784,18 @@ async def process_chat_join_request(self, chat_join_request): """ await self._process_updates(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') + async def process_new_chat_boost(self, chat_boost): + """ + :meta private: + """ + await self._process_updates(self.chat_boost_handlers, chat_boost, 'chat_boost') + + async def process_new_removed_chat_boost(self, removed_chat_boost): + """ + :meta private: + """ + await self._process_updates(self.removed_chat_boost_handlers, removed_chat_boost, 'removed_chat_boost') + async def _get_middlewares(self, update_type): """ :meta private: @@ -2002,6 +2026,105 @@ def register_chat_join_request_handler(self, callback: Callable[[Any], Awaitable handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_join_request_handler(handler_dict) + + def chat_boost_handler(self, func=None, **kwargs): + """ + Handles new incoming chat boost state. + it passes :class:`telebot.types.ChatBoostUpdated` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_chat_boost_handler(handler_dict) + return handler + + return decorator + + def add_chat_boost_handler(self, handler_dict): + """ + Adds a chat_boost handler. + Note that you should use register_chat_boost_handler to add chat_boost_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.chat_boost_handlers.append(handler_dict) + + def register_chat_boost_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + """ + Registers chat boost handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_chat_boost_handler(handler_dict) + + def removed_chat_boost_handler(self, func=None, **kwargs): + """ + Handles new incoming chat boost state. + it passes :class:`telebot.types.ChatBoostRemoved` object. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_removed_chat_boost_handler(handler_dict) + return handler + + return decorator + + def add_removed_chat_boost_handler(self, handler_dict): + """ + Adds a removed_chat_boost handler. + Note that you should use register_removed_chat_boost_handler to add removed_chat_boost_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.removed_chat_boost_handlers.append(handler_dict) + + def register_removed_chat_boost_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + """ + Registers removed chat boost handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_removed_chat_boost_handler(handler_dict) + @staticmethod def _build_handler_dict(handler, pass_bot=False, **filters): """ diff --git a/telebot/types.py b/telebot/types.py index f68045d23..dc7c15f49 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -173,6 +173,12 @@ class Update(JsonDeserializable): can_invite_users administrator right in the chat to receive these updates. :type chat_join_request: :class:`telebot.types.ChatJoinRequest` + :param chat_boost: Optional. A chat boost was added or changed. The bot must be an administrator in the chat to receive these updates. + :type chat_boost: :class:`telebot.types.ChatBoost` + + :param removed_chat_boost: Optional. A chat boost was removed. The bot must be an administrator in the chat to receive these updates. + :type removed_chat_boost: :class:`telebot.types.RemovedChatBoost` + :return: Instance of the class :rtype: :class:`telebot.types.Update` @@ -198,13 +204,15 @@ def de_json(cls, json_string): chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request')) message_reaction = MessageReactionUpdated.de_json(obj.get('message_reaction')) message_reaction_count = MessageReactionCountUpdated.de_json(obj.get('message_reaction_count')) + removed_chat_boost = ChatBoostRemoved.de_json(obj.get('removed_chat_boost')) + chat_boost = ChatBoost.de_json(obj.get('chat_boost')) 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) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, removed_chat_boost, chat_boost) 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): + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, removed_chat_boost, chat_boost): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -222,6 +230,8 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.chat_join_request = chat_join_request self.message_reaction = message_reaction self.message_reaction_count = message_reaction_count + self.removed_chat_boost = removed_chat_boost + self.chat_boost = chat_boost class ChatMemberUpdated(JsonDeserializable): @@ -8774,4 +8784,247 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - return cls(**obj) \ No newline at end of file + return cls(**obj) + + +class ChatBoostUpdated(JsonDeserializable): + """ + This object represents a boost added to a chat or changed. + + Telegram documentation: https://core.telegram.org/bots/api#chatboostupdated + + :param chat: Chat which was boosted + :type chat: :class:`Chat` + + :param boost: Infomation about the chat boost + :type boost: :class:`ChatBoost` + + :return: Instance of the class + :rtype: :class:`ChatBoostUpdated` + """ + + def __init__(self, chat, boost): + self.chat = chat + self.boost = boost + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + obj['chat'] = Chat.de_json(obj.get('chat')) + obj['boost'] = ChatBoost.de_json(obj.get('boost')) + + return cls(**obj) + +# ChatBoostRemoved +# This object represents a boost removed from a chat. + +# Field Type Description +# chat Chat Chat which was boosted +# boost_id String Unique identifier of the boost +# remove_date Integer Point in time (Unix timestamp) when the boost was removed +# source ChatBoostSource Source of the removed boost + +class ChatBoostRemoved(JsonDeserializable): + """ + This object represents a boost removed from a chat. + + Telegram documentation: https://core.telegram.org/bots/api#chatboostremoved + + :param chat: Chat which was boosted + :type chat: :class:`Chat` + + :param boost_id: Unique identifier of the boost + :type boost_id: :obj:`str` + + :param remove_date: Point in time (Unix timestamp) when the boost was removed + :type remove_date: :obj:`int` + + :param source: Source of the removed boost + :type source: :class:`ChatBoostSource` + + :return: Instance of the class + :rtype: :class:`ChatBoostRemoved` + """ + + def __init__(self, chat, boost_id, remove_date, source): + self.chat = chat + self.boost_id = boost_id + self.remove_date = remove_date + self.source = source + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + obj['chat'] = Chat.de_json(obj.get('chat')) + obj['source'] = ChatBoostSource.de_json(obj.get('source')) + + return cls(**obj) + +class ChatBoostSource(JsonDeserializable): + """ + This object describes the source of a chat boost. + + Telegram documentation: https://core.telegram.org/bots/api#chatboostsource + + :param source: Source of the boost + :type source: :obj:`str` + + :return: Instance of the class + :rtype: :class:`ChatBoostSource` + """ + + def __init__(self, source): + self.source = source + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + return cls(**cls.check_json(json_string)) + + +class ChatBoostSourcePremium(ChatBoostSource): + """ + The boost was obtained by subscribing to Telegram Premium or by gifting a Telegram Premium subscription to another user. + + Telegram documentation: https://core.telegram.org/bots/api#chatboostsourcepremium + + :param source: Source of the boost, always “premium” + :type source: :obj:`str` + + :param user: User that boosted the chat + :type user: :class:`User` + + :return: Instance of the class + :rtype: :class:`ChatBoostSourcePremium` + """ + + def __init__(self, user): + super().__init__('premium') + self.user = user + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + user = User.de_json(json.dumps(obj['user'])) + return cls(user) + +class ChatBoostSourceGiftCode(ChatBoostSource): + """ + The boost was obtained by the creation of Telegram Premium gift codes to boost a chat. + + Telegram documentation: https://core.telegram.org/bots/api#chatboostsourcegiftcode + + :param source: Source of the boost, always “gift_code” + :type source: :obj:`str` + + :param user: User for which the gift code was created + :type user: :class:`User` + + :return: Instance of the class + :rtype: :class:`ChatBoostSourceGiftCode` + """ + + def __init__(self, user): + super().__init__('gift_code') + self.user = user + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + user = User.de_json(json.dumps(obj['user'])) + return cls(user) + +class ChatBoostSourceGiveaway(ChatBoostSource): + """ + The boost was obtained by the creation of a Telegram Premium giveaway. + + Telegram documentation: https://core.telegram.org/bots/api#chatboostsourcegiveaway + + :param source: Source of the boost, always “giveaway” + :type source: :obj:`str` + + :param giveaway_message_id: Identifier of a message in the chat with the giveaway; the message could have been deleted already. May be 0 if the message isn't sent yet. + :type giveaway_message_id: :obj:`int` + + :param user: User that won the prize in the giveaway if any + :type user: :class:`User` + + :param is_unclaimed: True, if the giveaway was completed, but there was no user to win the prize + :type is_unclaimed: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`ChatBoostSourceGiveaway` + """ + + def __init__(self, giveaway_message_id, user=None, is_unclaimed=None): + super().__init__('giveaway') + self.giveaway_message_id = giveaway_message_id + self.user = user + self.is_unclaimed = is_unclaimed + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + user = User.de_json(json.dumps(obj['user'])) if 'user' in obj else None + return cls(obj['giveaway_message_id'], user, obj.get('is_unclaimed')) + +#ChatBoost +# This object contains information about a chat boost. + +# Field Type Description +# boost_id String Unique identifier of the boost +# add_date Integer Point in time (Unix timestamp) when the chat was boosted +# expiration_date Integer Point in time (Unix timestamp) when the boost will automatically expire, unless the booster's Telegram Premium subscription is prolonged +# source ChatBoostSource Source of the added boost + +class ChatBoost(JsonDeserializable): + """ + This object contains information about a chat boost. + + Telegram documentation: https://core.telegram.org/bots/api#chatboost + + :param boost_id: Unique identifier of the boost + :type boost_id: :obj:`str` + + :param add_date: Point in time (Unix timestamp) when the chat was boosted + :type add_date: :obj:`int` + + :param expiration_date: Point in time (Unix timestamp) when the boost will automatically expire, unless the booster's Telegram Premium subscription is prolonged + :type expiration_date: :obj:`int` + + :param source: Source of the added boost + :type source: :class:`ChatBoostSource` + + :return: Instance of the class + :rtype: :class:`ChatBoost` + """ + + def __init__(self, boost_id, add_date, expiration_date, source): + self.boost_id = boost_id + self.add_date = add_date + self.expiration_date = expiration_date + self.source: ChatBoostSource = source + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + + obj['source'] = ChatBoostSource.de_json(json.dumps(obj['source'])) + + return cls(**obj) + diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index ef22e8520..fa893c489 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -67,9 +67,11 @@ def update_type(message): chat_join_request = None message_reaction = None message_reaction_count = None + chat_boost = None + chat_boost_removed = None return types.Update(1001234038283, 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) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed) @pytest.fixture() @@ -89,9 +91,11 @@ def reply_to_message_update_type(reply_to_message): chat_join_request = None message_reaction = None message_reaction_count = None + chat_boost = None + chat_boost_removed = None return types.Update(1001234038284, reply_to_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) + poll, poll_answer, my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed) def next_handler(message): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index c1f08f8f0..52adbc2fe 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -544,9 +544,11 @@ def create_message_update(text): chat_join_request = None message_reaction = None message_reaction_count = None + chat_boost = None + chat_boost_removed = None return types.Update(-1001234038283, 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) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed) def test_is_string_unicode(self): s1 = u'string' From 137ea69e88e8808ac1bc1b21d9e6bf010462eeee Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 12:48:00 +0500 Subject: [PATCH 1403/1808] get_user_chat_boosts for sync and async; +UserChatBoosts --- telebot/__init__.py | 21 ++++++++++++++++++++- telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 27 +++++++++++++++++++++++++++ telebot/asyncio_helper.py | 4 ++++ telebot/types.py | 26 ++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 223d679f5..e9120066a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -905,7 +905,7 @@ def process_new_removed_chat_boost(self, removed_chat_boosts): :meta private: """ self._notify_command_handlers(self.removed_chat_boost_handlers, removed_chat_boosts, 'removed_chat_boost') - + def process_middlewares(self, update): """ @@ -5069,6 +5069,25 @@ def answer_callback_query( :rtype: :obj:`bool` """ return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) + + def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types.UserChatBoosts: + """ + Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. + + Telegram documentation: https://core.telegram.org/bots/api#getuserchatboosts + + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :return: On success, a UserChatBoosts object is returned. + :rtype: :class:`telebot.types.UserChatBoosts` + """ + + result = apihelper.get_user_chat_boosts(self.token, chat_id, user_id) + return types.UserChatBoosts.de_json(result) def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 88a4e697a..a50b61830 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1616,6 +1616,11 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None, return _make_request(token, method_url, params=payload, method='post') +def get_user_chat_boosts(token, chat_id, user_id): + method_url = 'getUserChatBoosts' + payload = {'chat_id': chat_id, 'user_id': user_id} + return _make_request(token, method_url, params=payload) + def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, button=None): method_url = 'answerInlineQuery' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 34cd56051..6e02ce19a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6135,6 +6135,33 @@ async def answer_callback_query( :rtype: :obj:`bool` """ return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) + +# getUserChatBoosts +# Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. + +# Parameter Type Required Description +# chat_id Integer or String Yes Unique identifier for the chat or username of the channel (in the format @channelusername) +# user_id Integer Yes Unique identifier of the target user + + async def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types.UserChatBoosts: + """ + Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. + + Telegram documentation: https://core.telegram.org/bots/api#getuserchatboosts + + :param chat_id: Unique identifier for the target chat or username of the target channel + :type chat_id: :obj:`int` | :obj:`str` + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :return: On success, a UserChatBoosts object is returned. + :rtype: :class:`telebot.types.UserChatBoosts` + """ + + result = await asyncio_helper.get_user_chat_boosts(self.token, chat_id, user_id) + return types.UserChatBoosts.de_json(result) + async def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None): """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index ae119078e..5da6859cf 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1591,6 +1591,10 @@ async def answer_callback_query(token, callback_query_id, text=None, show_alert= payload['cache_time'] = cache_time return await _process_request(token, method_url, params=payload, method='post') +async def get_user_chat_boosts(token, chat_id, user_id): + method_url = 'getUserChatBoosts' + payload = {'chat_id': chat_id, 'user_id': user_id} + return await _process_request(token, method_url, params=payload) async def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None, button=None): diff --git a/telebot/types.py b/telebot/types.py index dc7c15f49..fe5241c17 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9028,3 +9028,29 @@ def de_json(cls, json_string): return cls(**obj) +class UserChatBoosts(JsonDeserializable): + """ + This object represents a list of boosts added to a chat by a user. + + Telegram documentation: https://core.telegram.org/bots/api#userchatboosts + + :param boosts: The list of boosts added to the chat by the user + :type boosts: :obj:`list` of :class:`ChatBoost` + + :return: Instance of the class + :rtype: :class:`UserChatBoosts` + """ + + def __init__(self, boosts): + self.boosts: ChatBoost = boosts + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + + obj = cls.check_json(json_string) + obj['boosts'] = [ChatBoost.de_json(boost) for boost in obj.get('boosts', [])] + + return cls(**obj) + \ No newline at end of file From ab2d1facd688d9d3e12f1204fad83070788ed873 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 12:59:39 +0500 Subject: [PATCH 1404/1808] Giveaways full support: start and end of giveaways, and information about winners --- telebot/types.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index fe5241c17..058ffa96c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1003,6 +1003,18 @@ class Message(JsonDeserializable): :param general_forum_topic_unhidden: Optional. Service message: the 'General' forum topic unhidden :type general_forum_topic_unhidden: :class:`telebot.types.GeneralForumTopicUnhidden` + :param giveaway_created: Optional. Service message: a giveaway has been created + :type giveaway_created: :class:`telebot.types.GiveawayCreated` + + :param giveaway: Optional. The message is a scheduled giveaway message + :type giveaway: :class:`telebot.types.Giveaway` + + :param giveaway_winners: Optional. Service message: giveaway winners(public winners) + :type giveaway_winners: :class:`telebot.types.GiveawayWinners` + + :param giveaway_completed: Optional. Service message: giveaway completed, without public winners + :type giveaway_completed: :class:`telebot.types.GiveawayCompleted` + :param video_chat_scheduled: Optional. Service message: video chat scheduled :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` @@ -1238,6 +1250,20 @@ def de_json(cls, json_string): if 'link_preview_options' in obj: opts['link_preview_options'] = LinkPreviewOptions.de_json(obj['link_preview_options']) + if 'giveaway_created' in obj: + opts['giveaway_created'] = GiveawayCreated.de_json(obj['giveaway_created']) + content_type = 'giveaway_created' + if 'giveaway' in obj: + opts['giveaway'] = Giveaway.de_json(obj['giveaway']) + content_type = 'giveaway' + if 'giveaway_winners' in obj: + opts['giveaway_winners'] = GiveawayWinners.de_json(obj['giveaway_winners']) + content_type = 'giveaway_winners' + if 'giveaway_completed' in obj: + opts['giveaway_completed'] = GiveawayCompleted.de_json(obj['giveaway_completed']) + content_type = 'giveaway_completed' + + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1341,6 +1367,11 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.external_reply: Optional[ExternalReplyInfo] = None self.quote: Optional[TextQuote] = None self.LinkPreviewOptions: Optional[LinkPreviewOptions] = None + self.giveaway_created: Optional[GiveawayCreated] = None + self.giveaway: Optional[Giveaway] = None + self.giveaway_winners: Optional[GiveawayWinners] = None + self.giveaway_completed: Optional[GiveawayCompleted] = None + for key in options: setattr(self, key, options[key]) @@ -8634,7 +8665,45 @@ def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: self.was_refunded: Optional[bool] = was_refunded self.prize_description: Optional[str] = prize_description +class GiveawayCompleted(JsonDeserializable): + """ + This object represents a service message about the completion of a giveaway without public winners. + + Telegram documentation: https://core.telegram.org/bots/api#giveawaycompleted + + :param winner_count: Number of winners in the giveaway + :type winner_count: :obj:`int` + + :param unclaimed_prize_count: Optional. Number of undistributed prizes + :type unclaimed_prize_count: :obj:`int` + + :param giveaway_message: Optional. Message with the giveaway that was completed, if it wasn't deleted + :type giveaway_message: :class:`Message` + + :return: Instance of the class + :rtype: :class:`GiveawayCompleted` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + obj = cls.check_json(json_string) + obj['giveaway_message'] = Message.de_json(obj.get('giveaway_message')) + + return cls(**obj) + + def __init__(self, winner_count: int, unclaimed_prize_count: Optional[int] = None, + giveaway_message: Optional[Message] = None) -> None: + self.winner_count: int = winner_count + self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count + self.giveaway_message: Optional[Message] = giveaway_message + +class GiveawayCreated(JsonDeserializable): + """ + This object represents a service message about the creation of a scheduled giveaway. Currently holds no information. + """ class TextQuote(JsonDeserializable): """ From e290f27f6a6ec0cc8910880b5e200d4e9b366e73 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 13:11:31 +0500 Subject: [PATCH 1405/1808] Added the fields has_visible_story, accent_color_id, background_custom_emoji_id, profile_accent_color_id, and profile_background_custom_emoji_id to the class Chat. --- telebot/types.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 058ffa96c..66a0e19f4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -565,6 +565,22 @@ class Chat(JsonDeserializable): 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. + :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. + :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. + :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. :type emoji_status_custom_emoji_id: :obj:`str` @@ -626,6 +642,10 @@ class Chat(JsonDeserializable): 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. + :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` @@ -671,7 +691,8 @@ def __init__(self, id, type, title=None, username=None, first_name=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, has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, - available_reactions=None,**kwargs): + 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,**kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -702,6 +723,12 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.has_aggressive_anti_spam_enabled: bool = has_aggressive_anti_spam_enabled self.emoji_status_expiration_date: int = emoji_status_expiration_date self.available_reactions: List[ReactionType] = available_reactions + self.accent_color_id: int = accent_color_id + self.background_custom_emoji_id: str = background_custom_emoji_id + self.profile_accent_color_id: int = profile_accent_color_id + self.profile_background_custom_emoji_id: str = profile_background_custom_emoji_id + self.has_visible_history: bool = has_visible_history + class MessageID(JsonDeserializable): From af6f485ba6e3a50d7770562c74746ec2f83cf305 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 13:19:02 +0500 Subject: [PATCH 1406/1808] Added the class MessageOrigin and replaced the fields forward_from, forward_from_chat, forward_from_message_id, forward_signature, forward_sender_name, and forward_date with the field forward_origin of type MessageOrigin in the class Message. --- telebot/types.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 66a0e19f4..63b69b14a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -833,6 +833,9 @@ class Message(JsonDeserializable): :param forward_date: Optional. For forwarded messages, date the original message was sent in Unix time :type forward_date: :obj:`int` + :forward_origin: Optional. For forwarded messages, information about the original message; + :type forward_origin: :class:`telebot.types.MessageOrigin` + :param is_topic_message: Optional. True, if the message is sent to a forum topic :type is_topic_message: :obj:`bool` @@ -1289,8 +1292,8 @@ def de_json(cls, json_string): if 'giveaway_completed' in obj: opts['giveaway_completed'] = GiveawayCompleted.de_json(obj['giveaway_completed']) content_type = 'giveaway_completed' - - + if 'message_origin' in obj: + opts['message_origin'] = MessageOrigin.de_json(obj['message_origin']) return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1398,6 +1401,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.giveaway: Optional[Giveaway] = None self.giveaway_winners: Optional[GiveawayWinners] = None self.giveaway_completed: Optional[GiveawayCompleted] = None + self.forward_origin: Optional[MessageOrigin] = None for key in options: From e94d5cecbc1602989fd8a7daa0623f5e139454fb Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 13:31:54 +0500 Subject: [PATCH 1407/1808] Added InaccessibleMessage support, not sure on the implementation for pinned_message and answer_callback_query --- telebot/types.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 63b69b14a..f47670917 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9153,4 +9153,39 @@ def de_json(cls, json_string): obj['boosts'] = [ChatBoost.de_json(boost) for boost in obj.get('boosts', [])] return cls(**obj) + + +class InaccessibleMessage(JsonDeserializable): + """ + This object describes a message that was deleted or is otherwise inaccessible to the bot. + + Telegram documentation: https://core.telegram.org/bots/api#inaccessiblemessage + + :param chat: Chat the message belonged to + :type chat: :class:`Chat` + + :param message_id: Unique message identifier inside the chat + :type message_id: :obj:`int` + + :param date: Always 0. The field can be used to differentiate regular and inaccessible messages. + :type date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`InaccessibleMessage` + """ + + def __init__(self, chat, message_id, date): + self.chat = chat + self.message_id = message_id + self.date = date + + @classmethod + def de_json(cls, json_string): + if json_string is None: + return None + + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj.get('chat')) + + return cls(**obj) \ No newline at end of file From 456a9ed3c8b6026cd860ab2fdacf8395f8270b2a Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 14:00:16 +0500 Subject: [PATCH 1408/1808] Minor utils edit that adds all update types --- telebot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 174239d35..9f0c7ab4c 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -45,7 +45,7 @@ update_types = [ "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" + "chat_join_request", "message_reaction", "message_reaction_count", "chat_boost", "removed_chat_boost", ] From 39abb9d555a2681c74ae07bb290be779164bd829 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 14:45:28 +0500 Subject: [PATCH 1409/1808] Fix a typo --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6e02ce19a..7e7ae4689 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -581,7 +581,7 @@ async def process_new_updates(self, updates: List[types.Update]): new_channel_posts = None new_edited_channel_posts = None new_message_reactions = None - mew_message_reaction_count_handlers = None + new_message_reaction_count_handlers = None new_inline_queries = None new_chosen_inline_results = None new_callback_queries = None From 87404d3f2a738a04bd108c17fdbab2f894c69067 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 14:52:58 +0500 Subject: [PATCH 1410/1808] Set func=None by default for new handlers; --- telebot/__init__.py | 8 ++++---- telebot/async_telebot.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e9120066a..39332c166 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6639,7 +6639,7 @@ def register_edited_channel_post_handler(self, callback: Callable, content_types self.add_edited_channel_post_handler(handler_dict) - def message_reaction_handler(self, func, **kwargs): + def message_reaction_handler(self, func=None, **kwargs): """ Handles new incoming message reaction. As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. @@ -6670,7 +6670,7 @@ def add_message_reaction_handler(self, handler_dict): """ self.message_reaction_handlers.append(handler_dict) - def register_message_reaction_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_message_reaction_handler(self, callback: Callable, func: Callable=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message reaction handler. @@ -6690,7 +6690,7 @@ def register_message_reaction_handler(self, callback: Callable, func: Callable, handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_message_reaction_handler(handler_dict) - def message_reaction_count_handler(self, func, **kwargs): + def message_reaction_count_handler(self, func=None, **kwargs): """ Handles new incoming message reaction count. As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. @@ -6721,7 +6721,7 @@ def add_message_reaction_count_handler(self, handler_dict): """ self.message_reaction_count_handlers.append(handler_dict) - def register_message_reaction_count_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_message_reaction_count_handler(self, callback: Callable, func: Callable=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message reaction count handler. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7e7ae4689..658443124 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1397,7 +1397,7 @@ def register_edited_channel_post_handler(self, callback: Callable[[Any], Awaitab **kwargs) self.add_edited_channel_post_handler(handler_dict) - def messsage_reaction_handler(self, func, **kwargs): + def message_reaction_handler(self, func=None, **kwargs): """ Handles new incoming message reaction. As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. @@ -1429,7 +1429,7 @@ def add_message_reaction_handler(self, handler_dict): """ self.message_reaction_handlers.append(handler_dict) - def register_message_reaction_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_message_reaction_handler(self, callback: Callable[[Any], Awaitable], func: Callable=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message reaction handler. @@ -1449,7 +1449,7 @@ def register_message_reaction_handler(self, callback: Callable[[Any], Awaitable] handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_message_reaction_handler(handler_dict) - def message_reaction_count_handler(self, func, **kwargs): + def message_reaction_count_handler(self, func=None, **kwargs): """ Handles new incoming message reaction count. As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. @@ -1481,7 +1481,7 @@ def add_message_reaction_count_handler(self, handler_dict): """ self.message_reaction_count_handlers.append(handler_dict) - def register_message_reaction_count_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): + def register_message_reaction_count_handler(self, callback: Callable[[Any], Awaitable], func: Callable=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message reaction count handler. From bc8120ed4b6910b6b1f019ef62b94206b69812f4 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 15:14:25 +0500 Subject: [PATCH 1411/1808] Fix issues related with reactions handlers --- telebot/apihelper.py | 2 +- telebot/asyncio_helper.py | 2 +- telebot/types.py | 22 +++++++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index a50b61830..3558219f0 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -335,7 +335,7 @@ def set_message_reaction(token, chat_id, message_id, reaction=None, is_big=None) method_url = r'setMessageReaction' payload = {'chat_id': chat_id, 'message_id': message_id} if reaction: - payload['reaction'] = reaction + payload['reaction'] = json.dumps([r.to_dict() for r in reaction]) if is_big is not None: payload['is_big'] = is_big return _make_request(token, method_url, params=payload) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 5da6859cf..872fb591d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -320,7 +320,7 @@ async def set_message_reaction(token, chat_id, message_id, reaction=None, is_big method_url = r'setMessageReaction' payload = {'chat_id': chat_id, 'message_id': message_id} if reaction: - payload['reaction'] = reaction + payload['reaction'] = json.dumps([r.to_dict() for r in reaction]) if is_big is not None: payload['is_big'] = is_big return await _process_request(token, method_url, params=payload) diff --git a/telebot/types.py b/telebot/types.py index f47670917..8a9930412 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7986,7 +7986,13 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - return cls(**obj) + # remove type + if obj['type'] == 'emoji': + del obj['type'] + return ReactionTypeEmoji(**obj) + elif obj['type'] == 'custom_emoji': + del obj['type'] + return ReactionTypeCustomEmoji(**obj) def __init__(self, type: str) -> None: self.type: str = type @@ -8028,6 +8034,8 @@ def to_dict(self) -> dict: return json_dict + + class ReactionTypeCustomEmoji(ReactionType): """ @@ -8055,6 +8063,8 @@ def to_dict(self) -> dict: return json_dict + + class MessageReactionUpdated(JsonDeserializable): """ @@ -8092,6 +8102,16 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + + if 'user' in obj: + obj['user'] = User.de_json(obj['user']) + if 'actor_chat' in obj: + obj['actor_chat'] = Chat.de_json(obj['actor_chat']) + obj['old_reaction'] = [ReactionType.de_json(reaction) for reaction in obj['old_reaction']] + obj['new_reaction'] = [ReactionType.de_json(reaction) for reaction in obj['new_reaction']] + if 'chat' in obj: + obj['chat'] = Chat.de_json(obj['chat']) + return cls(**obj) def __init__(self, chat: Chat, message_id: int, date: int, old_reaction: List[ReactionType], new_reaction: List[ReactionType], From f081ec8bc084c0b8be3c87984aa2d2e02922c409 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 30 Dec 2023 15:23:48 +0500 Subject: [PATCH 1412/1808] Fix de_json for some classes --- telebot/types.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 8a9930412..feb16094a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8153,6 +8153,10 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + + obj['reactions'] = [ReactionCount.de_json(reaction) for reaction in obj['reactions']] + obj['chat'] = Chat.de_json(obj['chat']) + return cls(**obj) def __init__(self, chat: Chat, message_id: int, date: int, reactions: List[ReactionCount]) -> None: @@ -8183,6 +8187,9 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + + obj['type'] = ReactionType.de_json(obj['type']) + return cls(**obj) def __init__(self, type: ReactionType, total_count: int) -> None: From 05a939bd8dbd449b55def3b3732efef2dbd11d54 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 31 Dec 2023 21:22:49 +0500 Subject: [PATCH 1413/1808] Fix reply_markup issue with reply_parameters conflict --- telebot/apihelper.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3558219f0..5f8702533 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -261,7 +261,7 @@ def send_message( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, method='post') @@ -427,7 +427,7 @@ 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: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if timeout: payload['timeout'] = timeout if protect_content is not None: @@ -449,7 +449,7 @@ def send_dice( payload['disable_notification'] = disable_notification if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: @@ -496,7 +496,7 @@ def send_photo( payload['has_spoiler'] = has_spoiler if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') @@ -511,7 +511,7 @@ def send_media_group( payload['disable_notification'] = disable_notification if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if timeout: payload['timeout'] = timeout if protect_content is not None: @@ -553,7 +553,7 @@ def send_location( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -650,7 +650,7 @@ def send_contact( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -712,7 +712,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['has_spoiler'] = has_spoiler if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') @@ -752,7 +752,7 @@ def send_animation( payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if width: @@ -796,7 +796,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup= payload['message_thread_id'] = message_thread_id if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') @@ -832,7 +832,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark payload['thumbnail'] = thumbnail if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -882,7 +882,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['message_thread_id'] = message_thread_id if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') @@ -923,7 +923,7 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: @@ -1392,7 +1392,7 @@ def send_game( payload['timeout'] = timeout if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: @@ -1544,7 +1544,7 @@ def send_invoice( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: # to json - payload['reply_markup'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) From 52a0314f1d790c2e3b02f89cacb08b1df90577bf Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 2 Jan 2024 22:23:38 +0300 Subject: [PATCH 1414/1808] Fix naming in Update class Fix "new_" usage. No valuable changes. --- telebot/__init__.py | 82 ++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 39332c166..6c872cda4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -675,7 +675,7 @@ def process_new_updates(self, updates: List[types.Update]): new_channel_posts = None new_edited_channel_posts = None new_message_reactions = None - message_reaction_counts = None + new_message_reaction_counts = None new_inline_queries = None new_chosen_inline_results = None new_callback_queries = None @@ -686,8 +686,8 @@ def process_new_updates(self, updates: List[types.Update]): new_my_chat_members = None new_chat_members = None new_chat_join_request = None - removed_chat_boosts = None - chat_boosts = None + new_chat_boosts = None + new_removed_chat_boosts = None for update in updates: if apihelper.ENABLE_MIDDLEWARE and not self.use_class_middlewares: @@ -749,14 +749,14 @@ def process_new_updates(self, updates: List[types.Update]): if new_message_reactions is None: new_message_reactions = [] new_message_reactions.append(update.message_reaction) if update.message_reaction_count: - if message_reaction_counts is None: message_reaction_counts = [] - message_reaction_counts.append(update.message_reaction_count) + if new_message_reaction_counts is None: new_message_reaction_counts = [] + new_message_reaction_counts.append(update.message_reaction_count) if update.chat_boost: - if chat_boosts is None: chat_boosts = [] - chat_boosts.append(update.chat_boost) + if new_chat_boosts is None: new_chat_boosts = [] + new_chat_boosts.append(update.chat_boost) if update.removed_chat_boost: - if removed_chat_boosts is None: removed_chat_boosts = [] - removed_chat_boosts.append(update.removed_chat_boost) + if new_removed_chat_boosts is None: new_removed_chat_boosts = [] + new_removed_chat_boosts.append(update.removed_chat_boost) if new_messages: self.process_new_messages(new_messages) @@ -788,12 +788,12 @@ def process_new_updates(self, updates: List[types.Update]): self.process_new_chat_join_request(new_chat_join_request) if new_message_reactions: self.process_new_message_reaction(new_message_reactions) - if message_reaction_counts: - self.process_new_message_reaction_count(message_reaction_counts) - if chat_boosts: - self.process_new_chat_boost(chat_boosts) - if removed_chat_boosts: - self.process_new_removed_chat_boost(removed_chat_boosts) + if new_message_reaction_counts: + self.process_new_message_reaction_count(new_message_reaction_counts) + if new_chat_boosts: + self.process_new_chat_boost(new_chat_boosts) + if new_removed_chat_boosts: + self.process_new_removed_chat_boost(new_removed_chat_boosts) def process_new_messages(self, new_messages): """ @@ -804,35 +804,35 @@ def process_new_messages(self, new_messages): self.__notify_update(new_messages) self._notify_command_handlers(self.message_handlers, new_messages, 'message') - def process_new_edited_messages(self, edited_message): + def process_new_edited_messages(self, new_edited_message): """ :meta private: """ - self._notify_command_handlers(self.edited_message_handlers, edited_message, 'edited_message') + self._notify_command_handlers(self.edited_message_handlers, new_edited_message, 'edited_message') - def process_new_channel_posts(self, channel_post): + def process_new_channel_posts(self, new_channel_post): """ :meta private: """ - self._notify_command_handlers(self.channel_post_handlers, channel_post, 'channel_post') + self._notify_command_handlers(self.channel_post_handlers, new_channel_post, 'channel_post') - def process_new_edited_channel_posts(self, edited_channel_post): + def process_new_edited_channel_posts(self, new_edited_channel_post): """ :meta private: """ - self._notify_command_handlers(self.edited_channel_post_handlers, edited_channel_post, 'edited_channel_post') + self._notify_command_handlers(self.edited_channel_post_handlers, new_edited_channel_post, 'edited_channel_post') - def process_new_message_reaction(self, message_reactions): + def process_new_message_reaction(self, new_message_reactions): """ :meta private: """ - self._notify_command_handlers(self.message_reaction_handlers, message_reactions, 'message_reaction') + self._notify_command_handlers(self.message_reaction_handlers, new_message_reactions, 'message_reaction') - def process_new_message_reaction_count(self, message_reaction_counts): + def process_new_message_reaction_count(self, new_message_reaction_counts): """ :meta private: """ - self._notify_command_handlers(self.message_reaction_count_handlers, message_reaction_counts, 'message_reaction_count') + self._notify_command_handlers(self.message_reaction_count_handlers, new_message_reaction_counts, 'message_reaction_count') def process_new_inline_query(self, new_inline_queries): """ @@ -858,53 +858,53 @@ def process_new_shipping_query(self, new_shipping_queries): """ self._notify_command_handlers(self.shipping_query_handlers, new_shipping_queries, 'shipping_query') - def process_new_pre_checkout_query(self, pre_checkout_queries): + def process_new_pre_checkout_query(self, new_pre_checkout_queries): """ :meta private: """ - self._notify_command_handlers(self.pre_checkout_query_handlers, pre_checkout_queries, 'pre_checkout_query') + self._notify_command_handlers(self.pre_checkout_query_handlers, new_pre_checkout_queries, 'pre_checkout_query') - def process_new_poll(self, polls): + def process_new_poll(self, new_polls): """ :meta private: """ - self._notify_command_handlers(self.poll_handlers, polls, 'poll') + self._notify_command_handlers(self.poll_handlers, new_polls, 'poll') - def process_new_poll_answer(self, poll_answers): + def process_new_poll_answer(self, new_poll_answers): """ :meta private: """ - self._notify_command_handlers(self.poll_answer_handlers, poll_answers, 'poll_answer') + self._notify_command_handlers(self.poll_answer_handlers, new_poll_answers, 'poll_answer') - def process_new_my_chat_member(self, my_chat_members): + def process_new_my_chat_member(self, new_my_chat_members): """ :meta private: """ - self._notify_command_handlers(self.my_chat_member_handlers, my_chat_members, 'my_chat_member') + self._notify_command_handlers(self.my_chat_member_handlers, new_my_chat_members, 'my_chat_member') - def process_new_chat_member(self, chat_members): + def process_new_chat_member(self, new_chat_members): """ :meta private: """ - self._notify_command_handlers(self.chat_member_handlers, chat_members, 'chat_member') + self._notify_command_handlers(self.chat_member_handlers, new_chat_members, 'chat_member') - def process_new_chat_join_request(self, chat_join_request): + def process_new_chat_join_request(self, new_chat_join_request): """ :meta private: """ - self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request, 'chat_join_request') + self._notify_command_handlers(self.chat_join_request_handlers, new_chat_join_request, 'chat_join_request') - def process_new_chat_boost(self, chat_boosts): + def process_new_chat_boost(self, new_chat_boosts): """ :meta private: """ - self._notify_command_handlers(self.chat_boost_handlers, chat_boosts, 'chat_boost') + self._notify_command_handlers(self.chat_boost_handlers, new_chat_boosts, 'chat_boost') - def process_new_removed_chat_boost(self, removed_chat_boosts): + def process_new_removed_chat_boost(self, new_removed_chat_boosts): """ :meta private: """ - self._notify_command_handlers(self.removed_chat_boost_handlers, removed_chat_boosts, 'removed_chat_boost') + self._notify_command_handlers(self.removed_chat_boost_handlers, new_removed_chat_boosts, 'removed_chat_boost') def process_middlewares(self, update): From 79cc12772fe8f9d6a2e3740e8ab31ad9462afefb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 00:25:39 +0300 Subject: [PATCH 1415/1808] reply_to_message_id and allow_sending_without_reply deprecation reply_to_message_id and allow_sending_without_reply deprecation extended with more precise values processing --- telebot/__init__.py | 723 +++++++++++++++++++++++++++----------------- 1 file changed, 450 insertions(+), 273 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c872cda4..1cfc5a558 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1553,13 +1553,13 @@ def send_message( disable_web_page_preview: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - link_preview_options : Optional[types.ReplyParameters]=None) -> types.Message: + link_preview_options : Optional[types.LinkPreviewOptions]=None) -> types.Message: """ Use this method to send text messages. @@ -1590,10 +1590,10 @@ def send_message( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :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. @@ -1619,17 +1619,27 @@ def send_message( disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if disable_web_page_preview: # show a deprecation warning @@ -1698,9 +1708,8 @@ def copy_message( caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.MessageID: @@ -1732,10 +1741,10 @@ def copy_message( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :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 @@ -1758,17 +1767,27 @@ def copy_message( disable_notification = self.disable_notification if (disable_notification is None) else disable_notification parse_mode = self.parse_mode if (parse_mode is None) else parse_mode protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.MessageID.de_json( apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, @@ -1898,10 +1917,10 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], def send_dice( self, chat_id: Union[int, str], emoji: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -1920,20 +1939,20 @@ def send_dice( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`int` - :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` or :class:`telebot.types.ForceReply` - :param timeout: Timeout in seconds for the request. - :type timeout: :obj:`int` + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :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 timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -1948,17 +1967,27 @@ def send_dice( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( apihelper.send_dice( @@ -1973,8 +2002,8 @@ def send_photo( caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, @@ -2008,10 +2037,10 @@ def send_photo( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :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 @@ -2037,17 +2066,27 @@ def send_photo( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( apihelper.send_photo( @@ -2060,14 +2099,14 @@ def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, performer: Optional[str]=None, title: Optional[str]=None, - reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, @@ -2101,9 +2140,6 @@ def send_audio( :param title: Track name :type title: :obj:`str` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`int` - :param reply_markup: :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` or :class:`telebot.types.ForceReply` @@ -2114,6 +2150,12 @@ def send_audio( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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 timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -2126,9 +2168,6 @@ def send_audio( :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -2147,17 +2186,27 @@ def send_audio( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if thumb is not None and thumbnail is None: thumbnail = thumb @@ -2173,13 +2222,13 @@ def send_audio( def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, - reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -2203,9 +2252,6 @@ def send_voice( :param duration: Duration of the voice message in seconds :type duration: :obj:`int` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`int` - :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` @@ -2217,15 +2263,18 @@ def send_voice( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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 timeout: Timeout in seconds for the request. :type timeout: :obj:`int` :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -2240,17 +2289,27 @@ def send_voice( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( apihelper.send_voice( @@ -2261,15 +2320,15 @@ def send_voice( # TODO: Rewrite this method like in API. def send_document( self, chat_id: Union[int, str], document: Union[Any, str], - reply_to_message_id: Optional[int]=None, caption: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, thumbnail: 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, disable_content_type_detection: Optional[bool]=None, data: Optional[Union[Any, str]]=None, @@ -2288,9 +2347,6 @@ def send_document( String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data :type document: :obj:`str` or :class:`telebot.types.InputFile` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`int` - :param caption: Document caption (may also be used when resending documents by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -2305,6 +2361,12 @@ def send_document( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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 timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -2314,9 +2376,6 @@ def send_document( :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: 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 visible_file_name: allows to define file name that will be visible in the Telegram instead of original file name :type visible_file_name: :obj:`str` @@ -2344,17 +2403,27 @@ def send_document( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if data and (not document): # function typo miss compatibility @@ -2378,11 +2447,11 @@ def send_document( def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], - reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content:Optional[bool]=None, data: Union[Any, str]=None, message_thread_id: Optional[int]=None, @@ -2401,9 +2470,6 @@ def send_sticker( as a String for Telegram to get a .webp file from the Internet, or upload a new one using multipart/form-data. :type sticker: :obj:`str` or :class:`telebot.types.InputFile` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`int` - :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` @@ -2412,12 +2478,15 @@ def send_sticker( :param disable_notification: to disable the notification :type disable_notification: :obj:`bool` - :param timeout: Timeout in seconds for the request. - :type timeout: :obj:`int` + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :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 timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -2438,23 +2507,33 @@ def send_sticker( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if data and (not sticker): - # function typo miss compatibility - logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') - sticker = data + if allow_sending_without_reply is not None: + # show a deprecation warning + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - if allow_sending_without_reply or reply_to_message_id: + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply + if data and (not sticker): + # function typo miss compatibility + logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') + sticker = data + return types.Message.de_json( apihelper.send_data( self.token, chat_id, sticker, 'sticker', reply_markup=reply_markup, @@ -2474,8 +2553,8 @@ def send_video( supports_streaming: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, data: Optional[Union[Any, str]]=None, @@ -2524,10 +2603,10 @@ def send_video( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :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 @@ -2559,7 +2638,27 @@ def send_video( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + + if allow_sending_without_reply is not None: + # show a deprecation warning + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if data and (not video): # function typo miss compatibility @@ -2570,16 +2669,6 @@ def send_video( thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') - if allow_sending_without_reply or reply_to_message_id: - # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") - - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) - return types.Message.de_json( apihelper.send_video( self.token, chat_id, video, duration, caption, reply_markup, @@ -2597,8 +2686,8 @@ def send_animation( caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, @@ -2642,9 +2731,12 @@ def send_animation( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` + :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. :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` @@ -2659,9 +2751,6 @@ def send_animation( :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: 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 message_thread_id: Identifier of a message thread, in which the video will be sent :type message_thread_id: :obj:`int` @@ -2680,17 +2769,27 @@ def send_animation( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if thumbnail is None and thumb is not None: thumbnail = thumb @@ -2706,12 +2805,12 @@ def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], duration: Optional[int]=None, length: Optional[int]=None, - reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, @@ -2735,9 +2834,6 @@ def send_video_note( :param length: Video width and height, i.e. diameter of the video message :type length: :obj:`int` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`int` - :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` @@ -2746,6 +2842,12 @@ def send_video_note( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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 timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -2755,9 +2857,6 @@ def send_video_note( so you can pass “attach://” if the thumbnail was uploaded using multipart/form-data under . :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` - :param allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -2775,21 +2874,31 @@ def send_video_note( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if thumbnail is None and thumb is not None: - thumbnail = thumb - logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + if allow_sending_without_reply is not None: + # show a deprecation warning + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - if allow_sending_without_reply or reply_to_message_id: + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply + + if thumbnail is None and thumb is not None: + thumbnail = thumb + logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') return types.Message.de_json( apihelper.send_video_note( @@ -2804,9 +2913,9 @@ def send_media_group( types.InputMediaPhoto, types.InputMediaVideo]], disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> List[types.Message]: """ @@ -2827,15 +2936,15 @@ def send_media_group( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` + :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 timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: 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 message_thread_id: Identifier of a message thread, in which the media group will be sent :type message_thread_id: :obj:`int` @@ -2847,17 +2956,27 @@ def send_media_group( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = apihelper.send_media_group( self.token, chat_id, media, disable_notification, timeout, @@ -2869,14 +2988,14 @@ def send_location( self, chat_id: Union[int, str], latitude: float, longitude: float, live_period: Optional[int]=None, - reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -2897,9 +3016,6 @@ def send_location( :param live_period: Period in seconds for which the location will be updated (see Live Locations, should be between 60 and 86400. :type live_period: :obj:`int` - :param reply_to_message_id: If the message is a reply, ID of the original message - :type reply_to_message_id: :obj:`int` - :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: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` @@ -2908,6 +3024,12 @@ def send_location( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :type reply_to_message_id: :obj:`int` + + :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 timeout: Timeout in seconds for the request. :type timeout: :obj:`int` @@ -2920,9 +3042,6 @@ def send_location( :param proximity_alert_radius: 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 allow_sending_without_reply: 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 protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -2937,17 +3056,27 @@ def send_location( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( apihelper.send_location( @@ -3056,10 +3185,10 @@ def send_venue( foursquare_id: Optional[str]=None, foursquare_type: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, google_place_id: Optional[str]=None, google_place_type: Optional[str]=None, protect_content: Optional[bool]=None, @@ -3095,9 +3224,12 @@ def send_venue( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` + :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. :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` @@ -3106,10 +3238,6 @@ def send_venue( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified - replied-to messages is not found. - :type allow_sending_without_reply: :obj:`bool` - :param google_place_id: Google Places identifier of the venue :type google_place_id: :obj:`str` @@ -3130,17 +3258,27 @@ def send_venue( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( apihelper.send_venue( @@ -3155,10 +3293,10 @@ def send_contact( first_name: str, last_name: Optional[str]=None, vcard: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ @@ -3184,9 +3322,12 @@ def send_contact( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` + :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. :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` @@ -3195,10 +3336,6 @@ def send_contact( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified - replied-to messages is not found. - :type allow_sending_without_reply: :obj:`bool` - :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -3213,17 +3350,27 @@ def send_contact( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( apihelper.send_contact( @@ -4305,10 +4452,10 @@ def edit_message_reply_markup( def send_game( self, chat_id: Union[int, str], game_short_name: str, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -4326,18 +4473,18 @@ def send_game( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` + :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. :type reply_markup: :obj:`InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` :param timeout: Timeout in seconds for waiting for a response from the bot. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified replied-to messages is not found. - :type allow_sending_without_reply: :obj:`bool` - :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` @@ -4352,17 +4499,27 @@ def send_game( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = apihelper.send_game( self.token, chat_id, game_short_name, disable_notification, @@ -4457,11 +4614,11 @@ def send_invoice( send_email_to_provider: Optional[bool]=None, is_flexible: Optional[bool]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, provider_data: Optional[str]=None, timeout: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None, @@ -4537,9 +4694,12 @@ def send_invoice( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` + :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: 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 :type reply_markup: :obj:`str` @@ -4551,9 +4711,6 @@ def send_invoice( :param timeout: Timeout of a request, defaults to None :type timeout: :obj:`int` - :param allow_sending_without_reply: 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 max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency :type max_tip_amount: :obj:`int` @@ -4576,17 +4733,27 @@ def send_invoice( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = apihelper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, @@ -4713,9 +4880,9 @@ def send_poll( close_date: Optional[Union[int, datetime]]=None, is_closed: Optional[bool]=None, disable_notification: Optional[bool]=False, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - allow_sending_without_reply: Optional[bool]=None, timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None, @@ -4768,10 +4935,10 @@ def send_poll( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the poll allows multiple options to be voted simultaneously. + :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, @@ -4799,20 +4966,30 @@ def send_poll( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if isinstance(question, types.Poll): - raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") - - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_to_message_id: + # show a deprecation warning + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply + + if isinstance(question, types.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 From 56fbcafa47beca89b78694ce83f2b959351bc072 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 00:39:06 +0300 Subject: [PATCH 1416/1808] disable_web_page_preview deprecation disable_web_page_preview deprecation extended with more precise values processing --- telebot/__init__.py | 47 ++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 1cfc5a558..80adb2154 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1550,7 +1550,7 @@ def send_message( self, chat_id: Union[int, str], text: str, parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, - disable_web_page_preview: Optional[bool]=None, + disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility @@ -1581,7 +1581,7 @@ def send_message( :param entities: List of special entities that appear in message text, which can be specified instead of parse_mode :type entities: Array of :class:`telebot.types.MessageEntity` - :param disable_web_page_preview: Disables link previews for links in this message + :param disable_web_page_preview: deprecated. Disables link previews for links in this message :type disable_web_page_preview: :obj:`bool` :param disable_notification: Sends the message silently. Users will receive a notification with no sound. @@ -1616,7 +1616,6 @@ def send_message( :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content @@ -1641,14 +1640,21 @@ def send_message( if reply_parameters and (reply_parameters.allow_sending_without_reply is None): reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply - if disable_web_page_preview: + if disable_web_page_preview is not None: # show a deprecation warning logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") - # create a LinkPreviewOptions object - link_preview_options = types.LinkPreviewOptions( - disable_web_page_preview=disable_web_page_preview - ) + if link_preview_options: + # show a conflict warning + logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") + else: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + disable_web_page_preview=disable_web_page_preview + ) + + if link_preview_options and (link_preview_options.disable_web_page_preview is None): + link_preview_options.disable_web_page_preview = self.disable_web_page_preview return types.Message.de_json( apihelper.send_message( @@ -4327,7 +4333,7 @@ def edit_message_text( inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, - disable_web_page_preview: Optional[bool]=None, + disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[types.InlineKeyboardMarkup]=None, link_preview_options : Optional[types.LinkPreviewOptions]=None) -> Union[types.Message, bool]: """ @@ -4353,7 +4359,7 @@ def edit_message_text( :param entities: List of special entities that appear in the message text, which can be specified instead of parse_mode :type entities: List of :obj:`telebot.types.MessageEntity` - :param disable_web_page_preview: Disables link previews for links in this message + :param disable_web_page_preview: deprecated. Disables link previews for links in this message :type disable_web_page_preview: :obj:`bool` :param reply_markup: A JSON-serialized object for an inline keyboard. @@ -4366,17 +4372,22 @@ def edit_message_text( :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview - - if disable_web_page_preview: + + if disable_web_page_preview is not None: # show a deprecation warning logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") - # create a LinkPreviewOptions object - link_preview_options = types.LinkPreviewOptions( - disable_web_page_preview=disable_web_page_preview - ) - + if link_preview_options: + # show a conflict warning + logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") + else: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + disable_web_page_preview=disable_web_page_preview + ) + + if link_preview_options and (link_preview_options.disable_web_page_preview is None): + link_preview_options.disable_web_page_preview = self.disable_web_page_preview result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, entities, reply_markup, link_preview_options) From 778a02c45b3291dca447d1cecc395d0c9de45b69 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 00:45:46 +0300 Subject: [PATCH 1417/1808] Fix forward_messages return value --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 80adb2154..8b0a5d1fc 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1879,8 +1879,8 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - return types.MessageID.de_json( - apihelper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id)) + return [types.MessageID.de_json(message_id) for message_id in + apihelper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id))] def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, From 0af7f6f56c19271f35b89e3f9de9be35daebd40c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 00:47:45 +0300 Subject: [PATCH 1418/1808] Fix forward_messages return value in Async --- telebot/async_telebot.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 658443124..685f725c7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2932,8 +2932,7 @@ async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[s disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content result = await asyncio_helper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id) - return types.MessageID.de_json( - result) + return [types.MessageID.de_json(message_id) for message_id in result] async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, @@ -2969,8 +2968,7 @@ async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, protect_content = self.protect_content if protect_content is None else protect_content result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id, remove_caption) - return [types.MessageID.de_json(message_id) for message_id in - result] + return [types.MessageID.de_json(message_id) for message_id in result] async def send_dice( self, chat_id: Union[int, str], From 0ace17bdb5928327d493cf0045165b390b1ce6f5 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 00:48:34 +0300 Subject: [PATCH 1419/1808] Fix the fix forward_messages return value --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8b0a5d1fc..5b2598d18 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1880,7 +1880,7 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in protect_content = self.protect_content if (protect_content is None) else protect_content return [types.MessageID.de_json(message_id) for message_id in - apihelper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id))] + apihelper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id)] def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, From 578d6a37d23a4b8d8454416b7a022f98465245bf Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 01:23:41 +0300 Subject: [PATCH 1420/1808] Fix message_reaction_x_handler description --- telebot/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5b2598d18..3cbc6d7b1 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6830,7 +6830,7 @@ def register_edited_channel_post_handler(self, callback: Callable, content_types def message_reaction_handler(self, func=None, **kwargs): """ Handles new incoming message reaction. - As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + As a parameter to the decorator function, it passes :class:`telebot.types.MessageReactionUpdated` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -6881,7 +6881,7 @@ def register_message_reaction_handler(self, callback: Callable, func: Callable=N def message_reaction_count_handler(self, func=None, **kwargs): """ Handles new incoming message reaction count. - As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + As a parameter to the decorator function, it passes :class:`telebot.types.MessageReactionCountUpdated` object. :param func: Function executed as a filter :type func: :obj:`function` From 4359f4f14da43aabe8c54bc6d5fc3739aa9710ca Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 01:23:51 +0300 Subject: [PATCH 1421/1808] Fix message_reaction_x_handler description for Async --- telebot/async_telebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 685f725c7..3e8b305a3 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1400,7 +1400,7 @@ def register_edited_channel_post_handler(self, callback: Callable[[Any], Awaitab def message_reaction_handler(self, func=None, **kwargs): """ Handles new incoming message reaction. - As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + As a parameter to the decorator function, it passes :class:`telebot.types.MessageReactionUpdated` object. :param func: Function executed as a filter :type func: :obj:`function` @@ -1452,7 +1452,7 @@ def register_message_reaction_handler(self, callback: Callable[[Any], Awaitable] def message_reaction_count_handler(self, func=None, **kwargs): """ Handles new incoming message reaction count. - As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + As a parameter to the decorator function, it passes :class:`telebot.types.MessageReactionCountUpdated` object. :param func: Function executed as a filter :type func: :obj:`function` From 9831ae2cd414fc3ff9bf8eeeef1c0bcea0af4b47 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 13:33:37 +0300 Subject: [PATCH 1422/1808] Fix and align reply_parameters --- telebot/apihelper.py | 59 +++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5f8702533..c69ce3c56 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -447,9 +447,6 @@ def send_dice( payload['emoji'] = emoji if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: @@ -458,6 +455,9 @@ def send_dice( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -509,15 +509,15 @@ def send_media_group( payload = {'chat_id': chat_id, 'media': media_json} if disable_notification is not None: payload['disable_notification'] = disable_notification - if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if timeout: payload['timeout'] = timeout if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id is not None: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -625,6 +625,9 @@ def send_venue( payload['protect_content'] = protect_content if message_thread_id is not None: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -750,9 +753,6 @@ def send_animation( payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if width: @@ -763,6 +763,9 @@ def send_animation( payload['message_thread_id'] = message_thread_id if has_spoiler is not None: payload['has_spoiler'] = has_spoiler + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') @@ -830,13 +833,13 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark files = {'thumbnail': thumbnail} else: payload['thumbnail'] = thumbnail - if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') @@ -883,7 +886,6 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non if reply_parameters is not None: # to json payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) - return _make_request(token, method_url, params=payload, files=files, method='post') @@ -921,9 +923,6 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['thumbnail'] = thumbnail if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) - if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if method_url == 'sendDocument' and disable_content_type_detection is not None: @@ -932,6 +931,9 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['message_thread_id'] = message_thread_id if emoji: payload['emoji'] = emoji + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1390,13 +1392,13 @@ def send_game( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout - if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if protect_content is not None: payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) @@ -1786,9 +1788,8 @@ def create_invoice_link(token, title, description, payload, provider_token, def send_poll( 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, + 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): method_url = r'sendPoll' payload = { @@ -1817,11 +1818,8 @@ 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: - payload['reply_parameters'] = reply_parameters.to_json() if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) if timeout: @@ -1833,6 +1831,9 @@ def send_poll( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if reply_parameters is not None: + # to json + payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) return _make_request(token, method_url, params=payload) def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_emoji_id=None): @@ -1931,9 +1932,7 @@ def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notifica payload['message_thread_id'] = message_thread_id if protect_content is not None: payload['protect_content'] = protect_content - - result = _make_request(token, method_url, params=payload) - return result + return _make_request(token, method_url, params=payload) def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, message_thread_id=None, protect_content=None, remove_caption=None): @@ -1951,9 +1950,7 @@ def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notificatio payload['protect_content'] = protect_content if remove_caption is not None: payload['remove_caption'] = remove_caption - - result = _make_request(token, method_url, params=payload) - return result + return _make_request(token, method_url, params=payload) def _convert_list_json_serializable(results): From 71f53d3b0e5bca8beaa088bc26beda7193c62e7d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 15:24:20 +0300 Subject: [PATCH 1423/1808] Fix minor bugs in types.py --- telebot/types.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index feb16094a..ae3b4162b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1272,14 +1272,10 @@ def de_json(cls, json_string): content_type = 'story' if 'external_reply' in obj: opts['external_reply'] = ExternalReplyInfo.de_json(obj['external_reply']) - content_type = 'text' # @Badiboy not sure about content_types in here, please check if 'quote' in obj: opts['quote'] = TextQuote.de_json(obj['quote']) - content_type = 'text' # Here too, check the content types - if 'link_preview_options' in obj: opts['link_preview_options'] = LinkPreviewOptions.de_json(obj['link_preview_options']) - if 'giveaway_created' in obj: opts['giveaway_created'] = GiveawayCreated.de_json(obj['giveaway_created']) content_type = 'giveaway_created' @@ -1396,7 +1392,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.story: Optional[Story] = None self.external_reply: Optional[ExternalReplyInfo] = None self.quote: Optional[TextQuote] = None - self.LinkPreviewOptions: Optional[LinkPreviewOptions] = None + self.link_preview_options: Optional[LinkPreviewOptions] = None self.giveaway_created: Optional[GiveawayCreated] = None self.giveaway: Optional[Giveaway] = None self.giveaway_winners: Optional[GiveawayWinners] = None @@ -3713,7 +3709,7 @@ class InputTextMessageContent(Dictionaryable): parse_mode :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param disable_web_page_preview: Optional. Disables link previews for links in the sent message + :param disable_web_page_preview: Optional, deprecated. Disables link previews for links in the sent message :type disable_web_page_preview: :obj:`bool` :return: Instance of the class @@ -3724,11 +3720,13 @@ def __init__(self, message_text, parse_mode=None, entities=None, disable_web_pag self.parse_mode: str = parse_mode self.entities: List[MessageEntity] = entities link_preview_options: LinkPreviewOptions = link_preview_options - if disable_web_page_preview is not None and link_preview_options is None: - # deprecated - self.link_preview_options: LinkPreviewOptions = LinkPreviewOptions(disable_web_page_preview) + if disable_web_page_preview is not None: + logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") - + if link_preview_options: + logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") + else: + self.link_preview_options: LinkPreviewOptions = LinkPreviewOptions(disable_web_page_preview) def to_dict(self): json_dict = {'message_text': self.message_text} @@ -9215,4 +9213,4 @@ def de_json(cls, json_string): obj['chat'] = Chat.de_json(obj.get('chat')) return cls(**obj) - \ No newline at end of file + From b96084f1f72346423aae3f3ab2671a05c4454c10 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 15:53:17 +0300 Subject: [PATCH 1424/1808] reply_parameters => to_json --- telebot/apihelper.py | 51 +++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c69ce3c56..d307e9bae 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -260,8 +260,7 @@ def send_message( if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, method='post') @@ -426,8 +425,7 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() if timeout: payload['timeout'] = timeout if protect_content is not None: @@ -456,8 +454,7 @@ def send_dice( if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -495,8 +492,7 @@ def send_photo( if has_spoiler is not None: payload['has_spoiler'] = has_spoiler if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -516,8 +512,7 @@ def send_media_group( if message_thread_id is not None: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -552,8 +547,7 @@ def send_location( if message_thread_id is not None: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -626,8 +620,7 @@ def send_venue( if message_thread_id is not None: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -652,8 +645,7 @@ def send_contact( if message_thread_id is not None: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -714,8 +706,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N if has_spoiler is not None: payload['has_spoiler'] = has_spoiler if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -764,8 +755,7 @@ def send_animation( if has_spoiler is not None: payload['has_spoiler'] = has_spoiler if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -798,8 +788,7 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup= if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -838,8 +827,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -884,8 +872,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -932,8 +919,7 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non if emoji: payload['emoji'] = emoji if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1397,8 +1383,7 @@ def send_game( if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -1545,8 +1530,7 @@ def send_invoice( if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -1832,8 +1816,7 @@ def send_poll( if message_thread_id: payload['message_thread_id'] = message_thread_id if reply_parameters is not None: - # to json - payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + payload['reply_parameters'] = reply_parameters.to_json() return _make_request(token, method_url, params=payload) def create_forum_topic(token, chat_id, name, icon_color=None, icon_custom_emoji_id=None): From 0c9bdfb981177859e99c98fa96dfbf96cf319b05 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 16:18:56 +0300 Subject: [PATCH 1425/1808] Fix classes loading in types.py --- telebot/types.py | 260 +++++++++++++++++++---------------------------- 1 file changed, 104 insertions(+), 156 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ae3b4162b..5b8c42536 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1148,7 +1148,7 @@ def de_json(cls, json_string): if 'caption' in obj: opts['caption'] = obj['caption'] if 'contact' in obj: - opts['contact'] = Contact.de_json(json.dumps(obj['contact'])) + opts['contact'] = Contact.de_json(obj['contact']) content_type = 'contact' if 'location' in obj: opts['location'] = Location.de_json(obj['location']) @@ -7932,7 +7932,6 @@ def __init__(self, text: str, web_app: Optional[WebAppInfo]=None, start_paramete self.web_app: Optional[WebAppInfo] = web_app self.start_parameter: Optional[str] = start_parameter - def to_dict(self) -> dict: json_dict = { 'text': self.text @@ -7965,6 +7964,7 @@ def de_json(cls, json_string): def __init__(self) -> None: pass + # base class class ReactionType(JsonDeserializable, Dictionaryable, JsonSerializable): """ @@ -7999,7 +7999,6 @@ def to_dict(self) -> dict: json_dict = { 'type': self.type } - return json_dict def to_json(self) -> str: @@ -8029,11 +8028,8 @@ def __init__(self, emoji: str) -> None: def to_dict(self) -> dict: json_dict = super().to_dict() json_dict['emoji'] = self.emoji + return json_dict - return json_dict - - - class ReactionTypeCustomEmoji(ReactionType): """ @@ -8058,11 +8054,8 @@ def __init__(self, custom_emoji: str) -> None: def to_dict(self) -> dict: json_dict = super().to_dict() json_dict['custom_emoji'] = self.custom_emoji - return json_dict - - - + class MessageReactionUpdated(JsonDeserializable): """ @@ -8101,15 +8094,13 @@ def de_json(cls, json_string): return None obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) if 'user' in obj: obj['user'] = User.de_json(obj['user']) if 'actor_chat' in obj: obj['actor_chat'] = Chat.de_json(obj['actor_chat']) obj['old_reaction'] = [ReactionType.de_json(reaction) for reaction in obj['old_reaction']] obj['new_reaction'] = [ReactionType.de_json(reaction) for reaction in obj['new_reaction']] - if 'chat' in obj: - obj['chat'] = Chat.de_json(obj['chat']) - return cls(**obj) def __init__(self, chat: Chat, message_id: int, date: int, old_reaction: List[ReactionType], new_reaction: List[ReactionType], @@ -8151,10 +8142,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - obj['reactions'] = [ReactionCount.de_json(reaction) for reaction in obj['reactions']] obj['chat'] = Chat.de_json(obj['chat']) - + obj['reactions'] = [ReactionCount.de_json(reaction) for reaction in obj['reactions']] return cls(**obj) def __init__(self, chat: Chat, message_id: int, date: int, reactions: List[ReactionCount]) -> None: @@ -8185,9 +8174,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['type'] = ReactionType.de_json(obj['type']) - return cls(**obj) def __init__(self, type: ReactionType, total_count: int) -> None: @@ -8195,7 +8182,6 @@ def __init__(self, type: ReactionType, total_count: int) -> None: self.total_count: int = total_count - class ExternalReplyInfo(JsonDeserializable): """ This object contains information about a message that is being replied to, @@ -8280,23 +8266,14 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + origin = MessageOrigin.de_json(obj['origin']) + if 'chat' in obj: + chat = Chat.de_json(obj['chat']) + if 'link_preview_options' in obj: + link_preview_options = LinkPreviewOptions.de_json(obj['link_preview_options']) - origin = obj.get('origin') - if origin is not None: - origin = MessageOrigin.de_json(origin) + #todo: update data processing to common way - chat = obj.get('chat') - if chat is not None: - chat = Chat.de_json(chat) - - message_id = obj.get('message_id') - if message_id is not None: - message_id = int(message_id) - - link_preview_options = obj.get('link_preview_options') - if link_preview_options is not None: - link_preview_options = LinkPreviewOptions.de_json(link_preview_options) - animation = obj.get('animation') if animation is not None: animation = Animation.de_json(animation) @@ -8414,6 +8391,7 @@ def __init__(self, origin: MessageOrigin, chat: Optional[Chat]=None, message_id: self.poll: Optional[Poll] = poll self.venue: Optional[Venue] = venue + class MessageOrigin(JsonDeserializable): """ This object describes the origin of a message. @@ -8450,16 +8428,16 @@ def de_json(cls, json_string): message_type = obj.get('type') if message_type == 'user': - sender_user = User.de_json(obj.get('sender_user')) - return MessageOriginUser(date=obj.get('date'), sender_user=sender_user) + sender_user = User.de_json(obj['sender_user']) + return MessageOriginUser(date=obj['date'], sender_user=sender_user) elif message_type == 'hidden_user': - return MessageOriginHiddenUser(date=obj.get('date'), sender_user_name=obj.get('sender_user_name')) + return MessageOriginHiddenUser(date=obj['date'], sender_user_name=obj['sender_user_name']) elif message_type == 'chat': - sender_chat = Chat.de_json(obj.get('sender_chat')) - return MessageOriginChat(date=obj.get('date'), sender_chat=sender_chat, author_signature=obj.get('author_signature')) + sender_chat = Chat.de_json(obj['sender_chat']) + return MessageOriginChat(date=obj['date'], sender_chat=sender_chat, author_signature=obj.get('author_signature')) elif message_type == 'channel': - chat = Chat.de_json(obj.get('chat')) - return MessageOriginChannel(date=obj.get('date'), chat=chat, message_id=obj.get('message_id'), author_signature=obj.get('author_signature')) + chat = Chat.de_json(obj['chat']) + return MessageOriginChannel(date=obj['date'], chat=chat, message_id=obj['message_id'], author_signature=obj.get('author_signature')) def __init__(self, type: str, date: int) -> None: self.type: str = type @@ -8474,9 +8452,9 @@ class MessageOriginUser(MessageOrigin): :type sender_user: :class:`User` """ - def __init__(self, date: int, sender_user: Optional[User] = None) -> None: + def __init__(self, date: int, sender_user: User) -> None: super().__init__('user', date) - self.sender_user: Optional[User] = sender_user + self.sender_user: User = sender_user class MessageOriginHiddenUser(MessageOrigin): @@ -8487,9 +8465,9 @@ class MessageOriginHiddenUser(MessageOrigin): :type sender_user_name: :obj:`str` """ - def __init__(self, date: int, sender_user_name: Optional[str] = None) -> None: + def __init__(self, date: int, sender_user_name: str) -> None: super().__init__('hidden_user', date) - self.sender_user_name: Optional[str] = sender_user_name + self.sender_user_name: str = sender_user_name class MessageOriginChat(MessageOrigin): @@ -8503,9 +8481,9 @@ class MessageOriginChat(MessageOrigin): :type author_signature: :obj:`str` """ - def __init__(self, date: int, sender_chat: Optional[Chat] = None, author_signature: Optional[str] = None) -> None: + def __init__(self, date: int, sender_chat: Chat, author_signature: Optional[str] = None) -> None: super().__init__('chat', date) - self.sender_chat: Optional[Chat] = sender_chat + self.sender_chat: Chat = sender_chat self.author_signature: Optional[str] = author_signature @@ -8523,10 +8501,10 @@ class MessageOriginChannel(MessageOrigin): :type author_signature: :obj:`str` """ - def __init__(self, date: int, chat: Optional[Chat] = None, message_id: Optional[int] = None, author_signature: Optional[str] = None) -> None: + def __init__(self, date: int, chat: Chat, message_id: int, author_signature: Optional[str] = None) -> None: super().__init__('channel', date) - self.chat: Optional[Chat] = chat - self.message_id: Optional[int] = message_id + self.chat: Chat = chat + self.message_id: int = message_id self.author_signature: Optional[str] = author_signature @@ -8560,7 +8538,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - + #todo: adopt to common way return cls(is_disabled=obj.get('is_disabled'), url=obj.get('url'), prefer_small_media=obj.get('prefer_small_media'), prefer_large_media=obj.get('prefer_large_media'), show_above_text=obj.get('show_above_text')) @@ -8632,10 +8610,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - chats = [Chat.de_json(chat) for chat in obj.get('chats', [])] - - return cls(**obj, chats=chats) + self.chats = [Chat.de_json(chat) for chat in obj['chats']] + return cls(**obj) def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: int, only_new_members: Optional[bool] = None, has_public_winners: Optional[bool] = None, @@ -8647,8 +8623,9 @@ def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: self.only_new_members: Optional[bool] = only_new_members self.has_public_winners: Optional[bool] = has_public_winners self.prize_description: Optional[str] = prize_description - self.country_codes: Optional[List[str]] = country_codes or [] + self.country_codes: Optional[List[str]] = country_codes self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count + class GiveawayWinners(JsonDeserializable): """ @@ -8698,10 +8675,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - obj['chat'] = Chat.de_json(obj.get('chat')) - obj['winners'] = [User.de_json(user) for user in obj.get('winners', [])] - + obj['chat'] = Chat.de_json(obj['chat']) + obj['winners'] = [User.de_json(user) for user in obj['winners']] return cls(**obj) def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: int, winner_count: int, @@ -8720,6 +8695,7 @@ def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: self.only_new_members: Optional[bool] = only_new_members self.was_refunded: Optional[bool] = was_refunded self.prize_description: Optional[str] = prize_description + class GiveawayCompleted(JsonDeserializable): """ @@ -8745,9 +8721,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - obj['giveaway_message'] = Message.de_json(obj.get('giveaway_message')) - + if 'giveaway_message' in obj: + obj['giveaway_message'] = Message.de_json(obj['giveaway_message']) return cls(**obj) def __init__(self, winner_count: int, unclaimed_prize_count: Optional[int] = None, @@ -8755,11 +8730,13 @@ def __init__(self, winner_count: int, unclaimed_prize_count: Optional[int] = Non self.winner_count: int = winner_count self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count self.giveaway_message: Optional[Message] = giveaway_message + class GiveawayCreated(JsonDeserializable): """ This object represents a service message about the creation of a scheduled giveaway. Currently holds no information. """ + class TextQuote(JsonDeserializable): """ @@ -8788,9 +8765,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['entities'] = [MessageEntity.de_json(entity) for entity in obj.get('entities', [])] - return cls(**obj) def __init__(self, text: str, entities: Optional[List[MessageEntity]] = None, @@ -8800,6 +8775,7 @@ def __init__(self, text: str, entities: Optional[List[MessageEntity]] = None, self.position: Optional[int] = position self.is_manual: Optional[bool] = is_manual + class ReplyParameters(JsonDeserializable, Dictionaryable, JsonSerializable): """ Describes reply parameters for the message that is being sent. @@ -8836,11 +8812,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['quote_entities'] = [MessageEntity.de_json(entity) for entity in obj.get('quote_entities', [])] - - return cls(**obj) - + return cls(**obj) def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, allow_sending_without_reply: Optional[bool] = None, quote: Optional[str] = None, @@ -8854,12 +8827,10 @@ def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, self.quote_entities: Optional[List[MessageEntity]] = quote_entities self.quote_position: Optional[int] = quote_position - def to_dict(self) -> dict: json_dict = { 'message_id': self.message_id } - if self.chat_id is not None: json_dict['chat_id'] = self.chat_id if self.allow_sending_without_reply is not None: @@ -8872,11 +8843,11 @@ def to_dict(self) -> dict: json_dict['quote_entities'] = [entity.to_dict() for entity in self.quote_entities] if self.quote_position is not None: json_dict['quote_position'] = self.quote_position - return json_dict def to_json(self) -> str: return json.dumps(self.to_dict()) + class UsersShared(JsonDeserializable): """ @@ -8900,16 +8871,16 @@ class UsersShared(JsonDeserializable): :rtype: :class:`UsersShared` """ - def __init__(self, request_id, user_ids): - self.request_id = request_id - self.user_ids = user_ids - @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, request_id, user_ids): + self.request_id = request_id + self.user_ids = user_ids class ChatBoostUpdated(JsonDeserializable): @@ -8928,29 +8899,19 @@ class ChatBoostUpdated(JsonDeserializable): :rtype: :class:`ChatBoostUpdated` """ - def __init__(self, chat, boost): - self.chat = chat - self.boost = boost - @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) - - obj['chat'] = Chat.de_json(obj.get('chat')) - obj['boost'] = ChatBoost.de_json(obj.get('boost')) - + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['boost'] = ChatBoost.de_json(obj['boost']) return cls(**obj) - -# ChatBoostRemoved -# This object represents a boost removed from a chat. - -# Field Type Description -# chat Chat Chat which was boosted -# boost_id String Unique identifier of the boost -# remove_date Integer Point in time (Unix timestamp) when the boost was removed -# source ChatBoostSource Source of the removed boost + + def __init__(self, chat, boost): + self.chat = chat + self.boost = boost + class ChatBoostRemoved(JsonDeserializable): """ @@ -8974,22 +8935,21 @@ class ChatBoostRemoved(JsonDeserializable): :rtype: :class:`ChatBoostRemoved` """ - def __init__(self, chat, boost_id, remove_date, source): - self.chat = chat - self.boost_id = boost_id - self.remove_date = remove_date - self.source = source - @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + obj['source'] = ChatBoostSource.de_json(obj['source']) + return cls(**obj) - obj['chat'] = Chat.de_json(obj.get('chat')) - obj['source'] = ChatBoostSource.de_json(obj.get('source')) + def __init__(self, chat, boost_id, remove_date, source): + self.chat = chat + self.boost_id = boost_id + self.remove_date = remove_date + self.source = source - return cls(**obj) class ChatBoostSource(JsonDeserializable): """ @@ -9004,14 +8964,14 @@ class ChatBoostSource(JsonDeserializable): :rtype: :class:`ChatBoostSource` """ - def __init__(self, source): - self.source = source - @classmethod def de_json(cls, json_string): if json_string is None: return None return cls(**cls.check_json(json_string)) + + def __init__(self, source): + self.source = source class ChatBoostSourcePremium(ChatBoostSource): @@ -9030,17 +8990,18 @@ class ChatBoostSourcePremium(ChatBoostSource): :rtype: :class:`ChatBoostSourcePremium` """ - def __init__(self, user): - super().__init__('premium') - self.user = user - @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - user = User.de_json(json.dumps(obj['user'])) + user = User.de_json(obj['user']) return cls(user) + + def __init__(self, user): + super().__init__('premium') + self.user = user + class ChatBoostSourceGiftCode(ChatBoostSource): """ @@ -9058,17 +9019,18 @@ class ChatBoostSourceGiftCode(ChatBoostSource): :rtype: :class:`ChatBoostSourceGiftCode` """ - def __init__(self, user): - super().__init__('gift_code') - self.user = user - @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - user = User.de_json(json.dumps(obj['user'])) + user = User.de_json(obj['user']) return cls(user) + + def __init__(self, user): + super().__init__('gift_code') + self.user = user + class ChatBoostSourceGiveaway(ChatBoostSource): """ @@ -9092,29 +9054,21 @@ class ChatBoostSourceGiveaway(ChatBoostSource): :rtype: :class:`ChatBoostSourceGiveaway` """ - def __init__(self, giveaway_message_id, user=None, is_unclaimed=None): - super().__init__('giveaway') - self.giveaway_message_id = giveaway_message_id - self.user = user - self.is_unclaimed = is_unclaimed - @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - user = User.de_json(json.dumps(obj['user'])) if 'user' in obj else None + user = User.de_json(obj['user']) if 'user' in obj else None return cls(obj['giveaway_message_id'], user, obj.get('is_unclaimed')) -#ChatBoost -# This object contains information about a chat boost. + def __init__(self, giveaway_message_id, user=None, is_unclaimed=None): + super().__init__('giveaway') + self.giveaway_message_id = giveaway_message_id + self.user = user + self.is_unclaimed = is_unclaimed + -# Field Type Description -# boost_id String Unique identifier of the boost -# add_date Integer Point in time (Unix timestamp) when the chat was boosted -# expiration_date Integer Point in time (Unix timestamp) when the boost will automatically expire, unless the booster's Telegram Premium subscription is prolonged -# source ChatBoostSource Source of the added boost - class ChatBoost(JsonDeserializable): """ This object contains information about a chat boost. @@ -9137,22 +9091,21 @@ class ChatBoost(JsonDeserializable): :rtype: :class:`ChatBoost` """ - def __init__(self, boost_id, add_date, expiration_date, source): - self.boost_id = boost_id - self.add_date = add_date - self.expiration_date = expiration_date - self.source: ChatBoostSource = source - @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - obj['source'] = ChatBoostSource.de_json(json.dumps(obj['source'])) - + obj['source'] = ChatBoostSource.de_json(obj['source']) return cls(**obj) + def __init__(self, boost_id, add_date, expiration_date, source): + self.boost_id = boost_id + self.add_date = add_date + self.expiration_date = expiration_date + self.source: ChatBoostSource = source + + class UserChatBoosts(JsonDeserializable): """ This object represents a list of boosts added to a chat by a user. @@ -9166,18 +9119,16 @@ class UserChatBoosts(JsonDeserializable): :rtype: :class:`UserChatBoosts` """ - def __init__(self, boosts): - self.boosts: ChatBoost = boosts - @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) - obj['boosts'] = [ChatBoost.de_json(boost) for boost in obj.get('boosts', [])] - + obj['boosts'] = [ChatBoost.de_json(boost) for boost in obj['boosts']] return cls(**obj) + + def __init__(self, boosts): + self.boosts: ChatBoost = boosts class InaccessibleMessage(JsonDeserializable): @@ -9199,18 +9150,15 @@ class InaccessibleMessage(JsonDeserializable): :rtype: :class:`InaccessibleMessage` """ - def __init__(self, chat, message_id, date): - self.chat = chat - self.message_id = message_id - self.date = date - @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) - obj['chat'] = Chat.de_json(obj.get('chat')) - + obj['chat'] = Chat.de_json(obj['chat']) return cls(**obj) - + + def __init__(self, chat, message_id, date): + self.chat = chat + self.message_id = message_id + self.date = date From d0c3a7de0dfdc79c5cc708ae717cb459aa017410 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 16:47:53 +0300 Subject: [PATCH 1426/1808] Types: fix de_json of new classes --- telebot/types.py | 182 ++++++++++++++++++----------------------------- 1 file changed, 69 insertions(+), 113 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 5b8c42536..6f88621d1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1360,7 +1360,6 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.venue: Optional[Venue] = None self.animation: Optional[Animation] = None self.dice: Optional[Dice] = None - self.new_chat_member: Optional[User] = None # Deprecated since Bot API 3.0. Not processed anymore self.new_chat_members: Optional[List[User]] = None self.left_chat_member: Optional[User] = None self.new_chat_title: Optional[str] = None @@ -1399,7 +1398,6 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.giveaway_completed: Optional[GiveawayCompleted] = None self.forward_origin: Optional[MessageOrigin] = None - for key in options: setattr(self, key, options[key]) self.json = json_string @@ -1514,6 +1512,11 @@ def html_caption(self): """ return self.__html_text(self.caption, self.caption_entities) + @property + def new_chat_member(self): + logger.warning('The parameter "new_chat_member" is deprecated, use "new_chat_members" instead') + return None + class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): """ @@ -8266,108 +8269,61 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - origin = MessageOrigin.de_json(obj['origin']) + obj['origin'] = MessageOrigin.de_json(obj['origin']) if 'chat' in obj: - chat = Chat.de_json(obj['chat']) + obj['chat'] = Chat.de_json(obj['chat']) if 'link_preview_options' in obj: - link_preview_options = LinkPreviewOptions.de_json(obj['link_preview_options']) + obj['link_preview_options'] = LinkPreviewOptions.de_json(obj['link_preview_options']) + if 'animation' in obj: + obj['animation'] = Animation.de_json(obj['animation']) + if 'audio' in obj: + obj['audio'] = Audio.de_json(obj['audio']) + if 'document' in obj: + obj['document'] = Document.de_json(obj['document']) + if 'photo' in obj: + obj['photo'] = Message.parse_photo(obj['photo']) + if 'sticker' in obj: + obj['sticker'] = Sticker.de_json(obj['sticker']) + if 'story' in obj: + obj['story'] = Story.de_json(obj['story']) + if 'video' in obj: + obj['video'] = Video.de_json(obj['video']) + if 'video_note' in obj: + obj['video_note'] = VideoNote.de_json(obj['video_note']) + if 'voice' in obj: + obj['voice'] = Voice.de_json(obj['voice']) + if 'contact' in obj: + obj['contact'] = Contact.de_json(obj['contact']) + if 'dice' in obj: + obj['dice'] = Dice.de_json(obj['dice']) + if 'game' in obj: + obj['game'] = Game.de_json(obj['game']) + if 'giveaway' in obj: + obj['giveaway'] = Giveaway.de_json(obj['giveaway']) + if 'giveaway_winners' in obj: + obj['giveaway_winners'] = GiveawayWinners.de_json(obj['giveaway_winners']) + if 'invoice' in obj: + obj['invoice'] = Invoice.de_json(obj['invoice']) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + if 'poll' in obj: + obj['poll'] = Poll.de_json(obj['poll']) + if 'venue' in obj: + obj['venue'] = Venue.de_json(obj['venue']) + return cls(**obj) - #todo: update data processing to common way - - animation = obj.get('animation') - if animation is not None: - animation = Animation.de_json(animation) - - audio = obj.get('audio') - if audio is not None: - audio = Audio.de_json(audio) - - document = obj.get('document') - if document is not None: - document = Document.de_json(document) - - photo = obj.get('photo') - if photo is not None: - photo = [PhotoSize.de_json(photo[i]) for i in range(len(photo))] - - sticker = obj.get('sticker') - if sticker is not None: - sticker = Sticker.de_json(sticker) - - story = obj.get('story') - if story is not None: - story = Story.de_json(story) - - video = obj.get('video') - if video is not None: - video = Video.de_json(video) - - video_note = obj.get('video_note') - if video_note is not None: - video_note = VideoNote.de_json(video_note) - - voice = obj.get('voice') - if voice is not None: - voice = Voice.de_json(voice) - - has_media_spoiler = obj.get('has_media_spoiler') - if has_media_spoiler is not None: - has_media_spoiler = bool(has_media_spoiler) - - contact = obj.get('contact') - if contact is not None: - contact = Contact.de_json(contact) - - dice = obj.get('dice') - if dice is not None: - dice = Dice.de_json(dice) - - game = obj.get('game') - if game is not None: - game = Game.de_json(game) - - giveaway = obj.get('giveaway') - if giveaway is not None: - giveaway = Giveaway.de_json(giveaway) - - giveaway_winners = obj.get('giveaway_winners') - if giveaway_winners is not None: - giveaway_winners = GiveawayWinners.de_json(giveaway_winners) - - invoice = obj.get('invoice') - if invoice is not None: - invoice = Invoice.de_json(invoice) - - location = obj.get('location') - if location is not None: - location = Location.de_json(location) - - poll = obj.get('poll') - if poll is not None: - poll = Poll.de_json(poll) - - venue = obj.get('venue') - if venue is not None: - venue = Venue.de_json(venue) - - return cls(origin=origin, chat=chat, message_id=message_id, link_preview_options=link_preview_options, - animation=animation, audio=audio, document=document, photo=photo, sticker=sticker, story=story, - video=video, video_note=video_note, voice=voice, has_media_spoiler=has_media_spoiler, - contact=contact, dice=dice, game=game, giveaway=giveaway, giveaway_winners=giveaway_winners, - invoice=invoice, location=location, poll=poll, venue=venue) - - def __init__(self, origin: MessageOrigin, chat: Optional[Chat]=None, message_id: Optional[int]=None, - link_preview_options: Optional[LinkPreviewOptions]=None, animation: Optional[Animation]=None, - audio: Optional[Audio]=None, document: Optional[Document]=None, photo: Optional[List[PhotoSize]]=None, - sticker: Optional[Sticker]=None, story: Optional[Story]=None, video: Optional[Video]=None, - video_note: Optional[VideoNote]=None, voice: Optional[Voice]=None, - has_media_spoiler: Optional[bool]=None, contact: Optional[Contact]=None, - dice: Optional[Dice]=None, game: Optional[Game]=None, giveaway: Optional[Giveaway]=None, - giveaway_winners: Optional[GiveawayWinners]=None, invoice: Optional[Invoice]=None, - location: Optional[Location]=None, poll: Optional[Poll]=None, - venue: Optional[Venue]=None) -> None: + def __init__( + self, origin: MessageOrigin, chat: Optional[Chat]=None, message_id: Optional[int]=None, + link_preview_options: Optional[LinkPreviewOptions]=None, animation: Optional[Animation]=None, + audio: Optional[Audio]=None, document: Optional[Document]=None, photo: Optional[List[PhotoSize]]=None, + sticker: Optional[Sticker]=None, story: Optional[Story]=None, video: Optional[Video]=None, + video_note: Optional[VideoNote]=None, voice: Optional[Voice]=None, + has_media_spoiler: Optional[bool]=None, contact: Optional[Contact]=None, + dice: Optional[Dice]=None, game: Optional[Game]=None, giveaway: Optional[Giveaway]=None, + giveaway_winners: Optional[GiveawayWinners]=None, invoice: Optional[Invoice]=None, + location: Optional[Location]=None, poll: Optional[Poll]=None, + venue: Optional[Venue]=None) -> None: self.origin: MessageOrigin = origin - self.chat: Optional[Chat] = chat self.message_id: Optional[int] = message_id self.link_preview_options: Optional[LinkPreviewOptions] = link_preview_options @@ -8425,8 +8381,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - message_type = obj.get('type') + message_type = obj['type'] if message_type == 'user': sender_user = User.de_json(obj['sender_user']) return MessageOriginUser(date=obj['date'], sender_user=sender_user) @@ -8538,10 +8493,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - #todo: adopt to common way - return cls(is_disabled=obj.get('is_disabled'), url=obj.get('url'), prefer_small_media=obj.get('prefer_small_media'), - prefer_large_media=obj.get('prefer_large_media'), show_above_text=obj.get('show_above_text')) - + return cls(**obj) def __init__(self, is_disabled: Optional[bool] = None, url: Optional[str] = None, prefer_small_media: Optional[bool] = None, prefer_large_media: Optional[bool] = None, @@ -8565,7 +8517,7 @@ def to_dict(self) -> dict: json_dict['prefer_large_media'] = self.prefer_large_media if self.show_above_text is not None: json_dict['show_above_text'] = self.show_above_text - + return json_dict def to_json(self) -> str: return json.dumps(self.to_dict()) @@ -8610,7 +8562,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - self.chats = [Chat.de_json(chat) for chat in obj['chats']] + obj['chats'] = [Chat.de_json(chat) for chat in obj['chats']] return cls(**obj) def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: int, @@ -8765,11 +8717,13 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['entities'] = [MessageEntity.de_json(entity) for entity in obj.get('entities', [])] + if 'entities' in obj: + obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] return cls(**obj) - def __init__(self, text: str, entities: Optional[List[MessageEntity]] = None, - position: Optional[int] = None, is_manual: Optional[bool] = None) -> None: + def __init__(self, text: str, position: int, + entities: Optional[List[MessageEntity]] = None, + is_manual: Optional[bool] = None) -> None: self.text: str = text self.entities: Optional[List[MessageEntity]] = entities self.position: Optional[int] = position @@ -8812,7 +8766,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['quote_entities'] = [MessageEntity.de_json(entity) for entity in obj.get('quote_entities', [])] + if 'quote_entities' in obj: + obj['quote_entities'] = [MessageEntity.de_json(entity) for entity in obj['quote_entities']] return cls(**obj) def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, @@ -8968,7 +8923,8 @@ class ChatBoostSource(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None - return cls(**cls.check_json(json_string)) + obj = cls.check_json(json_string) + return cls(**obj) def __init__(self, source): self.source = source From 391a7e9f59e783efca3cc32c0fba8417b918d21b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 16:57:47 +0300 Subject: [PATCH 1427/1808] Add Python 3.12 to supported versions. --- .github/workflows/setup_python.yml | 2 +- .travis.yml | 1 + README.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 2a9787e25..0276c0026 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8', '3.9', '3.10', '3.11', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] + python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 diff --git a/.travis.yml b/.travis.yml index 8e6b02e5c..1fe9399d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "3.9" - "3.10" - "3.11" + - "3.12" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/README.md b/README.md index 289e38d7a..bcc07fa47 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ ## Getting started -This API is tested with Python 3.8-3.11 and Pypy 3. +This API is tested with Python 3.8-3.12 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager): From 9a2c70460fa8f9a0ae38c45ee0825d20b41751b5 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 17:28:54 +0300 Subject: [PATCH 1428/1808] Rollback python 3.12 --- .github/workflows/setup_python.yml | 2 +- .travis.yml | 1 - README.md | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 0276c0026..2a9787e25 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] + python-version: [ '3.8', '3.9', '3.10', '3.11', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 diff --git a/.travis.yml b/.travis.yml index 1fe9399d6..8e6b02e5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ python: - "3.9" - "3.10" - "3.11" - - "3.12" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/README.md b/README.md index bcc07fa47..289e38d7a 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ ## Getting started -This API is tested with Python 3.8-3.12 and Pypy 3. +This API is tested with Python 3.8-3.11 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager): From a1ac42f35452b8046e8400846137dca1a218098a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 17:59:27 +0300 Subject: [PATCH 1429/1808] Deprecation fixes #1 --- telebot/types.py | 173 +++++++++++++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 65 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 6f88621d1..c575b3770 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -811,29 +811,26 @@ class Message(JsonDeserializable): :param chat: Conversation the message belongs to :type chat: :class:`telebot.types.Chat` - :param forward_from: Optional. For forwarded messages, sender of the original message + :param forward_from: deprecated. :type forward_from: :class:`telebot.types.User` - :param forward_from_chat: Optional. For messages forwarded from channels or from anonymous administrators, - information about the original sender chat + :param forward_from_chat: deprecated. :type forward_from_chat: :class:`telebot.types.Chat` - :param forward_from_message_id: Optional. For messages forwarded from channels, identifier of the original + :param forward_from_message_id: deprecated. message in the channel :type forward_from_message_id: :obj:`int` - :param forward_signature: Optional. For forwarded messages that were originally sent in channels or by an - anonymous chat administrator, signature of the message sender if present + :param forward_signature: deprecated. :type forward_signature: :obj:`str` - :param forward_sender_name: Optional. Sender's name for messages forwarded from users who disallow adding a link - to their account in forwarded messages + :param forward_sender_name: deprecated. :type forward_sender_name: :obj:`str` - :param forward_date: Optional. For forwarded messages, date the original message was sent in Unix time + :param forward_date: deprecated. :type forward_date: :obj:`int` - :forward_origin: Optional. For forwarded messages, information about the original message; + :forward_origin: Optional. For forwarded messages, information about the original message; :type forward_origin: :class:`telebot.types.MessageOrigin` :param is_topic_message: Optional. True, if the message is sent to a forum topic @@ -1079,18 +1076,6 @@ def de_json(cls, json_string): 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: - opts['forward_from_chat'] = Chat.de_json(obj['forward_from_chat']) - if 'forward_from_message_id' in obj: - opts['forward_from_message_id'] = obj.get('forward_from_message_id') - if 'forward_signature' in obj: - opts['forward_signature'] = obj.get('forward_signature') - if 'forward_sender_name' in obj: - opts['forward_sender_name'] = obj.get('forward_sender_name') - if 'forward_date' in obj: - opts['forward_date'] = obj.get('forward_date') if 'is_automatic_forward' in obj: opts['is_automatic_forward'] = obj.get('is_automatic_forward') if 'is_topic_message' in obj: @@ -1123,8 +1108,7 @@ def de_json(cls, json_string): opts['document'] = Document.de_json(obj['document']) content_type = 'document' if 'animation' in obj: - # Document content type accompanies "animation", - # so "animation" should be checked below "document" to override it + # Document content type accompanies "animation", so "animation" should be checked after "document" to override it opts['animation'] = Animation.de_json(obj['animation']) content_type = 'animation' if 'game' in obj: @@ -1216,19 +1200,15 @@ def de_json(cls, json_string): content_type = 'proximity_alert_triggered' if 'video_chat_scheduled' in obj: opts['video_chat_scheduled'] = VideoChatScheduled.de_json(obj['video_chat_scheduled']) - opts['voice_chat_scheduled'] = opts['video_chat_scheduled'] # deprecated, for backward compatibility content_type = 'video_chat_scheduled' if 'video_chat_started' in obj: opts['video_chat_started'] = VideoChatStarted.de_json(obj['video_chat_started']) - opts['voice_chat_started'] = opts['video_chat_started'] # deprecated, for backward compatibility content_type = 'video_chat_started' if 'video_chat_ended' in obj: opts['video_chat_ended'] = VideoChatEnded.de_json(obj['video_chat_ended']) - opts['voice_chat_ended'] = opts['video_chat_ended'] # deprecated, for backward compatibility content_type = 'video_chat_ended' if 'video_chat_participants_invited' in obj: opts['video_chat_participants_invited'] = VideoChatParticipantsInvited.de_json(obj['video_chat_participants_invited']) - opts['voice_chat_participants_invited'] = opts['video_chat_participants_invited'] # deprecated, for backward compatibility content_type = 'video_chat_participants_invited' if 'web_app_data' in obj: opts['web_app_data'] = WebAppData.de_json(obj['web_app_data']) @@ -1288,8 +1268,8 @@ def de_json(cls, json_string): if 'giveaway_completed' in obj: opts['giveaway_completed'] = GiveawayCompleted.de_json(obj['giveaway_completed']) content_type = 'giveaway_completed' - if 'message_origin' in obj: - opts['message_origin'] = MessageOrigin.de_json(obj['message_origin']) + if 'forward_origin' in obj: + opts['forward_origin'] = MessageOrigin.de_json(obj['forward_origin']) return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1331,12 +1311,6 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso 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 - self.forward_signature: Optional[str] = None - self.forward_sender_name: Optional[str] = None - self.forward_date: Optional[int] = None self.is_automatic_forward: Optional[bool] = None self.reply_to_message: Optional[Message] = None self.via_bot: Optional[User] = None @@ -1512,11 +1486,78 @@ def html_caption(self): """ return self.__html_text(self.caption, self.caption_entities) + @property + def voice_chat_scheduled(self): + logger.warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') + return self.video_chat_scheduled + + @property + def voice_chat_started(self): + logger.warning('The parameter "voice_chat_started" is deprecated, use "video_chat_started" instead') + return self.video_chat_started + + @property + def voice_chat_ended(self): + logger.warning('The parameter "voice_chat_ended" is deprecated, use "video_chat_ended" instead') + return self.video_chat_ended + + @property + def voice_chat_participants_invited(self): + logger.warning('The parameter "voice_chat_participants_invited" is deprecated, use "video_chat_participants_invited" instead') + return self.video_chat_participants_invited + @property def new_chat_member(self): logger.warning('The parameter "new_chat_member" is deprecated, use "new_chat_members" instead') return None + @property + def forward_from(self): + logger.warning('The parameter "forward_from" is deprecated, use "forward_origin" instead') + if self.forward_origin and isinstance(self.forward_origin, MessageOriginUser): + return self.forward_origin.sender_user + return None + + @property + def forward_from_chat(self): + logger.warning('The parameter "forward_from_chat" is deprecated, use "forward_origin" instead') + if self.forward_origin and isinstance(self.forward_origin, MessageOriginChat): + return self.forward_origin.sender_chat + elif self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): + return self.forward_origin.chat + return None + + @property + def forward_from_message_id(self): + logger.warning('The parameter "forward_from_message_id" is deprecated, use "forward_origin" instead') + if self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): + return self.forward_origin.message_id + return None + + @property + def forward_signature(self): + logger.warning('The parameter "forward_signature" is deprecated, use "forward_origin" instead') + if self.forward_origin and isinstance(self.forward_origin, MessageOriginChat): + return self.forward_origin.author_signature + elif self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): + return self.forward_origin.author_signature + return None + + @property + def forward_sender_name(self): + logger.warning('The parameter "forward_sender_name" is deprecated, use "forward_origin" instead') + if self.forward_origin and isinstance(self.forward_origin, MessageOriginHiddenUser): + return self.forward_origin.sender_user_name + return None + + @property + def forward_date(self): + logger.warning('The parameter "forward_date" is deprecated, use "forward_origin" instead') + if self.forward_origin: + return self.forward_origin.date + return None + + class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): """ @@ -3031,13 +3072,11 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_pin_messages: bool = can_pin_messages self.is_member: bool = is_member self.can_send_messages: bool = can_send_messages - #self.can_send_media_messages: bool = can_send_media_messages self.can_send_polls: bool = can_send_polls self.can_send_other_messages: bool = can_send_other_messages self.can_add_web_page_previews: bool = can_add_web_page_previews self.can_manage_chat: bool = can_manage_chat self.can_manage_video_chats: bool = can_manage_video_chats - self.can_manage_voice_chats: bool = self.can_manage_video_chats # deprecated, for backward compatibility self.until_date: int = until_date self.can_manage_topics: bool = can_manage_topics self.can_send_audios: bool = can_send_audios @@ -3049,7 +3088,12 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed self.can_post_stories: bool = can_post_stories self.can_edit_stories: bool = can_edit_stories self.can_delete_stories: bool = can_delete_stories - + + @property + def can_manage_voice_chats(self): + logger.warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') + return self.can_manage_video_chats + class ChatMemberOwner(ChatMember): @@ -3330,8 +3374,7 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): value of can_pin_messages :type can_manage_topics: :obj:`bool` - :param can_send_media_messages: deprecated. True, if the user is allowed to send audios, documents, photos, videos, - video notes and voice notes + :param can_send_media_messages: deprecated. :type can_send_media_messages: :obj:`bool` :return: Instance of the class @@ -3351,7 +3394,6 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send can_invite_users=None, can_pin_messages=None, can_manage_topics=None, **kwargs): self.can_send_messages: bool = can_send_messages - #self.can_send_media_messages: bool = can_send_media_messages self.can_send_polls: bool = can_send_polls self.can_send_other_messages: bool = can_send_other_messages self.can_add_web_page_previews: bool = can_add_web_page_previews @@ -3367,7 +3409,7 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send self.can_send_voice_notes: bool = can_send_voice_notes if can_send_media_messages is not None: - logger.warning("can_send_media_messages is deprecated. Use individual parameters like can_send_audios, can_send_documents, etc.") + logger.warning('The parameter "can_send_media_messages" is deprecated. Use individual parameters like "can_send_audios", "can_send_documents" etc.') self.can_send_audios = can_send_media_messages self.can_send_documents = can_send_media_messages self.can_send_photos = can_send_media_messages @@ -3712,9 +3754,12 @@ class InputTextMessageContent(Dictionaryable): parse_mode :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param disable_web_page_preview: Optional, deprecated. Disables link previews for links in the sent message + :param disable_web_page_preview: deprecated :type disable_web_page_preview: :obj:`bool` + :param link_preview_options: Optional. Link preview generation options for the message + :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :return: Instance of the class :rtype: :class:`telebot.types.InputTextMessageContent` """ @@ -3724,10 +3769,10 @@ def __init__(self, message_text, parse_mode=None, entities=None, disable_web_pag self.entities: List[MessageEntity] = entities link_preview_options: LinkPreviewOptions = link_preview_options if disable_web_page_preview is not None: - logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") + logger.warning('The parameter "disable_web_page_preview" is deprecated. Use "link_preview_options" instead.') if link_preview_options: - logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") + logger.warning('Both "link_preview_options" and "disable_web_page_preview" parameters are set: conflicting, "disable_web_page_preview" is deprecated') else: self.link_preview_options: LinkPreviewOptions = LinkPreviewOptions(disable_web_page_preview) @@ -5645,7 +5690,7 @@ class Game(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) obj['photo'] = Game.parse_photo(obj['photo']) if 'text_entities' in obj: @@ -5724,7 +5769,7 @@ class Animation(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: obj["thumbnail"] = PhotoSize.de_json(obj['thumbnail']) @@ -5770,7 +5815,7 @@ class GameHighScore(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) return cls(**obj) @@ -5841,7 +5886,7 @@ class Invoice(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) @@ -5882,7 +5927,7 @@ class ShippingAddress(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) @@ -5918,7 +5963,7 @@ class OrderInfo(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) obj['shipping_address'] = ShippingAddress.de_json(obj.get('shipping_address')) return cls(**obj) @@ -6008,7 +6053,7 @@ class SuccessfulPayment(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) return cls(**obj) @@ -6047,7 +6092,7 @@ class ShippingQuery(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) obj['from_user'] = User.de_json(obj.pop('from')) obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address']) @@ -6094,7 +6139,7 @@ class PreCheckoutQuery(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) obj['from_user'] = User.de_json(obj.pop('from')) obj['order_info'] = OrderInfo.de_json(obj.get('order_info')) @@ -6133,8 +6178,7 @@ class StickerSet(JsonDeserializable): :param is_video: True, if the sticker set contains video stickers :type is_video: :obj:`bool` - :param contains_masks: True, if the sticker set contains masks. Deprecated since Bot API 6.2, - use sticker_type instead. + :param contains_masks: deprecated :type contains_masks: :obj:`bool` :param stickers: List of all set stickers @@ -6148,7 +6192,7 @@ class StickerSet(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) stickers = [] for s in obj['stickers']: @@ -6179,7 +6223,7 @@ def contains_masks(self): """ Deprecated since Bot API 6.2, use sticker_type instead. """ - logger.warning("contains_masks is deprecated, use sticker_type instead") + logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type instead"') return self.sticker_type == 'mask' @@ -6244,7 +6288,7 @@ class Sticker(JsonDeserializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) @@ -6308,7 +6352,7 @@ class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) @@ -6714,7 +6758,7 @@ class PollOption(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) @@ -6780,7 +6824,7 @@ class Poll(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) obj['poll_id'] = obj.pop('id') options = [] @@ -6806,7 +6850,6 @@ def __init__( self.is_anonymous: bool = is_anonymous self.type: str = type if poll_type is not None: - # Wrong param name backward compatibility logger.warning("Poll: poll_type parameter is deprecated. Use type instead.") if type is None: self.type: str = poll_type @@ -6854,7 +6897,7 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): """ @classmethod def de_json(cls, json_string): - if (json_string is None): return None + if json_string is None: return None obj = cls.check_json(json_string) if 'user' in obj: obj['user'] = User.de_json(obj['user']) From cf176a93d3bdb51167052f8f5b7f8f190a051370 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 18:39:36 +0300 Subject: [PATCH 1430/1808] Added hint (for me) and kwargs (for safety) --- telebot/types.py | 559 ++++++++++++++++++++++++++--------------------- 1 file changed, 306 insertions(+), 253 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index c575b3770..58843f7e3 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -19,7 +19,6 @@ logger = logging.getLogger('TeleBot') - class JsonSerializable(object): """ Subclasses of this class are guaranteed to be able to be converted to JSON format. @@ -692,7 +691,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, is_forum=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,**kwargs): + profile_background_custom_emoji_id=None, has_visible_history=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -770,300 +769,301 @@ class WebAppData(JsonDeserializable, Dictionaryable): :rtype: :class:`telebot.types.WebAppData` """ - def __init__(self, data, button_text): - self.data = data - self.button_text = button_text - def to_dict(self): - return {'data': self.data, 'button_text': self.button_text} - @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, data, button_text, **kwargs): + self.data = data + self.button_text = button_text + def to_dict(self): + return {'data': self.data, 'button_text': self.button_text} + +# noinspection PyUnresolvedReferences class Message(JsonDeserializable): """ - This object represents a message. + This object represents a message. - Telegram Documentation: https://core.telegram.org/bots/api#message + Telegram Documentation: https://core.telegram.org/bots/api#message - :param message_id: Unique message identifier inside this chat - :type message_id: :obj:`int` + :param message_id: Unique message identifier inside this chat + :type message_id: :obj:`int` - :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups only - :type message_thread_id: :obj:`int` + :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups only + :type message_thread_id: :obj:`int` - :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the - field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. - :type from_user: :class:`telebot.types.User` + :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the + field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + :type from_user: :class:`telebot.types.User` - :param sender_chat: Optional. Sender of the message, sent on behalf of a chat. For example, the channel itself for - channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for - messages automatically forwarded to the discussion group. For backward compatibility, the field from contains a - fake sender user in non-channel chats, if the message was sent on behalf of a chat. - :type sender_chat: :class:`telebot.types.Chat` + :param sender_chat: Optional. Sender of the message, sent on behalf of a chat. For example, the channel itself for + channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for + messages automatically forwarded to the discussion group. For backward compatibility, the field from contains a + fake sender user in non-channel chats, if the message was sent on behalf of a chat. + :type sender_chat: :class:`telebot.types.Chat` - :param date: Date the message was sent in Unix time - :type date: :obj:`int` + :param date: Date the message was sent in Unix time + :type date: :obj:`int` - :param chat: Conversation the message belongs to - :type chat: :class:`telebot.types.Chat` + :param chat: Conversation the message belongs to + :type chat: :class:`telebot.types.Chat` - :param forward_from: deprecated. - :type forward_from: :class:`telebot.types.User` + :param forward_from: deprecated. + :type forward_from: :class:`telebot.types.User` - :param forward_from_chat: deprecated. - :type forward_from_chat: :class:`telebot.types.Chat` + :param forward_from_chat: deprecated. + :type forward_from_chat: :class:`telebot.types.Chat` - :param forward_from_message_id: deprecated. - message in the channel - :type forward_from_message_id: :obj:`int` + :param forward_from_message_id: deprecated. + message in the channel + :type forward_from_message_id: :obj:`int` - :param forward_signature: deprecated. - :type forward_signature: :obj:`str` + :param forward_signature: deprecated. + :type forward_signature: :obj:`str` - :param forward_sender_name: deprecated. - :type forward_sender_name: :obj:`str` + :param forward_sender_name: deprecated. + :type forward_sender_name: :obj:`str` - :param forward_date: deprecated. - :type forward_date: :obj:`int` + :param forward_date: deprecated. + :type forward_date: :obj:`int` - :forward_origin: Optional. For forwarded messages, information about the original message; - :type forward_origin: :class:`telebot.types.MessageOrigin` + :forward_origin: Optional. For forwarded messages, information about the original message; + :type forward_origin: :class:`telebot.types.MessageOrigin` - :param is_topic_message: Optional. True, if the message is sent to a forum topic - :type is_topic_message: :obj:`bool` + :param is_topic_message: Optional. True, if the message is sent to a forum topic + :type is_topic_message: :obj:`bool` - :param is_automatic_forward: Optional. :obj:`bool`, if the message is a channel post that was automatically - forwarded to the connected discussion group - :type is_automatic_forward: :obj:`bool` + :param is_automatic_forward: Optional. :obj:`bool`, if the message is a channel post that was automatically + forwarded to the connected discussion group + :type is_automatic_forward: :obj:`bool` - :param reply_to_message: Optional. For replies, the original message. Note that the Message object in this field - will not contain further reply_to_message fields even if it itself is a reply. - :type reply_to_message: :class:`telebot.types.Message` + :param reply_to_message: Optional. For replies, the original message. Note that the Message object in this field + will not contain further reply_to_message fields even if it itself is a reply. + :type reply_to_message: :class:`telebot.types.Message` - :param external_reply: Optional. Information about the message that is being replied to, which may come from another chat or forum topic - :type external_reply: :class:`telebot.types.ExternalReplyInfo` + :param external_reply: Optional. Information about the message that is being replied to, which may come from another chat or forum topic + :type external_reply: :class:`telebot.types.ExternalReplyInfo` - :param quote: Optional. For replies that quote part of the original message, the quoted part of the message - :type quote: :class:`telebot.types.TextQuote` + :param quote: Optional. For replies that quote part of the original message, the quoted part of the message + :type quote: :class:`telebot.types.TextQuote` - :param via_bot: Optional. Bot through which the message was sent - :type via_bot: :class:`telebot.types.User` + :param via_bot: Optional. Bot through which the message was sent + :type via_bot: :class:`telebot.types.User` - :param edit_date: Optional. Date the message was last edited in Unix time - :type edit_date: :obj:`int` + :param edit_date: Optional. Date the message was last edited in Unix time + :type edit_date: :obj:`int` - :param has_protected_content: Optional. :obj:`bool`, if the message can't be forwarded - :type has_protected_content: :obj:`bool` + :param has_protected_content: Optional. :obj:`bool`, if the message can't be forwarded + :type has_protected_content: :obj:`bool` - :param media_group_id: Optional. The unique identifier of a media message group this message belongs to - :type media_group_id: :obj:`str` + :param media_group_id: Optional. The unique identifier of a media message group this message belongs to + :type media_group_id: :obj:`str` - :param author_signature: Optional. Signature of the post author for messages in channels, or the custom title of an - anonymous group administrator - :type author_signature: :obj:`str` + :param author_signature: Optional. Signature of the post author for messages in channels, or the custom title of an + anonymous group administrator + :type author_signature: :obj:`str` - :param text: Optional. For text messages, the actual UTF-8 text of the message - :type text: :obj:`str` + :param text: Optional. For text messages, the actual UTF-8 text of the message + :type text: :obj:`str` - :param entities: Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that - appear in the text - :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param entities: Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that + appear in the text + :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param link_preview_options: Optional. Options used for link preview generation for the message, - if it is a text message and link preview options were changed - :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :param link_preview_options: Optional. Options used for link preview generation for the message, + if it is a text message and link preview options were changed + :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` - :param animation: Optional. Message is an animation, information about the animation. For backward - compatibility, when this field is set, the document field will also be set - :type animation: :class:`telebot.types.Animation` + :param animation: Optional. Message is an animation, information about the animation. For backward + compatibility, when this field is set, the document field will also be set + :type animation: :class:`telebot.types.Animation` - :param audio: Optional. Message is an audio file, information about the file - :type audio: :class:`telebot.types.Audio` + :param audio: Optional. Message is an audio file, information about the file + :type audio: :class:`telebot.types.Audio` - :param document: Optional. Message is a general file, information about the file - :type document: :class:`telebot.types.Document` + :param document: Optional. Message is a general file, information about the file + :type document: :class:`telebot.types.Document` - :param photo: Optional. Message is a photo, available sizes of the photo - :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` + :param photo: Optional. Message is a photo, available sizes of the photo + :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` - :param sticker: Optional. Message is a sticker, information about the sticker - :type sticker: :class:`telebot.types.Sticker` + :param sticker: Optional. Message is a sticker, information about the sticker + :type sticker: :class:`telebot.types.Sticker` - :param story: Optional. Message is a forwarded story - :type story: :class:`telebot.types.Story` + :param story: Optional. Message is a forwarded story + :type story: :class:`telebot.types.Story` - :param video: Optional. Message is a video, information about the video - :type video: :class:`telebot.types.Video` + :param video: Optional. Message is a video, information about the video + :type video: :class:`telebot.types.Video` - :param video_note: Optional. Message is a video note, information about the video message - :type video_note: :class:`telebot.types.VideoNote` + :param video_note: Optional. Message is a video note, information about the video message + :type video_note: :class:`telebot.types.VideoNote` - :param voice: Optional. Message is a voice message, information about the file - :type voice: :class:`telebot.types.Voice` + :param voice: Optional. Message is a voice message, information about the file + :type voice: :class:`telebot.types.Voice` - :param caption: Optional. Caption for the animation, audio, document, photo, video or voice - :type caption: :obj:`str` + :param caption: Optional. Caption for the animation, audio, document, photo, video or voice + :type caption: :obj:`str` - :param caption_entities: Optional. For messages with a caption, special entities like usernames, URLs, bot - commands, etc. that appear in the caption - :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param caption_entities: Optional. For messages with a caption, special entities like usernames, URLs, bot + commands, etc. that appear in the caption + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation - :type has_media_spoiler: :obj:`bool` + :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation + :type has_media_spoiler: :obj:`bool` - :param contact: Optional. Message is a shared contact, information about the contact - :type contact: :class:`telebot.types.Contact` + :param contact: Optional. Message is a shared contact, information about the contact + :type contact: :class:`telebot.types.Contact` - :param dice: Optional. Message is a dice with random value - :type dice: :class:`telebot.types.Dice` + :param dice: Optional. Message is a dice with random value + :type dice: :class:`telebot.types.Dice` - :param game: Optional. Message is a game, information about the game. More about games » - :type game: :class:`telebot.types.Game` + :param game: Optional. Message is a game, information about the game. More about games » + :type game: :class:`telebot.types.Game` - :param poll: Optional. Message is a native poll, information about the poll - :type poll: :class:`telebot.types.Poll` + :param poll: Optional. Message is a native poll, information about the poll + :type poll: :class:`telebot.types.Poll` - :param venue: Optional. Message is a venue, information about the venue. For backward compatibility, when this - field is set, the location field will also be set - :type venue: :class:`telebot.types.Venue` + :param venue: Optional. Message is a venue, information about the venue. For backward compatibility, when this + field is set, the location field will also be set + :type venue: :class:`telebot.types.Venue` - :param location: Optional. Message is a shared location, information about the location - :type location: :class:`telebot.types.Location` + :param location: Optional. Message is a shared location, information about the location + :type location: :class:`telebot.types.Location` - :param new_chat_members: Optional. New members that were added to the group or supergroup and information about - them (the bot itself may be one of these members) - :type new_chat_members: :obj:`list` of :class:`telebot.types.User` + :param new_chat_members: Optional. New members that were added to the group or supergroup and information about + them (the bot itself may be one of these members) + :type new_chat_members: :obj:`list` of :class:`telebot.types.User` - :param left_chat_member: Optional. A member was removed from the group, information about them (this member may be - the bot itself) - :type left_chat_member: :class:`telebot.types.User` + :param left_chat_member: Optional. A member was removed from the group, information about them (this member may be + the bot itself) + :type left_chat_member: :class:`telebot.types.User` - :param new_chat_title: Optional. A chat title was changed to this value - :type new_chat_title: :obj:`str` + :param new_chat_title: Optional. A chat title was changed to this value + :type new_chat_title: :obj:`str` - :param new_chat_photo: Optional. A chat photo was change to this value - :type new_chat_photo: :obj:`list` of :class:`telebot.types.PhotoSize` + :param new_chat_photo: Optional. A chat photo was change to this value + :type new_chat_photo: :obj:`list` of :class:`telebot.types.PhotoSize` - :param delete_chat_photo: Optional. Service message: the chat photo was deleted - :type delete_chat_photo: :obj:`bool` + :param delete_chat_photo: Optional. Service message: the chat photo was deleted + :type delete_chat_photo: :obj:`bool` - :param group_chat_created: Optional. Service message: the group has been created - :type group_chat_created: :obj:`bool` + :param group_chat_created: Optional. Service message: the group has been created + :type group_chat_created: :obj:`bool` - :param supergroup_chat_created: Optional. Service message: the supergroup has been created. This field can't be - received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can - only be found in reply_to_message if someone replies to a very first message in a directly created supergroup. - :type supergroup_chat_created: :obj:`bool` + :param supergroup_chat_created: Optional. Service message: the supergroup has been created. This field can't be + received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can + only be found in reply_to_message if someone replies to a very first message in a directly created supergroup. + :type supergroup_chat_created: :obj:`bool` - :param channel_chat_created: Optional. Service message: the channel has been created. This field can't be - received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only - be found in reply_to_message if someone replies to a very first message in a channel. - :type channel_chat_created: :obj:`bool` + :param channel_chat_created: Optional. Service message: the channel has been created. This field can't be + received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only + be found in reply_to_message if someone replies to a very first message in a channel. + :type channel_chat_created: :obj:`bool` - :param message_auto_delete_timer_changed: Optional. Service message: auto-delete timer settings changed in - the chat - :type message_auto_delete_timer_changed: :class:`telebot.types.MessageAutoDeleteTimerChanged` + :param message_auto_delete_timer_changed: Optional. Service message: auto-delete timer settings changed in + the chat + :type message_auto_delete_timer_changed: :class:`telebot.types.MessageAutoDeleteTimerChanged` - :param migrate_to_chat_id: Optional. The group has been migrated to a supergroup with the specified identifier. - This number may have more than 32 significant bits and some programming languages may have difficulty/silent - defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision - float type are safe for storing this identifier. - :type migrate_to_chat_id: :obj:`int` + :param migrate_to_chat_id: Optional. The group has been migrated to a supergroup with the specified identifier. + This number may have more than 32 significant bits and some programming languages may have difficulty/silent + defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision + float type are safe for storing this identifier. + :type migrate_to_chat_id: :obj:`int` - :param migrate_from_chat_id: Optional. The supergroup has been migrated from a group with the specified - identifier. This number may have more than 32 significant bits and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or - double-precision float type are safe for storing this identifier. - :type migrate_from_chat_id: :obj:`int` + :param migrate_from_chat_id: Optional. The supergroup has been migrated from a group with the specified + identifier. This number may have more than 32 significant bits and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this identifier. + :type migrate_from_chat_id: :obj:`int` - :param pinned_message: Optional. Specified message was pinned. Note that the Message object in this field will not - contain further reply_to_message fields even if it is itself a reply. - :type pinned_message: :class:`telebot.types.Message` + :param pinned_message: Optional. Specified message was pinned. Note that the Message object in this field will not + contain further reply_to_message fields even if it is itself a reply. + :type pinned_message: :class:`telebot.types.Message` - :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » - :type invoice: :class:`telebot.types.Invoice` + :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » + :type invoice: :class:`telebot.types.Invoice` - :param successful_payment: Optional. Message is a service message about a successful payment, information about - the payment. More about payments » - :type successful_payment: :class:`telebot.types.SuccessfulPayment` + :param successful_payment: Optional. Message is a service message about a successful payment, information about + the payment. More about payments » + :type successful_payment: :class:`telebot.types.SuccessfulPayment` - :param users_shared: Optional. Service message: a user was shared with the bot - :type users_shared: :class:`telebot.types.UserShared` + :param users_shared: Optional. Service message: a user was shared with the bot + :type users_shared: :class:`telebot.types.UserShared` - :param chat_shared: Optional. Service message: a chat was shared with the bot - :type chat_shared: :class:`telebot.types.ChatShared` + :param chat_shared: Optional. Service message: a chat was shared with the bot + :type chat_shared: :class:`telebot.types.ChatShared` - :param connected_website: Optional. The domain name of the website on which the user has logged in. More about - Telegram Login » - :type connected_website: :obj:`str` + :param connected_website: Optional. The domain name of the website on which the user has logged in. More about + Telegram Login » + :type connected_website: :obj:`str` - :param write_access_allowed: Optional. Service message: the user allowed the bot added to the attachment - menu to write messages - :type write_access_allowed: :class:`telebot.types.WriteAccessAllowed` + :param write_access_allowed: Optional. Service message: the user allowed the bot added to the attachment + menu to write messages + :type write_access_allowed: :class:`telebot.types.WriteAccessAllowed` - :param passport_data: Optional. Telegram Passport data - :type passport_data: :class:`telebot.types.PassportData` + :param passport_data: Optional. Telegram Passport data + :type passport_data: :class:`telebot.types.PassportData` - :param proximity_alert_triggered: Optional. Service message. A user in the chat triggered another user's - proximity alert while sharing Live Location. - :type proximity_alert_triggered: :class:`telebot.types.ProximityAlertTriggered` + :param proximity_alert_triggered: Optional. Service message. A user in the chat triggered another user's + proximity alert while sharing Live Location. + :type proximity_alert_triggered: :class:`telebot.types.ProximityAlertTriggered` - :param forum_topic_created: Optional. Service message: forum topic created - :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` + :param forum_topic_created: Optional. Service message: forum topic created + :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` - :param forum_topic_edited: Optional. Service message: forum topic edited - :type forum_topic_edited: :class:`telebot.types.ForumTopicEdited` + :param forum_topic_edited: Optional. Service message: forum topic edited + :type forum_topic_edited: :class:`telebot.types.ForumTopicEdited` - :param forum_topic_closed: Optional. Service message: forum topic closed - :type forum_topic_closed: :class:`telebot.types.ForumTopicClosed` + :param forum_topic_closed: Optional. Service message: forum topic closed + :type forum_topic_closed: :class:`telebot.types.ForumTopicClosed` - :param forum_topic_reopened: Optional. Service message: forum topic reopened - :type forum_topic_reopened: :class:`telebot.types.ForumTopicReopened` + :param forum_topic_reopened: Optional. Service message: forum topic reopened + :type forum_topic_reopened: :class:`telebot.types.ForumTopicReopened` - :param general_forum_topic_hidden: Optional. Service message: the 'General' forum topic hidden - :type general_forum_topic_hidden: :class:`telebot.types.GeneralForumTopicHidden` + :param general_forum_topic_hidden: Optional. Service message: the 'General' forum topic hidden + :type general_forum_topic_hidden: :class:`telebot.types.GeneralForumTopicHidden` - :param general_forum_topic_unhidden: Optional. Service message: the 'General' forum topic unhidden - :type general_forum_topic_unhidden: :class:`telebot.types.GeneralForumTopicUnhidden` + :param general_forum_topic_unhidden: Optional. Service message: the 'General' forum topic unhidden + :type general_forum_topic_unhidden: :class:`telebot.types.GeneralForumTopicUnhidden` - :param giveaway_created: Optional. Service message: a giveaway has been created - :type giveaway_created: :class:`telebot.types.GiveawayCreated` + :param giveaway_created: Optional. Service message: a giveaway has been created + :type giveaway_created: :class:`telebot.types.GiveawayCreated` - :param giveaway: Optional. The message is a scheduled giveaway message - :type giveaway: :class:`telebot.types.Giveaway` + :param giveaway: Optional. The message is a scheduled giveaway message + :type giveaway: :class:`telebot.types.Giveaway` - :param giveaway_winners: Optional. Service message: giveaway winners(public winners) - :type giveaway_winners: :class:`telebot.types.GiveawayWinners` + :param giveaway_winners: Optional. Service message: giveaway winners(public winners) + :type giveaway_winners: :class:`telebot.types.GiveawayWinners` - :param giveaway_completed: Optional. Service message: giveaway completed, without public winners - :type giveaway_completed: :class:`telebot.types.GiveawayCompleted` + :param giveaway_completed: Optional. Service message: giveaway completed, without public winners + :type giveaway_completed: :class:`telebot.types.GiveawayCompleted` - :param video_chat_scheduled: Optional. Service message: video chat scheduled - :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` + :param video_chat_scheduled: Optional. Service message: video chat scheduled + :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` - :param video_chat_started: Optional. Service message: video chat started - :type video_chat_started: :class:`telebot.types.VideoChatStarted` + :param video_chat_started: Optional. Service message: video chat started + :type video_chat_started: :class:`telebot.types.VideoChatStarted` - :param video_chat_ended: Optional. Service message: video chat ended - :type video_chat_ended: :class:`telebot.types.VideoChatEnded` + :param video_chat_ended: Optional. Service message: video chat ended + :type video_chat_ended: :class:`telebot.types.VideoChatEnded` - :param video_chat_participants_invited: Optional. Service message: new participants invited to a video chat - :type video_chat_participants_invited: :class:`telebot.types.VideoChatParticipantsInvited` + :param video_chat_participants_invited: Optional. Service message: new participants invited to a video chat + :type video_chat_participants_invited: :class:`telebot.types.VideoChatParticipantsInvited` - :param web_app_data: Optional. Service message: data sent by a Web App - :type web_app_data: :class:`telebot.types.WebAppData` + :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. - :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + :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 - :rtype: :class:`telebot.types.Message` - """ + :return: Instance of the class + :rtype: :class:`telebot.types.Message` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -1489,21 +1489,25 @@ def html_caption(self): @property def voice_chat_scheduled(self): logger.warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') + # noinspection PyUnresolvedReferences return self.video_chat_scheduled @property def voice_chat_started(self): logger.warning('The parameter "voice_chat_started" is deprecated, use "video_chat_started" instead') + # noinspection PyUnresolvedReferences return self.video_chat_started @property def voice_chat_ended(self): logger.warning('The parameter "voice_chat_ended" is deprecated, use "video_chat_ended" instead') + # noinspection PyUnresolvedReferences return self.video_chat_ended @property def voice_chat_participants_invited(self): logger.warning('The parameter "voice_chat_participants_invited" is deprecated, use "video_chat_participants_invited" instead') + # noinspection PyUnresolvedReferences return self.video_chat_participants_invited @property @@ -2197,6 +2201,7 @@ def __init__(self, file_id, file_unique_id, file_size=None, file_path=None, **kw self.file_path: str = file_path +# noinspection PyUnresolvedReferences class ForceReply(JsonSerializable): """ Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode. @@ -2232,6 +2237,7 @@ def to_json(self): return json.dumps(json_dict) +# noinspection PyUnresolvedReferences class ReplyKeyboardRemove(JsonSerializable): """ Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button (see ReplyKeyboardMarkup). @@ -2287,8 +2293,9 @@ def __init__(self, url, **kwargs): def to_dict(self): return {'url': self.url} - + +# noinspection PyUnresolvedReferences class ReplyKeyboardMarkup(JsonSerializable): """ This object represents a custom keyboard with reply options (see Introduction to bots for details and examples). @@ -3095,7 +3102,7 @@ def can_manage_voice_chats(self): return self.can_manage_video_chats - +# noinspection PyUnresolvedReferences class ChatMemberOwner(ChatMember): """ Represents a chat member that owns the chat and has all administrator privileges. @@ -3120,6 +3127,7 @@ class ChatMemberOwner(ChatMember): pass +# noinspection PyUnresolvedReferences class ChatMemberAdministrator(ChatMember): """ Represents a chat member that has some additional privileges. @@ -3195,6 +3203,7 @@ class ChatMemberAdministrator(ChatMember): pass +# noinspection PyUnresolvedReferences class ChatMemberMember(ChatMember): """ Represents a chat member that has no additional privileges or restrictions. @@ -3213,6 +3222,7 @@ class ChatMemberMember(ChatMember): pass +# noinspection PyUnresolvedReferences class ChatMemberRestricted(ChatMember): """ Represents a chat member that is under certain restrictions in the chat. Supergroups only. @@ -3281,6 +3291,7 @@ class ChatMemberRestricted(ChatMember): pass +# noinspection PyUnresolvedReferences class ChatMemberLeft(ChatMember): """ Represents a chat member that isn't currently a member of the chat, but may join it themselves. @@ -3299,6 +3310,7 @@ class ChatMemberLeft(ChatMember): pass +# noinspection PyUnresolvedReferences class ChatMemberBanned(ChatMember): """ Represents a chat member that was banned in the chat and can't return to the chat or view chat messages. @@ -3478,7 +3490,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) - def __init__(self, command, description): + def __init__(self, command, description, **kwargs): self.command: str = command self.description: str = description @@ -3547,6 +3559,7 @@ def to_json(self): return json.dumps(json_dict) +# noinspection PyUnresolvedReferences class BotCommandScopeDefault(BotCommandScope): """ Represents the default scope of bot commands. Default commands are used if no commands with a narrower scope are specified for the user. @@ -3567,6 +3580,7 @@ def __init__(self): super(BotCommandScopeDefault, self).__init__(type='default') +# noinspection PyUnresolvedReferences class BotCommandScopeAllPrivateChats(BotCommandScope): """ Represents the scope of bot commands, covering all private chats. @@ -3586,6 +3600,7 @@ def __init__(self): super(BotCommandScopeAllPrivateChats, self).__init__(type='all_private_chats') +# noinspection PyUnresolvedReferences class BotCommandScopeAllGroupChats(BotCommandScope): """ Represents the scope of bot commands, covering all group and supergroup chats. @@ -3605,6 +3620,7 @@ def __init__(self): super(BotCommandScopeAllGroupChats, self).__init__(type='all_group_chats') +# noinspection PyUnresolvedReferences class BotCommandScopeAllChatAdministrators(BotCommandScope): """ Represents the scope of bot commands, covering all group and supergroup chat administrators. @@ -3624,6 +3640,7 @@ def __init__(self): super(BotCommandScopeAllChatAdministrators, self).__init__(type='all_chat_administrators') +# noinspection PyUnresolvedReferences class BotCommandScopeChat(BotCommandScope): """ Represents the scope of bot commands, covering a specific chat. @@ -3644,6 +3661,7 @@ def __init__(self, chat_id=None): super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) +# noinspection PyUnresolvedReferences class BotCommandScopeChatAdministrators(BotCommandScope): """ Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat. @@ -3664,6 +3682,7 @@ def __init__(self, chat_id=None): super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) +# noinspection PyUnresolvedReferences class BotCommandScopeChatMember(BotCommandScope): """ Represents the scope of bot commands, covering a specific member of a group or supergroup chat. @@ -4206,7 +4225,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, inline_message_id=None): + def __init__(self, inline_message_id=None, **kwargs): self.inline_message_id = inline_message_id def to_dict(self): @@ -4216,6 +4235,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences class InlineQueryResultArticle(InlineQueryResultBase): """ Represents a link to an article or web page. @@ -4300,6 +4320,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4379,6 +4400,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4471,6 +4493,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4563,6 +4586,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4654,6 +4678,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4718,6 +4743,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4775,6 +4801,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4871,6 +4898,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -4975,6 +5003,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -5083,6 +5112,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences 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. @@ -5170,6 +5200,7 @@ def to_dict(self): return json_dict +# noinspection PyUnresolvedReferences class InlineQueryResultGame(InlineQueryResultBase): """ Represents a Game. @@ -5238,6 +5269,7 @@ def to_json(self): return json.dumps(json_dict) +# noinspection PyUnresolvedReferences 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. @@ -5296,6 +5328,7 @@ def __init__(self, id, photo_file_id, title=None, description=None, self.payload_dic['photo_file_id'] = photo_file_id +# noinspection PyUnresolvedReferences 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. @@ -5350,6 +5383,7 @@ def __init__(self, id, gif_file_id, title=None, description=None, self.payload_dic['gif_file_id'] = gif_file_id +# noinspection PyUnresolvedReferences 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. @@ -5404,6 +5438,7 @@ def __init__(self, id, mpeg4_file_id, title=None, description=None, self.payload_dic['mpeg4_file_id'] = mpeg4_file_id +# noinspection PyUnresolvedReferences 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. @@ -5438,6 +5473,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 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. @@ -5496,6 +5532,7 @@ def __init__(self, id, document_file_id, title, description=None, self.payload_dic['document_file_id'] = document_file_id +# noinspection PyUnresolvedReferences 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. @@ -5555,6 +5592,7 @@ def __init__(self, id, video_file_id, title, description=None, self.payload_dic['video_file_id'] = video_file_id +# noinspection PyUnresolvedReferences 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. @@ -5608,6 +5646,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 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. @@ -5975,6 +6014,7 @@ def __init__(self, name=None, phone_number=None, email=None, shipping_address=No self.shipping_address: ShippingAddress = shipping_address +# noinspection PyUnresolvedReferences class ShippingOption(JsonSerializable): """ This object represents one shipping option. @@ -6302,7 +6342,7 @@ def de_json(cls, json_string): def __init__(self, file_id, file_unique_id, type, width, height, is_animated, is_video, thumbnail=None, emoji=None, set_name=None, mask_position=None, file_size=None, - premium_animation=None, custom_emoji_id=None, needs_repainting=None ,**kwargs): + premium_animation=None, custom_emoji_id=None, needs_repainting=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.type: str = type @@ -7117,6 +7157,7 @@ def de_json(cls, json_string): def __init__(self, start_date, **kwargs): self.start_date: int = start_date + class VoiceChatScheduled(VideoChatScheduled): """ Deprecated, use :class:`VideoChatScheduled` instead. @@ -7147,6 +7188,7 @@ def de_json(cls, json_string): def __init__(self, duration, **kwargs): self.duration: int = duration + class VoiceChatEnded(VideoChatEnded): """ Deprecated, use :class:`VideoChatEnded` instead. @@ -7180,6 +7222,7 @@ def de_json(cls, json_string): def __init__(self, users=None, **kwargs): self.users: List[User] = users + class VoiceChatParticipantsInvited(VideoChatParticipantsInvited): """ Deprecated, use :class:`VideoChatParticipantsInvited` instead. @@ -7259,7 +7302,7 @@ class MenuButtonCommands(MenuButton): :rtype: :class:`telebot.types.MenuButtonCommands` """ - def __init__(self, type): + def __init__(self, type, **kwargs): self.type = type def to_dict(self): @@ -7268,6 +7311,7 @@ def to_dict(self): def to_json(self): return json.dumps(self.to_dict()) + class MenuButtonWebApp(MenuButton): """ Represents a menu button, which launches a Web App. @@ -7288,7 +7332,7 @@ class MenuButtonWebApp(MenuButton): :rtype: :class:`telebot.types.MenuButtonWebApp` """ - def __init__(self, type, text, web_app): + def __init__(self, type, text, web_app, **kwargs): self.type: str = type self.text: str = text self.web_app: WebAppInfo = web_app @@ -7298,6 +7342,7 @@ def to_dict(self): def to_json(self): return json.dumps(self.to_dict()) + class MenuButtonDefault(MenuButton): """ @@ -7311,7 +7356,7 @@ class MenuButtonDefault(MenuButton): :return: Instance of the class :rtype: :class:`telebot.types.MenuButtonDefault` """ - def __init__(self, type): + def __init__(self, type, **kwargs): self.type: str = type def to_dict(self): @@ -7393,7 +7438,7 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, can_post_messages: bool=None, can_edit_messages: bool=None, can_pin_messages: bool=None, can_manage_topics: bool=None, can_post_stories: bool=None, can_edit_stories: bool=None, - can_delete_stories: bool=None + can_delete_stories: bool=None, **kwargs ) -> None: self.is_anonymous: bool = is_anonymous @@ -7485,7 +7530,8 @@ class InputFile: def __init__(self, file) -> None: self._file, self.file_name = self._resolve_file(file) - def _resolve_file(self, file): + @staticmethod + def _resolve_file(file): if isinstance(file, str): _file = open(file, 'rb') return _file, os.path.basename(_file.name) @@ -7497,7 +7543,6 @@ def _resolve_file(self, file): else: raise TypeError("File must be a string or a file-like object(pathlib.Path, io.IOBase).") - @property def file(self): """ @@ -7530,7 +7575,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None) -> None: + def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None, **kwargs) -> None: self.name: str = name self.icon_color: int = icon_color self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id @@ -7584,7 +7629,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, name: Optional[str]=None, icon_custom_emoji_id: Optional[str]=None) -> None: + def __init__(self, name: Optional[str]=None, icon_custom_emoji_id: Optional[str]=None, **kwargs) -> None: self.name: Optional[str] = name self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id @@ -7649,7 +7694,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, message_thread_id: int, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None) -> None: + def __init__(self, message_thread_id: int, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None, + **kwargs) -> None: self.message_thread_id: int = message_thread_id self.name: str = name self.icon_color: int = icon_color @@ -7681,7 +7727,8 @@ def de_json(cls, json_string): return cls(**obj) - def __init__(self, from_request: Optional[bool]=None, web_app_name: Optional[str]=None, from_attachment_menu: Optional[bool]=None) -> None: + def __init__(self, from_request: Optional[bool]=None, web_app_name: Optional[str]=None, from_attachment_menu: Optional[bool]=None, + **kwargs) -> None: self.web_app_name: str = web_app_name self.from_request: bool = from_request self.from_attachment_menu: bool = from_attachment_menu @@ -7714,7 +7761,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, request_id: int, user_id: int) -> None: + def __init__(self, request_id: int, user_id: int, **kwargs) -> None: self.request_id: int = request_id self.user_id: int = user_id @@ -7746,7 +7793,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, request_id: int, chat_id: int) -> None: + def __init__(self, request_id: int, chat_id: int, **kwargs) -> None: self.request_id: int = request_id self.chat_id: int = chat_id @@ -7770,7 +7817,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, description: str) -> None: + def __init__(self, description: str, **kwargs) -> None: self.description: str = description @@ -7793,7 +7840,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, short_description: str) -> None: + def __init__(self, short_description: str, **kwargs) -> None: self.short_description: str = short_description @@ -7895,7 +7942,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, query=None, allow_user_chats=None, allow_bot_chats=None, allow_group_chats=None, - allow_channel_chats=None): + allow_channel_chats=None, **kwargs): self.query: str = query self.allow_user_chats: bool = allow_user_chats self.allow_bot_chats: bool = allow_bot_chats @@ -7942,7 +7989,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, name: str): + def __init__(self, name: str, **kwargs): self.name: str = name @@ -8007,7 +8054,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self) -> None: + def __init__(self, **kwargs) -> None: pass @@ -8049,8 +8096,9 @@ def to_dict(self) -> dict: def to_json(self) -> str: return json.dumps(self.to_dict()) - + +# noinspection PyUnresolvedReferences class ReactionTypeEmoji(ReactionType): """ This object represents an emoji reaction type. @@ -8067,16 +8115,17 @@ class ReactionTypeEmoji(ReactionType): :rtype: :class:`ReactionTypeEmoji` """ - def __init__(self, emoji: str) -> None: + def __init__(self, emoji: str, **kwargs) -> None: super().__init__('emoji') self.emoji: str = emoji def to_dict(self) -> dict: json_dict = super().to_dict() json_dict['emoji'] = self.emoji - return json_dict + return json_dict +# noinspection PyUnresolvedReferences class ReactionTypeCustomEmoji(ReactionType): """ This object represents a custom emoji reaction type. @@ -8093,7 +8142,7 @@ class ReactionTypeCustomEmoji(ReactionType): :rtype: :class:`ReactionTypeCustomEmoji` """ - def __init__(self, custom_emoji: str) -> None: + def __init__(self, custom_emoji: str, **kwargs) -> None: super().__init__('custom_emoji') self.custom_emoji: str = custom_emoji @@ -8150,7 +8199,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, chat: Chat, message_id: int, date: int, old_reaction: List[ReactionType], new_reaction: List[ReactionType], - user: Optional[User]=None, actor_chat: Optional[Chat]=None) -> None: + user: Optional[User]=None, actor_chat: Optional[Chat]=None, **kwargs) -> None: self.chat: Chat = chat self.message_id: int = message_id self.user: Optional[User] = user @@ -8192,7 +8241,7 @@ def de_json(cls, json_string): obj['reactions'] = [ReactionCount.de_json(reaction) for reaction in obj['reactions']] return cls(**obj) - def __init__(self, chat: Chat, message_id: int, date: int, reactions: List[ReactionCount]) -> None: + def __init__(self, chat: Chat, message_id: int, date: int, reactions: List[ReactionCount], **kwargs) -> None: self.chat: Chat = chat self.message_id: int = message_id self.date: int = date @@ -8223,7 +8272,7 @@ def de_json(cls, json_string): obj['type'] = ReactionType.de_json(obj['type']) return cls(**obj) - def __init__(self, type: ReactionType, total_count: int) -> None: + def __init__(self, type: ReactionType, total_count: int, **kwargs) -> None: self.type: ReactionType = type self.total_count: int = total_count @@ -8365,7 +8414,7 @@ def __init__( dice: Optional[Dice]=None, game: Optional[Game]=None, giveaway: Optional[Giveaway]=None, giveaway_winners: Optional[GiveawayWinners]=None, invoice: Optional[Invoice]=None, location: Optional[Location]=None, poll: Optional[Poll]=None, - venue: Optional[Venue]=None) -> None: + venue: Optional[Venue]=None, **kwargs) -> None: self.origin: MessageOrigin = origin self.chat: Optional[Chat] = chat self.message_id: Optional[int] = message_id @@ -8391,6 +8440,7 @@ def __init__( self.venue: Optional[Venue] = venue +# noinspection PyUnresolvedReferences class MessageOrigin(JsonDeserializable): """ This object describes the origin of a message. @@ -8540,7 +8590,7 @@ def de_json(cls, json_string): def __init__(self, is_disabled: Optional[bool] = None, url: Optional[str] = None, prefer_small_media: Optional[bool] = None, prefer_large_media: Optional[bool] = None, - show_above_text: Optional[bool] = None) -> None: + show_above_text: Optional[bool] = None, **kwargs) -> None: self.is_disabled: Optional[bool] = is_disabled self.url: Optional[str] = url self.prefer_small_media: Optional[bool] = prefer_small_media @@ -8611,7 +8661,7 @@ def de_json(cls, json_string): def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: int, only_new_members: Optional[bool] = None, has_public_winners: Optional[bool] = None, prize_description: Optional[str] = None, country_codes: Optional[List[str]] = None, - premium_subscription_month_count: Optional[int] = None) -> None: + premium_subscription_month_count: Optional[int] = None, **kwargs) -> None: self.chats: List[Chat] = chats self.winners_selection_date: int = winners_selection_date self.winner_count: int = winner_count @@ -8678,7 +8728,7 @@ def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: winners: List[User], additional_chat_count: Optional[int] = None, premium_subscription_month_count: Optional[int] = None, unclaimed_prize_count: Optional[int] = None, only_new_members: Optional[bool] = None, was_refunded: Optional[bool] = None, - prize_description: Optional[str] = None) -> None: + prize_description: Optional[str] = None, **kwargs) -> None: self.chat: Chat = chat self.giveaway_message_id: int = giveaway_message_id self.winners_selection_date: int = winners_selection_date @@ -8721,7 +8771,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, winner_count: int, unclaimed_prize_count: Optional[int] = None, - giveaway_message: Optional[Message] = None) -> None: + giveaway_message: Optional[Message] = None, **kwargs) -> None: self.winner_count: int = winner_count self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count self.giveaway_message: Optional[Message] = giveaway_message @@ -8766,7 +8816,7 @@ def de_json(cls, json_string): def __init__(self, text: str, position: int, entities: Optional[List[MessageEntity]] = None, - is_manual: Optional[bool] = None) -> None: + is_manual: Optional[bool] = None, **kwargs) -> None: self.text: str = text self.entities: Optional[List[MessageEntity]] = entities self.position: Optional[int] = position @@ -8816,7 +8866,7 @@ def de_json(cls, json_string): def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, allow_sending_without_reply: Optional[bool] = None, quote: Optional[str] = None, quote_parse_mode: Optional[str] = None, quote_entities: Optional[List[MessageEntity]] = None, - quote_position: Optional[int] = None) -> None: + quote_position: Optional[int] = None, **kwargs) -> None: self.message_id: int = message_id self.chat_id: Optional[Union[int, str]] = chat_id self.allow_sending_without_reply: Optional[bool] = allow_sending_without_reply @@ -8876,7 +8926,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, request_id, user_ids): + def __init__(self, request_id, user_ids, **kwargs): self.request_id = request_id self.user_ids = user_ids @@ -8906,7 +8956,7 @@ def de_json(cls, json_string): obj['boost'] = ChatBoost.de_json(obj['boost']) return cls(**obj) - def __init__(self, chat, boost): + def __init__(self, chat, boost, **kwargs): self.chat = chat self.boost = boost @@ -8942,7 +8992,7 @@ def de_json(cls, json_string): obj['source'] = ChatBoostSource.de_json(obj['source']) return cls(**obj) - def __init__(self, chat, boost_id, remove_date, source): + def __init__(self, chat, boost_id, remove_date, source, **kwargs): self.chat = chat self.boost_id = boost_id self.remove_date = remove_date @@ -8969,10 +9019,11 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, source): + def __init__(self, source, **kwargs): self.source = source - + +# noinspection PyUnresolvedReferences class ChatBoostSourcePremium(ChatBoostSource): """ The boost was obtained by subscribing to Telegram Premium or by gifting a Telegram Premium subscription to another user. @@ -9000,8 +9051,9 @@ def de_json(cls, json_string): def __init__(self, user): super().__init__('premium') self.user = user - - + + +# noinspection PyUnresolvedReferences class ChatBoostSourceGiftCode(ChatBoostSource): """ The boost was obtained by the creation of Telegram Premium gift codes to boost a chat. @@ -9029,8 +9081,9 @@ def de_json(cls, json_string): def __init__(self, user): super().__init__('gift_code') self.user = user - - + + +# noinspection PyUnresolvedReferences class ChatBoostSourceGiveaway(ChatBoostSource): """ The boost was obtained by the creation of a Telegram Premium giveaway. @@ -9098,7 +9151,7 @@ def de_json(cls, json_string): obj['source'] = ChatBoostSource.de_json(obj['source']) return cls(**obj) - def __init__(self, boost_id, add_date, expiration_date, source): + def __init__(self, boost_id, add_date, expiration_date, source, **kwargs): self.boost_id = boost_id self.add_date = add_date self.expiration_date = expiration_date @@ -9126,7 +9179,7 @@ def de_json(cls, json_string): obj['boosts'] = [ChatBoost.de_json(boost) for boost in obj['boosts']] return cls(**obj) - def __init__(self, boosts): + def __init__(self, boosts, **kwargs): self.boosts: ChatBoost = boosts @@ -9157,7 +9210,7 @@ def de_json(cls, json_string): obj['chat'] = Chat.de_json(obj['chat']) return cls(**obj) - def __init__(self, chat, message_id, date): + def __init__(self, chat, message_id, date, **kwargs): self.chat = chat self.message_id = message_id self.date = date From e2f53118630320c2f4167fd315e263a2bb50eeb1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 23:17:20 +0300 Subject: [PATCH 1431/1808] Tune apihelper call parameters All non-necessary parameters of apihelper calls are used with named parameters to avoid call errors. --- telebot/__init__.py | 827 +++++++++++++++++++++++++++----------------- 1 file changed, 512 insertions(+), 315 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3cbc6d7b1..13fdda5a2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -440,8 +440,10 @@ def set_webhook(self, url: Optional[str]=None, certificate: Optional[Union[str, :rtype: :obj:`bool` if the request was successful. """ - return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, - drop_pending_updates, timeout, secret_token) + return apihelper.set_webhook( + self.token, url = url, certificate = certificate, max_connections = max_connections, + allowed_updates = allowed_updates, ip_address = ip_address, drop_pending_updates = drop_pending_updates, + timeout = timeout, secret_token = secret_token) def run_webhooks(self, @@ -566,7 +568,8 @@ def delete_webhook(self, drop_pending_updates: Optional[bool]=None, timeout: Opt :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.delete_webhook(self.token, drop_pending_updates, timeout) + return apihelper.delete_webhook( + self.token, drop_pending_updates=drop_pending_updates, timeout=timeout) def get_webhook_info(self, timeout: Optional[int]=None) -> types.WebhookInfo: @@ -582,8 +585,9 @@ def get_webhook_info(self, timeout: Optional[int]=None) -> types.WebhookInfo: :return: On success, returns a WebhookInfo object. :rtype: :class:`telebot.types.WebhookInfo` """ - result = apihelper.get_webhook_info(self.token, timeout) - return types.WebhookInfo.de_json(result) + return types.WebhookInfo.de_json( + apihelper.get_webhook_info(self.token, timeout=timeout) + ) def remove_webhook(self) -> bool: @@ -626,7 +630,9 @@ def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, :return: An Array of Update objects is returned. :rtype: :obj:`list` of :class:`telebot.types.Update` """ - json_updates = apihelper.get_updates(self.token, offset, limit, timeout, allowed_updates, long_polling_timeout) + json_updates = apihelper.get_updates( + self.token, offset=offset, limit=limit, timeout=timeout, allowed_updates=allowed_updates, + long_polling_timeout=long_polling_timeout) return [types.Update.de_json(ju) for ju in json_updates] def __skip_updates(self): @@ -1071,7 +1077,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F so unwanted updates may be received for a short period of time. :type allowed_updates: :obj:`list` of :obj:`str` - :param none_stop: Deprecated, use non_stop. Old typo, kept for backward compatibility. + :param none_stop: deprecated. :type none_stop: :obj:`bool` :param restart_on_change: Restart a file on file(s) change. Defaults to False @@ -1278,8 +1284,9 @@ def get_me(self) -> types.User: Telegram documentation: https://core.telegram.org/bots/api#getme """ - result = apihelper.get_me(self.token) - return types.User.de_json(result) + return types.User.de_json( + apihelper.get_me(self.token) + ) def get_file(self, file_id: Optional[str]) -> types.File: @@ -1297,7 +1304,9 @@ def get_file(self, file_id: Optional[str]) -> types.File: :return: :class:`telebot.types.File` """ - return types.File.de_json(apihelper.get_file(self.token, file_id)) + return types.File.de_json( + apihelper.get_file(self.token, file_id) + ) def get_file_url(self, file_id: Optional[str]) -> str: @@ -1378,7 +1387,8 @@ def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reacti :return: :obj:`bool` """ - return apihelper.set_message_reaction(self.token, chat_id, message_id, reaction, is_big) + return apihelper.set_message_reaction( + self.token, chat_id, message_id, reaction = reaction, is_big = is_big) def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, @@ -1402,8 +1412,9 @@ def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, :rtype: :class:`telebot.types.UserProfilePhotos` """ - result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit) - return types.UserProfilePhotos.de_json(result) + return types.UserProfilePhotos.de_json( + apihelper.get_user_profile_photos(self.token, user_id, offset=offset, limit=limit) + ) def get_chat(self, chat_id: Union[int, str]) -> types.Chat: @@ -1419,8 +1430,9 @@ def get_chat(self, chat_id: Union[int, str]) -> types.Chat: :return: Chat information :rtype: :class:`telebot.types.Chat` """ - result = apihelper.get_chat(self.token, chat_id) - return types.Chat.de_json(result) + return types.Chat.de_json( + apihelper.get_chat(self.token, chat_id) + ) def leave_chat(self, chat_id: Union[int, str]) -> bool: @@ -1434,8 +1446,7 @@ def leave_chat(self, chat_id: Union[int, str]) -> bool: :return: :obj:`bool` """ - result = apihelper.leave_chat(self.token, chat_id) - return result + return apihelper.leave_chat(self.token, chat_id) def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMember]: @@ -1473,9 +1484,9 @@ def get_chat_members_count(self, chat_id: Union[int, str]) -> int: :return: Number of members in the chat. :rtype: :obj:`int` """ - result = apihelper.get_chat_member_count(self.token, chat_id) - return result - + return apihelper.get_chat_member_count(self.token, chat_id) + + def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ Use this method to get the number of members in a chat. @@ -1488,8 +1499,8 @@ def get_chat_member_count(self, chat_id: Union[int, str]) -> int: :return: Number of members in the chat. :rtype: :obj:`int` """ - result = apihelper.get_chat_member_count(self.token, chat_id) - return result + return apihelper.get_chat_member_count(self.token, chat_id) + def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) -> types.StickerSet: """ @@ -1508,8 +1519,8 @@ def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) :return: StickerSet object :rtype: :class:`telebot.types.StickerSet` """ - result = apihelper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) - return result + return apihelper.set_chat_sticker_set(self.token, chat_id, sticker_set_name) + def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: """ @@ -1525,8 +1536,8 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: :return: Returns True on success. :rtype: :obj:`bool` """ - result = apihelper.delete_chat_sticker_set(self.token, chat_id) - return result + return apihelper.delete_chat_sticker_set(self.token, chat_id) + def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ @@ -1543,8 +1554,10 @@ def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatM :return: Returns ChatMember object on success. :rtype: :class:`telebot.types.ChatMember` """ - result = apihelper.get_chat_member(self.token, chat_id, user_id) - return types.ChatMember.de_json(result) + return types.ChatMember.de_json( + apihelper.get_chat_member(self.token, chat_id, user_id) + ) + def send_message( self, chat_id: Union[int, str], text: str, @@ -1581,7 +1594,7 @@ def send_message( :param entities: List of special entities that appear in message text, which can be specified instead of parse_mode :type entities: Array of :class:`telebot.types.MessageEntity` - :param disable_web_page_preview: deprecated. Disables link previews for links in this message + :param disable_web_page_preview: deprecated. :type disable_web_page_preview: :obj:`bool` :param disable_notification: Sends the message silently. Users will receive a notification with no sound. @@ -1590,10 +1603,10 @@ def send_message( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :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. @@ -1620,15 +1633,12 @@ def send_message( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -1641,11 +1651,9 @@ def send_message( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if disable_web_page_preview is not None: - # show a deprecation warning logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") if link_preview_options: - # show a conflict warning logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") else: # create a LinkPreviewOptions object @@ -1659,8 +1667,10 @@ def send_message( return types.Message.de_json( apihelper.send_message( self.token, chat_id, text, - reply_markup, parse_mode, disable_notification, timeout, - entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, link_preview_options=link_preview_options)) + reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, + timeout=timeout, entities=entities, protect_content=protect_content, message_thread_id=message_thread_id, + reply_parameters=reply_parameters, link_preview_options=link_preview_options)) + def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -1701,8 +1711,9 @@ def forward_message( protect_content = self.protect_content if (protect_content is None) else protect_content return types.Message.de_json( - apihelper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content, - message_thread_id)) + apihelper.forward_message( + self.token, chat_id, from_chat_id, message_id, disable_notification=disable_notification, + timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id)) def copy_message( @@ -1716,6 +1727,7 @@ def copy_message( protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.MessageID: @@ -1747,10 +1759,10 @@ def copy_message( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :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 @@ -1775,15 +1787,12 @@ def copy_message( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -1796,9 +1805,11 @@ def copy_message( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.MessageID.de_json( - apihelper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, - disable_notification, reply_markup, - timeout, protect_content, message_thread_id, reply_parameters)) + 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)) + def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -1827,7 +1838,8 @@ def delete_message(self, chat_id: Union[int, str], message_id: int, :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.delete_message(self.token, chat_id, message_id, timeout) + return apihelper.delete_message(self.token, chat_id, message_id, timeout=timeout) + def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ @@ -1848,6 +1860,7 @@ def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ return apihelper.delete_messages(self.token, chat_id, message_ids) + def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: @@ -1879,8 +1892,12 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - return [types.MessageID.de_json(message_id) for message_id in - apihelper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id)] + result = apihelper.forward_messages( + self.token, chat_id, from_chat_id, message_ids, + disable_notification=disable_notification, message_thread_id=message_thread_id, + protect_content=protect_content) + return [types.MessageID.de_json(message_id) for message_id in result] + def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, @@ -1915,9 +1932,10 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], disable_notification = self.disable_notification if disable_notification is None else disable_notification protect_content = self.protect_content if protect_content is None else protect_content - return [types.MessageID.de_json(message_id) for message_id in - apihelper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, - protect_content, message_thread_id, remove_caption)] + result = apihelper.copy_messages( + self.token, chat_id, from_chat_id, message_ids, disable_notification=disable_notification, + message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption) + return [types.MessageID.de_json(message_id) for message_id in result] def send_dice( @@ -1950,10 +1968,10 @@ def send_dice( :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` or :class:`telebot.types.ForceReply` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -1975,15 +1993,12 @@ def send_dice( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -1997,8 +2012,9 @@ def send_dice( return types.Message.de_json( apihelper.send_dice( - self.token, chat_id, emoji, disable_notification, - reply_markup, timeout, protect_content, message_thread_id, reply_parameters) + self.token, chat_id, emoji=emoji, disable_notification=disable_notification, + reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, + message_thread_id=message_thread_id, reply_parameters=reply_parameters) ) @@ -2043,10 +2059,10 @@ def send_photo( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :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 @@ -2074,15 +2090,12 @@ def send_photo( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2096,11 +2109,13 @@ def send_photo( return types.Message.de_json( apihelper.send_photo( - self.token, chat_id, photo, caption, reply_markup, - parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, has_spoiler, reply_parameters)) + self.token, chat_id, photo, caption=caption, reply_markup=reply_markup, + parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, + caption_entities=caption_entities, protect_content=protect_content, + message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters) + ) + - # TODO: Rewrite this method like in API. def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, @@ -2156,10 +2171,10 @@ def send_audio( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -2180,7 +2195,7 @@ def send_audio( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` - :param thumb: Deprecated. Use thumbnail instead + :param thumb: deprecated. :type thumb: :obj:`str` or :class:`telebot.types.InputFile` :param reply_parameters: Reply parameters. @@ -2194,15 +2209,12 @@ def send_audio( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2215,16 +2227,18 @@ def send_audio( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if thumb is not None and thumbnail is None: - thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + thumbnail = thumb return types.Message.de_json( apihelper.send_audio( - self.token, chat_id, audio, caption, duration, performer, title, - reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, protect_content, message_thread_id, reply_parameters)) + self.token, chat_id, audio, caption=caption, duration=duration, performer=performer, title=title, + reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, + timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, + message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) + - # TODO: Rewrite this method like in API. def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, @@ -2269,10 +2283,10 @@ def send_voice( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -2297,15 +2311,12 @@ def send_voice( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2319,11 +2330,13 @@ def send_voice( return types.Message.de_json( apihelper.send_voice( - self.token, chat_id, voice, caption, duration, reply_markup, - parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, reply_parameters)) + self.token, chat_id, voice, caption=caption, duration=duration, reply_markup=reply_markup, + parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, + caption_entities=caption_entities, protect_content=protect_content, + message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) + - # TODO: Rewrite this method like in API. def send_document( self, chat_id: Union[int, str], document: Union[Any, str], caption: Optional[str]=None, @@ -2367,10 +2380,10 @@ def send_document( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -2397,7 +2410,7 @@ def send_document( :param message_thread_id: The thread to which the message will be sent :type message_thread_id: :obj:`int` - :param thumb: Deprecated. Use thumbnail instead + :param thumb: deprecated. :type thumb: :obj:`str` or :class:`telebot.types.InputFile` :param reply_parameters: Reply parameters. @@ -2411,15 +2424,12 @@ def send_document( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2432,24 +2442,23 @@ def send_document( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if data and (not document): - # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "document" instead.') document = data if thumb is not None and thumbnail is None: - thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + thumbnail = thumb return types.Message.de_json( apihelper.send_data( self.token, chat_id, document, 'document', - reply_parameters=reply_parameters, reply_markup = reply_markup, parse_mode = parse_mode, - disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, - caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, - visible_file_name = visible_file_name, protect_content = protect_content, message_thread_id = message_thread_id)) + reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, + timeout=timeout, caption=caption, thumbnail=thumbnail, caption_entities=caption_entities, + disable_content_type_detection=disable_content_type_detection, visible_file_name=visible_file_name, + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) - # TODO: Rewrite this method like in API. def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], @@ -2484,10 +2493,10 @@ def send_sticker( :param disable_notification: to disable the notification :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -2515,15 +2524,12 @@ def send_sticker( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2536,16 +2542,17 @@ def send_sticker( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if data and (not sticker): - # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data return types.Message.de_json( apihelper.send_data( - self.token, chat_id, sticker, 'sticker', reply_markup=reply_markup, - disable_notification=disable_notification, timeout=timeout, + self.token, chat_id, sticker, 'sticker', + reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, - reply_parameters=reply_parameters)) + reply_parameters=reply_parameters) + ) + def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -2609,10 +2616,10 @@ def send_video( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :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 @@ -2632,7 +2639,7 @@ def send_video( :param has_spoiler: Pass True, if the video should be sent as a spoiler :type has_spoiler: :obj:`bool` - :param thumb: Deprecated. Use thumbnail instead + :param thumb: deprecated. :type thumb: :obj:`str` or :class:`telebot.types.InputFile` :param reply_parameters: Reply parameters @@ -2646,15 +2653,12 @@ def send_video( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2667,19 +2671,23 @@ def send_video( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if data and (not video): - # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "video" instead.') video = data if thumb is not None and thumbnail is None: - thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + thumbnail = thumb return types.Message.de_json( apihelper.send_video( - self.token, chat_id, video, duration, caption, reply_markup, - parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, - caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters)) + self.token, chat_id, video, + duration=duration, caption=caption, reply_markup=reply_markup, parse_mode=parse_mode, + supports_streaming=supports_streaming, disable_notification=disable_notification, timeout=timeout, + thumbnail=thumbnail, height=height, width=width, caption_entities=caption_entities, + protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, + reply_parameters=reply_parameters) + ) + def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -2737,10 +2745,10 @@ def send_animation( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :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 @@ -2763,7 +2771,7 @@ def send_animation( :param has_spoiler: Pass True, if the animation should be sent as a spoiler :type has_spoiler: :obj:`bool` - :param thumb: Deprecated. Use thumbnail instead + :param thumb: deprecated. :type thumb: :obj:`str` or :class:`telebot.types.InputFile` :param reply_parameters: Reply parameters. @@ -2777,15 +2785,12 @@ def send_animation( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2798,15 +2803,19 @@ def send_animation( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if thumbnail is None and thumb is not None: - thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + thumbnail = thumb + return types.Message.de_json( apihelper.send_animation( - self.token, chat_id, animation, duration, caption, - reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, protect_content, width, height, message_thread_id, has_spoiler, reply_parameters)) + self.token, chat_id, animation, duration=duration, caption=caption, reply_markup=reply_markup, + parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, + thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, + width=width, height=height, message_thread_id=message_thread_id, reply_parameters=reply_parameters, + has_spoiler=has_spoiler) + ) + - # TODO: Rewrite this method like in API. def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], duration: Optional[int]=None, @@ -2848,10 +2857,10 @@ def send_video_note( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -2869,7 +2878,7 @@ def send_video_note( :param message_thread_id: Identifier of a message thread, in which the video note will be sent :type message_thread_id: :obj:`int` - :param thumb: Deprecated. Use thumbnail instead + :param thumb: deprecated. :type thumb: :obj:`str` or :class:`telebot.types.InputFile` :param reply_parameters: Reply parameters. @@ -2882,15 +2891,12 @@ def send_video_note( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2903,13 +2909,15 @@ def send_video_note( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if thumbnail is None and thumb is not None: - thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') + thumbnail = thumb return types.Message.de_json( apihelper.send_video_note( - self.token, chat_id, data, duration, length, reply_markup, - disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters)) + self.token, chat_id, data, duration=duration, length=length, reply_markup=reply_markup, + disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) def send_media_group( @@ -2942,10 +2950,10 @@ def send_media_group( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -2964,15 +2972,12 @@ def send_media_group( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -2985,11 +2990,11 @@ def send_media_group( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = apihelper.send_media_group( - self.token, chat_id, media, disable_notification, timeout, - protect_content, message_thread_id, reply_parameters) + self.token, chat_id, media, disable_notification=disable_notification, timeout=timeout, + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) return [types.Message.de_json(msg) for msg in result] - # TODO: Rewrite this method like in API. + def send_location( self, chat_id: Union[int, str], latitude: float, longitude: float, @@ -3030,10 +3035,10 @@ def send_location( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param timeout: Timeout in seconds for the request. @@ -3064,15 +3069,12 @@ def send_location( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -3086,10 +3088,12 @@ def send_location( return types.Message.de_json( apihelper.send_location( - self.token, chat_id, latitude, longitude, live_period, - reply_markup, disable_notification, timeout, - horizontal_accuracy, heading, proximity_alert_radius, - protect_content, message_thread_id, reply_parameters)) + self.token, chat_id, latitude, longitude, live_period=live_period, reply_markup=reply_markup, + disable_notification=disable_notification, timeout=timeout, horizontal_accuracy=horizontal_accuracy, + heading=heading, proximity_alert_radius=proximity_alert_radius, protect_content=protect_content, + message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) + def edit_message_live_location( self, latitude: float, longitude: float, @@ -3144,9 +3148,11 @@ def edit_message_live_location( """ return types.Message.de_json( apihelper.edit_message_live_location( - self.token, latitude, longitude, chat_id, message_id, - inline_message_id, reply_markup, timeout, - horizontal_accuracy, heading, proximity_alert_radius)) + 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) + ) + def stop_message_live_location( self, chat_id: Optional[Union[int, str]]=None, @@ -3181,9 +3187,11 @@ def stop_message_live_location( """ return types.Message.de_json( apihelper.stop_message_live_location( - self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) + self.token, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, + reply_markup=reply_markup, timeout=timeout) + ) + - # TODO: Rewrite this method like in API. def send_venue( self, chat_id: Union[int, str], latitude: Optional[float], longitude: Optional[float], @@ -3230,10 +3238,10 @@ def send_venue( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, @@ -3266,15 +3274,12 @@ def send_venue( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -3288,12 +3293,13 @@ def send_venue( return types.Message.de_json( apihelper.send_venue( - self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, - disable_notification, reply_markup, timeout, - google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters)) + self.token, chat_id, latitude, longitude, title, address, foursquare_id=foursquare_id, + foursquare_type=foursquare_type, disable_notification=disable_notification, reply_markup=reply_markup, + timeout=timeout, google_place_id=google_place_id, google_place_type=google_place_type, + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) - # TODO: Rewrite this method like in API. def send_contact( self, chat_id: Union[int, str], phone_number: str, first_name: str, last_name: Optional[str]=None, @@ -3328,10 +3334,10 @@ def send_contact( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: deprecated. If the message is a reply, ID of the original message + :param reply_to_message_id: deprecated. :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: deprecated. Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: deprecated. :type allow_sending_without_reply: :obj:`bool` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, @@ -3358,15 +3364,12 @@ def send_contact( protect_content = self.protect_content if (protect_content is None) else protect_content if allow_sending_without_reply is not None: - # show a deprecation warning logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") if reply_to_message_id: - # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") if reply_parameters: - # show a conflict warning logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") else: # create a ReplyParameters object @@ -3380,9 +3383,11 @@ def send_contact( return types.Message.de_json( apihelper.send_contact( - self.token, chat_id, phone_number, first_name, last_name, vcard, - disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters)) + self.token, chat_id, phone_number, first_name, last_name=last_name, vcard=vcard, + disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) + def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool: @@ -3414,7 +3419,9 @@ def send_chat_action( :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id) + return apihelper.send_chat_action( + self.token, chat_id, action, timeout=timeout, message_thread_id=message_thread_id) + @util.deprecated(deprecation_text="Use ban_chat_member instead") def kick_chat_member( @@ -3424,7 +3431,9 @@ def kick_chat_member( """ This function is deprecated. Use `ban_chat_member` instead. """ - return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + return apihelper.ban_chat_member( + self.token, chat_id, user_id, until_date=until_date, revoke_messages=revoke_messages) + def ban_chat_member( self, chat_id: Union[int, str], user_id: int, @@ -3457,7 +3466,9 @@ def ban_chat_member( :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) + return apihelper.ban_chat_member( + self.token, chat_id, user_id, until_date=until_date, revoke_messages=revoke_messages) + def unban_chat_member( self, chat_id: Union[int, str], user_id: int, @@ -3486,6 +3497,7 @@ def unban_chat_member( """ return apihelper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) + def restrict_chat_member( self, chat_id: Union[int, str], user_id: int, until_date: Optional[Union[int, datetime]]=None, @@ -3562,7 +3574,6 @@ def restrict_chat_member( :return: True on success :rtype: :obj:`bool` """ - if permissions is None: permissions = types.ChatPermissions( can_send_messages=can_send_messages, @@ -3575,8 +3586,11 @@ def restrict_chat_member( can_pin_messages=can_pin_messages ) logger.warning('The parameters "can_..." are deprecated, use "permissions" instead.') + return apihelper.restrict_chat_member( - self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) + self.token, chat_id, user_id, permissions, until_date=until_date, + use_independent_chat_permissions=use_independent_chat_permissions) + def promote_chat_member( self, chat_id: Union[int, str], user_id: int, @@ -3674,11 +3688,15 @@ def promote_chat_member( can_manage_video_chats = can_manage_voice_chats return apihelper.promote_chat_member( - self.token, chat_id, user_id, can_change_info, can_post_messages, - can_edit_messages, can_delete_messages, can_invite_users, - can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics, - can_post_stories, can_edit_stories, can_delete_stories) + self.token, chat_id, user_id, can_change_info=can_change_info, can_post_messages=can_post_messages, + can_edit_messages=can_edit_messages, can_delete_messages=can_delete_messages, + can_invite_users=can_invite_users, can_restrict_members=can_restrict_members, + can_pin_messages=can_pin_messages, can_promote_members=can_promote_members, + is_anonymous=is_anonymous, can_manage_chat=can_manage_chat, + can_manage_video_chats=can_manage_video_chats, can_manage_topics=can_manage_topics, + can_post_stories=can_post_stories, can_edit_stories=can_edit_stories, + can_delete_stories=can_delete_stories) + def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: @@ -3703,6 +3721,7 @@ def set_chat_administrator_custom_title( :rtype: :obj:`bool` """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) + def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ @@ -3726,6 +3745,7 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i """ return apihelper.ban_chat_sender_chat(self.token, chat_id, sender_chat_id) + def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ Use this method to unban a previously banned channel chat in a supergroup or channel. @@ -3746,6 +3766,7 @@ def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union """ return apihelper.unban_chat_sender_chat(self.token, chat_id, sender_chat_id) + def set_chat_permissions( self, chat_id: Union[int, str], permissions: types.ChatPermissions, use_independent_chat_permissions: Optional[bool]=None) -> bool: @@ -3772,7 +3793,9 @@ def set_chat_permissions( :return: True on success :rtype: :obj:`bool` """ - return apihelper.set_chat_permissions(self.token, chat_id, permissions, use_independent_chat_permissions) + return apihelper.set_chat_permissions( + self.token, chat_id, permissions, use_independent_chat_permissions=use_independent_chat_permissions) + def create_chat_invite_link( self, chat_id: Union[int, str], @@ -3811,6 +3834,7 @@ def create_chat_invite_link( apihelper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request) ) + def edit_chat_invite_link( self, chat_id: Union[int, str], invite_link: Optional[str] = None, @@ -3850,6 +3874,7 @@ def edit_chat_invite_link( apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request) ) + def revoke_chat_invite_link( self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: """ @@ -3873,6 +3898,7 @@ def revoke_chat_invite_link( apihelper.revoke_chat_invite_link(self.token, chat_id, invite_link) ) + def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: """ Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator @@ -3889,6 +3915,7 @@ def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: """ return apihelper.export_chat_invite_link(self.token, chat_id) + def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ Use this method to approve a chat join request. @@ -3909,6 +3936,7 @@ def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int """ return apihelper.approve_chat_join_request(self.token, chat_id, user_id) + def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ Use this method to decline a chat join request. @@ -3929,6 +3957,7 @@ def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int """ return apihelper.decline_chat_join_request(self.token, chat_id, user_id) + def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: """ Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. @@ -3950,6 +3979,7 @@ def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool: """ return apihelper.set_chat_photo(self.token, chat_id, photo) + def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ Use this method to delete a chat photo. Photos can't be changed for private chats. @@ -3967,6 +3997,7 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: :rtype: :obj:`bool` """ return apihelper.delete_chat_photo(self.token, chat_id) + def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> List[types.BotCommand]: @@ -3988,9 +4019,10 @@ def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, :return: List of BotCommand on success. :rtype: :obj:`list` of :class:`telebot.types.BotCommand` """ - result = apihelper.get_my_commands(self.token, scope, language_code) + result = apihelper.get_my_commands(self.token, scope=scope, language_code=language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=None): """ Use this method to change the bot's name. Returns True on success. @@ -4006,8 +4038,8 @@ def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=Non :return: True on success. """ + return apihelper.set_my_name(self.token, name=name, language_code=language_code) - return apihelper.set_my_name(self.token, name, language_code) def get_my_name(self, language_code: Optional[str]=None): """ @@ -4021,10 +4053,11 @@ def get_my_name(self, language_code: Optional[str]=None): :return: :class:`telebot.types.BotName` """ + return types.BotName.de_json( + apihelper.get_my_name(self.token, language_code=language_code) + ) + - result = apihelper.get_my_name(self.token, language_code) - return types.BotName.de_json(result) - def set_my_description(self, description: Optional[str]=None, language_code: Optional[str]=None): """ Use this method to change the bot's description, which is shown in @@ -4043,7 +4076,9 @@ def set_my_description(self, description: Optional[str]=None, language_code: Opt :return: True on success. """ - return apihelper.set_my_description(self.token, description, language_code) + return apihelper.set_my_description( + self.token, description=description, language_code=language_code) + def get_my_description(self, language_code: Optional[str]=None): """ @@ -4057,8 +4092,9 @@ def get_my_description(self, language_code: Optional[str]=None): :return: :class:`telebot.types.BotDescription` """ - - return types.BotDescription.de_json(apihelper.get_my_description(self.token, language_code)) + return types.BotDescription.de_json( + apihelper.get_my_description(self.token, language_code=language_code)) + def set_my_short_description(self, short_description:Optional[str]=None, language_code:Optional[str]=None): """ @@ -4077,8 +4113,9 @@ def set_my_short_description(self, short_description:Optional[str]=None, languag :return: True on success. """ + return apihelper.set_my_short_description( + self.token, short_description=short_description, language_code=language_code) - return apihelper.set_my_short_description(self.token, short_description, language_code) def get_my_short_description(self, language_code: Optional[str]=None): """ @@ -4092,11 +4129,11 @@ def get_my_short_description(self, language_code: Optional[str]=None): :return: :class:`telebot.types.BotShortDescription` """ - - return types.BotShortDescription.de_json(apihelper.get_my_short_description(self.token, language_code)) + return types.BotShortDescription.de_json( + apihelper.get_my_short_description(self.token, language_code=language_code)) + - def set_chat_menu_button(self, chat_id: Union[int, str]=None, - menu_button: types.MenuButton=None) -> bool: + def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: """ Use this method to change the bot's menu button in a private chat, or the default menu button. @@ -4114,7 +4151,8 @@ def set_chat_menu_button(self, chat_id: Union[int, str]=None, :return: True on success. :rtype: :obj:`bool` """ - return apihelper.set_chat_menu_button(self.token, chat_id, menu_button) + return apihelper.set_chat_menu_button(self.token, chat_id=chat_id, menu_button=menu_button) + def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButton: """ @@ -4131,7 +4169,9 @@ def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButto :return: types.MenuButton :rtype: :class:`telebot.types.MenuButton` """ - return types.MenuButton.de_json(apihelper.get_chat_menu_button(self.token, chat_id)) + return types.MenuButton.de_json( + apihelper.get_chat_menu_button(self.token, chat_id=chat_id)) + def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, for_channels: Optional[bool]=None) -> bool: @@ -4155,7 +4195,8 @@ def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRig :return: True on success. :rtype: :obj:`bool` """ - return apihelper.set_my_default_administrator_rights(self.token, rights, for_channels) + return apihelper.set_my_default_administrator_rights(self.token, rights=rights, for_channels=for_channels) + def get_my_default_administrator_rights(self, for_channels: Optional[bool]=None) -> types.ChatAdministratorRights: """ @@ -4170,7 +4211,10 @@ def get_my_default_administrator_rights(self, for_channels: Optional[bool]=None) :return: Returns ChatAdministratorRights on success. :rtype: :class:`telebot.types.ChatAdministratorRights` """ - return types.ChatAdministratorRights.de_json(apihelper.get_my_default_administrator_rights(self.token, for_channels)) + return types.ChatAdministratorRights.de_json( + apihelper.get_my_default_administrator_rights(self.token, for_channels=for_channels) + ) + def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, @@ -4195,7 +4239,8 @@ def set_my_commands(self, commands: List[types.BotCommand], :return: True on success. :rtype: :obj:`bool` """ - return apihelper.set_my_commands(self.token, commands, scope, language_code) + return apihelper.set_my_commands(self.token, commands, scope=scope, language_code=language_code) + def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: @@ -4218,7 +4263,8 @@ def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, :return: True on success. :rtype: :obj:`bool` """ - return apihelper.delete_my_commands(self.token, scope, language_code) + return apihelper.delete_my_commands(self.token, scope=scope, language_code=language_code) + def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: """ @@ -4242,6 +4288,7 @@ def set_chat_title(self, chat_id: Union[int, str], title: str) -> bool: """ return apihelper.set_chat_title(self.token, chat_id, title) + def set_chat_description(self, chat_id: Union[int, str], description: Optional[str]=None) -> bool: """ Use this method to change the description of a supergroup or a channel. @@ -4261,6 +4308,7 @@ def set_chat_description(self, chat_id: Union[int, str], description: Optional[s """ return apihelper.set_chat_description(self.token, chat_id, description) + def pin_chat_message( self, chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=False) -> bool: @@ -4287,7 +4335,8 @@ def pin_chat_message( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification - return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification) + return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification=disable_notification) + def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: """ @@ -4309,6 +4358,7 @@ def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int] """ return apihelper.unpin_chat_message(self.token, chat_id, message_id) + def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: """ Use this method to unpin a all pinned messages in a supergroup chat. @@ -4326,6 +4376,7 @@ def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: """ return apihelper.unpin_all_chat_messages(self.token, chat_id) + def edit_message_text( self, text: str, chat_id: Optional[Union[int, str]]=None, @@ -4389,12 +4440,15 @@ def edit_message_text( if link_preview_options and (link_preview_options.disable_web_page_preview is None): link_preview_options.disable_web_page_preview = self.disable_web_page_preview - result = apihelper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - entities, reply_markup, link_preview_options) + result = apihelper.edit_message_text( + self.token, text, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, + parse_mode=parse_mode, entities=entities, reply_markup=reply_markup, link_preview_options=link_preview_options) + if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) + def edit_message_media( self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, @@ -4425,11 +4479,15 @@ def edit_message_media( :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ - result = apihelper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) + result = apihelper.edit_message_media( + self.token, media, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, + reply_markup=reply_markup) + if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) + def edit_message_reply_markup( self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, @@ -4455,11 +4513,15 @@ def edit_message_reply_markup( :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ - result = apihelper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) + result = apihelper.edit_message_reply_markup( + self.token, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, + reply_markup=reply_markup) + if type(result) == bool: return result return types.Message.de_json(result) + def send_game( self, chat_id: Union[int, str], game_short_name: str, disable_notification: Optional[bool]=None, @@ -4532,11 +4594,13 @@ def send_game( if reply_parameters and (reply_parameters.allow_sending_without_reply is None): reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply - result = apihelper.send_game( - self.token, chat_id, game_short_name, disable_notification, - reply_markup, timeout, - protect_content, message_thread_id, reply_parameters) - return types.Message.de_json(result) + return types.Message.de_json( + apihelper.send_game( + self.token, chat_id, game_short_name, disable_notification=disable_notification, + reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, + message_thread_id=message_thread_id, reply_parameters=reply_parameters) + ) + def set_game_score( self, user_id: Union[int, str], score: int, @@ -4574,12 +4638,15 @@ def set_game_score( :return: On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. :rtype: :obj:`types.Message` or :obj:`bool` """ - result = apihelper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, - message_id, inline_message_id) + result = apihelper.set_game_score( + self.token, user_id, score, force=force, disable_edit_message=disable_edit_message, + chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id) + if type(result) == bool: return result return types.Message.de_json(result) + def get_game_high_scores( self, user_id: int, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, @@ -4609,10 +4676,11 @@ def get_game_high_scores( :return: On success, returns an Array of GameHighScore objects. :rtype: List[types.GameHighScore] """ - result = apihelper.get_game_high_scores(self.token, user_id, chat_id, message_id, inline_message_id) + result = apihelper.get_game_high_scores( + self.token, user_id, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id) return [types.GameHighScore.de_json(r) for r in result] - # TODO: rewrite this method like in API + def send_invoice( self, chat_id: Union[int, str], title: str, description: str, invoice_payload: str, provider_token: str, currency: str, @@ -4766,14 +4834,19 @@ def send_invoice( if reply_parameters and (reply_parameters.allow_sending_without_reply is None): reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply - result = apihelper.send_invoice( - self.token, chat_id, title, description, invoice_payload, provider_token, - currency, prices, start_parameter, photo_url, photo_size, photo_width, - photo_height, need_name, need_phone_number, need_email, need_shipping_address, - send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, - reply_markup, provider_data, timeout, - max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters) - return types.Message.de_json(result) + return types.Message.de_json( + apihelper.send_invoice( + self.token, chat_id, title, description, invoice_payload, provider_token, + currency, prices, start_parameter=start_parameter, photo_url=photo_url, + photo_size=photo_size, photo_width=photo_width, photo_height=photo_height, + need_name=need_name, need_phone_number=need_phone_number, need_email=need_email, + need_shipping_address=need_shipping_address, send_phone_number_to_provider=send_phone_number_to_provider, + send_email_to_provider=send_email_to_provider, is_flexible=is_flexible, + disable_notification=disable_notification, reply_markup=reply_markup, + provider_data=provider_data, timeout=timeout, protect_content=protect_content, + message_thread_id=message_thread_id, reply_parameters=reply_parameters, + max_tip_amount=max_tip_amount, suggested_tip_amounts=suggested_tip_amounts) + ) def create_invoice_link(self, title: str, description: str, payload:str, provider_token: str, @@ -4870,16 +4943,17 @@ def create_invoice_link(self, :return: Created invoice link as String on success. :rtype: :obj:`str` """ - result = apihelper.create_invoice_link( + return apihelper.create_invoice_link( self.token, title, description, payload, provider_token, - currency, prices, max_tip_amount, suggested_tip_amounts, provider_data, - photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, - need_email, need_shipping_address, send_phone_number_to_provider, - send_email_to_provider, is_flexible) - return result + currency, prices, max_tip_amount=max_tip_amount, suggested_tip_amounts=suggested_tip_amounts, + provider_data=provider_data, photo_url=photo_url, photo_size=photo_size, + photo_width=photo_width, photo_height=photo_height, need_name=need_name, + need_phone_number=need_phone_number, need_email=need_email, + need_shipping_address=need_shipping_address, send_phone_number_to_provider=send_phone_number_to_provider, + send_email_to_provider=send_email_to_provider, is_flexible=is_flexible) + # noinspection PyShadowingBuiltins - # TODO: rewrite this method like in API def send_poll( self, chat_id: Union[int, str], question: str, options: List[str], is_anonymous: Optional[bool]=None, type: Optional[str]=None, @@ -5006,12 +5080,16 @@ def send_poll( return types.Message.de_json( apihelper.send_poll( - self.token, chat_id, - question, options, - 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)) + self.token, chat_id, question, options, + is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, + correct_option_id=correct_option_id, explanation=explanation, + explanation_parse_mode=explanation_parse_mode, open_period=open_period, + 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) + ) + def stop_poll( self, chat_id: Union[int, str], message_id: int, @@ -5033,7 +5111,10 @@ def stop_poll( :return: On success, the stopped Poll is returned. :rtype: :obj:`types.Poll` """ - return types.Poll.de_json(apihelper.stop_poll(self.token, chat_id, message_id, reply_markup)) + return types.Poll.de_json( + apihelper.stop_poll(self.token, chat_id, message_id, reply_markup=reply_markup) + ) + def answer_shipping_query( self, shipping_query_id: str, ok: bool, @@ -5060,7 +5141,9 @@ def answer_shipping_query( :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) + return apihelper.answer_shipping_query( + self.token, shipping_query_id, ok, shipping_options=shipping_options, error_message=error_message) + def answer_pre_checkout_query( self, pre_checkout_query_id: int, ok: bool, @@ -5089,7 +5172,9 @@ def answer_pre_checkout_query( :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) + return apihelper.answer_pre_checkout_query( + self.token, pre_checkout_query_id, ok, error_message=error_message) + def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, @@ -5129,12 +5214,15 @@ def edit_message_caption( """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - result = apihelper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, caption_entities, reply_markup) + result = apihelper.edit_message_caption( + self.token, caption, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, + parse_mode=parse_mode, caption_entities=caption_entities, reply_markup=reply_markup) + if type(result) == bool: return result return types.Message.de_json(result) + def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: """ Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` @@ -5152,6 +5240,7 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message """ return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) + def answer_inline_query( self, inline_query_id: str, results: List[Any], @@ -5206,8 +5295,10 @@ def answer_inline_query( logger.warning("switch_pm_text and switch_pm_parameter are deprecated for answer_inline_query. Use button instead.") button = types.InlineQueryResultsButton(text=switch_pm_text, start_parameter=switch_pm_parameter) - return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset, - button) + return apihelper.answer_inline_query( + self.token, inline_query_id, results, cache_time=cache_time, is_personal=is_personal, + next_offset=next_offset, button=button) + def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool: """ @@ -5224,9 +5315,9 @@ def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bo :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.unpin_all_general_forum_topic_messages(self.token, chat_id) + def answer_callback_query( self, callback_query_id: int, text: Optional[str]=None, show_alert: Optional[bool]=None, @@ -5256,7 +5347,9 @@ def answer_callback_query( :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) + return apihelper.answer_callback_query( + self.token, callback_query_id, text=text, show_alert=show_alert, url=url, cache_time=cache_time) + def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types.UserChatBoosts: """ @@ -5273,9 +5366,10 @@ def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types. :return: On success, a UserChatBoosts object is returned. :rtype: :class:`telebot.types.UserChatBoosts` """ + return types.UserChatBoosts.de_json( + apihelper.get_user_chat_boosts(self.token, chat_id, user_id) + ) - result = apihelper.get_user_chat_boosts(self.token, chat_id, user_id) - return types.UserChatBoosts.de_json(result) def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None): """ @@ -5296,8 +5390,8 @@ def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[An :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail) + @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]=None): @@ -5320,7 +5414,8 @@ def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]= :rtype: :obj:`bool` """ # deprecated - return self.set_sticker_set_thumbnail(name, user_id, thumb) + return self.set_sticker_set_thumbnail(name, user_id, thumbnail=thumb) + def get_sticker_set(self, name: str) -> types.StickerSet: """ @@ -5334,8 +5429,9 @@ def get_sticker_set(self, name: str) -> types.StickerSet: :return: On success, a StickerSet object is returned. :rtype: :class:`telebot.types.StickerSet` """ - result = apihelper.get_sticker_set(self.token, name) - return types.StickerSet.de_json(result) + return types.StickerSet.de_json( + apihelper.get_sticker_set(self.token, name) + ) def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.Sticker]: """ @@ -5350,6 +5446,7 @@ def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.S """ result = apihelper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] + def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: """ @@ -5366,7 +5463,8 @@ def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.set_sticker_keywords(self.token, sticker, keywords) + return apihelper.set_sticker_keywords(self.token, sticker, keywords=keywords) + def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosition=None) -> bool: """ @@ -5383,7 +5481,7 @@ def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosit :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.set_sticker_mask_position(self.token, sticker, mask_position) + return apihelper.set_sticker_mask_position(self.token, sticker, mask_position=mask_position) def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: @@ -5400,7 +5498,8 @@ def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Opt :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) + return apihelper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id=custom_emoji_id) + def set_sticker_set_title(self, name: str, title: str) -> bool: """ @@ -5416,8 +5515,8 @@ def set_sticker_set_title(self, name: str, title: str) -> bool: :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.set_sticker_set_title(self.token, name, title) + def delete_sticker_set(self, name:str) -> bool: """ @@ -5429,8 +5528,8 @@ def delete_sticker_set(self, name:str) -> bool: :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.delete_sticker_set(self.token, name) + def set_sticker_emoji_list(self, sticker: str, emoji_list: List[str]) -> bool: """ @@ -5446,7 +5545,6 @@ def set_sticker_emoji_list(self, sticker: str, emoji_list: List[str]) -> bool: :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.set_sticker_emoji_list(self.token, sticker, emoji_list) @@ -5479,8 +5577,10 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s sticker = png_sticker sticker_format = "static" - result = apihelper.upload_sticker_file(self.token, user_id, sticker, sticker_format) - return types.File.de_json(result) + return types.File.de_json( + apihelper.upload_sticker_file(self.token, user_id, sticker, sticker_format) + ) + def create_new_sticker_set( self, user_id: int, name: str, title: str, @@ -5570,11 +5670,10 @@ def create_new_sticker_set( if stickers is None: raise ValueError('You must pass at least one sticker') stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] - - return apihelper.create_new_sticker_set( - self.token, user_id, name, title, stickers, sticker_format, sticker_type, needs_repainting) + self.token, user_id, name, title, stickers, sticker_format, sticker_type=sticker_type, + needs_repainting=needs_repainting) def add_sticker_to_set( @@ -5638,8 +5737,8 @@ def add_sticker_to_set( raise ValueError('You must pass at least one sticker.') sticker = types.InputSticker(old_sticker, emojis, mask_position) - return apihelper.add_sticker_to_set( - self.token, user_id, name, sticker) + return apihelper.add_sticker_to_set(self.token, user_id, name, sticker) + def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ @@ -5658,6 +5757,7 @@ def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ return apihelper.set_sticker_position_in_set(self.token, sticker, position) + def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. @@ -5670,6 +5770,7 @@ def delete_sticker_from_set(self, sticker: str) -> bool: """ return apihelper.delete_sticker_from_set(self.token, sticker) + def create_forum_topic(self, chat_id: int, name: str, icon_color: Optional[int]=None, icon_custom_emoji_id: Optional[str]=None) -> types.ForumTopic: @@ -5696,9 +5797,11 @@ def create_forum_topic(self, :rtype: :class:`telebot.types.ForumTopic` """ return types.ForumTopic.de_json( - apihelper.create_forum_topic(self.token, chat_id, name, icon_color, icon_custom_emoji_id) + apihelper.create_forum_topic( + self.token, chat_id, name, icon_color=icon_color, icon_custom_emoji_id=icon_custom_emoji_id) ) + def edit_forum_topic( self, chat_id: Union[int, str], message_thread_id: int, name: Optional[str]=None, @@ -5729,7 +5832,9 @@ def edit_forum_topic( :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.edit_forum_topic(self.token, chat_id, message_thread_id, name, icon_custom_emoji_id) + return apihelper.edit_forum_topic( + self.token, chat_id, message_thread_id, name=name, icon_custom_emoji_id=icon_custom_emoji_id) + def close_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: """ @@ -5739,7 +5844,7 @@ def close_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic - :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :aram chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` :param message_thread_id: Identifier of the topic to close @@ -5750,6 +5855,7 @@ def close_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> """ return apihelper.close_forum_topic(self.token, chat_id, message_thread_id) + def reopen_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: """ Use this method to reopen a closed topic in a forum supergroup chat. The bot must be an administrator in the chat @@ -5769,6 +5875,7 @@ def reopen_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) - """ return apihelper.reopen_forum_topic(self.token, chat_id, message_thread_id) + def delete_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) -> bool: """ Use this method to delete a topic in a forum supergroup chat. The bot must be an administrator in the chat for this @@ -5788,6 +5895,7 @@ def delete_forum_topic(self, chat_id: Union[str, int], message_thread_id: int) - """ return apihelper.delete_forum_topic(self.token, chat_id, message_thread_id) + def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message_thread_id: int) -> bool: """ Use this method to clear the list of pinned messages in a forum topic. The bot must be an administrator in the @@ -5807,6 +5915,7 @@ def unpin_all_forum_topic_messages(self, chat_id: Union[str, int], message_threa """ return apihelper.unpin_all_forum_topic_messages(self.token, chat_id, message_thread_id) + def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool: """ Use this method to edit the name of the 'General' topic in a forum supergroup chat. @@ -5821,9 +5930,9 @@ def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool: :param name: New topic name, 1-128 characters :type name: :obj:`str` """ - return apihelper.edit_general_forum_topic(self.token, chat_id, name) + def close_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ Use this method to close the 'General' topic in a forum supergroup chat. @@ -5837,6 +5946,7 @@ def close_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ return apihelper.close_general_forum_topic(self.token, chat_id) + def reopen_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ Use this method to reopen the 'General' topic in a forum supergroup chat. @@ -5850,6 +5960,7 @@ def reopen_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ return apihelper.reopen_general_forum_topic(self.token, chat_id) + def hide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ Use this method to hide the 'General' topic in a forum supergroup chat. @@ -5863,6 +5974,7 @@ def hide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ return apihelper.hide_general_forum_topic(self.token, chat_id) + def unhide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ Use this method to unhide the 'General' topic in a forum supergroup chat. @@ -5876,6 +5988,7 @@ def unhide_general_forum_topic(self, chat_id: Union[int, str]) -> bool: """ return apihelper.unhide_general_forum_topic(self.token, chat_id) + def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: """ Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. @@ -5888,6 +6001,7 @@ def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: """ return apihelper.get_forum_topic_icon_stickers(self.token) + def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryResultBase) -> types.SentWebAppMessage: """ Use this method to set the result of an interaction with a Web App and @@ -5908,6 +6022,7 @@ def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryR """ return apihelper.answer_web_app_query(self.token, web_app_query_id, result) + def register_for_reply(self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when a reply to `message` arrives. @@ -5926,8 +6041,8 @@ def register_for_reply(self, message: types.Message, callback: Callable, *args, :return: None """ - message_id = message.message_id - self.register_for_reply_by_message_id(message_id, callback, *args, **kwargs) + self.register_for_reply_by_message_id(message.message_id, callback, *args, **kwargs) + def register_for_reply_by_message_id( self, message_id: int, callback: Callable, *args, **kwargs) -> None: @@ -5950,6 +6065,7 @@ def register_for_reply_by_message_id( """ self.reply_backend.register_handler(message_id, Handler(callback, *args, **kwargs)) + def _notify_reply_handlers(self, new_messages) -> None: """ Notify handlers of the answers @@ -5964,6 +6080,7 @@ def _notify_reply_handlers(self, new_messages) -> None: for handler in handlers: self._exec_task(handler["callback"], message, *handler["args"], **handler["kwargs"]) + def register_next_step_handler(self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ Registers a callback function to be notified when new message arrives after `message`. @@ -5982,8 +6099,8 @@ def register_next_step_handler(self, message: types.Message, callback: Callable, :return: None """ - chat_id = message.chat.id - self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs) + self.register_next_step_handler_by_chat_id(message.chat.id, callback, *args, **kwargs) + def setup_middleware(self, middleware: BaseMiddleware): """ @@ -6007,6 +6124,7 @@ def setup_middleware(self, middleware: BaseMiddleware): self.middlewares.append(middleware) + def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None) -> None: """ Sets a new state of a user. @@ -6032,6 +6150,7 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Option chat_id = user_id self.current_states.set_state(chat_id, user_id, state) + def reset_data(self, user_id: int, chat_id: Optional[int]=None): """ Reset data for a user in chat. @@ -6048,6 +6167,7 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None): chat_id = user_id self.current_states.reset_data(chat_id, user_id) + def delete_state(self, user_id: int, chat_id: Optional[int]=None) -> None: """ Delete the current state of a user. @@ -6064,6 +6184,7 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None) -> None: chat_id = user_id self.current_states.delete_state(chat_id, user_id) + def retrieve_data(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Any]: """ Returns context manager with data for a user in chat. @@ -6081,6 +6202,7 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None) -> Optional[A chat_id = user_id return self.current_states.get_interactive_data(chat_id, user_id) + def get_state(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Union[int, str, State]]: """ Gets current state of a user. @@ -6099,6 +6221,7 @@ def get_state(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Union chat_id = user_id return self.current_states.get_state(chat_id, user_id) + def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): """ Add data to states. @@ -6117,6 +6240,7 @@ def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): for key, value in kwargs.items(): self.current_states.set_data(chat_id, user_id, key, value) + def register_next_step_handler_by_chat_id( self, chat_id: int, callback: Callable, *args, **kwargs) -> None: """ @@ -6138,6 +6262,7 @@ def register_next_step_handler_by_chat_id( """ self.next_step_backend.register_handler(chat_id, Handler(callback, *args, **kwargs)) + def clear_step_handler(self, message: types.Message) -> None: """ Clears all callback functions registered by register_next_step_handler(). @@ -6147,8 +6272,8 @@ def clear_step_handler(self, message: types.Message) -> None: :return: None """ - chat_id = message.chat.id - self.clear_step_handler_by_chat_id(chat_id) + self.clear_step_handler_by_chat_id(message.chat.id) + def clear_step_handler_by_chat_id(self, chat_id: Union[int, str]) -> None: """ @@ -6161,6 +6286,7 @@ def clear_step_handler_by_chat_id(self, chat_id: Union[int, str]) -> None: """ self.next_step_backend.clear_handlers(chat_id) + def clear_reply_handlers(self, message: types.Message) -> None: """ Clears all callback functions registered by register_for_reply() and register_for_reply_by_message_id(). @@ -6170,8 +6296,8 @@ def clear_reply_handlers(self, message: types.Message) -> None: :return: None """ - message_id = message.message_id - self.clear_reply_handlers_by_message_id(message_id) + self.clear_reply_handlers_by_message_id(message.message_id) + def clear_reply_handlers_by_message_id(self, message_id: int) -> None: """ @@ -6184,6 +6310,7 @@ def clear_reply_handlers_by_message_id(self, message_id: int) -> None: """ self.reply_backend.clear_handlers(message_id) + def _notify_next_handlers(self, new_messages): """ Description: TBD @@ -6202,6 +6329,7 @@ def _notify_next_handlers(self, new_messages): # removing message that was detected with next_step_handler new_messages.pop(i) + @staticmethod def _build_handler_dict(handler, pass_bot=False, **filters): """ @@ -6219,6 +6347,7 @@ def _build_handler_dict(handler, pass_bot=False, **filters): #'filters': filters } + def middleware_handler(self, update_types: Optional[List[str]]=None): """ Function-based middleware handler decorator. @@ -6255,6 +6384,7 @@ def decorator(handler): return decorator + def add_middleware_handler(self, handler, update_types=None): """ Add middleware handler. @@ -6281,6 +6411,7 @@ def add_middleware_handler(self, handler, update_types=None): if not added: self.default_middleware_handlers.append(handler) + # function register_middleware_handler def register_middleware_handler(self, callback, update_types=None): """ @@ -6305,6 +6436,7 @@ def register_middleware_handler(self, callback, update_types=None): """ self.add_middleware_handler(callback, update_types) + @staticmethod def check_commands_input(commands, method_name): """ @@ -6313,6 +6445,7 @@ def check_commands_input(commands, method_name): if not isinstance(commands, list) or not all(isinstance(item, str) for item in commands): logger.error(f"{method_name}: Commands filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use the supplied type.") + @staticmethod def check_regexp_input(regexp, method_name): """ @@ -6321,6 +6454,7 @@ def check_regexp_input(regexp, method_name): if not isinstance(regexp, str): logger.error(f"{method_name}: Regexp filter should be string. Not able to use the supplied type.") + def message_handler( self, commands: Optional[List[str]]=None, @@ -6413,6 +6547,7 @@ def decorator(handler): return decorator + def add_message_handler(self, handler_dict): """ Adds a message handler @@ -6425,6 +6560,7 @@ def add_message_handler(self, handler_dict): """ self.message_handlers.append(handler_dict) + def register_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): """ @@ -6481,6 +6617,7 @@ def register_message_handler(self, callback: Callable, content_types: Optional[L **kwargs) self.add_message_handler(handler_dict) + def edited_message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Handles new version of a message that is known to the bot and was edited. @@ -6535,6 +6672,7 @@ def decorator(handler): return decorator + def add_edited_message_handler(self, handler_dict): """ Adds the edit message handler @@ -6547,6 +6685,7 @@ def add_edited_message_handler(self, handler_dict): """ self.edited_message_handlers.append(handler_dict) + def register_edited_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, chat_types: Optional[List[str]]=None, pass_bot: Optional[bool]=False, **kwargs): @@ -6602,6 +6741,7 @@ def register_edited_message_handler(self, callback: Callable, content_types: Opt **kwargs) self.add_edited_message_handler(handler_dict) + def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Handles new incoming channel post of any kind - text, photo, sticker, etc. @@ -6652,6 +6792,7 @@ def decorator(handler): return decorator + def add_channel_post_handler(self, handler_dict): """ Adds channel post handler @@ -6663,8 +6804,10 @@ def add_channel_post_handler(self, handler_dict): :return: """ self.channel_post_handlers.append(handler_dict) - - def register_channel_post_handler(self, callback: Callable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + + + def register_channel_post_handler( + self, callback: Callable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers channel post message handler. @@ -6714,6 +6857,7 @@ def register_channel_post_handler(self, callback: Callable, content_types: Optio **kwargs) self.add_channel_post_handler(handler_dict) + def edited_channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ Handles new version of a channel post that is known to the bot and was edited. @@ -6764,6 +6908,7 @@ def decorator(handler): return decorator + def add_edited_channel_post_handler(self, handler_dict): """ Adds the edit channel post handler @@ -6776,8 +6921,10 @@ def add_edited_channel_post_handler(self, handler_dict): """ self.edited_channel_post_handlers.append(handler_dict) - def register_edited_channel_post_handler(self, callback: Callable, content_types: Optional[List[str]]=None, - commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): + + def register_edited_channel_post_handler( + self, callback: Callable, content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, + regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers edited channel post message handler. @@ -6845,7 +6992,8 @@ def decorator(handler): return handler return decorator - + + def add_message_reaction_handler(self, handler_dict): """ Adds message reaction handler @@ -6858,6 +7006,7 @@ def add_message_reaction_handler(self, handler_dict): """ self.message_reaction_handlers.append(handler_dict) + def register_message_reaction_handler(self, callback: Callable, func: Callable=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message reaction handler. @@ -6878,6 +7027,7 @@ def register_message_reaction_handler(self, callback: Callable, func: Callable=N handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_message_reaction_handler(handler_dict) + def message_reaction_count_handler(self, func=None, **kwargs): """ Handles new incoming message reaction count. @@ -6896,6 +7046,7 @@ def decorator(handler): return handler return decorator + def add_message_reaction_count_handler(self, handler_dict): """ @@ -6909,6 +7060,7 @@ def add_message_reaction_count_handler(self, handler_dict): """ self.message_reaction_count_handlers.append(handler_dict) + def register_message_reaction_count_handler(self, callback: Callable, func: Callable=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers message reaction count handler. @@ -6929,6 +7081,7 @@ def register_message_reaction_count_handler(self, callback: Callable, func: Call handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_message_reaction_count_handler(handler_dict) + def inline_handler(self, func, **kwargs): """ Handles new incoming inline query. @@ -6948,6 +7101,7 @@ def decorator(handler): return decorator + def add_inline_handler(self, handler_dict): """ Adds inline call handler @@ -6960,6 +7114,7 @@ def add_inline_handler(self, handler_dict): """ self.inline_handlers.append(handler_dict) + def register_inline_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers inline handler. @@ -6980,6 +7135,7 @@ def register_inline_handler(self, callback: Callable, func: Callable, pass_bot: handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_inline_handler(handler_dict) + def chosen_inline_handler(self, func, **kwargs): """ Handles the result of an inline query that was chosen by a user and sent to their chat partner. @@ -7000,6 +7156,7 @@ def decorator(handler): return decorator + def add_chosen_inline_handler(self, handler_dict): """ Description: TBD @@ -7012,6 +7169,7 @@ def add_chosen_inline_handler(self, handler_dict): """ self.chosen_inline_handlers.append(handler_dict) + def register_chosen_inline_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers chosen inline handler. @@ -7032,6 +7190,7 @@ def register_chosen_inline_handler(self, callback: Callable, func: Callable, pas handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chosen_inline_handler(handler_dict) + def callback_query_handler(self, func, **kwargs): """ Handles new incoming callback query. @@ -7051,6 +7210,7 @@ def decorator(handler): return decorator + def add_callback_query_handler(self, handler_dict): """ Adds a callback request handler @@ -7063,6 +7223,7 @@ def add_callback_query_handler(self, handler_dict): """ self.callback_query_handlers.append(handler_dict) + def register_callback_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers callback query handler. @@ -7083,6 +7244,7 @@ def register_callback_query_handler(self, callback: Callable, func: Callable, pa handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_callback_query_handler(handler_dict) + def shipping_query_handler(self, func, **kwargs): """ Handles new incoming shipping query. Only for invoices with flexible price. @@ -7102,6 +7264,7 @@ def decorator(handler): return decorator + def add_shipping_query_handler(self, handler_dict): """ Adds a shipping request handler. @@ -7114,6 +7277,7 @@ def add_shipping_query_handler(self, handler_dict): """ self.shipping_query_handlers.append(handler_dict) + def register_shipping_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers shipping query handler. @@ -7134,6 +7298,7 @@ def register_shipping_query_handler(self, callback: Callable, func: Callable, pa handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_shipping_query_handler(handler_dict) + def pre_checkout_query_handler(self, func, **kwargs): """ New incoming pre-checkout query. Contains full information about checkout. @@ -7153,6 +7318,7 @@ def decorator(handler): return decorator + def add_pre_checkout_query_handler(self, handler_dict): """ Adds a pre-checkout request handler @@ -7164,6 +7330,7 @@ def add_pre_checkout_query_handler(self, handler_dict): :return: """ self.pre_checkout_query_handlers.append(handler_dict) + def register_pre_checkout_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ @@ -7184,6 +7351,7 @@ def register_pre_checkout_query_handler(self, callback: Callable, func: Callable handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_pre_checkout_query_handler(handler_dict) + def poll_handler(self, func, **kwargs): """ Handles new state of a poll. Bots receive only updates about stopped polls and polls, which are sent by the bot @@ -7202,6 +7370,7 @@ def decorator(handler): return decorator + def add_poll_handler(self, handler_dict): """ Adds a poll request handler @@ -7214,6 +7383,7 @@ def add_poll_handler(self, handler_dict): """ self.poll_handlers.append(handler_dict) + def register_poll_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll handler. @@ -7234,6 +7404,7 @@ def register_poll_handler(self, callback: Callable, func: Callable, pass_bot: Op handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_handler(handler_dict) + def poll_answer_handler(self, func=None, **kwargs): """ Handles change of user's answer in a non-anonymous poll(when user changes the vote). @@ -7254,6 +7425,7 @@ def decorator(handler): return decorator + def add_poll_answer_handler(self, handler_dict): """ Adds a poll_answer request handler. @@ -7266,6 +7438,7 @@ def add_poll_answer_handler(self, handler_dict): """ self.poll_answer_handlers.append(handler_dict) + def register_poll_answer_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers poll answer handler. @@ -7286,6 +7459,7 @@ def register_poll_answer_handler(self, callback: Callable, func: Callable, pass_ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_poll_answer_handler(handler_dict) + def my_chat_member_handler(self, func=None, **kwargs): """ Handles update in a status of a bot. For private chats, @@ -7306,6 +7480,7 @@ def decorator(handler): return decorator + def add_my_chat_member_handler(self, handler_dict): """ Adds a my_chat_member handler. @@ -7318,6 +7493,7 @@ def add_my_chat_member_handler(self, handler_dict): """ self.my_chat_member_handlers.append(handler_dict) + def register_my_chat_member_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers my chat member handler. @@ -7338,6 +7514,7 @@ def register_my_chat_member_handler(self, callback: Callable, func: Optional[Cal handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_my_chat_member_handler(handler_dict) + def chat_member_handler(self, func=None, **kwargs): """ Handles update in a status of a user in a chat. @@ -7359,6 +7536,7 @@ def decorator(handler): return decorator + def add_chat_member_handler(self, handler_dict): """ Adds a chat_member handler. @@ -7371,7 +7549,9 @@ def add_chat_member_handler(self, handler_dict): """ self.chat_member_handlers.append(handler_dict) - def register_chat_member_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): + + def register_chat_member_handler( + self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers chat member handler. @@ -7391,6 +7571,7 @@ def register_chat_member_handler(self, callback: Callable, func: Optional[Callab handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_member_handler(handler_dict) + def chat_join_request_handler(self, func=None, **kwargs): """ Handles a request to join the chat has been sent. The bot must have the can_invite_users @@ -7411,6 +7592,7 @@ def decorator(handler): return decorator + def add_chat_join_request_handler(self, handler_dict): """ Adds a chat_join_request handler. @@ -7423,7 +7605,9 @@ def add_chat_join_request_handler(self, handler_dict): """ self.chat_join_request_handlers.append(handler_dict) - def register_chat_join_request_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + + def register_chat_join_request_handler( + self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): """ Registers chat join request handler. @@ -7443,6 +7627,7 @@ def register_chat_join_request_handler(self, callback: Callable, func: Optional[ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_join_request_handler(handler_dict) + def chat_boost_handler(self, func=None, **kwargs): """ Handles new incoming chat boost state. @@ -7460,7 +7645,8 @@ def decorator(handler): return handler return decorator - + + def add_chat_boost_handler(self, handler_dict): """ Adds a chat_boost handler. @@ -7473,7 +7659,9 @@ def add_chat_boost_handler(self, handler_dict): """ self.chat_boost_handlers.append(handler_dict) - def register_chat_boost_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + + def register_chat_boost_handler( + self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): """ Registers chat boost handler. @@ -7492,6 +7680,7 @@ def register_chat_boost_handler(self, callback: Callable, func: Optional[Callabl handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chat_boost_handler(handler_dict) + def removed_chat_boost_handler(self, func=None, **kwargs): """ Handles new incoming chat boost state. @@ -7509,6 +7698,7 @@ def decorator(handler): return handler return decorator + def add_removed_chat_boost_handler(self, handler_dict): """ @@ -7522,7 +7712,9 @@ def add_removed_chat_boost_handler(self, handler_dict): """ self.removed_chat_boost_handlers.append(handler_dict) - def register_removed_chat_boost_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + + def register_removed_chat_boost_handler( + self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): """ Registers removed chat boost handler. @@ -7540,24 +7732,7 @@ def register_removed_chat_boost_handler(self, callback: Callable, func: Optional """ handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_removed_chat_boost_handler(handler_dict) - - def _test_message_handler(self, message_handler, message): - """ - Test message handler - - :param message_handler: - :param message: - :return: - """ - for message_filter, filter_value in message_handler['filters'].items(): - if filter_value is None: - continue - - if not self._test_filter(message_filter, filter_value, message): - return False - - return True def add_custom_filter(self, custom_filter: Union[SimpleCustomFilter, AdvancedCustomFilter]): """ @@ -7577,6 +7752,25 @@ def check(self, message, text): """ self.custom_filters[custom_filter.key] = custom_filter + + def _test_message_handler(self, message_handler, message): + """ + Test message handler + + :param message_handler: + :param message: + :return: + """ + for message_filter, filter_value in message_handler['filters'].items(): + if filter_value is None: + continue + + if not self._test_filter(message_filter, filter_value, message): + return False + + return True + + def _test_filter(self, message_filter, filter_value, message): """ Test filters @@ -7601,6 +7795,7 @@ def _test_filter(self, message_filter, filter_value, message): else: return False + def _check_filter(self, message_filter, filter_value, message): filter_check = self.custom_filters.get(message_filter) if not filter_check: @@ -7613,6 +7808,7 @@ def _check_filter(self, message_filter, filter_value, message): logger.error("Custom filter: wrong type. Should be SimpleCustomFilter or AdvancedCustomFilter.") return False + # middleware check-up method def _get_middlewares(self, update_type): """ @@ -7626,6 +7822,7 @@ def _get_middlewares(self, update_type): middlewares = [i for i in self.middlewares if update_type in i.update_types] return middlewares + def _run_middlewares_and_handler(self, message, handlers, middlewares, update_type): """ This method is made to run handlers and middlewares in queue. @@ -7636,7 +7833,6 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty :param update_type: handler/update type (Update field name) :return: """ - if not self.use_class_middlewares: if handlers: for handler in handlers: @@ -7719,6 +7915,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty else: middleware.post_process(message, data, handler_error) + def _notify_command_handlers(self, handlers, new_messages, update_type): """ Notifies command handlers. From 60d581f9ea79cc9362d01f59f39debbd1f58bcd1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Jan 2024 23:48:59 +0300 Subject: [PATCH 1432/1808] Fix UserShared with UsersShared --- telebot/types.py | 54 +++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 40 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 58843f7e3..7c945f40b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -992,7 +992,7 @@ class Message(JsonDeserializable): :type successful_payment: :class:`telebot.types.SuccessfulPayment` :param users_shared: Optional. Service message: a user was shared with the bot - :type users_shared: :class:`telebot.types.UserShared` + :type users_shared: :class:`telebot.types.UsersShared` :param chat_shared: Optional. Service message: a chat was shared with the bot :type chat_shared: :class:`telebot.types.ChatShared` @@ -1242,7 +1242,7 @@ def de_json(cls, json_string): opts['write_access_allowed'] = WriteAccessAllowed.de_json(obj['write_access_allowed']) content_type = 'write_access_allowed' if 'users_shared' in obj: - opts['users_shared'] = UserShared.de_json(obj['users_shared']) + opts['users_shared'] = UsersShared.de_json(obj['users_shared']) content_type = 'users_shared' # COMPATIBILITY BROKEN! if 'chat_shared' in obj: opts['chat_shared'] = ChatShared.de_json(obj['chat_shared']) @@ -1359,8 +1359,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.general_forum_topic_hidden: Optional[GeneralForumTopicHidden] = None self.general_forum_topic_unhidden: Optional[GeneralForumTopicUnhidden] = None self.write_access_allowed: Optional[WriteAccessAllowed] = None - self.users_shared: Optional[UserShared] = None - self.user_shared: Optional[UserShared] = self.users_shared + self.users_shared: Optional[UsersShared] = None self.chat_shared: Optional[ChatShared] = None self.story: Optional[Story] = None self.external_reply: Optional[ExternalReplyInfo] = None @@ -1561,6 +1560,10 @@ def forward_date(self): return self.forward_origin.date return None + @property + def user_shared(self): + logger.warning('The parameter "user_shared" is deprecated, use "users_shared" instead') + return self.users_shared class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): @@ -2464,7 +2467,7 @@ class KeyboardButtonRequestUsers(Dictionaryable): Telegram documentation: https://core.telegram.org/bots/api#keyboardbuttonrequestusers - :param request_id: Signed 32-bit identifier of the request, which will be received back in the UserShared object. + :param request_id: Signed 32-bit identifier of the request, which will be received back in the UsersShared object. Must be unique within the message :type request_id: :obj:`int` @@ -2601,7 +2604,7 @@ class KeyboardButton(Dictionaryable, JsonSerializable): :type web_app: :class:`telebot.types.WebAppInfo` :param request_user: Optional. If specified, pressing the button will open a list of suitable users. Tapping on any user - will send their identifier to the bot in a “user_shared” service message. Available in private chats only. + will send their identifier to the bot in a “users_shared” service message. Available in private chats only. :type request_user: :class:`telebot.types.KeyboardButtonRequestUsers` :param request_chat: Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will @@ -7732,41 +7735,8 @@ def __init__(self, from_request: Optional[bool]=None, web_app_name: Optional[str self.web_app_name: str = web_app_name self.from_request: bool = from_request self.from_attachment_menu: bool = from_attachment_menu - - - -class UserShared(JsonDeserializable): - """ - This object contains information about the user whose identifier was shared with the bot using a - `telebot.types.KeyboardButtonRequestUsers` button. - - Telegram documentation: https://core.telegram.org/bots/api#usershared - :param request_id: identifier of the request - :type request_id: :obj:`int` - :param user_id: Identifier of the shared user. This number may have more than 32 significant bits and some programming - languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit - integer or double-precision float type are safe for storing this identifier. The bot may not have access to the user - and could be unable to use this identifier, unless the user is already known to the bot by some other means. - :type user_id: :obj:`int` - - :return: Instance of the class - :rtype: :class:`telebot.types.UserShared` - """ - - @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, request_id: int, user_id: int, **kwargs) -> None: - self.request_id: int = request_id - self.user_id: int = user_id - - - class ChatShared(JsonDeserializable): """ This object contains information about the chat whose identifier was shared with the bot using a @@ -8918,7 +8888,6 @@ class UsersShared(JsonDeserializable): :return: Instance of the class :rtype: :class:`UsersShared` """ - @classmethod def de_json(cls, json_string): if json_string is None: @@ -8929,6 +8898,11 @@ def de_json(cls, json_string): def __init__(self, request_id, user_ids, **kwargs): self.request_id = request_id self.user_ids = user_ids + + @property + def user_id(self): + logger.warning('The parameter "user_id" is deprecated, use "user_ids" instead') + return None class ChatBoostUpdated(JsonDeserializable): From 071fff34ff636e450d9aa44a7a8f740602407c7f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 4 Jan 2024 00:10:04 +0300 Subject: [PATCH 1433/1808] Deprecation fixes N2 request_user deprecated, request_users updates. Classes also. --- telebot/types.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 7c945f40b..da41be80a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2484,10 +2484,10 @@ class KeyboardButtonRequestUsers(Dictionaryable): :return: Instance of the class :rtype: :class:`telebot.types.KeyboardButtonRequestUsers` - """ - - def __init__(self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, max_quantity: Optional[int]=None) -> None: + def __init__( + self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, + max_quantity: Optional[int]=None) -> None: self.request_id: int = request_id self.user_is_bot: Optional[bool] = user_is_bot self.user_is_premium: Optional[bool] = user_is_premium @@ -2503,7 +2503,15 @@ def to_dict(self) -> dict: data['max_quantity'] = self.max_quantity return data -KeyboardButtonRequestUser = KeyboardButtonRequestUsers + +class KeyboardButtonRequestUser(KeyboardButtonRequestUsers): + """Deprecated. Use KeyboardButtonRequestUsers instead.""" + def __init__( + self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, + max_quantity: Optional[int]=None) -> None: + logger.warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') + super().__init__(request_id, user_is_bot=user_is_bot, user_is_premium=user_is_premium, max_quantity=max_quantity) + class KeyboardButtonRequestChat(Dictionaryable): """ @@ -2603,9 +2611,12 @@ class KeyboardButton(Dictionaryable, JsonSerializable): will be able to send a “web_app_data” service message. Available in private chats only. :type web_app: :class:`telebot.types.WebAppInfo` - :param request_user: Optional. If specified, pressing the button will open a list of suitable users. Tapping on any user - will send their identifier to the bot in a “users_shared” service message. Available in private chats only. - :type request_user: :class:`telebot.types.KeyboardButtonRequestUsers` + :param request_user: deprecated + :type request_user: :class:`telebot.types.KeyboardButtonRequestUser` + + :param request_users: Optional. If specified, pressing the button will open a list of suitable users. + Identifiers of selected users will be sent to the bot in a “users_shared” service message. Available in private chats only. + :type request_users: :class:`telebot.types.KeyboardButtonRequestUsers` :param request_chat: Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a “chat_shared” service message. Available in private chats only. @@ -2616,18 +2627,19 @@ class KeyboardButton(Dictionaryable, JsonSerializable): """ def __init__(self, text: str, request_contact: Optional[bool]=None, request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, - web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUsers]=None, + web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUser]=None, request_chat: Optional[KeyboardButtonRequestChat]=None, request_users: Optional[KeyboardButtonRequestUsers]=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location self.request_poll: KeyboardButtonPollType = request_poll self.web_app: WebAppInfo = web_app - self.request_user: KeyboardButtonRequestUsers = request_user self.request_chat: KeyboardButtonRequestChat = request_chat self.request_users: KeyboardButtonRequestUsers = request_users if request_user is not None: - self.request_users = request_user + logger.warning('The parameter "request_user" is deprecated, use "request_users" instead') + if self.request_users is None: + self.request_users = request_user def to_json(self): @@ -2643,8 +2655,8 @@ def to_dict(self): json_dict['request_poll'] = self.request_poll.to_dict() if self.web_app is not None: json_dict['web_app'] = self.web_app.to_dict() - if self.request_user is not None: - json_dict['request_user'] = self.request_user.to_dict() + if self.request_users is not None: + json_dict['request_users'] = self.request_users.to_dict() if self.request_chat is not None: json_dict['request_chat'] = self.request_chat.to_dict() return json_dict From 71cdf48b5bc2591d6356705e24047b483de015c8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 4 Jan 2024 00:53:55 +0300 Subject: [PATCH 1434/1808] New content_types added --- telebot/__init__.py | 2 +- telebot/types.py | 4 +++- telebot/util.py | 20 +++++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 13fdda5a2..fb49abdc0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4410,7 +4410,7 @@ def edit_message_text( :param entities: List of special entities that appear in the message text, which can be specified instead of parse_mode :type entities: List of :obj:`telebot.types.MessageEntity` - :param disable_web_page_preview: deprecated. Disables link previews for links in this message + :param disable_web_page_preview: deprecated. :type disable_web_page_preview: :obj:`bool` :param reply_markup: A JSON-serialized object for an inline keyboard. diff --git a/telebot/types.py b/telebot/types.py index da41be80a..72d631f8c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3797,7 +3797,9 @@ class InputTextMessageContent(Dictionaryable): :return: Instance of the class :rtype: :class:`telebot.types.InputTextMessageContent` """ - def __init__(self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None, link_preview_options=None): + def __init__( + self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None, + link_preview_options=None): self.message_text: str = message_text self.parse_mode: str = parse_mode self.entities: List[MessageEntity] = entities diff --git a/telebot/util.py b/telebot/util.py index 9f0c7ab4c..94622fcb7 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -27,18 +27,20 @@ #: Contains all media content types. content_type_media = [ - 'text', 'audio', 'document', 'animation', 'game', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', - 'location', 'venue', 'dice', 'invoice', 'successful_payment', 'connected_website', 'poll', 'passport_data', - 'web_app_data', + 'text', 'animation', 'audio', 'document', 'photo', 'sticker', 'story', 'video', 'video_note', 'voice', 'contact', + 'dice', 'game', 'poll', 'venue', 'location', 'invoice', 'successful_payment', 'connected_website', + 'passport_data', 'web_app_data', ] #: Contains all service content types such as `User joined the group`. content_type_service = [ 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', - 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', - 'migrate_from_chat_id', 'pinned_message', 'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', - 'video_chat_ended', 'video_chat_participants_invited', 'message_auto_delete_timer_changed', 'forum_topic_created', - 'forum_topic_closed', 'forum_topic_reopened', 'user_shared', 'chat_shared', + 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'message_auto_delete_timer_changed', + 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', 'users_shared', 'chat_shared', + 'write_access_allowed', 'proximity_alert_triggered', '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', ] #: All update types, should be used for allowed_updates parameter in polling. @@ -187,7 +189,7 @@ def wait(self): return self.result -class CustomRequestResponse(): +class CustomRequestResponse: """ :meta private: """ @@ -365,7 +367,7 @@ def _text_before_last(substr: str) -> str: text = text[len(part):] -def escape(text: str) -> str: +def escape(text: str) -> Optional[str]: """ Replaces the following chars in `text` ('&' with '&', '<' with '<' and '>' with '>'). From 45434299afe90d6c56cb7de231f665d01e0f9227 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 4 Jan 2024 01:33:25 +0300 Subject: [PATCH 1435/1808] MaybeInaccessibleMessage processing Improved documentation for the field message of the class callbackQuery and the field pinned_message of the class Message by adding the classes MaybeInaccessibleMessage and InaccessibleMessage. --- telebot/types.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 72d631f8c..e0a578220 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -982,7 +982,7 @@ class Message(JsonDeserializable): :param pinned_message: Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply. - :type pinned_message: :class:`telebot.types.Message` + :type message: :class:`telebot.types.Message` or :class:`telebot.types.InaccessibleMessage` :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » :type invoice: :class:`telebot.types.Invoice` @@ -1177,7 +1177,12 @@ def de_json(cls, json_string): opts['migrate_from_chat_id'] = obj['migrate_from_chat_id'] content_type = 'migrate_from_chat_id' if 'pinned_message' in obj: - opts['pinned_message'] = Message.de_json(obj['pinned_message']) + pinned_message = obj['pinned_message'] + if pinned_message['date'] == 0: + # date. Always 0. The field can be used to differentiate regular and inaccessible messages. + opts['pinned_message'] = InaccessibleMessage.de_json(pinned_message) + else: + opts['pinned_message'] = Message.de_json(pinned_message) content_type = 'pinned_message' if 'invoice' in obj: opts['invoice'] = Invoice.de_json(obj['invoice']) @@ -1344,7 +1349,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.channel_chat_created: Optional[bool] = None self.migrate_to_chat_id: Optional[int] = None self.migrate_from_chat_id: Optional[int] = None - self.pinned_message: Optional[Message] = None + self.pinned_message: Union[Message, InaccessibleMessage] = message self.invoice: Optional[Invoice] = None self.successful_payment: Optional[SuccessfulPayment] = None self.connected_website: Optional[str] = None @@ -2944,9 +2949,8 @@ class CallbackQuery(JsonDeserializable): :param from_user: Sender :type from_user: :class:`telebot.types.User` - :param message: Optional. Message with the callback button that originated the query. Note that message content and - message date will not be available if the message is too old - :type message: :class:`telebot.types.Message` + :param message: Optional. Message sent by the bot with the callback button that originated the query + :type message: :class:`telebot.types.Message` or :class:`telebot.types.InaccessibleMessage` :param inline_message_id: Optional. Identifier of the message sent via the bot in inline mode, that originated the query. @@ -2975,14 +2979,21 @@ def de_json(cls, json_string): obj['data'] = None obj['from_user'] = User.de_json(obj.pop('from')) if 'message' in obj: - obj['message'] = Message.de_json(obj.get('message')) + message = obj['message'] + if message['date'] == 0: + # date. Always 0. The field can be used to differentiate regular and inaccessible messages. + obj['message'] = InaccessibleMessage.de_json(message) + else: + obj['message'] = Message.de_json(message) obj['json_string'] = json_string return cls(**obj) - def __init__(self, id, from_user, data, chat_instance, json_string, message=None, inline_message_id=None, game_short_name=None, **kwargs): + def __init__( + self, id, from_user, data, chat_instance, json_string, message=None, inline_message_id=None, + game_short_name=None, **kwargs): self.id: int = id self.from_user: User = from_user - self.message: Message = message + self.message: Union[Message, InaccessibleMessage] = message self.inline_message_id: str = inline_message_id self.chat_instance: str = chat_instance self.data: str = data From 8bc3d1cc1657aefa45ae5fba8c998873f32ff8ef Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 4 Jan 2024 01:37:51 +0300 Subject: [PATCH 1436/1808] MaybeInaccessibleMessage processing fix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e0a578220..82efd0216 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1349,7 +1349,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.channel_chat_created: Optional[bool] = None self.migrate_to_chat_id: Optional[int] = None self.migrate_from_chat_id: Optional[int] = None - self.pinned_message: Union[Message, InaccessibleMessage] = message + self.pinned_message: Optional[Union[Message, InaccessibleMessage]] = None self.invoice: Optional[Invoice] = None self.successful_payment: Optional[SuccessfulPayment] = None self.connected_website: Optional[str] = None From cb0540ba2461a59cf42c1bd5e3ec404d713230af Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 5 Jan 2024 17:01:39 +0300 Subject: [PATCH 1437/1808] Formatting fix. --- telebot/types.py | 398 +++++++++++++++++++++++------------------------ 1 file changed, 199 insertions(+), 199 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 82efd0216..3da07f7ac 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -785,285 +785,285 @@ def to_dict(self): # noinspection PyUnresolvedReferences class Message(JsonDeserializable): """ - This object represents a message. + This object represents a message. - Telegram Documentation: https://core.telegram.org/bots/api#message + Telegram Documentation: https://core.telegram.org/bots/api#message - :param message_id: Unique message identifier inside this chat - :type message_id: :obj:`int` - - :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups only - :type message_thread_id: :obj:`int` - - :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the - field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. - :type from_user: :class:`telebot.types.User` + :param message_id: Unique message identifier inside this chat + :type message_id: :obj:`int` - :param sender_chat: Optional. Sender of the message, sent on behalf of a chat. For example, the channel itself for - channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for - messages automatically forwarded to the discussion group. For backward compatibility, the field from contains a - fake sender user in non-channel chats, if the message was sent on behalf of a chat. - :type sender_chat: :class:`telebot.types.Chat` + :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups only + :type message_thread_id: :obj:`int` - :param date: Date the message was sent in Unix time - :type date: :obj:`int` + :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the + field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. + :type from_user: :class:`telebot.types.User` - :param chat: Conversation the message belongs to - :type chat: :class:`telebot.types.Chat` + :param sender_chat: Optional. Sender of the message, sent on behalf of a chat. For example, the channel itself for + channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for + messages automatically forwarded to the discussion group. For backward compatibility, the field from contains a + fake sender user in non-channel chats, if the message was sent on behalf of a chat. + :type sender_chat: :class:`telebot.types.Chat` - :param forward_from: deprecated. - :type forward_from: :class:`telebot.types.User` + :param date: Date the message was sent in Unix time + :type date: :obj:`int` - :param forward_from_chat: deprecated. - :type forward_from_chat: :class:`telebot.types.Chat` + :param chat: Conversation the message belongs to + :type chat: :class:`telebot.types.Chat` - :param forward_from_message_id: deprecated. - message in the channel - :type forward_from_message_id: :obj:`int` + :param forward_from: deprecated. + :type forward_from: :class:`telebot.types.User` - :param forward_signature: deprecated. - :type forward_signature: :obj:`str` + :param forward_from_chat: deprecated. + :type forward_from_chat: :class:`telebot.types.Chat` - :param forward_sender_name: deprecated. - :type forward_sender_name: :obj:`str` + :param forward_from_message_id: deprecated. + message in the channel + :type forward_from_message_id: :obj:`int` - :param forward_date: deprecated. - :type forward_date: :obj:`int` + :param forward_signature: deprecated. + :type forward_signature: :obj:`str` - :forward_origin: Optional. For forwarded messages, information about the original message; - :type forward_origin: :class:`telebot.types.MessageOrigin` + :param forward_sender_name: deprecated. + :type forward_sender_name: :obj:`str` - :param is_topic_message: Optional. True, if the message is sent to a forum topic - :type is_topic_message: :obj:`bool` + :param forward_date: deprecated. + :type forward_date: :obj:`int` - :param is_automatic_forward: Optional. :obj:`bool`, if the message is a channel post that was automatically - forwarded to the connected discussion group - :type is_automatic_forward: :obj:`bool` + :forward_origin: Optional. For forwarded messages, information about the original message; + :type forward_origin: :class:`telebot.types.MessageOrigin` - :param reply_to_message: Optional. For replies, the original message. Note that the Message object in this field - will not contain further reply_to_message fields even if it itself is a reply. - :type reply_to_message: :class:`telebot.types.Message` + :param is_topic_message: Optional. True, if the message is sent to a forum topic + :type is_topic_message: :obj:`bool` - :param external_reply: Optional. Information about the message that is being replied to, which may come from another chat or forum topic - :type external_reply: :class:`telebot.types.ExternalReplyInfo` + :param is_automatic_forward: Optional. :obj:`bool`, if the message is a channel post that was automatically + forwarded to the connected discussion group + :type is_automatic_forward: :obj:`bool` - :param quote: Optional. For replies that quote part of the original message, the quoted part of the message - :type quote: :class:`telebot.types.TextQuote` + :param reply_to_message: Optional. For replies, the original message. Note that the Message object in this field + will not contain further reply_to_message fields even if it itself is a reply. + :type reply_to_message: :class:`telebot.types.Message` - :param via_bot: Optional. Bot through which the message was sent - :type via_bot: :class:`telebot.types.User` + :param external_reply: Optional. Information about the message that is being replied to, which may come from another chat or forum topic + :type external_reply: :class:`telebot.types.ExternalReplyInfo` - :param edit_date: Optional. Date the message was last edited in Unix time - :type edit_date: :obj:`int` + :param quote: Optional. For replies that quote part of the original message, the quoted part of the message + :type quote: :class:`telebot.types.TextQuote` - :param has_protected_content: Optional. :obj:`bool`, if the message can't be forwarded - :type has_protected_content: :obj:`bool` + :param via_bot: Optional. Bot through which the message was sent + :type via_bot: :class:`telebot.types.User` - :param media_group_id: Optional. The unique identifier of a media message group this message belongs to - :type media_group_id: :obj:`str` + :param edit_date: Optional. Date the message was last edited in Unix time + :type edit_date: :obj:`int` - :param author_signature: Optional. Signature of the post author for messages in channels, or the custom title of an - anonymous group administrator - :type author_signature: :obj:`str` + :param has_protected_content: Optional. :obj:`bool`, if the message can't be forwarded + :type has_protected_content: :obj:`bool` - :param text: Optional. For text messages, the actual UTF-8 text of the message - :type text: :obj:`str` + :param media_group_id: Optional. The unique identifier of a media message group this message belongs to + :type media_group_id: :obj:`str` - :param entities: Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that - appear in the text - :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param author_signature: Optional. Signature of the post author for messages in channels, or the custom title of an + anonymous group administrator + :type author_signature: :obj:`str` - :param link_preview_options: Optional. Options used for link preview generation for the message, - if it is a text message and link preview options were changed - :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :param text: Optional. For text messages, the actual UTF-8 text of the message + :type text: :obj:`str` - :param animation: Optional. Message is an animation, information about the animation. For backward - compatibility, when this field is set, the document field will also be set - :type animation: :class:`telebot.types.Animation` + :param entities: Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that + appear in the text + :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param audio: Optional. Message is an audio file, information about the file - :type audio: :class:`telebot.types.Audio` + :param link_preview_options: Optional. Options used for link preview generation for the message, + if it is a text message and link preview options were changed + :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` - :param document: Optional. Message is a general file, information about the file - :type document: :class:`telebot.types.Document` + :param animation: Optional. Message is an animation, information about the animation. For backward + compatibility, when this field is set, the document field will also be set + :type animation: :class:`telebot.types.Animation` - :param photo: Optional. Message is a photo, available sizes of the photo - :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` + :param audio: Optional. Message is an audio file, information about the file + :type audio: :class:`telebot.types.Audio` - :param sticker: Optional. Message is a sticker, information about the sticker - :type sticker: :class:`telebot.types.Sticker` + :param document: Optional. Message is a general file, information about the file + :type document: :class:`telebot.types.Document` - :param story: Optional. Message is a forwarded story - :type story: :class:`telebot.types.Story` + :param photo: Optional. Message is a photo, available sizes of the photo + :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` - :param video: Optional. Message is a video, information about the video - :type video: :class:`telebot.types.Video` + :param sticker: Optional. Message is a sticker, information about the sticker + :type sticker: :class:`telebot.types.Sticker` - :param video_note: Optional. Message is a video note, information about the video message - :type video_note: :class:`telebot.types.VideoNote` + :param story: Optional. Message is a forwarded story + :type story: :class:`telebot.types.Story` - :param voice: Optional. Message is a voice message, information about the file - :type voice: :class:`telebot.types.Voice` + :param video: Optional. Message is a video, information about the video + :type video: :class:`telebot.types.Video` - :param caption: Optional. Caption for the animation, audio, document, photo, video or voice - :type caption: :obj:`str` + :param video_note: Optional. Message is a video note, information about the video message + :type video_note: :class:`telebot.types.VideoNote` - :param caption_entities: Optional. For messages with a caption, special entities like usernames, URLs, bot - commands, etc. that appear in the caption - :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param voice: Optional. Message is a voice message, information about the file + :type voice: :class:`telebot.types.Voice` - :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation - :type has_media_spoiler: :obj:`bool` + :param caption: Optional. Caption for the animation, audio, document, photo, video or voice + :type caption: :obj:`str` - :param contact: Optional. Message is a shared contact, information about the contact - :type contact: :class:`telebot.types.Contact` + :param caption_entities: Optional. For messages with a caption, special entities like usernames, URLs, bot + commands, etc. that appear in the caption + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param dice: Optional. Message is a dice with random value - :type dice: :class:`telebot.types.Dice` + :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation + :type has_media_spoiler: :obj:`bool` - :param game: Optional. Message is a game, information about the game. More about games » - :type game: :class:`telebot.types.Game` + :param contact: Optional. Message is a shared contact, information about the contact + :type contact: :class:`telebot.types.Contact` - :param poll: Optional. Message is a native poll, information about the poll - :type poll: :class:`telebot.types.Poll` + :param dice: Optional. Message is a dice with random value + :type dice: :class:`telebot.types.Dice` - :param venue: Optional. Message is a venue, information about the venue. For backward compatibility, when this - field is set, the location field will also be set - :type venue: :class:`telebot.types.Venue` + :param game: Optional. Message is a game, information about the game. More about games » + :type game: :class:`telebot.types.Game` - :param location: Optional. Message is a shared location, information about the location - :type location: :class:`telebot.types.Location` + :param poll: Optional. Message is a native poll, information about the poll + :type poll: :class:`telebot.types.Poll` - :param new_chat_members: Optional. New members that were added to the group or supergroup and information about - them (the bot itself may be one of these members) - :type new_chat_members: :obj:`list` of :class:`telebot.types.User` + :param venue: Optional. Message is a venue, information about the venue. For backward compatibility, when this + field is set, the location field will also be set + :type venue: :class:`telebot.types.Venue` - :param left_chat_member: Optional. A member was removed from the group, information about them (this member may be - the bot itself) - :type left_chat_member: :class:`telebot.types.User` + :param location: Optional. Message is a shared location, information about the location + :type location: :class:`telebot.types.Location` - :param new_chat_title: Optional. A chat title was changed to this value - :type new_chat_title: :obj:`str` + :param new_chat_members: Optional. New members that were added to the group or supergroup and information about + them (the bot itself may be one of these members) + :type new_chat_members: :obj:`list` of :class:`telebot.types.User` - :param new_chat_photo: Optional. A chat photo was change to this value - :type new_chat_photo: :obj:`list` of :class:`telebot.types.PhotoSize` + :param left_chat_member: Optional. A member was removed from the group, information about them (this member may be + the bot itself) + :type left_chat_member: :class:`telebot.types.User` - :param delete_chat_photo: Optional. Service message: the chat photo was deleted - :type delete_chat_photo: :obj:`bool` + :param new_chat_title: Optional. A chat title was changed to this value + :type new_chat_title: :obj:`str` - :param group_chat_created: Optional. Service message: the group has been created - :type group_chat_created: :obj:`bool` + :param new_chat_photo: Optional. A chat photo was change to this value + :type new_chat_photo: :obj:`list` of :class:`telebot.types.PhotoSize` - :param supergroup_chat_created: Optional. Service message: the supergroup has been created. This field can't be - received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can - only be found in reply_to_message if someone replies to a very first message in a directly created supergroup. - :type supergroup_chat_created: :obj:`bool` + :param delete_chat_photo: Optional. Service message: the chat photo was deleted + :type delete_chat_photo: :obj:`bool` - :param channel_chat_created: Optional. Service message: the channel has been created. This field can't be - received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only - be found in reply_to_message if someone replies to a very first message in a channel. - :type channel_chat_created: :obj:`bool` + :param group_chat_created: Optional. Service message: the group has been created + :type group_chat_created: :obj:`bool` - :param message_auto_delete_timer_changed: Optional. Service message: auto-delete timer settings changed in - the chat - :type message_auto_delete_timer_changed: :class:`telebot.types.MessageAutoDeleteTimerChanged` + :param supergroup_chat_created: Optional. Service message: the supergroup has been created. This field can't be + received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can + only be found in reply_to_message if someone replies to a very first message in a directly created supergroup. + :type supergroup_chat_created: :obj:`bool` - :param migrate_to_chat_id: Optional. The group has been migrated to a supergroup with the specified identifier. - This number may have more than 32 significant bits and some programming languages may have difficulty/silent - defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision - float type are safe for storing this identifier. - :type migrate_to_chat_id: :obj:`int` + :param channel_chat_created: Optional. Service message: the channel has been created. This field can't be + received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only + be found in reply_to_message if someone replies to a very first message in a channel. + :type channel_chat_created: :obj:`bool` - :param migrate_from_chat_id: Optional. The supergroup has been migrated from a group with the specified - identifier. This number may have more than 32 significant bits and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or - double-precision float type are safe for storing this identifier. - :type migrate_from_chat_id: :obj:`int` + :param message_auto_delete_timer_changed: Optional. Service message: auto-delete timer settings changed in + the chat + :type message_auto_delete_timer_changed: :class:`telebot.types.MessageAutoDeleteTimerChanged` - :param pinned_message: Optional. Specified message was pinned. Note that the Message object in this field will not - contain further reply_to_message fields even if it is itself a reply. - :type message: :class:`telebot.types.Message` or :class:`telebot.types.InaccessibleMessage` + :param migrate_to_chat_id: Optional. The group has been migrated to a supergroup with the specified identifier. + This number may have more than 32 significant bits and some programming languages may have difficulty/silent + defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision + float type are safe for storing this identifier. + :type migrate_to_chat_id: :obj:`int` + + :param migrate_from_chat_id: Optional. The supergroup has been migrated from a group with the specified + identifier. This number may have more than 32 significant bits and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + double-precision float type are safe for storing this identifier. + :type migrate_from_chat_id: :obj:`int` + + :param pinned_message: Optional. Specified message was pinned. Note that the Message object in this field will not + contain further reply_to_message fields even if it is itself a reply. + :type message: :class:`telebot.types.Message` or :class:`telebot.types.InaccessibleMessage` - :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » - :type invoice: :class:`telebot.types.Invoice` + :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » + :type invoice: :class:`telebot.types.Invoice` - :param successful_payment: Optional. Message is a service message about a successful payment, information about - the payment. More about payments » - :type successful_payment: :class:`telebot.types.SuccessfulPayment` + :param successful_payment: Optional. Message is a service message about a successful payment, information about + the payment. More about payments » + :type successful_payment: :class:`telebot.types.SuccessfulPayment` - :param users_shared: Optional. Service message: a user was shared with the bot - :type users_shared: :class:`telebot.types.UsersShared` + :param users_shared: Optional. Service message: a user was shared with the bot + :type users_shared: :class:`telebot.types.UsersShared` - :param chat_shared: Optional. Service message: a chat was shared with the bot - :type chat_shared: :class:`telebot.types.ChatShared` + :param chat_shared: Optional. Service message: a chat was shared with the bot + :type chat_shared: :class:`telebot.types.ChatShared` - :param connected_website: Optional. The domain name of the website on which the user has logged in. More about - Telegram Login » - :type connected_website: :obj:`str` + :param connected_website: Optional. The domain name of the website on which the user has logged in. More about + Telegram Login » + :type connected_website: :obj:`str` - :param write_access_allowed: Optional. Service message: the user allowed the bot added to the attachment - menu to write messages - :type write_access_allowed: :class:`telebot.types.WriteAccessAllowed` + :param write_access_allowed: Optional. Service message: the user allowed the bot added to the attachment + menu to write messages + :type write_access_allowed: :class:`telebot.types.WriteAccessAllowed` - :param passport_data: Optional. Telegram Passport data - :type passport_data: :class:`telebot.types.PassportData` + :param passport_data: Optional. Telegram Passport data + :type passport_data: :class:`telebot.types.PassportData` - :param proximity_alert_triggered: Optional. Service message. A user in the chat triggered another user's - proximity alert while sharing Live Location. - :type proximity_alert_triggered: :class:`telebot.types.ProximityAlertTriggered` + :param proximity_alert_triggered: Optional. Service message. A user in the chat triggered another user's + proximity alert while sharing Live Location. + :type proximity_alert_triggered: :class:`telebot.types.ProximityAlertTriggered` - :param forum_topic_created: Optional. Service message: forum topic created - :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` + :param forum_topic_created: Optional. Service message: forum topic created + :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` - :param forum_topic_edited: Optional. Service message: forum topic edited - :type forum_topic_edited: :class:`telebot.types.ForumTopicEdited` + :param forum_topic_edited: Optional. Service message: forum topic edited + :type forum_topic_edited: :class:`telebot.types.ForumTopicEdited` - :param forum_topic_closed: Optional. Service message: forum topic closed - :type forum_topic_closed: :class:`telebot.types.ForumTopicClosed` + :param forum_topic_closed: Optional. Service message: forum topic closed + :type forum_topic_closed: :class:`telebot.types.ForumTopicClosed` - :param forum_topic_reopened: Optional. Service message: forum topic reopened - :type forum_topic_reopened: :class:`telebot.types.ForumTopicReopened` + :param forum_topic_reopened: Optional. Service message: forum topic reopened + :type forum_topic_reopened: :class:`telebot.types.ForumTopicReopened` - :param general_forum_topic_hidden: Optional. Service message: the 'General' forum topic hidden - :type general_forum_topic_hidden: :class:`telebot.types.GeneralForumTopicHidden` + :param general_forum_topic_hidden: Optional. Service message: the 'General' forum topic hidden + :type general_forum_topic_hidden: :class:`telebot.types.GeneralForumTopicHidden` - :param general_forum_topic_unhidden: Optional. Service message: the 'General' forum topic unhidden - :type general_forum_topic_unhidden: :class:`telebot.types.GeneralForumTopicUnhidden` + :param general_forum_topic_unhidden: Optional. Service message: the 'General' forum topic unhidden + :type general_forum_topic_unhidden: :class:`telebot.types.GeneralForumTopicUnhidden` - :param giveaway_created: Optional. Service message: a giveaway has been created - :type giveaway_created: :class:`telebot.types.GiveawayCreated` + :param giveaway_created: Optional. Service message: a giveaway has been created + :type giveaway_created: :class:`telebot.types.GiveawayCreated` - :param giveaway: Optional. The message is a scheduled giveaway message - :type giveaway: :class:`telebot.types.Giveaway` + :param giveaway: Optional. The message is a scheduled giveaway message + :type giveaway: :class:`telebot.types.Giveaway` - :param giveaway_winners: Optional. Service message: giveaway winners(public winners) - :type giveaway_winners: :class:`telebot.types.GiveawayWinners` + :param giveaway_winners: Optional. Service message: giveaway winners(public winners) + :type giveaway_winners: :class:`telebot.types.GiveawayWinners` - :param giveaway_completed: Optional. Service message: giveaway completed, without public winners - :type giveaway_completed: :class:`telebot.types.GiveawayCompleted` + :param giveaway_completed: Optional. Service message: giveaway completed, without public winners + :type giveaway_completed: :class:`telebot.types.GiveawayCompleted` - :param video_chat_scheduled: Optional. Service message: video chat scheduled - :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` + :param video_chat_scheduled: Optional. Service message: video chat scheduled + :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` - :param video_chat_started: Optional. Service message: video chat started - :type video_chat_started: :class:`telebot.types.VideoChatStarted` + :param video_chat_started: Optional. Service message: video chat started + :type video_chat_started: :class:`telebot.types.VideoChatStarted` - :param video_chat_ended: Optional. Service message: video chat ended - :type video_chat_ended: :class:`telebot.types.VideoChatEnded` + :param video_chat_ended: Optional. Service message: video chat ended + :type video_chat_ended: :class:`telebot.types.VideoChatEnded` - :param video_chat_participants_invited: Optional. Service message: new participants invited to a video chat - :type video_chat_participants_invited: :class:`telebot.types.VideoChatParticipantsInvited` + :param video_chat_participants_invited: Optional. Service message: new participants invited to a video chat + :type video_chat_participants_invited: :class:`telebot.types.VideoChatParticipantsInvited` - :param web_app_data: Optional. Service message: data sent by a Web App - :type web_app_data: :class:`telebot.types.WebAppData` + :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. - :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + :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 - :rtype: :class:`telebot.types.Message` - """ + :return: Instance of the class + :rtype: :class:`telebot.types.Message` + """ @classmethod def de_json(cls, json_string): if json_string is None: return None From c2ef96580ffb4ffa9946c5f10cde791528f37e48 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Jan 2024 20:28:59 +0400 Subject: [PATCH 1438/1808] Fix description for pinned/message --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 3da07f7ac..f924d1542 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -982,7 +982,7 @@ class Message(JsonDeserializable): :param pinned_message: Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply. - :type message: :class:`telebot.types.Message` or :class:`telebot.types.InaccessibleMessage` + :type pinned_message: :class:`telebot.types.Message` or :class:`telebot.types.InaccessibleMessage` :param invoice: Optional. Message is an invoice for a payment, information about the invoice. More about payments » :type invoice: :class:`telebot.types.Invoice` From 53920aafe427f31932f099457a2a5de9744f7fbb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 5 Jan 2024 19:59:15 +0300 Subject: [PATCH 1439/1808] ReactionTypeCustomEmoji fix --- telebot/types.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 3da07f7ac..aaa91a23c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8120,7 +8120,7 @@ def to_dict(self) -> dict: return json_dict -# noinspection PyUnresolvedReferences +# noinspection PyUnresolvedReferences,PyUnusedLocal class ReactionTypeCustomEmoji(ReactionType): """ This object represents a custom emoji reaction type. @@ -8130,20 +8130,20 @@ class ReactionTypeCustomEmoji(ReactionType): :param type: Type of the reaction, must be custom_emoji :type type: :obj:`str` - :param custom_emoji: Identifier of the custom emoji - :type custom_emoji: :obj:`str` + :param custom_emoji_id: Identifier of the custom emoji + :type custom_emoji_id: :obj:`str` :return: Instance of the class :rtype: :class:`ReactionTypeCustomEmoji` """ - def __init__(self, custom_emoji: str, **kwargs) -> None: + def __init__(self, custom_emoji_id: str, **kwargs) -> None: super().__init__('custom_emoji') - self.custom_emoji: str = custom_emoji + self.custom_emoji_id: str = custom_emoji_id def to_dict(self) -> dict: json_dict = super().to_dict() - json_dict['custom_emoji'] = self.custom_emoji + json_dict['custom_emoji_id'] = self.custom_emoji_id return json_dict From dcb98c69d74fb45d7d3c877ec68a6d97fb5727c5 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Jan 2024 22:12:36 +0400 Subject: [PATCH 1440/1808] Fix documentation deprecation in favour of build.os --- .readthedocs.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 0a15ad409..20e759213 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,16 +1,22 @@ -# .readthedocs.yml -# Read the Docs configuration file +# Read the Docs configuration file for Sphinx projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 -# Build documentation in the docs/ directory with Sphinx +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the "docs/" directory with Sphinx sphinx: configuration: docs/source/conf.py - -# Optionally build your docs in additional formats such as PDF and ePub -formats: all + # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs + # builder: "dirhtml" + # Fail on all warnings to avoid broken references + # fail_on_warning: true # Optionally set the version of Python and requirements required to build your docs python: From 41c5bb4014f812af5743bdfcad2294d06a42f2e2 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Jan 2024 22:14:30 +0400 Subject: [PATCH 1441/1808] Update .readthedocs.yml --- .readthedocs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 20e759213..d157652e3 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -18,6 +18,8 @@ sphinx: # Fail on all warnings to avoid broken references # fail_on_warning: true +formats: all + # Optionally set the version of Python and requirements required to build your docs python: version: 3.11 From 7ef91c4bee33b0ce8e0785c0e2801ad74d4d29d0 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 5 Jan 2024 22:20:13 +0400 Subject: [PATCH 1442/1808] Doc update --- .../locales/en/LC_MESSAGES/async_version.po | 379 +++- .../locales/en/LC_MESSAGES/sync_version.po | 945 +++++---- docs/source/locales/en/LC_MESSAGES/types.po | 1845 +++++++++++++---- .../locales/ru/LC_MESSAGES/async_version.po | 438 +++- .../locales/ru/LC_MESSAGES/sync_version.po | 1039 ++++++---- docs/source/locales/ru/LC_MESSAGES/types.po | 1845 +++++++++++++---- 6 files changed, 4957 insertions(+), 1534 deletions(-) diff --git a/docs/source/locales/en/LC_MESSAGES/async_version.po b/docs/source/locales/en/LC_MESSAGES/async_version.po index cce40349e..fd2679e40 100644 --- a/docs/source/locales/en/LC_MESSAGES/async_version.po +++ b/docs/source/locales/en/LC_MESSAGES/async_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-08 23:07+0500\n" +"POT-Creation-Date: 2024-01-05 22:16+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -87,12 +87,14 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.callback_query_handler #: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.chat_member_handler #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.close_forum_topic #: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.copy_messages #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic #: telebot.async_telebot.AsyncTeleBot.create_invoice_link @@ -102,6 +104,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic #: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_messages #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set @@ -121,6 +124,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.enable_saving_states #: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.forward_messages #: telebot.async_telebot.AsyncTeleBot.get_chat #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators #: telebot.async_telebot.AsyncTeleBot.get_chat_member @@ -138,6 +142,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info #: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic @@ -145,6 +150,8 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.inline_handler #: telebot.async_telebot.AsyncTeleBot.leave_chat #: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.pin_chat_message #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler @@ -155,6 +162,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.promote_chat_member #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler @@ -162,11 +170,15 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler #: telebot.async_telebot.AsyncTeleBot.register_inline_handler #: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_handler #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic #: telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.reply_to @@ -202,6 +214,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_title #: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights #: telebot.async_telebot.AsyncTeleBot.set_my_description @@ -225,6 +238,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message #: telebot.async_telebot.AsyncTeleBot.upload_sticker_file #: telebot.asyncio_filters.TextFilter @@ -298,12 +312,14 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.callback_query_handler #: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.chat_member_handler #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.close #: telebot.async_telebot.AsyncTeleBot.close_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.copy_messages #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic #: telebot.async_telebot.AsyncTeleBot.create_invoice_link @@ -313,6 +329,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic #: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_messages #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set @@ -330,6 +347,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.edited_message_handler #: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.forward_messages #: telebot.async_telebot.AsyncTeleBot.get_chat #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators #: telebot.async_telebot.AsyncTeleBot.get_chat_member @@ -348,6 +366,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info #: telebot.async_telebot.AsyncTeleBot.infinity_polling @@ -355,6 +374,8 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.leave_chat #: telebot.async_telebot.AsyncTeleBot.log_out #: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.pin_chat_message #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler @@ -365,17 +386,22 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.promote_chat_member #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler #: telebot.async_telebot.AsyncTeleBot.register_inline_handler #: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_handler #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic #: telebot.async_telebot.AsyncTeleBot.reply_to #: telebot.async_telebot.AsyncTeleBot.reset_data @@ -410,6 +436,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_title #: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights #: telebot.async_telebot.AsyncTeleBot.set_my_description @@ -432,6 +459,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message #: telebot.async_telebot.AsyncTeleBot.upload_sticker_file #: telebot.asyncio_filters.TextFilter @@ -443,6 +471,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.add_data:10 #: telebot.async_telebot.AsyncTeleBot.callback_query_handler:9 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:17 +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler:8 #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:10 #: telebot.async_telebot.AsyncTeleBot.chat_member_handler:11 #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:10 @@ -457,14 +486,19 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.process_new_updates:8 #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:23 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:13 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:26 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:26 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:13 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:14 +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:8 #: telebot.async_telebot.AsyncTeleBot.reset_data:9 #: telebot.async_telebot.AsyncTeleBot.set_state:18 #: telebot.async_telebot.AsyncTeleBot.set_update_listener:15 @@ -587,6 +621,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 #: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:13 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:11 msgid "On success, True is returned." msgstr "" @@ -601,6 +636,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.close_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.copy_messages #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic #: telebot.async_telebot.AsyncTeleBot.create_invoice_link @@ -624,6 +660,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.edit_message_text #: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.forward_messages #: telebot.async_telebot.AsyncTeleBot.get_chat #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators #: telebot.async_telebot.AsyncTeleBot.get_chat_member @@ -638,6 +675,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info #: telebot.async_telebot.AsyncTeleBot.log_out @@ -689,6 +727,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message #: telebot.async_telebot.AsyncTeleBot.upload_sticker_file msgid "Return type" @@ -718,7 +757,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.leave_chat:8 #: telebot.async_telebot.AsyncTeleBot.log_out:11 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 -#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:70 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:26 @@ -729,6 +768,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_photo:16 #: telebot.async_telebot.AsyncTeleBot.set_chat_title:17 #: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:11 +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction:18 #: telebot.async_telebot.AsyncTeleBot.set_my_commands:18 #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:18 #: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:11 @@ -741,6 +781,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:12 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:14 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:12 #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:15 msgid ":obj:`bool`" msgstr "" @@ -980,6 +1021,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.ban_chat_member:12 #: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:11 #: telebot.async_telebot.AsyncTeleBot.get_chat_member:8 +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:8 #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:6 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:11 #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 @@ -995,7 +1037,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.delete_my_commands:16 #: telebot.async_telebot.AsyncTeleBot.log_out:10 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:18 -#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:60 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:69 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:17 #: telebot.async_telebot.AsyncTeleBot.set_chat_description:13 #: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:14 @@ -1050,6 +1092,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 #: telebot.async_telebot.AsyncTeleBot.delete_message:22 +#: telebot.async_telebot.AsyncTeleBot.delete_messages:14 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:6 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:25 @@ -1079,13 +1122,16 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.copy_message:5 +#: telebot.async_telebot.AsyncTeleBot.copy_messages:3 #: telebot.async_telebot.AsyncTeleBot.create_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_message:13 +#: telebot.async_telebot.AsyncTeleBot.delete_messages:8 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:13 #: telebot.async_telebot.AsyncTeleBot.forward_message:8 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:3 #: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:7 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:7 @@ -1127,18 +1173,22 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:4 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:10 +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler:4 #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:5 #: telebot.async_telebot.AsyncTeleBot.chat_member_handler:6 #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:5 #: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:10 #: telebot.async_telebot.AsyncTeleBot.edited_message_handler:11 #: telebot.async_telebot.AsyncTeleBot.inline_handler:4 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler:4 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler:4 #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:5 #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:5 #: telebot.async_telebot.AsyncTeleBot.poll_handler:4 #: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:4 #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:6 @@ -1146,17 +1196,22 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:15 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:4 #: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:4 msgid "Function executed as a filter" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:7 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:16 +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler:7 #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:8 #: telebot.async_telebot.AsyncTeleBot.chat_member_handler:9 #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:8 @@ -1164,12 +1219,15 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.edited_message_handler:20 #: telebot.async_telebot.AsyncTeleBot.inline_handler:7 #: telebot.async_telebot.AsyncTeleBot.message_handler:50 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler:7 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler:7 #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:8 #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:8 #: telebot.async_telebot.AsyncTeleBot.poll_handler:7 #: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:7 #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:11 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:12 @@ -1177,11 +1235,15 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:24 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:24 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:11 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:11 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:12 +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:7 #: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:7 msgid "Optional keyword arguments(custom filters)" msgstr "" @@ -1218,6 +1280,12 @@ msgstr "" msgid "Supported message content types. Must be a list. Defaults to ['text']." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.chat_boost_handler:1 +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostUpdated` object." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:1 msgid "" "Handles a request to join the chat has been sent. The bot must have the " @@ -1293,6 +1361,7 @@ msgid "" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.copy_message:1 +#: telebot.async_telebot.AsyncTeleBot.copy_messages:1 msgid "Use this method to copy messages of any kind." msgstr "" @@ -1301,7 +1370,9 @@ msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.copy_message:8 +#: telebot.async_telebot.AsyncTeleBot.copy_messages:6 #: telebot.async_telebot.AsyncTeleBot.forward_message:11 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:6 msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" @@ -1462,8 +1533,6 @@ msgid "Identifier of a message thread, in which the message will be sent" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.copy_message:45 -#: telebot.async_telebot.AsyncTeleBot.forward_message:26 -#: telebot.async_telebot.AsyncTeleBot.reply_to:11 #: telebot.async_telebot.AsyncTeleBot.send_animation:66 #: telebot.async_telebot.AsyncTeleBot.send_audio:66 #: telebot.async_telebot.AsyncTeleBot.send_contact:44 @@ -1472,6 +1541,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_game:32 #: telebot.async_telebot.AsyncTeleBot.send_invoice:101 #: telebot.async_telebot.AsyncTeleBot.send_location:49 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:30 #: telebot.async_telebot.AsyncTeleBot.send_message:46 #: telebot.async_telebot.AsyncTeleBot.send_photo:48 #: telebot.async_telebot.AsyncTeleBot.send_poll:70 @@ -1480,25 +1550,53 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.send_video:67 #: telebot.async_telebot.AsyncTeleBot.send_video_note:51 #: telebot.async_telebot.AsyncTeleBot.send_voice:49 -msgid "On success, the sent Message is returned." +msgid "Reply parameters." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.copy_message:46 -#: telebot.async_telebot.AsyncTeleBot.forward_message:27 -#: telebot.async_telebot.AsyncTeleBot.reply_to:12 -#: telebot.async_telebot.AsyncTeleBot.send_animation:67 -#: telebot.async_telebot.AsyncTeleBot.send_audio:67 -#: telebot.async_telebot.AsyncTeleBot.send_contact:45 -#: telebot.async_telebot.AsyncTeleBot.send_dice:36 -#: telebot.async_telebot.AsyncTeleBot.send_document:60 -#: telebot.async_telebot.AsyncTeleBot.send_location:50 -#: telebot.async_telebot.AsyncTeleBot.send_message:47 -#: telebot.async_telebot.AsyncTeleBot.send_photo:49 -#: telebot.async_telebot.AsyncTeleBot.send_sticker:43 -#: telebot.async_telebot.AsyncTeleBot.send_venue:58 -#: telebot.async_telebot.AsyncTeleBot.send_video:68 -#: telebot.async_telebot.AsyncTeleBot.send_video_note:52 -msgid ":class:`telebot.types.Message`" +#: of telebot.async_telebot.AsyncTeleBot.copy_message:48 +msgid "On success, the MessageId of the sent message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:49 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:22 +msgid ":class:`telebot.types.MessageID`" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:9 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:9 +msgid "Message identifiers in the chat specified in from_chat_id" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:12 +#: telebot.async_telebot.AsyncTeleBot.forward_message:5 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:12 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:15 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:15 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:27 +msgid "Identifier of a message thread, in which the messages will be sent" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:18 +#: telebot.async_telebot.AsyncTeleBot.forward_message:17 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:18 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:21 +msgid "Pass True to copy the messages without their captions" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:24 +msgid "On success, an array of MessageId of the sent messages is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:25 +msgid ":obj:`list` of :class:`telebot.types.MessageID`" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:1 @@ -1888,6 +1986,22 @@ msgstr "" msgid "Identifier of the message to delete" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.delete_messages:1 +msgid "" +"Use this method to delete multiple messages in a chat. The number of " +"messages to be deleted must not exceed 100. If the chat is a private " +"chat, the user must be an administrator of the chat for this to work and " +"must have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_messages:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessages" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_messages:11 +msgid "Identifiers of the messages to be deleted" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:1 msgid "" "Use this method to delete the list of the bot's commands for the given " @@ -2093,7 +2207,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:26 #: telebot.async_telebot.AsyncTeleBot.edit_message_media:22 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:17 -#: telebot.async_telebot.AsyncTeleBot.edit_message_text:29 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:32 msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." @@ -2222,7 +2336,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:23 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:18 -#: telebot.async_telebot.AsyncTeleBot.edit_message_text:30 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:33 #: telebot.async_telebot.AsyncTeleBot.set_game_score:27 msgid ":obj:`types.Message` or :obj:`bool`" msgstr "" @@ -2265,6 +2379,12 @@ msgstr "" msgid "Disables link previews for links in this message" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:29 +msgid "" +"A JSON-serialized object for options used to automatically generate " +"Telegram link previews for messages." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:1 msgid "" "Handles new version of a channel post that is known to the bot and was " @@ -2320,6 +2440,7 @@ msgid "exported invite link as String on success." msgstr "" #: of telebot.async_telebot.AsyncTeleBot.forward_message:1 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:1 msgid "Use this method to forward messages of any kind." msgstr "" @@ -2327,16 +2448,6 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 -msgid "" -"Sends the message silently. Users will receive a notification with no " -"sound" -msgstr "" - -#: of telebot.async_telebot.AsyncTeleBot.forward_message:17 -msgid "Protects the contents of the forwarded message from forwarding and saving" -msgstr "" - #: of telebot.async_telebot.AsyncTeleBot.forward_message:23 #: telebot.async_telebot.AsyncTeleBot.send_message:43 msgid "" @@ -2344,6 +2455,45 @@ msgid "" " forum supergroups only" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.forward_message:26 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:21 +#: telebot.async_telebot.AsyncTeleBot.reply_to:11 +#: telebot.async_telebot.AsyncTeleBot.send_animation:69 +#: telebot.async_telebot.AsyncTeleBot.send_audio:69 +#: telebot.async_telebot.AsyncTeleBot.send_contact:47 +#: telebot.async_telebot.AsyncTeleBot.send_dice:38 +#: telebot.async_telebot.AsyncTeleBot.send_document:62 +#: telebot.async_telebot.AsyncTeleBot.send_game:35 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:104 +#: telebot.async_telebot.AsyncTeleBot.send_location:52 +#: telebot.async_telebot.AsyncTeleBot.send_message:52 +#: telebot.async_telebot.AsyncTeleBot.send_photo:51 +#: telebot.async_telebot.AsyncTeleBot.send_poll:73 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:45 +#: telebot.async_telebot.AsyncTeleBot.send_venue:60 +#: telebot.async_telebot.AsyncTeleBot.send_video:70 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:54 +#: telebot.async_telebot.AsyncTeleBot.send_voice:52 +msgid "On success, the sent Message is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:27 +#: telebot.async_telebot.AsyncTeleBot.reply_to:12 +#: telebot.async_telebot.AsyncTeleBot.send_animation:70 +#: telebot.async_telebot.AsyncTeleBot.send_audio:70 +#: telebot.async_telebot.AsyncTeleBot.send_contact:48 +#: telebot.async_telebot.AsyncTeleBot.send_dice:39 +#: telebot.async_telebot.AsyncTeleBot.send_document:63 +#: telebot.async_telebot.AsyncTeleBot.send_location:53 +#: telebot.async_telebot.AsyncTeleBot.send_message:53 +#: telebot.async_telebot.AsyncTeleBot.send_photo:52 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:46 +#: telebot.async_telebot.AsyncTeleBot.send_venue:61 +#: telebot.async_telebot.AsyncTeleBot.send_video:71 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:55 +msgid ":class:`telebot.types.Message`" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.get_chat:1 msgid "" "Use this method to get up to date information about the chat (current " @@ -2359,6 +2509,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators:7 #: telebot.async_telebot.AsyncTeleBot.get_chat_member_count:5 #: telebot.async_telebot.AsyncTeleBot.leave_chat:5 +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction:5 msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" @@ -2740,6 +2891,36 @@ msgstr "" msgid ":obj:`list` of :class:`telebot.types.Update`" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:1 +msgid "" +"Use this method to get the list of boosts added to a chat by a user. " +"Requires administrator rights in the chat. Returns a UserChatBoosts " +"object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserchatboosts" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:5 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:10 +#: telebot.async_telebot.AsyncTeleBot.send_contact:5 +#: telebot.async_telebot.AsyncTeleBot.send_poll:6 +#: telebot.async_telebot.AsyncTeleBot.send_venue:5 +#: telebot.async_telebot.AsyncTeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:11 +msgid "On success, a UserChatBoosts object is returned." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:12 +msgid ":class:`telebot.types.UserChatBoosts`" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:1 msgid "" "Use this method to get a list of profile pictures for a user. Returns a " @@ -2942,6 +3123,19 @@ msgstr "" msgid "decorated function" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler:1 +msgid "" +"Handles new incoming message reaction count. As a parameter to the " +"decorator function, it passes " +":class:`telebot.types.MessageReactionCountUpdated` object." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.message_reaction_handler:1 +msgid "" +"Handles new incoming message reaction. As a parameter to the decorator " +"function, it passes :class:`telebot.types.MessageReactionUpdated` object." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:1 msgid "" "Handles update in a status of a bot. For private chats, this update is " @@ -3128,12 +3322,25 @@ msgid "" "forum topics, supergroups only" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:60 +msgid "Pass True if the administrator can create the channel's stories" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:63 +msgid "Pass True if the administrator can edit the channel's stories" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:66 +msgid "Pass True if the administrator can delete the channel's stories" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." msgstr "" #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:3 @@ -3141,16 +3348,20 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:3 msgid "function to be called" msgstr "" #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:18 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:9 @@ -3158,10 +3369,13 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:21 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:8 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:9 msgid "" "True if you need to pass TeleBot instance to handler(useful for " @@ -3185,6 +3399,10 @@ msgstr "" msgid "Regular expression" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:1 +msgid "Registers chat boost handler." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:1 msgid "Registers chat join request handler." msgstr "" @@ -3225,6 +3443,15 @@ msgstr "" msgid "List of chat types" msgstr "" +#: of +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:1 +msgid "Registers message reaction count handler." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:1 +msgid "Registers message reaction handler." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:1 msgid "Registers my chat member handler." msgstr "" @@ -3241,6 +3468,10 @@ msgstr "" msgid "Registers pre-checkout request handler." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:1 +msgid "Registers removed chat boost handler." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:1 msgid "Registers shipping query handler." msgstr "" @@ -3249,6 +3480,12 @@ msgstr "" msgid "Alternative for delete_webhook but uses set_webhook" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:1 +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostRemoved` object." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:1 msgid "" "Use this method to reopen a closed topic in a forum supergroup chat. The " @@ -3670,14 +3907,6 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:10 -#: telebot.async_telebot.AsyncTeleBot.send_contact:5 -#: telebot.async_telebot.AsyncTeleBot.send_poll:6 -#: telebot.async_telebot.AsyncTeleBot.send_venue:5 -#: telebot.async_telebot.AsyncTeleBot.stop_poll:5 -msgid "Unique identifier for the target chat or username of the target channel" -msgstr "" - #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:13 msgid "" "Type of action to broadcast. Choose one, depending on what the user is " @@ -3843,9 +4072,9 @@ msgstr "" msgid "Identifier of the thread to which the message will be sent." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.send_game:33 -#: telebot.async_telebot.AsyncTeleBot.send_invoice:102 -#: telebot.async_telebot.AsyncTeleBot.send_poll:71 +#: of telebot.async_telebot.AsyncTeleBot.send_game:36 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:105 +#: telebot.async_telebot.AsyncTeleBot.send_poll:74 msgid ":obj:`types.Message`" msgstr "" @@ -3953,11 +4182,7 @@ msgid "" "sound." msgstr "" -#: of telebot.async_telebot.AsyncTeleBot.send_media_group:27 -msgid "Identifier of a message thread, in which the messages will be sent" -msgstr "" - -#: of telebot.async_telebot.AsyncTeleBot.send_media_group:30 +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:33 msgid "On success, an array of Messages that were sent is returned." msgstr "" @@ -3992,6 +4217,10 @@ msgid "" "target user" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.send_message:49 +msgid "Options for previewing links." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_photo:1 msgid "Use this method to send photos. On success, the sent Message is returned." msgstr "" @@ -4471,6 +4700,35 @@ msgid "" "Message, otherwise returns True." msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:1 +msgid "" +"Use this method to set a reaction to a message in a chat. The bot must be" +" an administrator in the chat for this to work and must have the " +"appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmessagereaction" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:8 +msgid "Identifier of the message to set reaction to" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:11 +msgid "" +"New list of reaction types to set on the message. Currently, as non-" +"premium users, bots can set up to one reaction per message. A custom " +"emoji reaction can be used if it is either already present on the message" +" or explicitly allowed by chat administrators." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:15 +msgid "Pass True to set the reaction with a big animation" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." msgstr "" @@ -4952,6 +5210,27 @@ msgstr "" msgid "Identifier of the topic" msgstr "" +#: of +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:1 +msgid "" +"Use this method to clear the list of pinned messages in a General forum " +"topic. The bot must be an administrator in the chat for this to work and " +"must have the can_pin_messages administrator right in the supergroup. " +"Returns True on success." +msgstr "" + +#: of +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages" +msgstr "" + +#: of +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:8 +msgid "Unique identifier for the target chat or username of chat" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:1 msgid "" "Use this method to unpin specific pinned message in a supergroup chat. " diff --git a/docs/source/locales/en/LC_MESSAGES/sync_version.po b/docs/source/locales/en/LC_MESSAGES/sync_version.po index 13aa6b3cf..30d0fb92f 100644 --- a/docs/source/locales/en/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/en/LC_MESSAGES/sync_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-08 23:07+0500\n" +"POT-Creation-Date: 2024-01-05 22:16+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -98,7 +98,7 @@ msgstr "" #: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query #: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member #: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler -#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.channel_post_handler telebot.TeleBot.chat_boost_handler #: telebot.TeleBot.chat_join_request_handler #: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler #: telebot.TeleBot.clear_reply_handlers @@ -106,16 +106,17 @@ msgstr "" #: telebot.TeleBot.clear_step_handler #: telebot.TeleBot.clear_step_handler_by_chat_id #: telebot.TeleBot.close_forum_topic telebot.TeleBot.close_general_forum_topic -#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link -#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link -#: telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.copy_message telebot.TeleBot.copy_messages +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic -#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands -#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook -#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link -#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_general_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_messages +#: telebot.TeleBot.delete_my_commands telebot.TeleBot.delete_state +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_sticker_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_general_forum_topic #: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup @@ -125,9 +126,10 @@ msgstr "" #: telebot.TeleBot.enable_save_next_step_handlers #: telebot.TeleBot.enable_save_reply_handlers #: telebot.TeleBot.enable_saving_states telebot.TeleBot.export_chat_invite_link -#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat -#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member -#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.forward_message telebot.TeleBot.forward_messages +#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators +#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count +#: telebot.TeleBot.get_chat_menu_button #: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file #: telebot.TeleBot.get_file_url telebot.TeleBot.get_game_high_scores #: telebot.TeleBot.get_my_commands @@ -135,17 +137,20 @@ msgstr "" #: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name #: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state #: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates -#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info -#: telebot.TeleBot.hide_general_forum_topic telebot.TeleBot.infinity_polling -#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat -#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers -#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.get_user_chat_boosts telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.hide_general_forum_topic +#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler +#: telebot.TeleBot.leave_chat telebot.TeleBot.load_next_step_handlers +#: telebot.TeleBot.load_reply_handlers telebot.TeleBot.message_handler +#: telebot.TeleBot.message_reaction_count_handler +#: telebot.TeleBot.message_reaction_handler telebot.TeleBot.middleware_handler #: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message #: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler #: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_boost_handler #: telebot.TeleBot.register_chat_join_request_handler #: telebot.TeleBot.register_chat_member_handler #: telebot.TeleBot.register_chosen_inline_handler @@ -155,6 +160,8 @@ msgstr "" #: telebot.TeleBot.register_for_reply_by_message_id #: telebot.TeleBot.register_inline_handler #: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_message_reaction_count_handler +#: telebot.TeleBot.register_message_reaction_handler #: telebot.TeleBot.register_middleware_handler #: telebot.TeleBot.register_my_chat_member_handler #: telebot.TeleBot.register_next_step_handler @@ -162,7 +169,9 @@ msgstr "" #: telebot.TeleBot.register_poll_answer_handler #: telebot.TeleBot.register_poll_handler #: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_removed_chat_boost_handler #: telebot.TeleBot.register_shipping_query_handler +#: telebot.TeleBot.removed_chat_boost_handler #: telebot.TeleBot.reopen_forum_topic #: telebot.TeleBot.reopen_general_forum_topic telebot.TeleBot.reply_to #: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member @@ -175,13 +184,15 @@ msgstr "" #: telebot.TeleBot.send_media_group telebot.TeleBot.send_message #: telebot.TeleBot.send_photo telebot.TeleBot.send_poll #: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_voice +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note +#: telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail -#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_message_reaction +#: telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights #: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name #: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state @@ -197,6 +208,7 @@ msgstr "" #: telebot.TeleBot.unhide_general_forum_topic #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_all_general_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file #: telebot.custom_filters.TextFilter msgid "Parameters" @@ -312,7 +324,7 @@ msgstr "" #: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query #: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member #: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler -#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.channel_post_handler telebot.TeleBot.chat_boost_handler #: telebot.TeleBot.chat_join_request_handler #: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler #: telebot.TeleBot.clear_reply_handlers @@ -320,15 +332,17 @@ msgstr "" #: telebot.TeleBot.clear_step_handler #: telebot.TeleBot.clear_step_handler_by_chat_id telebot.TeleBot.close #: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message -#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic -#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.copy_messages telebot.TeleBot.create_chat_invite_link +#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link +#: telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic -#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands -#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook -#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link -#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_messages +#: telebot.TeleBot.delete_my_commands telebot.TeleBot.delete_state +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_sticker_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text @@ -336,9 +350,9 @@ msgstr "" #: telebot.TeleBot.edited_message_handler #: telebot.TeleBot.enable_save_next_step_handlers #: telebot.TeleBot.export_chat_invite_link telebot.TeleBot.forward_message -#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators -#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count -#: telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.forward_messages telebot.TeleBot.get_chat +#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member +#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button #: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file #: telebot.TeleBot.get_file_url telebot.TeleBot.get_forum_topic_icon_stickers #: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands @@ -346,16 +360,19 @@ msgstr "" #: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name #: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state #: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates -#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info -#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler -#: telebot.TeleBot.leave_chat telebot.TeleBot.log_out -#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.get_user_chat_boosts telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.log_out telebot.TeleBot.message_handler +#: telebot.TeleBot.message_reaction_count_handler +#: telebot.TeleBot.message_reaction_handler telebot.TeleBot.middleware_handler #: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message #: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler #: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_boost_handler #: telebot.TeleBot.register_chat_join_request_handler #: telebot.TeleBot.register_chosen_inline_handler #: telebot.TeleBot.register_edited_channel_post_handler @@ -364,6 +381,8 @@ msgstr "" #: telebot.TeleBot.register_for_reply_by_message_id #: telebot.TeleBot.register_inline_handler #: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_message_reaction_count_handler +#: telebot.TeleBot.register_message_reaction_handler #: telebot.TeleBot.register_middleware_handler #: telebot.TeleBot.register_my_chat_member_handler #: telebot.TeleBot.register_next_step_handler @@ -371,25 +390,28 @@ msgstr "" #: telebot.TeleBot.register_poll_answer_handler #: telebot.TeleBot.register_poll_handler #: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_removed_chat_boost_handler #: telebot.TeleBot.register_shipping_query_handler -#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic -#: telebot.TeleBot.reply_to telebot.TeleBot.reset_data -#: telebot.TeleBot.restrict_chat_member telebot.TeleBot.retrieve_data -#: telebot.TeleBot.revoke_chat_invite_link telebot.TeleBot.send_animation -#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action -#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice -#: telebot.TeleBot.send_document telebot.TeleBot.send_game -#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location -#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message -#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll -#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_voice +#: telebot.TeleBot.remove_webhook telebot.TeleBot.removed_chat_boost_handler +#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member +#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link +#: telebot.TeleBot.send_animation telebot.TeleBot.send_audio +#: telebot.TeleBot.send_chat_action telebot.TeleBot.send_contact +#: telebot.TeleBot.send_dice telebot.TeleBot.send_document +#: telebot.TeleBot.send_game telebot.TeleBot.send_invoice +#: telebot.TeleBot.send_location telebot.TeleBot.send_media_group +#: telebot.TeleBot.send_message telebot.TeleBot.send_photo +#: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker +#: telebot.TeleBot.send_venue telebot.TeleBot.send_video +#: telebot.TeleBot.send_video_note telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail -#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_message_reaction +#: telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights #: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name #: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state @@ -403,6 +425,7 @@ msgstr "" #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_all_general_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file #: telebot.TeleBot.user telebot.custom_filters.TextFilter #: telebot.ext.sync.webhooks.SyncWebhookListener.run_app @@ -410,7 +433,7 @@ msgid "Returns" msgstr "" #: of telebot.TeleBot.add_data:10 telebot.TeleBot.callback_query_handler:9 -#: telebot.TeleBot.channel_post_handler:18 +#: telebot.TeleBot.channel_post_handler:18 telebot.TeleBot.chat_boost_handler:8 #: telebot.TeleBot.chat_join_request_handler:10 #: telebot.TeleBot.chat_member_handler:11 #: telebot.TeleBot.chosen_inline_handler:10 @@ -425,21 +448,26 @@ msgstr "" #: telebot.TeleBot.pre_checkout_query_handler:9 #: telebot.TeleBot.register_callback_query_handler:14 #: telebot.TeleBot.register_channel_post_handler:23 +#: telebot.TeleBot.register_chat_boost_handler:13 #: telebot.TeleBot.register_chat_join_request_handler:14 #: telebot.TeleBot.register_chosen_inline_handler:14 #: telebot.TeleBot.register_edited_message_handler:26 #: telebot.TeleBot.register_for_reply:15 #: telebot.TeleBot.register_for_reply_by_message_id:15 #: telebot.TeleBot.register_message_handler:26 +#: telebot.TeleBot.register_message_reaction_count_handler:14 +#: telebot.TeleBot.register_message_reaction_handler:14 #: telebot.TeleBot.register_middleware_handler:18 #: telebot.TeleBot.register_my_chat_member_handler:14 #: telebot.TeleBot.register_next_step_handler:15 #: telebot.TeleBot.register_next_step_handler_by_chat_id:15 #: telebot.TeleBot.register_poll_answer_handler:14 #: telebot.TeleBot.register_poll_handler:14 +#: telebot.TeleBot.register_removed_chat_boost_handler:13 #: telebot.TeleBot.register_shipping_query_handler:14 -#: telebot.TeleBot.reset_data:9 telebot.TeleBot.set_state:18 -#: telebot.TeleBot.setup_middleware:5 telebot.TeleBot.shipping_query_handler:9 +#: telebot.TeleBot.removed_chat_boost_handler:8 telebot.TeleBot.reset_data:9 +#: telebot.TeleBot.set_state:18 telebot.TeleBot.setup_middleware:5 +#: telebot.TeleBot.shipping_query_handler:9 #: telebot.custom_filters.TextFilter:22 #: telebot.ext.sync.webhooks.SyncWebhookListener.run_app:4 msgid "None" @@ -530,6 +558,7 @@ msgstr "" #: telebot.TeleBot.set_sticker_position_in_set:11 #: telebot.TeleBot.set_sticker_set_thumbnail:15 #: telebot.TeleBot.unpin_all_forum_topic_messages:13 +#: telebot.TeleBot.unpin_all_general_forum_topic_messages:11 msgid "On success, True is returned." msgstr "" @@ -539,9 +568,9 @@ msgstr "" #: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query #: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member #: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.close_forum_topic -#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link -#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link -#: telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.copy_message telebot.TeleBot.copy_messages +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands @@ -552,28 +581,30 @@ msgstr "" #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text telebot.TeleBot.export_chat_invite_link -#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat -#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member -#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.forward_message telebot.TeleBot.forward_messages +#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators +#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count +#: telebot.TeleBot.get_chat_menu_button #: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file_url #: telebot.TeleBot.get_forum_topic_icon_stickers #: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands #: telebot.TeleBot.get_my_default_administrator_rights #: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set -#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.log_out -#: telebot.TeleBot.pin_chat_message telebot.TeleBot.promote_chat_member -#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic -#: telebot.TeleBot.reply_to telebot.TeleBot.restrict_chat_member -#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link -#: telebot.TeleBot.send_animation telebot.TeleBot.send_audio -#: telebot.TeleBot.send_chat_action telebot.TeleBot.send_contact -#: telebot.TeleBot.send_dice telebot.TeleBot.send_document -#: telebot.TeleBot.send_game telebot.TeleBot.send_invoice -#: telebot.TeleBot.send_location telebot.TeleBot.send_media_group -#: telebot.TeleBot.send_message telebot.TeleBot.send_photo -#: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker -#: telebot.TeleBot.send_venue telebot.TeleBot.send_video +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_chat_boosts +#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info +#: telebot.TeleBot.log_out telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.promote_chat_member telebot.TeleBot.remove_webhook +#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.restrict_chat_member telebot.TeleBot.retrieve_data +#: telebot.TeleBot.revoke_chat_invite_link telebot.TeleBot.send_animation +#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action +#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice +#: telebot.TeleBot.send_document telebot.TeleBot.send_game +#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location +#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message +#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll +#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo @@ -590,6 +621,7 @@ msgstr "" #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_all_general_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file #: telebot.TeleBot.user msgid "Return type" @@ -613,18 +645,16 @@ msgstr "" #: telebot.TeleBot.delete_sticker_set:7 telebot.TeleBot.delete_webhook:13 #: telebot.TeleBot.edit_forum_topic:23 telebot.TeleBot.leave_chat:8 #: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 -#: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 +#: telebot.TeleBot.promote_chat_member:70 telebot.TeleBot.remove_webhook:4 #: telebot.TeleBot.reopen_forum_topic:14 #: telebot.TeleBot.restrict_chat_member:61 telebot.TeleBot.send_chat_action:26 -#: telebot.TeleBot.send_video_note:28 telebot.TeleBot.send_video_note:40 -#: telebot.TeleBot.send_video_note:43 #: telebot.TeleBot.set_chat_administrator_custom_title:18 #: telebot.TeleBot.set_chat_description:14 #: telebot.TeleBot.set_chat_menu_button:15 #: telebot.TeleBot.set_chat_permissions:21 telebot.TeleBot.set_chat_photo:16 #: telebot.TeleBot.set_chat_title:17 #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:11 -#: telebot.TeleBot.set_my_commands:18 +#: telebot.TeleBot.set_message_reaction:18 telebot.TeleBot.set_my_commands:18 #: telebot.TeleBot.set_my_default_administrator_rights:18 #: telebot.TeleBot.set_sticker_emoji_list:11 #: telebot.TeleBot.set_sticker_keywords:12 @@ -636,6 +666,7 @@ msgstr "" #: telebot.TeleBot.unban_chat_sender_chat:15 #: telebot.TeleBot.unpin_all_chat_messages:12 #: telebot.TeleBot.unpin_all_forum_topic_messages:14 +#: telebot.TeleBot.unpin_all_general_forum_topic_messages:12 #: telebot.TeleBot.unpin_chat_message:15 msgid ":obj:`bool`" msgstr "" @@ -873,7 +904,8 @@ msgstr "" #: of telebot.TeleBot.approve_chat_join_request:11 #: telebot.TeleBot.ban_chat_member:12 #: telebot.TeleBot.decline_chat_join_request:11 -#: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_profile_photos:6 +#: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_chat_boosts:8 +#: telebot.TeleBot.get_user_profile_photos:6 #: telebot.TeleBot.promote_chat_member:11 #: telebot.TeleBot.restrict_chat_member:14 #: telebot.TeleBot.set_chat_administrator_custom_title:10 @@ -886,7 +918,7 @@ msgstr "" #: telebot.TeleBot.decline_chat_join_request:14 #: telebot.TeleBot.delete_chat_photo:12 telebot.TeleBot.delete_my_commands:16 #: telebot.TeleBot.log_out:10 telebot.TeleBot.pin_chat_message:18 -#: telebot.TeleBot.promote_chat_member:60 telebot.TeleBot.remove_webhook:3 +#: telebot.TeleBot.promote_chat_member:69 telebot.TeleBot.remove_webhook:3 #: telebot.TeleBot.set_chat_administrator_custom_title:17 #: telebot.TeleBot.set_chat_description:13 #: telebot.TeleBot.set_chat_menu_button:14 telebot.TeleBot.set_chat_photo:15 @@ -935,8 +967,8 @@ msgstr "" #: of telebot.TeleBot.ban_chat_member:24 #: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 -#: telebot.TeleBot.delete_sticker_set:6 telebot.TeleBot.delete_webhook:12 -#: telebot.TeleBot.send_chat_action:25 +#: telebot.TeleBot.delete_messages:14 telebot.TeleBot.delete_sticker_set:6 +#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:25 #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:10 #: telebot.TeleBot.set_sticker_emoji_list:10 #: telebot.TeleBot.set_sticker_mask_position:11 @@ -962,11 +994,13 @@ msgstr "" #: of telebot.TeleBot.ban_chat_sender_chat:10 #: telebot.TeleBot.close_forum_topic:7 #: telebot.TeleBot.close_general_forum_topic:7 telebot.TeleBot.copy_message:5 -#: telebot.TeleBot.create_forum_topic:7 telebot.TeleBot.delete_forum_topic:7 -#: telebot.TeleBot.delete_message:13 telebot.TeleBot.edit_forum_topic:7 +#: telebot.TeleBot.copy_messages:3 telebot.TeleBot.create_forum_topic:7 +#: telebot.TeleBot.delete_forum_topic:7 telebot.TeleBot.delete_message:13 +#: telebot.TeleBot.delete_messages:8 telebot.TeleBot.edit_forum_topic:7 #: telebot.TeleBot.edit_general_forum_topic:7 #: telebot.TeleBot.edit_message_live_location:13 -#: telebot.TeleBot.forward_message:8 telebot.TeleBot.hide_general_forum_topic:7 +#: telebot.TeleBot.forward_message:8 telebot.TeleBot.forward_messages:3 +#: telebot.TeleBot.hide_general_forum_topic:7 #: telebot.TeleBot.pin_chat_message:7 telebot.TeleBot.reopen_forum_topic:7 #: telebot.TeleBot.reopen_general_forum_topic:7 #: telebot.TeleBot.send_animation:6 telebot.TeleBot.send_audio:9 @@ -998,17 +1032,20 @@ msgid "" msgstr "" #: of telebot.TeleBot.callback_query_handler:4 -#: telebot.TeleBot.channel_post_handler:10 +#: telebot.TeleBot.channel_post_handler:10 telebot.TeleBot.chat_boost_handler:4 #: telebot.TeleBot.chat_join_request_handler:5 #: telebot.TeleBot.chat_member_handler:6 #: telebot.TeleBot.chosen_inline_handler:5 #: telebot.TeleBot.edited_channel_post_handler:10 #: telebot.TeleBot.edited_message_handler:10 telebot.TeleBot.inline_handler:4 +#: telebot.TeleBot.message_reaction_count_handler:4 +#: telebot.TeleBot.message_reaction_handler:4 #: telebot.TeleBot.my_chat_member_handler:5 #: telebot.TeleBot.poll_answer_handler:5 telebot.TeleBot.poll_handler:4 #: telebot.TeleBot.pre_checkout_query_handler:4 #: telebot.TeleBot.register_callback_query_handler:6 #: telebot.TeleBot.register_channel_post_handler:15 +#: telebot.TeleBot.register_chat_boost_handler:6 #: telebot.TeleBot.register_chat_join_request_handler:6 #: telebot.TeleBot.register_chat_member_handler:6 #: telebot.TeleBot.register_chosen_inline_handler:6 @@ -1016,27 +1053,35 @@ msgstr "" #: telebot.TeleBot.register_edited_message_handler:15 #: telebot.TeleBot.register_inline_handler:6 #: telebot.TeleBot.register_message_handler:15 +#: telebot.TeleBot.register_message_reaction_count_handler:6 +#: telebot.TeleBot.register_message_reaction_handler:6 #: telebot.TeleBot.register_my_chat_member_handler:6 #: telebot.TeleBot.register_poll_answer_handler:6 #: telebot.TeleBot.register_poll_handler:6 #: telebot.TeleBot.register_pre_checkout_query_handler:6 +#: telebot.TeleBot.register_removed_chat_boost_handler:6 #: telebot.TeleBot.register_shipping_query_handler:6 +#: telebot.TeleBot.removed_chat_boost_handler:4 #: telebot.TeleBot.shipping_query_handler:4 msgid "Function executed as a filter" msgstr "" #: of telebot.TeleBot.callback_query_handler:7 -#: telebot.TeleBot.channel_post_handler:16 +#: telebot.TeleBot.channel_post_handler:16 telebot.TeleBot.chat_boost_handler:7 #: telebot.TeleBot.chat_join_request_handler:8 #: telebot.TeleBot.chat_member_handler:9 #: telebot.TeleBot.chosen_inline_handler:8 #: telebot.TeleBot.edited_channel_post_handler:16 #: telebot.TeleBot.edited_message_handler:19 telebot.TeleBot.inline_handler:7 -#: telebot.TeleBot.message_handler:50 telebot.TeleBot.my_chat_member_handler:8 +#: telebot.TeleBot.message_handler:50 +#: telebot.TeleBot.message_reaction_count_handler:7 +#: telebot.TeleBot.message_reaction_handler:7 +#: telebot.TeleBot.my_chat_member_handler:8 #: telebot.TeleBot.poll_answer_handler:8 telebot.TeleBot.poll_handler:7 #: telebot.TeleBot.pre_checkout_query_handler:7 #: telebot.TeleBot.register_callback_query_handler:12 #: telebot.TeleBot.register_channel_post_handler:21 +#: telebot.TeleBot.register_chat_boost_handler:11 #: telebot.TeleBot.register_chat_join_request_handler:12 #: telebot.TeleBot.register_chat_member_handler:12 #: telebot.TeleBot.register_chosen_inline_handler:12 @@ -1044,11 +1089,15 @@ msgstr "" #: telebot.TeleBot.register_edited_message_handler:24 #: telebot.TeleBot.register_inline_handler:12 #: telebot.TeleBot.register_message_handler:24 +#: telebot.TeleBot.register_message_reaction_count_handler:12 +#: telebot.TeleBot.register_message_reaction_handler:12 #: telebot.TeleBot.register_my_chat_member_handler:12 #: telebot.TeleBot.register_poll_answer_handler:12 #: telebot.TeleBot.register_poll_handler:12 #: telebot.TeleBot.register_pre_checkout_query_handler:11 +#: telebot.TeleBot.register_removed_chat_boost_handler:11 #: telebot.TeleBot.register_shipping_query_handler:12 +#: telebot.TeleBot.removed_chat_boost_handler:7 #: telebot.TeleBot.shipping_query_handler:7 msgid "Optional keyword arguments(custom filters)" msgstr "" @@ -1082,6 +1131,12 @@ msgstr "" msgid "Supported message content types. Must be a list. Defaults to ['text']." msgstr "" +#: of telebot.TeleBot.chat_boost_handler:1 +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostUpdated` object." +msgstr "" + #: of telebot.TeleBot.chat_join_request_handler:1 msgid "" "Handles a request to join the chat has been sent. The bot must have the " @@ -1163,6 +1218,10 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" msgstr "" +#: of telebot.TeleBot.close_forum_topic +msgid "aram chat_id" +msgstr "" + #: of telebot.TeleBot.close_forum_topic:10 msgid "Identifier of the topic to close" msgstr "" @@ -1180,7 +1239,7 @@ msgid "" "https://core.telegram.org/bots/api#closegeneralforumtopic" msgstr "" -#: of telebot.TeleBot.copy_message:1 +#: of telebot.TeleBot.copy_message:1 telebot.TeleBot.copy_messages:1 msgid "Use this method to copy messages of any kind." msgstr "" @@ -1188,7 +1247,8 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" msgstr "" -#: of telebot.TeleBot.copy_message:8 telebot.TeleBot.forward_message:11 +#: of telebot.TeleBot.copy_message:8 telebot.TeleBot.copy_messages:6 +#: telebot.TeleBot.forward_message:11 telebot.TeleBot.forward_messages:6 msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" @@ -1214,62 +1274,61 @@ msgid "" "caption, which can be specified instead of parse_mode" msgstr "" -#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 -#: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 -#: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:26 +#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:48 +#: telebot.TeleBot.send_audio:36 telebot.TeleBot.send_contact:20 +#: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:23 #: telebot.TeleBot.send_game:11 telebot.TeleBot.send_invoice:67 -#: telebot.TeleBot.send_location:25 telebot.TeleBot.send_message:24 +#: telebot.TeleBot.send_location:22 telebot.TeleBot.send_message:24 #: telebot.TeleBot.send_photo:22 telebot.TeleBot.send_poll:44 #: telebot.TeleBot.send_venue:27 telebot.TeleBot.send_video:35 -#: telebot.TeleBot.send_video_note:27 telebot.TeleBot.send_voice:31 +#: telebot.TeleBot.send_video_note:24 telebot.TeleBot.send_voice:28 msgid "" "Sends the message silently. Users will receive a notification with no " "sound." msgstr "" #: of telebot.TeleBot.copy_message:25 telebot.TeleBot.send_animation:34 -#: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:38 -#: telebot.TeleBot.send_document:50 telebot.TeleBot.send_invoice:95 +#: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:37 +#: telebot.TeleBot.send_dice:29 telebot.TeleBot.send_document:50 +#: telebot.TeleBot.send_game:26 telebot.TeleBot.send_invoice:95 #: telebot.TeleBot.send_location:43 telebot.TeleBot.send_media_group:15 -#: telebot.TeleBot.send_photo:25 telebot.TeleBot.send_poll:64 -#: telebot.TeleBot.send_sticker:30 telebot.TeleBot.send_venue:51 -#: telebot.TeleBot.send_video:38 telebot.TeleBot.send_video_note:42 -#: telebot.TeleBot.send_voice:43 +#: telebot.TeleBot.send_message:27 telebot.TeleBot.send_photo:25 +#: telebot.TeleBot.send_poll:64 telebot.TeleBot.send_sticker:30 +#: telebot.TeleBot.send_venue:50 telebot.TeleBot.send_video:38 +#: telebot.TeleBot.send_video_note:42 telebot.TeleBot.send_voice:43 msgid "Protects the contents of the sent message from forwarding and saving" msgstr "" -#: of telebot.TeleBot.copy_message:28 telebot.TeleBot.send_animation:37 -#: telebot.TeleBot.send_audio:29 telebot.TeleBot.send_contact:23 -#: telebot.TeleBot.send_dice:15 telebot.TeleBot.send_document:12 -#: telebot.TeleBot.send_game:14 telebot.TeleBot.send_invoice:70 -#: telebot.TeleBot.send_location:17 telebot.TeleBot.send_media_group:18 -#: telebot.TeleBot.send_message:30 telebot.TeleBot.send_photo:28 -#: telebot.TeleBot.send_poll:47 telebot.TeleBot.send_sticker:13 -#: telebot.TeleBot.send_venue:30 telebot.TeleBot.send_video:41 -#: telebot.TeleBot.send_video_note:19 telebot.TeleBot.send_voice:20 -msgid "If the message is a reply, ID of the original message" -msgstr "" - -#: of telebot.TeleBot.copy_message:31 telebot.TeleBot.send_animation:54 -#: telebot.TeleBot.send_audio:54 telebot.TeleBot.send_dice:26 -#: telebot.TeleBot.send_document:38 telebot.TeleBot.send_invoice:84 -#: telebot.TeleBot.send_location:40 telebot.TeleBot.send_media_group:24 -#: telebot.TeleBot.send_message:33 telebot.TeleBot.send_photo:31 -#: telebot.TeleBot.send_sticker:27 telebot.TeleBot.send_video:44 -#: telebot.TeleBot.send_video_note:39 telebot.TeleBot.send_voice:40 -msgid "" -"Pass True, if the message should be sent even if the specified replied-to" -" message is not found" -msgstr "" - -#: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:40 -#: telebot.TeleBot.send_contact:26 telebot.TeleBot.send_dice:18 -#: telebot.TeleBot.send_document:18 telebot.TeleBot.send_game:17 -#: telebot.TeleBot.send_location:20 telebot.TeleBot.send_message:36 +#: of telebot.TeleBot.copy_message:28 telebot.TeleBot.copy_message:31 +#: telebot.TeleBot.edit_message_text:23 telebot.TeleBot.polling:44 +#: telebot.TeleBot.send_animation:37 telebot.TeleBot.send_animation:40 +#: telebot.TeleBot.send_animation:63 telebot.TeleBot.send_audio:39 +#: telebot.TeleBot.send_audio:42 telebot.TeleBot.send_audio:63 +#: telebot.TeleBot.send_contact:23 telebot.TeleBot.send_contact:26 +#: telebot.TeleBot.send_dice:20 telebot.TeleBot.send_dice:23 +#: telebot.TeleBot.send_document:26 telebot.TeleBot.send_document:29 +#: telebot.TeleBot.send_document:56 telebot.TeleBot.send_location:25 +#: telebot.TeleBot.send_location:28 telebot.TeleBot.send_media_group:18 +#: telebot.TeleBot.send_media_group:21 telebot.TeleBot.send_message:21 +#: telebot.TeleBot.send_message:30 telebot.TeleBot.send_message:33 +#: telebot.TeleBot.send_photo:28 telebot.TeleBot.send_photo:31 +#: telebot.TeleBot.send_sticker:21 telebot.TeleBot.send_sticker:24 +#: telebot.TeleBot.send_venue:30 telebot.TeleBot.send_venue:33 +#: telebot.TeleBot.send_video:41 telebot.TeleBot.send_video:44 +#: telebot.TeleBot.send_video:64 telebot.TeleBot.send_video_note:27 +#: telebot.TeleBot.send_video_note:30 telebot.TeleBot.send_video_note:48 +#: telebot.TeleBot.send_voice:31 telebot.TeleBot.send_voice:34 +msgid "deprecated." +msgstr "" + +#: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:43 +#: telebot.TeleBot.send_contact:29 telebot.TeleBot.send_dice:15 +#: telebot.TeleBot.send_document:15 telebot.TeleBot.send_game:20 +#: telebot.TeleBot.send_location:17 telebot.TeleBot.send_message:36 #: telebot.TeleBot.send_photo:34 telebot.TeleBot.send_poll:53 -#: telebot.TeleBot.send_sticker:16 telebot.TeleBot.send_venue:33 -#: telebot.TeleBot.send_video:47 telebot.TeleBot.send_video_note:22 -#: telebot.TeleBot.send_voice:23 +#: telebot.TeleBot.send_sticker:13 telebot.TeleBot.send_venue:36 +#: telebot.TeleBot.send_video:47 telebot.TeleBot.send_video_note:19 +#: telebot.TeleBot.send_voice:20 msgid "" "Additional interface options. A JSON-serialized object for an inline " "keyboard, custom reply keyboard, instructions to remove reply keyboard or" @@ -1278,14 +1337,14 @@ msgstr "" #: of telebot.TeleBot.copy_message:39 telebot.TeleBot.delete_message:19 #: telebot.TeleBot.edit_message_live_location:23 -#: telebot.TeleBot.forward_message:20 telebot.TeleBot.send_animation:48 -#: telebot.TeleBot.send_audio:42 telebot.TeleBot.send_chat_action:19 -#: telebot.TeleBot.send_contact:31 telebot.TeleBot.send_dice:23 -#: telebot.TeleBot.send_document:29 telebot.TeleBot.send_location:28 -#: telebot.TeleBot.send_media_group:21 telebot.TeleBot.send_message:40 -#: telebot.TeleBot.send_photo:39 telebot.TeleBot.send_sticker:24 -#: telebot.TeleBot.send_venue:38 telebot.TeleBot.send_video:52 -#: telebot.TeleBot.send_video_note:30 telebot.TeleBot.send_voice:34 +#: telebot.TeleBot.forward_message:20 telebot.TeleBot.send_animation:51 +#: telebot.TeleBot.send_audio:45 telebot.TeleBot.send_chat_action:19 +#: telebot.TeleBot.send_contact:34 telebot.TeleBot.send_dice:26 +#: telebot.TeleBot.send_document:32 telebot.TeleBot.send_location:31 +#: telebot.TeleBot.send_media_group:24 telebot.TeleBot.send_message:40 +#: telebot.TeleBot.send_photo:39 telebot.TeleBot.send_sticker:27 +#: telebot.TeleBot.send_venue:41 telebot.TeleBot.send_video:52 +#: telebot.TeleBot.send_video_note:33 telebot.TeleBot.send_voice:37 #: telebot.TeleBot.stop_message_live_location:19 msgid "Timeout in seconds for the request." msgstr "" @@ -1297,26 +1356,49 @@ msgstr "" msgid "Identifier of a message thread, in which the message will be sent" msgstr "" -#: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 -#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:66 -#: telebot.TeleBot.send_audio:66 telebot.TeleBot.send_contact:44 -#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:59 -#: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 -#: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 -#: telebot.TeleBot.send_photo:48 telebot.TeleBot.send_poll:70 -#: telebot.TeleBot.send_sticker:42 telebot.TeleBot.send_venue:57 -#: telebot.TeleBot.send_video:67 telebot.TeleBot.send_voice:49 -msgid "On success, the sent Message is returned." +#: of telebot.TeleBot.copy_message:45 telebot.TeleBot.send_dice:35 +#: telebot.TeleBot.send_photo:48 +msgid "Additional parameters for replies to messages" msgstr "" -#: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 -#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:67 -#: telebot.TeleBot.send_audio:67 telebot.TeleBot.send_contact:45 -#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:60 -#: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 -#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:43 -#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:68 -msgid ":class:`telebot.types.Message`" +#: of telebot.TeleBot.copy_message:48 +msgid "On success, the MessageId of the sent message is returned." +msgstr "" + +#: of telebot.TeleBot.copy_message:49 telebot.TeleBot.forward_messages:22 +msgid ":class:`telebot.types.MessageID`" +msgstr "" + +#: of telebot.TeleBot.copy_messages:9 telebot.TeleBot.forward_messages:9 +msgid "Message identifiers in the chat specified in from_chat_id" +msgstr "" + +#: of telebot.TeleBot.copy_messages:12 telebot.TeleBot.forward_message:5 +#: telebot.TeleBot.forward_messages:12 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" +msgstr "" + +#: of telebot.TeleBot.copy_messages:15 telebot.TeleBot.forward_messages:15 +msgid "Identifier of a message thread, in which the messages will be sent" +msgstr "" + +#: of telebot.TeleBot.copy_messages:18 telebot.TeleBot.forward_message:17 +#: telebot.TeleBot.forward_messages:18 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "" + +#: of telebot.TeleBot.copy_messages:21 +msgid "Pass True to copy the messages without their captions" +msgstr "" + +#: of telebot.TeleBot.copy_messages:24 +msgid "On success, an array of MessageId of the sent messages is returned." +msgstr "" + +#: of telebot.TeleBot.copy_messages:25 +msgid ":obj:`list` of :class:`telebot.types.MessageID`" msgstr "" #: of telebot.TeleBot.create_chat_invite_link:1 @@ -1471,7 +1553,7 @@ msgid "" "increased order and must not exceed max_tip_amount." msgstr "" -#: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:77 +#: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:80 msgid "" "A JSON-serialized data about the invoice, which will be shared with the " "payment provider. A detailed description of required fields should be " @@ -1685,6 +1767,22 @@ msgstr "" msgid "Identifier of the message to delete" msgstr "" +#: of telebot.TeleBot.delete_messages:1 +msgid "" +"Use this method to delete multiple messages in a chat. The number of " +"messages to be deleted must not exceed 100. If the chat is a private " +"chat, the user must be an administrator of the chat for this to work and " +"must have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_messages:6 +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessages" +msgstr "" + +#: of telebot.TeleBot.delete_messages:11 +msgid "Identifiers of the messages to be deleted" +msgstr "" + #: of telebot.TeleBot.delete_my_commands:1 msgid "" "Use this method to delete the list of the bot's commands for the given " @@ -1909,7 +2007,7 @@ msgstr "" #: of telebot.TeleBot.edit_message_caption:26 #: telebot.TeleBot.edit_message_media:22 #: telebot.TeleBot.edit_message_reply_markup:17 -#: telebot.TeleBot.edit_message_text:29 +#: telebot.TeleBot.edit_message_text:32 msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." @@ -1968,7 +2066,7 @@ msgid "" msgstr "" #: of telebot.TeleBot.edit_message_live_location:29 -#: telebot.TeleBot.send_location:31 +#: telebot.TeleBot.send_location:34 msgid "The radius of uncertainty for the location, measured in meters; 0-1500" msgstr "" @@ -2035,7 +2133,7 @@ msgstr "" #: of telebot.TeleBot.edit_message_media:23 #: telebot.TeleBot.edit_message_reply_markup:18 -#: telebot.TeleBot.edit_message_text:30 telebot.TeleBot.set_game_score:27 +#: telebot.TeleBot.edit_message_text:33 telebot.TeleBot.set_game_score:27 msgid ":obj:`types.Message` or :obj:`bool`" msgstr "" @@ -2071,8 +2169,10 @@ msgid "" "specified instead of parse_mode" msgstr "" -#: of telebot.TeleBot.edit_message_text:23 telebot.TeleBot.send_message:21 -msgid "Disables link previews for links in this message" +#: of telebot.TeleBot.edit_message_text:29 +msgid "" +"A JSON-serialized object for options used to automatically generate " +"previews for links." msgstr "" #: of telebot.TeleBot.edited_channel_post_handler:1 @@ -2155,7 +2255,7 @@ msgstr "" msgid "exported invite link as String on success." msgstr "" -#: of telebot.TeleBot.forward_message:1 +#: of telebot.TeleBot.forward_message:1 telebot.TeleBot.forward_messages:1 msgid "Use this method to forward messages of any kind." msgstr "" @@ -2163,14 +2263,27 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" msgstr "" -#: of telebot.TeleBot.forward_message:5 -msgid "" -"Sends the message silently. Users will receive a notification with no " -"sound" +#: of telebot.TeleBot.forward_message:26 telebot.TeleBot.forward_messages:21 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:69 +#: telebot.TeleBot.send_audio:69 telebot.TeleBot.send_contact:46 +#: telebot.TeleBot.send_dice:38 telebot.TeleBot.send_document:62 +#: telebot.TeleBot.send_game:35 telebot.TeleBot.send_invoice:104 +#: telebot.TeleBot.send_location:52 telebot.TeleBot.send_message:52 +#: telebot.TeleBot.send_photo:51 telebot.TeleBot.send_poll:73 +#: telebot.TeleBot.send_sticker:45 telebot.TeleBot.send_venue:59 +#: telebot.TeleBot.send_video:70 telebot.TeleBot.send_video_note:54 +#: telebot.TeleBot.send_voice:52 +msgid "On success, the sent Message is returned." msgstr "" -#: of telebot.TeleBot.forward_message:17 -msgid "Protects the contents of the forwarded message from forwarding and saving" +#: of telebot.TeleBot.forward_message:27 telebot.TeleBot.reply_to:12 +#: telebot.TeleBot.send_animation:70 telebot.TeleBot.send_audio:70 +#: telebot.TeleBot.send_contact:47 telebot.TeleBot.send_dice:39 +#: telebot.TeleBot.send_document:63 telebot.TeleBot.send_location:53 +#: telebot.TeleBot.send_message:53 telebot.TeleBot.send_photo:52 +#: telebot.TeleBot.send_sticker:46 telebot.TeleBot.send_venue:60 +#: telebot.TeleBot.send_video:71 telebot.TeleBot.send_video_note:55 +msgid ":class:`telebot.types.Message`" msgstr "" #: of telebot.TeleBot.get_chat:1 @@ -2186,6 +2299,7 @@ msgstr "" #: of telebot.TeleBot.get_chat:6 telebot.TeleBot.get_chat_administrators:7 #: telebot.TeleBot.get_chat_member_count:5 telebot.TeleBot.leave_chat:5 +#: telebot.TeleBot.set_message_reaction:5 msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" @@ -2253,9 +2367,6 @@ msgid "Number of members in the chat." msgstr "" #: of telebot.TeleBot.get_chat_member_count:9 -#: telebot.TeleBot.send_video_note:14 telebot.TeleBot.send_video_note:17 -#: telebot.TeleBot.send_video_note:20 telebot.TeleBot.send_video_note:31 -#: telebot.TeleBot.send_video_note:46 msgid ":obj:`int`" msgstr "" @@ -2580,6 +2691,34 @@ msgstr "" msgid ":obj:`list` of :class:`telebot.types.Update`" msgstr "" +#: of telebot.TeleBot.get_user_chat_boosts:1 +msgid "" +"Use this method to get the list of boosts added to a chat by a user. " +"Requires administrator rights in the chat. Returns a UserChatBoosts " +"object." +msgstr "" + +#: of telebot.TeleBot.get_user_chat_boosts:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserchatboosts" +msgstr "" + +#: of telebot.TeleBot.get_user_chat_boosts:5 +#: telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 +#: telebot.TeleBot.send_poll:6 telebot.TeleBot.send_venue:5 +#: telebot.TeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "" + +#: of telebot.TeleBot.get_user_chat_boosts:11 +msgid "On success, a UserChatBoosts object is returned." +msgstr "" + +#: of telebot.TeleBot.get_user_chat_boosts:12 +msgid ":class:`telebot.types.UserChatBoosts`" +msgstr "" + #: of telebot.TeleBot.get_user_profile_photos:1 msgid "" "Use this method to get a list of profile pictures for a user. Returns a " @@ -2804,6 +2943,19 @@ msgstr "" msgid "decorated function" msgstr "" +#: of telebot.TeleBot.message_reaction_count_handler:1 +msgid "" +"Handles new incoming message reaction count. As a parameter to the " +"decorator function, it passes " +":class:`telebot.types.MessageReactionCountUpdated` object." +msgstr "" + +#: of telebot.TeleBot.message_reaction_handler:1 +msgid "" +"Handles new incoming message reaction. As a parameter to the decorator " +"function, it passes :class:`telebot.types.MessageReactionUpdated` object." +msgstr "" + #: of telebot.TeleBot.middleware_handler:1 msgid "Function-based middleware handler decorator." msgstr "" @@ -2931,10 +3083,6 @@ msgid "" " period of time." msgstr "" -#: of telebot.TeleBot.polling:44 -msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." -msgstr "" - #: of telebot.TeleBot.polling:50 msgid "Path to watch for changes. Defaults to None" msgstr "" @@ -3048,12 +3196,25 @@ msgid "" "forum topics, supergroups only" msgstr "" +#: of telebot.TeleBot.promote_chat_member:60 +msgid "Pass True if the administrator can create the channel's stories" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:63 +msgid "Pass True if the administrator can edit the channel's stories" +msgstr "" + +#: of telebot.TeleBot.promote_chat_member:66 +msgid "Pass True if the administrator can delete the channel's stories" +msgstr "" + #: of telebot.TeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." msgstr "" #: of telebot.TeleBot.register_callback_query_handler:3 #: telebot.TeleBot.register_channel_post_handler:3 +#: telebot.TeleBot.register_chat_boost_handler:3 #: telebot.TeleBot.register_chat_join_request_handler:3 #: telebot.TeleBot.register_chat_member_handler:3 #: telebot.TeleBot.register_chosen_inline_handler:3 @@ -3061,16 +3222,20 @@ msgstr "" #: telebot.TeleBot.register_edited_message_handler:3 #: telebot.TeleBot.register_inline_handler:3 #: telebot.TeleBot.register_message_handler:3 +#: telebot.TeleBot.register_message_reaction_count_handler:3 +#: telebot.TeleBot.register_message_reaction_handler:3 #: telebot.TeleBot.register_my_chat_member_handler:3 #: telebot.TeleBot.register_poll_answer_handler:3 #: telebot.TeleBot.register_poll_handler:3 #: telebot.TeleBot.register_pre_checkout_query_handler:3 +#: telebot.TeleBot.register_removed_chat_boost_handler:3 #: telebot.TeleBot.register_shipping_query_handler:3 msgid "function to be called" msgstr "" #: of telebot.TeleBot.register_callback_query_handler:9 #: telebot.TeleBot.register_channel_post_handler:18 +#: telebot.TeleBot.register_chat_boost_handler:9 #: telebot.TeleBot.register_chat_join_request_handler:9 #: telebot.TeleBot.register_chat_member_handler:9 #: telebot.TeleBot.register_chosen_inline_handler:9 @@ -3078,10 +3243,13 @@ msgstr "" #: telebot.TeleBot.register_edited_message_handler:21 #: telebot.TeleBot.register_inline_handler:9 #: telebot.TeleBot.register_message_handler:21 +#: telebot.TeleBot.register_message_reaction_count_handler:9 +#: telebot.TeleBot.register_message_reaction_handler:9 #: telebot.TeleBot.register_my_chat_member_handler:9 #: telebot.TeleBot.register_poll_answer_handler:9 #: telebot.TeleBot.register_poll_handler:9 #: telebot.TeleBot.register_pre_checkout_query_handler:8 +#: telebot.TeleBot.register_removed_chat_boost_handler:9 #: telebot.TeleBot.register_shipping_query_handler:9 msgid "" "True if you need to pass TeleBot instance to handler(useful for " @@ -3105,6 +3273,10 @@ msgstr "" msgid "Regular expression" msgstr "" +#: of telebot.TeleBot.register_chat_boost_handler:1 +msgid "Registers chat boost handler." +msgstr "" + #: of telebot.TeleBot.register_chat_join_request_handler:1 msgid "Registers chat join request handler." msgstr "" @@ -3184,6 +3356,14 @@ msgstr "" msgid "List of chat types" msgstr "" +#: of telebot.TeleBot.register_message_reaction_count_handler:1 +msgid "Registers message reaction count handler." +msgstr "" + +#: of telebot.TeleBot.register_message_reaction_handler:1 +msgid "Registers message reaction handler." +msgstr "" + #: of telebot.TeleBot.register_middleware_handler:1 msgid "Adds function-based middleware handler." msgstr "" @@ -3264,6 +3444,10 @@ msgstr "" msgid "Registers pre-checkout request handler." msgstr "" +#: of telebot.TeleBot.register_removed_chat_boost_handler:1 +msgid "Registers removed chat boost handler." +msgstr "" + #: of telebot.TeleBot.register_shipping_query_handler:1 msgid "Registers shipping query handler." msgstr "" @@ -3272,6 +3456,12 @@ msgstr "" msgid "Deletes webhooks using set_webhook() function." msgstr "" +#: of telebot.TeleBot.removed_chat_boost_handler:1 +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostRemoved` object." +msgstr "" + #: of telebot.TeleBot.reopen_forum_topic:1 msgid "" "Use this method to reopen a closed topic in a forum supergroup chat. The " @@ -3557,7 +3747,7 @@ msgid "Animation height" msgstr "" #: of telebot.TeleBot.send_animation:22 telebot.TeleBot.send_video:20 -#: telebot.TeleBot.send_video_note:33 +#: telebot.TeleBot.send_video_note:36 msgid "" "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" @@ -3578,7 +3768,7 @@ msgstr "" msgid "Mode for parsing entities in the animation caption" msgstr "" -#: of telebot.TeleBot.send_animation:51 telebot.TeleBot.send_video:29 +#: of telebot.TeleBot.send_animation:54 telebot.TeleBot.send_video:29 msgid "" "List of special entities that appear in the caption, which can be " "specified instead of parse_mode" @@ -3592,10 +3782,13 @@ msgstr "" msgid "Pass True, if the animation should be sent as a spoiler" msgstr "" -#: of telebot.TeleBot.send_animation:63 telebot.TeleBot.send_audio:63 -#: telebot.TeleBot.send_document:56 telebot.TeleBot.send_video:64 -#: telebot.TeleBot.send_video_note:48 -msgid "Deprecated. Use thumbnail instead" +#: of telebot.TeleBot.send_animation:66 telebot.TeleBot.send_audio:66 +#: telebot.TeleBot.send_contact:43 telebot.TeleBot.send_document:59 +#: telebot.TeleBot.send_location:49 telebot.TeleBot.send_media_group:30 +#: telebot.TeleBot.send_message:46 telebot.TeleBot.send_sticker:42 +#: telebot.TeleBot.send_venue:56 telebot.TeleBot.send_video_note:51 +#: telebot.TeleBot.send_voice:49 +msgid "Reply parameters." msgstr "" #: of telebot.TeleBot.send_audio:1 @@ -3640,13 +3833,13 @@ msgstr "" msgid "Track name" msgstr "" -#: of telebot.TeleBot.send_audio:36 +#: of telebot.TeleBot.send_audio:33 msgid "" "Mode for parsing entities in the audio caption. See formatting options " "for more details." msgstr "" -#: of telebot.TeleBot.send_audio:45 +#: of telebot.TeleBot.send_audio:48 msgid "" "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" @@ -3657,8 +3850,8 @@ msgid "" "uploaded using multipart/form-data under " msgstr "" -#: of telebot.TeleBot.send_audio:51 telebot.TeleBot.send_document:35 -#: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:37 +#: of telebot.TeleBot.send_audio:54 telebot.TeleBot.send_document:38 +#: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:40 msgid "" "A JSON-serialized list of special entities that appear in the caption, " "which can be specified instead of parse_mode" @@ -3684,12 +3877,6 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" msgstr "" -#: of telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 -#: telebot.TeleBot.send_poll:6 telebot.TeleBot.send_venue:5 -#: telebot.TeleBot.stop_poll:5 -msgid "Unique identifier for the target chat or username of the target channel" -msgstr "" - #: of telebot.TeleBot.send_chat_action:13 msgid "" "Type of action to broadcast. Choose one, depending on what the user is " @@ -3732,14 +3919,7 @@ msgstr "" msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" msgstr "" -#: of telebot.TeleBot.send_contact:34 telebot.TeleBot.send_game:23 -#: telebot.TeleBot.send_venue:41 -msgid "" -"Pass True, if the message should be sent even if one of the specified " -"replied-to messages is not found." -msgstr "" - -#: of telebot.TeleBot.send_contact:41 telebot.TeleBot.send_venue:54 +#: of telebot.TeleBot.send_contact:40 telebot.TeleBot.send_venue:53 msgid "The thread identifier of a message from which the reply will be sent" msgstr "" @@ -3761,10 +3941,6 @@ msgid "" "to “🎲”" msgstr "" -#: of telebot.TeleBot.send_dice:29 -msgid "Protects the contents of the sent message from forwarding" -msgstr "" - #: of telebot.TeleBot.send_document:1 msgid "Use this method to send general files." msgstr "" @@ -3781,17 +3957,17 @@ msgid "" "using multipart/form-data" msgstr "" -#: of telebot.TeleBot.send_document:15 +#: of telebot.TeleBot.send_document:12 msgid "" "Document caption (may also be used when resending documents by file_id), " "0-1024 characters after entities parsing" msgstr "" -#: of telebot.TeleBot.send_document:23 +#: of telebot.TeleBot.send_document:20 msgid "Mode for parsing entities in the document caption" msgstr "" -#: of telebot.TeleBot.send_document:32 +#: of telebot.TeleBot.send_document:35 msgid "" "InputFile or String : Thumbnail of the file sent; can be ignored if " "thumbnail generation for the file is supported server-side. The thumbnail" @@ -3838,14 +4014,20 @@ msgid "" " up your games via @BotFather." msgstr "" -#: of telebot.TeleBot.send_game:20 -msgid "Timeout in seconds for waiting for a response from the bot." +#: of telebot.TeleBot.send_game:14 telebot.TeleBot.send_invoice:70 +#: telebot.TeleBot.send_poll:47 +msgid "deprecated. If the message is a reply, ID of the original message" msgstr "" -#: of telebot.TeleBot.send_game:26 +#: of telebot.TeleBot.send_game:17 telebot.TeleBot.send_invoice:73 +#: telebot.TeleBot.send_poll:50 msgid "" -"Pass True, if content of the message needs to be protected from being " -"viewed by the bot." +"deprecated. Pass True, if the message should be sent even if the " +"specified replied-to message is not found" +msgstr "" + +#: of telebot.TeleBot.send_game:23 +msgid "Timeout in seconds for waiting for a response from the bot." msgstr "" #: of telebot.TeleBot.send_game:29 @@ -3854,8 +4036,12 @@ msgid "" "sent." msgstr "" -#: of telebot.TeleBot.send_game:33 telebot.TeleBot.send_invoice:102 -#: telebot.TeleBot.send_poll:71 +#: of telebot.TeleBot.send_game:32 telebot.TeleBot.send_video:67 +msgid "Reply parameters" +msgstr "" + +#: of telebot.TeleBot.send_game:36 telebot.TeleBot.send_invoice:105 +#: telebot.TeleBot.send_poll:74 msgid ":obj:`types.Message`" msgstr "" @@ -3884,14 +4070,14 @@ msgid "" " they are paying for." msgstr "" -#: of telebot.TeleBot.send_invoice:73 +#: of telebot.TeleBot.send_invoice:76 msgid "" "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" msgstr "" -#: of telebot.TeleBot.send_invoice:81 telebot.TeleBot.set_webhook:39 +#: of telebot.TeleBot.send_invoice:84 telebot.TeleBot.set_webhook:39 msgid "Timeout of a request, defaults to None" msgstr "" @@ -3901,6 +4087,10 @@ msgid "" "sent" msgstr "" +#: of telebot.TeleBot.send_invoice:101 +msgid "Required if the message is a reply. Additional interface options." +msgstr "" + #: of telebot.TeleBot.send_location:1 msgid "" "Use this method to send point on the map. On success, the sent Message is" @@ -3925,13 +4115,13 @@ msgid "" "Locations, should be between 60 and 86400." msgstr "" -#: of telebot.TeleBot.send_location:34 +#: of telebot.TeleBot.send_location:37 msgid "" "For live locations, a direction in which the user is moving, in degrees. " "Must be between 1 and 360 if specified." msgstr "" -#: of telebot.TeleBot.send_location:37 +#: of telebot.TeleBot.send_location:40 msgid "" "For live locations, a maximum distance for proximity alerts about " "approaching another chat member, in meters. Must be between 1 and 100000 " @@ -3966,7 +4156,7 @@ msgstr "" msgid "Identifier of a message thread, in which the media group will be sent" msgstr "" -#: of telebot.TeleBot.send_media_group:30 +#: of telebot.TeleBot.send_media_group:33 msgid "On success, an array of Messages that were sent is returned." msgstr "" @@ -3995,10 +4185,8 @@ msgid "" "specified instead of parse_mode" msgstr "" -#: of telebot.TeleBot.send_message:27 -msgid "" -"If True, the message content will be hidden for all users except for the " -"target user" +#: of telebot.TeleBot.send_message:49 +msgid "Link preview options." msgstr "" #: of telebot.TeleBot.send_photo:1 @@ -4102,10 +4290,6 @@ msgid "" " for poll preview." msgstr "" -#: of telebot.TeleBot.send_poll:50 -msgid "Pass True, if the poll allows multiple options to be voted simultaneously." -msgstr "" - #: of telebot.TeleBot.send_poll:57 msgid "Timeout in seconds for waiting for a response from the user." msgstr "" @@ -4120,6 +4304,10 @@ msgstr "" msgid "The identifier of a message thread, in which the poll will be sent" msgstr "" +#: of telebot.TeleBot.send_poll:70 +msgid "reply parameters." +msgstr "" + #: of telebot.TeleBot.send_sticker:1 msgid "" "Use this method to send static .WEBP, animated .TGS, or video .WEBM " @@ -4138,7 +4326,7 @@ msgid "" " multipart/form-data." msgstr "" -#: of telebot.TeleBot.send_sticker:21 +#: of telebot.TeleBot.send_sticker:18 msgid "to disable the notification" msgstr "" @@ -4183,11 +4371,11 @@ msgid "" "“food/icecream”.)" msgstr "" -#: of telebot.TeleBot.send_venue:45 +#: of telebot.TeleBot.send_venue:44 msgid "Google Places identifier of the venue" msgstr "" -#: of telebot.TeleBot.send_venue:48 +#: of telebot.TeleBot.send_venue:47 msgid "Google Places type of the venue." msgstr "" @@ -4249,22 +4437,6 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" msgstr "" -#: of telebot.TeleBot.send_video_note -msgid "param chat_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type chat_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note:7 -msgid ":obj:`int` or :obj:`str`" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param data" -msgstr "" - #: of telebot.TeleBot.send_video_note:9 msgid "" "Video note to send. Pass a file_id as String to send a video note that " @@ -4273,114 +4445,14 @@ msgid "" "unsupported" msgstr "" -#: of telebot.TeleBot.send_video_note -msgid "type data" -msgstr "" - -#: of telebot.TeleBot.send_video_note:11 telebot.TeleBot.send_video_note:37 -msgid ":obj:`str` or :class:`telebot.types.InputFile`" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param duration" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type duration" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param length" -msgstr "" - #: of telebot.TeleBot.send_video_note:16 msgid "Video width and height, i.e. diameter of the video message" msgstr "" -#: of telebot.TeleBot.send_video_note -msgid "type length" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param reply_to_message_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type reply_to_message_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param reply_markup" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type reply_markup" -msgstr "" - -#: of telebot.TeleBot.send_video_note:24 -msgid "" -":class:`telebot.types.InlineKeyboardMarkup` or " -":class:`telebot.types.ReplyKeyboardMarkup` or " -":class:`telebot.types.ReplyKeyboardRemove` or " -":class:`telebot.types.ForceReply`" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param disable_notification" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type disable_notification" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param timeout" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type timeout" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param thumbnail" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type thumbnail" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param allow_sending_without_reply" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type allow_sending_without_reply" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param protect_content" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type protect_content" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param message_thread_id" -msgstr "" - #: of telebot.TeleBot.send_video_note:45 msgid "Identifier of a message thread, in which the video note will be sent" msgstr "" -#: of telebot.TeleBot.send_video_note -msgid "type message_thread_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param thumb" -msgstr "" - #: of telebot.TeleBot.send_voice:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -4411,7 +4483,7 @@ msgstr "" msgid "Duration of the voice message in seconds" msgstr "" -#: of telebot.TeleBot.send_voice:28 +#: of telebot.TeleBot.send_voice:25 msgid "" "Mode for parsing entities in the voice message caption. See formatting " "options for more details." @@ -4593,6 +4665,35 @@ msgid "" "Message, otherwise returns True." msgstr "" +#: of telebot.TeleBot.set_message_reaction:1 +msgid "" +"Use this method to set a reaction to a message in a chat. The bot must be" +" an administrator in the chat for this to work and must have the " +"appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.set_message_reaction:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmessagereaction" +msgstr "" + +#: of telebot.TeleBot.set_message_reaction:8 +msgid "Identifier of the message to set reaction to" +msgstr "" + +#: of telebot.TeleBot.set_message_reaction:11 +msgid "" +"New list of reaction types to set on the message. Currently, as non-" +"premium users, bots can set up to one reaction per message. A custom " +"emoji reaction can be used if it is either already present on the message" +" or explicitly allowed by chat administrators." +msgstr "" + +#: of telebot.TeleBot.set_message_reaction:15 +msgid "Pass True to set the reaction with a big animation" +msgstr "" + #: of telebot.TeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." msgstr "" @@ -5081,6 +5182,24 @@ msgstr "" msgid "Identifier of the topic" msgstr "" +#: of telebot.TeleBot.unpin_all_general_forum_topic_messages:1 +msgid "" +"Use this method to clear the list of pinned messages in a General forum " +"topic. The bot must be an administrator in the chat for this to work and " +"must have the can_pin_messages administrator right in the supergroup. " +"Returns True on success." +msgstr "" + +#: of telebot.TeleBot.unpin_all_general_forum_topic_messages:6 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages" +msgstr "" + +#: of telebot.TeleBot.unpin_all_general_forum_topic_messages:8 +msgid "Unique identifier for the target chat or username of chat" +msgstr "" + #: of telebot.TeleBot.unpin_chat_message:1 msgid "" "Use this method to unpin specific pinned message in a supergroup chat. " @@ -5404,3 +5523,137 @@ msgstr "" #~ " exactly 512px." #~ msgstr "" +#~ msgid "If the message is a reply, ID of the original message" +#~ msgstr "" + +#~ msgid "" +#~ "Pass True, if the message should " +#~ "be sent even if the specified " +#~ "replied-to message is not found" +#~ msgstr "" + +#~ msgid "Disables link previews for links in this message" +#~ msgstr "" + +#~ msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." +#~ msgstr "" + +#~ msgid "Deprecated. Use thumbnail instead" +#~ msgstr "" + +#~ msgid "" +#~ "Pass True, if the message should " +#~ "be sent even if one of the " +#~ "specified replied-to messages is not " +#~ "found." +#~ msgstr "" + +#~ msgid "Protects the contents of the sent message from forwarding" +#~ msgstr "" + +#~ msgid "" +#~ "Pass True, if content of the " +#~ "message needs to be protected from " +#~ "being viewed by the bot." +#~ msgstr "" + +#~ msgid "" +#~ "If True, the message content will " +#~ "be hidden for all users except for" +#~ " the target user" +#~ msgstr "" + +#~ msgid "" +#~ "Pass True, if the poll allows " +#~ "multiple options to be voted " +#~ "simultaneously." +#~ msgstr "" + +#~ msgid "param chat_id" +#~ msgstr "" + +#~ msgid "type chat_id" +#~ msgstr "" + +#~ msgid ":obj:`int` or :obj:`str`" +#~ msgstr "" + +#~ msgid "param data" +#~ msgstr "" + +#~ msgid "type data" +#~ msgstr "" + +#~ msgid ":obj:`str` or :class:`telebot.types.InputFile`" +#~ msgstr "" + +#~ msgid "param duration" +#~ msgstr "" + +#~ msgid "type duration" +#~ msgstr "" + +#~ msgid "param length" +#~ msgstr "" + +#~ msgid "type length" +#~ msgstr "" + +#~ msgid "param reply_to_message_id" +#~ msgstr "" + +#~ msgid "type reply_to_message_id" +#~ msgstr "" + +#~ msgid "param reply_markup" +#~ msgstr "" + +#~ msgid "type reply_markup" +#~ msgstr "" + +#~ msgid "" +#~ ":class:`telebot.types.InlineKeyboardMarkup` or " +#~ ":class:`telebot.types.ReplyKeyboardMarkup` or " +#~ ":class:`telebot.types.ReplyKeyboardRemove` or " +#~ ":class:`telebot.types.ForceReply`" +#~ msgstr "" + +#~ msgid "param disable_notification" +#~ msgstr "" + +#~ msgid "type disable_notification" +#~ msgstr "" + +#~ msgid "param timeout" +#~ msgstr "" + +#~ msgid "type timeout" +#~ msgstr "" + +#~ msgid "param thumbnail" +#~ msgstr "" + +#~ msgid "type thumbnail" +#~ msgstr "" + +#~ msgid "param allow_sending_without_reply" +#~ msgstr "" + +#~ msgid "type allow_sending_without_reply" +#~ msgstr "" + +#~ msgid "param protect_content" +#~ msgstr "" + +#~ msgid "type protect_content" +#~ msgstr "" + +#~ msgid "param message_thread_id" +#~ msgstr "" + +#~ msgid "type message_thread_id" +#~ msgstr "" + +#~ msgid "param thumb" +#~ msgstr "" + diff --git a/docs/source/locales/en/LC_MESSAGES/types.po b/docs/source/locales/en/LC_MESSAGES/types.po index c846273fe..1dfef7706 100644 --- a/docs/source/locales/en/LC_MESSAGES/types.po +++ b/docs/source/locales/en/LC_MESSAGES/types.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-08 23:07+0500\n" +"POT-Creation-Date: 2024-01-05 22:16+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,26 +25,35 @@ msgstr "" #: of telebot.types.Animation:1 telebot.types.Audio:1 #: telebot.types.BotDescription:1 telebot.types.BotName:1 #: telebot.types.BotShortDescription:1 telebot.types.CallbackQuery:1 -#: telebot.types.Chat:1 telebot.types.ChatJoinRequest:1 +#: telebot.types.Chat:1 telebot.types.ChatBoost:1 +#: telebot.types.ChatBoostRemoved:1 telebot.types.ChatBoostSource:1 +#: telebot.types.ChatBoostUpdated:1 telebot.types.ChatJoinRequest:1 #: telebot.types.ChatMember:1 telebot.types.ChatMemberUpdated:1 #: telebot.types.ChatPhoto:1 telebot.types.ChatShared:1 #: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 -#: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 +#: telebot.types.Document:1 telebot.types.ExternalReplyInfo:1 +#: telebot.types.File:1 telebot.types.ForumTopic:1 #: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 #: telebot.types.ForumTopicEdited:1 telebot.types.ForumTopicReopened:1 #: telebot.types.Game:1 telebot.types.GameHighScore:1 #: telebot.types.GeneralForumTopicHidden:1 -#: telebot.types.GeneralForumTopicUnhidden:1 telebot.types.InlineQuery:1 -#: telebot.types.Invoice:1 telebot.types.Message:1 +#: telebot.types.GeneralForumTopicUnhidden:1 telebot.types.Giveaway:1 +#: telebot.types.GiveawayCompleted:1 telebot.types.GiveawayCreated:1 +#: telebot.types.GiveawayWinners:1 telebot.types.InaccessibleMessage:1 +#: telebot.types.InlineQuery:1 telebot.types.Invoice:1 telebot.types.Message:1 #: telebot.types.MessageAutoDeleteTimerChanged:1 telebot.types.MessageID:1 -#: telebot.types.OrderInfo:1 telebot.types.PhotoSize:1 telebot.types.Poll:1 -#: telebot.types.PollOption:1 telebot.types.PreCheckoutQuery:1 -#: telebot.types.ProximityAlertTriggered:1 telebot.types.ShippingAddress:1 +#: telebot.types.MessageOrigin:1 telebot.types.MessageReactionCountUpdated:1 +#: telebot.types.MessageReactionUpdated:1 telebot.types.OrderInfo:1 +#: telebot.types.PhotoSize:1 telebot.types.Poll:1 telebot.types.PollOption:1 +#: telebot.types.PreCheckoutQuery:1 telebot.types.ProximityAlertTriggered:1 +#: telebot.types.ReactionCount:1 telebot.types.ShippingAddress:1 #: telebot.types.ShippingQuery:1 telebot.types.Sticker:1 -#: telebot.types.StickerSet:1 telebot.types.SuccessfulPayment:1 -#: telebot.types.Update:1 telebot.types.UserProfilePhotos:1 -#: telebot.types.UserShared:1 telebot.types.Venue:1 telebot.types.Video:1 -#: telebot.types.VideoChatEnded:1 telebot.types.VideoChatParticipantsInvited:1 +#: telebot.types.StickerSet:1 telebot.types.Story:1 +#: telebot.types.SuccessfulPayment:1 telebot.types.TextQuote:1 +#: telebot.types.Update:1 telebot.types.UserChatBoosts:1 +#: telebot.types.UserProfilePhotos:1 telebot.types.UsersShared:1 +#: telebot.types.Venue:1 telebot.types.Video:1 telebot.types.VideoChatEnded:1 +#: telebot.types.VideoChatParticipantsInvited:1 #: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 #: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 #: telebot.types.WriteAccessAllowed:1 @@ -71,19 +80,24 @@ msgstr "" #: telebot.types.BotDescription telebot.types.BotName #: telebot.types.BotShortDescription telebot.types.CallbackQuery #: telebot.types.Chat telebot.types.ChatAdministratorRights -#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest -#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator -#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft -#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner -#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated -#: telebot.types.ChatPermissions telebot.types.ChatPhoto -#: telebot.types.ChatShared telebot.types.ChosenInlineResult -#: telebot.types.Contact telebot.types.Dice telebot.types.Document -#: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic +#: telebot.types.ChatBoost telebot.types.ChatBoostRemoved +#: telebot.types.ChatBoostSource telebot.types.ChatBoostSourceGiftCode +#: telebot.types.ChatBoostSourceGiveaway telebot.types.ChatBoostSourcePremium +#: telebot.types.ChatBoostUpdated telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChatShared +#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice +#: telebot.types.Document telebot.types.ExternalReplyInfo telebot.types.File +#: telebot.types.ForceReply telebot.types.ForumTopic #: telebot.types.ForumTopicCreated telebot.types.ForumTopicEdited -#: telebot.types.Game telebot.types.GameHighScore -#: telebot.types.InlineKeyboardButton telebot.types.InlineKeyboardMarkup -#: telebot.types.InlineKeyboardMarkup.add +#: telebot.types.Game telebot.types.GameHighScore telebot.types.Giveaway +#: telebot.types.GiveawayCompleted telebot.types.GiveawayWinners +#: telebot.types.InaccessibleMessage telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio #: telebot.types.InlineQueryResultCachedAudio @@ -109,23 +123,32 @@ msgstr "" #: telebot.types.InputVenueMessageContent telebot.types.Invoice #: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType #: telebot.types.KeyboardButtonRequestChat -#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice -#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.KeyboardButtonRequestUsers telebot.types.LabeledPrice +#: telebot.types.LinkPreviewOptions telebot.types.Location +#: telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message #: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity -#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize -#: telebot.types.Poll telebot.types.Poll.add telebot.types.PollAnswer -#: telebot.types.PollOption telebot.types.PreCheckoutQuery -#: telebot.types.ProximityAlertTriggered telebot.types.ReplyKeyboardMarkup -#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row -#: telebot.types.ReplyKeyboardRemove telebot.types.SentWebAppMessage +#: telebot.types.MessageID telebot.types.MessageOrigin +#: telebot.types.MessageOriginChannel telebot.types.MessageOriginChat +#: telebot.types.MessageOriginHiddenUser telebot.types.MessageOriginUser +#: telebot.types.MessageReactionCountUpdated +#: telebot.types.MessageReactionUpdated telebot.types.OrderInfo +#: telebot.types.PhotoSize telebot.types.Poll telebot.types.Poll.add +#: telebot.types.PollAnswer telebot.types.PollOption +#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered +#: telebot.types.ReactionCount telebot.types.ReactionType +#: telebot.types.ReactionTypeCustomEmoji telebot.types.ReactionTypeEmoji +#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add +#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.ReplyParameters telebot.types.SentWebAppMessage #: telebot.types.ShippingAddress telebot.types.ShippingOption #: telebot.types.ShippingOption.add_price telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet #: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat -#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos -#: telebot.types.UserShared telebot.types.Venue telebot.types.Video +#: telebot.types.TextQuote telebot.types.Update telebot.types.User +#: telebot.types.UserChatBoosts telebot.types.UserProfilePhotos +#: telebot.types.UsersShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -194,17 +217,23 @@ msgstr "" #: telebot.types.BotDescription telebot.types.BotName #: telebot.types.BotShortDescription telebot.types.CallbackQuery #: telebot.types.Chat telebot.types.ChatAdministratorRights -#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest -#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator -#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft -#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner -#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated -#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions -#: telebot.types.ChatPhoto telebot.types.ChatShared -#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice -#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ChatBoost telebot.types.ChatBoostRemoved +#: telebot.types.ChatBoostSource telebot.types.ChatBoostSourceGiftCode +#: telebot.types.ChatBoostSourceGiveaway telebot.types.ChatBoostSourcePremium +#: telebot.types.ChatBoostUpdated telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChatShared telebot.types.ChosenInlineResult +#: telebot.types.Contact telebot.types.Dice telebot.types.Document +#: telebot.types.ExternalReplyInfo telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game -#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.GameHighScore telebot.types.Giveaway +#: telebot.types.GiveawayCompleted telebot.types.GiveawayWinners +#: telebot.types.InaccessibleMessage telebot.types.InlineKeyboardButton #: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio @@ -231,22 +260,29 @@ msgstr "" #: telebot.types.InputVenueMessageContent telebot.types.Invoice #: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType #: telebot.types.KeyboardButtonRequestChat -#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice -#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.KeyboardButtonRequestUsers telebot.types.LabeledPrice +#: telebot.types.LinkPreviewOptions telebot.types.Location +#: telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message #: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity -#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize -#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption -#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered -#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add -#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.MessageID telebot.types.MessageOrigin +#: telebot.types.MessageReactionCountUpdated +#: telebot.types.MessageReactionUpdated telebot.types.OrderInfo +#: telebot.types.PhotoSize telebot.types.Poll telebot.types.PollAnswer +#: telebot.types.PollOption telebot.types.PreCheckoutQuery +#: telebot.types.ProximityAlertTriggered telebot.types.ReactionCount +#: telebot.types.ReactionType telebot.types.ReactionTypeCustomEmoji +#: telebot.types.ReactionTypeEmoji telebot.types.ReplyKeyboardMarkup +#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row +#: telebot.types.ReplyKeyboardRemove telebot.types.ReplyParameters #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingOption.add_price #: telebot.types.ShippingQuery telebot.types.Sticker telebot.types.StickerSet #: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat -#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos -#: telebot.types.UserShared telebot.types.Venue telebot.types.Video +#: telebot.types.TextQuote telebot.types.Update telebot.types.User +#: telebot.types.UserChatBoosts telebot.types.UserProfilePhotos +#: telebot.types.UsersShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -263,19 +299,26 @@ msgstr "" #: telebot.types.BotCommandScopeChatMember:15 #: telebot.types.BotCommandScopeDefault:8 telebot.types.BotDescription:8 #: telebot.types.BotName:8 telebot.types.BotShortDescription:8 -#: telebot.types.CallbackQuery:30 telebot.types.Chat:109 -#: telebot.types.ChatAdministratorRights:46 telebot.types.ChatInviteLink:34 -#: telebot.types.ChatJoinRequest:27 telebot.types.ChatLocation:11 -#: telebot.types.ChatMemberAdministrator:59 telebot.types.ChatMemberBanned:15 -#: telebot.types.ChatMemberLeft:11 telebot.types.ChatMemberMember:11 -#: telebot.types.ChatMemberOwner:17 telebot.types.ChatMemberRestricted:61 -#: telebot.types.ChatMemberUpdated:27 telebot.types.ChatPermissions:56 -#: telebot.types.ChatPhoto:21 telebot.types.ChatShared:15 -#: telebot.types.ChosenInlineResult:21 telebot.types.Contact:22 -#: telebot.types.Dice:11 telebot.types.Document:26 telebot.types.File:21 +#: telebot.types.CallbackQuery:29 telebot.types.Chat:137 +#: telebot.types.ChatAdministratorRights:55 telebot.types.ChatBoost:17 +#: telebot.types.ChatBoostRemoved:17 telebot.types.ChatBoostSource:8 +#: telebot.types.ChatBoostSourceGiftCode:11 +#: telebot.types.ChatBoostSourceGiveaway:17 +#: telebot.types.ChatBoostSourcePremium:11 telebot.types.ChatBoostUpdated:11 +#: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:27 +#: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:68 +#: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 +#: telebot.types.ChatMemberMember:11 telebot.types.ChatMemberOwner:17 +#: telebot.types.ChatMemberRestricted:61 telebot.types.ChatMemberUpdated:27 +#: telebot.types.ChatPermissions:55 telebot.types.ChatPhoto:21 +#: telebot.types.ChatShared:15 telebot.types.ChosenInlineResult:21 +#: telebot.types.Contact:22 telebot.types.Dice:11 telebot.types.Document:26 +#: telebot.types.ExternalReplyInfo:75 telebot.types.File:21 #: telebot.types.ForceReply:18 telebot.types.ForumTopic:17 #: telebot.types.ForumTopicCreated:14 telebot.types.Game:25 -#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:49 +#: telebot.types.GameHighScore:14 telebot.types.Giveaway:29 +#: telebot.types.GiveawayCompleted:14 telebot.types.GiveawayWinners:38 +#: telebot.types.InaccessibleMessage:14 telebot.types.InlineKeyboardButton:49 #: telebot.types.InlineKeyboardMarkup:28 telebot.types.InlineQuery:26 #: telebot.types.InlineQueryResultArticle:38 #: telebot.types.InlineQueryResultAudio:40 @@ -303,28 +346,34 @@ msgstr "" #: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:37 #: telebot.types.InputMediaDocument:32 telebot.types.InputMediaPhoto:24 #: telebot.types.InputMediaVideo:43 telebot.types.InputSticker:18 -#: telebot.types.InputTextMessageContent:19 +#: telebot.types.InputTextMessageContent:22 #: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 -#: telebot.types.KeyboardButton:33 telebot.types.KeyboardButtonPollType:9 +#: telebot.types.KeyboardButton:36 telebot.types.KeyboardButtonPollType:9 #: telebot.types.KeyboardButtonRequestChat:35 -#: telebot.types.KeyboardButtonRequestUser:18 telebot.types.LabeledPrice:13 -#: telebot.types.Location:25 telebot.types.LoginUrl:24 -#: telebot.types.MaskPosition:20 telebot.types.MenuButtonCommands:8 -#: telebot.types.MenuButtonDefault:8 telebot.types.MenuButtonWebApp:15 -#: telebot.types.Message:252 telebot.types.MessageAutoDeleteTimerChanged:8 -#: telebot.types.MessageEntity:32 telebot.types.MessageID:8 -#: telebot.types.OrderInfo:17 telebot.types.PhotoSize:21 telebot.types.Poll:47 -#: telebot.types.PollAnswer:15 telebot.types.PollOption:11 -#: telebot.types.PreCheckoutQuery:28 telebot.types.ProximityAlertTriggered:14 -#: telebot.types.ReplyKeyboardMarkup:49 telebot.types.ReplyKeyboardRemove:18 +#: telebot.types.KeyboardButtonRequestUsers:21 telebot.types.LabeledPrice:13 +#: telebot.types.LinkPreviewOptions:20 telebot.types.Location:25 +#: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 +#: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 +#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:277 +#: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 +#: telebot.types.MessageID:8 telebot.types.MessageOrigin:23 +#: telebot.types.MessageReactionCountUpdated:17 +#: telebot.types.MessageReactionUpdated:26 telebot.types.OrderInfo:17 +#: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:18 +#: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 +#: telebot.types.ProximityAlertTriggered:14 telebot.types.ReactionCount:11 +#: telebot.types.ReactionType:8 telebot.types.ReactionTypeCustomEmoji:11 +#: telebot.types.ReactionTypeEmoji:11 telebot.types.ReplyKeyboardMarkup:49 +#: telebot.types.ReplyKeyboardRemove:18 telebot.types.ReplyParameters:26 #: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 #: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 -#: telebot.types.Sticker:54 telebot.types.StickerSet:30 +#: telebot.types.Sticker:54 telebot.types.StickerSet:29 #: telebot.types.SuccessfulPayment:28 -#: telebot.types.SwitchInlineQueryChosenChat:22 telebot.types.Update:61 -#: telebot.types.User:41 telebot.types.UserProfilePhotos:11 -#: telebot.types.UserShared:15 telebot.types.Venue:27 telebot.types.Video:35 -#: telebot.types.VideoChatEnded:8 telebot.types.VideoChatParticipantsInvited:8 +#: telebot.types.SwitchInlineQueryChosenChat:22 telebot.types.TextQuote:17 +#: telebot.types.Update:75 telebot.types.User:41 telebot.types.UserChatBoosts:8 +#: telebot.types.UserProfilePhotos:11 telebot.types.UsersShared:17 +#: telebot.types.Venue:27 telebot.types.Video:35 telebot.types.VideoChatEnded:8 +#: telebot.types.VideoChatParticipantsInvited:8 #: telebot.types.VideoChatScheduled:9 telebot.types.VideoNote:24 #: telebot.types.Voice:23 telebot.types.WebAppData:12 #: telebot.types.WebAppInfo:8 telebot.types.WebhookInfo:37 @@ -342,17 +391,23 @@ msgstr "" #: telebot.types.BotDescription telebot.types.BotName #: telebot.types.BotShortDescription telebot.types.CallbackQuery #: telebot.types.Chat telebot.types.ChatAdministratorRights -#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest -#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator -#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft -#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner -#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated -#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions -#: telebot.types.ChatPhoto telebot.types.ChatShared -#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice -#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ChatBoost telebot.types.ChatBoostRemoved +#: telebot.types.ChatBoostSource telebot.types.ChatBoostSourceGiftCode +#: telebot.types.ChatBoostSourceGiveaway telebot.types.ChatBoostSourcePremium +#: telebot.types.ChatBoostUpdated telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChatShared telebot.types.ChosenInlineResult +#: telebot.types.Contact telebot.types.Dice telebot.types.Document +#: telebot.types.ExternalReplyInfo telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game -#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.GameHighScore telebot.types.Giveaway +#: telebot.types.GiveawayCompleted telebot.types.GiveawayWinners +#: telebot.types.InaccessibleMessage telebot.types.InlineKeyboardButton #: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio @@ -379,22 +434,29 @@ msgstr "" #: telebot.types.InputVenueMessageContent telebot.types.Invoice #: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType #: telebot.types.KeyboardButtonRequestChat -#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice -#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.KeyboardButtonRequestUsers telebot.types.LabeledPrice +#: telebot.types.LinkPreviewOptions telebot.types.Location +#: telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message #: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity -#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize -#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption -#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered -#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add -#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.MessageID telebot.types.MessageOrigin +#: telebot.types.MessageReactionCountUpdated +#: telebot.types.MessageReactionUpdated telebot.types.OrderInfo +#: telebot.types.PhotoSize telebot.types.Poll telebot.types.PollAnswer +#: telebot.types.PollOption telebot.types.PreCheckoutQuery +#: telebot.types.ProximityAlertTriggered telebot.types.ReactionCount +#: telebot.types.ReactionType telebot.types.ReactionTypeCustomEmoji +#: telebot.types.ReactionTypeEmoji telebot.types.ReplyKeyboardMarkup +#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row +#: telebot.types.ReplyKeyboardRemove telebot.types.ReplyParameters #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet #: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat -#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos -#: telebot.types.UserShared telebot.types.Venue telebot.types.Video +#: telebot.types.TextQuote telebot.types.Update telebot.types.User +#: telebot.types.UserChatBoosts telebot.types.UserProfilePhotos +#: telebot.types.UsersShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -801,38 +863,37 @@ msgstr "" #: of telebot.types.CallbackQuery:11 msgid "" -"Optional. Message with the callback button that originated the query. " -"Note that message content and message date will not be available if the " -"message is too old" +"Optional. Message sent by the bot with the callback button that " +"originated the query" msgstr "" -#: of telebot.types.CallbackQuery:15 +#: of telebot.types.CallbackQuery:14 msgid "" "Optional. Identifier of the message sent via the bot in inline mode, that" " originated the query." msgstr "" -#: of telebot.types.CallbackQuery:19 +#: of telebot.types.CallbackQuery:18 msgid "" "Global identifier, uniquely corresponding to the chat to which the " "message with the callback button was sent. Useful for high scores in " "games." msgstr "" -#: of telebot.types.CallbackQuery:23 +#: of telebot.types.CallbackQuery:22 msgid "" "Optional. Data associated with the callback button. Be aware that the " "message originated the query can contain no callback buttons with this " "data." msgstr "" -#: of telebot.types.CallbackQuery:27 +#: of telebot.types.CallbackQuery:26 msgid "" "Optional. Short name of a Game to be returned, serves as the unique " "identifier for the game" msgstr "" -#: of telebot.types.CallbackQuery:31 +#: of telebot.types.CallbackQuery:30 msgid ":class:`telebot.types.CallbackQuery`" msgstr "" @@ -891,114 +952,159 @@ msgstr "" #: of telebot.types.Chat:35 msgid "" +"Optional. List of available chat reactions; for private chats, " +"supergroups and channels. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:39 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Chat:43 +msgid "" +"Optional. Custom emoji identifier of emoji chosen by the chat for the " +"reply header and link preview background. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:47 +msgid "" +"Optional. Identifier of the accent color for the chat's profile " +"background. See profile accent colors for more details. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:51 +msgid "" +"Optional. Custom emoji identifier of the emoji chosen by the chat for its" +" profile background. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:55 +msgid "" "Optional. Custom emoji identifier of emoji status of the other party in a" " private chat. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:39 +#: of telebot.types.Chat:59 +msgid "" +"Optional. Expiration date of the emoji status of the other party in a " +"private chat, if any. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:63 msgid "" "Optional. Bio of the other party in a private chat. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:42 +#: of telebot.types.Chat:66 msgid "" "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." msgstr "" -#: of telebot.types.Chat:46 +#: of telebot.types.Chat:70 msgid "" "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." msgstr "" -#: of telebot.types.Chat:48 +#: of telebot.types.Chat:72 msgid ":type :obj:`bool`" msgstr "" -#: of telebot.types.Chat:50 +#: of telebot.types.Chat:74 msgid "" "Optional. :obj:`bool`, if users need to join the supergroup before they " "can send messages. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:54 +#: of telebot.types.Chat:78 msgid "" "Optional. :obj:`bool`, if all users directly joining the supergroup need " "to be approved by supergroup administrators. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:58 +#: of telebot.types.Chat:82 msgid "" "Optional. Description, for groups, supergroups and channel chats. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:61 +#: of telebot.types.Chat:85 msgid "" "Optional. Primary invite link, for groups, supergroups and channel chats." " Returned only in getChat." msgstr "" -#: of telebot.types.Chat:65 +#: of telebot.types.Chat:89 msgid "" "Optional. The most recent pinned message (by sending date). Returned only" " in getChat." msgstr "" -#: of telebot.types.Chat:68 +#: of telebot.types.Chat:92 msgid "" "Optional. Default chat member permissions, for groups and supergroups. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:72 +#: of telebot.types.Chat:96 msgid "" "Optional. For supergroups, the minimum allowed delay between consecutive " "messages sent by each unpriviledged user; in seconds. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:76 +#: of telebot.types.Chat:100 msgid "" "Optional. The time after which all messages sent to the chat will be " "automatically deleted; in seconds. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:80 +#: of telebot.types.Chat:104 msgid "" "Optional. :obj:`bool`, if the chat has enabled aggressive anti-spam " "protection. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:84 +#: of telebot.types.Chat:108 msgid "" "Optional. :obj:`bool`, if the chat has enabled hidden members. Returned " "only in getChat." msgstr "" -#: of telebot.types.Chat:88 +#: of telebot.types.Chat:112 msgid "" "Optional. :obj:`bool`, if messages from the chat can't be forwarded to " "other chats. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:92 +#: of telebot.types.Chat:116 +msgid "" +"Optional. True, if new chat members will have access to old messages; " +"available only to chat administrators. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:120 msgid "" "Optional. For supergroups, name of group sticker set. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:95 +#: of telebot.types.Chat:123 msgid "" "Optional. :obj:`bool`, if the bot can change the group sticker set. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:99 +#: of telebot.types.Chat:127 msgid "" "Optional. Unique identifier for the linked chat, i.e. the discussion " "group identifier for a channel and vice versa; for supergroups and " @@ -1009,13 +1115,13 @@ msgid "" "in getChat." msgstr "" -#: of telebot.types.Chat:105 +#: of telebot.types.Chat:133 msgid "" "Optional. For supergroups, the location to which the supergroup is " "connected. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:110 +#: of telebot.types.Chat:138 msgid ":class:`telebot.types.Chat`" msgstr "" @@ -1117,10 +1223,203 @@ msgid "" "reopen forum topics; supergroups only" msgstr "" -#: of telebot.types.ChatAdministratorRights:47 +#: of telebot.types.ChatAdministratorRights:46 +#: telebot.types.ChatMemberAdministrator:59 +msgid "Optional. True, if the administrator can post channel stories" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:49 +#: telebot.types.ChatMemberAdministrator:62 +msgid "Optional. True, if the administrator can edit stories" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:52 +#: telebot.types.ChatMemberAdministrator:65 +msgid "Optional. True, if the administrator can delete stories of other users" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:56 msgid ":class:`telebot.types.ChatAdministratorRights`" msgstr "" +#: of telebot.types.ChatBoost:1 +msgid "This object contains information about a chat boost." +msgstr "" + +#: of telebot.types.ChatBoost:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#chatboost" +msgstr "" + +#: of telebot.types.ChatBoost:5 telebot.types.ChatBoostRemoved:8 +msgid "Unique identifier of the boost" +msgstr "" + +#: of telebot.types.ChatBoost:8 +msgid "Point in time (Unix timestamp) when the chat was boosted" +msgstr "" + +#: of telebot.types.ChatBoost:11 +msgid "" +"Point in time (Unix timestamp) when the boost will automatically expire, " +"unless the booster's Telegram Premium subscription is prolonged" +msgstr "" + +#: of telebot.types.ChatBoost:14 +msgid "Source of the added boost" +msgstr "" + +#: of telebot.types.ChatBoost:18 +msgid ":class:`ChatBoost`" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:1 +msgid "This object represents a boost removed from a chat." +msgstr "" + +#: of telebot.types.ChatBoostRemoved:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostremoved" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:5 telebot.types.ChatBoostUpdated:5 +msgid "Chat which was boosted" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:11 +msgid "Point in time (Unix timestamp) when the boost was removed" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:14 +msgid "Source of the removed boost" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:18 +msgid ":class:`ChatBoostRemoved`" +msgstr "" + +#: of telebot.types.ChatBoostSource:1 +msgid "This object describes the source of a chat boost." +msgstr "" + +#: of telebot.types.ChatBoostSource:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#chatboostsource" +msgstr "" + +#: of telebot.types.ChatBoostSource:5 +msgid "Source of the boost" +msgstr "" + +#: of telebot.types.ChatBoostSource:9 +msgid ":class:`ChatBoostSource`" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:1 +#: telebot.types.ChatBoostSourceGiveaway:1 +#: telebot.types.ChatBoostSourcePremium:1 +msgid "Bases: :py:class:`telebot.types.ChatBoostSource`" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:1 +msgid "" +"The boost was obtained by the creation of Telegram Premium gift codes to " +"boost a chat." +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostsourcegiftcode" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:5 +msgid "Source of the boost, always “gift_code”" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:8 +msgid "User for which the gift code was created" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:12 +msgid ":class:`ChatBoostSourceGiftCode`" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:1 +msgid "The boost was obtained by the creation of a Telegram Premium giveaway." +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostsourcegiveaway" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:5 +msgid "Source of the boost, always “giveaway”" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:8 +msgid "" +"Identifier of a message in the chat with the giveaway; the message could " +"have been deleted already. May be 0 if the message isn't sent yet." +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:11 +msgid "User that won the prize in the giveaway if any" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:14 +msgid "" +"True, if the giveaway was completed, but there was no user to win the " +"prize" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:18 +msgid ":class:`ChatBoostSourceGiveaway`" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:1 +msgid "" +"The boost was obtained by subscribing to Telegram Premium or by gifting a" +" Telegram Premium subscription to another user." +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostsourcepremium" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:5 +msgid "Source of the boost, always “premium”" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:8 +msgid "User that boosted the chat" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:12 +msgid ":class:`ChatBoostSourcePremium`" +msgstr "" + +#: of telebot.types.ChatBoostUpdated:1 +msgid "This object represents a boost added to a chat or changed." +msgstr "" + +#: of telebot.types.ChatBoostUpdated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostupdated" +msgstr "" + +#: of telebot.types.ChatBoostUpdated:8 +msgid "Infomation about the chat boost" +msgstr "" + +#: of telebot.types.ChatBoostUpdated:12 +msgid ":class:`ChatBoostUpdated`" +msgstr "" + #: of telebot.types.ChatInviteLink:1 msgid "Represents an invite link for a chat." msgstr "" @@ -1254,7 +1553,7 @@ msgstr "" msgid ":class:`telebot.types.ChatMemberOwner`" msgstr "" -#: of telebot.types.ChatMember:5 telebot.types.ChatMemberAdministrator:60 +#: of telebot.types.ChatMember:5 telebot.types.ChatMemberAdministrator:69 msgid ":class:`telebot.types.ChatMemberAdministrator`" msgstr "" @@ -1591,13 +1890,13 @@ msgid "" " defaults to the value of can_pin_messages" msgstr "" -#: of telebot.types.ChatPermissions:52 -msgid "" -"deprecated. True, if the user is allowed to send audios, documents, " -"photos, videos, video notes and voice notes" +#: of telebot.types.ChatPermissions:52 telebot.types.Message:27 +#: telebot.types.Message:30 telebot.types.Message:37 telebot.types.Message:40 +#: telebot.types.Message:43 +msgid "deprecated." msgstr "" -#: of telebot.types.ChatPermissions:57 +#: of telebot.types.ChatPermissions:56 msgid ":class:`telebot.types.ChatPermissions`" msgstr "" @@ -1650,7 +1949,7 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#Chatshared" msgstr "" -#: of telebot.types.ChatShared:6 telebot.types.UserShared:6 +#: of telebot.types.ChatShared:6 msgid "identifier of the request" msgstr "" @@ -1806,6 +2105,124 @@ msgstr "" msgid ":class:`telebot.types.Document`" msgstr "" +#: of telebot.types.ExternalReplyInfo:1 +msgid "" +"This object contains information about a message that is being replied " +"to, which may come from another chat or forum topic." +msgstr "" + +#: of telebot.types.ExternalReplyInfo:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#externalreplyinfo" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:6 +msgid "Origin of the message replied to by the given message" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:9 +msgid "" +"Optional. Chat the original message belongs to. Available only if the " +"chat is a supergroup or a channel." +msgstr "" + +#: of telebot.types.ExternalReplyInfo:12 +msgid "" +"Optional. Unique message identifier inside the original chat. Available " +"only if the original chat is a supergroup or a channel." +msgstr "" + +#: of telebot.types.ExternalReplyInfo:15 +msgid "" +"Optional. Options used for link preview generation for the original " +"message, if it is a text message" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:18 +msgid "Optional. Message is an animation, information about the animation" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:21 telebot.types.Message:97 +msgid "Optional. Message is an audio file, information about the file" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:24 telebot.types.Message:100 +msgid "Optional. Message is a general file, information about the file" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:27 telebot.types.Message:103 +msgid "Optional. Message is a photo, available sizes of the photo" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:30 telebot.types.Message:106 +msgid "Optional. Message is a sticker, information about the sticker" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:33 telebot.types.Message:109 +msgid "Optional. Message is a forwarded story" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:36 telebot.types.Message:112 +msgid "Optional. Message is a video, information about the video" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:39 telebot.types.Message:115 +msgid "Optional. Message is a video note, information about the video message" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:42 telebot.types.Message:118 +msgid "Optional. Message is a voice message, information about the file" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:45 telebot.types.Message:128 +msgid "Optional. True, if the message media is covered by a spoiler animation" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:48 telebot.types.Message:131 +msgid "Optional. Message is a shared contact, information about the contact" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:51 telebot.types.Message:134 +msgid "Optional. Message is a dice with random value" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:54 telebot.types.Message:137 +msgid "" +"Optional. Message is a game, information about the game. More about games" +" »" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:57 +msgid "Optional. Message is a scheduled giveaway, information about the giveaway" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:60 +msgid "Optional. A giveaway with public winners was completed" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:63 telebot.types.Message:200 +msgid "" +"Optional. Message is an invoice for a payment, information about the " +"invoice. More about payments »" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:66 telebot.types.Message:147 +msgid "Optional. Message is a shared location, information about the location" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:69 telebot.types.Message:140 +msgid "Optional. Message is a native poll, information about the poll" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:72 +msgid "Optional. Message is a venue, information about the venue" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:76 +msgid ":class:`ExternalReplyInfo`" +msgstr "" + #: of telebot.types.File:1 msgid "" "This object represents a file ready to be downloaded. The file can be " @@ -2061,33 +2478,209 @@ msgid "" "https://core.telegram.org/bots/api#generalforumtopicunhidden" msgstr "" -#: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 -#: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 -msgid "" -"Bases: :py:class:`telebot.types.Dictionaryable`, " -":py:class:`telebot.types.JsonSerializable`, " -":py:class:`telebot.types.JsonDeserializable`" +#: of telebot.types.Giveaway:1 +msgid "This object represents a message about a scheduled giveaway." msgstr "" -#: of telebot.types.InlineKeyboardButton:1 -msgid "" -"This object represents one button of an inline keyboard. You must use " -"exactly one of the optional fields." +#: of telebot.types.Giveaway:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#giveaway" msgstr "" -#: of telebot.types.InlineKeyboardButton:3 -#: telebot.types.SwitchInlineQueryChosenChat:4 -msgid "" -"Telegram Documentation: " -"https://core.telegram.org/bots/api#inlinekeyboardbutton" +#: of telebot.types.Giveaway:5 +msgid "The list of chats which the user must join to participate in the giveaway" msgstr "" -#: of telebot.types.InlineKeyboardButton:5 -#: telebot.types.InlineQueryResultsButton:6 -msgid "Label text on the button" +#: of telebot.types.Giveaway:8 +msgid "" +"Point in time (Unix timestamp) when winners of the giveaway will be " +"selected" msgstr "" -#: of telebot.types.InlineKeyboardButton:8 +#: of telebot.types.Giveaway:11 +msgid "" +"The number of users which are supposed to be selected as winners of the " +"giveaway" +msgstr "" + +#: of telebot.types.Giveaway:14 +msgid "" +"Optional. True, if only users who join the chats after the giveaway " +"started should be eligible to win" +msgstr "" + +#: of telebot.types.Giveaway:17 +msgid "" +"Optional. True, if the list of giveaway winners will be visible to " +"everyone" +msgstr "" + +#: of telebot.types.Giveaway:20 telebot.types.GiveawayWinners:35 +msgid "Optional. Description of additional giveaway prize" +msgstr "" + +#: of telebot.types.Giveaway:23 +msgid "" +"Optional. A list of two-letter ISO 3166-1 alpha-2 country codes " +"indicating the countries from which eligible users for the giveaway must " +"come. If empty, then all users can participate in the giveaway." +msgstr "" + +#: of telebot.types.Giveaway:26 telebot.types.GiveawayWinners:23 +msgid "" +"Optional. The number of months the Telegram Premium subscription won from" +" the giveaway will be active for" +msgstr "" + +#: of telebot.types.Giveaway:30 +msgid ":class:`Giveaway`" +msgstr "" + +#: of telebot.types.GiveawayCompleted:1 +msgid "" +"This object represents a service message about the completion of a " +"giveaway without public winners." +msgstr "" + +#: of telebot.types.GiveawayCompleted:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#giveawaycompleted" +msgstr "" + +#: of telebot.types.GiveawayCompleted:5 +msgid "Number of winners in the giveaway" +msgstr "" + +#: of telebot.types.GiveawayCompleted:8 telebot.types.GiveawayWinners:26 +msgid "Optional. Number of undistributed prizes" +msgstr "" + +#: of telebot.types.GiveawayCompleted:11 +msgid "" +"Optional. Message with the giveaway that was completed, if it wasn't " +"deleted" +msgstr "" + +#: of telebot.types.GiveawayCompleted:15 +msgid ":class:`GiveawayCompleted`" +msgstr "" + +#: of telebot.types.GiveawayCreated:1 +msgid "" +"This object represents a service message about the creation of a " +"scheduled giveaway. Currently holds no information." +msgstr "" + +#: of telebot.types.GiveawayWinners:1 +msgid "" +"This object represents a message about the completion of a giveaway with " +"public winners." +msgstr "" + +#: of telebot.types.GiveawayWinners:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#giveawaywinners" +msgstr "" + +#: of telebot.types.GiveawayWinners:5 +msgid "The chat that created the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:8 +msgid "Identifier of the messsage with the giveaway in the chat" +msgstr "" + +#: of telebot.types.GiveawayWinners:11 +msgid "Point in time (Unix timestamp) when winners of the giveaway were selected" +msgstr "" + +#: of telebot.types.GiveawayWinners:14 +msgid "Total number of winners in the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:17 +msgid "List of up to 100 winners of the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:20 +msgid "" +"Optional. The number of other chats the user had to join in order to be " +"eligible for the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:29 +msgid "" +"Optional. True, if only users who had joined the chats after the giveaway" +" started were eligible to win" +msgstr "" + +#: of telebot.types.GiveawayWinners:32 +msgid "" +"Optional. True, if the giveaway was canceled because the payment for it " +"was refunded" +msgstr "" + +#: of telebot.types.GiveawayWinners:39 +msgid ":class:`GiveawayWinners`" +msgstr "" + +#: of telebot.types.InaccessibleMessage:1 +msgid "" +"This object describes a message that was deleted or is otherwise " +"inaccessible to the bot." +msgstr "" + +#: of telebot.types.InaccessibleMessage:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#inaccessiblemessage" +msgstr "" + +#: of telebot.types.InaccessibleMessage:5 +msgid "Chat the message belonged to" +msgstr "" + +#: of telebot.types.InaccessibleMessage:8 telebot.types.MessageOriginChannel:6 +#: telebot.types.MessageReactionCountUpdated:8 +msgid "Unique message identifier inside the chat" +msgstr "" + +#: of telebot.types.InaccessibleMessage:11 +msgid "" +"Always 0. The field can be used to differentiate regular and inaccessible" +" messages." +msgstr "" + +#: of telebot.types.InaccessibleMessage:15 +msgid ":class:`InaccessibleMessage`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 +#: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 +msgid "" +"This object represents one button of an inline keyboard. You must use " +"exactly one of the optional fields." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:3 +#: telebot.types.SwitchInlineQueryChosenChat:4 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinekeyboardbutton" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:5 +#: telebot.types.InlineQueryResultsButton:6 +msgid "Label text on the button" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:8 msgid "" "Optional. HTTP or tg:// URL to be opened when the button is pressed. " "Links tg://user?id= can be used to mention a user by their ID " @@ -3390,7 +3983,7 @@ msgstr "" #: telebot.types.InputVenueMessageContent:1 #: telebot.types.KeyboardButtonPollType:1 #: telebot.types.KeyboardButtonRequestChat:1 -#: telebot.types.KeyboardButtonRequestUser:1 +#: telebot.types.KeyboardButtonRequestUsers:1 msgid "Bases: :py:class:`telebot.types.Dictionaryable`" msgstr "" @@ -3842,11 +4435,16 @@ msgid "" " be specified instead of parse_mode" msgstr "" -#: of telebot.types.InputTextMessageContent:16 -msgid "Optional. Disables link previews for links in the sent message" +#: of telebot.types.InputTextMessageContent:16 telebot.types.KeyboardButton:25 +#: telebot.types.StickerSet:20 +msgid "deprecated" +msgstr "" + +#: of telebot.types.InputTextMessageContent:19 +msgid "Optional. Link preview generation options for the message" msgstr "" -#: of telebot.types.InputTextMessageContent:20 +#: of telebot.types.InputTextMessageContent:23 msgid ":class:`telebot.types.InputTextMessageContent`" msgstr "" @@ -3980,21 +4578,21 @@ msgid "" "service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:25 +#: of telebot.types.KeyboardButton:28 msgid "" "Optional. If specified, pressing the button will open a list of suitable " -"users. Tapping on any user will send their identifier to the bot in a " -"“user_shared” service message. Available in private chats only." +"users. Identifiers of selected users will be sent to the bot in a " +"“users_shared” service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:29 +#: of telebot.types.KeyboardButton:32 msgid "" "Optional. If specified, pressing the button will open a list of suitable " "chats. Tapping on a chat will send its identifier to the bot in a " "“chat_shared” service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:34 +#: of telebot.types.KeyboardButton:37 msgid ":class:`telebot.types.KeyboardButton`" msgstr "" @@ -4094,39 +4692,51 @@ msgid ":class:`telebot.types.KeyboardButtonRequestChat`" msgstr "" #: of telebot.types.KeyboardButtonRequestUser:1 +msgid "Bases: :py:class:`telebot.types.KeyboardButtonRequestUsers`" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:1 +msgid "Deprecated. Use KeyboardButtonRequestUsers instead." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUsers:1 msgid "" "This object defines the criteria used to request a suitable user. The " "identifier of the selected user will be shared with the bot when the " "corresponding button is pressed." msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:4 +#: of telebot.types.KeyboardButtonRequestUsers:4 msgid "" "Telegram documentation: " -"https://core.telegram.org/bots/api#keyboardbuttonrequestuser" +"https://core.telegram.org/bots/api#keyboardbuttonrequestusers" msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:6 +#: of telebot.types.KeyboardButtonRequestUsers:6 msgid "" "Signed 32-bit identifier of the request, which will be received back in " -"the UserShared object. Must be unique within the message" +"the UsersShared object. Must be unique within the message" msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:10 +#: of telebot.types.KeyboardButtonRequestUsers:10 msgid "" "Optional. Pass True to request a bot, pass False to request a regular " "user. If not specified, no additional restrictions are applied." msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:14 +#: of telebot.types.KeyboardButtonRequestUsers:14 msgid "" "Optional. Pass True to request a premium user, pass False to request a " "non-premium user. If not specified, no additional restrictions are " "applied." msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:19 -msgid ":class:`telebot.types.KeyboardButtonRequestUser`" +#: of telebot.types.KeyboardButtonRequestUsers:18 +msgid "Optional. The maximum number of users to be selected; 1-10. Defaults to 1." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUsers:22 +msgid ":class:`telebot.types.KeyboardButtonRequestUsers`" msgstr "" #: of telebot.types.LabeledPrice:1 @@ -4154,6 +4764,59 @@ msgstr "" msgid ":class:`telebot.types.LabeledPrice`" msgstr "" +#: of telebot.types.LinkPreviewOptions:1 telebot.types.ReactionType:1 +#: telebot.types.ReplyParameters:1 telebot.types.SwitchInlineQueryChosenChat:1 +#: telebot.types.User:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:1 +msgid "Describes the options used for link preview generation." +msgstr "" + +#: of telebot.types.LinkPreviewOptions:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#linkpreviewoptions" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:5 +msgid "Optional. True, if the link preview is disabled" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:8 +msgid "" +"Optional. URL to use for the link preview. If empty, then the first URL " +"found in the message text will be used" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:11 +msgid "" +"Optional. True, if the media in the link preview is supposed to be " +"shrunk; ignored if the URL isn't explicitly specified or media size " +"change isn't supported for the preview" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:14 +msgid "" +"Optional. True, if the media in the link preview is supposed to be " +"enlarged; ignored if the URL isn't explicitly specified or media size " +"change isn't supported for the preview" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:17 +msgid "" +"Optional. True, if the link preview must be shown above the message text;" +" otherwise, the link preview will be shown below the message text" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:21 +msgid ":class:`LinkPreviewOptions`" +msgstr "" + #: of telebot.types.Location:1 msgid "This object represents a point on the map." msgstr "" @@ -4421,39 +5084,8 @@ msgstr "" msgid "Conversation the message belongs to" msgstr "" -#: of telebot.types.Message:27 -msgid "Optional. For forwarded messages, sender of the original message" -msgstr "" - -#: of telebot.types.Message:30 -msgid "" -"Optional. For messages forwarded from channels or from anonymous " -"administrators, information about the original sender chat" -msgstr "" - -#: of telebot.types.Message:34 -msgid "" -"Optional. For messages forwarded from channels, identifier of the " -"original message in the channel" -msgstr "" - -#: of telebot.types.Message:38 -msgid "" -"Optional. For forwarded messages that were originally sent in channels or" -" by an anonymous chat administrator, signature of the message sender if " -"present" -msgstr "" - -#: of telebot.types.Message:42 -msgid "" -"Optional. Sender's name for messages forwarded from users who disallow " -"adding a link to their account in forwarded messages" -msgstr "" - -#: of telebot.types.Message:46 -msgid "" -"Optional. For forwarded messages, date the original message was sent in " -"Unix time" +#: of telebot.types.Message:33 +msgid "deprecated. message in the channel" msgstr "" #: of telebot.types.Message:49 @@ -4474,148 +5106,112 @@ msgid "" msgstr "" #: of telebot.types.Message:60 -msgid "Optional. Bot through which the message was sent" +msgid "" +"Optional. Information about the message that is being replied to, which " +"may come from another chat or forum topic" msgstr "" #: of telebot.types.Message:63 -msgid "Optional. Date the message was last edited in Unix time" +msgid "" +"Optional. For replies that quote part of the original message, the quoted" +" part of the message" msgstr "" #: of telebot.types.Message:66 -msgid "Optional. :obj:`bool`, if the message can't be forwarded" +msgid "Optional. Bot through which the message was sent" msgstr "" #: of telebot.types.Message:69 +msgid "Optional. Date the message was last edited in Unix time" +msgstr "" + +#: of telebot.types.Message:72 +msgid "Optional. :obj:`bool`, if the message can't be forwarded" +msgstr "" + +#: of telebot.types.Message:75 msgid "" "Optional. The unique identifier of a media message group this message " "belongs to" msgstr "" -#: of telebot.types.Message:72 +#: of telebot.types.Message:78 msgid "" "Optional. Signature of the post author for messages in channels, or the " "custom title of an anonymous group administrator" msgstr "" -#: of telebot.types.Message:76 +#: of telebot.types.Message:82 msgid "Optional. For text messages, the actual UTF-8 text of the message" msgstr "" -#: of telebot.types.Message:79 +#: of telebot.types.Message:85 msgid "" "Optional. For text messages, special entities like usernames, URLs, bot " "commands, etc. that appear in the text" msgstr "" -#: of telebot.types.Message:83 +#: of telebot.types.Message:89 msgid "" -"Optional. Message is an animation, information about the animation. For " -"backward compatibility, when this field is set, the document field will " -"also be set" -msgstr "" - -#: of telebot.types.Message:87 -msgid "Optional. Message is an audio file, information about the file" -msgstr "" - -#: of telebot.types.Message:90 -msgid "Optional. Message is a general file, information about the file" +"Optional. Options used for link preview generation for the message, if it" +" is a text message and link preview options were changed" msgstr "" #: of telebot.types.Message:93 -msgid "Optional. Message is a photo, available sizes of the photo" -msgstr "" - -#: of telebot.types.Message:96 -msgid "Optional. Message is a sticker, information about the sticker" -msgstr "" - -#: of telebot.types.Message:99 -msgid "Optional. Message is a video, information about the video" -msgstr "" - -#: of telebot.types.Message:102 -msgid "Optional. Message is a video note, information about the video message" -msgstr "" - -#: of telebot.types.Message:105 -msgid "Optional. Message is a voice message, information about the file" +msgid "" +"Optional. Message is an animation, information about the animation. For " +"backward compatibility, when this field is set, the document field will " +"also be set" msgstr "" -#: of telebot.types.Message:108 +#: of telebot.types.Message:121 msgid "" "Optional. Caption for the animation, audio, document, photo, video or " "voice" msgstr "" -#: of telebot.types.Message:111 +#: of telebot.types.Message:124 msgid "" "Optional. For messages with a caption, special entities like usernames, " "URLs, bot commands, etc. that appear in the caption" msgstr "" -#: of telebot.types.Message:115 -msgid "Optional. True, if the message media is covered by a spoiler animation" -msgstr "" - -#: of telebot.types.Message:118 -msgid "Optional. Message is a shared contact, information about the contact" -msgstr "" - -#: of telebot.types.Message:121 -msgid "Optional. Message is a dice with random value" -msgstr "" - -#: of telebot.types.Message:124 -msgid "" -"Optional. Message is a game, information about the game. More about games" -" »" -msgstr "" - -#: of telebot.types.Message:127 -msgid "Optional. Message is a native poll, information about the poll" -msgstr "" - -#: of telebot.types.Message:130 +#: of telebot.types.Message:143 msgid "" "Optional. Message is a venue, information about the venue. For backward " "compatibility, when this field is set, the location field will also be " "set" msgstr "" -#: of telebot.types.Message:134 -msgid "Optional. Message is a shared location, information about the location" -msgstr "" - -#: of telebot.types.Message:137 +#: of telebot.types.Message:150 msgid "" "Optional. New members that were added to the group or supergroup and " "information about them (the bot itself may be one of these members)" msgstr "" -#: of telebot.types.Message:141 +#: of telebot.types.Message:154 msgid "" "Optional. A member was removed from the group, information about them " "(this member may be the bot itself)" msgstr "" -#: of telebot.types.Message:145 +#: of telebot.types.Message:158 msgid "Optional. A chat title was changed to this value" msgstr "" -#: of telebot.types.Message:148 +#: of telebot.types.Message:161 msgid "Optional. A chat photo was change to this value" msgstr "" -#: of telebot.types.Message:151 +#: of telebot.types.Message:164 msgid "Optional. Service message: the chat photo was deleted" msgstr "" -#: of telebot.types.Message:154 +#: of telebot.types.Message:167 msgid "Optional. Service message: the group has been created" msgstr "" -#: of telebot.types.Message:157 +#: of telebot.types.Message:170 msgid "" "Optional. Service message: the supergroup has been created. This field " "can't be received in a message coming through updates, because bot can't " @@ -4624,7 +5220,7 @@ msgid "" " created supergroup." msgstr "" -#: of telebot.types.Message:162 +#: of telebot.types.Message:175 msgid "" "Optional. Service message: the channel has been created. This field can't" " be received in a message coming through updates, because bot can't be a " @@ -4632,11 +5228,11 @@ msgid "" "reply_to_message if someone replies to a very first message in a channel." msgstr "" -#: of telebot.types.Message:167 +#: of telebot.types.Message:180 msgid "Optional. Service message: auto-delete timer settings changed in the chat" msgstr "" -#: of telebot.types.Message:171 +#: of telebot.types.Message:184 msgid "" "Optional. The group has been migrated to a supergroup with the specified " "identifier. This number may have more than 32 significant bits and some " @@ -4645,7 +5241,7 @@ msgid "" " double-precision float type are safe for storing this identifier." msgstr "" -#: of telebot.types.Message:177 +#: of telebot.types.Message:190 msgid "" "Optional. The supergroup has been migrated from a group with the " "specified identifier. This number may have more than 32 significant bits " @@ -4655,106 +5251,124 @@ msgid "" "identifier." msgstr "" -#: of telebot.types.Message:183 +#: of telebot.types.Message:196 msgid "" "Optional. Specified message was pinned. Note that the Message object in " "this field will not contain further reply_to_message fields even if it is" " itself a reply." msgstr "" -#: of telebot.types.Message:187 -msgid "" -"Optional. Message is an invoice for a payment, information about the " -"invoice. More about payments »" -msgstr "" - -#: of telebot.types.Message:190 +#: of telebot.types.Message:203 msgid "" "Optional. Message is a service message about a successful payment, " "information about the payment. More about payments »" msgstr "" -#: of telebot.types.Message:194 +#: of telebot.types.Message:207 msgid "Optional. Service message: a user was shared with the bot" msgstr "" -#: of telebot.types.Message:197 +#: of telebot.types.Message:210 msgid "Optional. Service message: a chat was shared with the bot" msgstr "" -#: of telebot.types.Message:200 +#: of telebot.types.Message:213 msgid "" "Optional. The domain name of the website on which the user has logged in." " More about Telegram Login »" msgstr "" -#: of telebot.types.Message:204 +#: of telebot.types.Message:217 msgid "" "Optional. Service message: the user allowed the bot added to the " "attachment menu to write messages" msgstr "" -#: of telebot.types.Message:208 +#: of telebot.types.Message:221 msgid "Optional. Telegram Passport data" msgstr "" -#: of telebot.types.Message:211 +#: of telebot.types.Message:224 msgid "" "Optional. Service message. A user in the chat triggered another user's " "proximity alert while sharing Live Location." msgstr "" -#: of telebot.types.Message:215 +#: of telebot.types.Message:228 msgid "Optional. Service message: forum topic created" msgstr "" -#: of telebot.types.Message:218 +#: of telebot.types.Message:231 msgid "Optional. Service message: forum topic edited" msgstr "" -#: of telebot.types.Message:221 +#: of telebot.types.Message:234 msgid "Optional. Service message: forum topic closed" msgstr "" -#: of telebot.types.Message:224 +#: of telebot.types.Message:237 msgid "Optional. Service message: forum topic reopened" msgstr "" -#: of telebot.types.Message:227 +#: of telebot.types.Message:240 msgid "Optional. Service message: the 'General' forum topic hidden" msgstr "" -#: of telebot.types.Message:230 +#: of telebot.types.Message:243 msgid "Optional. Service message: the 'General' forum topic unhidden" msgstr "" -#: of telebot.types.Message:233 +#: of telebot.types.Message:246 +msgid "Optional. Service message: a giveaway has been created" +msgstr "" + +#: of telebot.types.Message:249 +msgid "Optional. The message is a scheduled giveaway message" +msgstr "" + +#: of telebot.types.Message:252 +msgid "Optional. Service message: giveaway winners(public winners)" +msgstr "" + +#: of telebot.types.Message:255 +msgid "Optional. Service message: giveaway completed, without public winners" +msgstr "" + +#: of telebot.types.Message:258 msgid "Optional. Service message: video chat scheduled" msgstr "" -#: of telebot.types.Message:236 +#: of telebot.types.Message:261 msgid "Optional. Service message: video chat started" msgstr "" -#: of telebot.types.Message:239 +#: of telebot.types.Message:264 msgid "Optional. Service message: video chat ended" msgstr "" -#: of telebot.types.Message:242 +#: of telebot.types.Message:267 msgid "Optional. Service message: new participants invited to a video chat" msgstr "" -#: of telebot.types.Message:245 +#: of telebot.types.Message:270 msgid "Optional. Service message: data sent by a Web App" msgstr "" -#: of telebot.types.Message:248 +#: of telebot.types.Message:273 msgid "" "Optional. Inline keyboard attached to the message. login_url buttons are " "represented as ordinary url buttons." msgstr "" -#: of telebot.types.Message:253 +#: of telebot.types.Message +msgid "forward_origin" +msgstr "" + +#: of telebot.types.Message:46 +msgid "Optional. For forwarded messages, information about the original message;" +msgstr "" + +#: of telebot.types.Message:278 msgid ":class:`telebot.types.Message`" msgstr "" @@ -4873,12 +5487,163 @@ msgstr "" msgid ":class:`telebot.types.MessageId`" msgstr "" -#: of telebot.types.OrderInfo:1 -msgid "This object represents information about an order." +#: of telebot.types.MessageOrigin:1 +msgid "This object describes the origin of a message." msgstr "" -#: of telebot.types.OrderInfo:3 -msgid "Telegram Documentation: https://core.telegram.org/bots/api#orderinfo" +#: of telebot.types.MessageOrigin:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#messageorigin" +msgstr "" + +#: of telebot.types.MessageOrigin:5 +msgid "Type of the message origin" +msgstr "" + +#: of telebot.types.MessageOrigin:8 +msgid "Date the message was sent originally in Unix time" +msgstr "" + +#: of telebot.types.MessageOrigin:11 +msgid "User that sent the message originally (for MessageOriginUser)" +msgstr "" + +#: of telebot.types.MessageOrigin:14 +msgid "" +"Name of the user that sent the message originally (for " +"MessageOriginHiddenUser)" +msgstr "" + +#: of telebot.types.MessageOrigin:17 +msgid "Chat that sent the message originally (for MessageOriginChat)" +msgstr "" + +#: of telebot.types.MessageOrigin:20 +msgid "Optional. Author signature for certain cases" +msgstr "" + +#: of telebot.types.MessageOrigin:24 +msgid ":class:`MessageOrigin`" +msgstr "" + +#: of telebot.types.MessageOriginChannel:1 telebot.types.MessageOriginChat:1 +#: telebot.types.MessageOriginHiddenUser:1 telebot.types.MessageOriginUser:1 +msgid "Bases: :py:class:`telebot.types.MessageOrigin`" +msgstr "" + +#: of telebot.types.MessageOriginChannel:1 +msgid "The message was originally sent to a channel chat." +msgstr "" + +#: of telebot.types.MessageOriginChannel:3 +msgid "Channel chat to which the message was originally sent" +msgstr "" + +#: of telebot.types.MessageOriginChannel:9 +msgid "Optional. Signature of the original post author" +msgstr "" + +#: of telebot.types.MessageOriginChat:1 +msgid "The message was originally sent on behalf of a chat to a group chat." +msgstr "" + +#: of telebot.types.MessageOriginChat:3 +msgid "Chat that sent the message originally" +msgstr "" + +#: of telebot.types.MessageOriginChat:6 +msgid "" +"Optional. For messages originally sent by an anonymous chat " +"administrator, original message author signature" +msgstr "" + +#: of telebot.types.MessageOriginHiddenUser:1 +msgid "The message was originally sent by an unknown user." +msgstr "" + +#: of telebot.types.MessageOriginHiddenUser:3 +msgid "Name of the user that sent the message originally" +msgstr "" + +#: of telebot.types.MessageOriginUser:1 +msgid "The message was originally sent by a known user." +msgstr "" + +#: of telebot.types.MessageOriginUser:3 +msgid "User that sent the message originally" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:1 +#: telebot.types.MessageReactionUpdated:1 +msgid "" +"This object represents a service message about a change in the list of " +"the current user's reactions to a message." +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#messagereactioncountupdated" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:5 +msgid "The chat containing the message" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:11 +#: telebot.types.MessageReactionUpdated:17 +msgid "Date of the change in Unix time" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:14 +msgid "List of reactions that are present on the message" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:18 +msgid ":class:`MessageReactionCountUpdated`" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#messagereactionupdated" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:5 +msgid "The chat containing the message the user reacted to" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:8 +msgid "Unique identifier of the message inside the chat" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:11 +msgid "Optional. The user that changed the reaction, if the user isn't anonymous" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:14 +msgid "" +"Optional. The chat on behalf of which the reaction was changed, if the " +"user is anonymous" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:20 +msgid "Previous list of reaction types that were set by the user" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:23 +msgid "New list of reaction types that have been set by the user" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:27 +msgid ":class:`MessageReactionUpdated`" +msgstr "" + +#: of telebot.types.OrderInfo:1 +msgid "This object represents information about an order." +msgstr "" + +#: of telebot.types.OrderInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#orderinfo" msgstr "" #: of telebot.types.OrderInfo:5 @@ -5016,16 +5781,22 @@ msgid "Telegram Documentation: https://core.telegram.org/bots/api#pollanswer" msgstr "" #: of telebot.types.PollAnswer:8 -msgid "The user, who changed the answer to the poll" +msgid "" +"Optional. The chat that changed the answer to the poll, if the voter is " +"anonymous" msgstr "" #: of telebot.types.PollAnswer:11 +msgid "Optional. The user, who changed the answer to the poll" +msgstr "" + +#: of telebot.types.PollAnswer:14 msgid "" "0-based identifiers of answer options, chosen by the user. May be empty " "if the user retracted their vote." msgstr "" -#: of telebot.types.PollAnswer:16 +#: of telebot.types.PollAnswer:19 msgid ":class:`telebot.types.PollAnswer`" msgstr "" @@ -5112,6 +5883,88 @@ msgstr "" msgid ":class:`telebot.types.ProximityAlertTriggered`" msgstr "" +#: of telebot.types.ReactionCount:1 +msgid "" +"This object represents a reaction added to a message along with the " +"number of times it was added." +msgstr "" + +#: of telebot.types.ReactionCount:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#reactioncount" +msgstr "" + +#: of telebot.types.ReactionCount:5 telebot.types.ReactionType:5 +msgid "Type of the reaction" +msgstr "" + +#: of telebot.types.ReactionCount:8 +msgid "Number of times the reaction was added" +msgstr "" + +#: of telebot.types.ReactionCount:12 +msgid ":class:`ReactionCount`" +msgstr "" + +#: of telebot.types.ReactionType:1 +msgid "This object represents a reaction type." +msgstr "" + +#: of telebot.types.ReactionType:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#reactiontype" +msgstr "" + +#: of telebot.types.ReactionType:9 +msgid ":class:`ReactionType`" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:1 telebot.types.ReactionTypeEmoji:1 +msgid "Bases: :py:class:`telebot.types.ReactionType`" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:1 +msgid "This object represents a custom emoji reaction type." +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reactiontypecustomemoji" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:5 +msgid "Type of the reaction, must be custom_emoji" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:8 +msgid "Identifier of the custom emoji" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:12 +msgid ":class:`ReactionTypeCustomEmoji`" +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:1 +msgid "This object represents an emoji reaction type." +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reactiontypeemoji" +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:5 +msgid "Type of the reaction, must be emoji" +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:8 +msgid "Reaction emoji. List is available on the API doc." +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:12 +msgid ":class:`ReactionTypeEmoji`" +msgstr "" + #: of telebot.types.ReplyKeyboardMarkup:1 msgid "" "This object represents a custom keyboard with reply options (see " @@ -5265,6 +6118,65 @@ msgstr "" msgid ":class:`telebot.types.ReplyKeyboardRemove`" msgstr "" +#: of telebot.types.ReplyParameters:1 +msgid "Describes reply parameters for the message that is being sent." +msgstr "" + +#: of telebot.types.ReplyParameters:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#replyparameters" +msgstr "" + +#: of telebot.types.ReplyParameters:5 +msgid "" +"Identifier of the message that will be replied to in the current chat, or" +" in the chat chat_id if it is specified" +msgstr "" + +#: of telebot.types.ReplyParameters:8 +msgid "" +"Optional. If the message to be replied to is from a different chat, " +"unique identifier for the chat or username of the channel (in the format " +"@channelusername)" +msgstr "" + +#: of telebot.types.ReplyParameters:11 +msgid "" +"Optional. Pass True if the message should be sent even if the specified " +"message to be replied to is not found; can be used only for replies in " +"the same chat and forum topic." +msgstr "" + +#: of telebot.types.ReplyParameters:14 +msgid "" +"Optional. Quoted part of the message to be replied to; 0-1024 characters " +"after entities parsing. The quote must be an exact substring of the " +"message to be replied to, including bold, italic, underline, " +"strikethrough, spoiler, and custom_emoji entities. The message will fail " +"to send if the quote isn't found in the original message." +msgstr "" + +#: of telebot.types.ReplyParameters:17 +msgid "" +"Optional. Mode for parsing entities in the quote. See formatting options " +"for more details." +msgstr "" + +#: of telebot.types.ReplyParameters:20 +msgid "" +"Optional. A JSON-serialized list of special entities that appear in the " +"quote. It can be specified instead of quote_parse_mode." +msgstr "" + +#: of telebot.types.ReplyParameters:23 +msgid "" +"Optional. Position of the quote in the original message in UTF-16 code " +"units" +msgstr "" + +#: of telebot.types.ReplyParameters:27 +msgid ":class:`ReplyParameters`" +msgstr "" + #: of telebot.types.SentWebAppMessage:1 telebot.types.WebAppData:1 #: telebot.types.WebAppInfo:1 msgid "" @@ -5476,21 +6388,15 @@ msgstr "" msgid "True, if the sticker set contains video stickers" msgstr "" -#: of telebot.types.StickerSet:20 -msgid "" -"True, if the sticker set contains masks. Deprecated since Bot API 6.2, " -"use sticker_type instead." -msgstr "" - -#: of telebot.types.StickerSet:24 +#: of telebot.types.StickerSet:23 msgid "List of all set stickers" msgstr "" -#: of telebot.types.StickerSet:27 +#: of telebot.types.StickerSet:26 msgid "Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format" msgstr "" -#: of telebot.types.StickerSet:31 +#: of telebot.types.StickerSet:30 msgid ":class:`telebot.types.StickerSet`" msgstr "" @@ -5498,6 +6404,12 @@ msgstr "" msgid "Deprecated since Bot API 6.2, use sticker_type instead." msgstr "" +#: of telebot.types.Story:1 +msgid "" +"This object represents a message about a forwarded story in the chat. " +"Currently holds no information." +msgstr "" + #: of telebot.types.SuccessfulPayment:1 msgid "This object contains basic information about a successful payment." msgstr "" @@ -5520,13 +6432,6 @@ msgstr "" msgid ":class:`telebot.types.SuccessfulPayment`" msgstr "" -#: of telebot.types.SwitchInlineQueryChosenChat:1 telebot.types.User:1 -msgid "" -"Bases: :py:class:`telebot.types.JsonDeserializable`, " -":py:class:`telebot.types.Dictionaryable`, " -":py:class:`telebot.types.JsonSerializable`" -msgstr "" - #: of telebot.types.SwitchInlineQueryChosenChat:1 msgid "" "Represents an inline button that switches the current user to inline mode" @@ -5559,6 +6464,45 @@ msgstr "" msgid ":class:`SwitchInlineQueryChosenChat`" msgstr "" +#: of telebot.types.TextQuote:1 +msgid "" +"This object contains information about the quoted part of a message that " +"is replied to by the given message." +msgstr "" + +#: of telebot.types.TextQuote:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#textquote" +msgstr "" + +#: of telebot.types.TextQuote:5 +msgid "" +"Text of the quoted part of a message that is replied to by the given " +"message" +msgstr "" + +#: of telebot.types.TextQuote:8 +msgid "" +"Optional. Special entities that appear in the quote. Currently, only " +"bold, italic, underline, strikethrough, spoiler, and custom_emoji " +"entities are kept in quotes." +msgstr "" + +#: of telebot.types.TextQuote:11 +msgid "" +"Approximate quote position in the original message in UTF-16 code units " +"as specified by the sender" +msgstr "" + +#: of telebot.types.TextQuote:14 +msgid "" +"Optional. True, if the quote was chosen manually by the message sender. " +"Otherwise, the quote was added automatically by the server." +msgstr "" + +#: of telebot.types.TextQuote:18 +msgid ":class:`TextQuote`" +msgstr "" + #: of telebot.types.Update:1 msgid "" "This object represents an incoming update.At most one of the optional " @@ -5600,66 +6544,94 @@ msgid "" msgstr "" #: of telebot.types.Update:23 +msgid "" +"Optional. A reaction to a message was changed by a user. The bot must be " +"an administrator in the chat and must explicitly specify " +"\"message_reaction\" in the list of allowed_updates to receive these " +"updates. The update isn't received for reactions set by bots." +msgstr "" + +#: of telebot.types.Update:27 +msgid "" +"Optional. Reactions to a message with anonymous reactions were changed. " +"The bot must be an administrator in the chat and must explicitly specify " +"\"message_reaction_count\" in the list of allowed_updates to receive " +"these updates." +msgstr "" + +#: of telebot.types.Update:31 msgid "Optional. New incoming inline query" msgstr "" -#: of telebot.types.Update:26 +#: of telebot.types.Update:34 msgid "" "Optional. The result of an inline query that was chosen by a user and " "sent to their chat partner. Please see our documentation on the feedback " "collecting for details on how to enable these updates for your bot." msgstr "" -#: of telebot.types.Update:31 +#: of telebot.types.Update:39 msgid "Optional. New incoming callback query" msgstr "" -#: of telebot.types.Update:34 +#: of telebot.types.Update:42 msgid "" "Optional. New incoming shipping query. Only for invoices with flexible " "price" msgstr "" -#: of telebot.types.Update:37 +#: of telebot.types.Update:45 msgid "" "Optional. New incoming pre-checkout query. Contains full information " "about checkout" msgstr "" -#: of telebot.types.Update:41 +#: of telebot.types.Update:49 msgid "" "Optional. New poll state. Bots receive only updates about stopped polls " "and polls, which are sent by the bot" msgstr "" -#: of telebot.types.Update:45 +#: of telebot.types.Update:53 msgid "" "Optional. A user changed their answer in a non-anonymous poll. Bots " "receive new votes only in polls that were sent by the bot itself." msgstr "" -#: of telebot.types.Update:49 +#: of telebot.types.Update:57 msgid "" "Optional. The bot's chat member status was updated in a chat. For private" " chats, this update is received only when the bot is blocked or unblocked" " by the user." msgstr "" -#: of telebot.types.Update:53 +#: of telebot.types.Update:61 msgid "" "Optional. A chat member's status was updated in a chat. The bot must be " "an administrator in the chat and must explicitly specify “chat_member” in" " the list of allowed_updates to receive these updates." msgstr "" -#: of telebot.types.Update:57 +#: of telebot.types.Update:65 msgid "" "Optional. A request to join the chat has been sent. The bot must have the" " can_invite_users administrator right in the chat to receive these " "updates." msgstr "" -#: of telebot.types.Update:62 +#: of telebot.types.Update:69 +msgid "" +"Optional. A chat boost was added or changed. The bot must be an " +"administrator in the chat to receive these updates." +msgstr "" + +#: of telebot.types.Update:72 +msgid "" +"Optional. A chat boost was removed. The bot must be an administrator in " +"the chat to receive these updates." +msgstr "" + +#: of telebot.types.Update:76 msgid ":class:`telebot.types.Update`" msgstr "" @@ -5742,6 +6714,22 @@ msgstr "" msgid "return" msgstr "" +#: of telebot.types.UserChatBoosts:1 +msgid "This object represents a list of boosts added to a chat by a user." +msgstr "" + +#: of telebot.types.UserChatBoosts:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#userchatboosts" +msgstr "" + +#: of telebot.types.UserChatBoosts:5 +msgid "The list of boosts added to the chat by the user" +msgstr "" + +#: of telebot.types.UserChatBoosts:9 +msgid ":class:`UserChatBoosts`" +msgstr "" + #: of telebot.types.UserProfilePhotos:1 msgid "This object represent a user's profile pictures." msgstr "" @@ -5764,30 +6752,33 @@ msgstr "" msgid ":class:`telebot.types.UserProfilePhotos`" msgstr "" -#: of telebot.types.UserShared:1 +#: of telebot.types.UsersShared:1 msgid "" -"This object contains information about the user whose identifier was " -"shared with the bot using a `telebot.types.KeyboardButtonRequestUser` " -"button." +"This object contains information about the users whose identifiers were " +"shared with the bot using a KeyboardButtonRequestUsers button." msgstr "" -#: of telebot.types.UserShared:4 -msgid "Telegram documentation: https://core.telegram.org/bots/api#usershared" +#: of telebot.types.UsersShared:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#usersshared" msgstr "" -#: of telebot.types.UserShared:9 +#: of telebot.types.UsersShared:6 +msgid "Identifier of the request" +msgstr "" + +#: of telebot.types.UsersShared:9 msgid "" -"Identifier of the shared user. This number may have more than 32 " +"Identifiers of the shared users. These numbers may have more than 32 " "significant bits and some programming languages may have " -"difficulty/silent defects in interpreting it. But it has at most 52 " -"significant bits, so a 64-bit integer or double-precision float type are " -"safe for storing this identifier. The bot may not have access to the user" -" and could be unable to use this identifier, unless the user is already " -"known to the bot by some other means." +"difficulty/silent defects in interpreting them. But they have at most 52 " +"significant bits, so 64-bit integers or double-precision float types are " +"safe for storing these identifiers. The bot may not have access to the " +"users and could be unable to use these identifiers unless the users are " +"already known to the bot by some other means." msgstr "" -#: of telebot.types.UserShared:16 -msgid ":class:`telebot.types.UserShared`" +#: of telebot.types.UsersShared:18 +msgid ":class:`UsersShared`" msgstr "" #: of telebot.types.Venue:1 @@ -6065,20 +7056,34 @@ msgstr "" #: of telebot.types.WriteAccessAllowed:1 msgid "" -"This object represents a service message about a user allowed to post " -"messages in the chat. Currently holds no information." +"This object represents a service message about a user allowing a bot to " +"write messages after adding it to the attachment menu, launching a Web " +"App from a link, or accepting an explicit request from a Web App sent by " +"the method requestWriteAccess." msgstr "" -#: of telebot.types.WriteAccessAllowed:4 +#: of telebot.types.WriteAccessAllowed:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#writeaccessallowed" msgstr "" -#: of telebot.types.WriteAccessAllowed:6 +#: of telebot.types.WriteAccessAllowed:7 +msgid "" +"Optional. True, if the access was granted after the user accepted an " +"explicit request from a Web App sent by the method requestWriteAccess" +msgstr "" + +#: of telebot.types.WriteAccessAllowed:11 msgid "Optional. Name of the Web App which was launched from a link" msgstr "" +#: of telebot.types.WriteAccessAllowed:14 +msgid "" +"Optional. True, if the access was granted when the bot was added to the " +"attachment or side menu" +msgstr "" + #~ msgid "Type of the result, must be animation" #~ msgstr "" @@ -6109,3 +7114,127 @@ msgstr "" #~ "can_send_media_messages" #~ msgstr "" +#~ msgid "" +#~ "Optional. Message with the callback " +#~ "button that originated the query. Note" +#~ " that message content and message " +#~ "date will not be available if the" +#~ " message is too old" +#~ msgstr "" + +#~ msgid "" +#~ "deprecated. True, if the user is " +#~ "allowed to send audios, documents, " +#~ "photos, videos, video notes and voice" +#~ " notes" +#~ msgstr "" + +#~ msgid "Optional. Disables link previews for links in the sent message" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. If specified, pressing the " +#~ "button will open a list of " +#~ "suitable users. Tapping on any user " +#~ "will send their identifier to the " +#~ "bot in a “user_shared” service message." +#~ " Available in private chats only." +#~ msgstr "" + +#~ msgid "" +#~ "Telegram documentation: " +#~ "https://core.telegram.org/bots/api#keyboardbuttonrequestuser" +#~ msgstr "" + +#~ msgid "" +#~ "Signed 32-bit identifier of the request," +#~ " which will be received back in " +#~ "the UserShared object. Must be unique" +#~ " within the message" +#~ msgstr "" + +#~ msgid ":class:`telebot.types.KeyboardButtonRequestUser`" +#~ msgstr "" + +#~ msgid "Optional. For forwarded messages, sender of the original message" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For messages forwarded from " +#~ "channels or from anonymous administrators, " +#~ "information about the original sender " +#~ "chat" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For messages forwarded from " +#~ "channels, identifier of the original " +#~ "message in the channel" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For forwarded messages that " +#~ "were originally sent in channels or " +#~ "by an anonymous chat administrator, " +#~ "signature of the message sender if " +#~ "present" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. Sender's name for messages " +#~ "forwarded from users who disallow adding" +#~ " a link to their account in " +#~ "forwarded messages" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For forwarded messages, date " +#~ "the original message was sent in " +#~ "Unix time" +#~ msgstr "" + +#~ msgid "The user, who changed the answer to the poll" +#~ msgstr "" + +#~ msgid "" +#~ "True, if the sticker set contains " +#~ "masks. Deprecated since Bot API 6.2, " +#~ "use sticker_type instead." +#~ msgstr "" + +#~ msgid "" +#~ "This object contains information about " +#~ "the user whose identifier was shared " +#~ "with the bot using a " +#~ "`telebot.types.KeyboardButtonRequestUser` button." +#~ msgstr "" + +#~ msgid "Telegram documentation: https://core.telegram.org/bots/api#usershared" +#~ msgstr "" + +#~ msgid "" +#~ "Identifier of the shared user. This " +#~ "number may have more than 32 " +#~ "significant bits and some programming " +#~ "languages may have difficulty/silent defects" +#~ " in interpreting it. But it has " +#~ "at most 52 significant bits, so a" +#~ " 64-bit integer or double-precision " +#~ "float type are safe for storing " +#~ "this identifier. The bot may not " +#~ "have access to the user and could" +#~ " be unable to use this identifier," +#~ " unless the user is already known " +#~ "to the bot by some other means." +#~ msgstr "" + +#~ msgid ":class:`telebot.types.UserShared`" +#~ msgstr "" + +#~ msgid "" +#~ "This object represents a service message" +#~ " about a user allowed to post " +#~ "messages in the chat. Currently holds" +#~ " no information." +#~ msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index 8644f09fb..570c22026 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-08 23:07+0500\n" +"POT-Creation-Date: 2024-01-05 22:16+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -89,12 +89,14 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.callback_query_handler #: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.chat_member_handler #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.close_forum_topic #: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.copy_messages #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic #: telebot.async_telebot.AsyncTeleBot.create_invoice_link @@ -104,6 +106,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic #: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_messages #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set @@ -123,6 +126,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.enable_saving_states #: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.forward_messages #: telebot.async_telebot.AsyncTeleBot.get_chat #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators #: telebot.async_telebot.AsyncTeleBot.get_chat_member @@ -140,6 +144,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info #: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic @@ -147,6 +152,8 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.inline_handler #: telebot.async_telebot.AsyncTeleBot.leave_chat #: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.pin_chat_message #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler @@ -157,6 +164,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.promote_chat_member #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler @@ -164,11 +172,15 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler #: telebot.async_telebot.AsyncTeleBot.register_inline_handler #: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_handler #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic #: telebot.async_telebot.AsyncTeleBot.reopen_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.reply_to @@ -204,6 +216,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.set_chat_title #: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights #: telebot.async_telebot.AsyncTeleBot.set_my_description @@ -227,6 +240,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.async_telebot.AsyncTeleBot.unhide_general_forum_topic #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message #: telebot.async_telebot.AsyncTeleBot.upload_sticker_file #: telebot.asyncio_filters.TextFilter @@ -300,12 +314,14 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.callback_query_handler #: telebot.async_telebot.AsyncTeleBot.channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.chat_member_handler #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.close #: telebot.async_telebot.AsyncTeleBot.close_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.copy_messages #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic #: telebot.async_telebot.AsyncTeleBot.create_invoice_link @@ -315,6 +331,7 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic #: telebot.async_telebot.AsyncTeleBot.delete_message +#: telebot.async_telebot.AsyncTeleBot.delete_messages #: telebot.async_telebot.AsyncTeleBot.delete_my_commands #: telebot.async_telebot.AsyncTeleBot.delete_state #: telebot.async_telebot.AsyncTeleBot.delete_sticker_from_set @@ -332,6 +349,7 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.edited_message_handler #: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.forward_messages #: telebot.async_telebot.AsyncTeleBot.get_chat #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators #: telebot.async_telebot.AsyncTeleBot.get_chat_member @@ -350,6 +368,7 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info #: telebot.async_telebot.AsyncTeleBot.infinity_polling @@ -357,6 +376,8 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.leave_chat #: telebot.async_telebot.AsyncTeleBot.log_out #: telebot.async_telebot.AsyncTeleBot.message_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.pin_chat_message #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler @@ -367,17 +388,22 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.promote_chat_member #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler #: telebot.async_telebot.AsyncTeleBot.register_edited_channel_post_handler #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler #: telebot.async_telebot.AsyncTeleBot.register_inline_handler #: telebot.async_telebot.AsyncTeleBot.register_message_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler #: telebot.async_telebot.AsyncTeleBot.register_poll_handler #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic #: telebot.async_telebot.AsyncTeleBot.reply_to #: telebot.async_telebot.AsyncTeleBot.reset_data @@ -412,6 +438,7 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.set_chat_title #: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail #: telebot.async_telebot.AsyncTeleBot.set_game_score +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction #: telebot.async_telebot.AsyncTeleBot.set_my_commands #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights #: telebot.async_telebot.AsyncTeleBot.set_my_description @@ -434,6 +461,7 @@ msgstr "Класс с методом check(message)" #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message #: telebot.async_telebot.AsyncTeleBot.upload_sticker_file #: telebot.asyncio_filters.TextFilter @@ -445,6 +473,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.add_data:10 #: telebot.async_telebot.AsyncTeleBot.callback_query_handler:9 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:17 +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler:8 #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:10 #: telebot.async_telebot.AsyncTeleBot.chat_member_handler:11 #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:10 @@ -459,14 +488,19 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.process_new_updates:8 #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:23 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:13 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:26 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:26 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:14 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:14 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:13 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:14 +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:8 #: telebot.async_telebot.AsyncTeleBot.reset_data:9 #: telebot.async_telebot.AsyncTeleBot.set_state:18 #: telebot.async_telebot.AsyncTeleBot.set_update_listener:15 @@ -599,6 +633,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_sticker_position_in_set:11 #: telebot.async_telebot.AsyncTeleBot.set_sticker_set_thumbnail:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:13 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:11 msgid "On success, True is returned." msgstr "В случае успеха возвращается True." @@ -613,6 +648,7 @@ msgstr "В случае успеха возвращается True." #: telebot.async_telebot.AsyncTeleBot.ban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.close_forum_topic #: telebot.async_telebot.AsyncTeleBot.copy_message +#: telebot.async_telebot.AsyncTeleBot.copy_messages #: telebot.async_telebot.AsyncTeleBot.create_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.create_forum_topic #: telebot.async_telebot.AsyncTeleBot.create_invoice_link @@ -636,6 +672,7 @@ msgstr "В случае успеха возвращается True." #: telebot.async_telebot.AsyncTeleBot.edit_message_text #: telebot.async_telebot.AsyncTeleBot.export_chat_invite_link #: telebot.async_telebot.AsyncTeleBot.forward_message +#: telebot.async_telebot.AsyncTeleBot.forward_messages #: telebot.async_telebot.AsyncTeleBot.get_chat #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators #: telebot.async_telebot.AsyncTeleBot.get_chat_member @@ -650,6 +687,7 @@ msgstr "В случае успеха возвращается True." #: telebot.async_telebot.AsyncTeleBot.get_state #: telebot.async_telebot.AsyncTeleBot.get_sticker_set #: telebot.async_telebot.AsyncTeleBot.get_updates +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos #: telebot.async_telebot.AsyncTeleBot.get_webhook_info #: telebot.async_telebot.AsyncTeleBot.log_out @@ -701,6 +739,7 @@ msgstr "В случае успеха возвращается True." #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message #: telebot.async_telebot.AsyncTeleBot.upload_sticker_file msgid "Return type" @@ -730,7 +769,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.leave_chat:8 #: telebot.async_telebot.AsyncTeleBot.log_out:11 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:19 -#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:61 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:70 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:14 #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:61 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:26 @@ -741,6 +780,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.set_chat_photo:16 #: telebot.async_telebot.AsyncTeleBot.set_chat_title:17 #: telebot.async_telebot.AsyncTeleBot.set_custom_emoji_sticker_set_thumbnail:11 +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction:18 #: telebot.async_telebot.AsyncTeleBot.set_my_commands:18 #: telebot.async_telebot.AsyncTeleBot.set_my_default_administrator_rights:18 #: telebot.async_telebot.AsyncTeleBot.set_sticker_emoji_list:11 @@ -753,6 +793,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.unban_chat_sender_chat:15 #: telebot.async_telebot.AsyncTeleBot.unpin_all_chat_messages:12 #: telebot.async_telebot.AsyncTeleBot.unpin_all_forum_topic_messages:14 +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:12 #: telebot.async_telebot.AsyncTeleBot.unpin_chat_message:15 msgid ":obj:`bool`" msgstr "" @@ -1074,6 +1115,7 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.ban_chat_member:12 #: telebot.async_telebot.AsyncTeleBot.decline_chat_join_request:11 #: telebot.async_telebot.AsyncTeleBot.get_chat_member:8 +#: telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:8 #: telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:6 #: telebot.async_telebot.AsyncTeleBot.promote_chat_member:11 #: telebot.async_telebot.AsyncTeleBot.restrict_chat_member:14 @@ -1089,7 +1131,7 @@ msgstr "Уникальный id сделавшего запрос пользов #: telebot.async_telebot.AsyncTeleBot.delete_my_commands:16 #: telebot.async_telebot.AsyncTeleBot.log_out:10 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:18 -#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:60 +#: telebot.async_telebot.AsyncTeleBot.promote_chat_member:69 #: telebot.async_telebot.AsyncTeleBot.set_chat_administrator_custom_title:17 #: telebot.async_telebot.AsyncTeleBot.set_chat_description:13 #: telebot.async_telebot.AsyncTeleBot.set_chat_menu_button:14 @@ -1156,6 +1198,7 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.ban_chat_member:24 #: telebot.async_telebot.AsyncTeleBot.delete_chat_sticker_set:10 #: telebot.async_telebot.AsyncTeleBot.delete_message:22 +#: telebot.async_telebot.AsyncTeleBot.delete_messages:14 #: telebot.async_telebot.AsyncTeleBot.delete_sticker_set:6 #: telebot.async_telebot.AsyncTeleBot.delete_webhook:12 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:25 @@ -1192,13 +1235,16 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.close_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.close_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.copy_message:5 +#: telebot.async_telebot.AsyncTeleBot.copy_messages:3 #: telebot.async_telebot.AsyncTeleBot.create_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.delete_message:13 +#: telebot.async_telebot.AsyncTeleBot.delete_messages:8 #: telebot.async_telebot.AsyncTeleBot.edit_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.edit_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.edit_message_live_location:13 #: telebot.async_telebot.AsyncTeleBot.forward_message:8 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:3 #: telebot.async_telebot.AsyncTeleBot.hide_general_forum_topic:7 #: telebot.async_telebot.AsyncTeleBot.pin_chat_message:7 #: telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:7 @@ -1242,18 +1288,22 @@ msgstr "" #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:4 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:10 +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler:4 #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:5 #: telebot.async_telebot.AsyncTeleBot.chat_member_handler:6 #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:5 #: telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:10 #: telebot.async_telebot.AsyncTeleBot.edited_message_handler:11 #: telebot.async_telebot.AsyncTeleBot.inline_handler:4 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler:4 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler:4 #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:5 #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:5 #: telebot.async_telebot.AsyncTeleBot.poll_handler:4 #: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:4 #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:6 @@ -1261,17 +1311,22 @@ msgstr "" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:15 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:15 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:6 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:6 +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:4 #: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:4 msgid "Function executed as a filter" msgstr "Функция, используемая в качестве фильтра" #: of telebot.async_telebot.AsyncTeleBot.callback_query_handler:7 #: telebot.async_telebot.AsyncTeleBot.channel_post_handler:16 +#: telebot.async_telebot.AsyncTeleBot.chat_boost_handler:7 #: telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:8 #: telebot.async_telebot.AsyncTeleBot.chat_member_handler:9 #: telebot.async_telebot.AsyncTeleBot.chosen_inline_handler:8 @@ -1279,12 +1334,15 @@ msgstr "Функция, используемая в качестве фильт #: telebot.async_telebot.AsyncTeleBot.edited_message_handler:20 #: telebot.async_telebot.AsyncTeleBot.inline_handler:7 #: telebot.async_telebot.AsyncTeleBot.message_handler:50 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler:7 +#: telebot.async_telebot.AsyncTeleBot.message_reaction_handler:7 #: telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:8 #: telebot.async_telebot.AsyncTeleBot.poll_answer_handler:8 #: telebot.async_telebot.AsyncTeleBot.poll_handler:7 #: telebot.async_telebot.AsyncTeleBot.pre_checkout_query_handler:7 #: telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:11 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:12 @@ -1292,11 +1350,15 @@ msgstr "Функция, используемая в качестве фильт #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:24 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:24 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:12 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:12 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:11 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:11 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:12 +#: telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:7 #: telebot.async_telebot.AsyncTeleBot.shipping_query_handler:7 msgid "Optional keyword arguments(custom filters)" msgstr "Необязательные именованные аргументы(кастомные фильтры)" @@ -1336,6 +1398,15 @@ msgstr "Необязательное регулярное выражение." msgid "Supported message content types. Must be a list. Defaults to ['text']." msgstr "Обрабатываемые виды контента. Обязан быть списком. По умолчанию ['text']" +#: of telebot.async_telebot.AsyncTeleBot.chat_boost_handler:1 +#, fuzzy +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostUpdated` object." +msgstr "" +"Обрабатывает новый callback запрос. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." + #: of telebot.async_telebot.AsyncTeleBot.chat_join_request_handler:1 msgid "" "Handles a request to join the chat has been sent. The bot must have the " @@ -1439,6 +1510,7 @@ msgstr "" "работы поллинга или вебхука." #: of telebot.async_telebot.AsyncTeleBot.copy_message:1 +#: telebot.async_telebot.AsyncTeleBot.copy_messages:1 msgid "Use this method to copy messages of any kind." msgstr "Используйте этот метод для копирования любых сообщений." @@ -1447,7 +1519,9 @@ msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" msgstr "Документация Telegram: https://core.telegram.org/bots/api#copymessage" #: of telebot.async_telebot.AsyncTeleBot.copy_message:8 +#: telebot.async_telebot.AsyncTeleBot.copy_messages:6 #: telebot.async_telebot.AsyncTeleBot.forward_message:11 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:6 msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" @@ -1621,8 +1695,6 @@ msgid "Identifier of a message thread, in which the message will be sent" msgstr "id топика, в который нужно отправить сообщение" #: of telebot.async_telebot.AsyncTeleBot.copy_message:45 -#: telebot.async_telebot.AsyncTeleBot.forward_message:26 -#: telebot.async_telebot.AsyncTeleBot.reply_to:11 #: telebot.async_telebot.AsyncTeleBot.send_animation:66 #: telebot.async_telebot.AsyncTeleBot.send_audio:66 #: telebot.async_telebot.AsyncTeleBot.send_contact:44 @@ -1631,6 +1703,7 @@ msgstr "id топика, в который нужно отправить соо #: telebot.async_telebot.AsyncTeleBot.send_game:32 #: telebot.async_telebot.AsyncTeleBot.send_invoice:101 #: telebot.async_telebot.AsyncTeleBot.send_location:49 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:30 #: telebot.async_telebot.AsyncTeleBot.send_message:46 #: telebot.async_telebot.AsyncTeleBot.send_photo:48 #: telebot.async_telebot.AsyncTeleBot.send_poll:70 @@ -1639,26 +1712,61 @@ msgstr "id топика, в который нужно отправить соо #: telebot.async_telebot.AsyncTeleBot.send_video:67 #: telebot.async_telebot.AsyncTeleBot.send_video_note:51 #: telebot.async_telebot.AsyncTeleBot.send_voice:49 -msgid "On success, the sent Message is returned." +msgid "Reply parameters." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_message:48 +#, fuzzy +msgid "On success, the MessageId of the sent message is returned." msgstr "В случае успеха возвращает отправленное сообщение (Message)." -#: of telebot.async_telebot.AsyncTeleBot.copy_message:46 -#: telebot.async_telebot.AsyncTeleBot.forward_message:27 -#: telebot.async_telebot.AsyncTeleBot.reply_to:12 -#: telebot.async_telebot.AsyncTeleBot.send_animation:67 -#: telebot.async_telebot.AsyncTeleBot.send_audio:67 -#: telebot.async_telebot.AsyncTeleBot.send_contact:45 -#: telebot.async_telebot.AsyncTeleBot.send_dice:36 -#: telebot.async_telebot.AsyncTeleBot.send_document:60 -#: telebot.async_telebot.AsyncTeleBot.send_location:50 -#: telebot.async_telebot.AsyncTeleBot.send_message:47 -#: telebot.async_telebot.AsyncTeleBot.send_photo:49 -#: telebot.async_telebot.AsyncTeleBot.send_sticker:43 -#: telebot.async_telebot.AsyncTeleBot.send_venue:58 -#: telebot.async_telebot.AsyncTeleBot.send_video:68 -#: telebot.async_telebot.AsyncTeleBot.send_video_note:52 -msgid ":class:`telebot.types.Message`" +#: of telebot.async_telebot.AsyncTeleBot.copy_message:49 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:22 +#, fuzzy +msgid ":class:`telebot.types.MessageID`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:9 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:9 +#, fuzzy +msgid "Message identifiers in the chat specified in from_chat_id" +msgstr "id сообщения в чате, заданном в from_chat_id" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:12 +#: telebot.async_telebot.AsyncTeleBot.forward_message:5 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:12 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" msgstr "" +"Отправить сообщение, при получении которого пользователи получат " +"уведомление без звука" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:15 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:15 +#: telebot.async_telebot.AsyncTeleBot.send_media_group:27 +msgid "Identifier of a message thread, in which the messages will be sent" +msgstr "id топика, в который будет отправлена группа медиа" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:18 +#: telebot.async_telebot.AsyncTeleBot.forward_message:17 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:18 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:21 +msgid "Pass True to copy the messages without their captions" +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:24 +#, fuzzy +msgid "On success, an array of MessageId of the sent messages is returned." +msgstr "В случае успеха возвращает отправленное сообщение (Message)." + +#: of telebot.async_telebot.AsyncTeleBot.copy_messages:25 +#, fuzzy +msgid ":obj:`list` of :class:`telebot.types.MessageID`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" #: of telebot.async_telebot.AsyncTeleBot.create_chat_invite_link:1 msgid "" @@ -2137,6 +2245,24 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#de msgid "Identifier of the message to delete" msgstr "id сообщения, которое нужно удалить" +#: of telebot.async_telebot.AsyncTeleBot.delete_messages:1 +msgid "" +"Use this method to delete multiple messages in a chat. The number of " +"messages to be deleted must not exceed 100. If the chat is a private " +"chat, the user must be an administrator of the chat for this to work and " +"must have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.delete_messages:6 +#, fuzzy +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessages" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletemessage" + +#: of telebot.async_telebot.AsyncTeleBot.delete_messages:11 +#, fuzzy +msgid "Identifiers of the messages to be deleted" +msgstr "id сообщения, которое нужно удалить" + #: of telebot.async_telebot.AsyncTeleBot.delete_my_commands:1 msgid "" "Use this method to delete the list of the bot's commands for the given " @@ -2385,7 +2511,7 @@ msgstr "JSON-сериализованный объект inline клавиату #: of telebot.async_telebot.AsyncTeleBot.edit_message_caption:26 #: telebot.async_telebot.AsyncTeleBot.edit_message_media:22 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:17 -#: telebot.async_telebot.AsyncTeleBot.edit_message_text:29 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:32 msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." @@ -2538,7 +2664,7 @@ msgstr "Обязательный, если не указан inline_message_id. #: of telebot.async_telebot.AsyncTeleBot.edit_message_media:23 #: telebot.async_telebot.AsyncTeleBot.edit_message_reply_markup:18 -#: telebot.async_telebot.AsyncTeleBot.edit_message_text:30 +#: telebot.async_telebot.AsyncTeleBot.edit_message_text:33 #: telebot.async_telebot.AsyncTeleBot.set_game_score:27 msgid ":obj:`types.Message` or :obj:`bool`" msgstr "" @@ -2585,6 +2711,12 @@ msgstr "" msgid "Disables link previews for links in this message" msgstr "Отключает превью ссылок в сообщении" +#: of telebot.async_telebot.AsyncTeleBot.edit_message_text:29 +msgid "" +"A JSON-serialized object for options used to automatically generate " +"Telegram link previews for messages." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.edited_channel_post_handler:1 msgid "" "Handles new version of a channel post that is known to the bot and was " @@ -2655,6 +2787,7 @@ msgid "exported invite link as String on success." msgstr "новая ссылка-приглашение (String) в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.forward_message:1 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:1 msgid "Use this method to forward messages of any kind." msgstr "Используйте этот метод, чтобы переслать любое сообщение." @@ -2662,18 +2795,6 @@ msgstr "Используйте этот метод, чтобы переслат msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" msgstr "Документация Telegram: https://core.telegram.org/bots/api#forwardmessage" -#: of telebot.async_telebot.AsyncTeleBot.forward_message:5 -msgid "" -"Sends the message silently. Users will receive a notification with no " -"sound" -msgstr "" -"Отправить сообщение, при получении которого пользователи получат " -"уведомление без звука" - -#: of telebot.async_telebot.AsyncTeleBot.forward_message:17 -msgid "Protects the contents of the forwarded message from forwarding and saving" -msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" - #: of telebot.async_telebot.AsyncTeleBot.forward_message:23 #: telebot.async_telebot.AsyncTeleBot.send_message:43 msgid "" @@ -2683,6 +2804,45 @@ msgstr "" "Уникальный id топика, в который нужно переслать сообщение; только для " "супергрупп с топиками" +#: of telebot.async_telebot.AsyncTeleBot.forward_message:26 +#: telebot.async_telebot.AsyncTeleBot.forward_messages:21 +#: telebot.async_telebot.AsyncTeleBot.reply_to:11 +#: telebot.async_telebot.AsyncTeleBot.send_animation:69 +#: telebot.async_telebot.AsyncTeleBot.send_audio:69 +#: telebot.async_telebot.AsyncTeleBot.send_contact:47 +#: telebot.async_telebot.AsyncTeleBot.send_dice:38 +#: telebot.async_telebot.AsyncTeleBot.send_document:62 +#: telebot.async_telebot.AsyncTeleBot.send_game:35 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:104 +#: telebot.async_telebot.AsyncTeleBot.send_location:52 +#: telebot.async_telebot.AsyncTeleBot.send_message:52 +#: telebot.async_telebot.AsyncTeleBot.send_photo:51 +#: telebot.async_telebot.AsyncTeleBot.send_poll:73 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:45 +#: telebot.async_telebot.AsyncTeleBot.send_venue:60 +#: telebot.async_telebot.AsyncTeleBot.send_video:70 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:54 +#: telebot.async_telebot.AsyncTeleBot.send_voice:52 +msgid "On success, the sent Message is returned." +msgstr "В случае успеха возвращает отправленное сообщение (Message)." + +#: of telebot.async_telebot.AsyncTeleBot.forward_message:27 +#: telebot.async_telebot.AsyncTeleBot.reply_to:12 +#: telebot.async_telebot.AsyncTeleBot.send_animation:70 +#: telebot.async_telebot.AsyncTeleBot.send_audio:70 +#: telebot.async_telebot.AsyncTeleBot.send_contact:48 +#: telebot.async_telebot.AsyncTeleBot.send_dice:39 +#: telebot.async_telebot.AsyncTeleBot.send_document:63 +#: telebot.async_telebot.AsyncTeleBot.send_location:53 +#: telebot.async_telebot.AsyncTeleBot.send_message:53 +#: telebot.async_telebot.AsyncTeleBot.send_photo:52 +#: telebot.async_telebot.AsyncTeleBot.send_sticker:46 +#: telebot.async_telebot.AsyncTeleBot.send_venue:61 +#: telebot.async_telebot.AsyncTeleBot.send_video:71 +#: telebot.async_telebot.AsyncTeleBot.send_video_note:55 +msgid ":class:`telebot.types.Message`" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.get_chat:1 msgid "" "Use this method to get up to date information about the chat (current " @@ -2702,6 +2862,7 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#ge #: telebot.async_telebot.AsyncTeleBot.get_chat_administrators:7 #: telebot.async_telebot.AsyncTeleBot.get_chat_member_count:5 #: telebot.async_telebot.AsyncTeleBot.leave_chat:5 +#: telebot.async_telebot.AsyncTeleBot.set_message_reaction:5 msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" @@ -3160,6 +3321,43 @@ msgstr "Возвращается массив объектов Update." msgid ":obj:`list` of :class:`telebot.types.Update`" msgstr "" +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:1 +#, fuzzy +msgid "" +"Use this method to get the list of boosts added to a chat by a user. " +"Requires administrator rights in the chat. Returns a UserChatBoosts " +"object." +msgstr "" +"Используйте этот метод, чтобы получить текущие права администратора для " +"бота по умолчанию. Возвращает объект ChatAdministratorRights в случае " +"успеха." + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:3 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserchatboosts" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchat" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:5 +#: telebot.async_telebot.AsyncTeleBot.send_chat_action:10 +#: telebot.async_telebot.AsyncTeleBot.send_contact:5 +#: telebot.async_telebot.AsyncTeleBot.send_poll:6 +#: telebot.async_telebot.AsyncTeleBot.send_venue:5 +#: telebot.async_telebot.AsyncTeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "Уникальный id чата или username канала" + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:11 +#, fuzzy +msgid "On success, a UserChatBoosts object is returned." +msgstr "В случае успеха, возвращается объект StickerSet." + +#: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:12 +#, fuzzy +msgid ":class:`telebot.types.UserChatBoosts`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + #: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:1 msgid "" "Use this method to get a list of profile pictures for a user. Returns a " @@ -3407,6 +3605,25 @@ msgstr "" msgid "decorated function" msgstr "декорируемая функция" +#: of telebot.async_telebot.AsyncTeleBot.message_reaction_count_handler:1 +#, fuzzy +msgid "" +"Handles new incoming message reaction count. As a parameter to the " +"decorator function, it passes " +":class:`telebot.types.MessageReactionCountUpdated` object." +msgstr "" +"Обрабатывает новый callback запрос. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." + +#: of telebot.async_telebot.AsyncTeleBot.message_reaction_handler:1 +#, fuzzy +msgid "" +"Handles new incoming message reaction. As a parameter to the decorator " +"function, it passes :class:`telebot.types.MessageReactionUpdated` object." +msgstr "" +"Обрабатывает новый callback запрос. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." + #: of telebot.async_telebot.AsyncTeleBot.my_chat_member_handler:1 msgid "" "Handles update in a status of a bot. For private chats, this update is " @@ -3655,12 +3872,34 @@ msgstr "" "Передайте True, если пользователю разрешено создавать, переименовывать, " "закрывать, и возобновлять топики, только для супергрупп" +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:60 +#, fuzzy +msgid "Pass True if the administrator can create the channel's stories" +msgstr "" +"Передайте True, если администратор может создавать посты в канале, только" +" для каналов" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:63 +#, fuzzy +msgid "Pass True if the administrator can edit the channel's stories" +msgstr "" +"Передайте True, если администратор может изменять сообщения других " +"пользователей, только для каналов" + +#: of telebot.async_telebot.AsyncTeleBot.promote_chat_member:66 +#, fuzzy +msgid "Pass True if the administrator can delete the channel's stories" +msgstr "" +"Передайте True, если администратор может создавать посты в канале, только" +" для каналов" + #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." msgstr "Регистрирует хендлер callback query." #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:3 @@ -3668,16 +3907,20 @@ msgstr "Регистрирует хендлер callback query." #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:3 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:3 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:3 msgid "function to be called" msgstr "функция-хендлер" #: of telebot.async_telebot.AsyncTeleBot.register_callback_query_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_channel_post_handler:18 +#: telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_chat_member_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_chosen_inline_handler:9 @@ -3685,10 +3928,13 @@ msgstr "функция-хендлер" #: telebot.async_telebot.AsyncTeleBot.register_edited_message_handler:21 #: telebot.async_telebot.AsyncTeleBot.register_inline_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_message_handler:21 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:9 +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_poll_answer_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_poll_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_pre_checkout_query_handler:8 +#: telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:9 #: telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:9 msgid "" "True if you need to pass TeleBot instance to handler(useful for " @@ -3714,6 +3960,11 @@ msgstr "список команд" msgid "Regular expression" msgstr "Регулярное выражение" +#: of telebot.async_telebot.AsyncTeleBot.register_chat_boost_handler:1 +#, fuzzy +msgid "Registers chat boost handler." +msgstr "Регистрирует хендлер смены состояний участников чата." + #: of telebot.async_telebot.AsyncTeleBot.register_chat_join_request_handler:1 msgid "Registers chat join request handler." msgstr "Регистрирует хендлер запросов на вступление в чат." @@ -3754,6 +4005,17 @@ msgstr "Регистрирует хендлер сообщений." msgid "List of chat types" msgstr "Список видов чатов" +#: of +#: telebot.async_telebot.AsyncTeleBot.register_message_reaction_count_handler:1 +#, fuzzy +msgid "Registers message reaction count handler." +msgstr "Регистрирует хендлер сообщений." + +#: of telebot.async_telebot.AsyncTeleBot.register_message_reaction_handler:1 +#, fuzzy +msgid "Registers message reaction handler." +msgstr "Регистрирует хендлер сообщений." + #: of telebot.async_telebot.AsyncTeleBot.register_my_chat_member_handler:1 msgid "Registers my chat member handler." msgstr "Регистрирует хендлер изменений статуса бота." @@ -3770,6 +4032,11 @@ msgstr "Регистрирует хендлер изменений состоя msgid "Registers pre-checkout request handler." msgstr "Регистрирует хендлер pre-checkout query." +#: of telebot.async_telebot.AsyncTeleBot.register_removed_chat_boost_handler:1 +#, fuzzy +msgid "Registers removed chat boost handler." +msgstr "Регистрирует хендлер изменений статуса бота." + #: of telebot.async_telebot.AsyncTeleBot.register_shipping_query_handler:1 msgid "Registers shipping query handler." msgstr "Регистрирует хендлер shipping query." @@ -3778,6 +4045,15 @@ msgstr "Регистрирует хендлер shipping query." msgid "Alternative for delete_webhook but uses set_webhook" msgstr "Альтернатива delete_webhook, но использует set_webhook" +#: of telebot.async_telebot.AsyncTeleBot.removed_chat_boost_handler:1 +#, fuzzy +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostRemoved` object." +msgstr "" +"Обрабатывает inline query. В качестве параметра, передаёт в декорируемую " +"функцию объект :class:`telebot.types.InlineQuery`." + #: of telebot.async_telebot.AsyncTeleBot.reopen_forum_topic:1 msgid "" "Use this method to reopen a closed topic in a forum supergroup chat. The " @@ -4309,14 +4585,6 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendchataction" -#: of telebot.async_telebot.AsyncTeleBot.send_chat_action:10 -#: telebot.async_telebot.AsyncTeleBot.send_contact:5 -#: telebot.async_telebot.AsyncTeleBot.send_poll:6 -#: telebot.async_telebot.AsyncTeleBot.send_venue:5 -#: telebot.async_telebot.AsyncTeleBot.stop_poll:5 -msgid "Unique identifier for the target chat or username of the target channel" -msgstr "Уникальный id чата или username канала" - #: of telebot.async_telebot.AsyncTeleBot.send_chat_action:13 msgid "" "Type of action to broadcast. Choose one, depending on what the user is " @@ -4520,9 +4788,9 @@ msgstr "" msgid "Identifier of the thread to which the message will be sent." msgstr "id топика, в которые будет сообщение будет отправлено." -#: of telebot.async_telebot.AsyncTeleBot.send_game:33 -#: telebot.async_telebot.AsyncTeleBot.send_invoice:102 -#: telebot.async_telebot.AsyncTeleBot.send_poll:71 +#: of telebot.async_telebot.AsyncTeleBot.send_game:36 +#: telebot.async_telebot.AsyncTeleBot.send_invoice:105 +#: telebot.async_telebot.AsyncTeleBot.send_poll:74 msgid ":obj:`types.Message`" msgstr "" @@ -4654,11 +4922,7 @@ msgstr "" "Отправить сообщение, при получении которого пользователя пользователи " "получат уведомление без звука." -#: of telebot.async_telebot.AsyncTeleBot.send_media_group:27 -msgid "Identifier of a message thread, in which the messages will be sent" -msgstr "id топика, в который будет отправлена группа медиа" - -#: of telebot.async_telebot.AsyncTeleBot.send_media_group:30 +#: of telebot.async_telebot.AsyncTeleBot.send_media_group:33 msgid "On success, an array of Messages that were sent is returned." msgstr "В случае успеха, возвращается массив отправленных сообщений (Message)." @@ -4701,6 +4965,10 @@ msgstr "" "Если True, содержимое сообщения будет скрыто от всех пользователей, кроме" " заданного" +#: of telebot.async_telebot.AsyncTeleBot.send_message:49 +msgid "Options for previewing links." +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.send_photo:1 msgid "Use this method to send photos. On success, the sent Message is returned." msgstr "" @@ -5298,6 +5566,43 @@ msgstr "" "В случае успеха, если сообщение было отправлено ботом, возвращает " "измененное сообщение (Message), иначе возвращает True." +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:1 +#, fuzzy +msgid "" +"Use this method to set a reaction to a message in a chat. The bot must be" +" an administrator in the chat for this to work and must have the " +"appropriate admin rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот " +"должен быть администратором чата и иметь соответствующие права " +"администратора. Возвращает True в случае успеха." + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:3 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmessagereaction" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagecaption" + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:8 +#, fuzzy +msgid "Identifier of the message to set reaction to" +msgstr "id сообщения, которое нужно удалить" + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:11 +msgid "" +"New list of reaction types to set on the message. Currently, as non-" +"premium users, bots can set up to one reaction per message. A custom " +"emoji reaction can be used if it is either already present on the message" +" or explicitly allowed by chat administrators." +msgstr "" + +#: of telebot.async_telebot.AsyncTeleBot.set_message_reaction:15 +msgid "Pass True to set the reaction with a big animation" +msgstr "" + #: of telebot.async_telebot.AsyncTeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." msgstr "Используйте этот метод, чтобы изменить список команд бота." @@ -5937,6 +6242,35 @@ msgstr "" msgid "Identifier of the topic" msgstr "id топика" +#: of +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:1 +#, fuzzy +msgid "" +"Use this method to clear the list of pinned messages in a General forum " +"topic. The bot must be an administrator in the chat for this to work and " +"must have the can_pin_messages administrator right in the supergroup. " +"Returns True on success." +msgstr "" +"Используйте этот метод, что открепить все закрепленные сообщения в " +"топике. Бот должен быть администратором чата и иметь права администратора" +" can_pin_messages в супергруппе. Возвращает True в случае успеха." + +#: of +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:6 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" + +#: of +#: telebot.async_telebot.AsyncTeleBot.unpin_all_general_forum_topic_messages:8 +#, fuzzy +msgid "Unique identifier for the target chat or username of chat" +msgstr "Уникальный id чата или username канала" + #: of telebot.async_telebot.AsyncTeleBot.unpin_chat_message:1 msgid "" "Use this method to unpin specific pinned message in a supergroup chat. " diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index c9243db70..e304f04ad 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-08 23:07+0500\n" +"POT-Creation-Date: 2024-01-05 22:16+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -100,7 +100,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query #: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member #: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler -#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.channel_post_handler telebot.TeleBot.chat_boost_handler #: telebot.TeleBot.chat_join_request_handler #: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler #: telebot.TeleBot.clear_reply_handlers @@ -108,16 +108,17 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.clear_step_handler #: telebot.TeleBot.clear_step_handler_by_chat_id #: telebot.TeleBot.close_forum_topic telebot.TeleBot.close_general_forum_topic -#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link -#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link -#: telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.copy_message telebot.TeleBot.copy_messages +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic -#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands -#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook -#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link -#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_general_forum_topic +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_messages +#: telebot.TeleBot.delete_my_commands telebot.TeleBot.delete_state +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_sticker_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_general_forum_topic #: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup @@ -127,9 +128,10 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.enable_save_next_step_handlers #: telebot.TeleBot.enable_save_reply_handlers #: telebot.TeleBot.enable_saving_states telebot.TeleBot.export_chat_invite_link -#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat -#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member -#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.forward_message telebot.TeleBot.forward_messages +#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators +#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count +#: telebot.TeleBot.get_chat_menu_button #: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file #: telebot.TeleBot.get_file_url telebot.TeleBot.get_game_high_scores #: telebot.TeleBot.get_my_commands @@ -137,17 +139,20 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name #: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state #: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates -#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info -#: telebot.TeleBot.hide_general_forum_topic telebot.TeleBot.infinity_polling -#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat -#: telebot.TeleBot.load_next_step_handlers telebot.TeleBot.load_reply_handlers -#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.get_user_chat_boosts telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.hide_general_forum_topic +#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler +#: telebot.TeleBot.leave_chat telebot.TeleBot.load_next_step_handlers +#: telebot.TeleBot.load_reply_handlers telebot.TeleBot.message_handler +#: telebot.TeleBot.message_reaction_count_handler +#: telebot.TeleBot.message_reaction_handler telebot.TeleBot.middleware_handler #: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message #: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler #: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.process_new_updates telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_boost_handler #: telebot.TeleBot.register_chat_join_request_handler #: telebot.TeleBot.register_chat_member_handler #: telebot.TeleBot.register_chosen_inline_handler @@ -157,6 +162,8 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.register_for_reply_by_message_id #: telebot.TeleBot.register_inline_handler #: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_message_reaction_count_handler +#: telebot.TeleBot.register_message_reaction_handler #: telebot.TeleBot.register_middleware_handler #: telebot.TeleBot.register_my_chat_member_handler #: telebot.TeleBot.register_next_step_handler @@ -164,7 +171,9 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.register_poll_answer_handler #: telebot.TeleBot.register_poll_handler #: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_removed_chat_boost_handler #: telebot.TeleBot.register_shipping_query_handler +#: telebot.TeleBot.removed_chat_boost_handler #: telebot.TeleBot.reopen_forum_topic #: telebot.TeleBot.reopen_general_forum_topic telebot.TeleBot.reply_to #: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member @@ -177,13 +186,15 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.send_media_group telebot.TeleBot.send_message #: telebot.TeleBot.send_photo telebot.TeleBot.send_poll #: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_voice +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note +#: telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail -#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_message_reaction +#: telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights #: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name #: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state @@ -199,6 +210,7 @@ msgstr "Установите пакет coloredlogs для использова #: telebot.TeleBot.unhide_general_forum_topic #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_all_general_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file #: telebot.custom_filters.TextFilter msgid "Parameters" @@ -314,7 +326,7 @@ msgstr "Данные для добавления" #: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query #: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member #: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.callback_query_handler -#: telebot.TeleBot.channel_post_handler +#: telebot.TeleBot.channel_post_handler telebot.TeleBot.chat_boost_handler #: telebot.TeleBot.chat_join_request_handler #: telebot.TeleBot.chat_member_handler telebot.TeleBot.chosen_inline_handler #: telebot.TeleBot.clear_reply_handlers @@ -322,15 +334,17 @@ msgstr "Данные для добавления" #: telebot.TeleBot.clear_step_handler #: telebot.TeleBot.clear_step_handler_by_chat_id telebot.TeleBot.close #: telebot.TeleBot.close_forum_topic telebot.TeleBot.copy_message -#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic -#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.copy_messages telebot.TeleBot.create_chat_invite_link +#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link +#: telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic -#: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands -#: telebot.TeleBot.delete_state telebot.TeleBot.delete_sticker_from_set -#: telebot.TeleBot.delete_sticker_set telebot.TeleBot.delete_webhook -#: telebot.TeleBot.download_file telebot.TeleBot.edit_chat_invite_link -#: telebot.TeleBot.edit_forum_topic telebot.TeleBot.edit_message_caption +#: telebot.TeleBot.delete_message telebot.TeleBot.delete_messages +#: telebot.TeleBot.delete_my_commands telebot.TeleBot.delete_state +#: telebot.TeleBot.delete_sticker_from_set telebot.TeleBot.delete_sticker_set +#: telebot.TeleBot.delete_webhook telebot.TeleBot.download_file +#: telebot.TeleBot.edit_chat_invite_link telebot.TeleBot.edit_forum_topic +#: telebot.TeleBot.edit_message_caption #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text @@ -338,9 +352,9 @@ msgstr "Данные для добавления" #: telebot.TeleBot.edited_message_handler #: telebot.TeleBot.enable_save_next_step_handlers #: telebot.TeleBot.export_chat_invite_link telebot.TeleBot.forward_message -#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators -#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count -#: telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.forward_messages telebot.TeleBot.get_chat +#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member +#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button #: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file #: telebot.TeleBot.get_file_url telebot.TeleBot.get_forum_topic_icon_stickers #: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands @@ -348,16 +362,19 @@ msgstr "Данные для добавления" #: telebot.TeleBot.get_my_description telebot.TeleBot.get_my_name #: telebot.TeleBot.get_my_short_description telebot.TeleBot.get_state #: telebot.TeleBot.get_sticker_set telebot.TeleBot.get_updates -#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info -#: telebot.TeleBot.infinity_polling telebot.TeleBot.inline_handler -#: telebot.TeleBot.leave_chat telebot.TeleBot.log_out -#: telebot.TeleBot.message_handler telebot.TeleBot.middleware_handler +#: telebot.TeleBot.get_user_chat_boosts telebot.TeleBot.get_user_profile_photos +#: telebot.TeleBot.get_webhook_info telebot.TeleBot.infinity_polling +#: telebot.TeleBot.inline_handler telebot.TeleBot.leave_chat +#: telebot.TeleBot.log_out telebot.TeleBot.message_handler +#: telebot.TeleBot.message_reaction_count_handler +#: telebot.TeleBot.message_reaction_handler telebot.TeleBot.middleware_handler #: telebot.TeleBot.my_chat_member_handler telebot.TeleBot.pin_chat_message #: telebot.TeleBot.poll_answer_handler telebot.TeleBot.poll_handler #: telebot.TeleBot.polling telebot.TeleBot.pre_checkout_query_handler #: telebot.TeleBot.promote_chat_member #: telebot.TeleBot.register_callback_query_handler #: telebot.TeleBot.register_channel_post_handler +#: telebot.TeleBot.register_chat_boost_handler #: telebot.TeleBot.register_chat_join_request_handler #: telebot.TeleBot.register_chosen_inline_handler #: telebot.TeleBot.register_edited_channel_post_handler @@ -366,6 +383,8 @@ msgstr "Данные для добавления" #: telebot.TeleBot.register_for_reply_by_message_id #: telebot.TeleBot.register_inline_handler #: telebot.TeleBot.register_message_handler +#: telebot.TeleBot.register_message_reaction_count_handler +#: telebot.TeleBot.register_message_reaction_handler #: telebot.TeleBot.register_middleware_handler #: telebot.TeleBot.register_my_chat_member_handler #: telebot.TeleBot.register_next_step_handler @@ -373,25 +392,28 @@ msgstr "Данные для добавления" #: telebot.TeleBot.register_poll_answer_handler #: telebot.TeleBot.register_poll_handler #: telebot.TeleBot.register_pre_checkout_query_handler +#: telebot.TeleBot.register_removed_chat_boost_handler #: telebot.TeleBot.register_shipping_query_handler -#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic -#: telebot.TeleBot.reply_to telebot.TeleBot.reset_data -#: telebot.TeleBot.restrict_chat_member telebot.TeleBot.retrieve_data -#: telebot.TeleBot.revoke_chat_invite_link telebot.TeleBot.send_animation -#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action -#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice -#: telebot.TeleBot.send_document telebot.TeleBot.send_game -#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location -#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message -#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll -#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue -#: telebot.TeleBot.send_video telebot.TeleBot.send_voice +#: telebot.TeleBot.remove_webhook telebot.TeleBot.removed_chat_boost_handler +#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.reset_data telebot.TeleBot.restrict_chat_member +#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link +#: telebot.TeleBot.send_animation telebot.TeleBot.send_audio +#: telebot.TeleBot.send_chat_action telebot.TeleBot.send_contact +#: telebot.TeleBot.send_dice telebot.TeleBot.send_document +#: telebot.TeleBot.send_game telebot.TeleBot.send_invoice +#: telebot.TeleBot.send_location telebot.TeleBot.send_media_group +#: telebot.TeleBot.send_message telebot.TeleBot.send_photo +#: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker +#: telebot.TeleBot.send_venue telebot.TeleBot.send_video +#: telebot.TeleBot.send_video_note telebot.TeleBot.send_voice #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo #: telebot.TeleBot.set_chat_sticker_set telebot.TeleBot.set_chat_title #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail -#: telebot.TeleBot.set_game_score telebot.TeleBot.set_my_commands +#: telebot.TeleBot.set_game_score telebot.TeleBot.set_message_reaction +#: telebot.TeleBot.set_my_commands #: telebot.TeleBot.set_my_default_administrator_rights #: telebot.TeleBot.set_my_description telebot.TeleBot.set_my_name #: telebot.TeleBot.set_my_short_description telebot.TeleBot.set_state @@ -405,6 +427,7 @@ msgstr "Данные для добавления" #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_all_general_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file #: telebot.TeleBot.user telebot.custom_filters.TextFilter #: telebot.ext.sync.webhooks.SyncWebhookListener.run_app @@ -412,7 +435,7 @@ msgid "Returns" msgstr "" #: of telebot.TeleBot.add_data:10 telebot.TeleBot.callback_query_handler:9 -#: telebot.TeleBot.channel_post_handler:18 +#: telebot.TeleBot.channel_post_handler:18 telebot.TeleBot.chat_boost_handler:8 #: telebot.TeleBot.chat_join_request_handler:10 #: telebot.TeleBot.chat_member_handler:11 #: telebot.TeleBot.chosen_inline_handler:10 @@ -427,21 +450,26 @@ msgstr "" #: telebot.TeleBot.pre_checkout_query_handler:9 #: telebot.TeleBot.register_callback_query_handler:14 #: telebot.TeleBot.register_channel_post_handler:23 +#: telebot.TeleBot.register_chat_boost_handler:13 #: telebot.TeleBot.register_chat_join_request_handler:14 #: telebot.TeleBot.register_chosen_inline_handler:14 #: telebot.TeleBot.register_edited_message_handler:26 #: telebot.TeleBot.register_for_reply:15 #: telebot.TeleBot.register_for_reply_by_message_id:15 #: telebot.TeleBot.register_message_handler:26 +#: telebot.TeleBot.register_message_reaction_count_handler:14 +#: telebot.TeleBot.register_message_reaction_handler:14 #: telebot.TeleBot.register_middleware_handler:18 #: telebot.TeleBot.register_my_chat_member_handler:14 #: telebot.TeleBot.register_next_step_handler:15 #: telebot.TeleBot.register_next_step_handler_by_chat_id:15 #: telebot.TeleBot.register_poll_answer_handler:14 #: telebot.TeleBot.register_poll_handler:14 +#: telebot.TeleBot.register_removed_chat_boost_handler:13 #: telebot.TeleBot.register_shipping_query_handler:14 -#: telebot.TeleBot.reset_data:9 telebot.TeleBot.set_state:18 -#: telebot.TeleBot.setup_middleware:5 telebot.TeleBot.shipping_query_handler:9 +#: telebot.TeleBot.removed_chat_boost_handler:8 telebot.TeleBot.reset_data:9 +#: telebot.TeleBot.set_state:18 telebot.TeleBot.setup_middleware:5 +#: telebot.TeleBot.shipping_query_handler:9 #: telebot.custom_filters.TextFilter:22 #: telebot.ext.sync.webhooks.SyncWebhookListener.run_app:4 msgid "None" @@ -542,6 +570,7 @@ msgstr "" #: telebot.TeleBot.set_sticker_position_in_set:11 #: telebot.TeleBot.set_sticker_set_thumbnail:15 #: telebot.TeleBot.unpin_all_forum_topic_messages:13 +#: telebot.TeleBot.unpin_all_general_forum_topic_messages:11 msgid "On success, True is returned." msgstr "В случае успеха возвращается True." @@ -551,9 +580,9 @@ msgstr "В случае успеха возвращается True." #: telebot.TeleBot.answer_shipping_query telebot.TeleBot.answer_web_app_query #: telebot.TeleBot.approve_chat_join_request telebot.TeleBot.ban_chat_member #: telebot.TeleBot.ban_chat_sender_chat telebot.TeleBot.close_forum_topic -#: telebot.TeleBot.copy_message telebot.TeleBot.create_chat_invite_link -#: telebot.TeleBot.create_forum_topic telebot.TeleBot.create_invoice_link -#: telebot.TeleBot.create_new_sticker_set +#: telebot.TeleBot.copy_message telebot.TeleBot.copy_messages +#: telebot.TeleBot.create_chat_invite_link telebot.TeleBot.create_forum_topic +#: telebot.TeleBot.create_invoice_link telebot.TeleBot.create_new_sticker_set #: telebot.TeleBot.decline_chat_join_request telebot.TeleBot.delete_chat_photo #: telebot.TeleBot.delete_chat_sticker_set telebot.TeleBot.delete_forum_topic #: telebot.TeleBot.delete_message telebot.TeleBot.delete_my_commands @@ -564,28 +593,30 @@ msgstr "В случае успеха возвращается True." #: telebot.TeleBot.edit_message_live_location #: telebot.TeleBot.edit_message_media telebot.TeleBot.edit_message_reply_markup #: telebot.TeleBot.edit_message_text telebot.TeleBot.export_chat_invite_link -#: telebot.TeleBot.forward_message telebot.TeleBot.get_chat -#: telebot.TeleBot.get_chat_administrators telebot.TeleBot.get_chat_member -#: telebot.TeleBot.get_chat_member_count telebot.TeleBot.get_chat_menu_button +#: telebot.TeleBot.forward_message telebot.TeleBot.forward_messages +#: telebot.TeleBot.get_chat telebot.TeleBot.get_chat_administrators +#: telebot.TeleBot.get_chat_member telebot.TeleBot.get_chat_member_count +#: telebot.TeleBot.get_chat_menu_button #: telebot.TeleBot.get_custom_emoji_stickers telebot.TeleBot.get_file_url #: telebot.TeleBot.get_forum_topic_icon_stickers #: telebot.TeleBot.get_game_high_scores telebot.TeleBot.get_my_commands #: telebot.TeleBot.get_my_default_administrator_rights #: telebot.TeleBot.get_state telebot.TeleBot.get_sticker_set -#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_profile_photos -#: telebot.TeleBot.get_webhook_info telebot.TeleBot.log_out -#: telebot.TeleBot.pin_chat_message telebot.TeleBot.promote_chat_member -#: telebot.TeleBot.remove_webhook telebot.TeleBot.reopen_forum_topic -#: telebot.TeleBot.reply_to telebot.TeleBot.restrict_chat_member -#: telebot.TeleBot.retrieve_data telebot.TeleBot.revoke_chat_invite_link -#: telebot.TeleBot.send_animation telebot.TeleBot.send_audio -#: telebot.TeleBot.send_chat_action telebot.TeleBot.send_contact -#: telebot.TeleBot.send_dice telebot.TeleBot.send_document -#: telebot.TeleBot.send_game telebot.TeleBot.send_invoice -#: telebot.TeleBot.send_location telebot.TeleBot.send_media_group -#: telebot.TeleBot.send_message telebot.TeleBot.send_photo -#: telebot.TeleBot.send_poll telebot.TeleBot.send_sticker -#: telebot.TeleBot.send_venue telebot.TeleBot.send_video +#: telebot.TeleBot.get_updates telebot.TeleBot.get_user_chat_boosts +#: telebot.TeleBot.get_user_profile_photos telebot.TeleBot.get_webhook_info +#: telebot.TeleBot.log_out telebot.TeleBot.pin_chat_message +#: telebot.TeleBot.promote_chat_member telebot.TeleBot.remove_webhook +#: telebot.TeleBot.reopen_forum_topic telebot.TeleBot.reply_to +#: telebot.TeleBot.restrict_chat_member telebot.TeleBot.retrieve_data +#: telebot.TeleBot.revoke_chat_invite_link telebot.TeleBot.send_animation +#: telebot.TeleBot.send_audio telebot.TeleBot.send_chat_action +#: telebot.TeleBot.send_contact telebot.TeleBot.send_dice +#: telebot.TeleBot.send_document telebot.TeleBot.send_game +#: telebot.TeleBot.send_invoice telebot.TeleBot.send_location +#: telebot.TeleBot.send_media_group telebot.TeleBot.send_message +#: telebot.TeleBot.send_photo telebot.TeleBot.send_poll +#: telebot.TeleBot.send_sticker telebot.TeleBot.send_venue +#: telebot.TeleBot.send_video telebot.TeleBot.send_video_note #: telebot.TeleBot.set_chat_administrator_custom_title #: telebot.TeleBot.set_chat_description telebot.TeleBot.set_chat_menu_button #: telebot.TeleBot.set_chat_permissions telebot.TeleBot.set_chat_photo @@ -602,6 +633,7 @@ msgstr "В случае успеха возвращается True." #: telebot.TeleBot.unban_chat_member telebot.TeleBot.unban_chat_sender_chat #: telebot.TeleBot.unpin_all_chat_messages #: telebot.TeleBot.unpin_all_forum_topic_messages +#: telebot.TeleBot.unpin_all_general_forum_topic_messages #: telebot.TeleBot.unpin_chat_message telebot.TeleBot.upload_sticker_file #: telebot.TeleBot.user msgid "Return type" @@ -625,18 +657,16 @@ msgstr "" #: telebot.TeleBot.delete_sticker_set:7 telebot.TeleBot.delete_webhook:13 #: telebot.TeleBot.edit_forum_topic:23 telebot.TeleBot.leave_chat:8 #: telebot.TeleBot.log_out:11 telebot.TeleBot.pin_chat_message:19 -#: telebot.TeleBot.promote_chat_member:61 telebot.TeleBot.remove_webhook:4 +#: telebot.TeleBot.promote_chat_member:70 telebot.TeleBot.remove_webhook:4 #: telebot.TeleBot.reopen_forum_topic:14 #: telebot.TeleBot.restrict_chat_member:61 telebot.TeleBot.send_chat_action:26 -#: telebot.TeleBot.send_video_note:28 telebot.TeleBot.send_video_note:40 -#: telebot.TeleBot.send_video_note:43 #: telebot.TeleBot.set_chat_administrator_custom_title:18 #: telebot.TeleBot.set_chat_description:14 #: telebot.TeleBot.set_chat_menu_button:15 #: telebot.TeleBot.set_chat_permissions:21 telebot.TeleBot.set_chat_photo:16 #: telebot.TeleBot.set_chat_title:17 #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:11 -#: telebot.TeleBot.set_my_commands:18 +#: telebot.TeleBot.set_message_reaction:18 telebot.TeleBot.set_my_commands:18 #: telebot.TeleBot.set_my_default_administrator_rights:18 #: telebot.TeleBot.set_sticker_emoji_list:11 #: telebot.TeleBot.set_sticker_keywords:12 @@ -648,6 +678,7 @@ msgstr "" #: telebot.TeleBot.unban_chat_sender_chat:15 #: telebot.TeleBot.unpin_all_chat_messages:12 #: telebot.TeleBot.unpin_all_forum_topic_messages:14 +#: telebot.TeleBot.unpin_all_general_forum_topic_messages:12 #: telebot.TeleBot.unpin_chat_message:15 msgid ":obj:`bool`" msgstr "" @@ -967,7 +998,8 @@ msgstr "" #: of telebot.TeleBot.approve_chat_join_request:11 #: telebot.TeleBot.ban_chat_member:12 #: telebot.TeleBot.decline_chat_join_request:11 -#: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_profile_photos:6 +#: telebot.TeleBot.get_chat_member:8 telebot.TeleBot.get_user_chat_boosts:8 +#: telebot.TeleBot.get_user_profile_photos:6 #: telebot.TeleBot.promote_chat_member:11 #: telebot.TeleBot.restrict_chat_member:14 #: telebot.TeleBot.set_chat_administrator_custom_title:10 @@ -980,7 +1012,7 @@ msgstr "Уникальный id сделавшего запрос пользов #: telebot.TeleBot.decline_chat_join_request:14 #: telebot.TeleBot.delete_chat_photo:12 telebot.TeleBot.delete_my_commands:16 #: telebot.TeleBot.log_out:10 telebot.TeleBot.pin_chat_message:18 -#: telebot.TeleBot.promote_chat_member:60 telebot.TeleBot.remove_webhook:3 +#: telebot.TeleBot.promote_chat_member:69 telebot.TeleBot.remove_webhook:3 #: telebot.TeleBot.set_chat_administrator_custom_title:17 #: telebot.TeleBot.set_chat_description:13 #: telebot.TeleBot.set_chat_menu_button:14 telebot.TeleBot.set_chat_photo:15 @@ -1041,8 +1073,8 @@ msgstr "" #: of telebot.TeleBot.ban_chat_member:24 #: telebot.TeleBot.delete_chat_sticker_set:10 telebot.TeleBot.delete_message:22 -#: telebot.TeleBot.delete_sticker_set:6 telebot.TeleBot.delete_webhook:12 -#: telebot.TeleBot.send_chat_action:25 +#: telebot.TeleBot.delete_messages:14 telebot.TeleBot.delete_sticker_set:6 +#: telebot.TeleBot.delete_webhook:12 telebot.TeleBot.send_chat_action:25 #: telebot.TeleBot.set_custom_emoji_sticker_set_thumbnail:10 #: telebot.TeleBot.set_sticker_emoji_list:10 #: telebot.TeleBot.set_sticker_mask_position:11 @@ -1075,11 +1107,13 @@ msgstr "" #: of telebot.TeleBot.ban_chat_sender_chat:10 #: telebot.TeleBot.close_forum_topic:7 #: telebot.TeleBot.close_general_forum_topic:7 telebot.TeleBot.copy_message:5 -#: telebot.TeleBot.create_forum_topic:7 telebot.TeleBot.delete_forum_topic:7 -#: telebot.TeleBot.delete_message:13 telebot.TeleBot.edit_forum_topic:7 +#: telebot.TeleBot.copy_messages:3 telebot.TeleBot.create_forum_topic:7 +#: telebot.TeleBot.delete_forum_topic:7 telebot.TeleBot.delete_message:13 +#: telebot.TeleBot.delete_messages:8 telebot.TeleBot.edit_forum_topic:7 #: telebot.TeleBot.edit_general_forum_topic:7 #: telebot.TeleBot.edit_message_live_location:13 -#: telebot.TeleBot.forward_message:8 telebot.TeleBot.hide_general_forum_topic:7 +#: telebot.TeleBot.forward_message:8 telebot.TeleBot.forward_messages:3 +#: telebot.TeleBot.hide_general_forum_topic:7 #: telebot.TeleBot.pin_chat_message:7 telebot.TeleBot.reopen_forum_topic:7 #: telebot.TeleBot.reopen_general_forum_topic:7 #: telebot.TeleBot.send_animation:6 telebot.TeleBot.send_audio:9 @@ -1113,17 +1147,20 @@ msgstr "" "декорируемую функцию объект :class:`telebot.types.CallbackQuery`." #: of telebot.TeleBot.callback_query_handler:4 -#: telebot.TeleBot.channel_post_handler:10 +#: telebot.TeleBot.channel_post_handler:10 telebot.TeleBot.chat_boost_handler:4 #: telebot.TeleBot.chat_join_request_handler:5 #: telebot.TeleBot.chat_member_handler:6 #: telebot.TeleBot.chosen_inline_handler:5 #: telebot.TeleBot.edited_channel_post_handler:10 #: telebot.TeleBot.edited_message_handler:10 telebot.TeleBot.inline_handler:4 +#: telebot.TeleBot.message_reaction_count_handler:4 +#: telebot.TeleBot.message_reaction_handler:4 #: telebot.TeleBot.my_chat_member_handler:5 #: telebot.TeleBot.poll_answer_handler:5 telebot.TeleBot.poll_handler:4 #: telebot.TeleBot.pre_checkout_query_handler:4 #: telebot.TeleBot.register_callback_query_handler:6 #: telebot.TeleBot.register_channel_post_handler:15 +#: telebot.TeleBot.register_chat_boost_handler:6 #: telebot.TeleBot.register_chat_join_request_handler:6 #: telebot.TeleBot.register_chat_member_handler:6 #: telebot.TeleBot.register_chosen_inline_handler:6 @@ -1131,27 +1168,35 @@ msgstr "" #: telebot.TeleBot.register_edited_message_handler:15 #: telebot.TeleBot.register_inline_handler:6 #: telebot.TeleBot.register_message_handler:15 +#: telebot.TeleBot.register_message_reaction_count_handler:6 +#: telebot.TeleBot.register_message_reaction_handler:6 #: telebot.TeleBot.register_my_chat_member_handler:6 #: telebot.TeleBot.register_poll_answer_handler:6 #: telebot.TeleBot.register_poll_handler:6 #: telebot.TeleBot.register_pre_checkout_query_handler:6 +#: telebot.TeleBot.register_removed_chat_boost_handler:6 #: telebot.TeleBot.register_shipping_query_handler:6 +#: telebot.TeleBot.removed_chat_boost_handler:4 #: telebot.TeleBot.shipping_query_handler:4 msgid "Function executed as a filter" msgstr "Функция, используемая в качестве фильтра" #: of telebot.TeleBot.callback_query_handler:7 -#: telebot.TeleBot.channel_post_handler:16 +#: telebot.TeleBot.channel_post_handler:16 telebot.TeleBot.chat_boost_handler:7 #: telebot.TeleBot.chat_join_request_handler:8 #: telebot.TeleBot.chat_member_handler:9 #: telebot.TeleBot.chosen_inline_handler:8 #: telebot.TeleBot.edited_channel_post_handler:16 #: telebot.TeleBot.edited_message_handler:19 telebot.TeleBot.inline_handler:7 -#: telebot.TeleBot.message_handler:50 telebot.TeleBot.my_chat_member_handler:8 +#: telebot.TeleBot.message_handler:50 +#: telebot.TeleBot.message_reaction_count_handler:7 +#: telebot.TeleBot.message_reaction_handler:7 +#: telebot.TeleBot.my_chat_member_handler:8 #: telebot.TeleBot.poll_answer_handler:8 telebot.TeleBot.poll_handler:7 #: telebot.TeleBot.pre_checkout_query_handler:7 #: telebot.TeleBot.register_callback_query_handler:12 #: telebot.TeleBot.register_channel_post_handler:21 +#: telebot.TeleBot.register_chat_boost_handler:11 #: telebot.TeleBot.register_chat_join_request_handler:12 #: telebot.TeleBot.register_chat_member_handler:12 #: telebot.TeleBot.register_chosen_inline_handler:12 @@ -1159,11 +1204,15 @@ msgstr "Функция, используемая в качестве фильт #: telebot.TeleBot.register_edited_message_handler:24 #: telebot.TeleBot.register_inline_handler:12 #: telebot.TeleBot.register_message_handler:24 +#: telebot.TeleBot.register_message_reaction_count_handler:12 +#: telebot.TeleBot.register_message_reaction_handler:12 #: telebot.TeleBot.register_my_chat_member_handler:12 #: telebot.TeleBot.register_poll_answer_handler:12 #: telebot.TeleBot.register_poll_handler:12 #: telebot.TeleBot.register_pre_checkout_query_handler:11 +#: telebot.TeleBot.register_removed_chat_boost_handler:11 #: telebot.TeleBot.register_shipping_query_handler:12 +#: telebot.TeleBot.removed_chat_boost_handler:7 #: telebot.TeleBot.shipping_query_handler:7 msgid "Optional keyword arguments(custom filters)" msgstr "Необязательные именованные аргументы(кастомные фильтры)" @@ -1200,6 +1249,15 @@ msgstr "Необязательное регулярное выражение." msgid "Supported message content types. Must be a list. Defaults to ['text']." msgstr "Обрабатываемые виды контента. Обязан быть списком. По умолчанию ['text']" +#: of telebot.TeleBot.chat_boost_handler:1 +#, fuzzy +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostUpdated` object." +msgstr "" +"Обрабатывает новый callback query. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." + #: of telebot.TeleBot.chat_join_request_handler:1 msgid "" "Handles a request to join the chat has been sent. The bot must have the " @@ -1307,6 +1365,10 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#closeforumtopic" msgstr "Документация Telegram: https://core.telegram.org/bots/api#closeforumtopic" +#: of telebot.TeleBot.close_forum_topic +msgid "aram chat_id" +msgstr "" + #: of telebot.TeleBot.close_forum_topic:10 msgid "Identifier of the topic to close" msgstr "id топика для закрытия" @@ -1328,7 +1390,7 @@ msgid "" "https://core.telegram.org/bots/api#closegeneralforumtopic" msgstr "Документация Telegram: https://core.telegram.org/bots/api#closeforumtopic" -#: of telebot.TeleBot.copy_message:1 +#: of telebot.TeleBot.copy_message:1 telebot.TeleBot.copy_messages:1 msgid "Use this method to copy messages of any kind." msgstr "Используйте этот метод для копирования любых сообщений." @@ -1336,7 +1398,8 @@ msgstr "Используйте этот метод для копирования msgid "Telegram documentation: https://core.telegram.org/bots/api#copymessage" msgstr "Документация Telegram: https://core.telegram.org/bots/api#copymessage" -#: of telebot.TeleBot.copy_message:8 telebot.TeleBot.forward_message:11 +#: of telebot.TeleBot.copy_message:8 telebot.TeleBot.copy_messages:6 +#: telebot.TeleBot.forward_message:11 telebot.TeleBot.forward_messages:6 msgid "" "Unique identifier for the chat where the original message was sent (or " "channel username in the format @channelusername)" @@ -1368,14 +1431,14 @@ msgstr "" "Список отформатированных частей новой подписи в формате JSON, можно " "использовать вместо parse_mode" -#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:45 -#: telebot.TeleBot.send_audio:39 telebot.TeleBot.send_contact:20 -#: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:26 +#: of telebot.TeleBot.copy_message:22 telebot.TeleBot.send_animation:48 +#: telebot.TeleBot.send_audio:36 telebot.TeleBot.send_contact:20 +#: telebot.TeleBot.send_dice:12 telebot.TeleBot.send_document:23 #: telebot.TeleBot.send_game:11 telebot.TeleBot.send_invoice:67 -#: telebot.TeleBot.send_location:25 telebot.TeleBot.send_message:24 +#: telebot.TeleBot.send_location:22 telebot.TeleBot.send_message:24 #: telebot.TeleBot.send_photo:22 telebot.TeleBot.send_poll:44 #: telebot.TeleBot.send_venue:27 telebot.TeleBot.send_video:35 -#: telebot.TeleBot.send_video_note:27 telebot.TeleBot.send_voice:31 +#: telebot.TeleBot.send_video_note:24 telebot.TeleBot.send_voice:28 msgid "" "Sends the message silently. Users will receive a notification with no " "sound." @@ -1384,50 +1447,47 @@ msgstr "" "уведомление без звука." #: of telebot.TeleBot.copy_message:25 telebot.TeleBot.send_animation:34 -#: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:38 -#: telebot.TeleBot.send_document:50 telebot.TeleBot.send_invoice:95 +#: telebot.TeleBot.send_audio:57 telebot.TeleBot.send_contact:37 +#: telebot.TeleBot.send_dice:29 telebot.TeleBot.send_document:50 +#: telebot.TeleBot.send_game:26 telebot.TeleBot.send_invoice:95 #: telebot.TeleBot.send_location:43 telebot.TeleBot.send_media_group:15 -#: telebot.TeleBot.send_photo:25 telebot.TeleBot.send_poll:64 -#: telebot.TeleBot.send_sticker:30 telebot.TeleBot.send_venue:51 -#: telebot.TeleBot.send_video:38 telebot.TeleBot.send_video_note:42 -#: telebot.TeleBot.send_voice:43 +#: telebot.TeleBot.send_message:27 telebot.TeleBot.send_photo:25 +#: telebot.TeleBot.send_poll:64 telebot.TeleBot.send_sticker:30 +#: telebot.TeleBot.send_venue:50 telebot.TeleBot.send_video:38 +#: telebot.TeleBot.send_video_note:42 telebot.TeleBot.send_voice:43 msgid "Protects the contents of the sent message from forwarding and saving" msgstr "Запретить пересылку и сохранение содержимого сообщения" -#: of telebot.TeleBot.copy_message:28 telebot.TeleBot.send_animation:37 -#: telebot.TeleBot.send_audio:29 telebot.TeleBot.send_contact:23 -#: telebot.TeleBot.send_dice:15 telebot.TeleBot.send_document:12 -#: telebot.TeleBot.send_game:14 telebot.TeleBot.send_invoice:70 -#: telebot.TeleBot.send_location:17 telebot.TeleBot.send_media_group:18 -#: telebot.TeleBot.send_message:30 telebot.TeleBot.send_photo:28 -#: telebot.TeleBot.send_poll:47 telebot.TeleBot.send_sticker:13 -#: telebot.TeleBot.send_venue:30 telebot.TeleBot.send_video:41 -#: telebot.TeleBot.send_video_note:19 telebot.TeleBot.send_voice:20 -msgid "If the message is a reply, ID of the original message" -msgstr "Если сообщение является ответом - id сообщения, на которое дан ответ" - -#: of telebot.TeleBot.copy_message:31 telebot.TeleBot.send_animation:54 -#: telebot.TeleBot.send_audio:54 telebot.TeleBot.send_dice:26 -#: telebot.TeleBot.send_document:38 telebot.TeleBot.send_invoice:84 -#: telebot.TeleBot.send_location:40 telebot.TeleBot.send_media_group:24 -#: telebot.TeleBot.send_message:33 telebot.TeleBot.send_photo:31 -#: telebot.TeleBot.send_sticker:27 telebot.TeleBot.send_video:44 -#: telebot.TeleBot.send_video_note:39 telebot.TeleBot.send_voice:40 -msgid "" -"Pass True, if the message should be sent even if the specified replied-to" -" message is not found" -msgstr "" -"Передайте True, если сообщение нужно отправить даже в случае отсутствия " -"сообщения, на которое дан ответ" - -#: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:40 -#: telebot.TeleBot.send_contact:26 telebot.TeleBot.send_dice:18 -#: telebot.TeleBot.send_document:18 telebot.TeleBot.send_game:17 -#: telebot.TeleBot.send_location:20 telebot.TeleBot.send_message:36 +#: of telebot.TeleBot.copy_message:28 telebot.TeleBot.copy_message:31 +#: telebot.TeleBot.edit_message_text:23 telebot.TeleBot.polling:44 +#: telebot.TeleBot.send_animation:37 telebot.TeleBot.send_animation:40 +#: telebot.TeleBot.send_animation:63 telebot.TeleBot.send_audio:39 +#: telebot.TeleBot.send_audio:42 telebot.TeleBot.send_audio:63 +#: telebot.TeleBot.send_contact:23 telebot.TeleBot.send_contact:26 +#: telebot.TeleBot.send_dice:20 telebot.TeleBot.send_dice:23 +#: telebot.TeleBot.send_document:26 telebot.TeleBot.send_document:29 +#: telebot.TeleBot.send_document:56 telebot.TeleBot.send_location:25 +#: telebot.TeleBot.send_location:28 telebot.TeleBot.send_media_group:18 +#: telebot.TeleBot.send_media_group:21 telebot.TeleBot.send_message:21 +#: telebot.TeleBot.send_message:30 telebot.TeleBot.send_message:33 +#: telebot.TeleBot.send_photo:28 telebot.TeleBot.send_photo:31 +#: telebot.TeleBot.send_sticker:21 telebot.TeleBot.send_sticker:24 +#: telebot.TeleBot.send_venue:30 telebot.TeleBot.send_venue:33 +#: telebot.TeleBot.send_video:41 telebot.TeleBot.send_video:44 +#: telebot.TeleBot.send_video:64 telebot.TeleBot.send_video_note:27 +#: telebot.TeleBot.send_video_note:30 telebot.TeleBot.send_video_note:48 +#: telebot.TeleBot.send_voice:31 telebot.TeleBot.send_voice:34 +msgid "deprecated." +msgstr "" + +#: of telebot.TeleBot.copy_message:34 telebot.TeleBot.send_animation:43 +#: telebot.TeleBot.send_contact:29 telebot.TeleBot.send_dice:15 +#: telebot.TeleBot.send_document:15 telebot.TeleBot.send_game:20 +#: telebot.TeleBot.send_location:17 telebot.TeleBot.send_message:36 #: telebot.TeleBot.send_photo:34 telebot.TeleBot.send_poll:53 -#: telebot.TeleBot.send_sticker:16 telebot.TeleBot.send_venue:33 -#: telebot.TeleBot.send_video:47 telebot.TeleBot.send_video_note:22 -#: telebot.TeleBot.send_voice:23 +#: telebot.TeleBot.send_sticker:13 telebot.TeleBot.send_venue:36 +#: telebot.TeleBot.send_video:47 telebot.TeleBot.send_video_note:19 +#: telebot.TeleBot.send_voice:20 msgid "" "Additional interface options. A JSON-serialized object for an inline " "keyboard, custom reply keyboard, instructions to remove reply keyboard or" @@ -1439,14 +1499,14 @@ msgstr "" #: of telebot.TeleBot.copy_message:39 telebot.TeleBot.delete_message:19 #: telebot.TeleBot.edit_message_live_location:23 -#: telebot.TeleBot.forward_message:20 telebot.TeleBot.send_animation:48 -#: telebot.TeleBot.send_audio:42 telebot.TeleBot.send_chat_action:19 -#: telebot.TeleBot.send_contact:31 telebot.TeleBot.send_dice:23 -#: telebot.TeleBot.send_document:29 telebot.TeleBot.send_location:28 -#: telebot.TeleBot.send_media_group:21 telebot.TeleBot.send_message:40 -#: telebot.TeleBot.send_photo:39 telebot.TeleBot.send_sticker:24 -#: telebot.TeleBot.send_venue:38 telebot.TeleBot.send_video:52 -#: telebot.TeleBot.send_video_note:30 telebot.TeleBot.send_voice:34 +#: telebot.TeleBot.forward_message:20 telebot.TeleBot.send_animation:51 +#: telebot.TeleBot.send_audio:45 telebot.TeleBot.send_chat_action:19 +#: telebot.TeleBot.send_contact:34 telebot.TeleBot.send_dice:26 +#: telebot.TeleBot.send_document:32 telebot.TeleBot.send_location:31 +#: telebot.TeleBot.send_media_group:24 telebot.TeleBot.send_message:40 +#: telebot.TeleBot.send_photo:39 telebot.TeleBot.send_sticker:27 +#: telebot.TeleBot.send_venue:41 telebot.TeleBot.send_video:52 +#: telebot.TeleBot.send_video_note:33 telebot.TeleBot.send_voice:37 #: telebot.TeleBot.stop_message_live_location:19 msgid "Timeout in seconds for the request." msgstr "Таймаут запроса в секундах." @@ -1458,27 +1518,58 @@ msgstr "Таймаут запроса в секундах." msgid "Identifier of a message thread, in which the message will be sent" msgstr "id топика, в который нужно отправить сообщение" -#: of telebot.TeleBot.copy_message:45 telebot.TeleBot.forward_message:26 -#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:66 -#: telebot.TeleBot.send_audio:66 telebot.TeleBot.send_contact:44 -#: telebot.TeleBot.send_dice:35 telebot.TeleBot.send_document:59 -#: telebot.TeleBot.send_game:32 telebot.TeleBot.send_invoice:101 -#: telebot.TeleBot.send_location:49 telebot.TeleBot.send_message:46 -#: telebot.TeleBot.send_photo:48 telebot.TeleBot.send_poll:70 -#: telebot.TeleBot.send_sticker:42 telebot.TeleBot.send_venue:57 -#: telebot.TeleBot.send_video:67 telebot.TeleBot.send_voice:49 -msgid "On success, the sent Message is returned." +#: of telebot.TeleBot.copy_message:45 telebot.TeleBot.send_dice:35 +#: telebot.TeleBot.send_photo:48 +msgid "Additional parameters for replies to messages" +msgstr "" + +#: of telebot.TeleBot.copy_message:48 +#, fuzzy +msgid "On success, the MessageId of the sent message is returned." msgstr "В случае успеха возвращает отправленное сообщение (Message)." -#: of telebot.TeleBot.copy_message:46 telebot.TeleBot.forward_message:27 -#: telebot.TeleBot.reply_to:12 telebot.TeleBot.send_animation:67 -#: telebot.TeleBot.send_audio:67 telebot.TeleBot.send_contact:45 -#: telebot.TeleBot.send_dice:36 telebot.TeleBot.send_document:60 -#: telebot.TeleBot.send_location:50 telebot.TeleBot.send_message:47 -#: telebot.TeleBot.send_photo:49 telebot.TeleBot.send_sticker:43 -#: telebot.TeleBot.send_venue:58 telebot.TeleBot.send_video:68 -msgid ":class:`telebot.types.Message`" +#: of telebot.TeleBot.copy_message:49 telebot.TeleBot.forward_messages:22 +#, fuzzy +msgid ":class:`telebot.types.MessageID`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + +#: of telebot.TeleBot.copy_messages:9 telebot.TeleBot.forward_messages:9 +#, fuzzy +msgid "Message identifiers in the chat specified in from_chat_id" +msgstr "id сообщения в чате, заданном в from_chat_id" + +#: of telebot.TeleBot.copy_messages:12 telebot.TeleBot.forward_message:5 +#: telebot.TeleBot.forward_messages:12 +msgid "" +"Sends the message silently. Users will receive a notification with no " +"sound" msgstr "" +"Отправить сообщение, при получении которого пользователи получат " +"уведомление без звука" + +#: of telebot.TeleBot.copy_messages:15 telebot.TeleBot.forward_messages:15 +#, fuzzy +msgid "Identifier of a message thread, in which the messages will be sent" +msgstr "id топика, в который нужно отправить сообщение" + +#: of telebot.TeleBot.copy_messages:18 telebot.TeleBot.forward_message:17 +#: telebot.TeleBot.forward_messages:18 +msgid "Protects the contents of the forwarded message from forwarding and saving" +msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" + +#: of telebot.TeleBot.copy_messages:21 +msgid "Pass True to copy the messages without their captions" +msgstr "" + +#: of telebot.TeleBot.copy_messages:24 +#, fuzzy +msgid "On success, an array of MessageId of the sent messages is returned." +msgstr "В случае успеха возвращает отправленное сообщение (Message)." + +#: of telebot.TeleBot.copy_messages:25 +#, fuzzy +msgid ":obj:`list` of :class:`telebot.types.MessageID`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" #: of telebot.TeleBot.create_chat_invite_link:1 msgid "" @@ -1662,7 +1753,7 @@ msgstr "" " должны быть больше нуля, перечисленные в порядке строгого возрастания и " "не превышать max_tip_amount." -#: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:77 +#: of telebot.TeleBot.create_invoice_link:36 telebot.TeleBot.send_invoice:80 msgid "" "A JSON-serialized data about the invoice, which will be shared with the " "payment provider. A detailed description of required fields should be " @@ -1936,6 +2027,24 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#de msgid "Identifier of the message to delete" msgstr "id сообщения, которое нужно удалить" +#: of telebot.TeleBot.delete_messages:1 +msgid "" +"Use this method to delete multiple messages in a chat. The number of " +"messages to be deleted must not exceed 100. If the chat is a private " +"chat, the user must be an administrator of the chat for this to work and " +"must have the appropriate admin rights. Returns True on success." +msgstr "" + +#: of telebot.TeleBot.delete_messages:6 +#, fuzzy +msgid "Telegram documentation: https://core.telegram.org/bots/api#deletemessages" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#deletemessage" + +#: of telebot.TeleBot.delete_messages:11 +#, fuzzy +msgid "Identifiers of the messages to be deleted" +msgstr "id сообщения, которое нужно удалить" + #: of telebot.TeleBot.delete_my_commands:1 msgid "" "Use this method to delete the list of the bot's commands for the given " @@ -2213,7 +2322,7 @@ msgstr "JSON-сериализованный объект inline клавиату #: of telebot.TeleBot.edit_message_caption:26 #: telebot.TeleBot.edit_message_media:22 #: telebot.TeleBot.edit_message_reply_markup:17 -#: telebot.TeleBot.edit_message_text:29 +#: telebot.TeleBot.edit_message_text:32 msgid "" "On success, if edited message is sent by the bot, the edited Message is " "returned, otherwise True is returned." @@ -2283,7 +2392,7 @@ msgid "" msgstr "Обязательный, если не указаны chat_id и message_id. id inline сообщения" #: of telebot.TeleBot.edit_message_live_location:29 -#: telebot.TeleBot.send_location:31 +#: telebot.TeleBot.send_location:34 msgid "The radius of uncertainty for the location, measured in meters; 0-1500" msgstr "Радиус погрешности местоположения, измеряется в метрах; 0-1500" @@ -2363,7 +2472,7 @@ msgstr "Обязательный, если не указан inline_message_id. #: of telebot.TeleBot.edit_message_media:23 #: telebot.TeleBot.edit_message_reply_markup:18 -#: telebot.TeleBot.edit_message_text:30 telebot.TeleBot.set_game_score:27 +#: telebot.TeleBot.edit_message_text:33 telebot.TeleBot.set_game_score:27 msgid ":obj:`types.Message` or :obj:`bool`" msgstr "" @@ -2403,9 +2512,11 @@ msgstr "" "Список отформатированных частей в тексте сообщения, можно использовать " "вместо parse_mode" -#: of telebot.TeleBot.edit_message_text:23 telebot.TeleBot.send_message:21 -msgid "Disables link previews for links in this message" -msgstr "Отключает превью ссылок в сообщении" +#: of telebot.TeleBot.edit_message_text:29 +msgid "" +"A JSON-serialized object for options used to automatically generate " +"previews for links." +msgstr "" #: of telebot.TeleBot.edited_channel_post_handler:1 msgid "" @@ -2507,7 +2618,7 @@ msgstr "" msgid "exported invite link as String on success." msgstr "новая ссылка-приглашение (String) в случае успеха." -#: of telebot.TeleBot.forward_message:1 +#: of telebot.TeleBot.forward_message:1 telebot.TeleBot.forward_messages:1 msgid "Use this method to forward messages of any kind." msgstr "Используйте этот метод, чтобы переслать любое сообщение." @@ -2515,17 +2626,28 @@ msgstr "Используйте этот метод, чтобы переслат msgid "Telegram documentation: https://core.telegram.org/bots/api#forwardmessage" msgstr "Документация Telegram: https://core.telegram.org/bots/api#forwardmessage" -#: of telebot.TeleBot.forward_message:5 -msgid "" -"Sends the message silently. Users will receive a notification with no " -"sound" -msgstr "" -"Отправить сообщение, при получении которого пользователи получат " -"уведомление без звука" +#: of telebot.TeleBot.forward_message:26 telebot.TeleBot.forward_messages:21 +#: telebot.TeleBot.reply_to:11 telebot.TeleBot.send_animation:69 +#: telebot.TeleBot.send_audio:69 telebot.TeleBot.send_contact:46 +#: telebot.TeleBot.send_dice:38 telebot.TeleBot.send_document:62 +#: telebot.TeleBot.send_game:35 telebot.TeleBot.send_invoice:104 +#: telebot.TeleBot.send_location:52 telebot.TeleBot.send_message:52 +#: telebot.TeleBot.send_photo:51 telebot.TeleBot.send_poll:73 +#: telebot.TeleBot.send_sticker:45 telebot.TeleBot.send_venue:59 +#: telebot.TeleBot.send_video:70 telebot.TeleBot.send_video_note:54 +#: telebot.TeleBot.send_voice:52 +msgid "On success, the sent Message is returned." +msgstr "В случае успеха возвращает отправленное сообщение (Message)." -#: of telebot.TeleBot.forward_message:17 -msgid "Protects the contents of the forwarded message from forwarding and saving" -msgstr "Запретить пересылку и сохранение содержимого пересланного сообщения" +#: of telebot.TeleBot.forward_message:27 telebot.TeleBot.reply_to:12 +#: telebot.TeleBot.send_animation:70 telebot.TeleBot.send_audio:70 +#: telebot.TeleBot.send_contact:47 telebot.TeleBot.send_dice:39 +#: telebot.TeleBot.send_document:63 telebot.TeleBot.send_location:53 +#: telebot.TeleBot.send_message:53 telebot.TeleBot.send_photo:52 +#: telebot.TeleBot.send_sticker:46 telebot.TeleBot.send_venue:60 +#: telebot.TeleBot.send_video:71 telebot.TeleBot.send_video_note:55 +msgid ":class:`telebot.types.Message`" +msgstr "" #: of telebot.TeleBot.get_chat:1 msgid "" @@ -2544,6 +2666,7 @@ msgstr "Документация Telegram: https://core.telegram.org/bots/api#ge #: of telebot.TeleBot.get_chat:6 telebot.TeleBot.get_chat_administrators:7 #: telebot.TeleBot.get_chat_member_count:5 telebot.TeleBot.leave_chat:5 +#: telebot.TeleBot.set_message_reaction:5 msgid "" "Unique identifier for the target chat or username of the target " "supergroup or channel (in the format @channelusername)" @@ -2622,9 +2745,6 @@ msgid "Number of members in the chat." msgstr "Количество участников чата." #: of telebot.TeleBot.get_chat_member_count:9 -#: telebot.TeleBot.send_video_note:14 telebot.TeleBot.send_video_note:17 -#: telebot.TeleBot.send_video_note:20 telebot.TeleBot.send_video_note:31 -#: telebot.TeleBot.send_video_note:46 msgid ":obj:`int`" msgstr "" @@ -3022,6 +3142,41 @@ msgstr "Возвращается массив объектов Update." msgid ":obj:`list` of :class:`telebot.types.Update`" msgstr "" +#: of telebot.TeleBot.get_user_chat_boosts:1 +#, fuzzy +msgid "" +"Use this method to get the list of boosts added to a chat by a user. " +"Requires administrator rights in the chat. Returns a UserChatBoosts " +"object." +msgstr "" +"Используйте этот метод, чтобы получить текущие права администратора для " +"бота по умолчанию. Возвращает объект ChatAdministratorRights в случае " +"успеха." + +#: of telebot.TeleBot.get_user_chat_boosts:3 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#getuserchatboosts" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchat" + +#: of telebot.TeleBot.get_user_chat_boosts:5 +#: telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 +#: telebot.TeleBot.send_poll:6 telebot.TeleBot.send_venue:5 +#: telebot.TeleBot.stop_poll:5 +msgid "Unique identifier for the target chat or username of the target channel" +msgstr "Уникальный id чата или username канала" + +#: of telebot.TeleBot.get_user_chat_boosts:11 +#, fuzzy +msgid "On success, a UserChatBoosts object is returned." +msgstr "В случае успеха возвращается объект StickerSet." + +#: of telebot.TeleBot.get_user_chat_boosts:12 +#, fuzzy +msgid ":class:`telebot.types.UserChatBoosts`" +msgstr "Экземпляр класса :class:`telebot.types.Message`" + #: of telebot.TeleBot.get_user_profile_photos:1 msgid "" "Use this method to get a list of profile pictures for a user. Returns a " @@ -3303,6 +3458,25 @@ msgstr "" msgid "decorated function" msgstr "декорируемая функция" +#: of telebot.TeleBot.message_reaction_count_handler:1 +#, fuzzy +msgid "" +"Handles new incoming message reaction count. As a parameter to the " +"decorator function, it passes " +":class:`telebot.types.MessageReactionCountUpdated` object." +msgstr "" +"Обрабатывает новый callback query. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." + +#: of telebot.TeleBot.message_reaction_handler:1 +#, fuzzy +msgid "" +"Handles new incoming message reaction. As a parameter to the decorator " +"function, it passes :class:`telebot.types.MessageReactionUpdated` object." +msgstr "" +"Обрабатывает новый callback query. В качестве параметра передаёт в " +"декорируемую функцию объект :class:`telebot.types.CallbackQuery`." + #: of telebot.TeleBot.middleware_handler:1 msgid "Function-based middleware handler decorator." msgstr "Функция-декоратор для middleware хендлера." @@ -3473,12 +3647,6 @@ msgstr "" "отправленные до вызова get_updates, поэтому нежелательные апдейты могут " "быть получены в течение короткого периода времени." -#: of telebot.TeleBot.polling:44 -msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." -msgstr "" -"Устарело, используйте non_stop. Старая опечатка, оставлено для обратной " -"совместимости" - #: of telebot.TeleBot.polling:50 msgid "Path to watch for changes. Defaults to None" msgstr "Путь для мониторинга изменений. По умолчанию None" @@ -3630,12 +3798,34 @@ msgstr "" "Передайте True, если пользователю разрешено создавать, переименовывать, " "закрывать, и возобновлять топики, только для супергрупп" +#: of telebot.TeleBot.promote_chat_member:60 +#, fuzzy +msgid "Pass True if the administrator can create the channel's stories" +msgstr "" +"Передайте True, если администратор может создавать посты в канале, только" +" для каналов" + +#: of telebot.TeleBot.promote_chat_member:63 +#, fuzzy +msgid "Pass True if the administrator can edit the channel's stories" +msgstr "" +"Передайте True, если администратор может изменять сообщения других " +"пользователей, только для каналов" + +#: of telebot.TeleBot.promote_chat_member:66 +#, fuzzy +msgid "Pass True if the administrator can delete the channel's stories" +msgstr "" +"Передайте True, если администратор может создавать посты в канале, только" +" для каналов" + #: of telebot.TeleBot.register_callback_query_handler:1 msgid "Registers callback query handler." msgstr "Регистрирует хендлер callback query." #: of telebot.TeleBot.register_callback_query_handler:3 #: telebot.TeleBot.register_channel_post_handler:3 +#: telebot.TeleBot.register_chat_boost_handler:3 #: telebot.TeleBot.register_chat_join_request_handler:3 #: telebot.TeleBot.register_chat_member_handler:3 #: telebot.TeleBot.register_chosen_inline_handler:3 @@ -3643,16 +3833,20 @@ msgstr "Регистрирует хендлер callback query." #: telebot.TeleBot.register_edited_message_handler:3 #: telebot.TeleBot.register_inline_handler:3 #: telebot.TeleBot.register_message_handler:3 +#: telebot.TeleBot.register_message_reaction_count_handler:3 +#: telebot.TeleBot.register_message_reaction_handler:3 #: telebot.TeleBot.register_my_chat_member_handler:3 #: telebot.TeleBot.register_poll_answer_handler:3 #: telebot.TeleBot.register_poll_handler:3 #: telebot.TeleBot.register_pre_checkout_query_handler:3 +#: telebot.TeleBot.register_removed_chat_boost_handler:3 #: telebot.TeleBot.register_shipping_query_handler:3 msgid "function to be called" msgstr "функция-хендлер" #: of telebot.TeleBot.register_callback_query_handler:9 #: telebot.TeleBot.register_channel_post_handler:18 +#: telebot.TeleBot.register_chat_boost_handler:9 #: telebot.TeleBot.register_chat_join_request_handler:9 #: telebot.TeleBot.register_chat_member_handler:9 #: telebot.TeleBot.register_chosen_inline_handler:9 @@ -3660,10 +3854,13 @@ msgstr "функция-хендлер" #: telebot.TeleBot.register_edited_message_handler:21 #: telebot.TeleBot.register_inline_handler:9 #: telebot.TeleBot.register_message_handler:21 +#: telebot.TeleBot.register_message_reaction_count_handler:9 +#: telebot.TeleBot.register_message_reaction_handler:9 #: telebot.TeleBot.register_my_chat_member_handler:9 #: telebot.TeleBot.register_poll_answer_handler:9 #: telebot.TeleBot.register_poll_handler:9 #: telebot.TeleBot.register_pre_checkout_query_handler:8 +#: telebot.TeleBot.register_removed_chat_boost_handler:9 #: telebot.TeleBot.register_shipping_query_handler:9 msgid "" "True if you need to pass TeleBot instance to handler(useful for " @@ -3689,6 +3886,11 @@ msgstr "список команд" msgid "Regular expression" msgstr "Регулярное выражение" +#: of telebot.TeleBot.register_chat_boost_handler:1 +#, fuzzy +msgid "Registers chat boost handler." +msgstr "Регистрирует хендлер смены состояний участников чата." + #: of telebot.TeleBot.register_chat_join_request_handler:1 msgid "Registers chat join request handler." msgstr "Регистрирует хендлер запросов на вступление в чат." @@ -3774,6 +3976,16 @@ msgstr "Регистрирует хендлер сообщений." msgid "List of chat types" msgstr "Список видов чатов" +#: of telebot.TeleBot.register_message_reaction_count_handler:1 +#, fuzzy +msgid "Registers message reaction count handler." +msgstr "Регистрирует хендлер сообщений." + +#: of telebot.TeleBot.register_message_reaction_handler:1 +#, fuzzy +msgid "Registers message reaction handler." +msgstr "Регистрирует хендлер сообщений." + #: of telebot.TeleBot.register_middleware_handler:1 msgid "Adds function-based middleware handler." msgstr "Добавляет функцию-middleware." @@ -3863,6 +4075,11 @@ msgstr "Регистрирует хендлер изменений состоя msgid "Registers pre-checkout request handler." msgstr "Регистрирует хендлер pre-checkout query." +#: of telebot.TeleBot.register_removed_chat_boost_handler:1 +#, fuzzy +msgid "Registers removed chat boost handler." +msgstr "Регистрирует хендлер изменений статуса бота." + #: of telebot.TeleBot.register_shipping_query_handler:1 msgid "Registers shipping query handler." msgstr "Регистрирует хендлер shipping query." @@ -3871,6 +4088,15 @@ msgstr "Регистрирует хендлер shipping query." msgid "Deletes webhooks using set_webhook() function." msgstr "Удаляет вебхук, используя set_webhook()." +#: of telebot.TeleBot.removed_chat_boost_handler:1 +#, fuzzy +msgid "" +"Handles new incoming chat boost state. it passes " +":class:`telebot.types.ChatBoostRemoved` object." +msgstr "" +"Обрабатывает inline query. В качестве параметра, передаёт в декорируемую " +"функцию объект :class:`telebot.types.InlineQuery`." + #: of telebot.TeleBot.reopen_forum_topic:1 msgid "" "Use this method to reopen a closed topic in a forum supergroup chat. The " @@ -4223,7 +4449,7 @@ msgid "Animation height" msgstr "Высота гифки" #: of telebot.TeleBot.send_animation:22 telebot.TeleBot.send_video:20 -#: telebot.TeleBot.send_video_note:33 +#: telebot.TeleBot.send_video_note:36 msgid "" "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" @@ -4254,7 +4480,7 @@ msgstr "" msgid "Mode for parsing entities in the animation caption" msgstr "Режим форматирования подписи к гифке" -#: of telebot.TeleBot.send_animation:51 telebot.TeleBot.send_video:29 +#: of telebot.TeleBot.send_animation:54 telebot.TeleBot.send_video:29 msgid "" "List of special entities that appear in the caption, which can be " "specified instead of parse_mode" @@ -4270,10 +4496,13 @@ msgstr "id топика, в который будет отправлено ви msgid "Pass True, if the animation should be sent as a spoiler" msgstr "Передайте True, если гифку нужно отправить как спойлер" -#: of telebot.TeleBot.send_animation:63 telebot.TeleBot.send_audio:63 -#: telebot.TeleBot.send_document:56 telebot.TeleBot.send_video:64 -#: telebot.TeleBot.send_video_note:48 -msgid "Deprecated. Use thumbnail instead" +#: of telebot.TeleBot.send_animation:66 telebot.TeleBot.send_audio:66 +#: telebot.TeleBot.send_contact:43 telebot.TeleBot.send_document:59 +#: telebot.TeleBot.send_location:49 telebot.TeleBot.send_media_group:30 +#: telebot.TeleBot.send_message:46 telebot.TeleBot.send_sticker:42 +#: telebot.TeleBot.send_venue:56 telebot.TeleBot.send_video_note:51 +#: telebot.TeleBot.send_voice:49 +msgid "Reply parameters." msgstr "" #: of telebot.TeleBot.send_audio:1 @@ -4328,7 +4557,7 @@ msgstr "Исполнитель" msgid "Track name" msgstr "Название трека" -#: of telebot.TeleBot.send_audio:36 +#: of telebot.TeleBot.send_audio:33 msgid "" "Mode for parsing entities in the audio caption. See formatting options " "for more details." @@ -4336,7 +4565,7 @@ msgstr "" "Режим форматирования подписи к аудио. См. formatting options для " "получения подробностей." -#: of telebot.TeleBot.send_audio:45 +#: of telebot.TeleBot.send_audio:48 msgid "" "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" @@ -4355,8 +4584,8 @@ msgstr "" "“attach://” если обложка была загружена с помощью " "multipart/form-data под именем ." -#: of telebot.TeleBot.send_audio:51 telebot.TeleBot.send_document:35 -#: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:37 +#: of telebot.TeleBot.send_audio:54 telebot.TeleBot.send_document:38 +#: telebot.TeleBot.send_photo:19 telebot.TeleBot.send_voice:40 msgid "" "A JSON-serialized list of special entities that appear in the caption, " "which can be specified instead of parse_mode" @@ -4393,12 +4622,6 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#sendchataction" msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendchataction" -#: of telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 -#: telebot.TeleBot.send_poll:6 telebot.TeleBot.send_venue:5 -#: telebot.TeleBot.stop_poll:5 -msgid "Unique identifier for the target chat or username of the target channel" -msgstr "Уникальный id чата или username канала" - #: of telebot.TeleBot.send_chat_action:13 msgid "" "Type of action to broadcast. Choose one, depending on what the user is " @@ -4449,16 +4672,7 @@ msgstr "Фамилия контакта" msgid "Additional data about the contact in the form of a vCard, 0-2048 bytes" msgstr "Дополнительные данные о контакте в формате vCard, 0-2048 байт" -#: of telebot.TeleBot.send_contact:34 telebot.TeleBot.send_game:23 -#: telebot.TeleBot.send_venue:41 -msgid "" -"Pass True, if the message should be sent even if one of the specified " -"replied-to messages is not found." -msgstr "" -"Передайте True, если сообщение должно быть отправлено даже если одно из " -"сообщений, на которые нужно ответить, не найдено." - -#: of telebot.TeleBot.send_contact:41 telebot.TeleBot.send_venue:54 +#: of telebot.TeleBot.send_contact:40 telebot.TeleBot.send_venue:53 msgid "The thread identifier of a message from which the reply will be sent" msgstr "id топика, на сообщение из которого нужно ответить" @@ -4486,10 +4700,6 @@ msgstr "" "одним из “🎲”, “🎯”, “🏀”, “⚽”, “🎳”, или “🎰”. Значение может быть 1-6 для " "“🎲”, “🎯” и “🎳”, 1-5 для “🏀” и “⚽”, и 1-64 для “🎰”. По умолчанию “🎲”" -#: of telebot.TeleBot.send_dice:29 -msgid "Protects the contents of the sent message from forwarding" -msgstr "Защищает содержимое отправленного сообщения от пересылки" - #: of telebot.TeleBot.send_document:1 msgid "Use this method to send general files." msgstr "Используйте этот метод, чтобы отправить файл." @@ -4510,7 +4720,7 @@ msgstr "" "передайте HTTP URL (String), чтобы отправить файл из интернета или " "загрузите новый с помощью multipart/form-data" -#: of telebot.TeleBot.send_document:15 +#: of telebot.TeleBot.send_document:12 msgid "" "Document caption (may also be used when resending documents by file_id), " "0-1024 characters after entities parsing" @@ -4518,11 +4728,11 @@ msgstr "" "Подпись к файлу (может быть использована при повторной отправке файла по " "file_id), 0-1024 символа после форматирования" -#: of telebot.TeleBot.send_document:23 +#: of telebot.TeleBot.send_document:20 msgid "Mode for parsing entities in the document caption" msgstr "Режим форматирования частей подписи к файлу" -#: of telebot.TeleBot.send_document:32 +#: of telebot.TeleBot.send_document:35 msgid "" "InputFile or String : Thumbnail of the file sent; can be ignored if " "thumbnail generation for the file is supported server-side. The thumbnail" @@ -4583,17 +4793,25 @@ msgstr "" "Короткое имя игры, служит в качестве уникального id игры. Настройте свои " "игрычерез @BotFather." -#: of telebot.TeleBot.send_game:20 -msgid "Timeout in seconds for waiting for a response from the bot." -msgstr "Тайм-аут в секундах, ожидание ответа от бота." +#: of telebot.TeleBot.send_game:14 telebot.TeleBot.send_invoice:70 +#: telebot.TeleBot.send_poll:47 +#, fuzzy +msgid "deprecated. If the message is a reply, ID of the original message" +msgstr "Если сообщение является ответом - id сообщения, на которое дан ответ" -#: of telebot.TeleBot.send_game:26 +#: of telebot.TeleBot.send_game:17 telebot.TeleBot.send_invoice:73 +#: telebot.TeleBot.send_poll:50 +#, fuzzy msgid "" -"Pass True, if content of the message needs to be protected from being " -"viewed by the bot." +"deprecated. Pass True, if the message should be sent even if the " +"specified replied-to message is not found" msgstr "" -"Передайте True, если содержимое сообщение должно быть защищено от " -"просмотра ботом." +"Передайте True, если сообщение нужно отправить даже в случае отсутствия " +"сообщения, на которое дан ответ" + +#: of telebot.TeleBot.send_game:23 +msgid "Timeout in seconds for waiting for a response from the bot." +msgstr "Тайм-аут в секундах, ожидание ответа от бота." #: of telebot.TeleBot.send_game:29 msgid "" @@ -4601,8 +4819,12 @@ msgid "" "sent." msgstr "id топика, в который будет отправлено сообщение с игрой." -#: of telebot.TeleBot.send_game:33 telebot.TeleBot.send_invoice:102 -#: telebot.TeleBot.send_poll:71 +#: of telebot.TeleBot.send_game:32 telebot.TeleBot.send_video:67 +msgid "Reply parameters" +msgstr "" + +#: of telebot.TeleBot.send_game:36 telebot.TeleBot.send_invoice:105 +#: telebot.TeleBot.send_poll:74 msgid ":obj:`types.Message`" msgstr "" @@ -4635,7 +4857,7 @@ msgstr "" "URL фото продукта. Может быть фото товаров или рекламным изображением " "сервиса. Людям больше нравится, когда они видят, за что платят." -#: of telebot.TeleBot.send_invoice:73 +#: of telebot.TeleBot.send_invoice:76 msgid "" "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 " @@ -4645,7 +4867,7 @@ msgstr "" "показана одна кнопка 'Pay total price'. Если не пустой, первая кнопка " "должна быть кнопкой для оплаты" -#: of telebot.TeleBot.send_invoice:81 telebot.TeleBot.set_webhook:39 +#: of telebot.TeleBot.send_invoice:84 telebot.TeleBot.set_webhook:39 msgid "Timeout of a request, defaults to None" msgstr "Тайм-аут запроса, по умолчанию None" @@ -4655,6 +4877,10 @@ msgid "" "sent" msgstr "id топика, в который будет отправлен инвойс" +#: of telebot.TeleBot.send_invoice:101 +msgid "Required if the message is a reply. Additional interface options." +msgstr "" + #: of telebot.TeleBot.send_location:1 msgid "" "Use this method to send point on the map. On success, the sent Message is" @@ -4683,7 +4909,7 @@ msgstr "" "Время в секундах, в течение которого местоположение будет обновляться " "(см. Live Locations), должно быть между 60 и 86400." -#: of telebot.TeleBot.send_location:34 +#: of telebot.TeleBot.send_location:37 msgid "" "For live locations, a direction in which the user is moving, in degrees. " "Must be between 1 and 360 if specified." @@ -4691,7 +4917,7 @@ msgstr "" "Для live местоположений, направление, в котором пользователь двигается, в" " градусах. Должно быть между 1 и 360, если указано." -#: of telebot.TeleBot.send_location:37 +#: of telebot.TeleBot.send_location:40 msgid "" "For live locations, a maximum distance for proximity alerts about " "approaching another chat member, in meters. Must be between 1 and 100000 " @@ -4737,7 +4963,7 @@ msgstr "" msgid "Identifier of a message thread, in which the media group will be sent" msgstr "id топика, в который будет отправлена группа медиа" -#: of telebot.TeleBot.send_media_group:30 +#: of telebot.TeleBot.send_media_group:33 msgid "On success, an array of Messages that were sent is returned." msgstr "В случае успеха возвращается массив отправленных сообщений (Message)." @@ -4772,13 +4998,9 @@ msgstr "" "Список отформатированных частей в тексте сообщения, можно использовать " "вместо parse_mode" -#: of telebot.TeleBot.send_message:27 -msgid "" -"If True, the message content will be hidden for all users except for the " -"target user" +#: of telebot.TeleBot.send_message:49 +msgid "Link preview options." msgstr "" -"Если True, содержимое сообщения будет скрыто от всех пользователей, кроме" -" заданного" #: of telebot.TeleBot.send_photo:1 msgid "Use this method to send photos. On success, the sent Message is returned." @@ -4906,12 +5128,6 @@ msgstr "" "Передайте True, если опрос должен быть завершен немедленно. Может быть " "полезно для предпросмотра опроса." -#: of telebot.TeleBot.send_poll:50 -msgid "Pass True, if the poll allows multiple options to be voted simultaneously." -msgstr "" -"Передайте True, если опрос позволяет выбрать несколько вариантов " -"одновременно." - #: of telebot.TeleBot.send_poll:57 msgid "Timeout in seconds for waiting for a response from the user." msgstr "Тайм-аут в секундах, ожидание ответа от пользователя." @@ -4928,6 +5144,10 @@ msgstr "" msgid "The identifier of a message thread, in which the poll will be sent" msgstr "id топика, в который будет отправлен опрос" +#: of telebot.TeleBot.send_poll:70 +msgid "reply parameters." +msgstr "" + #: of telebot.TeleBot.send_sticker:1 msgid "" "Use this method to send static .WEBP, animated .TGS, or video .WEBM " @@ -4953,7 +5173,7 @@ msgstr "" "URL (String), чтобы отправить .webp файл из интернета или загрузите новый" " с помощью multipart/form-data." -#: of telebot.TeleBot.send_sticker:21 +#: of telebot.TeleBot.send_sticker:18 msgid "to disable the notification" msgstr "отключить уведомление" @@ -5003,11 +5223,11 @@ msgstr "" "“arts_entertainment/default”, “arts_entertainment/aquarium” или " "“food/icecream”.)" -#: of telebot.TeleBot.send_venue:45 +#: of telebot.TeleBot.send_venue:44 msgid "Google Places identifier of the venue" msgstr "id места на Google Places" -#: of telebot.TeleBot.send_venue:48 +#: of telebot.TeleBot.send_venue:47 msgid "Google Places type of the venue." msgstr "Тип места на Google Places." @@ -5081,22 +5301,6 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#sendvideonote" msgstr "Документация Telegram: https://core.telegram.org/bots/api#sendvideonote" -#: of telebot.TeleBot.send_video_note -msgid "param chat_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type chat_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note:7 -msgid ":obj:`int` or :obj:`str`" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param data" -msgstr "" - #: of telebot.TeleBot.send_video_note:9 msgid "" "Video note to send. Pass a file_id as String to send a video note that " @@ -5109,120 +5313,14 @@ msgstr "" "новую с помощью multipart/form-data. На текущий момент, отправка видео " "заметок по URL не поддерживается" -#: of telebot.TeleBot.send_video_note -#, fuzzy -msgid "type data" -msgstr "тип" - -#: of telebot.TeleBot.send_video_note:11 telebot.TeleBot.send_video_note:37 -#, fuzzy -msgid ":obj:`str` or :class:`telebot.types.InputFile`" -msgstr "Список объектов :class:`telebot.types.Update`." - -#: of telebot.TeleBot.send_video_note -msgid "param duration" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type duration" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param length" -msgstr "" - #: of telebot.TeleBot.send_video_note:16 msgid "Video width and height, i.e. diameter of the video message" msgstr "Ширина и высота видео (диаметр видео сообщения)" -#: of telebot.TeleBot.send_video_note -msgid "type length" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param reply_to_message_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type reply_to_message_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param reply_markup" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type reply_markup" -msgstr "" - -#: of telebot.TeleBot.send_video_note:24 -msgid "" -":class:`telebot.types.InlineKeyboardMarkup` or " -":class:`telebot.types.ReplyKeyboardMarkup` or " -":class:`telebot.types.ReplyKeyboardRemove` or " -":class:`telebot.types.ForceReply`" -msgstr "" - -#: of telebot.TeleBot.send_video_note -#, fuzzy -msgid "param disable_notification" -msgstr "отключить уведомление" - -#: of telebot.TeleBot.send_video_note -#, fuzzy -msgid "type disable_notification" -msgstr "отключить уведомление" - -#: of telebot.TeleBot.send_video_note -msgid "param timeout" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type timeout" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param thumbnail" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type thumbnail" -msgstr "" - -#: of telebot.TeleBot.send_video_note -#, fuzzy -msgid "param allow_sending_without_reply" -msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" - -#: of telebot.TeleBot.send_video_note -#, fuzzy -msgid "type allow_sending_without_reply" -msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" - -#: of telebot.TeleBot.send_video_note -msgid "param protect_content" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "type protect_content" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param message_thread_id" -msgstr "" - #: of telebot.TeleBot.send_video_note:45 msgid "Identifier of a message thread, in which the video note will be sent" msgstr "id топика, в который будет отправлена видео заметка" -#: of telebot.TeleBot.send_video_note -msgid "type message_thread_id" -msgstr "" - -#: of telebot.TeleBot.send_video_note -msgid "param thumb" -msgstr "" - #: of telebot.TeleBot.send_voice:1 msgid "" "Use this method to send audio files, if you want Telegram clients to " @@ -5263,7 +5361,7 @@ msgstr "Подпись к голосовому сообщению, 0-1024 сим msgid "Duration of the voice message in seconds" msgstr "Длительность голосового сообщения в секундах" -#: of telebot.TeleBot.send_voice:28 +#: of telebot.TeleBot.send_voice:25 msgid "" "Mode for parsing entities in the voice message caption. See formatting " "options for more details." @@ -5496,6 +5594,43 @@ msgstr "" "В случае успеха, если сообщение было отправлено ботом, возвращает " "измененное сообщение (Message), иначе возвращает True." +#: of telebot.TeleBot.set_message_reaction:1 +#, fuzzy +msgid "" +"Use this method to set a reaction to a message in a chat. The bot must be" +" an administrator in the chat for this to work and must have the " +"appropriate admin rights. Returns True on success." +msgstr "" +"Используйте этот метод, чтобы закрепить сообщение в супергруппе. Бот " +"должен быть администратором чата и иметь соответствующие права " +"администратора. Возвращает True в случае успеха." + +#: of telebot.TeleBot.set_message_reaction:3 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#setmessagereaction" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#editmessagecaption" + +#: of telebot.TeleBot.set_message_reaction:8 +#, fuzzy +msgid "Identifier of the message to set reaction to" +msgstr "id сообщения, которое нужно удалить" + +#: of telebot.TeleBot.set_message_reaction:11 +msgid "" +"New list of reaction types to set on the message. Currently, as non-" +"premium users, bots can set up to one reaction per message. A custom " +"emoji reaction can be used if it is either already present on the message" +" or explicitly allowed by chat administrators." +msgstr "" + +#: of telebot.TeleBot.set_message_reaction:15 +msgid "Pass True to set the reaction with a big animation" +msgstr "" + #: of telebot.TeleBot.set_my_commands:1 msgid "Use this method to change the list of the bot's commands." msgstr "Используйте этот метод, чтобы изменить список команд бота." @@ -6141,6 +6276,32 @@ msgstr "" msgid "Identifier of the topic" msgstr "id топика" +#: of telebot.TeleBot.unpin_all_general_forum_topic_messages:1 +#, fuzzy +msgid "" +"Use this method to clear the list of pinned messages in a General forum " +"topic. The bot must be an administrator in the chat for this to work and " +"must have the can_pin_messages administrator right in the supergroup. " +"Returns True on success." +msgstr "" +"Используйте этот метод, что открепить все закрепленные сообщения в " +"топике. Бот должен быть администратором чата и иметь права администратора" +" can_pin_messages в супергруппе. Возвращает True в случае успеха." + +#: of telebot.TeleBot.unpin_all_general_forum_topic_messages:6 +#, fuzzy +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages" +msgstr "" +"Документация Telegram: " +"https://core.telegram.org/bots/api#unpinallforumtopicmessages" + +#: of telebot.TeleBot.unpin_all_general_forum_topic_messages:8 +#, fuzzy +msgid "Unique identifier for the target chat or username of chat" +msgstr "Уникальный id чата или username канала" + #: of telebot.TeleBot.unpin_chat_message:1 msgid "" "Use this method to unpin specific pinned message in a supergroup chat. " @@ -6502,3 +6663,141 @@ msgstr "" #~ "Возвращает True в случае успешного " #~ "добавления." +#~ msgid "Disables link previews for links in this message" +#~ msgstr "Отключает превью ссылок в сообщении" + +#~ msgid "Deprecated, use non_stop. Old typo, kept for backward compatibility." +#~ msgstr "" +#~ "Устарело, используйте non_stop. Старая " +#~ "опечатка, оставлено для обратной совместимости" + +#~ msgid "Deprecated. Use thumbnail instead" +#~ msgstr "" + +#~ msgid "" +#~ "Pass True, if the message should " +#~ "be sent even if one of the " +#~ "specified replied-to messages is not " +#~ "found." +#~ msgstr "" +#~ "Передайте True, если сообщение должно " +#~ "быть отправлено даже если одно из " +#~ "сообщений, на которые нужно ответить, не" +#~ " найдено." + +#~ msgid "Protects the contents of the sent message from forwarding" +#~ msgstr "Защищает содержимое отправленного сообщения от пересылки" + +#~ msgid "" +#~ "Pass True, if content of the " +#~ "message needs to be protected from " +#~ "being viewed by the bot." +#~ msgstr "" +#~ "Передайте True, если содержимое сообщение " +#~ "должно быть защищено от просмотра ботом." + +#~ msgid "" +#~ "If True, the message content will " +#~ "be hidden for all users except for" +#~ " the target user" +#~ msgstr "" +#~ "Если True, содержимое сообщения будет " +#~ "скрыто от всех пользователей, кроме " +#~ "заданного" + +#~ msgid "" +#~ "Pass True, if the poll allows " +#~ "multiple options to be voted " +#~ "simultaneously." +#~ msgstr "" +#~ "Передайте True, если опрос позволяет " +#~ "выбрать несколько вариантов одновременно." + +#~ msgid "param chat_id" +#~ msgstr "" + +#~ msgid "type chat_id" +#~ msgstr "" + +#~ msgid ":obj:`int` or :obj:`str`" +#~ msgstr "" + +#~ msgid "param data" +#~ msgstr "" + +#~ msgid "type data" +#~ msgstr "тип" + +#~ msgid ":obj:`str` or :class:`telebot.types.InputFile`" +#~ msgstr "Список объектов :class:`telebot.types.Update`." + +#~ msgid "param duration" +#~ msgstr "" + +#~ msgid "type duration" +#~ msgstr "" + +#~ msgid "param length" +#~ msgstr "" + +#~ msgid "type length" +#~ msgstr "" + +#~ msgid "param reply_to_message_id" +#~ msgstr "" + +#~ msgid "type reply_to_message_id" +#~ msgstr "" + +#~ msgid "param reply_markup" +#~ msgstr "" + +#~ msgid "type reply_markup" +#~ msgstr "" + +#~ msgid "" +#~ ":class:`telebot.types.InlineKeyboardMarkup` or " +#~ ":class:`telebot.types.ReplyKeyboardMarkup` or " +#~ ":class:`telebot.types.ReplyKeyboardRemove` or " +#~ ":class:`telebot.types.ForceReply`" +#~ msgstr "" + +#~ msgid "param disable_notification" +#~ msgstr "отключить уведомление" + +#~ msgid "type disable_notification" +#~ msgstr "отключить уведомление" + +#~ msgid "param timeout" +#~ msgstr "" + +#~ msgid "type timeout" +#~ msgstr "" + +#~ msgid "param thumbnail" +#~ msgstr "" + +#~ msgid "type thumbnail" +#~ msgstr "" + +#~ msgid "param allow_sending_without_reply" +#~ msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" + +#~ msgid "type allow_sending_without_reply" +#~ msgstr "Глобальное значение allow_sending_without_reply, по умолчанию None" + +#~ msgid "param protect_content" +#~ msgstr "" + +#~ msgid "type protect_content" +#~ msgstr "" + +#~ msgid "param message_thread_id" +#~ msgstr "" + +#~ msgid "type message_thread_id" +#~ msgstr "" + +#~ msgid "param thumb" +#~ msgstr "" + diff --git a/docs/source/locales/ru/LC_MESSAGES/types.po b/docs/source/locales/ru/LC_MESSAGES/types.po index c846273fe..1dfef7706 100644 --- a/docs/source/locales/ru/LC_MESSAGES/types.po +++ b/docs/source/locales/ru/LC_MESSAGES/types.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-08 23:07+0500\n" +"POT-Creation-Date: 2024-01-05 22:16+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,26 +25,35 @@ msgstr "" #: of telebot.types.Animation:1 telebot.types.Audio:1 #: telebot.types.BotDescription:1 telebot.types.BotName:1 #: telebot.types.BotShortDescription:1 telebot.types.CallbackQuery:1 -#: telebot.types.Chat:1 telebot.types.ChatJoinRequest:1 +#: telebot.types.Chat:1 telebot.types.ChatBoost:1 +#: telebot.types.ChatBoostRemoved:1 telebot.types.ChatBoostSource:1 +#: telebot.types.ChatBoostUpdated:1 telebot.types.ChatJoinRequest:1 #: telebot.types.ChatMember:1 telebot.types.ChatMemberUpdated:1 #: telebot.types.ChatPhoto:1 telebot.types.ChatShared:1 #: telebot.types.ChosenInlineResult:1 telebot.types.Contact:1 -#: telebot.types.Document:1 telebot.types.File:1 telebot.types.ForumTopic:1 +#: telebot.types.Document:1 telebot.types.ExternalReplyInfo:1 +#: telebot.types.File:1 telebot.types.ForumTopic:1 #: telebot.types.ForumTopicClosed:1 telebot.types.ForumTopicCreated:1 #: telebot.types.ForumTopicEdited:1 telebot.types.ForumTopicReopened:1 #: telebot.types.Game:1 telebot.types.GameHighScore:1 #: telebot.types.GeneralForumTopicHidden:1 -#: telebot.types.GeneralForumTopicUnhidden:1 telebot.types.InlineQuery:1 -#: telebot.types.Invoice:1 telebot.types.Message:1 +#: telebot.types.GeneralForumTopicUnhidden:1 telebot.types.Giveaway:1 +#: telebot.types.GiveawayCompleted:1 telebot.types.GiveawayCreated:1 +#: telebot.types.GiveawayWinners:1 telebot.types.InaccessibleMessage:1 +#: telebot.types.InlineQuery:1 telebot.types.Invoice:1 telebot.types.Message:1 #: telebot.types.MessageAutoDeleteTimerChanged:1 telebot.types.MessageID:1 -#: telebot.types.OrderInfo:1 telebot.types.PhotoSize:1 telebot.types.Poll:1 -#: telebot.types.PollOption:1 telebot.types.PreCheckoutQuery:1 -#: telebot.types.ProximityAlertTriggered:1 telebot.types.ShippingAddress:1 +#: telebot.types.MessageOrigin:1 telebot.types.MessageReactionCountUpdated:1 +#: telebot.types.MessageReactionUpdated:1 telebot.types.OrderInfo:1 +#: telebot.types.PhotoSize:1 telebot.types.Poll:1 telebot.types.PollOption:1 +#: telebot.types.PreCheckoutQuery:1 telebot.types.ProximityAlertTriggered:1 +#: telebot.types.ReactionCount:1 telebot.types.ShippingAddress:1 #: telebot.types.ShippingQuery:1 telebot.types.Sticker:1 -#: telebot.types.StickerSet:1 telebot.types.SuccessfulPayment:1 -#: telebot.types.Update:1 telebot.types.UserProfilePhotos:1 -#: telebot.types.UserShared:1 telebot.types.Venue:1 telebot.types.Video:1 -#: telebot.types.VideoChatEnded:1 telebot.types.VideoChatParticipantsInvited:1 +#: telebot.types.StickerSet:1 telebot.types.Story:1 +#: telebot.types.SuccessfulPayment:1 telebot.types.TextQuote:1 +#: telebot.types.Update:1 telebot.types.UserChatBoosts:1 +#: telebot.types.UserProfilePhotos:1 telebot.types.UsersShared:1 +#: telebot.types.Venue:1 telebot.types.Video:1 telebot.types.VideoChatEnded:1 +#: telebot.types.VideoChatParticipantsInvited:1 #: telebot.types.VideoChatScheduled:1 telebot.types.VideoChatStarted:1 #: telebot.types.VideoNote:1 telebot.types.Voice:1 telebot.types.WebhookInfo:1 #: telebot.types.WriteAccessAllowed:1 @@ -71,19 +80,24 @@ msgstr "" #: telebot.types.BotDescription telebot.types.BotName #: telebot.types.BotShortDescription telebot.types.CallbackQuery #: telebot.types.Chat telebot.types.ChatAdministratorRights -#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest -#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator -#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft -#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner -#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated -#: telebot.types.ChatPermissions telebot.types.ChatPhoto -#: telebot.types.ChatShared telebot.types.ChosenInlineResult -#: telebot.types.Contact telebot.types.Dice telebot.types.Document -#: telebot.types.File telebot.types.ForceReply telebot.types.ForumTopic +#: telebot.types.ChatBoost telebot.types.ChatBoostRemoved +#: telebot.types.ChatBoostSource telebot.types.ChatBoostSourceGiftCode +#: telebot.types.ChatBoostSourceGiveaway telebot.types.ChatBoostSourcePremium +#: telebot.types.ChatBoostUpdated telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatPermissions +#: telebot.types.ChatPhoto telebot.types.ChatShared +#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice +#: telebot.types.Document telebot.types.ExternalReplyInfo telebot.types.File +#: telebot.types.ForceReply telebot.types.ForumTopic #: telebot.types.ForumTopicCreated telebot.types.ForumTopicEdited -#: telebot.types.Game telebot.types.GameHighScore -#: telebot.types.InlineKeyboardButton telebot.types.InlineKeyboardMarkup -#: telebot.types.InlineKeyboardMarkup.add +#: telebot.types.Game telebot.types.GameHighScore telebot.types.Giveaway +#: telebot.types.GiveawayCompleted telebot.types.GiveawayWinners +#: telebot.types.InaccessibleMessage telebot.types.InlineKeyboardButton +#: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio #: telebot.types.InlineQueryResultCachedAudio @@ -109,23 +123,32 @@ msgstr "" #: telebot.types.InputVenueMessageContent telebot.types.Invoice #: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType #: telebot.types.KeyboardButtonRequestChat -#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice -#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.KeyboardButtonRequestUsers telebot.types.LabeledPrice +#: telebot.types.LinkPreviewOptions telebot.types.Location +#: telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message #: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity -#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize -#: telebot.types.Poll telebot.types.Poll.add telebot.types.PollAnswer -#: telebot.types.PollOption telebot.types.PreCheckoutQuery -#: telebot.types.ProximityAlertTriggered telebot.types.ReplyKeyboardMarkup -#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row -#: telebot.types.ReplyKeyboardRemove telebot.types.SentWebAppMessage +#: telebot.types.MessageID telebot.types.MessageOrigin +#: telebot.types.MessageOriginChannel telebot.types.MessageOriginChat +#: telebot.types.MessageOriginHiddenUser telebot.types.MessageOriginUser +#: telebot.types.MessageReactionCountUpdated +#: telebot.types.MessageReactionUpdated telebot.types.OrderInfo +#: telebot.types.PhotoSize telebot.types.Poll telebot.types.Poll.add +#: telebot.types.PollAnswer telebot.types.PollOption +#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered +#: telebot.types.ReactionCount telebot.types.ReactionType +#: telebot.types.ReactionTypeCustomEmoji telebot.types.ReactionTypeEmoji +#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add +#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.ReplyParameters telebot.types.SentWebAppMessage #: telebot.types.ShippingAddress telebot.types.ShippingOption #: telebot.types.ShippingOption.add_price telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet #: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat -#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos -#: telebot.types.UserShared telebot.types.Venue telebot.types.Video +#: telebot.types.TextQuote telebot.types.Update telebot.types.User +#: telebot.types.UserChatBoosts telebot.types.UserProfilePhotos +#: telebot.types.UsersShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -194,17 +217,23 @@ msgstr "" #: telebot.types.BotDescription telebot.types.BotName #: telebot.types.BotShortDescription telebot.types.CallbackQuery #: telebot.types.Chat telebot.types.ChatAdministratorRights -#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest -#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator -#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft -#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner -#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated -#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions -#: telebot.types.ChatPhoto telebot.types.ChatShared -#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice -#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ChatBoost telebot.types.ChatBoostRemoved +#: telebot.types.ChatBoostSource telebot.types.ChatBoostSourceGiftCode +#: telebot.types.ChatBoostSourceGiveaway telebot.types.ChatBoostSourcePremium +#: telebot.types.ChatBoostUpdated telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChatShared telebot.types.ChosenInlineResult +#: telebot.types.Contact telebot.types.Dice telebot.types.Document +#: telebot.types.ExternalReplyInfo telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game -#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.GameHighScore telebot.types.Giveaway +#: telebot.types.GiveawayCompleted telebot.types.GiveawayWinners +#: telebot.types.InaccessibleMessage telebot.types.InlineKeyboardButton #: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio @@ -231,22 +260,29 @@ msgstr "" #: telebot.types.InputVenueMessageContent telebot.types.Invoice #: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType #: telebot.types.KeyboardButtonRequestChat -#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice -#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.KeyboardButtonRequestUsers telebot.types.LabeledPrice +#: telebot.types.LinkPreviewOptions telebot.types.Location +#: telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message #: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity -#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize -#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption -#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered -#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add -#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.MessageID telebot.types.MessageOrigin +#: telebot.types.MessageReactionCountUpdated +#: telebot.types.MessageReactionUpdated telebot.types.OrderInfo +#: telebot.types.PhotoSize telebot.types.Poll telebot.types.PollAnswer +#: telebot.types.PollOption telebot.types.PreCheckoutQuery +#: telebot.types.ProximityAlertTriggered telebot.types.ReactionCount +#: telebot.types.ReactionType telebot.types.ReactionTypeCustomEmoji +#: telebot.types.ReactionTypeEmoji telebot.types.ReplyKeyboardMarkup +#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row +#: telebot.types.ReplyKeyboardRemove telebot.types.ReplyParameters #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingOption.add_price #: telebot.types.ShippingQuery telebot.types.Sticker telebot.types.StickerSet #: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat -#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos -#: telebot.types.UserShared telebot.types.Venue telebot.types.Video +#: telebot.types.TextQuote telebot.types.Update telebot.types.User +#: telebot.types.UserChatBoosts telebot.types.UserProfilePhotos +#: telebot.types.UsersShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -263,19 +299,26 @@ msgstr "" #: telebot.types.BotCommandScopeChatMember:15 #: telebot.types.BotCommandScopeDefault:8 telebot.types.BotDescription:8 #: telebot.types.BotName:8 telebot.types.BotShortDescription:8 -#: telebot.types.CallbackQuery:30 telebot.types.Chat:109 -#: telebot.types.ChatAdministratorRights:46 telebot.types.ChatInviteLink:34 -#: telebot.types.ChatJoinRequest:27 telebot.types.ChatLocation:11 -#: telebot.types.ChatMemberAdministrator:59 telebot.types.ChatMemberBanned:15 -#: telebot.types.ChatMemberLeft:11 telebot.types.ChatMemberMember:11 -#: telebot.types.ChatMemberOwner:17 telebot.types.ChatMemberRestricted:61 -#: telebot.types.ChatMemberUpdated:27 telebot.types.ChatPermissions:56 -#: telebot.types.ChatPhoto:21 telebot.types.ChatShared:15 -#: telebot.types.ChosenInlineResult:21 telebot.types.Contact:22 -#: telebot.types.Dice:11 telebot.types.Document:26 telebot.types.File:21 +#: telebot.types.CallbackQuery:29 telebot.types.Chat:137 +#: telebot.types.ChatAdministratorRights:55 telebot.types.ChatBoost:17 +#: telebot.types.ChatBoostRemoved:17 telebot.types.ChatBoostSource:8 +#: telebot.types.ChatBoostSourceGiftCode:11 +#: telebot.types.ChatBoostSourceGiveaway:17 +#: telebot.types.ChatBoostSourcePremium:11 telebot.types.ChatBoostUpdated:11 +#: telebot.types.ChatInviteLink:34 telebot.types.ChatJoinRequest:27 +#: telebot.types.ChatLocation:11 telebot.types.ChatMemberAdministrator:68 +#: telebot.types.ChatMemberBanned:15 telebot.types.ChatMemberLeft:11 +#: telebot.types.ChatMemberMember:11 telebot.types.ChatMemberOwner:17 +#: telebot.types.ChatMemberRestricted:61 telebot.types.ChatMemberUpdated:27 +#: telebot.types.ChatPermissions:55 telebot.types.ChatPhoto:21 +#: telebot.types.ChatShared:15 telebot.types.ChosenInlineResult:21 +#: telebot.types.Contact:22 telebot.types.Dice:11 telebot.types.Document:26 +#: telebot.types.ExternalReplyInfo:75 telebot.types.File:21 #: telebot.types.ForceReply:18 telebot.types.ForumTopic:17 #: telebot.types.ForumTopicCreated:14 telebot.types.Game:25 -#: telebot.types.GameHighScore:14 telebot.types.InlineKeyboardButton:49 +#: telebot.types.GameHighScore:14 telebot.types.Giveaway:29 +#: telebot.types.GiveawayCompleted:14 telebot.types.GiveawayWinners:38 +#: telebot.types.InaccessibleMessage:14 telebot.types.InlineKeyboardButton:49 #: telebot.types.InlineKeyboardMarkup:28 telebot.types.InlineQuery:26 #: telebot.types.InlineQueryResultArticle:38 #: telebot.types.InlineQueryResultAudio:40 @@ -303,28 +346,34 @@ msgstr "" #: telebot.types.InputMediaAnimation:40 telebot.types.InputMediaAudio:37 #: telebot.types.InputMediaDocument:32 telebot.types.InputMediaPhoto:24 #: telebot.types.InputMediaVideo:43 telebot.types.InputSticker:18 -#: telebot.types.InputTextMessageContent:19 +#: telebot.types.InputTextMessageContent:22 #: telebot.types.InputVenueMessageContent:30 telebot.types.Invoice:22 -#: telebot.types.KeyboardButton:33 telebot.types.KeyboardButtonPollType:9 +#: telebot.types.KeyboardButton:36 telebot.types.KeyboardButtonPollType:9 #: telebot.types.KeyboardButtonRequestChat:35 -#: telebot.types.KeyboardButtonRequestUser:18 telebot.types.LabeledPrice:13 -#: telebot.types.Location:25 telebot.types.LoginUrl:24 -#: telebot.types.MaskPosition:20 telebot.types.MenuButtonCommands:8 -#: telebot.types.MenuButtonDefault:8 telebot.types.MenuButtonWebApp:15 -#: telebot.types.Message:252 telebot.types.MessageAutoDeleteTimerChanged:8 -#: telebot.types.MessageEntity:32 telebot.types.MessageID:8 -#: telebot.types.OrderInfo:17 telebot.types.PhotoSize:21 telebot.types.Poll:47 -#: telebot.types.PollAnswer:15 telebot.types.PollOption:11 -#: telebot.types.PreCheckoutQuery:28 telebot.types.ProximityAlertTriggered:14 -#: telebot.types.ReplyKeyboardMarkup:49 telebot.types.ReplyKeyboardRemove:18 +#: telebot.types.KeyboardButtonRequestUsers:21 telebot.types.LabeledPrice:13 +#: telebot.types.LinkPreviewOptions:20 telebot.types.Location:25 +#: telebot.types.LoginUrl:24 telebot.types.MaskPosition:20 +#: telebot.types.MenuButtonCommands:8 telebot.types.MenuButtonDefault:8 +#: telebot.types.MenuButtonWebApp:15 telebot.types.Message:277 +#: telebot.types.MessageAutoDeleteTimerChanged:8 telebot.types.MessageEntity:32 +#: telebot.types.MessageID:8 telebot.types.MessageOrigin:23 +#: telebot.types.MessageReactionCountUpdated:17 +#: telebot.types.MessageReactionUpdated:26 telebot.types.OrderInfo:17 +#: telebot.types.PhotoSize:21 telebot.types.Poll:47 telebot.types.PollAnswer:18 +#: telebot.types.PollOption:11 telebot.types.PreCheckoutQuery:28 +#: telebot.types.ProximityAlertTriggered:14 telebot.types.ReactionCount:11 +#: telebot.types.ReactionType:8 telebot.types.ReactionTypeCustomEmoji:11 +#: telebot.types.ReactionTypeEmoji:11 telebot.types.ReplyKeyboardMarkup:49 +#: telebot.types.ReplyKeyboardRemove:18 telebot.types.ReplyParameters:26 #: telebot.types.SentWebAppMessage:9 telebot.types.ShippingAddress:23 #: telebot.types.ShippingOption:14 telebot.types.ShippingQuery:17 -#: telebot.types.Sticker:54 telebot.types.StickerSet:30 +#: telebot.types.Sticker:54 telebot.types.StickerSet:29 #: telebot.types.SuccessfulPayment:28 -#: telebot.types.SwitchInlineQueryChosenChat:22 telebot.types.Update:61 -#: telebot.types.User:41 telebot.types.UserProfilePhotos:11 -#: telebot.types.UserShared:15 telebot.types.Venue:27 telebot.types.Video:35 -#: telebot.types.VideoChatEnded:8 telebot.types.VideoChatParticipantsInvited:8 +#: telebot.types.SwitchInlineQueryChosenChat:22 telebot.types.TextQuote:17 +#: telebot.types.Update:75 telebot.types.User:41 telebot.types.UserChatBoosts:8 +#: telebot.types.UserProfilePhotos:11 telebot.types.UsersShared:17 +#: telebot.types.Venue:27 telebot.types.Video:35 telebot.types.VideoChatEnded:8 +#: telebot.types.VideoChatParticipantsInvited:8 #: telebot.types.VideoChatScheduled:9 telebot.types.VideoNote:24 #: telebot.types.Voice:23 telebot.types.WebAppData:12 #: telebot.types.WebAppInfo:8 telebot.types.WebhookInfo:37 @@ -342,17 +391,23 @@ msgstr "" #: telebot.types.BotDescription telebot.types.BotName #: telebot.types.BotShortDescription telebot.types.CallbackQuery #: telebot.types.Chat telebot.types.ChatAdministratorRights -#: telebot.types.ChatInviteLink telebot.types.ChatJoinRequest -#: telebot.types.ChatLocation telebot.types.ChatMemberAdministrator -#: telebot.types.ChatMemberBanned telebot.types.ChatMemberLeft -#: telebot.types.ChatMemberMember telebot.types.ChatMemberOwner -#: telebot.types.ChatMemberRestricted telebot.types.ChatMemberUpdated -#: telebot.types.ChatMemberUpdated.difference telebot.types.ChatPermissions -#: telebot.types.ChatPhoto telebot.types.ChatShared -#: telebot.types.ChosenInlineResult telebot.types.Contact telebot.types.Dice -#: telebot.types.Document telebot.types.File telebot.types.ForceReply +#: telebot.types.ChatBoost telebot.types.ChatBoostRemoved +#: telebot.types.ChatBoostSource telebot.types.ChatBoostSourceGiftCode +#: telebot.types.ChatBoostSourceGiveaway telebot.types.ChatBoostSourcePremium +#: telebot.types.ChatBoostUpdated telebot.types.ChatInviteLink +#: telebot.types.ChatJoinRequest telebot.types.ChatLocation +#: telebot.types.ChatMemberAdministrator telebot.types.ChatMemberBanned +#: telebot.types.ChatMemberLeft telebot.types.ChatMemberMember +#: telebot.types.ChatMemberOwner telebot.types.ChatMemberRestricted +#: telebot.types.ChatMemberUpdated telebot.types.ChatMemberUpdated.difference +#: telebot.types.ChatPermissions telebot.types.ChatPhoto +#: telebot.types.ChatShared telebot.types.ChosenInlineResult +#: telebot.types.Contact telebot.types.Dice telebot.types.Document +#: telebot.types.ExternalReplyInfo telebot.types.File telebot.types.ForceReply #: telebot.types.ForumTopic telebot.types.ForumTopicCreated telebot.types.Game -#: telebot.types.GameHighScore telebot.types.InlineKeyboardButton +#: telebot.types.GameHighScore telebot.types.Giveaway +#: telebot.types.GiveawayCompleted telebot.types.GiveawayWinners +#: telebot.types.InaccessibleMessage telebot.types.InlineKeyboardButton #: telebot.types.InlineKeyboardMarkup telebot.types.InlineKeyboardMarkup.add #: telebot.types.InlineKeyboardMarkup.row telebot.types.InlineQuery #: telebot.types.InlineQueryResultArticle telebot.types.InlineQueryResultAudio @@ -379,22 +434,29 @@ msgstr "" #: telebot.types.InputVenueMessageContent telebot.types.Invoice #: telebot.types.KeyboardButton telebot.types.KeyboardButtonPollType #: telebot.types.KeyboardButtonRequestChat -#: telebot.types.KeyboardButtonRequestUser telebot.types.LabeledPrice -#: telebot.types.Location telebot.types.LoginUrl telebot.types.MaskPosition +#: telebot.types.KeyboardButtonRequestUsers telebot.types.LabeledPrice +#: telebot.types.LinkPreviewOptions telebot.types.Location +#: telebot.types.LoginUrl telebot.types.MaskPosition #: telebot.types.MenuButtonCommands telebot.types.MenuButtonDefault #: telebot.types.MenuButtonWebApp telebot.types.Message #: telebot.types.MessageAutoDeleteTimerChanged telebot.types.MessageEntity -#: telebot.types.MessageID telebot.types.OrderInfo telebot.types.PhotoSize -#: telebot.types.Poll telebot.types.PollAnswer telebot.types.PollOption -#: telebot.types.PreCheckoutQuery telebot.types.ProximityAlertTriggered -#: telebot.types.ReplyKeyboardMarkup telebot.types.ReplyKeyboardMarkup.add -#: telebot.types.ReplyKeyboardMarkup.row telebot.types.ReplyKeyboardRemove +#: telebot.types.MessageID telebot.types.MessageOrigin +#: telebot.types.MessageReactionCountUpdated +#: telebot.types.MessageReactionUpdated telebot.types.OrderInfo +#: telebot.types.PhotoSize telebot.types.Poll telebot.types.PollAnswer +#: telebot.types.PollOption telebot.types.PreCheckoutQuery +#: telebot.types.ProximityAlertTriggered telebot.types.ReactionCount +#: telebot.types.ReactionType telebot.types.ReactionTypeCustomEmoji +#: telebot.types.ReactionTypeEmoji telebot.types.ReplyKeyboardMarkup +#: telebot.types.ReplyKeyboardMarkup.add telebot.types.ReplyKeyboardMarkup.row +#: telebot.types.ReplyKeyboardRemove telebot.types.ReplyParameters #: telebot.types.SentWebAppMessage telebot.types.ShippingAddress #: telebot.types.ShippingOption telebot.types.ShippingQuery #: telebot.types.Sticker telebot.types.StickerSet #: telebot.types.SuccessfulPayment telebot.types.SwitchInlineQueryChosenChat -#: telebot.types.Update telebot.types.User telebot.types.UserProfilePhotos -#: telebot.types.UserShared telebot.types.Venue telebot.types.Video +#: telebot.types.TextQuote telebot.types.Update telebot.types.User +#: telebot.types.UserChatBoosts telebot.types.UserProfilePhotos +#: telebot.types.UsersShared telebot.types.Venue telebot.types.Video #: telebot.types.VideoChatEnded telebot.types.VideoChatParticipantsInvited #: telebot.types.VideoChatScheduled telebot.types.VideoNote telebot.types.Voice #: telebot.types.WebAppData telebot.types.WebAppInfo telebot.types.WebhookInfo @@ -801,38 +863,37 @@ msgstr "" #: of telebot.types.CallbackQuery:11 msgid "" -"Optional. Message with the callback button that originated the query. " -"Note that message content and message date will not be available if the " -"message is too old" +"Optional. Message sent by the bot with the callback button that " +"originated the query" msgstr "" -#: of telebot.types.CallbackQuery:15 +#: of telebot.types.CallbackQuery:14 msgid "" "Optional. Identifier of the message sent via the bot in inline mode, that" " originated the query." msgstr "" -#: of telebot.types.CallbackQuery:19 +#: of telebot.types.CallbackQuery:18 msgid "" "Global identifier, uniquely corresponding to the chat to which the " "message with the callback button was sent. Useful for high scores in " "games." msgstr "" -#: of telebot.types.CallbackQuery:23 +#: of telebot.types.CallbackQuery:22 msgid "" "Optional. Data associated with the callback button. Be aware that the " "message originated the query can contain no callback buttons with this " "data." msgstr "" -#: of telebot.types.CallbackQuery:27 +#: of telebot.types.CallbackQuery:26 msgid "" "Optional. Short name of a Game to be returned, serves as the unique " "identifier for the game" msgstr "" -#: of telebot.types.CallbackQuery:31 +#: of telebot.types.CallbackQuery:30 msgid ":class:`telebot.types.CallbackQuery`" msgstr "" @@ -891,114 +952,159 @@ msgstr "" #: of telebot.types.Chat:35 msgid "" +"Optional. List of available chat reactions; for private chats, " +"supergroups and channels. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:39 +msgid "" +"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." +msgstr "" + +#: of telebot.types.Chat:43 +msgid "" +"Optional. Custom emoji identifier of emoji chosen by the chat for the " +"reply header and link preview background. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:47 +msgid "" +"Optional. Identifier of the accent color for the chat's profile " +"background. See profile accent colors for more details. Returned only in " +"getChat." +msgstr "" + +#: of telebot.types.Chat:51 +msgid "" +"Optional. Custom emoji identifier of the emoji chosen by the chat for its" +" profile background. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:55 +msgid "" "Optional. Custom emoji identifier of emoji status of the other party in a" " private chat. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:39 +#: of telebot.types.Chat:59 +msgid "" +"Optional. Expiration date of the emoji status of the other party in a " +"private chat, if any. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:63 msgid "" "Optional. Bio of the other party in a private chat. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:42 +#: of telebot.types.Chat:66 msgid "" "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." msgstr "" -#: of telebot.types.Chat:46 +#: of telebot.types.Chat:70 msgid "" "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." msgstr "" -#: of telebot.types.Chat:48 +#: of telebot.types.Chat:72 msgid ":type :obj:`bool`" msgstr "" -#: of telebot.types.Chat:50 +#: of telebot.types.Chat:74 msgid "" "Optional. :obj:`bool`, if users need to join the supergroup before they " "can send messages. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:54 +#: of telebot.types.Chat:78 msgid "" "Optional. :obj:`bool`, if all users directly joining the supergroup need " "to be approved by supergroup administrators. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:58 +#: of telebot.types.Chat:82 msgid "" "Optional. Description, for groups, supergroups and channel chats. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:61 +#: of telebot.types.Chat:85 msgid "" "Optional. Primary invite link, for groups, supergroups and channel chats." " Returned only in getChat." msgstr "" -#: of telebot.types.Chat:65 +#: of telebot.types.Chat:89 msgid "" "Optional. The most recent pinned message (by sending date). Returned only" " in getChat." msgstr "" -#: of telebot.types.Chat:68 +#: of telebot.types.Chat:92 msgid "" "Optional. Default chat member permissions, for groups and supergroups. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:72 +#: of telebot.types.Chat:96 msgid "" "Optional. For supergroups, the minimum allowed delay between consecutive " "messages sent by each unpriviledged user; in seconds. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:76 +#: of telebot.types.Chat:100 msgid "" "Optional. The time after which all messages sent to the chat will be " "automatically deleted; in seconds. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:80 +#: of telebot.types.Chat:104 msgid "" "Optional. :obj:`bool`, if the chat has enabled aggressive anti-spam " "protection. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:84 +#: of telebot.types.Chat:108 msgid "" "Optional. :obj:`bool`, if the chat has enabled hidden members. Returned " "only in getChat." msgstr "" -#: of telebot.types.Chat:88 +#: of telebot.types.Chat:112 msgid "" "Optional. :obj:`bool`, if messages from the chat can't be forwarded to " "other chats. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:92 +#: of telebot.types.Chat:116 +msgid "" +"Optional. True, if new chat members will have access to old messages; " +"available only to chat administrators. Returned only in getChat." +msgstr "" + +#: of telebot.types.Chat:120 msgid "" "Optional. For supergroups, name of group sticker set. Returned only in " "getChat." msgstr "" -#: of telebot.types.Chat:95 +#: of telebot.types.Chat:123 msgid "" "Optional. :obj:`bool`, if the bot can change the group sticker set. " "Returned only in getChat." msgstr "" -#: of telebot.types.Chat:99 +#: of telebot.types.Chat:127 msgid "" "Optional. Unique identifier for the linked chat, i.e. the discussion " "group identifier for a channel and vice versa; for supergroups and " @@ -1009,13 +1115,13 @@ msgid "" "in getChat." msgstr "" -#: of telebot.types.Chat:105 +#: of telebot.types.Chat:133 msgid "" "Optional. For supergroups, the location to which the supergroup is " "connected. Returned only in getChat." msgstr "" -#: of telebot.types.Chat:110 +#: of telebot.types.Chat:138 msgid ":class:`telebot.types.Chat`" msgstr "" @@ -1117,10 +1223,203 @@ msgid "" "reopen forum topics; supergroups only" msgstr "" -#: of telebot.types.ChatAdministratorRights:47 +#: of telebot.types.ChatAdministratorRights:46 +#: telebot.types.ChatMemberAdministrator:59 +msgid "Optional. True, if the administrator can post channel stories" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:49 +#: telebot.types.ChatMemberAdministrator:62 +msgid "Optional. True, if the administrator can edit stories" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:52 +#: telebot.types.ChatMemberAdministrator:65 +msgid "Optional. True, if the administrator can delete stories of other users" +msgstr "" + +#: of telebot.types.ChatAdministratorRights:56 msgid ":class:`telebot.types.ChatAdministratorRights`" msgstr "" +#: of telebot.types.ChatBoost:1 +msgid "This object contains information about a chat boost." +msgstr "" + +#: of telebot.types.ChatBoost:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#chatboost" +msgstr "" + +#: of telebot.types.ChatBoost:5 telebot.types.ChatBoostRemoved:8 +msgid "Unique identifier of the boost" +msgstr "" + +#: of telebot.types.ChatBoost:8 +msgid "Point in time (Unix timestamp) when the chat was boosted" +msgstr "" + +#: of telebot.types.ChatBoost:11 +msgid "" +"Point in time (Unix timestamp) when the boost will automatically expire, " +"unless the booster's Telegram Premium subscription is prolonged" +msgstr "" + +#: of telebot.types.ChatBoost:14 +msgid "Source of the added boost" +msgstr "" + +#: of telebot.types.ChatBoost:18 +msgid ":class:`ChatBoost`" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:1 +msgid "This object represents a boost removed from a chat." +msgstr "" + +#: of telebot.types.ChatBoostRemoved:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostremoved" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:5 telebot.types.ChatBoostUpdated:5 +msgid "Chat which was boosted" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:11 +msgid "Point in time (Unix timestamp) when the boost was removed" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:14 +msgid "Source of the removed boost" +msgstr "" + +#: of telebot.types.ChatBoostRemoved:18 +msgid ":class:`ChatBoostRemoved`" +msgstr "" + +#: of telebot.types.ChatBoostSource:1 +msgid "This object describes the source of a chat boost." +msgstr "" + +#: of telebot.types.ChatBoostSource:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#chatboostsource" +msgstr "" + +#: of telebot.types.ChatBoostSource:5 +msgid "Source of the boost" +msgstr "" + +#: of telebot.types.ChatBoostSource:9 +msgid ":class:`ChatBoostSource`" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:1 +#: telebot.types.ChatBoostSourceGiveaway:1 +#: telebot.types.ChatBoostSourcePremium:1 +msgid "Bases: :py:class:`telebot.types.ChatBoostSource`" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:1 +msgid "" +"The boost was obtained by the creation of Telegram Premium gift codes to " +"boost a chat." +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostsourcegiftcode" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:5 +msgid "Source of the boost, always “gift_code”" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:8 +msgid "User for which the gift code was created" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiftCode:12 +msgid ":class:`ChatBoostSourceGiftCode`" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:1 +msgid "The boost was obtained by the creation of a Telegram Premium giveaway." +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostsourcegiveaway" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:5 +msgid "Source of the boost, always “giveaway”" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:8 +msgid "" +"Identifier of a message in the chat with the giveaway; the message could " +"have been deleted already. May be 0 if the message isn't sent yet." +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:11 +msgid "User that won the prize in the giveaway if any" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:14 +msgid "" +"True, if the giveaway was completed, but there was no user to win the " +"prize" +msgstr "" + +#: of telebot.types.ChatBoostSourceGiveaway:18 +msgid ":class:`ChatBoostSourceGiveaway`" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:1 +msgid "" +"The boost was obtained by subscribing to Telegram Premium or by gifting a" +" Telegram Premium subscription to another user." +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostsourcepremium" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:5 +msgid "Source of the boost, always “premium”" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:8 +msgid "User that boosted the chat" +msgstr "" + +#: of telebot.types.ChatBoostSourcePremium:12 +msgid ":class:`ChatBoostSourcePremium`" +msgstr "" + +#: of telebot.types.ChatBoostUpdated:1 +msgid "This object represents a boost added to a chat or changed." +msgstr "" + +#: of telebot.types.ChatBoostUpdated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#chatboostupdated" +msgstr "" + +#: of telebot.types.ChatBoostUpdated:8 +msgid "Infomation about the chat boost" +msgstr "" + +#: of telebot.types.ChatBoostUpdated:12 +msgid ":class:`ChatBoostUpdated`" +msgstr "" + #: of telebot.types.ChatInviteLink:1 msgid "Represents an invite link for a chat." msgstr "" @@ -1254,7 +1553,7 @@ msgstr "" msgid ":class:`telebot.types.ChatMemberOwner`" msgstr "" -#: of telebot.types.ChatMember:5 telebot.types.ChatMemberAdministrator:60 +#: of telebot.types.ChatMember:5 telebot.types.ChatMemberAdministrator:69 msgid ":class:`telebot.types.ChatMemberAdministrator`" msgstr "" @@ -1591,13 +1890,13 @@ msgid "" " defaults to the value of can_pin_messages" msgstr "" -#: of telebot.types.ChatPermissions:52 -msgid "" -"deprecated. True, if the user is allowed to send audios, documents, " -"photos, videos, video notes and voice notes" +#: of telebot.types.ChatPermissions:52 telebot.types.Message:27 +#: telebot.types.Message:30 telebot.types.Message:37 telebot.types.Message:40 +#: telebot.types.Message:43 +msgid "deprecated." msgstr "" -#: of telebot.types.ChatPermissions:57 +#: of telebot.types.ChatPermissions:56 msgid ":class:`telebot.types.ChatPermissions`" msgstr "" @@ -1650,7 +1949,7 @@ msgstr "" msgid "Telegram documentation: https://core.telegram.org/bots/api#Chatshared" msgstr "" -#: of telebot.types.ChatShared:6 telebot.types.UserShared:6 +#: of telebot.types.ChatShared:6 msgid "identifier of the request" msgstr "" @@ -1806,6 +2105,124 @@ msgstr "" msgid ":class:`telebot.types.Document`" msgstr "" +#: of telebot.types.ExternalReplyInfo:1 +msgid "" +"This object contains information about a message that is being replied " +"to, which may come from another chat or forum topic." +msgstr "" + +#: of telebot.types.ExternalReplyInfo:4 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#externalreplyinfo" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:6 +msgid "Origin of the message replied to by the given message" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:9 +msgid "" +"Optional. Chat the original message belongs to. Available only if the " +"chat is a supergroup or a channel." +msgstr "" + +#: of telebot.types.ExternalReplyInfo:12 +msgid "" +"Optional. Unique message identifier inside the original chat. Available " +"only if the original chat is a supergroup or a channel." +msgstr "" + +#: of telebot.types.ExternalReplyInfo:15 +msgid "" +"Optional. Options used for link preview generation for the original " +"message, if it is a text message" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:18 +msgid "Optional. Message is an animation, information about the animation" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:21 telebot.types.Message:97 +msgid "Optional. Message is an audio file, information about the file" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:24 telebot.types.Message:100 +msgid "Optional. Message is a general file, information about the file" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:27 telebot.types.Message:103 +msgid "Optional. Message is a photo, available sizes of the photo" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:30 telebot.types.Message:106 +msgid "Optional. Message is a sticker, information about the sticker" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:33 telebot.types.Message:109 +msgid "Optional. Message is a forwarded story" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:36 telebot.types.Message:112 +msgid "Optional. Message is a video, information about the video" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:39 telebot.types.Message:115 +msgid "Optional. Message is a video note, information about the video message" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:42 telebot.types.Message:118 +msgid "Optional. Message is a voice message, information about the file" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:45 telebot.types.Message:128 +msgid "Optional. True, if the message media is covered by a spoiler animation" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:48 telebot.types.Message:131 +msgid "Optional. Message is a shared contact, information about the contact" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:51 telebot.types.Message:134 +msgid "Optional. Message is a dice with random value" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:54 telebot.types.Message:137 +msgid "" +"Optional. Message is a game, information about the game. More about games" +" »" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:57 +msgid "Optional. Message is a scheduled giveaway, information about the giveaway" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:60 +msgid "Optional. A giveaway with public winners was completed" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:63 telebot.types.Message:200 +msgid "" +"Optional. Message is an invoice for a payment, information about the " +"invoice. More about payments »" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:66 telebot.types.Message:147 +msgid "Optional. Message is a shared location, information about the location" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:69 telebot.types.Message:140 +msgid "Optional. Message is a native poll, information about the poll" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:72 +msgid "Optional. Message is a venue, information about the venue" +msgstr "" + +#: of telebot.types.ExternalReplyInfo:76 +msgid ":class:`ExternalReplyInfo`" +msgstr "" + #: of telebot.types.File:1 msgid "" "This object represents a file ready to be downloaded. The file can be " @@ -2061,33 +2478,209 @@ msgid "" "https://core.telegram.org/bots/api#generalforumtopicunhidden" msgstr "" -#: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 -#: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 -msgid "" -"Bases: :py:class:`telebot.types.Dictionaryable`, " -":py:class:`telebot.types.JsonSerializable`, " -":py:class:`telebot.types.JsonDeserializable`" +#: of telebot.types.Giveaway:1 +msgid "This object represents a message about a scheduled giveaway." msgstr "" -#: of telebot.types.InlineKeyboardButton:1 -msgid "" -"This object represents one button of an inline keyboard. You must use " -"exactly one of the optional fields." +#: of telebot.types.Giveaway:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#giveaway" msgstr "" -#: of telebot.types.InlineKeyboardButton:3 -#: telebot.types.SwitchInlineQueryChosenChat:4 -msgid "" -"Telegram Documentation: " -"https://core.telegram.org/bots/api#inlinekeyboardbutton" +#: of telebot.types.Giveaway:5 +msgid "The list of chats which the user must join to participate in the giveaway" msgstr "" -#: of telebot.types.InlineKeyboardButton:5 -#: telebot.types.InlineQueryResultsButton:6 -msgid "Label text on the button" +#: of telebot.types.Giveaway:8 +msgid "" +"Point in time (Unix timestamp) when winners of the giveaway will be " +"selected" msgstr "" -#: of telebot.types.InlineKeyboardButton:8 +#: of telebot.types.Giveaway:11 +msgid "" +"The number of users which are supposed to be selected as winners of the " +"giveaway" +msgstr "" + +#: of telebot.types.Giveaway:14 +msgid "" +"Optional. True, if only users who join the chats after the giveaway " +"started should be eligible to win" +msgstr "" + +#: of telebot.types.Giveaway:17 +msgid "" +"Optional. True, if the list of giveaway winners will be visible to " +"everyone" +msgstr "" + +#: of telebot.types.Giveaway:20 telebot.types.GiveawayWinners:35 +msgid "Optional. Description of additional giveaway prize" +msgstr "" + +#: of telebot.types.Giveaway:23 +msgid "" +"Optional. A list of two-letter ISO 3166-1 alpha-2 country codes " +"indicating the countries from which eligible users for the giveaway must " +"come. If empty, then all users can participate in the giveaway." +msgstr "" + +#: of telebot.types.Giveaway:26 telebot.types.GiveawayWinners:23 +msgid "" +"Optional. The number of months the Telegram Premium subscription won from" +" the giveaway will be active for" +msgstr "" + +#: of telebot.types.Giveaway:30 +msgid ":class:`Giveaway`" +msgstr "" + +#: of telebot.types.GiveawayCompleted:1 +msgid "" +"This object represents a service message about the completion of a " +"giveaway without public winners." +msgstr "" + +#: of telebot.types.GiveawayCompleted:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#giveawaycompleted" +msgstr "" + +#: of telebot.types.GiveawayCompleted:5 +msgid "Number of winners in the giveaway" +msgstr "" + +#: of telebot.types.GiveawayCompleted:8 telebot.types.GiveawayWinners:26 +msgid "Optional. Number of undistributed prizes" +msgstr "" + +#: of telebot.types.GiveawayCompleted:11 +msgid "" +"Optional. Message with the giveaway that was completed, if it wasn't " +"deleted" +msgstr "" + +#: of telebot.types.GiveawayCompleted:15 +msgid ":class:`GiveawayCompleted`" +msgstr "" + +#: of telebot.types.GiveawayCreated:1 +msgid "" +"This object represents a service message about the creation of a " +"scheduled giveaway. Currently holds no information." +msgstr "" + +#: of telebot.types.GiveawayWinners:1 +msgid "" +"This object represents a message about the completion of a giveaway with " +"public winners." +msgstr "" + +#: of telebot.types.GiveawayWinners:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#giveawaywinners" +msgstr "" + +#: of telebot.types.GiveawayWinners:5 +msgid "The chat that created the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:8 +msgid "Identifier of the messsage with the giveaway in the chat" +msgstr "" + +#: of telebot.types.GiveawayWinners:11 +msgid "Point in time (Unix timestamp) when winners of the giveaway were selected" +msgstr "" + +#: of telebot.types.GiveawayWinners:14 +msgid "Total number of winners in the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:17 +msgid "List of up to 100 winners of the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:20 +msgid "" +"Optional. The number of other chats the user had to join in order to be " +"eligible for the giveaway" +msgstr "" + +#: of telebot.types.GiveawayWinners:29 +msgid "" +"Optional. True, if only users who had joined the chats after the giveaway" +" started were eligible to win" +msgstr "" + +#: of telebot.types.GiveawayWinners:32 +msgid "" +"Optional. True, if the giveaway was canceled because the payment for it " +"was refunded" +msgstr "" + +#: of telebot.types.GiveawayWinners:39 +msgid ":class:`GiveawayWinners`" +msgstr "" + +#: of telebot.types.InaccessibleMessage:1 +msgid "" +"This object describes a message that was deleted or is otherwise " +"inaccessible to the bot." +msgstr "" + +#: of telebot.types.InaccessibleMessage:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#inaccessiblemessage" +msgstr "" + +#: of telebot.types.InaccessibleMessage:5 +msgid "Chat the message belonged to" +msgstr "" + +#: of telebot.types.InaccessibleMessage:8 telebot.types.MessageOriginChannel:6 +#: telebot.types.MessageReactionCountUpdated:8 +msgid "Unique message identifier inside the chat" +msgstr "" + +#: of telebot.types.InaccessibleMessage:11 +msgid "" +"Always 0. The field can be used to differentiate regular and inaccessible" +" messages." +msgstr "" + +#: of telebot.types.InaccessibleMessage:15 +msgid ":class:`InaccessibleMessage`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 telebot.types.InlineKeyboardMarkup:1 +#: telebot.types.LoginUrl:1 telebot.types.MessageEntity:1 +msgid "" +"Bases: :py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`, " +":py:class:`telebot.types.JsonDeserializable`" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:1 +msgid "" +"This object represents one button of an inline keyboard. You must use " +"exactly one of the optional fields." +msgstr "" + +#: of telebot.types.InlineKeyboardButton:3 +#: telebot.types.SwitchInlineQueryChosenChat:4 +msgid "" +"Telegram Documentation: " +"https://core.telegram.org/bots/api#inlinekeyboardbutton" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:5 +#: telebot.types.InlineQueryResultsButton:6 +msgid "Label text on the button" +msgstr "" + +#: of telebot.types.InlineKeyboardButton:8 msgid "" "Optional. HTTP or tg:// URL to be opened when the button is pressed. " "Links tg://user?id= can be used to mention a user by their ID " @@ -3390,7 +3983,7 @@ msgstr "" #: telebot.types.InputVenueMessageContent:1 #: telebot.types.KeyboardButtonPollType:1 #: telebot.types.KeyboardButtonRequestChat:1 -#: telebot.types.KeyboardButtonRequestUser:1 +#: telebot.types.KeyboardButtonRequestUsers:1 msgid "Bases: :py:class:`telebot.types.Dictionaryable`" msgstr "" @@ -3842,11 +4435,16 @@ msgid "" " be specified instead of parse_mode" msgstr "" -#: of telebot.types.InputTextMessageContent:16 -msgid "Optional. Disables link previews for links in the sent message" +#: of telebot.types.InputTextMessageContent:16 telebot.types.KeyboardButton:25 +#: telebot.types.StickerSet:20 +msgid "deprecated" +msgstr "" + +#: of telebot.types.InputTextMessageContent:19 +msgid "Optional. Link preview generation options for the message" msgstr "" -#: of telebot.types.InputTextMessageContent:20 +#: of telebot.types.InputTextMessageContent:23 msgid ":class:`telebot.types.InputTextMessageContent`" msgstr "" @@ -3980,21 +4578,21 @@ msgid "" "service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:25 +#: of telebot.types.KeyboardButton:28 msgid "" "Optional. If specified, pressing the button will open a list of suitable " -"users. Tapping on any user will send their identifier to the bot in a " -"“user_shared” service message. Available in private chats only." +"users. Identifiers of selected users will be sent to the bot in a " +"“users_shared” service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:29 +#: of telebot.types.KeyboardButton:32 msgid "" "Optional. If specified, pressing the button will open a list of suitable " "chats. Tapping on a chat will send its identifier to the bot in a " "“chat_shared” service message. Available in private chats only." msgstr "" -#: of telebot.types.KeyboardButton:34 +#: of telebot.types.KeyboardButton:37 msgid ":class:`telebot.types.KeyboardButton`" msgstr "" @@ -4094,39 +4692,51 @@ msgid ":class:`telebot.types.KeyboardButtonRequestChat`" msgstr "" #: of telebot.types.KeyboardButtonRequestUser:1 +msgid "Bases: :py:class:`telebot.types.KeyboardButtonRequestUsers`" +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUser:1 +msgid "Deprecated. Use KeyboardButtonRequestUsers instead." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUsers:1 msgid "" "This object defines the criteria used to request a suitable user. The " "identifier of the selected user will be shared with the bot when the " "corresponding button is pressed." msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:4 +#: of telebot.types.KeyboardButtonRequestUsers:4 msgid "" "Telegram documentation: " -"https://core.telegram.org/bots/api#keyboardbuttonrequestuser" +"https://core.telegram.org/bots/api#keyboardbuttonrequestusers" msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:6 +#: of telebot.types.KeyboardButtonRequestUsers:6 msgid "" "Signed 32-bit identifier of the request, which will be received back in " -"the UserShared object. Must be unique within the message" +"the UsersShared object. Must be unique within the message" msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:10 +#: of telebot.types.KeyboardButtonRequestUsers:10 msgid "" "Optional. Pass True to request a bot, pass False to request a regular " "user. If not specified, no additional restrictions are applied." msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:14 +#: of telebot.types.KeyboardButtonRequestUsers:14 msgid "" "Optional. Pass True to request a premium user, pass False to request a " "non-premium user. If not specified, no additional restrictions are " "applied." msgstr "" -#: of telebot.types.KeyboardButtonRequestUser:19 -msgid ":class:`telebot.types.KeyboardButtonRequestUser`" +#: of telebot.types.KeyboardButtonRequestUsers:18 +msgid "Optional. The maximum number of users to be selected; 1-10. Defaults to 1." +msgstr "" + +#: of telebot.types.KeyboardButtonRequestUsers:22 +msgid ":class:`telebot.types.KeyboardButtonRequestUsers`" msgstr "" #: of telebot.types.LabeledPrice:1 @@ -4154,6 +4764,59 @@ msgstr "" msgid ":class:`telebot.types.LabeledPrice`" msgstr "" +#: of telebot.types.LinkPreviewOptions:1 telebot.types.ReactionType:1 +#: telebot.types.ReplyParameters:1 telebot.types.SwitchInlineQueryChosenChat:1 +#: telebot.types.User:1 +msgid "" +"Bases: :py:class:`telebot.types.JsonDeserializable`, " +":py:class:`telebot.types.Dictionaryable`, " +":py:class:`telebot.types.JsonSerializable`" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:1 +msgid "Describes the options used for link preview generation." +msgstr "" + +#: of telebot.types.LinkPreviewOptions:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#linkpreviewoptions" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:5 +msgid "Optional. True, if the link preview is disabled" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:8 +msgid "" +"Optional. URL to use for the link preview. If empty, then the first URL " +"found in the message text will be used" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:11 +msgid "" +"Optional. True, if the media in the link preview is supposed to be " +"shrunk; ignored if the URL isn't explicitly specified or media size " +"change isn't supported for the preview" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:14 +msgid "" +"Optional. True, if the media in the link preview is supposed to be " +"enlarged; ignored if the URL isn't explicitly specified or media size " +"change isn't supported for the preview" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:17 +msgid "" +"Optional. True, if the link preview must be shown above the message text;" +" otherwise, the link preview will be shown below the message text" +msgstr "" + +#: of telebot.types.LinkPreviewOptions:21 +msgid ":class:`LinkPreviewOptions`" +msgstr "" + #: of telebot.types.Location:1 msgid "This object represents a point on the map." msgstr "" @@ -4421,39 +5084,8 @@ msgstr "" msgid "Conversation the message belongs to" msgstr "" -#: of telebot.types.Message:27 -msgid "Optional. For forwarded messages, sender of the original message" -msgstr "" - -#: of telebot.types.Message:30 -msgid "" -"Optional. For messages forwarded from channels or from anonymous " -"administrators, information about the original sender chat" -msgstr "" - -#: of telebot.types.Message:34 -msgid "" -"Optional. For messages forwarded from channels, identifier of the " -"original message in the channel" -msgstr "" - -#: of telebot.types.Message:38 -msgid "" -"Optional. For forwarded messages that were originally sent in channels or" -" by an anonymous chat administrator, signature of the message sender if " -"present" -msgstr "" - -#: of telebot.types.Message:42 -msgid "" -"Optional. Sender's name for messages forwarded from users who disallow " -"adding a link to their account in forwarded messages" -msgstr "" - -#: of telebot.types.Message:46 -msgid "" -"Optional. For forwarded messages, date the original message was sent in " -"Unix time" +#: of telebot.types.Message:33 +msgid "deprecated. message in the channel" msgstr "" #: of telebot.types.Message:49 @@ -4474,148 +5106,112 @@ msgid "" msgstr "" #: of telebot.types.Message:60 -msgid "Optional. Bot through which the message was sent" +msgid "" +"Optional. Information about the message that is being replied to, which " +"may come from another chat or forum topic" msgstr "" #: of telebot.types.Message:63 -msgid "Optional. Date the message was last edited in Unix time" +msgid "" +"Optional. For replies that quote part of the original message, the quoted" +" part of the message" msgstr "" #: of telebot.types.Message:66 -msgid "Optional. :obj:`bool`, if the message can't be forwarded" +msgid "Optional. Bot through which the message was sent" msgstr "" #: of telebot.types.Message:69 +msgid "Optional. Date the message was last edited in Unix time" +msgstr "" + +#: of telebot.types.Message:72 +msgid "Optional. :obj:`bool`, if the message can't be forwarded" +msgstr "" + +#: of telebot.types.Message:75 msgid "" "Optional. The unique identifier of a media message group this message " "belongs to" msgstr "" -#: of telebot.types.Message:72 +#: of telebot.types.Message:78 msgid "" "Optional. Signature of the post author for messages in channels, or the " "custom title of an anonymous group administrator" msgstr "" -#: of telebot.types.Message:76 +#: of telebot.types.Message:82 msgid "Optional. For text messages, the actual UTF-8 text of the message" msgstr "" -#: of telebot.types.Message:79 +#: of telebot.types.Message:85 msgid "" "Optional. For text messages, special entities like usernames, URLs, bot " "commands, etc. that appear in the text" msgstr "" -#: of telebot.types.Message:83 +#: of telebot.types.Message:89 msgid "" -"Optional. Message is an animation, information about the animation. For " -"backward compatibility, when this field is set, the document field will " -"also be set" -msgstr "" - -#: of telebot.types.Message:87 -msgid "Optional. Message is an audio file, information about the file" -msgstr "" - -#: of telebot.types.Message:90 -msgid "Optional. Message is a general file, information about the file" +"Optional. Options used for link preview generation for the message, if it" +" is a text message and link preview options were changed" msgstr "" #: of telebot.types.Message:93 -msgid "Optional. Message is a photo, available sizes of the photo" -msgstr "" - -#: of telebot.types.Message:96 -msgid "Optional. Message is a sticker, information about the sticker" -msgstr "" - -#: of telebot.types.Message:99 -msgid "Optional. Message is a video, information about the video" -msgstr "" - -#: of telebot.types.Message:102 -msgid "Optional. Message is a video note, information about the video message" -msgstr "" - -#: of telebot.types.Message:105 -msgid "Optional. Message is a voice message, information about the file" +msgid "" +"Optional. Message is an animation, information about the animation. For " +"backward compatibility, when this field is set, the document field will " +"also be set" msgstr "" -#: of telebot.types.Message:108 +#: of telebot.types.Message:121 msgid "" "Optional. Caption for the animation, audio, document, photo, video or " "voice" msgstr "" -#: of telebot.types.Message:111 +#: of telebot.types.Message:124 msgid "" "Optional. For messages with a caption, special entities like usernames, " "URLs, bot commands, etc. that appear in the caption" msgstr "" -#: of telebot.types.Message:115 -msgid "Optional. True, if the message media is covered by a spoiler animation" -msgstr "" - -#: of telebot.types.Message:118 -msgid "Optional. Message is a shared contact, information about the contact" -msgstr "" - -#: of telebot.types.Message:121 -msgid "Optional. Message is a dice with random value" -msgstr "" - -#: of telebot.types.Message:124 -msgid "" -"Optional. Message is a game, information about the game. More about games" -" »" -msgstr "" - -#: of telebot.types.Message:127 -msgid "Optional. Message is a native poll, information about the poll" -msgstr "" - -#: of telebot.types.Message:130 +#: of telebot.types.Message:143 msgid "" "Optional. Message is a venue, information about the venue. For backward " "compatibility, when this field is set, the location field will also be " "set" msgstr "" -#: of telebot.types.Message:134 -msgid "Optional. Message is a shared location, information about the location" -msgstr "" - -#: of telebot.types.Message:137 +#: of telebot.types.Message:150 msgid "" "Optional. New members that were added to the group or supergroup and " "information about them (the bot itself may be one of these members)" msgstr "" -#: of telebot.types.Message:141 +#: of telebot.types.Message:154 msgid "" "Optional. A member was removed from the group, information about them " "(this member may be the bot itself)" msgstr "" -#: of telebot.types.Message:145 +#: of telebot.types.Message:158 msgid "Optional. A chat title was changed to this value" msgstr "" -#: of telebot.types.Message:148 +#: of telebot.types.Message:161 msgid "Optional. A chat photo was change to this value" msgstr "" -#: of telebot.types.Message:151 +#: of telebot.types.Message:164 msgid "Optional. Service message: the chat photo was deleted" msgstr "" -#: of telebot.types.Message:154 +#: of telebot.types.Message:167 msgid "Optional. Service message: the group has been created" msgstr "" -#: of telebot.types.Message:157 +#: of telebot.types.Message:170 msgid "" "Optional. Service message: the supergroup has been created. This field " "can't be received in a message coming through updates, because bot can't " @@ -4624,7 +5220,7 @@ msgid "" " created supergroup." msgstr "" -#: of telebot.types.Message:162 +#: of telebot.types.Message:175 msgid "" "Optional. Service message: the channel has been created. This field can't" " be received in a message coming through updates, because bot can't be a " @@ -4632,11 +5228,11 @@ msgid "" "reply_to_message if someone replies to a very first message in a channel." msgstr "" -#: of telebot.types.Message:167 +#: of telebot.types.Message:180 msgid "Optional. Service message: auto-delete timer settings changed in the chat" msgstr "" -#: of telebot.types.Message:171 +#: of telebot.types.Message:184 msgid "" "Optional. The group has been migrated to a supergroup with the specified " "identifier. This number may have more than 32 significant bits and some " @@ -4645,7 +5241,7 @@ msgid "" " double-precision float type are safe for storing this identifier." msgstr "" -#: of telebot.types.Message:177 +#: of telebot.types.Message:190 msgid "" "Optional. The supergroup has been migrated from a group with the " "specified identifier. This number may have more than 32 significant bits " @@ -4655,106 +5251,124 @@ msgid "" "identifier." msgstr "" -#: of telebot.types.Message:183 +#: of telebot.types.Message:196 msgid "" "Optional. Specified message was pinned. Note that the Message object in " "this field will not contain further reply_to_message fields even if it is" " itself a reply." msgstr "" -#: of telebot.types.Message:187 -msgid "" -"Optional. Message is an invoice for a payment, information about the " -"invoice. More about payments »" -msgstr "" - -#: of telebot.types.Message:190 +#: of telebot.types.Message:203 msgid "" "Optional. Message is a service message about a successful payment, " "information about the payment. More about payments »" msgstr "" -#: of telebot.types.Message:194 +#: of telebot.types.Message:207 msgid "Optional. Service message: a user was shared with the bot" msgstr "" -#: of telebot.types.Message:197 +#: of telebot.types.Message:210 msgid "Optional. Service message: a chat was shared with the bot" msgstr "" -#: of telebot.types.Message:200 +#: of telebot.types.Message:213 msgid "" "Optional. The domain name of the website on which the user has logged in." " More about Telegram Login »" msgstr "" -#: of telebot.types.Message:204 +#: of telebot.types.Message:217 msgid "" "Optional. Service message: the user allowed the bot added to the " "attachment menu to write messages" msgstr "" -#: of telebot.types.Message:208 +#: of telebot.types.Message:221 msgid "Optional. Telegram Passport data" msgstr "" -#: of telebot.types.Message:211 +#: of telebot.types.Message:224 msgid "" "Optional. Service message. A user in the chat triggered another user's " "proximity alert while sharing Live Location." msgstr "" -#: of telebot.types.Message:215 +#: of telebot.types.Message:228 msgid "Optional. Service message: forum topic created" msgstr "" -#: of telebot.types.Message:218 +#: of telebot.types.Message:231 msgid "Optional. Service message: forum topic edited" msgstr "" -#: of telebot.types.Message:221 +#: of telebot.types.Message:234 msgid "Optional. Service message: forum topic closed" msgstr "" -#: of telebot.types.Message:224 +#: of telebot.types.Message:237 msgid "Optional. Service message: forum topic reopened" msgstr "" -#: of telebot.types.Message:227 +#: of telebot.types.Message:240 msgid "Optional. Service message: the 'General' forum topic hidden" msgstr "" -#: of telebot.types.Message:230 +#: of telebot.types.Message:243 msgid "Optional. Service message: the 'General' forum topic unhidden" msgstr "" -#: of telebot.types.Message:233 +#: of telebot.types.Message:246 +msgid "Optional. Service message: a giveaway has been created" +msgstr "" + +#: of telebot.types.Message:249 +msgid "Optional. The message is a scheduled giveaway message" +msgstr "" + +#: of telebot.types.Message:252 +msgid "Optional. Service message: giveaway winners(public winners)" +msgstr "" + +#: of telebot.types.Message:255 +msgid "Optional. Service message: giveaway completed, without public winners" +msgstr "" + +#: of telebot.types.Message:258 msgid "Optional. Service message: video chat scheduled" msgstr "" -#: of telebot.types.Message:236 +#: of telebot.types.Message:261 msgid "Optional. Service message: video chat started" msgstr "" -#: of telebot.types.Message:239 +#: of telebot.types.Message:264 msgid "Optional. Service message: video chat ended" msgstr "" -#: of telebot.types.Message:242 +#: of telebot.types.Message:267 msgid "Optional. Service message: new participants invited to a video chat" msgstr "" -#: of telebot.types.Message:245 +#: of telebot.types.Message:270 msgid "Optional. Service message: data sent by a Web App" msgstr "" -#: of telebot.types.Message:248 +#: of telebot.types.Message:273 msgid "" "Optional. Inline keyboard attached to the message. login_url buttons are " "represented as ordinary url buttons." msgstr "" -#: of telebot.types.Message:253 +#: of telebot.types.Message +msgid "forward_origin" +msgstr "" + +#: of telebot.types.Message:46 +msgid "Optional. For forwarded messages, information about the original message;" +msgstr "" + +#: of telebot.types.Message:278 msgid ":class:`telebot.types.Message`" msgstr "" @@ -4873,12 +5487,163 @@ msgstr "" msgid ":class:`telebot.types.MessageId`" msgstr "" -#: of telebot.types.OrderInfo:1 -msgid "This object represents information about an order." +#: of telebot.types.MessageOrigin:1 +msgid "This object describes the origin of a message." msgstr "" -#: of telebot.types.OrderInfo:3 -msgid "Telegram Documentation: https://core.telegram.org/bots/api#orderinfo" +#: of telebot.types.MessageOrigin:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#messageorigin" +msgstr "" + +#: of telebot.types.MessageOrigin:5 +msgid "Type of the message origin" +msgstr "" + +#: of telebot.types.MessageOrigin:8 +msgid "Date the message was sent originally in Unix time" +msgstr "" + +#: of telebot.types.MessageOrigin:11 +msgid "User that sent the message originally (for MessageOriginUser)" +msgstr "" + +#: of telebot.types.MessageOrigin:14 +msgid "" +"Name of the user that sent the message originally (for " +"MessageOriginHiddenUser)" +msgstr "" + +#: of telebot.types.MessageOrigin:17 +msgid "Chat that sent the message originally (for MessageOriginChat)" +msgstr "" + +#: of telebot.types.MessageOrigin:20 +msgid "Optional. Author signature for certain cases" +msgstr "" + +#: of telebot.types.MessageOrigin:24 +msgid ":class:`MessageOrigin`" +msgstr "" + +#: of telebot.types.MessageOriginChannel:1 telebot.types.MessageOriginChat:1 +#: telebot.types.MessageOriginHiddenUser:1 telebot.types.MessageOriginUser:1 +msgid "Bases: :py:class:`telebot.types.MessageOrigin`" +msgstr "" + +#: of telebot.types.MessageOriginChannel:1 +msgid "The message was originally sent to a channel chat." +msgstr "" + +#: of telebot.types.MessageOriginChannel:3 +msgid "Channel chat to which the message was originally sent" +msgstr "" + +#: of telebot.types.MessageOriginChannel:9 +msgid "Optional. Signature of the original post author" +msgstr "" + +#: of telebot.types.MessageOriginChat:1 +msgid "The message was originally sent on behalf of a chat to a group chat." +msgstr "" + +#: of telebot.types.MessageOriginChat:3 +msgid "Chat that sent the message originally" +msgstr "" + +#: of telebot.types.MessageOriginChat:6 +msgid "" +"Optional. For messages originally sent by an anonymous chat " +"administrator, original message author signature" +msgstr "" + +#: of telebot.types.MessageOriginHiddenUser:1 +msgid "The message was originally sent by an unknown user." +msgstr "" + +#: of telebot.types.MessageOriginHiddenUser:3 +msgid "Name of the user that sent the message originally" +msgstr "" + +#: of telebot.types.MessageOriginUser:1 +msgid "The message was originally sent by a known user." +msgstr "" + +#: of telebot.types.MessageOriginUser:3 +msgid "User that sent the message originally" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:1 +#: telebot.types.MessageReactionUpdated:1 +msgid "" +"This object represents a service message about a change in the list of " +"the current user's reactions to a message." +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#messagereactioncountupdated" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:5 +msgid "The chat containing the message" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:11 +#: telebot.types.MessageReactionUpdated:17 +msgid "Date of the change in Unix time" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:14 +msgid "List of reactions that are present on the message" +msgstr "" + +#: of telebot.types.MessageReactionCountUpdated:18 +msgid ":class:`MessageReactionCountUpdated`" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#messagereactionupdated" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:5 +msgid "The chat containing the message the user reacted to" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:8 +msgid "Unique identifier of the message inside the chat" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:11 +msgid "Optional. The user that changed the reaction, if the user isn't anonymous" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:14 +msgid "" +"Optional. The chat on behalf of which the reaction was changed, if the " +"user is anonymous" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:20 +msgid "Previous list of reaction types that were set by the user" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:23 +msgid "New list of reaction types that have been set by the user" +msgstr "" + +#: of telebot.types.MessageReactionUpdated:27 +msgid ":class:`MessageReactionUpdated`" +msgstr "" + +#: of telebot.types.OrderInfo:1 +msgid "This object represents information about an order." +msgstr "" + +#: of telebot.types.OrderInfo:3 +msgid "Telegram Documentation: https://core.telegram.org/bots/api#orderinfo" msgstr "" #: of telebot.types.OrderInfo:5 @@ -5016,16 +5781,22 @@ msgid "Telegram Documentation: https://core.telegram.org/bots/api#pollanswer" msgstr "" #: of telebot.types.PollAnswer:8 -msgid "The user, who changed the answer to the poll" +msgid "" +"Optional. The chat that changed the answer to the poll, if the voter is " +"anonymous" msgstr "" #: of telebot.types.PollAnswer:11 +msgid "Optional. The user, who changed the answer to the poll" +msgstr "" + +#: of telebot.types.PollAnswer:14 msgid "" "0-based identifiers of answer options, chosen by the user. May be empty " "if the user retracted their vote." msgstr "" -#: of telebot.types.PollAnswer:16 +#: of telebot.types.PollAnswer:19 msgid ":class:`telebot.types.PollAnswer`" msgstr "" @@ -5112,6 +5883,88 @@ msgstr "" msgid ":class:`telebot.types.ProximityAlertTriggered`" msgstr "" +#: of telebot.types.ReactionCount:1 +msgid "" +"This object represents a reaction added to a message along with the " +"number of times it was added." +msgstr "" + +#: of telebot.types.ReactionCount:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#reactioncount" +msgstr "" + +#: of telebot.types.ReactionCount:5 telebot.types.ReactionType:5 +msgid "Type of the reaction" +msgstr "" + +#: of telebot.types.ReactionCount:8 +msgid "Number of times the reaction was added" +msgstr "" + +#: of telebot.types.ReactionCount:12 +msgid ":class:`ReactionCount`" +msgstr "" + +#: of telebot.types.ReactionType:1 +msgid "This object represents a reaction type." +msgstr "" + +#: of telebot.types.ReactionType:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#reactiontype" +msgstr "" + +#: of telebot.types.ReactionType:9 +msgid ":class:`ReactionType`" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:1 telebot.types.ReactionTypeEmoji:1 +msgid "Bases: :py:class:`telebot.types.ReactionType`" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:1 +msgid "This object represents a custom emoji reaction type." +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reactiontypecustomemoji" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:5 +msgid "Type of the reaction, must be custom_emoji" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:8 +msgid "Identifier of the custom emoji" +msgstr "" + +#: of telebot.types.ReactionTypeCustomEmoji:12 +msgid ":class:`ReactionTypeCustomEmoji`" +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:1 +msgid "This object represents an emoji reaction type." +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:3 +msgid "" +"Telegram documentation: " +"https://core.telegram.org/bots/api#reactiontypeemoji" +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:5 +msgid "Type of the reaction, must be emoji" +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:8 +msgid "Reaction emoji. List is available on the API doc." +msgstr "" + +#: of telebot.types.ReactionTypeEmoji:12 +msgid ":class:`ReactionTypeEmoji`" +msgstr "" + #: of telebot.types.ReplyKeyboardMarkup:1 msgid "" "This object represents a custom keyboard with reply options (see " @@ -5265,6 +6118,65 @@ msgstr "" msgid ":class:`telebot.types.ReplyKeyboardRemove`" msgstr "" +#: of telebot.types.ReplyParameters:1 +msgid "Describes reply parameters for the message that is being sent." +msgstr "" + +#: of telebot.types.ReplyParameters:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#replyparameters" +msgstr "" + +#: of telebot.types.ReplyParameters:5 +msgid "" +"Identifier of the message that will be replied to in the current chat, or" +" in the chat chat_id if it is specified" +msgstr "" + +#: of telebot.types.ReplyParameters:8 +msgid "" +"Optional. If the message to be replied to is from a different chat, " +"unique identifier for the chat or username of the channel (in the format " +"@channelusername)" +msgstr "" + +#: of telebot.types.ReplyParameters:11 +msgid "" +"Optional. Pass True if the message should be sent even if the specified " +"message to be replied to is not found; can be used only for replies in " +"the same chat and forum topic." +msgstr "" + +#: of telebot.types.ReplyParameters:14 +msgid "" +"Optional. Quoted part of the message to be replied to; 0-1024 characters " +"after entities parsing. The quote must be an exact substring of the " +"message to be replied to, including bold, italic, underline, " +"strikethrough, spoiler, and custom_emoji entities. The message will fail " +"to send if the quote isn't found in the original message." +msgstr "" + +#: of telebot.types.ReplyParameters:17 +msgid "" +"Optional. Mode for parsing entities in the quote. See formatting options " +"for more details." +msgstr "" + +#: of telebot.types.ReplyParameters:20 +msgid "" +"Optional. A JSON-serialized list of special entities that appear in the " +"quote. It can be specified instead of quote_parse_mode." +msgstr "" + +#: of telebot.types.ReplyParameters:23 +msgid "" +"Optional. Position of the quote in the original message in UTF-16 code " +"units" +msgstr "" + +#: of telebot.types.ReplyParameters:27 +msgid ":class:`ReplyParameters`" +msgstr "" + #: of telebot.types.SentWebAppMessage:1 telebot.types.WebAppData:1 #: telebot.types.WebAppInfo:1 msgid "" @@ -5476,21 +6388,15 @@ msgstr "" msgid "True, if the sticker set contains video stickers" msgstr "" -#: of telebot.types.StickerSet:20 -msgid "" -"True, if the sticker set contains masks. Deprecated since Bot API 6.2, " -"use sticker_type instead." -msgstr "" - -#: of telebot.types.StickerSet:24 +#: of telebot.types.StickerSet:23 msgid "List of all set stickers" msgstr "" -#: of telebot.types.StickerSet:27 +#: of telebot.types.StickerSet:26 msgid "Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format" msgstr "" -#: of telebot.types.StickerSet:31 +#: of telebot.types.StickerSet:30 msgid ":class:`telebot.types.StickerSet`" msgstr "" @@ -5498,6 +6404,12 @@ msgstr "" msgid "Deprecated since Bot API 6.2, use sticker_type instead." msgstr "" +#: of telebot.types.Story:1 +msgid "" +"This object represents a message about a forwarded story in the chat. " +"Currently holds no information." +msgstr "" + #: of telebot.types.SuccessfulPayment:1 msgid "This object contains basic information about a successful payment." msgstr "" @@ -5520,13 +6432,6 @@ msgstr "" msgid ":class:`telebot.types.SuccessfulPayment`" msgstr "" -#: of telebot.types.SwitchInlineQueryChosenChat:1 telebot.types.User:1 -msgid "" -"Bases: :py:class:`telebot.types.JsonDeserializable`, " -":py:class:`telebot.types.Dictionaryable`, " -":py:class:`telebot.types.JsonSerializable`" -msgstr "" - #: of telebot.types.SwitchInlineQueryChosenChat:1 msgid "" "Represents an inline button that switches the current user to inline mode" @@ -5559,6 +6464,45 @@ msgstr "" msgid ":class:`SwitchInlineQueryChosenChat`" msgstr "" +#: of telebot.types.TextQuote:1 +msgid "" +"This object contains information about the quoted part of a message that " +"is replied to by the given message." +msgstr "" + +#: of telebot.types.TextQuote:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#textquote" +msgstr "" + +#: of telebot.types.TextQuote:5 +msgid "" +"Text of the quoted part of a message that is replied to by the given " +"message" +msgstr "" + +#: of telebot.types.TextQuote:8 +msgid "" +"Optional. Special entities that appear in the quote. Currently, only " +"bold, italic, underline, strikethrough, spoiler, and custom_emoji " +"entities are kept in quotes." +msgstr "" + +#: of telebot.types.TextQuote:11 +msgid "" +"Approximate quote position in the original message in UTF-16 code units " +"as specified by the sender" +msgstr "" + +#: of telebot.types.TextQuote:14 +msgid "" +"Optional. True, if the quote was chosen manually by the message sender. " +"Otherwise, the quote was added automatically by the server." +msgstr "" + +#: of telebot.types.TextQuote:18 +msgid ":class:`TextQuote`" +msgstr "" + #: of telebot.types.Update:1 msgid "" "This object represents an incoming update.At most one of the optional " @@ -5600,66 +6544,94 @@ msgid "" msgstr "" #: of telebot.types.Update:23 +msgid "" +"Optional. A reaction to a message was changed by a user. The bot must be " +"an administrator in the chat and must explicitly specify " +"\"message_reaction\" in the list of allowed_updates to receive these " +"updates. The update isn't received for reactions set by bots." +msgstr "" + +#: of telebot.types.Update:27 +msgid "" +"Optional. Reactions to a message with anonymous reactions were changed. " +"The bot must be an administrator in the chat and must explicitly specify " +"\"message_reaction_count\" in the list of allowed_updates to receive " +"these updates." +msgstr "" + +#: of telebot.types.Update:31 msgid "Optional. New incoming inline query" msgstr "" -#: of telebot.types.Update:26 +#: of telebot.types.Update:34 msgid "" "Optional. The result of an inline query that was chosen by a user and " "sent to their chat partner. Please see our documentation on the feedback " "collecting for details on how to enable these updates for your bot." msgstr "" -#: of telebot.types.Update:31 +#: of telebot.types.Update:39 msgid "Optional. New incoming callback query" msgstr "" -#: of telebot.types.Update:34 +#: of telebot.types.Update:42 msgid "" "Optional. New incoming shipping query. Only for invoices with flexible " "price" msgstr "" -#: of telebot.types.Update:37 +#: of telebot.types.Update:45 msgid "" "Optional. New incoming pre-checkout query. Contains full information " "about checkout" msgstr "" -#: of telebot.types.Update:41 +#: of telebot.types.Update:49 msgid "" "Optional. New poll state. Bots receive only updates about stopped polls " "and polls, which are sent by the bot" msgstr "" -#: of telebot.types.Update:45 +#: of telebot.types.Update:53 msgid "" "Optional. A user changed their answer in a non-anonymous poll. Bots " "receive new votes only in polls that were sent by the bot itself." msgstr "" -#: of telebot.types.Update:49 +#: of telebot.types.Update:57 msgid "" "Optional. The bot's chat member status was updated in a chat. For private" " chats, this update is received only when the bot is blocked or unblocked" " by the user." msgstr "" -#: of telebot.types.Update:53 +#: of telebot.types.Update:61 msgid "" "Optional. A chat member's status was updated in a chat. The bot must be " "an administrator in the chat and must explicitly specify “chat_member” in" " the list of allowed_updates to receive these updates." msgstr "" -#: of telebot.types.Update:57 +#: of telebot.types.Update:65 msgid "" "Optional. A request to join the chat has been sent. The bot must have the" " can_invite_users administrator right in the chat to receive these " "updates." msgstr "" -#: of telebot.types.Update:62 +#: of telebot.types.Update:69 +msgid "" +"Optional. A chat boost was added or changed. The bot must be an " +"administrator in the chat to receive these updates." +msgstr "" + +#: of telebot.types.Update:72 +msgid "" +"Optional. A chat boost was removed. The bot must be an administrator in " +"the chat to receive these updates." +msgstr "" + +#: of telebot.types.Update:76 msgid ":class:`telebot.types.Update`" msgstr "" @@ -5742,6 +6714,22 @@ msgstr "" msgid "return" msgstr "" +#: of telebot.types.UserChatBoosts:1 +msgid "This object represents a list of boosts added to a chat by a user." +msgstr "" + +#: of telebot.types.UserChatBoosts:3 +msgid "Telegram documentation: https://core.telegram.org/bots/api#userchatboosts" +msgstr "" + +#: of telebot.types.UserChatBoosts:5 +msgid "The list of boosts added to the chat by the user" +msgstr "" + +#: of telebot.types.UserChatBoosts:9 +msgid ":class:`UserChatBoosts`" +msgstr "" + #: of telebot.types.UserProfilePhotos:1 msgid "This object represent a user's profile pictures." msgstr "" @@ -5764,30 +6752,33 @@ msgstr "" msgid ":class:`telebot.types.UserProfilePhotos`" msgstr "" -#: of telebot.types.UserShared:1 +#: of telebot.types.UsersShared:1 msgid "" -"This object contains information about the user whose identifier was " -"shared with the bot using a `telebot.types.KeyboardButtonRequestUser` " -"button." +"This object contains information about the users whose identifiers were " +"shared with the bot using a KeyboardButtonRequestUsers button." msgstr "" -#: of telebot.types.UserShared:4 -msgid "Telegram documentation: https://core.telegram.org/bots/api#usershared" +#: of telebot.types.UsersShared:4 +msgid "Telegram documentation: https://core.telegram.org/bots/api#usersshared" msgstr "" -#: of telebot.types.UserShared:9 +#: of telebot.types.UsersShared:6 +msgid "Identifier of the request" +msgstr "" + +#: of telebot.types.UsersShared:9 msgid "" -"Identifier of the shared user. This number may have more than 32 " +"Identifiers of the shared users. These numbers may have more than 32 " "significant bits and some programming languages may have " -"difficulty/silent defects in interpreting it. But it has at most 52 " -"significant bits, so a 64-bit integer or double-precision float type are " -"safe for storing this identifier. The bot may not have access to the user" -" and could be unable to use this identifier, unless the user is already " -"known to the bot by some other means." +"difficulty/silent defects in interpreting them. But they have at most 52 " +"significant bits, so 64-bit integers or double-precision float types are " +"safe for storing these identifiers. The bot may not have access to the " +"users and could be unable to use these identifiers unless the users are " +"already known to the bot by some other means." msgstr "" -#: of telebot.types.UserShared:16 -msgid ":class:`telebot.types.UserShared`" +#: of telebot.types.UsersShared:18 +msgid ":class:`UsersShared`" msgstr "" #: of telebot.types.Venue:1 @@ -6065,20 +7056,34 @@ msgstr "" #: of telebot.types.WriteAccessAllowed:1 msgid "" -"This object represents a service message about a user allowed to post " -"messages in the chat. Currently holds no information." +"This object represents a service message about a user allowing a bot to " +"write messages after adding it to the attachment menu, launching a Web " +"App from a link, or accepting an explicit request from a Web App sent by " +"the method requestWriteAccess." msgstr "" -#: of telebot.types.WriteAccessAllowed:4 +#: of telebot.types.WriteAccessAllowed:5 msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#writeaccessallowed" msgstr "" -#: of telebot.types.WriteAccessAllowed:6 +#: of telebot.types.WriteAccessAllowed:7 +msgid "" +"Optional. True, if the access was granted after the user accepted an " +"explicit request from a Web App sent by the method requestWriteAccess" +msgstr "" + +#: of telebot.types.WriteAccessAllowed:11 msgid "Optional. Name of the Web App which was launched from a link" msgstr "" +#: of telebot.types.WriteAccessAllowed:14 +msgid "" +"Optional. True, if the access was granted when the bot was added to the " +"attachment or side menu" +msgstr "" + #~ msgid "Type of the result, must be animation" #~ msgstr "" @@ -6109,3 +7114,127 @@ msgstr "" #~ "can_send_media_messages" #~ msgstr "" +#~ msgid "" +#~ "Optional. Message with the callback " +#~ "button that originated the query. Note" +#~ " that message content and message " +#~ "date will not be available if the" +#~ " message is too old" +#~ msgstr "" + +#~ msgid "" +#~ "deprecated. True, if the user is " +#~ "allowed to send audios, documents, " +#~ "photos, videos, video notes and voice" +#~ " notes" +#~ msgstr "" + +#~ msgid "Optional. Disables link previews for links in the sent message" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. If specified, pressing the " +#~ "button will open a list of " +#~ "suitable users. Tapping on any user " +#~ "will send their identifier to the " +#~ "bot in a “user_shared” service message." +#~ " Available in private chats only." +#~ msgstr "" + +#~ msgid "" +#~ "Telegram documentation: " +#~ "https://core.telegram.org/bots/api#keyboardbuttonrequestuser" +#~ msgstr "" + +#~ msgid "" +#~ "Signed 32-bit identifier of the request," +#~ " which will be received back in " +#~ "the UserShared object. Must be unique" +#~ " within the message" +#~ msgstr "" + +#~ msgid ":class:`telebot.types.KeyboardButtonRequestUser`" +#~ msgstr "" + +#~ msgid "Optional. For forwarded messages, sender of the original message" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For messages forwarded from " +#~ "channels or from anonymous administrators, " +#~ "information about the original sender " +#~ "chat" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For messages forwarded from " +#~ "channels, identifier of the original " +#~ "message in the channel" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For forwarded messages that " +#~ "were originally sent in channels or " +#~ "by an anonymous chat administrator, " +#~ "signature of the message sender if " +#~ "present" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. Sender's name for messages " +#~ "forwarded from users who disallow adding" +#~ " a link to their account in " +#~ "forwarded messages" +#~ msgstr "" + +#~ msgid "" +#~ "Optional. For forwarded messages, date " +#~ "the original message was sent in " +#~ "Unix time" +#~ msgstr "" + +#~ msgid "The user, who changed the answer to the poll" +#~ msgstr "" + +#~ msgid "" +#~ "True, if the sticker set contains " +#~ "masks. Deprecated since Bot API 6.2, " +#~ "use sticker_type instead." +#~ msgstr "" + +#~ msgid "" +#~ "This object contains information about " +#~ "the user whose identifier was shared " +#~ "with the bot using a " +#~ "`telebot.types.KeyboardButtonRequestUser` button." +#~ msgstr "" + +#~ msgid "Telegram documentation: https://core.telegram.org/bots/api#usershared" +#~ msgstr "" + +#~ msgid "" +#~ "Identifier of the shared user. This " +#~ "number may have more than 32 " +#~ "significant bits and some programming " +#~ "languages may have difficulty/silent defects" +#~ " in interpreting it. But it has " +#~ "at most 52 significant bits, so a" +#~ " 64-bit integer or double-precision " +#~ "float type are safe for storing " +#~ "this identifier. The bot may not " +#~ "have access to the user and could" +#~ " be unable to use this identifier," +#~ " unless the user is already known " +#~ "to the bot by some other means." +#~ msgstr "" + +#~ msgid ":class:`telebot.types.UserShared`" +#~ msgstr "" + +#~ msgid "" +#~ "This object represents a service message" +#~ " about a user allowed to post " +#~ "messages in the chat. Currently holds" +#~ " no information." +#~ msgstr "" + From af95b5e2c1ad2d46d77bcdcddcd42a96e512a5cc Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Jan 2024 22:24:50 +0400 Subject: [PATCH 1443/1808] Update .readthedocs.yml --- .readthedocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index d157652e3..948e734bf 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -22,6 +22,5 @@ formats: all # Optionally set the version of Python and requirements required to build your docs python: - version: 3.11 install: - requirements: doc_req.txt From 1533e1a2e15739f7545aef7b14a2b5dfa4b61191 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Jan 2024 22:26:15 +0400 Subject: [PATCH 1444/1808] Delete .readthedocs.yaml --- .readthedocs.yaml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index ac48ea5a0..000000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# .readthedocs.yaml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the version of Python and other tools you might need -build: - os: ubuntu-22.04 - tools: - python: "3.9" - -# Build documentation in the docs/ directory with Sphinx -sphinx: - configuration: docs/conf.py - -# We recommend specifying your dependencies to enable reproducible builds: -# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -python: - install: - - requirements: docs/requirements.txt From 83b744c86a4bb242029aece99e08cb979dde7b3d Mon Sep 17 00:00:00 2001 From: l27001 <65807480+l27001@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:16:53 +0400 Subject: [PATCH 1445/1808] Translate get_user_chat_boosts in sync_version.po --- docs/source/locales/ru/LC_MESSAGES/sync_version.po | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/sync_version.po b/docs/source/locales/ru/LC_MESSAGES/sync_version.po index e304f04ad..b6e677773 100644 --- a/docs/source/locales/ru/LC_MESSAGES/sync_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/sync_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-05 22:16+0400\n" +"POT-Creation-Date: 2024-01-06 13:16+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -3149,16 +3149,16 @@ msgid "" "Requires administrator rights in the chat. Returns a UserChatBoosts " "object." msgstr "" -"Используйте этот метод, чтобы получить текущие права администратора для " -"бота по умолчанию. Возвращает объект ChatAdministratorRights в случае " -"успеха." +"Используйте этот метод, чтобы получить список бустов, полученных от " +"пользователя. Бот должен быть администратором чата. Возвращает объект " +"UserChatBoosts в случае успеха." #: of telebot.TeleBot.get_user_chat_boosts:3 #, fuzzy msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getuserchatboosts" -msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchat" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getuserchatboosts" #: of telebot.TeleBot.get_user_chat_boosts:5 #: telebot.TeleBot.send_chat_action:10 telebot.TeleBot.send_contact:5 @@ -3170,12 +3170,12 @@ msgstr "Уникальный id чата или username канала" #: of telebot.TeleBot.get_user_chat_boosts:11 #, fuzzy msgid "On success, a UserChatBoosts object is returned." -msgstr "В случае успеха возвращается объект StickerSet." +msgstr "В случае успеха возвращается объект UserChatBoosts." #: of telebot.TeleBot.get_user_chat_boosts:12 #, fuzzy msgid ":class:`telebot.types.UserChatBoosts`" -msgstr "Экземпляр класса :class:`telebot.types.Message`" +msgstr "Экземпляр класса :class:`telebot.types.UserChatBoosts`" #: of telebot.TeleBot.get_user_profile_photos:1 msgid "" From aa6695e3c0baa09b276077c60e9d9d4b265ffe2c Mon Sep 17 00:00:00 2001 From: l27001 <65807480+l27001@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:19:05 +0400 Subject: [PATCH 1446/1808] Translate get_user_chat_boosts in async_version.po --- .../source/locales/ru/LC_MESSAGES/async_version.po | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/locales/ru/LC_MESSAGES/async_version.po b/docs/source/locales/ru/LC_MESSAGES/async_version.po index 570c22026..e4547a9be 100644 --- a/docs/source/locales/ru/LC_MESSAGES/async_version.po +++ b/docs/source/locales/ru/LC_MESSAGES/async_version.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: pyTelegramBotAPI Documentation \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-01-05 22:16+0400\n" +"POT-Creation-Date: 2024-01-06 13:19+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -3328,16 +3328,16 @@ msgid "" "Requires administrator rights in the chat. Returns a UserChatBoosts " "object." msgstr "" -"Используйте этот метод, чтобы получить текущие права администратора для " -"бота по умолчанию. Возвращает объект ChatAdministratorRights в случае " -"успеха." +"Используйте этот метод, чтобы получить список бустов, полученных от " +"пользователя. Бот должен быть администратором чата. Возвращает объект " +"UserChatBoosts в случае успеха." #: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:3 #, fuzzy msgid "" "Telegram documentation: " "https://core.telegram.org/bots/api#getuserchatboosts" -msgstr "Документация Telegram: https://core.telegram.org/bots/api#getchat" +msgstr "Документация Telegram: https://core.telegram.org/bots/api#getuserchatboosts" #: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:5 #: telebot.async_telebot.AsyncTeleBot.send_chat_action:10 @@ -3351,12 +3351,12 @@ msgstr "Уникальный id чата или username канала" #: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:11 #, fuzzy msgid "On success, a UserChatBoosts object is returned." -msgstr "В случае успеха, возвращается объект StickerSet." +msgstr "В случае успеха, возвращается объект UserChatBoosts." #: of telebot.async_telebot.AsyncTeleBot.get_user_chat_boosts:12 #, fuzzy msgid ":class:`telebot.types.UserChatBoosts`" -msgstr "Экземпляр класса :class:`telebot.types.Message`" +msgstr "Экземпляр класса :class:`telebot.types.UserChatBoosts`" #: of telebot.async_telebot.AsyncTeleBot.get_user_profile_photos:1 msgid "" From 3b17169579e57f204e3636158c1575ffd4158f58 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 8 Jan 2024 18:56:23 +0300 Subject: [PATCH 1447/1808] Bump version to 4.15.0 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 321ff4966..c36bb58d7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.14.1' +release = '4.15.0' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index d06b2f7a7..cd7b2ebfa 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.14.1' +__version__ = '4.15.0' From 956c2697a201e0ef03b16bc064a2367b73b2785f Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 10 Jan 2024 17:59:06 +0400 Subject: [PATCH 1448/1808] Update types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index c98af1fad..f3737d381 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3814,7 +3814,7 @@ def __init__( self.message_text: str = message_text self.parse_mode: str = parse_mode self.entities: List[MessageEntity] = entities - link_preview_options: LinkPreviewOptions = link_preview_options + self.link_preview_options: LinkPreviewOptions = link_preview_options if disable_web_page_preview is not None: logger.warning('The parameter "disable_web_page_preview" is deprecated. Use "link_preview_options" instead.') From e5f5bb3b2acca4e8e7e657d9a31ac6465c9d0485 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 11 Jan 2024 00:33:37 +0300 Subject: [PATCH 1449/1808] Fix disable_web_page_preview in LinkPreviewOptions --- telebot/__init__.py | 16 ++++++---------- telebot/async_telebot.py | 4 ++-- telebot/types.py | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fb49abdc0..405c718b4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1657,12 +1657,10 @@ def send_message( logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") else: # create a LinkPreviewOptions object - link_preview_options = types.LinkPreviewOptions( - disable_web_page_preview=disable_web_page_preview - ) + link_preview_options = types.LinkPreviewOptions(is_disabled=disable_web_page_preview) - if link_preview_options and (link_preview_options.disable_web_page_preview is None): - link_preview_options.disable_web_page_preview = self.disable_web_page_preview + if link_preview_options and (link_preview_options.is_disabled is None): + link_preview_options.is_disabled = self.disable_web_page_preview return types.Message.de_json( apihelper.send_message( @@ -4433,12 +4431,10 @@ def edit_message_text( logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") else: # create a LinkPreviewOptions object - link_preview_options = types.LinkPreviewOptions( - disable_web_page_preview=disable_web_page_preview - ) + link_preview_options = types.LinkPreviewOptions(is_disabled=disable_web_page_preview) - if link_preview_options and (link_preview_options.disable_web_page_preview is None): - link_preview_options.disable_web_page_preview = self.disable_web_page_preview + if link_preview_options and (link_preview_options.is_disabled is None): + link_preview_options.is_disabled = self.disable_web_page_preview result = apihelper.edit_message_text( self.token, text, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3e8b305a3..efdcd0cae 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2716,7 +2716,7 @@ async def send_message( # create a LinkPreviewOptions object link_preview_options = types.LinkPreviewOptions( - disable_web_page_preview=disable_web_page_preview + is_disabled=disable_web_page_preview ) return types.Message.de_json( @@ -5294,7 +5294,7 @@ async def edit_message_text( # create a LinkPreviewOptions object link_preview_options = types.LinkPreviewOptions( - disable_web_page_preview=disable_web_page_preview + is_disabled=disable_web_page_preview ) result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, diff --git a/telebot/types.py b/telebot/types.py index f3737d381..5584d13f5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3821,7 +3821,7 @@ def __init__( if link_preview_options: logger.warning('Both "link_preview_options" and "disable_web_page_preview" parameters are set: conflicting, "disable_web_page_preview" is deprecated') else: - self.link_preview_options: LinkPreviewOptions = LinkPreviewOptions(disable_web_page_preview) + self.link_preview_options: LinkPreviewOptions = LinkPreviewOptions(is_disabled=disable_web_page_preview) def to_dict(self): json_dict = {'message_text': self.message_text} From be2c1e498721420f1b7d20f69b73d517d89d6934 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 12 Jan 2024 20:51:23 +0400 Subject: [PATCH 1450/1808] Changes in deprecated parameters for async --- telebot/async_telebot.py | 581 ++++++++++++++++++++++++++------------- 1 file changed, 386 insertions(+), 195 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index efdcd0cae..38f1ef1c8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -111,7 +111,7 @@ class AsyncTeleBot: :param protect_content: Default value for protect_content, defaults to None :type protect_content: :obj:`bool`, optional - :param allow_sending_without_reply: Default value for allow_sending_without_reply, defaults to None + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. Default value for allow_sending_without_reply, defaults to None :type allow_sending_without_reply: :obj:`bool`, optional :param colorful_logs: Outputs colorful logs @@ -2661,7 +2661,7 @@ async def send_message( :param entities: List of special entities that appear in message text, which can be specified instead of parse_mode :type entities: Array of :class:`telebot.types.MessageEntity` - :param disable_web_page_preview: Disables link previews for links in this message + :param disable_web_page_preview: Deprecated - Use link_preview_options instead. :type disable_web_page_preview: :obj:`bool` :param disable_notification: Sends the message silently. Users will receive a notification with no sound. @@ -2670,10 +2670,10 @@ async def send_message( :param protect_content: If True, the message content will be hidden for all users except for the target user :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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. @@ -2699,25 +2699,43 @@ async def send_message( disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) - if disable_web_page_preview: + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply + + if disable_web_page_preview is not None: # show a deprecation warning logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") + + if link_preview_options: + # show a conflict warning + logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") + else: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + is_disabled=disable_web_page_preview + ) - # create a LinkPreviewOptions object - link_preview_options = types.LinkPreviewOptions( - is_disabled=disable_web_page_preview - ) + if link_preview_options and (link_preview_options.is_disabled is None): + link_preview_options.is_disabled = self.disable_web_page_preview return types.Message.de_json( await asyncio_helper.send_message( @@ -2810,10 +2828,10 @@ async def copy_message( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 @@ -2836,17 +2854,26 @@ async def copy_message( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, @@ -2995,7 +3022,7 @@ async def send_dice( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions @@ -3006,7 +3033,7 @@ async def send_dice( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 protect_content: Protects the contents of the sent message from forwarding @@ -3023,17 +3050,27 @@ async def send_dice( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_dice( @@ -3082,10 +3119,10 @@ async def send_photo( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 @@ -3111,17 +3148,27 @@ async def send_photo( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_photo( @@ -3174,7 +3221,7 @@ async def send_audio( :param title: Track name :type title: :obj:`str` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: @@ -3199,7 +3246,7 @@ async def send_audio( :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 protect_content: Protects the contents of the sent message from forwarding and saving @@ -3220,21 +3267,31 @@ async def send_audio( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if thumb is not None and thumbnail is None: thumbnail = thumb logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_audio( @@ -3275,7 +3332,7 @@ async def send_voice( :param duration: Duration of the voice message in seconds :type duration: :obj:`int` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions @@ -3295,7 +3352,7 @@ async def send_voice( :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 protect_content: Protects the contents of the sent message from forwarding and saving @@ -3312,17 +3369,27 @@ async def send_voice( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_voice( @@ -3360,7 +3427,7 @@ async def send_document( String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data :type document: :obj:`str` or :class:`telebot.types.InputFile` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param caption: Document caption (may also be used when resending documents by file_id), 0-1024 characters after entities parsing @@ -3386,7 +3453,7 @@ async def send_document( :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 visible_file_name: allows to define file name that will be visible in the Telegram instead of original file name @@ -3416,7 +3483,7 @@ async def send_document( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(document): # function typo miss compatibility @@ -3427,15 +3494,25 @@ async def send_document( thumbnail = thumb logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_data( @@ -3471,7 +3548,7 @@ async def send_sticker( as a String for Telegram to get a .webp file from the Internet, or upload a new one using multipart/form-data. :type sticker: :obj:`str` or :class:`telebot.types.InputFile` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard @@ -3485,7 +3562,7 @@ async def send_sticker( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 protect_content: Protects the contents of the sent message from forwarding and saving @@ -3508,22 +3585,32 @@ async def send_sticker( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + if data and not(sticker): # function typo miss compatibility logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_data( @@ -3595,10 +3682,10 @@ async def send_video( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 @@ -3630,17 +3717,27 @@ async def send_video( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if data and not(video): # function typo miss compatibility @@ -3713,7 +3810,7 @@ async def send_animation( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard @@ -3730,7 +3827,7 @@ async def send_animation( :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 message_thread_id: Identifier of a message thread, in which the video will be sent @@ -3751,17 +3848,27 @@ async def send_animation( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if thumb is not None and thumbnail is None: thumbnail = thumb @@ -3806,7 +3913,7 @@ async def send_video_note( :param length: Video width and height, i.e. diameter of the video message :type length: :obj:`int` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard @@ -3826,7 +3933,7 @@ async def send_video_note( so you can pass “attach://” if the thumbnail was uploaded using multipart/form-data under . :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 protect_content: Protects the contents of the sent message from forwarding and saving @@ -3846,17 +3953,27 @@ async def send_video_note( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if thumb is not None and thumbnail is None: thumbnail = thumb @@ -3897,13 +4014,13 @@ async def send_media_group( :param protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 message_thread_id: Identifier of a message thread, in which the messages will be sent @@ -3917,17 +4034,27 @@ async def send_media_group( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters) @@ -3965,7 +4092,7 @@ async def send_location( :param live_period: Period in seconds for which the location will be updated (see Live Locations, should be between 60 and 86400. :type live_period: :obj:`int` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard @@ -3988,7 +4115,7 @@ async def send_location( :param proximity_alert_radius: 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 allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 protect_content: Protects the contents of the sent message from forwarding and saving @@ -4005,17 +4132,27 @@ async def send_location( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_location( @@ -4162,7 +4299,7 @@ async def send_venue( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, @@ -4173,7 +4310,7 @@ async def send_venue( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. Pass True, if the message should be sent even if one of the specified replied-to messages is not found. :type allow_sending_without_reply: :obj:`bool` @@ -4197,17 +4334,27 @@ async def send_venue( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_venue( @@ -4251,7 +4398,7 @@ async def send_contact( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, @@ -4262,7 +4409,7 @@ async def send_contact( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. Pass True, if the message should be sent even if one of the specified replied-to messages is not found. :type allow_sending_without_reply: :obj:`bool` @@ -4280,17 +4427,27 @@ async def send_contact( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.Message.de_json( await asyncio_helper.send_contact( @@ -5273,7 +5430,7 @@ async def edit_message_text( :param entities: List of special entities that appear in the message text, which can be specified instead of parse_mode :type entities: List of :obj:`telebot.types.MessageEntity` - :param disable_web_page_preview: Disables link previews for links in this message + :param disable_web_page_preview: Deprecated - Use link_preview_options instead. :type disable_web_page_preview: :obj:`bool` :param reply_markup: A JSON-serialized object for an inline keyboard. @@ -5288,14 +5445,18 @@ async def edit_message_text( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview - if disable_web_page_preview: - # show a deprecation warning - logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") + if disable_web_page_preview is not None: + if link_preview_options: + # show a conflict warning + logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") + else: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions( + is_disabled=disable_web_page_preview + ) - # create a LinkPreviewOptions object - link_preview_options = types.LinkPreviewOptions( - is_disabled=disable_web_page_preview - ) + if link_preview_options and (link_preview_options.is_disabled is None): + link_preview_options.is_disabled = self.disable_web_page_preview result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, entities, reply_markup, link_preview_options) @@ -5392,7 +5553,7 @@ async def send_game( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :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. @@ -5401,7 +5562,7 @@ async def send_game( :param timeout: Timeout in seconds for waiting for a response from the bot. :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if one of the specified replied-to messages is not found. + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. Pass True, if the message should be sent even if one of the specified replied-to messages is not found. :type allow_sending_without_reply: :obj:`bool` :param protect_content: Pass True, if content of the message needs to be protected from being viewed by the bot. @@ -5418,17 +5579,27 @@ async def send_game( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, @@ -5602,7 +5773,7 @@ async def send_invoice( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :param reply_markup: A JSON-serialized object for an inline keyboard. If empty, @@ -5616,7 +5787,7 @@ async def send_invoice( :param timeout: Timeout of a request, defaults to None :type timeout: :obj:`int` - :param allow_sending_without_reply: Pass True, if the message should be sent even if the specified replied-to message is not found + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency @@ -5641,17 +5812,27 @@ async def send_invoice( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = await asyncio_helper.send_invoice( self.token, chat_id, title, description, invoice_payload, provider_token, @@ -5833,10 +6014,10 @@ async def send_poll( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` - :param allow_sending_without_reply: Pass True, if the poll allows multiple options to be voted simultaneously. + :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. Pass True, if the poll allows multiple options to be voted simultaneously. :type allow_sending_without_reply: :obj:`bool` :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, @@ -5864,18 +6045,28 @@ async def send_poll( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - allow_sending_without_reply = self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + explanation_parse_mode = self.parse_mode if (explanation_parse_mode is None) else explanation_parse_mode - if allow_sending_without_reply or reply_to_message_id: + if allow_sending_without_reply is not None: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + + if reply_to_message_id: # show a deprecation warning - logger.warning("The parameters 'allow_sending_without_reply' and 'reply_to_message_id' are deprecated. Use 'reply_parameters' instead.") + logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") + + if reply_parameters: + # show a conflict warning + logger.warning("Both 'reply_parameters' and 'reply_to_message_id' parameters are set: conflicting, 'reply_to_message_id' is deprecated") + else: + # create a ReplyParameters object + reply_parameters = types.ReplyParameters( + reply_to_message_id, + allow_sending_without_reply=self.allow_sending_without_reply if (allow_sending_without_reply is None) else allow_sending_without_reply + ) - # create a ReplyParameters object - reply_parameters = types.ReplyParameters( - allow_sending_without_reply=allow_sending_without_reply, - message_id=reply_to_message_id - ) + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply if isinstance(question, types.Poll): raise RuntimeError("The send_poll signature was changed, please see send_poll function details.") From a8252d7d9d9fda39cfda55d3a3140534f22ab81a Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 12 Jan 2024 21:12:54 +0400 Subject: [PATCH 1451/1808] Added mcite and hcite support to formatting --- telebot/formatting.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/telebot/formatting.py b/telebot/formatting.py index 32a09fe2d..c0f442013 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -324,3 +324,37 @@ def hide_link(url: str) -> str: :rtype: :obj:`str` """ return f'' + + +def mcite(content: str, escape: Optional[bool]=True) -> str: + """ + Returns a Markdown-formatted block-quotation string. + + :param content: The string to bold. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` + """ + content = escape_markdown(content) if escape else content + content = '\n'.join(['>' + line for line in content.split('\n')]) + return content + + +def hcite(content: str, escape: Optional[bool]=True) -> str: + """ + Returns a html-formatted block-quotation string. + + :param content: The string to bold. + :type content: :obj:`str` + + :param escape: True if you need to escape special characters. Defaults to True. + :type escape: :obj:`bool` + + :return: The formatted string. + :rtype: :obj:`str` + """ + return '
{}
'.format(escape_html(content) if escape else content) From eb48a5a153a7262840d1b24f35ef8da7e6464023 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 12 Jan 2024 21:23:35 +0400 Subject: [PATCH 1452/1808] Add support for blockquote parsing for html_text for messages --- telebot/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 5584d13f5..91f48f0ea 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1416,7 +1416,8 @@ def __html_text(self, text, entities): "strikethrough": "{text}", "underline": "{text}", "spoiler": "{text}", - "custom_emoji": "{text}" + "custom_emoji": "{text}", + "blockquote": "
{text}
", } if hasattr(self, "custom_subs"): From 36cfe9060af8aaebbfeb26e630fa72551efab147 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 12 Jan 2024 21:40:41 +0400 Subject: [PATCH 1453/1808] Revert parameter sequences for sync --- telebot/__init__.py | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 405c718b4..466f939ce 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1939,10 +1939,10 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], def send_dice( self, chat_id: Union[int, str], emoji: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -2118,14 +2118,14 @@ def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, performer: Optional[str]=None, title: Optional[str]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, @@ -2240,13 +2240,13 @@ def send_audio( def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], caption: Optional[str]=None, duration: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -2337,15 +2337,15 @@ def send_voice( def send_document( self, chat_id: Union[int, str], document: Union[Any, str], + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility caption: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility visible_file_name: Optional[str]=None, disable_content_type_detection: Optional[bool]=None, data: Optional[Union[Any, str]]=None, @@ -2460,11 +2460,11 @@ def send_document( def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content:Optional[bool]=None, data: Union[Any, str]=None, message_thread_id: Optional[int]=None, @@ -2818,12 +2818,12 @@ def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], duration: Optional[int]=None, length: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, @@ -2926,8 +2926,8 @@ def send_media_group( disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> List[types.Message]: """ @@ -2996,15 +2996,15 @@ def send_media_group( def send_location( self, chat_id: Union[int, str], latitude: float, longitude: float, - live_period: Optional[int]=None, + live_period: Optional[int]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, horizontal_accuracy: Optional[float]=None, heading: Optional[int]=None, - proximity_alert_radius: Optional[int]=None, + proximity_alert_radius: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -3198,9 +3198,9 @@ def send_venue( foursquare_type: Optional[str]=None, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility google_place_id: Optional[str]=None, google_place_type: Optional[str]=None, protect_content: Optional[bool]=None, @@ -3304,9 +3304,9 @@ def send_contact( vcard: Optional[str]=None, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: """ @@ -4522,9 +4522,9 @@ def send_game( self, chat_id: Union[int, str], game_short_name: str, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: @@ -4690,10 +4690,10 @@ def send_invoice( is_flexible: Optional[bool]=None, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, provider_data: Optional[str]=None, timeout: Optional[int]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None, @@ -4962,8 +4962,8 @@ def send_poll( is_closed: Optional[bool]=None, disable_notification: Optional[bool]=False, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None, From ed66dba4d54140a5e7f8d3c01152afd69d69fb71 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 14 Jan 2024 13:17:19 +0400 Subject: [PATCH 1454/1808] Fixing docstrings (#2126) --- telebot/__init__.py | 23 +++++++++++++++++------ telebot/async_telebot.py | 24 +++++++++++++++++------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 466f939ce..b7df22505 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1368,7 +1368,9 @@ def close(self) -> bool: def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reaction: Optional[List[types.ReactionType]]=None, is_big: Optional[bool]=None) -> bool: """ - Use this method to set a reaction to a message in a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + Use this method to change the chosen reactions on a message. + Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same + available reactions as messages in the channel. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setmessagereaction @@ -1841,9 +1843,7 @@ def delete_message(self, chat_id: Union[int, str], message_id: int, def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ - Use this method to delete multiple messages in a chat. - The number of messages to be deleted must not exceed 100. - If the chat is a private chat, the user must be an administrator of the chat for this to work and must have the appropriate admin rights. + Use this method to delete multiple messages simultaneously. If some of the specified messages can't be found, they are skipped. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemessages @@ -1863,7 +1863,11 @@ def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: """ - Use this method to forward messages of any kind. + Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. + Service messages and messages with protected content can't be forwarded. Album grouping is kept for forwarded messages. + On success, an array of MessageId of the sent messages is returned. + + Telegram documentation: https://core.telegram.org/bots/api#forwardmessages :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -1901,7 +1905,14 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: """ - Use this method to copy messages of any kind. + Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. + Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. + A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. + The method is analogous to the method forwardMessages, but the copied messages don't have a link to the original message. + Album grouping is kept for copied messages. On success, an array of MessageId of the sent messages is returned. + + Telegram documentation: https://core.telegram.org/bots/api#copymessages + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 38f1ef1c8..dd61a9ad6 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2422,7 +2422,9 @@ async def get_webhook_info(self, timeout: Optional[int]=None) -> types.WebhookIn async def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reaction: Optional[List[types.ReactionType]]=None, is_big: Optional[bool]=None) -> bool: """ - Use this method to set a reaction to a message in a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. + Use this method to change the chosen reactions on a message. + Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same + available reactions as messages in the channel. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setmessagereaction @@ -2911,10 +2913,8 @@ async def delete_message(self, chat_id: Union[int, str], message_id: int, async def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ - Use this method to delete multiple messages in a chat. - The number of messages to be deleted must not exceed 100. - If the chat is a private chat, the user must be an administrator of the chat for this to work and must have the appropriate admin rights. - Returns True on success. + Use this method to delete multiple messages simultaneously. + If some of the specified messages can't be found, they are skipped. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemessages @@ -2932,7 +2932,11 @@ async def delete_messages(self, chat_id: Union[int, str], message_ids: List[int] async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: """ - Use this method to forward messages of any kind. + Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, + they are skipped. Service messages and messages with protected content can't be forwarded. + Album grouping is kept for forwarded messages. On success, an array of MessageId of the sent messages is returned. + + Telegram documentation: https://core.telegram.org/bots/api#forwardmessages :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2965,7 +2969,13 @@ async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: """ - Use this method to copy messages of any kind. + Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. + Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied + only if the value of the field correct_option_id is known to the bot. The method is analogous to the method forwardMessages, but + the copied messages don't have a link to the original message. Album grouping is kept for copied messages. + On success, an array of MessageId of the sent messages is returned. + + Telegram documentation: https://core.telegram.org/bots/api#copymessages :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` From f7403f3672144714fdb9cdc49ef1f7d99d55fbfe Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 14 Jan 2024 17:43:28 +0400 Subject: [PATCH 1455/1808] Update apihelper.py --- telebot/apihelper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d307e9bae..b8f4e7855 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1897,7 +1897,7 @@ def delete_messages(token, chat_id, message_ids): method_url = 'deleteMessages' payload = { 'chat_id': chat_id, - 'message_ids': message_ids + 'message_ids': json.dumps(message_ids) } return _make_request(token, method_url, params=payload) @@ -1907,7 +1907,7 @@ def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notifica payload = { 'chat_id': chat_id, 'from_chat_id': from_chat_id, - 'message_ids': message_ids, + 'message_ids': json.dumps(message_ids), } if disable_notification is not None: payload['disable_notification'] = disable_notification @@ -1923,7 +1923,7 @@ def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notificatio payload = { 'chat_id': chat_id, 'from_chat_id': from_chat_id, - 'message_ids': message_ids, + 'message_ids': json.dumps(message_ids), } if disable_notification is not None: payload['disable_notification'] = disable_notification From 4eecc5ea2f1c76213721199d39677043e10f942f Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 14 Jan 2024 17:45:11 +0400 Subject: [PATCH 1456/1808] Update asyncio_helper.py --- telebot/asyncio_helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 872fb591d..c7d4df835 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1879,7 +1879,7 @@ async def delete_messages(token, chat_id, message_ids): method_url = 'deleteMessages' payload = { 'chat_id': chat_id, - 'message_ids': message_ids + 'message_ids': json.dumps(message_ids) } return await _process_request(token, method_url, params=payload) @@ -1889,7 +1889,7 @@ async def forward_messages(token, chat_id, from_chat_id, message_ids, disable_no payload = { 'chat_id': chat_id, 'from_chat_id': from_chat_id, - 'message_ids': message_ids, + 'message_ids': json.dumps(message_ids), } if disable_notification is not None: payload['disable_notification'] = disable_notification @@ -1907,7 +1907,7 @@ async def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notif payload = { 'chat_id': chat_id, 'from_chat_id': from_chat_id, - 'message_ids': message_ids, + 'message_ids': json.dumps(message_ids), } if disable_notification is not None: payload['disable_notification'] = disable_notification From eaa64bb6c09bd084fb5b5ba9eadc652017f531cc Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 14 Jan 2024 16:51:01 +0300 Subject: [PATCH 1457/1808] Bugfix: fix link_preview_options passing for Telebot --- telebot/apihelper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d307e9bae..7c22f4742 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -244,7 +244,7 @@ def send_message( method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: - payload['link_preview'] = link_preview_options.to_json() + payload['link_preview_options'] = link_preview_options.to_json() if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if parse_mode: @@ -1304,7 +1304,7 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) if link_preview_options is not None: - payload['link_preview'] = link_preview_options.to_json() + payload['link_preview_options'] = link_preview_options.to_json() return _make_request(token, method_url, params=payload, method='post') From 38568bde31af5fb203f7ed9c525449d1c7b67a48 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 14 Jan 2024 18:18:29 +0300 Subject: [PATCH 1458/1808] Bump version to 4.15.1 (bugfix) --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index c36bb58d7..663b23c53 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.15.0' +release = '4.15.1' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index cd7b2ebfa..0282ef2a7 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.15.0' +__version__ = '4.15.1' From 34cf5494232533f8b1f816fcbbfe782b6205da29 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 14 Jan 2024 20:41:07 +0300 Subject: [PATCH 1459/1808] Deprecation: stubs for Message fields in InaccessibleMessage --- telebot/types.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 91f48f0ea..ca4c3c028 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1451,13 +1451,13 @@ def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): html_text += func(utf16_text[offset * 2 : entity.offset * 2]) offset = entity.offset - new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user, entity.custom_emoji_id) + new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], subst_type=entity.type, url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) start_index = len(html_text) html_text += new_string offset += entity.length end_index = len(html_text) elif entity.offset == offset: - new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user, entity.custom_emoji_id) + new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], subst_type=entity.type, url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) start_index = len(html_text) html_text += new_string end_index = len(html_text) @@ -1468,7 +1468,8 @@ def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, # And we don't change it). entity_string = html_text[start_index : end_index].encode("utf-16-le") - formatted_string = func(entity_string, entity.type, entity.url, entity.user, entity.custom_emoji_id).replace("&", "&").replace("<", "<").replace(">",">") + formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id).\ + replace("&", "&").replace("<", "<").replace(">",">") html_text = html_text[:start_index] + formatted_string + html_text[end_index:] end_index = len(html_text) @@ -9214,3 +9215,28 @@ def __init__(self, chat, message_id, date, **kwargs): self.chat = chat self.message_id = message_id self.date = date + + @staticmethod + def __universal_deprecation(property_name): + logger.warning(f'Deprecation warning: the filed "{property_name}" is not accessible for InaccessibleMessage. You should check if your object is Message instance before access.') + return None + + def __getattr__(self, item): + if item in [ + 'message_thread_id', 'from_user', 'sender_chat', 'forward_origin', 'is_topic_message', + 'is_automatic_forward', 'reply_to_message', 'external_reply', 'qoute', 'via_bot', 'edit_date', + 'has_protected_content', 'media_group_id', 'author_signature', 'text', 'entities', 'link_preview_options', + 'animation', 'audio', 'document', 'photo', 'sticker', 'story', 'video', 'video_note', 'voice', 'caption', + 'caption_entities', 'has_media_spoiler', 'contact', 'dice', 'game', 'poll', 'venue', 'location', + 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', + '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', + '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' + ]: + return self.__universal_deprecation(item) + else: + raise AttributeError(f'"{self.__class__.__name__}" object has no attribute "{item}"') From a28f9503cf2707f7a03800e932212ac2b6357bed Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 15 Jan 2024 09:34:17 +0300 Subject: [PATCH 1460/1808] Bump version to 4.15.2 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 663b23c53..18ad5d829 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.15.1' +release = '4.15.2' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 0282ef2a7..4757b8468 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.15.1' +__version__ = '4.15.2' From e8b2118bc1944c88bf16065e9a0b599842a2ce55 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Jan 2024 12:06:20 +0300 Subject: [PATCH 1461/1808] Fix ChatBoostSource issues 1. "source" is temporary made optional 2. source types are parsed correctly --- telebot/types.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ca4c3c028..a34fb855f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9000,9 +9000,12 @@ def __init__(self, chat, boost_id, remove_date, source, **kwargs): self.source = source -class ChatBoostSource(JsonDeserializable): +class ChatBoostSource(ABC, JsonDeserializable): """ - This object describes the source of a chat boost. + This object describes the source of a chat boost. It can be one of + ChatBoostSourcePremium + ChatBoostSourceGiftCode + ChatBoostSourceGiveaway Telegram documentation: https://core.telegram.org/bots/api#chatboostsource @@ -9010,7 +9013,7 @@ class ChatBoostSource(JsonDeserializable): :type source: :obj:`str` :return: Instance of the class - :rtype: :class:`ChatBoostSource` + :rtype: :class:`ChatBoostSourcePremium` or :class:`ChatBoostSourceGiftCode` or :class:`ChatBoostSourceGiveaway` """ @classmethod @@ -9018,10 +9021,13 @@ 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, source, **kwargs): - self.source = source + if obj["type"] == "premium": + return ChatBoostSourcePremium.de_json(obj) + elif obj["type"] == "gift_code": + return ChatBoostSourceGiftCode.de_json(obj) + elif obj["type"] == "giveaway": + return ChatBoostSourceGiveaway.de_json(obj) + return None # noinspection PyUnresolvedReferences @@ -9137,10 +9143,10 @@ class ChatBoost(JsonDeserializable): :param expiration_date: Point in time (Unix timestamp) when the boost will automatically expire, unless the booster's Telegram Premium subscription is prolonged :type expiration_date: :obj:`int` - :param source: Source of the added boost + :param source: Optional. Source of the added boost (made Optional for now due to API error) :type source: :class:`ChatBoostSource` - :return: Instance of the class + :return: Instance of the class (made Optional for now due to API error) :rtype: :class:`ChatBoost` """ @@ -9149,7 +9155,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['source'] = ChatBoostSource.de_json(obj['source']) + source = obj.get('source', None) + obj['source'] = ChatBoostSource.de_json(source) if source else None return cls(**obj) def __init__(self, boost_id, add_date, expiration_date, source, **kwargs): From 5844b2eff35908e3dbeaec1f1fd10094030a1d97 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Jan 2024 12:08:19 +0300 Subject: [PATCH 1462/1808] Description fix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index a34fb855f..d35676893 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9146,7 +9146,7 @@ class ChatBoost(JsonDeserializable): :param source: Optional. Source of the added boost (made Optional for now due to API error) :type source: :class:`ChatBoostSource` - :return: Instance of the class (made Optional for now due to API error) + :return: Instance of the class :rtype: :class:`ChatBoost` """ From 9d37128bcd0f5d8bbd2288db28b5f8b3a212eb02 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 26 Jan 2024 13:10:28 +0300 Subject: [PATCH 1463/1808] Bump version to 4.15.3 --- docs/source/conf.py | 2 +- telebot/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 18ad5d829..ee8ed1f30 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.15.2' +release = '4.15.3' # -- General configuration --------------------------------------------------- diff --git a/telebot/version.py b/telebot/version.py index 4757b8468..89fb4bff9 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.15.2' +__version__ = '4.15.3' From 6f43238be65a89d21a99ff457229d9048c5bc253 Mon Sep 17 00:00:00 2001 From: Evgeniy Khlusov Date: Tue, 30 Jan 2024 15:30:00 +0300 Subject: [PATCH 1464/1808] Fix preview link options if link_preview_options not provided. Get param from class --- telebot/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index b7df22505..6e1e5040a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1664,6 +1664,11 @@ def send_message( if link_preview_options and (link_preview_options.is_disabled is None): link_preview_options.is_disabled = self.disable_web_page_preview + # Fix preview link options if link_preview_options not provided. Get param from class + if not link_preview_options and self.disable_web_page_preview: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions(is_disabled=self.disable_web_page_preview) + return types.Message.de_json( apihelper.send_message( self.token, chat_id, text, From 9014839bcb62f767e915f2ee27acaaeea3e29d48 Mon Sep 17 00:00:00 2001 From: ekhlusov Date: Tue, 30 Jan 2024 18:20:51 +0300 Subject: [PATCH 1465/1808] Fix preview link options if link_preview_options not provided in async telebot --- telebot/__init__.py | 5 +++++ telebot/async_telebot.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6e1e5040a..f977139cc 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4452,6 +4452,11 @@ def edit_message_text( if link_preview_options and (link_preview_options.is_disabled is None): link_preview_options.is_disabled = self.disable_web_page_preview + # Fix preview link options if link_preview_options not provided. Get param from class + if not link_preview_options and self.disable_web_page_preview: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions(is_disabled=self.disable_web_page_preview) + result = apihelper.edit_message_text( self.token, text, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, parse_mode=parse_mode, entities=entities, reply_markup=reply_markup, link_preview_options=link_preview_options) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index dd61a9ad6..2adf4275c 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2739,6 +2739,11 @@ async def send_message( if link_preview_options and (link_preview_options.is_disabled is None): link_preview_options.is_disabled = self.disable_web_page_preview + # Fix preview link options if link_preview_options not provided. Get param from class + if not link_preview_options and self.disable_web_page_preview: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions(is_disabled=self.disable_web_page_preview) + return types.Message.de_json( await asyncio_helper.send_message( self.token, chat_id, text, @@ -5468,6 +5473,11 @@ async def edit_message_text( if link_preview_options and (link_preview_options.is_disabled is None): link_preview_options.is_disabled = self.disable_web_page_preview + # Fix preview link options if link_preview_options not provided. Get param from class + if not link_preview_options and self.disable_web_page_preview: + # create a LinkPreviewOptions object + link_preview_options = types.LinkPreviewOptions(is_disabled=self.disable_web_page_preview) + result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, entities, reply_markup, link_preview_options) if type(result) == bool: # if edit inline message return is bool not Message. From 4b1422f79c39e7284e38bec8367679e13823047b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 30 Jan 2024 19:50:19 +0300 Subject: [PATCH 1466/1808] Bug in ChatBoostSource parse --- docs/source/conf.py | 2 +- telebot/types.py | 6 +++--- telebot/version.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ee8ed1f30..b4369c1c2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.15.3' +release = '4.15.4' # -- General configuration --------------------------------------------------- diff --git a/telebot/types.py b/telebot/types.py index d35676893..dce297af7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9021,11 +9021,11 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if obj["type"] == "premium": + if obj["source"] == "premium": return ChatBoostSourcePremium.de_json(obj) - elif obj["type"] == "gift_code": + elif obj["source"] == "gift_code": return ChatBoostSourceGiftCode.de_json(obj) - elif obj["type"] == "giveaway": + elif obj["source"] == "giveaway": return ChatBoostSourceGiveaway.de_json(obj) return None diff --git a/telebot/version.py b/telebot/version.py index 89fb4bff9..1e29eda03 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.15.3' +__version__ = '4.15.4' From 64e503c4e705fc2246389e1486d00f8c808ccf71 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 3 Feb 2024 20:30:34 +0300 Subject: [PATCH 1467/1808] Fix deprecated parameters 1. Fix descriptions 2. Fix bot.reply_to function 3. Fix ChatPermissions.de_json --- telebot/__init__.py | 51 +++++++++++++++++++++------------------ telebot/apihelper.py | 2 -- telebot/async_telebot.py | 44 ++++++++++++++++++--------------- telebot/asyncio_helper.py | 2 -- telebot/types.py | 21 +++++++++------- 5 files changed, 65 insertions(+), 55 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f977139cc..0c7ef6e22 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3547,48 +3547,43 @@ def restrict_chat_member( they are considered to be restricted forever :type until_date: :obj:`int` or :obj:`datetime` - :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues + :param can_send_messages: deprecated :type can_send_messages: :obj:`bool` - :param can_send_media_messages: Pass True, if the user can send audios, documents, photos, videos, video notes - and voice notes, implies can_send_messages + :param can_send_media_messages: deprecated :type can_send_media_messages: :obj:`bool` - :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages + :param can_send_polls: deprecated :type can_send_polls: :obj:`bool` - :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages + :param can_send_other_messages: deprecated :type can_send_other_messages: :obj:`bool` - :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, - implies can_send_media_messages + :param can_add_web_page_previews: deprecated :type can_add_web_page_previews: :obj:`bool` - :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. - Ignored in public supergroups + :param can_change_info: deprecated :type can_change_info: :obj:`bool` - :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, - implies can_invite_users + :param can_invite_users: deprecated :type can_invite_users: :obj:`bool` - :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups + :param can_pin_messages: deprecated :type can_pin_messages: :obj:`bool` - :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, - the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, - can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and - can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. - :type use_independent_chat_permissions: :obj:`bool` + :param use_independent_chat_permissions: Optional Pass True if chat permissions are set independently. + Otherwise, the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, + can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and can_send_voice_notes + permissions; the can_send_polls permission will imply the can_send_messages permission. - :param permissions: Pass ChatPermissions object to set all permissions at once. Use this param instead of - passing all boolean parameters. + :param permissions: ChatPermissions object defining permissions. :type permissions: :class:`telebot.types.ChatPermissions` :return: True on success :rtype: :obj:`bool` """ if permissions is None: + logger.warning('The parameters "can_..." are deprecated, use "permissions" instead.') permissions = types.ChatPermissions( can_send_messages=can_send_messages, can_send_media_messages=can_send_media_messages, @@ -3599,7 +3594,6 @@ def restrict_chat_member( can_invite_users=can_invite_users, can_pin_messages=can_pin_messages ) - logger.warning('The parameters "can_..." are deprecated, use "permissions" instead.') return apihelper.restrict_chat_member( self.token, chat_id, user_id, permissions, until_date=until_date, @@ -5242,8 +5236,8 @@ def edit_message_caption( def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: """ - Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` - + Convenience function for `send_message(message.chat.id, text, reply_parameters=(message.message_id...), **kwargs)` + :param message: Instance of :class:`telebot.types.Message` :type message: :obj:`types.Message` @@ -5255,7 +5249,18 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ - return self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) + if "reply_parameters" in kwargs: + reply_parameters = kwargs["reply_parameters"] + else: + reply_parameters = None + if not reply_parameters: + reply_parameters = types.ReplyParameters(message.message_id) + + if "allow_sending_without_reply" in kwargs: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + reply_parameters.allow_sending_without_reply = kwargs.pop("allow_sending_without_reply") + + return self.send_message(message.chat.id, text, reply_parameters=reply_parameters, **kwargs) def answer_inline_query( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5a2376dfe..599874820 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1473,11 +1473,9 @@ def send_invoice( :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider :param send_email_to_provider: Pass True, if user's email address should be sent to provider :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: 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 :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 allow_sending_without_reply: :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. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2adf4275c..d783bfc0a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4608,39 +4608,34 @@ async def restrict_chat_member( they are considered to be restricted forever :type until_date: :obj:`int` or :obj:`datetime` - :param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues + :param can_send_messages: deprecated :type can_send_messages: :obj:`bool` - :param can_send_media_messages: Pass True, if the user can send audios, documents, photos, videos, video notes - and voice notes, implies can_send_messages + :param can_send_media_messages: deprecated :type can_send_media_messages: :obj:`bool` - :param can_send_polls: Pass True, if the user is allowed to send polls, implies can_send_messages + :param can_send_polls: deprecated :type can_send_polls: :obj:`bool` - :param can_send_other_messages: Pass True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages + :param can_send_other_messages: deprecated :type can_send_other_messages: :obj:`bool` - :param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages, - implies can_send_media_messages + :param can_add_web_page_previews: deprecated :type can_add_web_page_previews: :obj:`bool` - :param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. - Ignored in public supergroups + :param can_change_info: deprecated :type can_change_info: :obj:`bool` - :param can_invite_users: Pass True, if the user is allowed to invite new users to the chat, - implies can_invite_users + :param can_invite_users: deprecated :type can_invite_users: :obj:`bool` - :param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups + :param can_pin_messages: deprecated :type can_pin_messages: :obj:`bool` - :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, - the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, - can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and - can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. - :type use_independent_chat_permissions: :obj:`bool` + :param use_independent_chat_permissions: Optional Pass True if chat permissions are set independently. + Otherwise, the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, + can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and can_send_voice_notes + permissions; the can_send_polls permission will imply the can_send_messages permission. :param permissions: Pass ChatPermissions object to set all permissions at once. Use this parameter instead of passing all boolean parameters to avoid backward compatibility problems in future. @@ -6224,7 +6219,7 @@ async def edit_message_caption( async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: """ - Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)` + Convenience function for `send_message(message.chat.id, text, reply_parameters=(message.message_id...), **kwargs)` :param message: Instance of :class:`telebot.types.Message` :type message: :obj:`types.Message` @@ -6237,7 +6232,18 @@ async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.M :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ - return await self.send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs) + if "reply_parameters" in kwargs: + reply_parameters = kwargs["reply_parameters"] + else: + reply_parameters = None + if not reply_parameters: + reply_parameters = types.ReplyParameters(message.message_id) + + if "allow_sending_without_reply" in kwargs: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") + reply_parameters.allow_sending_without_reply = kwargs.pop("allow_sending_without_reply") + + return await self.send_message(message.chat.id, text, reply_parameters=reply_parameters, **kwargs) async def answer_inline_query( self, inline_query_id: str, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index c7d4df835..2b6bc2ce3 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1463,11 +1463,9 @@ async def send_invoice( :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider :param send_email_to_provider: Pass True, if user's email address should be sent to provider :param disable_notification: Sends the message silently. Users will receive a notification with no sound. - :param reply_to_message_id: If the message is a reply, ID of the original message :param reply_markup: 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 :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 allow_sending_without_reply: :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. diff --git a/telebot/types.py b/telebot/types.py index dce297af7..d8521411b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2226,9 +2226,9 @@ class ForceReply(JsonSerializable): 1-64 characters :type input_field_placeholder: :obj:`str` - :param selective: Optional. Use this parameter if you want to force reply from specific users only. Targets: 1) users - that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), - sender of the original message. + :param selective: Optional. Use this parameter if you want to force reply from specific users only. Targets: 1) users + that are @mentioned in the text of the Message object; 2) if the bot's message is a reply to a message in the same + chat and forum topic, sender of the original message. :type selective: :obj:`bool` :return: Instance of the class @@ -2343,10 +2343,11 @@ class ReplyKeyboardMarkup(JsonSerializable): active; 1-64 characters :type input_field_placeholder: :obj:`str` - :param selective: Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) - users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has - reply_to_message_id), sender of the original message.Example: A user requests to change the bot's language, bot - replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard. + :param selective: Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: + 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply to a message + in the same chat and forum topic, sender of the original message. Example: A user requests to change the bot's + language, bot replies to the request with a keyboard to select the new language. Other users in the group don't + see the keyboard. :type selective: :obj:`bool` :param is_persistent: Optional. Use this parameter if you want to show the keyboard to specific users only. @@ -3424,7 +3425,7 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): def de_json(cls, json_string): if json_string is None: return json_string obj = cls.check_json(json_string, dict_copy=False) - return cls(**obj) + return cls(**obj, de_json = True) def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send_audios=None, can_send_documents=None, can_send_photos=None, @@ -3448,7 +3449,9 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send self.can_send_video_notes: bool = can_send_video_notes self.can_send_voice_notes: bool = can_send_voice_notes - if can_send_media_messages is not None: + if kwargs.get("de_json", False) and can_send_media_messages is not None: + # Telegram passes can_send_media_messages in Chat.permissions. Temporary created parameter "de_json" allows avoid + # deprection warning and individual parameters overriding. logger.warning('The parameter "can_send_media_messages" is deprecated. Use individual parameters like "can_send_audios", "can_send_documents" etc.') self.can_send_audios = can_send_media_messages self.can_send_documents = can_send_media_messages From aacfc9e938e585817e505550b7f15d79d3a0155b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 3 Feb 2024 20:36:55 +0300 Subject: [PATCH 1468/1808] Better process kwargs in reply_to --- telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0c7ef6e22..188f2e853 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5249,8 +5249,8 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ - if "reply_parameters" in kwargs: - reply_parameters = kwargs["reply_parameters"] + if kwargs: + reply_parameters = kwargs.pop("reply_parameters", None) else: reply_parameters = None if not reply_parameters: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d783bfc0a..37e07ef22 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6232,8 +6232,8 @@ async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.M :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ - if "reply_parameters" in kwargs: - reply_parameters = kwargs["reply_parameters"] + if kwargs: + reply_parameters = kwargs.pop("reply_parameters", None) else: reply_parameters = None if not reply_parameters: From 19f5c054d5628540623cf89708067a4bc02de742 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 3 Feb 2024 21:58:03 +0300 Subject: [PATCH 1469/1808] Fix allow_sending_without_reply in reply_to --- telebot/__init__.py | 19 +++++++++++-------- telebot/async_telebot.py | 17 ++++++++++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 188f2e853..dbfca5642 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3472,7 +3472,7 @@ def ban_chat_member( less than 30 seconds from the current time they are considered to be banned forever :type until_date: :obj:`int` or :obj:`datetime` - :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. + :param revoke_messages: Pass True to delete all messages from the chat for the user that is being removed. If False, the user will be able to see messages in the group that were sent before the user was removed. Always True for supergroups and channels. :type revoke_messages: :obj:`bool` @@ -3545,7 +3545,7 @@ def restrict_chat_member( :param until_date: Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever - :type until_date: :obj:`int` or :obj:`datetime` + :type until_date: :obj:`int` or :obj:`datetime`, optional :param can_send_messages: deprecated :type can_send_messages: :obj:`bool` @@ -3571,10 +3571,11 @@ def restrict_chat_member( :param can_pin_messages: deprecated :type can_pin_messages: :obj:`bool` - :param use_independent_chat_permissions: Optional Pass True if chat permissions are set independently. + :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. + :type use_independent_chat_permissions: :obj:`bool`, optional :param permissions: ChatPermissions object defining permissions. :type permissions: :class:`telebot.types.ChatPermissions` @@ -5251,14 +5252,16 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message """ if kwargs: reply_parameters = kwargs.pop("reply_parameters", None) + if "allow_sending_without_reply" in kwargs: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") else: reply_parameters = None - if not reply_parameters: - reply_parameters = types.ReplyParameters(message.message_id) - if "allow_sending_without_reply" in kwargs: - logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - reply_parameters.allow_sending_without_reply = kwargs.pop("allow_sending_without_reply") + if not reply_parameters: + reply_parameters = types.ReplyParameters( + message.message_id, + allow_sending_without_reply=kwargs.pop("allow_sending_without_reply", None) if kwargs else None + ) return self.send_message(message.chat.id, text, reply_parameters=reply_parameters, **kwargs) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 37e07ef22..dcf947a52 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4606,7 +4606,7 @@ async def restrict_chat_member( :param until_date: Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever - :type until_date: :obj:`int` or :obj:`datetime` + :type until_date: :obj:`int` or :obj:`datetime`, optional :param can_send_messages: deprecated :type can_send_messages: :obj:`bool` @@ -4632,10 +4632,11 @@ async def restrict_chat_member( :param can_pin_messages: deprecated :type can_pin_messages: :obj:`bool` - :param use_independent_chat_permissions: Optional Pass True if chat permissions are set independently. + :param use_independent_chat_permissions: Pass True if chat permissions are set independently. Otherwise, the can_send_other_messages and can_add_web_page_previews permissions will imply the can_send_messages, can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and can_send_voice_notes permissions; the can_send_polls permission will imply the can_send_messages permission. + :type use_independent_chat_permissions: :obj:`bool`, optional :param permissions: Pass ChatPermissions object to set all permissions at once. Use this parameter instead of passing all boolean parameters to avoid backward compatibility problems in future. @@ -6234,14 +6235,16 @@ async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.M """ if kwargs: reply_parameters = kwargs.pop("reply_parameters", None) + if "allow_sending_without_reply" in kwargs: + logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") else: reply_parameters = None - if not reply_parameters: - reply_parameters = types.ReplyParameters(message.message_id) - if "allow_sending_without_reply" in kwargs: - logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - reply_parameters.allow_sending_without_reply = kwargs.pop("allow_sending_without_reply") + if not reply_parameters: + reply_parameters = types.ReplyParameters( + message.message_id, + allow_sending_without_reply=kwargs.pop("allow_sending_without_reply", None) if kwargs else None + ) return await self.send_message(message.chat.id, text, reply_parameters=reply_parameters, **kwargs) From 08c7ccef7e3403e4be1bab02cc6b694d3c685813 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 4 Feb 2024 15:08:55 +0400 Subject: [PATCH 1470/1808] Create pyproject.toml --- pyproject.toml | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..4b04cc3de --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "pyTelegramBotAPI" +version = "4.15.4" +description = "Python Telegram bot api." +authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] +license = {text = "GPL2"} +readme = "README.md" +requires-python = ">=3.7" +keywords = ["telegram", "bot", "api", "tools"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Environment :: Console", + "License :: OSI Approved :: GNU General Public License v2 (GPLv2)" +] +dependencies = ["requests"] + +[project.urls] +Homepage = "https://github.com/eternnoir/pyTelegramBotAPI" +Documentation = "https://pytba.readthedocs.org" +Repository = "https://github.com/eternnoir/pyTelegramBotAPI" +Issues = "https://github.com/eternnoir/pyTelegramBotAPI/issues" + + +[project.optional-dependencies] +json = ["ujson"] +PIL = ["Pillow"] +redis = ["redis>=3.4.1"] +aioredis = ["aioredis"] +aiohttp = ["aiohttp"] +fastapi = ["fastapi"] +uvicorn = ["uvicorn"] +psutil = ["psutil"] +coloredlogs = ["coloredlogs"] +watchdog = ["watchdog"] + + +[tool.hatch.build.targets.wheel] +package = ["telebot"] From 0d2b886ab70d51ea8eecb8a54443c5eaaa9bacb7 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 4 Feb 2024 15:09:06 +0400 Subject: [PATCH 1471/1808] Delete setup.py --- setup.py | 45 --------------------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 5180f5623..000000000 --- a/setup.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -from setuptools import setup, find_packages -from io import open -import re - -def read(filename): - with open(filename, encoding='utf-8') as file: - return file.read() - -with open('telebot/version.py', 'r', encoding='utf-8') as f: # Credits: LonamiWebs - version = re.search(r"^__version__\s*=\s*'(.*)'.*$", - f.read(), flags=re.MULTILINE).group(1) - -setup(name='pyTelegramBotAPI', - version=version, - description='Python Telegram bot api. ', - long_description=read('README.md'), - long_description_content_type="text/markdown", - author='eternnoir', - author_email='eternnoir@gmail.com', - url='https://github.com/eternnoir/pyTelegramBotAPI', - packages = find_packages(exclude = ['tests', 'examples']), - license='GPL2', - keywords='telegram bot api tools', - install_requires=['requests'], - extras_require={ - 'json': 'ujson', - 'PIL': 'Pillow', - 'redis': 'redis>=3.4.1', - 'aioredis': 'aioredis', - 'aiohttp': 'aiohttp', - 'fastapi': 'fastapi', - 'uvicorn': 'uvicorn', - 'psutil': 'psutil', - 'coloredlogs': 'coloredlogs', - 'watchdog': 'watchdog' - }, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Programming Language :: Python :: 3', - 'Environment :: Console', - 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', - ], - - ) From 0998c36f20183b44ed5edd384bdc104af7b72005 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 4 Feb 2024 15:10:35 +0400 Subject: [PATCH 1472/1808] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4b04cc3de..66eeabca5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,4 +46,4 @@ watchdog = ["watchdog"] [tool.hatch.build.targets.wheel] -package = ["telebot"] +include = ["telebot/*"] From f49b34b54da490e8e478e9399e87bdf1eb50fce7 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 4 Feb 2024 15:17:52 +0400 Subject: [PATCH 1473/1808] Update setup_python.yml --- .github/workflows/setup_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 2a9787e25..d21a08ce7 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -31,5 +31,5 @@ jobs: architecture: x64 - run: | pip3 install -r requirements.txt - python setup.py install + pip3 install . cd tests && py.test From 8771f408dd78bb033d865cf5d74492b66c4ff192 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 4 Feb 2024 15:19:14 +0400 Subject: [PATCH 1474/1808] Update aiohttp to support python 3.12 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fdbff1c63..049b84f84 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest requests==2.31.0 wheel==0.38.1 -aiohttp>=3.8.0,<3.9.0 \ No newline at end of file +aiohttp==3.9.0 From d8b4dcce9ae3c291e9f02cd6d69defa60fab977d Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 4 Feb 2024 15:19:40 +0400 Subject: [PATCH 1475/1808] add tests for python 3.12 --- .github/workflows/setup_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index d21a08ce7..5518ff2f3 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8', '3.9', '3.10', '3.11', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] + python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 From 018cc660f18b09e3f497c3f3c1a3026cac260d8a Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 8 Feb 2024 18:17:26 +0300 Subject: [PATCH 1476/1808] One more fix for BoostSource --- telebot/types.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index d8521411b..c92775890 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9055,11 +9055,11 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - user = User.de_json(obj['user']) - return cls(user) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) - def __init__(self, user): - super().__init__('premium') + def __init__(self, source, user, **kwargs): + self.source = source self.user = user @@ -9085,11 +9085,11 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - user = User.de_json(obj['user']) - return cls(user) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) - def __init__(self, user): - super().__init__('gift_code') + def __init__(self, source, user, **kwargs): + self.source = source self.user = user @@ -9121,11 +9121,11 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - user = User.de_json(obj['user']) if 'user' in obj else None - return cls(obj['giveaway_message_id'], user, obj.get('is_unclaimed')) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) - def __init__(self, giveaway_message_id, user=None, is_unclaimed=None): - super().__init__('giveaway') + def __init__(self, source, giveaway_message_id, user=None, is_unclaimed=None, **kwargs): + self.source = source self.giveaway_message_id = giveaway_message_id self.user = user self.is_unclaimed = is_unclaimed From cf323fc5441c3645470a28c3bc59142aba31436d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 8 Feb 2024 23:46:41 +0300 Subject: [PATCH 1477/1808] ChatBoost protect from errors --- telebot/types.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index c92775890..326c172f9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9158,6 +9158,18 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + if not 'boost_id' in obj: + # Suppose that the field "boost_id" is not always provided by Telegram + logger.warning('The field "boost_id" is not found in received ChatBoost.') + obj['boost_id'] = None + if not 'add_date' in obj: + # Suppose that the field "boost_id" is not always provided by Telegram + logger.warning('The field "add_date" is not found in received ChatBoost.') + obj['add_date'] = None + if not 'expiration_date' in obj: + # Suppose that the field "boost_id" is not always provided by Telegram + logger.warning('The field "expiration_date" is not found in received ChatBoost.') + obj['expiration_date'] = None source = obj.get('source', None) obj['source'] = ChatBoostSource.de_json(source) if source else None return cls(**obj) @@ -9191,7 +9203,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, boosts, **kwargs): - self.boosts: ChatBoost = boosts + self.boosts: List[ChatBoost] = boosts class InaccessibleMessage(JsonDeserializable): From bd315963e1b3c5ff8a1ea3ce5c60e52e46baaf69 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 13 Feb 2024 22:25:50 +0400 Subject: [PATCH 1478/1808] Update types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 326c172f9..9d065086e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -204,7 +204,7 @@ def de_json(cls, json_string): message_reaction = MessageReactionUpdated.de_json(obj.get('message_reaction')) message_reaction_count = MessageReactionCountUpdated.de_json(obj.get('message_reaction_count')) removed_chat_boost = ChatBoostRemoved.de_json(obj.get('removed_chat_boost')) - chat_boost = ChatBoost.de_json(obj.get('chat_boost')) + chat_boost = ChatBoostUpdated.de_json(obj.get('chat_boost')) 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) From b9291cbe885690b19d5efcdd1357304a64399798 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 13 Feb 2024 22:38:46 +0400 Subject: [PATCH 1479/1808] Added typehints for recent bot api 7.0 --- telebot/types.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 9d065086e..2a86de5b8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8961,8 +8961,8 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, chat, boost, **kwargs): - self.chat = chat - self.boost = boost + self.chat: Chat = chat + self.boost: ChatBoost = boost class ChatBoostRemoved(JsonDeserializable): @@ -8997,10 +8997,10 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, chat, boost_id, remove_date, source, **kwargs): - self.chat = chat - self.boost_id = boost_id - self.remove_date = remove_date - self.source = source + self.chat: Chat = chat + self.boost_id: str = boost_id + self.remove_date: int = remove_date + self.source: ChatBoostSource = source class ChatBoostSource(ABC, JsonDeserializable): @@ -9059,8 +9059,8 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, source, user, **kwargs): - self.source = source - self.user = user + self.source: str = source + self.user: User = user # noinspection PyUnresolvedReferences @@ -9089,8 +9089,8 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, source, user, **kwargs): - self.source = source - self.user = user + self.source: str = source + self.user: User = user # noinspection PyUnresolvedReferences @@ -9125,10 +9125,10 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, source, giveaway_message_id, user=None, is_unclaimed=None, **kwargs): - self.source = source - self.giveaway_message_id = giveaway_message_id - self.user = user - self.is_unclaimed = is_unclaimed + self.source: str = source + self.giveaway_message_id: int = giveaway_message_id + self.user: User = user + self.is_unclaimed: bool = is_unclaimed class ChatBoost(JsonDeserializable): @@ -9175,9 +9175,9 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, boost_id, add_date, expiration_date, source, **kwargs): - self.boost_id = boost_id - self.add_date = add_date - self.expiration_date = expiration_date + self.boost_id: str = boost_id + self.add_date: int = add_date + self.expiration_date: int = expiration_date self.source: ChatBoostSource = source @@ -9234,9 +9234,9 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, chat, message_id, date, **kwargs): - self.chat = chat - self.message_id = message_id - self.date = date + self.chat: Chat = chat + self.message_id: int = message_id + self.date: int = date @staticmethod def __universal_deprecation(property_name): From 89bfd038991dca778707e6e7388b89998e86fdf3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 16 Feb 2024 16:43:11 +0300 Subject: [PATCH 1480/1808] Bump version to 4.15.5 --- 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 b4369c1c2..540a0ccf2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.15.4' +release = '4.15.5' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 66eeabca5..b6f8625e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.15.4" +version = "4.15.5" 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 1e29eda03..5edc33436 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.15.4' +__version__ = '4.15.5' From 4c4c198fc3985a038b9136dd94df12bf6a7ac1ed Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 16 Feb 2024 17:19:57 +0300 Subject: [PATCH 1481/1808] Fix python versions 3.8-3.12 --- .travis.yml | 1 + README.md | 2 +- pyproject.toml | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8e6b02e5c..1fe9399d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - "3.9" - "3.10" - "3.11" + - "3.12" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/README.md b/README.md index 289e38d7a..bcc07fa47 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ ## Getting started -This API is tested with Python 3.8-3.11 and Pypy 3. +This API is tested with Python 3.8-3.12 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager): diff --git a/pyproject.toml b/pyproject.toml index b6f8625e3..729592cff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,12 +9,11 @@ description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.8" keywords = ["telegram", "bot", "api", "tools"] classifiers = [ "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", From 0e125ab942e6a746549540d97dd0a49b8b1e78fb Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 16 Feb 2024 21:59:51 +0400 Subject: [PATCH 1482/1808] Added the class ChatBoostAdded and the field boost_added to the class Message for service messages about a user boosting a chat. --- telebot/types.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 2a86de5b8..8f467fdde 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1012,6 +1012,9 @@ class Message(JsonDeserializable): proximity alert while sharing Live Location. :type proximity_alert_triggered: :class:`telebot.types.ProximityAlertTriggered` + :param boost_added: Optional. Service message: user boosted the chat + :type boost_added: :class:`telebot.types.ChatBoostAdded` + :param forum_topic_created: Optional. Service message: forum topic created :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` @@ -1275,6 +1278,10 @@ def de_json(cls, json_string): content_type = 'giveaway_completed' if 'forward_origin' in obj: opts['forward_origin'] = MessageOrigin.de_json(obj['forward_origin']) + if 'boost_added' in obj: + opts['boost_added'] = ChatBoostAdded.de_json(obj['boost_added']) + content_type = 'boost_added' + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1375,6 +1382,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.giveaway_winners: Optional[GiveawayWinners] = None self.giveaway_completed: Optional[GiveawayCompleted] = None self.forward_origin: Optional[MessageOrigin] = None + self.boost_added: Optional[ChatBoostAdded] = None for key in options: setattr(self, key, options[key]) @@ -9262,3 +9270,27 @@ def __getattr__(self, item): return self.__universal_deprecation(item) else: raise AttributeError(f'"{self.__class__.__name__}" object has no attribute "{item}"') + + +class ChatBoostAdded(JsonDeserializable): + """ + This object represents a service message about a user boosting a chat. + + Telegram documentation: https://core.telegram.org/bots/api#chatboostadded + + :param boost_count: Number of boosts added by the user + :type boost_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`ChatBoostAdded` + """ + + @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, boost_count, **kwargs): + self.boost_count: int = boost_count From f5477d76b78c0daa0b43c9f9773f74d087308a50 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 16 Feb 2024 22:02:21 +0400 Subject: [PATCH 1483/1808] Added the field sender_boost_count to the class Message. --- telebot/types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 8f467fdde..08a9760db 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -805,6 +805,9 @@ class Message(JsonDeserializable): fake sender user in non-channel chats, if the message was sent on behalf of a chat. :type sender_chat: :class:`telebot.types.Chat` + :param sender_boost_count: Optional. If the sender of the message boosted the chat, the number of boosts added by the user + :type sender_boost_count: :obj:`int` + :param date: Date the message was sent in Unix time :type date: :obj:`int` @@ -1281,6 +1284,8 @@ def de_json(cls, json_string): if 'boost_added' in obj: opts['boost_added'] = ChatBoostAdded.de_json(obj['boost_added']) content_type = 'boost_added' + if 'sender_boost_count' in obj: + opts['sender_boost_count'] = obj['sender_boost_count'] return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1383,6 +1388,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.giveaway_completed: Optional[GiveawayCompleted] = None self.forward_origin: Optional[MessageOrigin] = None self.boost_added: Optional[ChatBoostAdded] = None + self.sender_boost_count: Optional[int] = None for key in options: setattr(self, key, options[key]) From 86285a67fbbb0d5e9f031e27d763709866fda731 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 16 Feb 2024 22:04:17 +0400 Subject: [PATCH 1484/1808] Added the field reply_to_story to the class Message. --- telebot/types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 08a9760db..68a11a17c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -853,6 +853,9 @@ class Message(JsonDeserializable): :param quote: Optional. For replies that quote part of the original message, the quoted part of the message :type quote: :class:`telebot.types.TextQuote` + :param reply_to_story: Optional. For replies to a story, the original story + :type reply_to_story: :class:`telebot.types.Story` + :param via_bot: Optional. Bot through which the message was sent :type via_bot: :class:`telebot.types.User` @@ -1286,6 +1289,8 @@ def de_json(cls, json_string): content_type = 'boost_added' if 'sender_boost_count' in obj: opts['sender_boost_count'] = obj['sender_boost_count'] + if 'reply_to_story' in obj: + opts['reply_to_story'] = Story.de_json(obj['reply_to_story']) return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1389,6 +1394,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.forward_origin: Optional[MessageOrigin] = None self.boost_added: Optional[ChatBoostAdded] = None self.sender_boost_count: Optional[int] = None + self.reply_to_story: Optional[Story] = None for key in options: setattr(self, key, options[key]) From 1eceda12b1271f89bff783e776eb7069eb601bd1 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 16 Feb 2024 22:06:27 +0400 Subject: [PATCH 1485/1808] Added the fields chat and id to the class Story. --- telebot/types.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 68a11a17c..8b9dfc2bb 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8063,8 +8063,18 @@ def to_json(self) -> str: class Story(JsonDeserializable): """ - This object represents a message about a forwarded story in the chat. - Currently holds no information. + This object represents a story. + + Telegram documentation: https://core.telegram.org/bots/api#story + + :param chat: Chat that posted the story + :type chat: :class:`telebot.types.Chat` + + :param id: Unique identifier for the story in the chat + :type id: :obj:`int` + + :return: Instance of the class + :rtype: :class:`Story` """ @classmethod @@ -8072,10 +8082,12 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) return cls(**obj) - - def __init__(self, **kwargs) -> None: - pass + + def __init__(self, chat: Chat, id: int, **kwargs) -> None: + self.chat: Chat = chat + self.id: int = id # base class From 8386a596b7db4e112df761746caacb0d4cabf738 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 16 Feb 2024 22:12:47 +0400 Subject: [PATCH 1486/1808] Added the field unrestrict_boost_count to the class Chat. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 8b9dfc2bb..79e875fc4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -625,6 +625,10 @@ class Chat(JsonDeserializable): 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. + :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. :type message_auto_delete_time: :obj:`int` @@ -691,7 +695,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, is_forum=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, **kwargs): + profile_background_custom_emoji_id=None, has_visible_history=None, + unrestrict_boost_count=None,**kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -727,6 +732,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.profile_accent_color_id: int = profile_accent_color_id self.profile_background_custom_emoji_id: str = profile_background_custom_emoji_id self.has_visible_history: bool = has_visible_history + self.unrestrict_boost_count: int = unrestrict_boost_count From 9c5e3d6e205a2097bff0900b14c4eeefa025205a Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 16 Feb 2024 22:14:20 +0400 Subject: [PATCH 1487/1808] Added the field custom_emoji_sticker_set_name to the class Chat. --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 79e875fc4..fe1285bc2 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -656,6 +656,10 @@ class Chat(JsonDeserializable): 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. + Custom emoji from this set can be used by all users and bots in the group. Returned only in getChat. + :param custom_emoji_sticker_set_name: :obj:`str` + :param linked_chat_id: Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a @@ -696,7 +700,7 @@ def __init__(self, id, type, title=None, username=None, first_name=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, - unrestrict_boost_count=None,**kwargs): + unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -733,6 +737,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.profile_background_custom_emoji_id: str = profile_background_custom_emoji_id self.has_visible_history: bool = has_visible_history self.unrestrict_boost_count: int = unrestrict_boost_count + self.custom_emoji_sticker_set_name: str = custom_emoji_sticker_set_name From 9c1b2489df8c895beeb5381fff1f1c5256ffdb80 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 16 Feb 2024 22:20:40 +0400 Subject: [PATCH 1488/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bcc07fa47..470127a49 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.0! +##

Supported Bot API version: 7.1!

Official documentation

Official ru documentation

From 0de74806763e53e6c7d6f215e60ced79e67212c9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Feb 2024 15:43:20 +0300 Subject: [PATCH 1489/1808] Fix ChatBoostSourceGiveaway and bump version --- docs/source/conf.py | 2 +- pyproject.toml | 2 +- telebot/types.py | 2 +- telebot/version.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 540a0ccf2..dd00ce713 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.15.5' +release = '4.16.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 729592cff..7026a1345 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.15.5" +version = "4.16.0" description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} diff --git a/telebot/types.py b/telebot/types.py index fe1285bc2..03516d4b9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9164,7 +9164,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - obj['user'] = User.de_json(obj['user']) + obj['user'] = User.de_json(obj.get('user')) return cls(**obj) def __init__(self, source, giveaway_message_id, user=None, is_unclaimed=None, **kwargs): diff --git a/telebot/version.py b/telebot/version.py index 5edc33436..2e13bf898 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.15.5' +__version__ = '4.16.0' From 3ea992447b4a35560e67b872d1712671ea16999e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Feb 2024 16:20:00 +0300 Subject: [PATCH 1490/1808] Fix GiveawayCreated not implemented --- telebot/types.py | 100 +++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 03516d4b9..3451448d0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -173,7 +173,7 @@ class Update(JsonDeserializable): :type chat_join_request: :class:`telebot.types.ChatJoinRequest` :param chat_boost: Optional. A chat boost was added or changed. The bot must be an administrator in the chat to receive these updates. - :type chat_boost: :class:`telebot.types.ChatBoost` + :type chat_boost: :class:`telebot.types.ChatBoostUpdated` :param removed_chat_boost: Optional. A chat boost was removed. The bot must be an administrator in the chat to receive these updates. :type removed_chat_boost: :class:`telebot.types.RemovedChatBoost` @@ -3454,7 +3454,7 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): """ @classmethod def de_json(cls, json_string): - if json_string is None: return json_string + if json_string is None: return None obj = cls.check_json(json_string, dict_copy=False) return cls(**obj, de_json = True) @@ -7050,7 +7050,7 @@ class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): """ @classmethod def de_json(cls, json_string): - if json_string is None: return json_string + if json_string is None: return None obj = cls.check_json(json_string) obj['location'] = Location.de_json(obj['location']) return cls(**obj) @@ -7967,8 +7967,7 @@ class SwitchInlineQueryChosenChat(JsonDeserializable, Dictionaryable, JsonSerial @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -8015,8 +8014,7 @@ class BotName(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -8090,8 +8088,7 @@ class Story(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) return cls(**obj) @@ -8117,8 +8114,7 @@ class ReactionType(JsonDeserializable, Dictionaryable, JsonSerializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) # remove type if obj['type'] == 'emoji': @@ -8228,8 +8224,7 @@ class MessageReactionUpdated(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) @@ -8277,8 +8272,7 @@ class MessageReactionCountUpdated(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) obj['reactions'] = [ReactionCount.de_json(reaction) for reaction in obj['reactions']] @@ -8309,8 +8303,7 @@ class ReactionCount(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['type'] = ReactionType.de_json(obj['type']) return cls(**obj) @@ -8401,8 +8394,7 @@ class ExternalReplyInfo(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['origin'] = MessageOrigin.de_json(obj['origin']) if 'chat' in obj: @@ -8514,8 +8506,7 @@ class MessageOrigin(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) message_type = obj['type'] if message_type == 'user': @@ -8626,8 +8617,7 @@ class LinkPreviewOptions(JsonDeserializable, Dictionaryable, JsonSerializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -8695,8 +8685,7 @@ class Giveaway(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chats'] = [Chat.de_json(chat) for chat in obj['chats']] return cls(**obj) @@ -8760,8 +8749,7 @@ class GiveawayWinners(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) obj['winners'] = [User.de_json(user) for user in obj['winners']] @@ -8806,8 +8794,7 @@ class GiveawayCompleted(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) if 'giveaway_message' in obj: obj['giveaway_message'] = Message.de_json(obj['giveaway_message']) @@ -8824,8 +8811,17 @@ class GiveawayCreated(JsonDeserializable): """ This object represents a service message about the creation of a scheduled giveaway. Currently holds no information. """ - - + + @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, **kwargs) -> None: + pass + + class TextQuote(JsonDeserializable): """ This object contains information about the quoted part of a message that is replied to by the given message. @@ -8850,8 +8846,7 @@ class TextQuote(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) if 'entities' in obj: obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] @@ -8899,8 +8894,7 @@ class ReplyParameters(JsonDeserializable, Dictionaryable, JsonSerializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) if 'quote_entities' in obj: obj['quote_entities'] = [MessageEntity.de_json(entity) for entity in obj['quote_entities']] @@ -8963,8 +8957,7 @@ class UsersShared(JsonDeserializable): """ @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) @@ -8996,8 +8989,7 @@ class ChatBoostUpdated(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) obj['boost'] = ChatBoost.de_json(obj['boost']) @@ -9032,8 +9024,7 @@ class ChatBoostRemoved(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) obj['source'] = ChatBoostSource.de_json(obj['source']) @@ -9055,17 +9046,13 @@ class ChatBoostSource(ABC, JsonDeserializable): Telegram documentation: https://core.telegram.org/bots/api#chatboostsource - :param source: Source of the boost - :type source: :obj:`str` - :return: Instance of the class :rtype: :class:`ChatBoostSourcePremium` or :class:`ChatBoostSourceGiftCode` or :class:`ChatBoostSourceGiveaway` """ @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) if obj["source"] == "premium": return ChatBoostSourcePremium.de_json(obj) @@ -9095,8 +9082,7 @@ class ChatBoostSourcePremium(ChatBoostSource): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) return cls(**obj) @@ -9125,8 +9111,7 @@ class ChatBoostSourceGiftCode(ChatBoostSource): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) return cls(**obj) @@ -9161,8 +9146,7 @@ class ChatBoostSourceGiveaway(ChatBoostSource): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj.get('user')) return cls(**obj) @@ -9198,8 +9182,7 @@ class ChatBoost(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) if not 'boost_id' in obj: # Suppose that the field "boost_id" is not always provided by Telegram @@ -9239,8 +9222,7 @@ class UserChatBoosts(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['boosts'] = [ChatBoost.de_json(boost) for boost in obj['boosts']] return cls(**obj) @@ -9270,8 +9252,7 @@ class InaccessibleMessage(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) return cls(**obj) @@ -9322,8 +9303,7 @@ class ChatBoostAdded(JsonDeserializable): @classmethod def de_json(cls, json_string): - if json_string is None: - return None + if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) From 93eab1207079bb1a56c1607ea55d05f916b3d3d9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Feb 2024 21:14:28 +0300 Subject: [PATCH 1491/1808] Readme fix + bump version --- README.md | 6 ------ docs/source/conf.py | 2 +- pyproject.toml | 2 +- telebot/version.py | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 470127a49..adee76fc5 100644 --- a/README.md +++ b/README.md @@ -79,12 +79,6 @@ $ pip install pyTelegramBotAPI ``` * Installation from source (requires git): -``` -$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git -$ cd pyTelegramBotAPI -$ python setup.py install -``` -or: ``` $ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git ``` diff --git a/docs/source/conf.py b/docs/source/conf.py index dd00ce713..c9edf448c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.16.0' +release = '4.16.1' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 7026a1345..68f0254ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.16.0" +version = "4.16.1" 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 2e13bf898..0b4b31ee0 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.16.0' +__version__ = '4.16.1' From 224b0db6fe703fc18684f8679df7cfdf0eadde20 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 18 Feb 2024 09:24:22 +0300 Subject: [PATCH 1492/1808] MenuButton set default types --- telebot/types.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 3451448d0..222048cfc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7279,8 +7279,7 @@ class VideoChatParticipantsInvited(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - if 'users' in obj: - obj['users'] = [User.de_json(u) for u in obj['users']] + obj['users'] = [User.de_json(u) for u in obj['users']] return cls(**obj) def __init__(self, users=None, **kwargs): @@ -7366,8 +7365,8 @@ class MenuButtonCommands(MenuButton): :rtype: :class:`telebot.types.MenuButtonCommands` """ - def __init__(self, type, **kwargs): - self.type = type + def __init__(self, type = None, **kwargs): + self.type: str = "commands" def to_dict(self): return {'type': self.type} @@ -7397,7 +7396,7 @@ class MenuButtonWebApp(MenuButton): """ def __init__(self, type, text, web_app, **kwargs): - self.type: str = type + self.type: str = "web_app" self.text: str = text self.web_app: WebAppInfo = web_app @@ -7420,8 +7419,8 @@ class MenuButtonDefault(MenuButton): :return: Instance of the class :rtype: :class:`telebot.types.MenuButtonDefault` """ - def __init__(self, type, **kwargs): - self.type: str = type + def __init__(self, type = None, **kwargs): + self.type: str = "default" def to_dict(self): return {'type': self.type} From 61be27fd3a17555a65a29147b23486303ca4e898 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 24 Feb 2024 11:26:43 +0300 Subject: [PATCH 1493/1808] Allow to pass no message_id to bot.reply_to --- telebot/__init__.py | 3 +++ telebot/async_telebot.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index dbfca5642..54716eac2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5263,6 +5263,9 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message allow_sending_without_reply=kwargs.pop("allow_sending_without_reply", None) if kwargs else None ) + if not reply_parameters.message_id: + reply_parameters.message_id = message.message_id + return self.send_message(message.chat.id, text, reply_parameters=reply_parameters, **kwargs) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index dcf947a52..8ae599e6a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6246,6 +6246,9 @@ async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.M allow_sending_without_reply=kwargs.pop("allow_sending_without_reply", None) if kwargs else None ) + if not reply_parameters.message_id: + reply_parameters.message_id = message.message_id + return await self.send_message(message.chat.id, text, reply_parameters=reply_parameters, **kwargs) async def answer_inline_query( From 62244c68168ea02b4b295ed2c56369da3d27ef85 Mon Sep 17 00:00:00 2001 From: Jasmine Date: Tue, 27 Feb 2024 11:49:57 +0800 Subject: [PATCH 1494/1808] fix(hint): add_sticker_to_set -> sticker --- 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 54716eac2..37cd2e6df 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5710,7 +5710,7 @@ def add_sticker_to_set( tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, mask_position: Optional[types.MaskPosition]=None, - sticker: Optional[List[types.InputSticker]]=None) -> bool: + sticker: Optional[types.InputSticker]=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. The format of the added sticker must match the format of the other stickers in the set. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8ae599e6a..a1e453d6d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6689,7 +6689,7 @@ async def add_sticker_to_set( tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, mask_position: Optional[types.MaskPosition]=None, - sticker: Optional[List[types.InputSticker]]=None) -> bool: + sticker: Optional[types.InputSticker]=None) -> bool: """ Use this method to add a new sticker to a set created by the bot. The format of the added sticker must match the format of the other stickers in the set. From 7cab63984bbb94c08e6f4cba6612826c2ac4230c Mon Sep 17 00:00:00 2001 From: Jasmine Date: Tue, 27 Feb 2024 11:54:33 +0800 Subject: [PATCH 1495/1808] fix(comments) --- telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 37cd2e6df..55f264dfa 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5746,8 +5746,8 @@ def add_sticker_to_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` - :param sticker: A JSON-serialized list of 1-50 initial stickers to be added to the sticker set - :type sticker: :obj:`list` of :class:`telebot.types.InputSticker` + :param sticker: A JSON-serialized object for stickers to be added to the sticker set + :type sticker: :class:`telebot.types.InputSticker` :return: On success, True is returned. :rtype: :obj:`bool` diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a1e453d6d..6a0d5ab90 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6725,8 +6725,8 @@ async def add_sticker_to_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` - :param sticker: A JSON-serialized list of 1-50 initial stickers to be added to the sticker set - :type sticker: :obj:`list` of :class:`telebot.types.InputSticker` + :param sticker: A JSON-serialized object for stickers to be added to the sticker set + :type sticker: :class:`telebot.types.InputSticker` :return: On success, True is returned. :rtype: :obj:`bool` From 112266d8dd916d9ada5965ab89179b685abb485d Mon Sep 17 00:00:00 2001 From: Jasmine Date: Tue, 27 Feb 2024 11:56:47 +0800 Subject: [PATCH 1496/1808] fix --- 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 55f264dfa..39a9c25aa 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5746,7 +5746,7 @@ def add_sticker_to_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` - :param sticker: A JSON-serialized object for stickers to be added to the sticker set + :param sticker: A JSON-serialized object for sticker to be added to the sticker set :type sticker: :class:`telebot.types.InputSticker` :return: On success, True is returned. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6a0d5ab90..fece7a2e7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6725,7 +6725,7 @@ async def add_sticker_to_set( :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :type mask_position: :class:`telebot.types.MaskPosition` - :param sticker: A JSON-serialized object for stickers to be added to the sticker set + :param sticker: A JSON-serialized object for sticker to be added to the sticker set :type sticker: :class:`telebot.types.InputSticker` :return: On success, True is returned. From a5927350bd92f339231e9de7afdef319cca1905f Mon Sep 17 00:00:00 2001 From: Hansana Dasanayaka <75363593+hansanaD@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:58:28 +0530 Subject: [PATCH 1497/1808] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index adee76fc5..ad8d01fae 100644 --- a/README.md +++ b/README.md @@ -889,5 +889,6 @@ Here are some examples of template: * [QR-Code For You Bot](https://t.me/qrcode_for_you_bot) ([source](https://github.com/arashnm80/qrcode-for-you-bot)) by [Arashnm80](https://github.com/arashnm80). Telegram qrcode generator bot created with pyhton and telebot. * [Best Instagram Downloader Bot](https://t.me/Best_Instagram_Downloader_Bot) ([source](https://github.com/arashnm80/best-instagram-downloader)) by [Arashnm80](https://github.com/arashnm80). Free and open source telegram bot to download posts and reels from Instagram. * [Personal bot for ChatGPT & Bard](https://github.com/Simatwa/pyTelegramBotAPI.git) by [Simatwa](https://github.com/Simatwa/telegram-chatbots). Chat with ChatGPT & Bard on the go. +* [4K YouTube Downloader](https://github.com/hansanaD/TelegramYTDLBot/) - Youtube Downloader with upto 4K resolution support. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 0570bf1166b622d7e78fc2c6f5cc8a36d253a3e1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 28 Feb 2024 11:16:14 +0300 Subject: [PATCH 1498/1808] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ad8d01fae..6bda55484 100644 --- a/README.md +++ b/README.md @@ -888,7 +888,6 @@ Here are some examples of template: * [AwesomeChatGPTBot](https://github.com/Kourva/AwesomeChatGPTBot) - Simple ChatGTP-3.5 bot. It is FREE and can remember chat history for a while With pre-defined roles! * [QR-Code For You Bot](https://t.me/qrcode_for_you_bot) ([source](https://github.com/arashnm80/qrcode-for-you-bot)) by [Arashnm80](https://github.com/arashnm80). Telegram qrcode generator bot created with pyhton and telebot. * [Best Instagram Downloader Bot](https://t.me/Best_Instagram_Downloader_Bot) ([source](https://github.com/arashnm80/best-instagram-downloader)) by [Arashnm80](https://github.com/arashnm80). Free and open source telegram bot to download posts and reels from Instagram. -* [Personal bot for ChatGPT & Bard](https://github.com/Simatwa/pyTelegramBotAPI.git) by [Simatwa](https://github.com/Simatwa/telegram-chatbots). Chat with ChatGPT & Bard on the go. * [4K YouTube Downloader](https://github.com/hansanaD/TelegramYTDLBot/) - Youtube Downloader with upto 4K resolution support. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 6391348a2074f5e0503e2d636af83a4fd83423a6 Mon Sep 17 00:00:00 2001 From: Ivan Popravka Date: Sun, 10 Mar 2024 21:47:50 +0300 Subject: [PATCH 1499/1808] Add new bot to the list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6bda55484..aeb4f1686 100644 --- a/README.md +++ b/README.md @@ -889,5 +889,6 @@ Here are some examples of template: * [QR-Code For You Bot](https://t.me/qrcode_for_you_bot) ([source](https://github.com/arashnm80/qrcode-for-you-bot)) by [Arashnm80](https://github.com/arashnm80). Telegram qrcode generator bot created with pyhton and telebot. * [Best Instagram Downloader Bot](https://t.me/Best_Instagram_Downloader_Bot) ([source](https://github.com/arashnm80/best-instagram-downloader)) by [Arashnm80](https://github.com/arashnm80). Free and open source telegram bot to download posts and reels from Instagram. * [4K YouTube Downloader](https://github.com/hansanaD/TelegramYTDLBot/) - Youtube Downloader with upto 4K resolution support. +* [DrinkGenius-Bot](https://t.me/cocktail_recommendation_bot) ([source](https://github.com/Povladarchik/DrinkGenius-Bot)) by [Povladarchik](https://github.com/Povladarchik). Your personal assistant in the world of cocktails. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From 6bb3968376c93666f7b57f206a94135e9a9cde8b Mon Sep 17 00:00:00 2001 From: Suren Khorenyan Date: Mon, 18 Mar 2024 15:04:32 +0300 Subject: [PATCH 1500/1808] Fix TextFilter error: if no message text and no caption -> return False --- telebot/custom_filters.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index e924f134c..d4d4ffeca 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -149,6 +149,8 @@ def check(self, obj: Union[types.Message, types.CallbackQuery, types.InlineQuery text = obj.question elif isinstance(obj, types.Message): text = obj.text or obj.caption + if text is None: + return False elif isinstance(obj, types.CallbackQuery): text = obj.data elif isinstance(obj, types.InlineQuery): From 3a50a2140097df81d8ebc42fca9e39b137407cec Mon Sep 17 00:00:00 2001 From: Jacopo Tediosi Date: Thu, 28 Mar 2024 15:49:38 +0100 Subject: [PATCH 1501/1808] Retry engine 2 backoff fixes Retry engine 2 behaved differently than retry engine 1. In particular, POST requests were not retried, because they were not included in the `allowed_methods` parameter of the urllib3 Retry strategy. Furthermore, requests were retried without delay, ignoring the `RETRY_TIMEOUT` setting, because also the `backoff_` parameters were not implemented in the the urllib3 Retry strategy. This commit aims to fix these problems, implementing the `allowed_methods`, `backoff_factor` and `backoff_max` parameters in the urllib3 Retry strategy. It is important to note that `urllib3.util.Retry` does not allow you to directly set a fixed backoff delay, so I set `backoff_factor=RETRY_TIMEOUT` and `backoff_max=RETRY_TIMEOUT`. In this way, already on the second request, the backoff delay exceeds the `backoff_max` and remains so. For more information, see the [urllib3.util.Retry](https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html) documentation. --- telebot/apihelper.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 599874820..3b1b2a29c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -147,6 +147,9 @@ def _make_request(token, method_name, method='get', params=None, files=None): # noinspection PyUnresolvedReferences retry_strategy = requests.packages.urllib3.util.retry.Retry( total=MAX_RETRIES, + allowed_methods=None, + backoff_factor=RETRY_TIMEOUT, + backoff_max=RETRY_TIMEOUT ) adapter = HTTPAdapter(max_retries=retry_strategy) for prefix in ('http://', 'https://'): From 12e9673adcaa308fc125937e2a5672cbb3243795 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 31 Mar 2024 23:46:54 +0500 Subject: [PATCH 1502/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aeb4f1686..20b062e70 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.1! +##

Supported Bot API version: 7.2!

Official documentation

Official ru documentation

From dcf856652b8b887972b25a9e5f687b8a47d28171 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 00:00:58 +0500 Subject: [PATCH 1503/1808] Added the class BusinessConnection and updates about the connection or disconnection of the bot to a business account, represented by the field business_connection in the class Update. --- telebot/types.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 222048cfc..41043fdf7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -178,6 +178,9 @@ class Update(JsonDeserializable): :param removed_chat_boost: Optional. A chat boost was removed. The bot must be an administrator in the chat to receive these updates. :type removed_chat_boost: :class:`telebot.types.RemovedChatBoost` + :param business_connection: Optional. The bot was connected to or disconnected from a business account, or a user edited an existing connection with the bot + :type business_connection: :class:`telebot.types.BusinessConnection` + :return: Instance of the class :rtype: :class:`telebot.types.Update` @@ -205,13 +208,16 @@ def de_json(cls, json_string): message_reaction_count = MessageReactionCountUpdated.de_json(obj.get('message_reaction_count')) removed_chat_boost = ChatBoostRemoved.de_json(obj.get('removed_chat_boost')) chat_boost = ChatBoostUpdated.de_json(obj.get('chat_boost')) + business_connection = BusinessConnection.de_json(obj.get('business_connection')) 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) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, removed_chat_boost, chat_boost, + business_connection) 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): + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, removed_chat_boost, chat_boost, + business_connection, **kwargs): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -231,6 +237,8 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.message_reaction_count = message_reaction_count self.removed_chat_boost = removed_chat_boost self.chat_boost = chat_boost + self.business_connection = business_connection + class ChatMemberUpdated(JsonDeserializable): @@ -9308,3 +9316,50 @@ def de_json(cls, json_string): def __init__(self, boost_count, **kwargs): self.boost_count: int = boost_count + + + +class BusinessConnection(JsonDeserializable): + """ + This object describes the connection of the bot with a business account. + + Telegram documentation: https://core.telegram.org/bots/api#businessconnection + + :param id: Unique identifier of the business connection + :type id: :obj:`str` + + :param user: Business account user that created the business connection + :type user: :class:`User` + + :param user_chat_id: Identifier of a private chat with the user who created the business connection + :type user_chat_id: :obj:`int` + + :param date: Date the connection was established in Unix time + :type date: :obj:`int` + + :param can_reply: True, if the bot can act on behalf of the business account in chats that were active in the last 24 hours + :type can_reply: :obj:`bool` + + :param is_enabled: True, if the connection is active + :type is_enabled: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`BusinessConnection` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + + def __init__(self, id, user, user_chat_id, date, can_reply, is_enabled, **kwargs): + self.id: str = id + self.user: User = user + self.user_chat_id: int = user_chat_id + self.date: int = date + self.can_reply: bool = can_reply + self.is_enabled: bool = is_enabled + + From f223cfb9b8f6779c10565c043f1f7fabaae2b908 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 00:27:41 +0500 Subject: [PATCH 1504/1808] Added 4 handlers; needs testing --- telebot/__init__.py | 397 +++++++++++++++++++++++++++++++++++++++ telebot/async_telebot.py | 394 ++++++++++++++++++++++++++++++++++++++ telebot/types.py | 53 +++++- 3 files changed, 842 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 39a9c25aa..e3eec483f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -231,6 +231,11 @@ def __init__( self.chat_join_request_handlers = [] self.chat_boost_handlers = [] self.removed_chat_boost_handlers = [] + self.business_connection_handlers = [] + self.business_message_handlers = [] + self.edited_business_message_handlers = [] + self.deleted_business_messages_handlers = [] + self.custom_filters = {} self.state_handlers = [] @@ -694,6 +699,10 @@ def process_new_updates(self, updates: List[types.Update]): new_chat_join_request = None new_chat_boosts = None new_removed_chat_boosts = None + new_business_connections = None + new_business_messages = None + new_edited_business_messages = None + new_deleted_business_messages = None for update in updates: if apihelper.ENABLE_MIDDLEWARE and not self.use_class_middlewares: @@ -763,6 +772,19 @@ def process_new_updates(self, updates: List[types.Update]): if update.removed_chat_boost: if new_removed_chat_boosts is None: new_removed_chat_boosts = [] new_removed_chat_boosts.append(update.removed_chat_boost) + if update.business_connection: + if new_business_connections is None: new_business_connections = [] + new_business_connections.append(update.business_connection) + if update.business_message: + if new_business_messages is None: new_business_messages = [] + new_business_messages.append(update.business_message) + if update.edited_business_message: + if new_edited_business_messages is None: new_edited_business_messages = [] + new_edited_business_messages.append(update.edited_business_message) + if update.deleted_business_messages: + if new_deleted_business_messages is None: new_deleted_business_messages = [] + new_deleted_business_messages.append(update.deleted_business_messages) + if new_messages: self.process_new_messages(new_messages) @@ -800,6 +822,14 @@ def process_new_updates(self, updates: List[types.Update]): self.process_new_chat_boost(new_chat_boosts) if new_removed_chat_boosts: self.process_new_removed_chat_boost(new_removed_chat_boosts) + if new_business_connections: + self.process_new_business_connection(new_business_connections) + if new_business_messages: + self.process_new_business_message(new_business_messages) + if new_edited_business_messages: + self.process_new_edited_business_message(new_edited_business_messages) + if new_deleted_business_messages: + self.process_new_deleted_business_messages(new_deleted_business_messages) def process_new_messages(self, new_messages): """ @@ -912,6 +942,32 @@ def process_new_removed_chat_boost(self, new_removed_chat_boosts): """ self._notify_command_handlers(self.removed_chat_boost_handlers, new_removed_chat_boosts, 'removed_chat_boost') + def process_new_business_connection(self, new_business_connections): + """ + :meta private: + """ + self._notify_command_handlers(self.business_connection_handlers, new_business_connections, 'business_connection') + + def process_new_business_message(self, new_business_messages): + """ + :meta private: + """ + self._notify_command_handlers(self.business_message_handlers, new_business_messages, 'business_message') + + def process_new_edited_business_message(self, new_edited_business_messages): + """ + :meta private: + """ + self._notify_command_handlers(self.edited_business_message_handlers, new_edited_business_messages, 'edited_business_message') + + def process_new_deleted_business_messages(self, new_deleted_business_messages): + """ + :meta private: + """ + self._notify_command_handlers(self.deleted_business_messages_handlers, new_deleted_business_messages, 'deleted_business_messages') + + + def process_middlewares(self, update): """ @@ -7761,6 +7817,347 @@ def register_removed_chat_boost_handler( handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_removed_chat_boost_handler(handler_dict) + def business_connection_handler(self, func=None, **kwargs): + """ + Handles new incoming business connection state. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_business_connection_handler(handler_dict) + return handler + + return decorator + + def add_business_connection_handler(self, handler_dict): + """ + Adds a business_connection handler. + Note that you should use register_business_connection_handler to add business_connection_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.business_connection_handlers.append(handler_dict) + + def register_business_connection_handler( + self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + """ + Registers business connection handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_business_connection_handler(handler_dict) + + def business_message_handler( + self, + commands: Optional[List[str]]=None, + regexp: Optional[str]=None, + func: Optional[Callable]=None, + content_types: Optional[List[str]]=None, + **kwargs): + """ + Handles New incoming message of any kind(for business accounts, see bot api 7.2 for more) - text, photo, sticker, etc. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + All message handlers are tested in the order they were added. + + Example: + + .. code-block:: python3 + :caption: Usage of business_message_handler + + bot = TeleBot('TOKEN') + + # Handles all messages which text matches regexp. + @bot.business_message_handler(regexp='someregexp') + def command_help(message): + bot.send_message(message.chat.id, 'Did someone call for help?') + + # Handle all sent documents of type 'text/plain'. + @bot.business_message_handler(func=lambda message: message.document.mime_type == 'text/plain', + content_types=['document']) + def command_handle_document(message): + bot.send_message(message.chat.id, 'Document received, sir!') + + # Handle all other messages. + @bot.business_message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', + 'text', 'location', 'contact', 'sticker']) + def default_command(message): + bot.send_message(message.chat.id, "This is the default command handler.") + + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Optional lambda function. The lambda receives the message to test as the first parameter. + It must return True if the command should handle the message. + :type func: :obj:`lambda` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: decorated function + """ + if content_types is None: + content_types = ["text"] + + method_name = "business_message_handler" + + if commands is not None: + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + + if regexp is not None: + self.check_regexp_input(regexp, method_name) + + if isinstance(content_types, str): + logger.warning("business_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_message_handler(handler_dict) + return handler + + return decorator + + def add_business_message_handler(self, handler_dict): + """ + Adds a business_message handler. + Note that you should use register_business_message_handler to add business_message_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.business_message_handlers.append(handler_dict) + + def register_business_message_handler(self, + callback: Callable, + commands: Optional[List[str]]=None, + regexp: Optional[str]=None, + func: Optional[Callable]=None, + content_types: Optional[List[str]]=None, + **kwargs): + """ + Registers business connection handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, **kwargs) + self.add_business_message_handler(handler_dict) + + + + + def edited_business_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Handles new version of a message(business accounts) that is known to the bot and was edited. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + if content_types is None: + content_types = ["text"] + + method_name = "edited_business_message_handler" + + if commands is not None: + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + + if regexp is not None: + self.check_regexp_input(regexp, method_name) + + if isinstance(content_types, str): + logger.warning("edited_business_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_message_handler(handler_dict) + return handler + + return decorator + + + def add_edited_business_message_handler(self, handler_dict): + """ + Adds the edit message handler + Note that you should use register_edited_business_message_handler to add edited_business_message_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.edited_message_handlers.append(handler_dict) + + + def register_edited_business_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, + commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, + pass_bot: Optional[bool]=False, **kwargs): + """ + Registers edited message handler for business accounts. + + :param callback: function to be called + :type callback: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param commands: list of commands + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + method_name = "edited_business_message_handler" + + if commands is not None: + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + + if regexp is not None: + self.check_regexp_input(regexp, method_name) + + if isinstance(content_types, str): + logger.warning("edited_business_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + pass_bot=pass_bot, + **kwargs) + self.add_edited_business_message_handler(handler_dict) + + + def deleted_business_messages_handler(self, func=None, **kwargs): + """ + Handles new incoming deleted messages state. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + + self.add_deleted_message_handler(handler_dict) + return handler + + return decorator + + def add_deleted_business_messages_handler(self, handler_dict): + """ + Adds a deleted_business_messages handler. + Note that you should use register_deleted_business_messages_handler to add deleted_business_messages_handler to the bot. + + :meta private: + """ + self.deleted_message_handlers.append(handler_dict) + + def register_deleted_business_messages_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers deleted business messages handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_deleted_business_messages_handler(handler_dict) + + + + def add_custom_filter(self, custom_filter: Union[SimpleCustomFilter, AdvancedCustomFilter]): """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fece7a2e7..88b29de3a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -173,6 +173,11 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.chat_join_request_handlers = [] self.removed_chat_boost_handlers = [] self.chat_boost_handlers = [] + self.business_connection_handlers = [] + self.business_message_handlers = [] + self.edited_business_message_handlers = [] + self.deleted_business_messages_handlers = [] + self.custom_filters = {} self.state_handlers = [] self.middlewares = [] @@ -594,6 +599,12 @@ async def process_new_updates(self, updates: List[types.Update]): chat_join_request = None removed_chat_boost_handlers = None chat_boost_handlers = None + new_business_connections = None + new_business_messages = None + new_edited_business_messages = None + new_deleted_business_messages = None + + for update in updates: logger.debug('Processing updates: {0}'.format(update)) if update.message: @@ -650,6 +661,18 @@ async def process_new_updates(self, updates: List[types.Update]): if update.removed_chat_boost: if removed_chat_boost_handlers is None: removed_chat_boost_handlers = [] removed_chat_boost_handlers.append(update.removed_chat_boost) + if update.business_connection: + if new_business_connections is None: new_business_connections = [] + new_business_connections.append(update.business_connection) + if update.business_message: + if new_business_messages is None: new_business_messages = [] + new_business_messages.append(update.business_message) + if update.edited_business_message: + if new_edited_business_messages is None: new_edited_business_messages = [] + new_edited_business_messages.append(update.edited_business_message) + if update.deleted_business_messages: + if new_deleted_business_messages is None: new_deleted_business_messages = [] + new_deleted_business_messages.append(update.deleted_business_messages) if new_messages: @@ -686,6 +709,14 @@ async def process_new_updates(self, updates: List[types.Update]): await self.process_new_message_reaction_count(new_message_reaction_count_handlers) if chat_boost_handlers: await self.process_new_chat_boost(chat_boost_handlers) + if new_business_connections: + await self.process_new_business_connection(new_business_connections) + if new_business_messages: + await self.process_new_business_message(new_business_messages) + if new_edited_business_messages: + await self.process_new_edited_business_message(new_edited_business_messages) + if new_deleted_business_messages: + await self.process_new_deleted_business_messages(new_deleted_business_messages) async def process_new_messages(self, new_messages): """ @@ -796,6 +827,30 @@ async def process_new_removed_chat_boost(self, removed_chat_boost): """ await self._process_updates(self.removed_chat_boost_handlers, removed_chat_boost, 'removed_chat_boost') + async def process_new_business_connection(self, new_business_connections): + """ + :meta private: + """ + await self._process_updates(self.business_connection_handlers, new_business_connections, 'business_connection') + + async def process_new_business_message(self, new_business_messages): + """ + :meta private: + """ + await self._process_updates(self.business_message_handlers, new_business_messages, 'business_message') + + async def process_new_edited_business_message(self, new_edited_business_messages): + """ + :meta private: + """ + await self._process_updates(self.edited_business_message_handlers, new_edited_business_messages, 'edited_business_message') + + async def process_new_deleted_business_messages(self, new_deleted_business_messages): + """ + :meta private: + """ + await self._process_updates(self.deleted_business_messages_handlers, new_deleted_business_messages, 'deleted_business_messages') + async def _get_middlewares(self, update_type): """ :meta private: @@ -2125,6 +2180,345 @@ def register_removed_chat_boost_handler(self, callback: Callable, func: Optional handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_removed_chat_boost_handler(handler_dict) + + def business_connection_handler(self, func=None, **kwargs): + """ + Handles new incoming business connection state. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_business_connection_handler(handler_dict) + return handler + + return decorator + + def add_business_connection_handler(self, handler_dict): + """ + Adds a business_connection handler. + Note that you should use register_business_connection_handler to add business_connection_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.business_connection_handlers.append(handler_dict) + + def register_business_connection_handler( + self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): + """ + Registers business connection handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_business_connection_handler(handler_dict) + + def business_message_handler( + self, + commands: Optional[List[str]]=None, + regexp: Optional[str]=None, + func: Optional[Callable]=None, + content_types: Optional[List[str]]=None, + **kwargs): + """ + Handles New incoming message of any kind(for business accounts, see bot api 7.2 for more) - text, photo, sticker, etc. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + All message handlers are tested in the order they were added. + + Example: + + .. code-block:: python3 + :caption: Usage of business_message_handler + + bot = TeleBot('TOKEN') + + # Handles all messages which text matches regexp. + @bot.business_message_handler(regexp='someregexp') + def command_help(message): + bot.send_message(message.chat.id, 'Did someone call for help?') + + # Handle all sent documents of type 'text/plain'. + @bot.business_message_handler(func=lambda message: message.document.mime_type == 'text/plain', + content_types=['document']) + def command_handle_document(message): + bot.send_message(message.chat.id, 'Document received, sir!') + + # Handle all other messages. + @bot.business_message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', + 'text', 'location', 'contact', 'sticker']) + def default_command(message): + bot.send_message(message.chat.id, "This is the default command handler.") + + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Optional lambda function. The lambda receives the message to test as the first parameter. + It must return True if the command should handle the message. + :type func: :obj:`lambda` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: decorated function + """ + if content_types is None: + content_types = ["text"] + + method_name = "business_message_handler" + + if commands is not None: + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + + if regexp is not None: + self.check_regexp_input(regexp, method_name) + + if isinstance(content_types, str): + logger.warning("business_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_message_handler(handler_dict) + return handler + + return decorator + + def add_business_message_handler(self, handler_dict): + """ + Adds a business_message handler. + Note that you should use register_business_message_handler to add business_message_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.business_message_handlers.append(handler_dict) + + def register_business_message_handler(self, + callback: Callable, + commands: Optional[List[str]]=None, + regexp: Optional[str]=None, + func: Optional[Callable]=None, + content_types: Optional[List[str]]=None, + **kwargs): + """ + Registers business connection handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, **kwargs) + self.add_business_message_handler(handler_dict) + + + + + def edited_business_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): + """ + Handles new version of a message(business accounts) that is known to the bot and was edited. + As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. + + :param commands: Optional list of strings (commands to handle). + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Optional regular expression. + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + if content_types is None: + content_types = ["text"] + + method_name = "edited_business_message_handler" + + if commands is not None: + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + + if regexp is not None: + self.check_regexp_input(regexp, method_name) + + if isinstance(content_types, str): + logger.warning("edited_business_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + def decorator(handler): + handler_dict = self._build_handler_dict(handler, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + **kwargs) + self.add_edited_message_handler(handler_dict) + return handler + + return decorator + + + def add_edited_business_message_handler(self, handler_dict): + """ + Adds the edit message handler + Note that you should use register_edited_business_message_handler to add edited_business_message_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.edited_message_handlers.append(handler_dict) + + + def register_edited_business_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, + commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, + pass_bot: Optional[bool]=False, **kwargs): + """ + Registers edited message handler for business accounts. + + :param callback: function to be called + :type callback: :obj:`function` + + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` + + :param commands: list of commands + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + method_name = "edited_business_message_handler" + + if commands is not None: + self.check_commands_input(commands, method_name) + if isinstance(commands, str): + commands = [commands] + + if regexp is not None: + self.check_regexp_input(regexp, method_name) + + if isinstance(content_types, str): + logger.warning("edited_business_message_handler: 'content_types' filter should be List of strings (content types), not string.") + content_types = [content_types] + + handler_dict = self._build_handler_dict(callback, + content_types=content_types, + commands=commands, + regexp=regexp, + func=func, + pass_bot=pass_bot, + **kwargs) + self.add_edited_business_message_handler(handler_dict) + + + def deleted_business_messages_handler(self, func=None, **kwargs): + """ + Handles new incoming deleted messages state. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + :return: None + + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + + self.add_deleted_message_handler(handler_dict) + return handler + + return decorator + + def add_deleted_business_messages_handler(self, handler_dict): + """ + Adds a deleted_business_messages handler. + Note that you should use register_deleted_business_messages_handler to add deleted_business_messages_handler to the bot. + + :meta private: + """ + self.deleted_message_handlers.append(handler_dict) + + def register_deleted_business_messages_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers deleted business messages handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_deleted_business_messages_handler(handler_dict) + @staticmethod def _build_handler_dict(handler, pass_bot=False, **filters): """ diff --git a/telebot/types.py b/telebot/types.py index 41043fdf7..fbb857ae0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -181,6 +181,15 @@ class Update(JsonDeserializable): :param business_connection: Optional. The bot was connected to or disconnected from a business account, or a user edited an existing connection with the bot :type business_connection: :class:`telebot.types.BusinessConnection` + :param business_message: Optional. New non-service message from a connected business account + :type business_message: :class:`telebot.types.Message` + + :param edited_business_message: Optional. New version of a non-service message from a connected business account that is known to the bot and was edited + :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` + :return: Instance of the class :rtype: :class:`telebot.types.Update` @@ -209,15 +218,19 @@ def de_json(cls, json_string): removed_chat_boost = ChatBoostRemoved.de_json(obj.get('removed_chat_boost')) chat_boost = ChatBoostUpdated.de_json(obj.get('chat_boost')) business_connection = BusinessConnection.de_json(obj.get('business_connection')) + business_message = Message.de_json(obj.get('business_message')) + edited_business_message = Message.de_json(obj.get('edited_business_message')) + deleted_business_messages = Message.de_json(obj.get('deleted_business_messages')) + 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_connection, business_message, edited_business_message, deleted_business_messages, **obj) 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, **kwargs): + business_connection, business_message, edited_business_message, deleted_business_messages, **kwargs): self.update_id = update_id self.message = message self.edited_message = edited_message @@ -238,6 +251,9 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.removed_chat_boost = removed_chat_boost self.chat_boost = chat_boost self.business_connection = business_connection + self.business_message = business_message + self.edited_business_message = edited_business_message + self.deleted_business_messages = deleted_business_messages @@ -9363,3 +9379,36 @@ def __init__(self, id, user, user_chat_id, date, can_reply, is_enabled, **kwargs self.is_enabled: bool = is_enabled + +class BusinessMessagesDeleted(JsonDeserializable): + """ + This object is received when messages are deleted from a connected business account. + + Telegram documentation: https://core.telegram.org/bots/api#businessmessagesdeleted + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param chat: Information about a chat in the business account. The bot may not have access to the chat or the corresponding user. + :type chat: :class:`Chat` + + :param message_ids: A JSON-serialized list of identifiers of deleted messages in the chat of the business account + :type message_ids: :obj:`list` of :obj:`int` + + :return: Instance of the class + :rtype: :class:`BusinessMessagesDeleted` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + return cls(**obj) + + + def __init__(self, business_connection_id, chat, message_ids, **kwargs): + self.business_connection_id: str = business_connection_id + self.chat: Chat = chat + self.message_ids: List[int] = message_ids + \ No newline at end of file From 2f61473d233560d1024b4d4e968a9d023a4cb84a Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 00:36:21 +0500 Subject: [PATCH 1505/1808] added getbusinessconnection --- telebot/__init__.py | 21 ++++++++++++++++++++- telebot/apihelper.py | 4 ++++ telebot/async_telebot.py | 20 ++++++++++++++++++++ telebot/asyncio_helper.py | 4 ++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index e3eec483f..ced007f93 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4280,6 +4280,25 @@ def get_my_default_administrator_rights(self, for_channels: Optional[bool]=None) apihelper.get_my_default_administrator_rights(self.token, for_channels=for_channels) ) + + def get_business_connection(self, business_connection_id: str) -> types.BusinessConnection: + """ + Use this method to get information about the connection of the bot with a business account. + Returns a BusinessConnection object on success. + + Telegram documentation: https://core.telegram.org/bots/api#getbusinessconnection + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :return: Returns a BusinessConnection object on success. + :rtype: :class:`telebot.types.BusinessConnection` + """ + + return types.BusinessConnection.de_json( + apihelper.get_business_connection(self.token, business_connection_id) + ) + def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, @@ -8155,7 +8174,7 @@ def register_deleted_business_messages_handler(self, callback: Callable, func: O handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_deleted_business_messages_handler(handler_dict) - + diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3b1b2a29c..eb7a30219 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1247,6 +1247,10 @@ def set_my_commands(token, commands, scope=None, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload, method='post') +def get_business_connection(token, business_connection_id): + method_url = 'getBusinessConnection' + payload = {'business_connection_id': business_connection_id} + return _make_request(token, method_url, params=payload , method='post') def delete_my_commands(token, scope=None, language_code=None): method_url = r'deleteMyCommands' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 88b29de3a..37dc712bc 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5647,6 +5647,26 @@ async def get_my_default_administrator_rights(self, for_channels: bool=None) -> """ return types.ChatAdministratorRights.de_json(await asyncio_helper.get_my_default_administrator_rights(self.token, for_channels)) + + async def get_business_connection(self, business_connection_id: str) -> types.BusinessConnection: + """ + Use this method to get information about the connection of the bot with a business account. + Returns a BusinessConnection object on success. + + Telegram documentation: https://core.telegram.org/bots/api#getbusinessconnection + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :return: Returns a BusinessConnection object on success. + :rtype: :class:`telebot.types.BusinessConnection` + """ + result = await asyncio_helper.get_business_connection(self.token, business_connection_id) + + return types.BusinessConnection.de_json( + result + ) + async def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 2b6bc2ce3..ceafe637f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1234,6 +1234,10 @@ async def set_my_commands(token, commands, scope=None, language_code=None): payload['language_code'] = language_code return await _process_request(token, method_url, params=payload, method='post') +async def get_business_connection(token, business_connection_id): + method_url = 'getBusinessConnection' + payload = {'business_connection_id': business_connection_id} + return await _process_request(token, method_url, params=payload , method='post') async def delete_my_commands(token, scope=None, language_code=None): method_url = r'deleteMyCommands' From 91143eb04c4125e9bceee32774d8a90c305e412a Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 01:09:39 +0500 Subject: [PATCH 1506/1808] added business connection ids --- telebot/__init__.py | 129 +++++++++++++++++++++++++++--------- telebot/apihelper.py | 64 +++++++++++++----- telebot/async_telebot.py | 134 ++++++++++++++++++++++++++++---------- telebot/asyncio_helper.py | 61 ++++++++++++----- telebot/types.py | 2 +- 5 files changed, 292 insertions(+), 98 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ced007f93..291b77ead 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1630,7 +1630,8 @@ def send_message( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - link_preview_options : Optional[types.LinkPreviewOptions]=None) -> types.Message: + link_preview_options : Optional[types.LinkPreviewOptions]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send text messages. @@ -1683,6 +1684,9 @@ def send_message( :param link_preview_options: Link preview options. :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1730,7 +1734,7 @@ def send_message( self.token, chat_id, text, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, entities=entities, protect_content=protect_content, message_thread_id=message_thread_id, - reply_parameters=reply_parameters, link_preview_options=link_preview_options)) + reply_parameters=reply_parameters, link_preview_options=link_preview_options, business_connection_id=business_connection_id)) def forward_message( @@ -2017,7 +2021,8 @@ def send_dice( allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -2056,6 +2061,9 @@ def send_dice( :param reply_parameters: Additional parameters for replies to messages :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2084,7 +2092,7 @@ def send_dice( apihelper.send_dice( self.token, chat_id, emoji=emoji, 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, business_connection_id=business_connection_id)) ) @@ -2100,7 +2108,8 @@ def send_photo( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2151,6 +2160,9 @@ def send_photo( :param reply_parameters: Additional parameters for replies to messages :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2182,7 +2194,7 @@ def send_photo( self.token, chat_id, photo, caption=caption, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, - message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters) + message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) ) @@ -2201,7 +2213,8 @@ def send_audio( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + 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 them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2271,6 +2284,9 @@ def send_audio( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2305,7 +2321,7 @@ def send_audio( self.token, chat_id, audio, caption=caption, duration=duration, performer=performer, title=title, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -2321,7 +2337,8 @@ def send_voice( allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + 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). @@ -2374,6 +2391,9 @@ def send_voice( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2403,7 +2423,7 @@ def send_voice( self.token, chat_id, voice, caption=caption, duration=duration, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -2423,7 +2443,8 @@ def send_document( data: Optional[Union[Any, str]]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send general files. @@ -2486,6 +2507,9 @@ def send_document( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2525,7 +2549,7 @@ def send_document( reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption=caption, thumbnail=thumbnail, caption_entities=caption_entities, disable_content_type_detection=disable_content_type_detection, visible_file_name=visible_file_name, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -2541,7 +2565,8 @@ def send_sticker( data: Union[Any, str]=None, message_thread_id: Optional[int]=None, emoji: Optional[str]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2587,6 +2612,9 @@ def send_sticker( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2620,7 +2648,7 @@ def send_sticker( self.token, chat_id, sticker, 'sticker', reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, - reply_parameters=reply_parameters) + reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -2644,7 +2672,8 @@ def send_video( message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2715,6 +2744,9 @@ def send_video( :param reply_parameters: Reply parameters :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2755,7 +2787,7 @@ def send_video( supports_streaming=supports_streaming, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, height=height, width=width, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, - reply_parameters=reply_parameters) + reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -2777,7 +2809,8 @@ def send_animation( message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2847,6 +2880,9 @@ def send_animation( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2882,7 +2918,7 @@ def send_animation( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, width=width, height=height, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - has_spoiler=has_spoiler) + has_spoiler=has_spoiler, business_connection_id=business_connection_id) ) @@ -2899,7 +2935,8 @@ def send_video_note( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -2954,6 +2991,9 @@ def send_video_note( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2986,7 +3026,7 @@ def send_video_note( apihelper.send_video_note( self.token, chat_id, data, duration=duration, length=length, reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -3001,7 +3041,8 @@ def send_media_group( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> List[types.Message]: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -3035,6 +3076,9 @@ def send_media_group( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -3061,7 +3105,8 @@ def send_media_group( result = apihelper.send_media_group( self.token, chat_id, media, disable_notification=disable_notification, timeout=timeout, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, + business_connection_id=business_connection_id) return [types.Message.de_json(msg) for msg in result] @@ -3079,7 +3124,8 @@ def send_location( allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -3132,6 +3178,9 @@ def send_location( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3161,7 +3210,7 @@ def send_location( self.token, chat_id, latitude, longitude, live_period=live_period, reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -3277,7 +3326,8 @@ def send_venue( google_place_type: Optional[str]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -3337,6 +3387,9 @@ def send_venue( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3366,7 +3419,7 @@ def send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id=foursquare_id, foursquare_type=foursquare_type, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, google_place_id=google_place_id, google_place_type=google_place_type, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -3380,7 +3433,8 @@ def send_contact( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -3427,6 +3481,9 @@ def send_contact( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3455,7 +3512,7 @@ def send_contact( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name=last_name, vcard=vcard, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) @@ -4618,7 +4675,8 @@ def send_game( allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Used to send the game. @@ -4654,6 +4712,9 @@ def send_game( :param reply_parameters: Reply parameters :type reply_parameters: :obj:`ReplyParameters` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4685,7 +4746,7 @@ def send_game( apihelper.send_game( self.token, chat_id, game_short_name, 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, business_connection_id=business_connection_id) ) @@ -5059,7 +5120,8 @@ def send_poll( explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -5133,6 +5195,9 @@ def send_poll( :param reply_parameters: reply parameters. :type reply_parameters: :obj:`ReplyParameters` + :param business_connection_id: Identifier of the business connection to use for the poll + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5174,7 +5239,7 @@ 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) + reply_parameters=reply_parameters, business_connection_id=business_connection_id) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index eb7a30219..93632ac05 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -243,7 +243,8 @@ def send_message( reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, - message_thread_id=None, reply_parameters=None, link_preview_options=None): + message_thread_id=None, reply_parameters=None, link_preview_options=None, + business_connection_id=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -264,6 +265,8 @@ def send_message( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') @@ -441,7 +444,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m def send_dice( token, chat_id, emoji=None, disable_notification=None, - reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None): + reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, + business_connection_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -458,6 +462,8 @@ def send_dice( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) @@ -466,7 +472,7 @@ def send_photo( caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -496,13 +502,15 @@ def send_photo( payload['has_spoiler'] = has_spoiler if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_media_group( token, chat_id, media, disable_notification=None, - timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None): + timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -516,6 +524,8 @@ def send_media_group( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -528,7 +538,7 @@ def send_location( reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, - message_thread_id=None, reply_parameters=None): + message_thread_id=None, reply_parameters=None, business_connection_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -551,6 +561,8 @@ def send_location( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) @@ -601,7 +613,7 @@ def send_venue( token, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, disable_notification=None, reply_markup=None, timeout=None, google_place_id=None, - google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None): + google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -624,13 +636,15 @@ def send_venue( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None, reply_parameters=None): + protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -649,6 +663,8 @@ def send_contact( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) @@ -666,7 +682,7 @@ def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=Non def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -710,6 +726,9 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['has_spoiler'] = has_spoiler if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id + return _make_request(token, method_url, params=payload, files=files, method='post') @@ -717,7 +736,7 @@ def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None, - has_spoiler=None): + has_spoiler=None, business_connection_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -759,12 +778,14 @@ def send_animation( payload['has_spoiler'] = has_spoiler if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - protect_content=None, message_thread_id=None, reply_parameters=None): + protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -792,12 +813,14 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup= payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, - message_thread_id=None, reply_parameters=None): + message_thread_id=None, reply_parameters=None,business_connection_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -831,12 +854,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, - caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None): + caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -876,13 +901,15 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, - protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None): + protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None, business_connection_id=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -923,6 +950,8 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['emoji'] = emoji if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1376,7 +1405,7 @@ def delete_message(token, chat_id, message_id, timeout=None): def send_game( token, chat_id, game_short_name, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None, reply_parameters=None): + protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1391,6 +1420,8 @@ def send_game( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) @@ -1779,7 +1810,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): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, + business_connection_id=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1822,6 +1854,8 @@ def send_poll( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if business_connection_id is not None: + payload['business_connection_id'] = business_connection_id 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 37dc712bc..f5f3a16fe 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3035,7 +3035,8 @@ async def send_message( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - link_preview_options: Optional[types.LinkPreviewOptions]=None) -> types.Message: + link_preview_options: Optional[types.LinkPreviewOptions]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send text messages. @@ -3088,6 +3089,9 @@ async def send_message( :param link_preview_options: Options for previewing links. :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :param business_connection_id: Unique identifier for the target business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3142,7 +3146,7 @@ async def send_message( await asyncio_helper.send_message( self.token, chat_id, text, reply_markup, parse_mode, disable_notification, timeout, - entities, protect_content, message_thread_id, reply_parameters, link_preview_options)) + entities, protect_content, message_thread_id, reply_parameters, link_preview_options, business_connection_id)) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -3415,7 +3419,8 @@ async def send_dice( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -3454,6 +3459,9 @@ async def send_dice( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Unique identifier for the target business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3484,8 +3492,8 @@ async def send_dice( return types.Message.de_json( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, - reply_markup, timeout, protect_content, message_thread_id, reply_parameters) - ) + reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id)) + async def send_photo( self, chat_id: Union[int, str], photo: Union[Any, str], @@ -3499,7 +3507,8 @@ async def send_photo( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -3550,6 +3559,9 @@ async def send_photo( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param business_connection_id: Unique identifier for the target business connection + :type business_connection_id: :obj:`str` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3583,7 +3595,7 @@ async def send_photo( await asyncio_helper.send_photo( self.token, chat_id, photo, caption, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, has_spoiler, reply_parameters)) + protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -3600,7 +3612,8 @@ async def send_audio( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + 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 them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -3670,6 +3683,9 @@ async def send_audio( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Unique identifier for the target business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3706,7 +3722,7 @@ async def send_audio( await asyncio_helper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, protect_content, message_thread_id, reply_parameters)) + caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id)) async def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -3720,7 +3736,8 @@ async def send_voice( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + 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). @@ -3773,6 +3790,9 @@ async def send_voice( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Unique identifier for the target business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -3804,7 +3824,7 @@ async def send_voice( await asyncio_helper.send_voice( self.token, chat_id, voice, caption, duration, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, reply_parameters)) + protect_content, message_thread_id, reply_parameters, business_connection_id)) async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], @@ -3823,7 +3843,8 @@ async def send_document( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send general files. @@ -3886,6 +3907,9 @@ async def send_document( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Unique identifier for the target business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3930,7 +3954,7 @@ async def send_document( disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content, - message_thread_id = message_thread_id, reply_parameters=reply_parameters)) + message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) async def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], @@ -3943,7 +3967,8 @@ async def send_sticker( data: Union[Any, str]=None, message_thread_id: Optional[int]=None, emoji: Optional[str]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -3989,6 +4014,9 @@ async def send_sticker( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Unique identifier for the target business connection + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4027,7 +4055,7 @@ async def send_sticker( reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, - message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters)) + message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) async def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -4049,7 +4077,8 @@ async def send_video( message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -4120,6 +4149,9 @@ async def send_video( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4161,7 +4193,7 @@ async def send_video( await asyncio_helper.send_video( self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, - caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters)) + caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -4181,7 +4213,8 @@ async def send_animation( message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -4251,6 +4284,9 @@ async def send_animation( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4287,7 +4323,7 @@ async def send_animation( await asyncio_helper.send_animation( self.token, chat_id, animation, duration, caption, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters)) + caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -4302,7 +4338,8 @@ async def send_video_note( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -4357,6 +4394,9 @@ async def send_video_note( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4391,7 +4431,7 @@ async def send_video_note( return types.Message.de_json( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_markup, - disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters)) + disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id)) async def send_media_group( self, chat_id: Union[int, str], @@ -4404,7 +4444,8 @@ async def send_media_group( timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> List[types.Message]: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -4438,6 +4479,9 @@ async def send_media_group( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -4466,7 +4510,7 @@ async def send_media_group( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = await asyncio_helper.send_media_group( - self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters) + self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -4483,7 +4527,8 @@ async def send_location( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -4536,6 +4581,9 @@ async def send_location( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4568,7 +4616,7 @@ async def send_location( self.token, chat_id, latitude, longitude, live_period, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - protect_content, message_thread_id, reply_parameters)) + protect_content, message_thread_id, reply_parameters, business_connection_id)) async def edit_message_live_location( self, latitude: float, longitude: float, @@ -4677,7 +4725,8 @@ async def send_venue( google_place_type: Optional[str]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -4738,6 +4787,9 @@ async def send_venue( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4769,8 +4821,8 @@ async def send_venue( await asyncio_helper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_markup, timeout, - google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters) - ) + google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id)) + async def send_contact( self, chat_id: Union[int, str], phone_number: str, @@ -4783,7 +4835,8 @@ async def send_contact( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -4831,6 +4884,9 @@ async def send_contact( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4862,8 +4918,8 @@ async def send_contact( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters) - ) + protect_content, message_thread_id, reply_parameters, business_connection_id)) + async def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool: @@ -5968,7 +6024,8 @@ async def send_game( allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Used to send the game. @@ -6004,6 +6061,9 @@ async def send_game( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of the business connection. + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6034,7 +6094,7 @@ async def send_game( result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters) + protect_content, message_thread_id, reply_parameters, business_connection_id) return types.Message.de_json(result) async def set_game_score( @@ -6396,7 +6456,8 @@ async def send_poll( explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -6470,6 +6531,9 @@ async def send_poll( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param business_connection_id: Identifier of the business connection to send the message through + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6508,7 +6572,7 @@ 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)) + reply_markup, timeout, explanation_entities, protect_content, message_thread_id, reply_parameters, business_connection_id)) 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 ceafe637f..186abf904 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -281,7 +281,7 @@ async def send_message( reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, - message_thread_id=None, reply_parameters=None, link_preview_options=None): + message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -302,6 +302,8 @@ async def send_message( params['protect_content'] = protect_content if message_thread_id: params['message_thread_id'] = message_thread_id + if business_connection_id: + params['business_connection_id'] = business_connection_id return await _process_request(token, method_name, params=params) @@ -433,7 +435,7 @@ async def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, - message_thread_id=None,reply_parameters=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -450,6 +452,8 @@ async def send_dice( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) @@ -458,7 +462,8 @@ async def send_photo( caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None,reply_parameters=None): + message_thread_id=None, has_spoiler=None,reply_parameters=None, + business_connection_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -488,13 +493,15 @@ async def send_photo( payload['message_thread_id'] = message_thread_id if has_spoiler is not None: payload['has_spoiler'] = has_spoiler + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_media_group( token, chat_id, media, disable_notification=None, - timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None): + timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendMediaGroup' media_json, files = await convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -508,6 +515,8 @@ async def send_media_group( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request( token, method_url, params=payload, method='post' if files else 'get', @@ -519,7 +528,7 @@ async def send_location( live_period=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None): + proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -542,6 +551,8 @@ async def send_location( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) @@ -593,7 +604,7 @@ async def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_markup=None, timeout=None, google_place_id=None, - google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None): + google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -616,13 +627,15 @@ async def send_venue( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None,reply_parameters=None): + protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -641,6 +654,8 @@ async def send_contact( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) @@ -657,7 +672,7 @@ async def send_chat_action(token, chat_id, action, timeout=None, message_thread_ async def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, - protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None): + protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -701,6 +716,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m payload['message_thread_id'] = message_thread_id if has_spoiler is not None: payload['has_spoiler'] = has_spoiler + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -708,7 +725,7 @@ async def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, width=None, height=None, protect_content=None, message_thread_id=None, - has_spoiler=None,reply_parameters=None): + has_spoiler=None,reply_parameters=None, business_connection_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -750,12 +767,14 @@ async def send_animation( payload['message_thread_id'] = message_thread_id if has_spoiler is not None: payload['has_spoiler'] = has_spoiler + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - protect_content=None, message_thread_id=None,reply_parameters=None): + protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -783,12 +802,14 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_ payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, - message_thread_id=None,reply_parameters=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -822,12 +843,14 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, rep payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, - caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None): + caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -867,13 +890,15 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, - message_thread_id=None, emoji=None,reply_parameters=None): + message_thread_id=None, emoji=None,reply_parameters=None, business_connection_id=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -914,6 +939,8 @@ async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_m payload['message_thread_id'] = message_thread_id if emoji: payload['emoji'] = emoji + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1363,7 +1390,7 @@ async def delete_message(token, chat_id, message_id, timeout=None): async def send_game( token, chat_id, game_short_name, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None,reply_parameters=None): + protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1378,6 +1405,8 @@ async def send_game( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) @@ -1763,7 +1792,7 @@ 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): + reply_markup=None, timeout=None, explanation_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1807,6 +1836,8 @@ async def send_poll( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) diff --git a/telebot/types.py b/telebot/types.py index fbb857ae0..7119003fa 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -225,7 +225,7 @@ 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, **obj) + 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, From 60f91937ecdddd1a5f2c15a44bab11ace86da806 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 01:14:24 +0500 Subject: [PATCH 1507/1808] Fix parantheses and add business connection to sendchataction --- telebot/__init__.py | 12 ++++++++---- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 8 ++++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 291b77ead..ccb98f0b8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2093,7 +2093,7 @@ def send_dice( self.token, chat_id, emoji=emoji, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) - ) + def send_photo( @@ -2195,7 +2195,7 @@ def send_photo( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) - ) + def send_audio( @@ -3517,7 +3517,8 @@ def send_contact( def send_chat_action( - self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool: + self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, + business_connection_id: Optional[str]=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). @@ -3543,11 +3544,14 @@ def send_chat_action( :param message_thread_id: The thread identifier of a message from which the reply will be sent(supergroups only) :type message_thread_id: :obj:`int` + :param business_connection_id: Identifier of a business connection + :type business_connection_id: :obj:`str` + :return: Returns True on success. :rtype: :obj:`bool` """ return apihelper.send_chat_action( - self.token, chat_id, action, timeout=timeout, message_thread_id=message_thread_id) + self.token, chat_id, action, timeout=timeout, message_thread_id=message_thread_id, business_connection_id=business_connection_id) @util.deprecated(deprecation_text="Use ban_chat_member instead") diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 93632ac05..af2972e04 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -669,13 +669,15 @@ def send_contact( return _make_request(token, method_url, params=payload) -def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None): +def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None, business_connection_id=None): method_url = r'sendChatAction' payload = {'chat_id': chat_id, 'action': action} if timeout: payload['timeout'] = timeout if message_thread_id is not None: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f5f3a16fe..1cf75f30e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4922,7 +4922,8 @@ async def send_contact( async def send_chat_action( - self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None) -> bool: + self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, + business_connection_id: Optional[str]=None) -> bool: """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). @@ -4948,10 +4949,13 @@ async def send_chat_action( :param message_thread_id: The thread to which the message will be sent(supergroups only) :type message_thread_id: :obj:`int` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: Returns True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id) + return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id, business_connection_id) async def kick_chat_member( self, chat_id: Union[int, str], user_id: int, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 186abf904..3a90cb41d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -659,13 +659,15 @@ async def send_contact( return await _process_request(token, method_url, params=payload) -async def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None): +async def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None, business_connection_id=None): method_url = r'sendChatAction' payload = {'chat_id': chat_id, 'action': action} if timeout: payload['timeout'] = timeout if message_thread_id: payload['message_thread_id'] = message_thread_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) From c9ceab76fb05599b21538979aec457a7a74077e0 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 01:20:27 +0500 Subject: [PATCH 1508/1808] fix --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 7119003fa..ac5ab32fd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -220,7 +220,7 @@ def de_json(cls, json_string): business_connection = BusinessConnection.de_json(obj.get('business_connection')) business_message = Message.de_json(obj.get('business_message')) edited_business_message = Message.de_json(obj.get('edited_business_message')) - deleted_business_messages = Message.de_json(obj.get('deleted_business_messages')) + deleted_business_messages = BusinessMessagesDeleted.de_json(obj.get('deleted_business_messages')) 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, From a151b78beb0baa33dfcbe31a016e81a1faa0fba1 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 01:25:58 +0500 Subject: [PATCH 1509/1808] added sender_business_bot and business_connection_id --- telebot/types.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index ac5ab32fd..82b081aff 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -843,9 +843,16 @@ class Message(JsonDeserializable): :param sender_boost_count: Optional. If the sender of the message boosted the chat, the number of boosts added by the user :type sender_boost_count: :obj:`int` + :param sender_business_bot info: Optional. Information about the business bot that sent the message + :type sender_business_bot_info: :class:`telebot.types.User` + :param date: Date the message was sent in Unix time :type date: :obj:`int` + :param business_connection_id: Optional. Unique identifier of the business connection from which the message was received. If non-empty, + the message belongs to a chat of the corresponding business account that is independent from any potential bot chat which might share the same identifier. + :type business_connection_id: :obj:`str` + :param chat: Conversation the message belongs to :type chat: :class:`telebot.types.Chat` @@ -1326,6 +1333,11 @@ def de_json(cls, json_string): opts['sender_boost_count'] = obj['sender_boost_count'] if 'reply_to_story' in obj: opts['reply_to_story'] = Story.de_json(obj['reply_to_story']) + if 'sender_business_bot' in obj: + opts['sender_business_bot'] = User.de_json(obj['sender_business_bot']) + if 'business_connection_id' in obj: + opts['business_connection_id'] = obj['business_connection_id'] + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1430,6 +1442,8 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.boost_added: Optional[ChatBoostAdded] = None self.sender_boost_count: Optional[int] = None self.reply_to_story: Optional[Story] = None + self.sender_business_bot: Optional[User] = None + self.business_connection_id: Optional[str] = None for key in options: setattr(self, key, options[key]) From 95b38ae7a87878091d456fa1afe5f79eb9e2361c Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 01:40:18 +0500 Subject: [PATCH 1510/1808] added business things --- telebot/__init__.py | 4 +- telebot/async_telebot.py | 4 +- telebot/types.py | 142 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ccb98f0b8..269136402 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -8208,7 +8208,7 @@ def deleted_business_messages_handler(self, func=None, **kwargs): def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_deleted_message_handler(handler_dict) + self.add_deleted_business_messages_handler(handler_dict) return handler return decorator @@ -8220,7 +8220,7 @@ def add_deleted_business_messages_handler(self, handler_dict): :meta private: """ - self.deleted_message_handlers.append(handler_dict) + self.deleted_business_messages_handlers.append(handler_dict) def register_deleted_business_messages_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1cf75f30e..16128ba10 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2484,7 +2484,7 @@ def deleted_business_messages_handler(self, func=None, **kwargs): def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) - self.add_deleted_message_handler(handler_dict) + self.add_deleted_business_messages_handler(handler_dict) return handler return decorator @@ -2496,7 +2496,7 @@ def add_deleted_business_messages_handler(self, handler_dict): :meta private: """ - self.deleted_message_handlers.append(handler_dict) + self.deleted_business_messages_handlers.append(handler_dict) def register_deleted_business_messages_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ diff --git a/telebot/types.py b/telebot/types.py index 82b081aff..d8afbdf52 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -584,6 +584,15 @@ class Chat(JsonDeserializable): Returned only in getChat. :type active_usernames: :obj:`list` of :obj:`str` + :param business_intro: Optional. Business intro for the chat. Returned only in getChat. + :type business_intro: :class:`telebot.types.BusinessIntro` + + :param business_location: Optional. Business location for the chat. Returned only in getChat. + :type business_location: :class:`telebot.types.BusinessLocation` + + :param business_opening_hours : Optional. Business opening hours for the chat. Returned only in getChat. + :type business_opening_hours: :class:`telebot.types.BusinessHours` + :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` @@ -711,6 +720,12 @@ def de_json(cls, json_string): obj['location'] = ChatLocation.de_json(obj['location']) if 'available_reactions' in obj: obj['available_reactions'] = [ReactionType(reaction) for reaction in obj['available_reactions']] + if 'business_intro' in obj: + obj['business_intro'] = BusinessIntro.de_json(obj['business_intro']) + if 'business_location' in obj: + obj['business_location'] = BusinessLocation.de_json(obj['business_location']) + if 'business_opening_hours' in obj: + obj['business_opening_hours'] = BusinessOpeningHours.de_json(obj['business_opening_hours']) return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -724,7 +739,8 @@ def __init__(self, id, type, title=None, username=None, first_name=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, - unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, **kwargs): + unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, + business_opening_hours=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -762,6 +778,9 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.has_visible_history: bool = has_visible_history self.unrestrict_boost_count: int = unrestrict_boost_count self.custom_emoji_sticker_set_name: str = custom_emoji_sticker_set_name + self.business_intro: BusinessIntro = business_intro + self.business_location: BusinessLocation = business_location + self.business_opening_hours: BusinessOpeningHours = business_opening_hours @@ -9425,4 +9444,123 @@ def __init__(self, business_connection_id, chat, message_ids, **kwargs): self.business_connection_id: str = business_connection_id self.chat: Chat = chat self.message_ids: List[int] = message_ids - \ No newline at end of file + + +class BusinessIntro(JsonDeserializable): + """ + This object represents a business intro. + + Telegram documentation: https://core.telegram.org/bots/api#businessintro + + :param title: Optional. Title text of the business intro + :type title: :obj:`str` + + :param message: Optional. Message text of the business intro + :type message: :obj:`str` + + :param sticker: Optional. Sticker of the business intro + :type sticker: :class:`Sticker` + + :return: Instance of the class + :rtype: :class:`BusinessIntro` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'sticker' in obj: + obj['sticker'] = Sticker.de_json(obj['sticker']) + return cls(**obj) + + def __init__(self, title=None, message=None, sticker=None, **kwargs): + self.title: Optional[str] = title + self.message: Optional[str] = message + self.sticker: Optional[Sticker] = sticker + + +class BusinessLocation(JsonDeserializable): + """ + This object represents a business location. + + Telegram documentation: https://core.telegram.org/bots/api#businesslocation + + :param address: Address of the business + :type address: :obj:`str` + + :param location: Optional. Location of the business + :type location: :class:`Location` + + :return: Instance of the class + :rtype: :class:`BusinessLocation` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'location' in obj: + obj['location'] = Location.de_json(obj['location']) + return cls(**obj) + + def __init__(self, address, location=None, **kwargs): + self.address: str = address + self.location: Optional[Location] = location + + +class BusinessOpeningHoursInterval(JsonDeserializable): + """ + This object represents a business opening hours interval. + + Telegram documentation: https://core.telegram.org/bots/api#businessopeninghoursinterval + + :param opening_minute: The minute's sequence number in a week, starting on Monday, marking the start of the time interval during which the business is open; 0 - 7 24 60 + :type opening_minute: :obj:`int` + + :param closing_minute: The minute's sequence number in a week, starting on Monday, marking the end of the time interval during which the business is open; 0 - 8 24 60 + :type closing_minute: :obj:`int` + + :return: Instance of the class + :rtype: :class:`BusinessOpeningHoursInterval` + """ + + @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, opening_minute, closing_minute, **kwargs): + self.opening_minute: int = opening_minute + self.closing_minute: int = closing_minute + + +class BusinessOpeningHours(JsonDeserializable): + """ + + This object represents business opening hours. + + Telegram documentation: https://core.telegram.org/bots/api#businessopeninghours + + :param time_zone_name: Unique name of the time zone for which the opening hours are defined + :type time_zone_name: :obj:`str` + + :param opening_hours: List of time intervals describing business opening hours + :type opening_hours: :obj:`list` of :class:`BusinessOpeningHoursInterval` + + :return: Instance of the class + + :rtype: :class:`BusinessOpeningHours` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['opening_hours'] = [BusinessOpeningHoursInterval.de_json(interval) for interval in obj['opening_hours']] + return cls(**obj) + + def __init__(self, time_zone_name, opening_hours, **kwargs): + self.time_zone_name: str = time_zone_name + self.opening_hours: List[BusinessOpeningHoursInterval] = opening_hours + From 63de545fab5d51dc2304c9b9c898b4505060b3b8 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 01:50:56 +0500 Subject: [PATCH 1511/1808] Added the field format to the class InputSticker. --- telebot/types.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index d8afbdf52..2dfc69c66 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7950,15 +7950,19 @@ class InputSticker(Dictionaryable, JsonSerializable): For “regular” and “custom_emoji” stickers only. :type keywords: :obj:`list` of :obj:`str` + :param format: Format of the added sticker, must be one of “static” for a .WEBP or .PNG image, “animated” for a .TGS animation, “video” for a WEBM video + :type format: :obj:`str` + :return: Instance of the class :rtype: :class:`telebot.types.InputSticker` """ - def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], mask_position: Optional[MaskPosition]=None, keywords: Optional[List[str]]=None) -> None: + def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], format: str, mask_position: Optional[MaskPosition]=None, keywords: Optional[List[str]]=None) -> None: self.sticker: Union[str, InputFile] = sticker self.emoji_list: List[str] = emoji_list self.mask_position: Optional[MaskPosition] = mask_position self.keywords: Optional[List[str]] = keywords + self.format: str = format if service_utils.is_string(self.sticker): self._sticker_name = '' @@ -7973,7 +7977,8 @@ def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], mask_p def to_dict(self) -> dict: json_dict = { 'sticker': self._sticker_dic, - 'emoji_list': self.emoji_list + 'emoji_list': self.emoji_list, + 'format': self.format } if self.mask_position is not None: From 4ed9b03aa2ec6d36b8c3ef9e43aeb6238d0c5ca3 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 01:55:32 +0500 Subject: [PATCH 1512/1808] added format to set sticker thumbnail --- telebot/__init__.py | 7 +++++-- telebot/apihelper.py | 4 ++-- telebot/async_telebot.py | 4 ++-- telebot/asyncio_helper.py | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 269136402..2007775d5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5543,7 +5543,7 @@ def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types. ) - def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None): + def set_sticker_set_thumbnail(self, name: str, user_id: int, format: str, thumbnail: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. @@ -5559,10 +5559,13 @@ def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[An :param thumbnail: A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements for video sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. :type thumbnail: :obj:`filelike object` + :param format: Format of the thumbnail, must be one of “static” for a .WEBP or .PNG image, “animated” for a .TGS animation, or “video” for a WEBM video + :type format: :obj:`str` + :return: On success, True is returned. :rtype: :obj:`bool` """ - return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail) + return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") diff --git a/telebot/apihelper.py b/telebot/apihelper.py index af2972e04..530257ff6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -371,9 +371,9 @@ def get_chat_member_count(token, chat_id): return _make_request(token, method_url, params=payload) -def set_sticker_set_thumbnail(token, name, user_id, thumbnail): +def set_sticker_set_thumbnail(token, name, user_id, thumbnail, format): method_url = r'setStickerSetThumbnail' - payload = {'name': name, 'user_id': user_id} + payload = {'name': name, 'user_id': user_id, 'format': format} files = {} if thumbnail: if not isinstance(thumbnail, str): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 16128ba10..8af0c4d1d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6866,7 +6866,7 @@ async def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> return types.UserChatBoosts.de_json(result) - async def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[Any, str]=None): + async def set_sticker_set_thumbnail(self, name: str, user_id: int, format: str, thumbnail: Union[Any, str]=None): """ Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. @@ -6885,7 +6885,7 @@ async def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Un :return: On success, True is returned. :rtype: :obj:`bool` """ - return await asyncio_helper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail) + return await asyncio_helper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") async def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]=None): diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3a90cb41d..40ef2927c 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -352,15 +352,16 @@ async def get_chat_member_count(token, chat_id): return await _process_request(token, method_url, params=payload) -async def set_sticker_set_thumbnail(token, name, user_id, thumbnail): +async def set_sticker_set_thumbnail(token, name, user_id, thumbnail, format): method_url = r'setStickerSetThumbnail' - payload = {'name': name, 'user_id': user_id} + payload = {'name': name, 'user_id': user_id, 'format': format} files = {} if thumbnail: if not isinstance(thumbnail, str): files['thumbnail'] = thumbnail else: payload['thumbnail'] = thumbnail + return await _process_request(token, method_url, params=payload, files=files or None) From d187aec77639014096f95976212a7bffc4e5e47c Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:02:05 +0500 Subject: [PATCH 1513/1808] added requests to sharing chats --- telebot/types.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 2dfc69c66..2c96560c1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2578,16 +2578,29 @@ class KeyboardButtonRequestUsers(Dictionaryable): :param max_quantity: Optional. The maximum number of users to be selected; 1-10. Defaults to 1. :type max_quantity: :obj:`int` + :param request_name: Optional. Request name + :type request_name: :obj:`bool` + + :param request_username: Optional. Request username + :type request_username: :obj:`bool` + + :param request_photo: Optional. Request photo + :type request_photo: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.KeyboardButtonRequestUsers` """ def __init__( self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, - max_quantity: Optional[int]=None) -> None: + max_quantity: Optional[int]=None, request_name: Optional[str]=None, request_username: Optional[bool]=None, + request_photo: Optional[bool]=None) -> None: self.request_id: int = request_id self.user_is_bot: Optional[bool] = user_is_bot self.user_is_premium: Optional[bool] = user_is_premium self.max_quantity: Optional[int] = max_quantity + self.request_name: Optional[str] = request_name + self.request_username: Optional[bool] = request_username + self.request_photo: Optional[bool] = request_photo def to_dict(self) -> dict: data = {'request_id': self.request_id} @@ -2597,6 +2610,12 @@ def to_dict(self) -> dict: data['user_is_premium'] = self.user_is_premium if self.max_quantity is not None: data['max_quantity'] = self.max_quantity + if self.request_name is not None: + data['request_name'] = self.request_name + if self.request_username is not None: + data['request_username'] = self.request_username + if self.request_photo is not None: + data['request_photo'] = self.request_photo return data @@ -2645,6 +2664,15 @@ class KeyboardButtonRequestChat(Dictionaryable): :param bot_is_member: Optional. Pass True to request a chat where the bot is a member. Otherwise, no additional restrictions are applied. :type bot_is_member: :obj:`bool` + :param request_title: Optional. Request title + :type request_title: :obj:`bool` + + :param request_photo: Optional. Request photo + :type request_photo: :obj:`bool` + + :param request_username: Optional. Request username + :type request_username: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.KeyboardButtonRequestChat` """ @@ -2652,7 +2680,8 @@ class KeyboardButtonRequestChat(Dictionaryable): def __init__(self, request_id: int, chat_is_channel: bool, chat_is_forum: Optional[bool]=None, chat_has_username: Optional[bool]=None, chat_is_created: Optional[bool]=None, user_administrator_rights: Optional[ChatAdministratorRights]=None, - bot_administrator_rights: Optional[ChatAdministratorRights]=None, bot_is_member: Optional[bool]=None) -> None: + bot_administrator_rights: Optional[ChatAdministratorRights]=None, bot_is_member: Optional[bool]=None, + request_title: Optional[str]=None, request_photo: Optional[bool]=None, request_username: Optional[bool]=None): self.request_id: int = request_id self.chat_is_channel: bool = chat_is_channel self.chat_is_forum: Optional[bool] = chat_is_forum @@ -2661,6 +2690,9 @@ def __init__(self, request_id: int, chat_is_channel: bool, chat_is_forum: Option self.user_administrator_rights: Optional[ChatAdministratorRights] = user_administrator_rights self.bot_administrator_rights: Optional[ChatAdministratorRights] = bot_administrator_rights self.bot_is_member: Optional[bool] = bot_is_member + self.request_title: Optional[str] = request_title + self.request_photo: Optional[bool] = request_photo + self.request_username: Optional[bool] = request_username def to_dict(self) -> dict: @@ -2677,6 +2709,12 @@ def to_dict(self) -> dict: data['bot_administrator_rights'] = self.bot_administrator_rights.to_dict() if self.bot_is_member is not None: data['bot_is_member'] = self.bot_is_member + if self.request_title is not None: + data['request_title'] = self.request_title + if self.request_photo is not None: + data['request_photo'] = self.request_photo + if self.request_username is not None: + data['request_username'] = self.request_username return data From 33dbe9c88780ab93dedbeabe3503c26784898912 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:07:14 +0500 Subject: [PATCH 1514/1808] Added the class SharedUser and replaced the field user_ids in the class UsersShared with the field users. --- telebot/types.py | 49 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 2c96560c1..6ade97704 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9043,13 +9043,13 @@ class UsersShared(JsonDeserializable): :param request_id: Identifier of the request :type request_id: :obj:`int` - :param user_ids: Identifiers of the shared users. These numbers may have more than 32 significant bits + :param user_ids: Array of :obj:`types.SharedUser` of the shared users. These numbers may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting them. But they have at most 52 significant bits, so 64-bit integers or double-precision float types are safe for storing these identifiers. The bot may not have access to the users and could be unable to use these identifiers unless the users are already known to the bot by some other means. - :type user_ids: :obj:`list` of :obj:`int` + :type user_ids: :obj:`list` of :obj:`types.SharedUser` :return: Instance of the class :rtype: :class:`UsersShared` @@ -9058,9 +9058,12 @@ class UsersShared(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) + + obj['user_ids'] = [SharedUser.de_json(user) for user in obj['user_ids']] + return cls(**obj) - def __init__(self, request_id, user_ids, **kwargs): + def __init__(self, request_id, user_ids: SharedUser, **kwargs): self.request_id = request_id self.user_ids = user_ids @@ -9607,3 +9610,43 @@ def __init__(self, time_zone_name, opening_hours, **kwargs): self.time_zone_name: str = time_zone_name self.opening_hours: List[BusinessOpeningHoursInterval] = opening_hours + +class SharedUser(JsonDeserializable): + """ + This object contains information about a user that was shared with the bot using a KeyboardButtonRequestUser button. + + Telegram documentation: https://core.telegram.org/bots/api#shareduser + + :param user_id: Identifier of the shared user. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so 64-bit integers or double-precision float types are safe for storing these identifiers. The bot may not have access to the user and could be unable to use this identifier, unless the user is already known to the bot by some other means. + :type user_id: :obj:`int` + + :param first_name: Optional. First name of the user, if the name was requested by the bot + :type first_name: :obj:`str` + + :param last_name: Optional. Last name of the user, if the name was requested by the bot + :type last_name: :obj:`str` + + :param username: Optional. Username of the user, if the username was requested by the bot + :type username: :obj:`str` + + :param photo: Optional. Available sizes of the chat photo, if the photo was requested by the bot + :type photo: :obj:`list` of :class:`PhotoSize` + + :return: Instance of the class + :rtype: :class:`SharedUser` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'photo' in obj: + obj['photo'] = [PhotoSize.de_json(photo) for photo in obj['photo']] + return cls(**obj) + + def __init__(self, user_id, first_name=None, last_name=None, username=None, photo=None, **kwargs): + self.user_id: int = user_id + self.first_name: Optional[str] = first_name + self.last_name: Optional[str] = last_name + self.username: Optional[str] = username + self.photo: Optional[List[PhotoSize]] = photo From 63d09f5fff6f48e88de4e92a89fdbebb28289d8f Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:15:09 +0500 Subject: [PATCH 1515/1808] Added the fields title, username, and photo to the class ChatShared. --- telebot/types.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 6ade97704..29d2b7432 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7908,6 +7908,15 @@ class ChatShared(JsonDeserializable): and could be unable to use this identifier, unless the chat is already known to the bot by some other means. :type chat_id: :obj:`int` + :param title: Optional. Title of the shared chat + :type title: :obj:`str` + + :param photo: Optional. Array of Photosize + :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` + + :param username: Optional. Username of the shared chat + :type username: :obj:`str` + :return: Instance of the class :rtype: :class:`telebot.types.ChatShared` """ @@ -7918,9 +7927,13 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, request_id: int, chat_id: int, **kwargs) -> None: + def __init__(self, request_id: int, chat_id: int, title: Optional[str]=None, photo: Optional[List[PhotoSize]]=None, + username: Optional[str]=None, **kwargs) -> None: self.request_id: int = request_id self.chat_id: int = chat_id + self.title: Optional[str] = title + self.photo: Optional[List[PhotoSize]] = photo + self.username: Optional[str] = username class BotDescription(JsonDeserializable): From 1717f0dd0f2ffe26cd469425e31580c2e6e0dcd6 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:17:22 +0500 Subject: [PATCH 1516/1808] Added the field is_from_offline to the class Message. --- telebot/types.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 29d2b7432..a61613022 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -926,6 +926,10 @@ class Message(JsonDeserializable): :param has_protected_content: Optional. :obj:`bool`, if the message can't be forwarded :type has_protected_content: :obj:`bool` + :param is_from_offline: Optional. True, if the message was sent by an implicit action, for example, + as an away or a greeting business message, or as a scheduled message + :type is_from_offline: :obj:`bool` + :param media_group_id: Optional. The unique identifier of a media message group this message belongs to :type media_group_id: :obj:`str` @@ -1356,6 +1360,8 @@ def de_json(cls, json_string): opts['sender_business_bot'] = User.de_json(obj['sender_business_bot']) if 'business_connection_id' in obj: opts['business_connection_id'] = obj['business_connection_id'] + if 'is_from_offline' in obj: + opts['is_from_offline'] = obj['is_from_offline'] @@ -1463,6 +1469,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.reply_to_story: Optional[Story] = None self.sender_business_bot: Optional[User] = None self.business_connection_id: Optional[str] = None + self.is_from_offline: Optional[bool] = None for key in options: setattr(self, key, options[key]) From f2344d262d1225cbbd4bd700c0345231dce6bfff Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:20:27 +0500 Subject: [PATCH 1517/1808] can_connect_to_business and personal_chat --- telebot/types.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index a61613022..0db7c2159 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -481,6 +481,9 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): :param supports_inline_queries: Optional. True, if the bot supports inline queries. Returned only in getMe. :type supports_inline_queries: :obj:`bool` + :param can_connect_to_business: Optional. True, if the bot can be connected to a Telegram Business account to receive its messages. Returned only in getMe. + :type can_connect_to_business: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.User` """ @@ -492,7 +495,7 @@ def de_json(cls, json_string): 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, **kwargs): + is_premium=None, added_to_attachment_menu=None, can_connect_to_business=None, **kwargs): self.id: int = id self.is_bot: bool = is_bot self.first_name: str = first_name @@ -504,6 +507,7 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.supports_inline_queries: bool = supports_inline_queries self.is_premium: bool = is_premium self.added_to_attachment_menu: bool = added_to_attachment_menu + self.can_connect_to_business: bool = can_connect_to_business @property @@ -530,7 +534,9 @@ def to_dict(self): 'can_read_all_group_messages': self.can_read_all_group_messages, 'supports_inline_queries': self.supports_inline_queries, 'is_premium': self.is_premium, - 'added_to_attachment_menu': self.added_to_attachment_menu} + 'added_to_attachment_menu': self.added_to_attachment_menu, + 'can_connect_to_business': self.can_connect_to_business} + class GroupChat(JsonDeserializable): @@ -593,6 +599,9 @@ class Chat(JsonDeserializable): :param business_opening_hours : Optional. Business opening hours for the chat. Returned only in getChat. :type business_opening_hours: :class:`telebot.types.BusinessHours` + :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. :type available_reactions: :obj:`list` of :class:`telebot.types.ReactionType` @@ -726,6 +735,8 @@ def de_json(cls, json_string): obj['business_location'] = BusinessLocation.de_json(obj['business_location']) if 'business_opening_hours' in obj: obj['business_opening_hours'] = BusinessOpeningHours.de_json(obj['business_opening_hours']) + if 'personal_chat' in obj: + obj['personal_chat'] = Chat.de_json(obj['personal_chat']) return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -740,7 +751,7 @@ def __init__(self, id, type, title=None, username=None, first_name=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, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, - business_opening_hours=None, **kwargs): + business_opening_hours=None, personal_chat=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -781,6 +792,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.business_intro: BusinessIntro = business_intro self.business_location: BusinessLocation = business_location self.business_opening_hours: BusinessOpeningHours = business_opening_hours + self.personal_chat: Chat = personal_chat From a7c94fa40fe2f441c102a25c78d796fb20cbb1f5 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:24:10 +0500 Subject: [PATCH 1518/1808] replacestickerinset --- telebot/__init__.py | 25 +++++++++++++++++++++++++ telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 24 ++++++++++++++++++++++++ telebot/asyncio_helper.py | 6 ++++++ 4 files changed, 60 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2007775d5..0d6e2aba0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5705,6 +5705,31 @@ def delete_sticker_set(self, name:str) -> bool: """ return apihelper.delete_sticker_set(self.token, name) + + def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: + """ + Use this method to replace an existing sticker in a sticker set with a new one. The method is equivalent to calling deleteStickerFromSet, then addStickerToSet, + then setStickerPositionInSet. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#replaceStickerInSet + + :param user_id: User identifier of the sticker set owner + :type user_id: :obj:`int` + + :param name: Sticker set name + :type name: :obj:`str` + + :param old_sticker: File identifier of the replaced sticker + :type old_sticker: :obj:`str` + + :param sticker: A JSON-serialized object with information about the added sticker. If exactly the same sticker had already been added to the set, then the set remains unchanged. + :type sticker: :class:`telebot.types.InputSticker` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.replace_sticker_in_set(self.token, user_id, name, old_sticker, sticker) + def set_sticker_emoji_list(self, sticker: str, emoji_list: List[str]) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 530257ff6..1b3a6df99 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -383,6 +383,11 @@ def set_sticker_set_thumbnail(token, name, user_id, thumbnail, format): return _make_request(token, method_url, params=payload, files=files or None) +def replace_sticker_in_set(token, user_id, name, old_sticker, sticker): + method_url = r'replaceStickerInSet' + payload = {'user_id': user_id, 'name': name, 'old_sticker': old_sticker, 'sticker': sticker.to_json()} + return _make_request(token, method_url, params=payload) + def set_chat_sticker_set(token, chat_id, sticker_set_name): method_url = r'setChatStickerSet' payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8af0c4d1d..57ae8e6a4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7051,6 +7051,30 @@ async def delete_sticker_set(self, name:str) -> bool: return await asyncio_helper.delete_sticker_set(self.token, name) + async def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: + """ + Use this method to replace an existing sticker in a sticker set with a new one. The method is equivalent to calling deleteStickerFromSet, then addStickerToSet, + then setStickerPositionInSet. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#replaceStickerInSet + + :param user_id: User identifier of the sticker set owner + :type user_id: :obj:`int` + + :param name: Sticker set name + :type name: :obj:`str` + + :param old_sticker: File identifier of the replaced sticker + :type old_sticker: :obj:`str` + + :param sticker: A JSON-serialized object with information about the added sticker. If exactly the same sticker had already been added to the set, then the set remains unchanged. + :type sticker: :class:`telebot.types.InputSticker` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + result = await asyncio_helper.replace_sticker_in_set(self.token, user_id, name, old_sticker, sticker) + return result async def set_sticker_emoji_list(self, name: str, emoji_list: List[str]) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 40ef2927c..e1fcdec36 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -352,6 +352,12 @@ async def get_chat_member_count(token, chat_id): return await _process_request(token, method_url, params=payload) + +async def replace_sticker_in_set(token, user_id, name, old_sticker, sticker): + method_url = r'replaceStickerInSet' + payload = {'user_id': user_id, 'name': name, 'old_sticker': old_sticker, 'sticker': sticker.to_json()} + return await _process_request(token, method_url, params=payload) + async def set_sticker_set_thumbnail(token, name, user_id, thumbnail, format): method_url = r'setStickerSetThumbnail' payload = {'name': name, 'user_id': user_id, 'format': format} From 86833ac38b6206f61dd9ec9e8eb85209704f5374 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:30:03 +0500 Subject: [PATCH 1519/1808] added birthdates --- telebot/types.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 0db7c2159..1d104a098 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -590,6 +590,9 @@ class Chat(JsonDeserializable): 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. + :type birthdate: :obj:`str` + :param business_intro: Optional. Business intro for the chat. Returned only in getChat. :type business_intro: :class:`telebot.types.BusinessIntro` @@ -737,6 +740,8 @@ def de_json(cls, json_string): obj['business_opening_hours'] = BusinessOpeningHours.de_json(obj['business_opening_hours']) if 'personal_chat' in obj: obj['personal_chat'] = Chat.de_json(obj['personal_chat']) + if 'birthdate' in obj: + obj['birthdate'] = Birthdate.de_json(obj['birthdate']) return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -751,7 +756,7 @@ def __init__(self, id, type, title=None, username=None, first_name=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, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, - business_opening_hours=None, personal_chat=None, **kwargs): + business_opening_hours=None, personal_chat=None, birthdate=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -793,6 +798,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.business_location: BusinessLocation = business_location self.business_opening_hours: BusinessOpeningHours = business_opening_hours self.personal_chat: Chat = personal_chat + self.birthdate: Birthdate = birthdate @@ -9682,3 +9688,34 @@ def __init__(self, user_id, first_name=None, last_name=None, username=None, phot self.last_name: Optional[str] = last_name self.username: Optional[str] = username self.photo: Optional[List[PhotoSize]] = photo + + +class Birthdate(JsonDeserializable): + """ + This object represents a user's birthdate. + + Telegram documentation: https://core.telegram.org/bots/api#birthdate + + :param day: Day of the user's birth; 1-31 + :type day: :obj:`int` + + :param month: Month of the user's birth; 1-12 + :type month: :obj:`int` + + :param year: Optional. Year of the user's birth + :type year: :obj:`int` + + :return: Instance of the class + :rtype: :class:`Birthdate` + """ + + @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, day, month, year=None, **kwargs): + self.day: int = day + self.month: int = month + self.year: Optional[int] = year \ No newline at end of file From 2f21c90e6efd5e7fb3d6a990ae9ff280a5cfe3a1 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:36:09 +0500 Subject: [PATCH 1520/1808] fix f*** tests --- tests/test_handler_backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index fa893c489..bb541bf8a 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -71,7 +71,7 @@ def update_type(message): chat_boost_removed = None return types.Update(1001234038283, 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, chat_boost, chat_boost_removed) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed, None, None, None, None) @pytest.fixture() @@ -95,7 +95,7 @@ def reply_to_message_update_type(reply_to_message): chat_boost_removed = None return types.Update(1001234038284, reply_to_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, chat_boost, chat_boost_removed) + poll, poll_answer, my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed, None, None, None, None) def next_handler(message): From 027d9d40e0d02d716c4b93661f7b584131124569 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Apr 2024 02:53:10 +0500 Subject: [PATCH 1521/1808] Fixing dumb issues --- telebot/__init__.py | 6 +++--- telebot/async_telebot.py | 23 ++++++++++++++++++++--- telebot/util.py | 1 + 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0d6e2aba0..fbd06b440 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -8060,7 +8060,7 @@ def decorator(handler): regexp=regexp, func=func, **kwargs) - self.add_message_handler(handler_dict) + self.add_business_message_handler(handler_dict) return handler return decorator @@ -8151,7 +8151,7 @@ def decorator(handler): regexp=regexp, func=func, **kwargs) - self.add_edited_message_handler(handler_dict) + self.add_edited_business_message_handler(handler_dict) return handler return decorator @@ -8167,7 +8167,7 @@ def add_edited_business_message_handler(self, handler_dict): :param handler_dict: :return: """ - self.edited_message_handlers.append(handler_dict) + self.edited_business_message_handlers.append(handler_dict) def register_edited_business_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 57ae8e6a4..df46813f5 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -999,6 +999,23 @@ def setup_middleware(self, middleware: BaseMiddleware): self.middlewares.append(middleware) + @staticmethod + def check_commands_input(commands, method_name): + """ + :meta private: + """ + if not isinstance(commands, list) or not all(isinstance(item, str) for item in commands): + logger.error(f"{method_name}: Commands filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use the supplied type.") + + + @staticmethod + def check_regexp_input(regexp, method_name): + """ + :meta private: + """ + if not isinstance(regexp, str): + logger.error(f"{method_name}: Regexp filter should be string. Not able to use the supplied type.") + def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ Handles ew incoming message of any kind - text, photo, sticker, etc. @@ -2308,7 +2325,7 @@ def decorator(handler): regexp=regexp, func=func, **kwargs) - self.add_message_handler(handler_dict) + self.add_business_message_handler(handler_dict) return handler return decorator @@ -2399,7 +2416,7 @@ def decorator(handler): regexp=regexp, func=func, **kwargs) - self.add_edited_message_handler(handler_dict) + self.add_edited_business_message_handler(handler_dict) return handler return decorator @@ -2415,7 +2432,7 @@ def add_edited_business_message_handler(self, handler_dict): :param handler_dict: :return: """ - self.edited_message_handlers.append(handler_dict) + self.edited_business_message_handlers.append(handler_dict) def register_edited_business_message_handler(self, callback: Callable, content_types: Optional[List[str]]=None, diff --git a/telebot/util.py b/telebot/util.py index 94622fcb7..295c0d1aa 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -48,6 +48,7 @@ "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", "chat_boost", "removed_chat_boost", + "business_connection", "business_message", "edited_business_message", "deleted_business_messages" ] From caf3df7a443d51db7f98e9f41c0317f4e6cb5eaa Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Apr 2024 03:11:21 +0500 Subject: [PATCH 1522/1808] made format optional --- telebot/__init__.py | 15 ++++++++++++--- telebot/async_telebot.py | 14 +++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fbd06b440..d1432726b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5543,12 +5543,12 @@ def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types. ) - def set_sticker_set_thumbnail(self, name: str, user_id: int, format: str, thumbnail: Union[Any, str]=None): + 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. Animated thumbnails can be set for animated sticker sets only. Returns True on success. - Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb + Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumbnail :param name: Sticker set name :type name: :obj:`str` @@ -5556,7 +5556,12 @@ def set_sticker_set_thumbnail(self, name: str, user_id: int, format: str, thumbn :param user_id: User identifier :type user_id: :obj:`int` - :param thumbnail: A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements for video sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. + :param thumbnail: A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation + with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), + or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements for video sticker technical + requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via + HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. :type thumbnail: :obj:`filelike object` :param format: Format of the thumbnail, must be one of “static” for a .WEBP or .PNG image, “animated” for a .TGS animation, or “video” for a WEBM video @@ -5565,6 +5570,10 @@ def set_sticker_set_thumbnail(self, name: str, user_id: int, format: str, thumbn :return: On success, True is returned. :rtype: :obj:`bool` """ + if format is None: + logger.warning("Format in set_sticker_set_thumbnail cannot be None. Setting format to 'static'.") + format = "static" + return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index df46813f5..e2d001659 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6883,12 +6883,12 @@ async def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> return types.UserChatBoosts.de_json(result) - async def set_sticker_set_thumbnail(self, name: str, user_id: int, format: str, thumbnail: Union[Any, str]=None): + async 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. Animated thumbnails can be set for animated sticker sets only. Returns True on success. - Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb + Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumbnail :param name: Sticker set name :type name: :obj:`str` @@ -6896,12 +6896,20 @@ async def set_sticker_set_thumbnail(self, name: str, user_id: int, format: str, :param user_id: User identifier :type user_id: :obj:`int` - :param thumbnail: A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements for video sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. + :param thumbnail: A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation + with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animated-sticker-requirements for animated sticker technical requirements), + or a WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-sticker-requirements for video sticker technical + requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via + HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail. :type thumbnail: :obj:`filelike object` :return: On success, True is returned. :rtype: :obj:`bool` """ + if format is None: + logger.warning("Format in set_sticker_set_thumbnail cannot be None. Setting format to 'static'.") + format = "static" return await asyncio_helper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") From 325da2fa0fc0cb6f845aafdfba956d44c76797c2 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 5 Apr 2024 03:37:37 +0500 Subject: [PATCH 1523/1808] Added all_states for StatesGroup (#2215) * Update asyncio_handler_backends.py * Update handler_backends.py * Update handler_backends.py * Update asyncio_handler_backends.py --- telebot/asyncio_handler_backends.py | 2 +- telebot/handler_backends.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index a565023fc..0861a9893 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -83,7 +83,7 @@ def __init_subclass__(cls) -> None: state_list.append(value) cls._state_list = state_list - @property + @classmethod def state_list(self): return self._state_list diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index 2e4f86eab..b95861e0b 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -194,7 +194,7 @@ def __init_subclass__(cls) -> None: state_list.append(value) cls._state_list = state_list - @property + @classmethod def state_list(self): return self._state_list From 2ee6c7f3fdc2109f335da9b48a621fde5a82b631 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 5 Apr 2024 15:38:40 +0300 Subject: [PATCH 1524/1808] Bot API 7.2 u1 - Removed the fields is_animated and is_video from the class [StickerSet](https://core.telegram.org/bots/api#stickerset). - Removed the parameter sticker_format from the method [createNewStickerSet](https://core.telegram.org/bots/api#createnewstickerset). --- telebot/__init__.py | 23 +++++++++++++++++------ telebot/apihelper.py | 12 ++++++------ telebot/async_telebot.py | 11 ++++++----- telebot/asyncio_helper.py | 15 ++++++++------- telebot/types.py | 23 ++++++++++++++++------- 5 files changed, 53 insertions(+), 31 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d1432726b..4c3040d84 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -213,6 +213,7 @@ def __init__( # handlers self.exception_handler = exception_handler self.update_listener = [] + self.message_handlers = [] self.edited_message_handlers = [] self.channel_post_handlers = [] @@ -247,6 +248,8 @@ def __init__( 'edited_message': [], 'channel_post': [], 'edited_channel_post': [], + 'message_reaction': [], + 'message_reaction_count': [], 'inline_query': [], 'chosen_inline_result': [], 'callback_query': [], @@ -256,7 +259,13 @@ def __init__( 'poll_answer': [], 'my_chat_member': [], 'chat_member': [], - 'chat_join_request': [] + 'chat_join_request': [], + 'chat_boost': [], + 'removed_chat_boost': [], + 'business_connection': [], + 'business_message': [], + 'edited_business_message': [], + 'deleted_business_messages': [], } self.default_middleware_handlers = [] if apihelper.ENABLE_MIDDLEWARE and use_class_middlewares: @@ -5570,8 +5579,8 @@ def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[An :return: On success, True is returned. :rtype: :obj:`bool` """ - if format is None: - logger.warning("Format in set_sticker_set_thumbnail cannot be None. Setting format to 'static'.") + if not format: + logger.warning("Deprecation warning. 'format' parameter is required in set_sticker_set_thumbnail. Setting format to 'static'.") format = "static" return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) @@ -5856,7 +5865,7 @@ def create_new_sticker_set( :param stickers: List of stickers to be added to the set :type stickers: :obj:`list` of :class:`telebot.types.InputSticker` - :param sticker_format: Format of stickers in the set, must be one of “static”, “animated”, “video” + :param sticker_format: deprecated :type sticker_format: :obj:`str` :return: On success, True is returned. @@ -5880,9 +5889,11 @@ def create_new_sticker_set( raise ValueError('You must pass at least one sticker') stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] + if sticker_format: + logger.warning('The parameter "sticker_format" is deprecated.') + return apihelper.create_new_sticker_set( - self.token, user_id, name, title, stickers, sticker_format, sticker_type=sticker_type, - needs_repainting=needs_repainting) + self.token, user_id, name, title, stickers, sticker_type=sticker_type, needs_repainting=needs_repainting) def add_sticker_to_set( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1b3a6df99..3f537065c 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1708,23 +1708,26 @@ def set_sticker_set_title(token, name, title): payload = {'name': name, 'title': title} return _make_request(token, method_url, params=payload, method='post') + def delete_sticker_set(token, name): method_url = 'deleteStickerSet' payload = {'name': name} return _make_request(token, method_url, params=payload, method='post') + def set_sticker_emoji_list(token, sticker, emoji_list): method_url = 'setStickerEmojiList' payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( - token, user_id, name, title, stickers, sticker_format, sticker_type=None, needs_repainting=None): + token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' - payload = {'user_id': user_id, 'name': name, 'title': title, 'sticker_format': sticker_format} + payload = {'user_id': user_id, 'name': name, 'title': title} if sticker_type: payload['sticker_type'] = sticker_type - if needs_repainting: + if needs_repainting is not None: payload['needs_repainting'] = needs_repainting files = {} @@ -1741,8 +1744,6 @@ def create_new_sticker_set( payload['stickers'] = json.dumps(lst) - - return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1750,7 +1751,6 @@ def add_sticker_to_set(token, user_id, name, sticker): method_url = 'addStickerToSet' json_dict, files = sticker.convert_input_sticker() payload = {'user_id': user_id, 'name': name, 'sticker': json_dict} - return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e2d001659..daeb55d5c 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6907,8 +6907,8 @@ async def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Un :return: On success, True is returned. :rtype: :obj:`bool` """ - if format is None: - logger.warning("Format in set_sticker_set_thumbnail cannot be None. Setting format to 'static'.") + if not format: + logger.warning("Deprecation warning. 'format' parameter is required in set_sticker_set_thumbnail. Setting format to 'static'.") format = "static" return await asyncio_helper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) @@ -7184,7 +7184,7 @@ async def create_new_sticker_set( :param stickers: List of stickers to be added to the set :type stickers: :obj:`list` of :class:`telebot.types.InputSticker` - :param sticker_format: Format of stickers in the set, must be one of “static”, “animated”, “video” + :param sticker_format: deprecated :type sticker_format: :obj:`str` :return: On success, True is returned. @@ -7208,10 +7208,11 @@ async def create_new_sticker_set( raise ValueError('You must pass at least one sticker') stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] + if sticker_format: + logger.warning('The parameter "sticker_format" is deprecated.') - return await asyncio_helper.create_new_sticker_set( - self.token, user_id, name, title, stickers, sticker_format, sticker_type, needs_repainting) + self.token, user_id, name, title, stickers, sticker_type, needs_repainting) async def add_sticker_to_set( diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index e1fcdec36..3aadccc65 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1481,7 +1481,7 @@ async def send_invoice( send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None,reply_parameters=None): + protect_content=None, message_thread_id=None, reply_parameters=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1513,6 +1513,7 @@ async def send_invoice( 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' @@ -1648,7 +1649,7 @@ async def answer_inline_query(token, inline_query_id, results, cache_time=None, payload['next_offset'] = next_offset if button is not None: payload["button"] = button.to_json() - + return await _process_request(token, method_url, params=payload, method='post') @@ -1706,12 +1707,12 @@ async def set_sticker_set_title(token, name, title): return await _process_request(token, method_url, params=payload, method='post') async def create_new_sticker_set( - token, user_id, name, title, stickers, sticker_format, sticker_type=None, needs_repainting=None): + token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' - payload = {'user_id': user_id, 'name': name, 'title': title, 'sticker_format': sticker_format} + payload = {'user_id': user_id, 'name': name, 'title': title} if sticker_type: payload['sticker_type'] = sticker_type - if needs_repainting: + if needs_repainting is not None: payload['needs_repainting'] = needs_repainting files = {} @@ -1728,7 +1729,7 @@ async def create_new_sticker_set( payload['stickers'] = json.dumps(lst) - + return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1736,7 +1737,7 @@ async def add_sticker_to_set(token, user_id, name, sticker): method_url = 'addStickerToSet' json_dict, files = sticker.convert_input_sticker() payload = {'user_id': user_id, 'name': name, 'sticker': json_dict} - + return await _process_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/types.py b/telebot/types.py index 1d104a098..9e3af63ec 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -900,7 +900,6 @@ class Message(JsonDeserializable): :type forward_from_chat: :class:`telebot.types.Chat` :param forward_from_message_id: deprecated. - message in the channel :type forward_from_message_id: :obj:`int` :param forward_signature: deprecated. @@ -6396,10 +6395,10 @@ class StickerSet(JsonDeserializable): :param sticker_type: Type of stickers in the set, currently one of “regular”, “mask”, “custom_emoji” :type sticker_type: :obj:`str` - :param is_animated: True, if the sticker set contains animated stickers + :param is_animated: deprecated :type is_animated: :obj:`bool` - :param is_video: True, if the sticker set contains video stickers + :param is_video: deprecated :type is_video: :obj:`bool` :param contains_masks: deprecated @@ -6428,15 +6427,20 @@ def de_json(cls, json_string): obj['thumbnail'] = None return cls(**obj) - def __init__(self, name, title, sticker_type, is_animated, is_video, stickers, thumbnail=None, **kwargs): + def __init__(self, name, title, sticker_type, is_animated=None, is_video=None, stickers=None, thumbnail=None, **kwargs): self.name: str = name self.title: str = title self.sticker_type: str = sticker_type - self.is_animated: bool = is_animated - self.is_video: bool = is_video self.stickers: List[Sticker] = stickers self.thumbnail: PhotoSize = thumbnail + if is_animated is not None: + logger.warning('The parameter "is_animated" is deprecated.') + if is_video is not None: + logger.warning('The parameter "is_video" is deprecated.') + if stickers is None: + raise ValueError('The parameter "stickers" is required for StickerSet.') + @property def thumb(self): logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') @@ -8033,13 +8037,18 @@ class InputSticker(Dictionaryable, JsonSerializable): :rtype: :class:`telebot.types.InputSticker` """ - def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], format: str, mask_position: Optional[MaskPosition]=None, keywords: Optional[List[str]]=None) -> None: + def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], format: Optional[str]=None, + mask_position: Optional[MaskPosition]=None, keywords: Optional[List[str]]=None) -> None: self.sticker: Union[str, InputFile] = sticker self.emoji_list: List[str] = emoji_list self.mask_position: Optional[MaskPosition] = mask_position self.keywords: Optional[List[str]] = keywords self.format: str = format + if not self.format: + logger.warning("Deprecation warning. 'format' parameter is required in InputSticker. Setting format to 'static'.") + self.format = "static" + if service_utils.is_string(self.sticker): self._sticker_name = '' self._sticker_dic = self.sticker From 96c9768d0a2554dafd98b09e3b70e2455a20aded Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 5 Apr 2024 18:56:09 +0300 Subject: [PATCH 1525/1808] Update telebot/__init__.py Co-authored-by: _run --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4c3040d84..0dde90f1b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5890,7 +5890,7 @@ def create_new_sticker_set( stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] if sticker_format: - logger.warning('The parameter "sticker_format" is deprecated.') + logger.warning('The parameter "sticker_format" is deprecated since Bot API 7.2+. Stickers can now be mixed') return apihelper.create_new_sticker_set( self.token, user_id, name, title, stickers, sticker_type=sticker_type, needs_repainting=needs_repainting) From 1d6d60fef85fa708bf0b4fffbe430c5bf507b463 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 5 Apr 2024 21:56:14 +0300 Subject: [PATCH 1526/1808] Update StickerSet to new signature. --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- telebot/types.py | 34 +++++++++++++++++----------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0dde90f1b..823324359 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5890,7 +5890,7 @@ def create_new_sticker_set( stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] if sticker_format: - logger.warning('The parameter "sticker_format" is deprecated since Bot API 7.2+. Stickers can now be mixed') + logger.warning('The parameter "sticker_format" is deprecated since Bot API 7.2. Stickers can now be mixed') return apihelper.create_new_sticker_set( self.token, user_id, name, title, stickers, sticker_type=sticker_type, needs_repainting=needs_repainting) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index daeb55d5c..d24a025dd 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7209,7 +7209,7 @@ async def create_new_sticker_set( stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] if sticker_format: - logger.warning('The parameter "sticker_format" is deprecated.') + logger.warning('The parameter "sticker_format" is deprecated since Bot API 7.2. Stickers can now be mixed') return await asyncio_helper.create_new_sticker_set( self.token, user_id, name, title, stickers, sticker_type, needs_repainting) diff --git a/telebot/types.py b/telebot/types.py index 9e3af63ec..f05df3cad 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6395,15 +6395,6 @@ class StickerSet(JsonDeserializable): :param sticker_type: Type of stickers in the set, currently one of “regular”, “mask”, “custom_emoji” :type sticker_type: :obj:`str` - :param is_animated: deprecated - :type is_animated: :obj:`bool` - - :param is_video: deprecated - :type is_video: :obj:`bool` - - :param contains_masks: deprecated - :type contains_masks: :obj:`bool` - :param stickers: List of all set stickers :type stickers: :obj:`list` of :class:`telebot.types.Sticker` @@ -6427,20 +6418,13 @@ def de_json(cls, json_string): obj['thumbnail'] = None return cls(**obj) - def __init__(self, name, title, sticker_type, is_animated=None, is_video=None, stickers=None, thumbnail=None, **kwargs): + def __init__(self, name, title, sticker_type, stickers, thumbnail=None, **kwargs): self.name: str = name self.title: str = title self.sticker_type: str = sticker_type self.stickers: List[Sticker] = stickers self.thumbnail: PhotoSize = thumbnail - if is_animated is not None: - logger.warning('The parameter "is_animated" is deprecated.') - if is_video is not None: - logger.warning('The parameter "is_video" is deprecated.') - if stickers is None: - raise ValueError('The parameter "stickers" is required for StickerSet.') - @property def thumb(self): logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') @@ -6454,6 +6438,22 @@ def contains_masks(self): logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type instead"') return self.sticker_type == 'mask' + @property + def is_animated(self): + """ + Deprecated since Bot API 7.2. Stickers can be mixed now. + """ + logger.warning('The parameter "is_animated" is deprecated since Bot API 7.2. Stickers can now be mixed') + return False + + @property + def is_video(self): + """ + Deprecated since Bot API 7.2. Stickers can be mixed now. + """ + logger.warning('The parameter "is_video" is deprecated since Bot API 7.2. Stickers can now be mixed') + return False + class Sticker(JsonDeserializable): """ From 9cb808a02230bf72c0bbbea084909f1351fdd305 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 5 Apr 2024 22:00:44 +0300 Subject: [PATCH 1527/1808] Clear old deprecated fields. --- telebot/types.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f05df3cad..ecedf69f2 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -893,24 +893,6 @@ class Message(JsonDeserializable): :param chat: Conversation the message belongs to :type chat: :class:`telebot.types.Chat` - :param forward_from: deprecated. - :type forward_from: :class:`telebot.types.User` - - :param forward_from_chat: deprecated. - :type forward_from_chat: :class:`telebot.types.Chat` - - :param forward_from_message_id: deprecated. - :type forward_from_message_id: :obj:`int` - - :param forward_signature: deprecated. - :type forward_signature: :obj:`str` - - :param forward_sender_name: deprecated. - :type forward_sender_name: :obj:`str` - - :param forward_date: deprecated. - :type forward_date: :obj:`int` - :forward_origin: Optional. For forwarded messages, information about the original message; :type forward_origin: :class:`telebot.types.MessageOrigin` From e8074eb2747628189a1c06fd428b1643a4115e10 Mon Sep 17 00:00:00 2001 From: Artem Lavrenov Date: Sat, 6 Apr 2024 16:47:40 +0300 Subject: [PATCH 1528/1808] Example bot using message reactions (emoji) --- .../message_reaction_example.py | 35 +++++++++++++++++++ examples/message_reaction_example.py | 33 +++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 examples/asynchronous_telebot/message_reaction_example.py create mode 100644 examples/message_reaction_example.py diff --git a/examples/asynchronous_telebot/message_reaction_example.py b/examples/asynchronous_telebot/message_reaction_example.py new file mode 100644 index 000000000..a3e078f60 --- /dev/null +++ b/examples/asynchronous_telebot/message_reaction_example.py @@ -0,0 +1,35 @@ +#!/usr/bin/python3 + +# This is a simple bot using message reactions (emoji) +# https://core.telegram.org/bots/api#reactiontype +# https://core.telegram.org/bots/api#update +# allowed_updates: Specify an empty list to receive all update types except, chat_member, message_reaction, and message_reaction_count. +# If you want to receive message_reaction events, you cannot simply leave the allowed_updates=None default value. +# The default list of events does not include chat_member, message_reaction, and message_reaction_count events. +# You must explicitly specify all the events you wish to receive and add message_reaction to this list. +# It’s also important to note that after using allowed_updates=[...], in the future, using allowed_updates=None will mean +# that the list of events you will receive will consist of the events you last explicitly specified. + +import random +from telebot.async_telebot import AsyncTeleBot +from telebot.types import ReactionTypeEmoji + +API_TOKEN = '' +bot = AsyncTeleBot(API_TOKEN) + + + +# Send a reactions to all messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +async def send_reaction(message): + emo = ["\U0001F525", "\U0001F917", "\U0001F60E"] # or use ["🔥", "🤗", "😎"] + await bot.set_message_reaction(message.chat.id, message.id, [ReactionTypeEmoji(random.choice(emo))], is_big=False) + + +@bot.message_reaction_handler(func=lambda message: True) +async def get_reactions(message): + await bot.reply_to(message, f"You changed the reaction from {[r.emoji for r in message.old_reaction]} to {[r.emoji for r in message.new_reaction]}") + + +import asyncio +asyncio.run(bot.infinity_polling(allowed_updates=['message', 'message_reaction'])) diff --git a/examples/message_reaction_example.py b/examples/message_reaction_example.py new file mode 100644 index 000000000..65178d25a --- /dev/null +++ b/examples/message_reaction_example.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 + +# This is a simple bot using message reactions (emoji) +# https://core.telegram.org/bots/api#reactiontype +# https://core.telegram.org/bots/api#update +# allowed_updates: Specify an empty list to receive all update types except, chat_member, message_reaction, and message_reaction_count. +# If you want to receive message_reaction events, you cannot simply leave the allowed_updates=None default value. +# The default list of events does not include chat_member, message_reaction, and message_reaction_count events. +# You must explicitly specify all the events you wish to receive and add message_reaction to this list. +# It’s also important to note that after using allowed_updates=[...], in the future, using allowed_updates=None will mean +# that the list of events you will receive will consist of the events you last explicitly specified. + +import random +import telebot +from telebot.types import ReactionTypeEmoji + +API_TOKEN = '' +bot = telebot.TeleBot(API_TOKEN) + + +# Send a reactions to all messages with content_type 'text' (content_types defaults to ['text']) +@bot.message_handler(func=lambda message: True) +def send_reaction(message): + emo = ["\U0001F525", "\U0001F917", "\U0001F60E"] # or use ["🔥", "🤗", "😎"] + bot.set_message_reaction(message.chat.id, message.id, [ReactionTypeEmoji(random.choice(emo))], is_big=False) + + +@bot.message_reaction_handler(func=lambda message: True) +def get_reactions(message): + bot.reply_to(message, f"You changed the reaction from {[r.emoji for r in message.old_reaction]} to {[r.emoji for r in message.new_reaction]}") + + +bot.infinity_polling(allowed_updates=['message', 'message_reaction']) From d075596d07822e848a053ad8b73eae8b9d6a0e07 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Tue, 9 Apr 2024 10:51:20 +0300 Subject: [PATCH 1529/1808] Bump version --- 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 c9edf448c..423df2324 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'coder2020official' # The full version, including alpha/beta/rc tags -release = '4.16.1' +release = '4.17.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 68f0254ea..fde7a8685 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.16.1" +version = "4.17.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 0b4b31ee0..3eb8b787f 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.16.1' +__version__ = '4.17.0' From 04e815cd91a9eca721984aa65e8f85476f24b34a Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Wed, 10 Apr 2024 21:01:17 +0400 Subject: [PATCH 1530/1808] added method html_text in TextQuote --- telebot/types.py | 104 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_types.py | 4 +- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 498e1097c..b1630fd1b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8988,6 +8988,110 @@ def __init__(self, text: str, position: int, self.position: Optional[int] = position self.is_manual: Optional[bool] = is_manual + def __html_text(self, text, entities): + """ + Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" + + .. code-block:: python3 + :caption: Example: + + message.quote.html_text + >> "Test parse formatting, url, text_mention and mention @username" + + Custom subs: + You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. + .. code-block:: python3 + :caption: Example: + + message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} + message.html_text + >> "Test parse formatting, url and text_mention and mention @username" + """ + + if not entities: + return text.replace("&", "&").replace("<", "<").replace(">", ">") + + _subs = { + "bold": "{text}", + "italic": "{text}", + "pre": "
{text}
", + "code": "{text}", + "text_link": "{text}", + "strikethrough": "{text}", + "underline": "{text}", + "spoiler": "{text}", + "custom_emoji": "{text}", + "blockquote": "
{text}
", + } + + if hasattr(self, "custom_subs"): + for key, value in self.custom_subs.items(): + _subs[key] = value + utf16_text = text.encode("utf-16-le") + html_text = "" + + def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): + upd_text = upd_text.decode("utf-16-le") + if subst_type == "text_mention": + subst_type = "text_link" + url = "tg://user?id={0}".format(user.id) + elif subst_type == "mention": + url = "https://t.me/{0}".format(upd_text[1:]) + upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") + if not subst_type or not _subs.get(subst_type): + return upd_text + subs = _subs.get(subst_type) + if subst_type == "custom_emoji": + return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) + return subs.format(text=upd_text, url=url) + + offset = 0 + start_index = 0 + end_index = 0 + for entity in entities: + if entity.offset > offset: + # when the offset is not 0: for example, a __b__ + # we need to add the text before the entity to the html_text + html_text += func(utf16_text[offset * 2: entity.offset * 2]) + offset = entity.offset + + new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string + offset += entity.length + end_index = len(html_text) + elif entity.offset == offset: + new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string + end_index = len(html_text) + offset += entity.length + else: + # Here we are processing nested entities. + # We shouldn't update offset, because they are the same as entity before. + # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, + # And we don't change it). + entity_string = html_text[start_index: end_index].encode("utf-16-le") + formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, + custom_emoji_id=entity.custom_emoji_id). \ + replace("&", "&").replace("<", "<").replace(">", ">") + html_text = html_text[:start_index] + formatted_string + html_text[end_index:] + end_index = len(html_text) + + if offset * 2 < len(utf16_text): + html_text += func(utf16_text[offset * 2:]) + + return html_text + + @property + def html_text(self): + """ + Returns html-rendered text. + """ + return self.__html_text(self.text, self.entities) + class ReplyParameters(JsonDeserializable, Dictionaryable, JsonSerializable): """ diff --git a/tests/test_types.py b/tests/test_types.py index 06bcae1ee..197834748 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -306,6 +306,8 @@ def test_message_entity(): assert message_6.html_text == "<b>b</b> <i>i</i>" - + sample_string_7 = r'{"update_id":934522167,"message":{"message_id":1374526,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179716,"reply_to_message":{"message_id":1374510,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en"},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1712765863,"text":"text @UserName b i s u c p #hashtag https://example.com","entities":[{"offset":5,"length":9,"type":"mention"},{"offset":15,"length":1,"type":"bold"},{"offset":17,"length":1,"type":"italic"},{"offset":19,"length":1,"type":"strikethrough"},{"offset":21,"length":1,"type":"underline"},{"offset":23,"length":1,"type":"code"},{"offset":25,"length":1,"type":"spoiler"},{"offset":27,"length":8,"type":"hashtag"},{"offset":36,"length":19,"type":"url"}],"link_preview_options":{"is_disabled":true}},"quote":{"text":"text @UserName b i s u c p #hashtag https://example.com","entities":[{"offset":15,"length":1,"type":"bold"},{"offset":17,"length":1,"type":"italic"},{"offset":19,"length":1,"type":"strikethrough"},{"offset":21,"length":1,"type":"underline"},{"offset":25,"length":1,"type":"spoiler"}],"position":0,"is_manual":true},"text":"quote reply"}}' + message_7 = types.Update.de_json(sample_string_7).message + assert message_7.quote.html_text == 'text @UserName b i s u c p #hashtag https://example.com' From e7d3b979fd01edca513d4ea31aaecc4a5b67361a Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Fri, 12 Apr 2024 21:31:38 +0400 Subject: [PATCH 1531/1808] Renamed telebot.types.Message.__html_text to apply_html_entities and moved it to telebot/formatting --- telebot/formatting.py | 111 +++++++++++++++++++++-- telebot/types.py | 203 ++---------------------------------------- 2 files changed, 111 insertions(+), 203 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index c0f442013..3ececdd65 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -4,11 +4,9 @@ .. versionadded:: 4.5.1 """ -import html - import re - -from typing import Optional +import html +from typing import Optional, List def format_text(*args, separator="\n"): @@ -34,7 +32,6 @@ def format_text(*args, separator="\n"): return separator.join(args) - def escape_html(content: str) -> str: """ Escapes HTML characters in a string of HTML. @@ -66,6 +63,110 @@ def escape_markdown(content: str) -> str: return reparse +def apply_html_entities(text: str, entities: Optional[List], custom_subs: Optional[dict[str, str]]) -> str: + """ + Author: @sviat9440 + Updaters: @badiboy, @EgorKhabarov + Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" + + .. code-block:: python3 + :caption: Example: + + apply_html_entities(text, entities) + >> "Test parse formatting, url, text_mention and mention @username" + + Custom subs: + You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. + .. code-block:: python3 + :caption: Example: + + apply_html_entities( + text, + entities, + {"bold": "{text}", "italic": "{text}", "mention": "{text}"}, + ) + >> "Test parse formatting, url and text_mention and mention @username" + """ + + if not entities: + return text.replace("&", "&").replace("<", "<").replace(">", ">") + + _subs = { + "bold": "{text}", + "italic": "{text}", + "pre": "
{text}
", + "code": "{text}", + # "url": "{text}", # @badiboy plain URLs have no text and do not need tags + "text_link": "{text}", + "strikethrough": "{text}", + "underline": "{text}", + "spoiler": "{text}", + "custom_emoji": "{text}", + "blockquote": "
{text}
", + } + + if custom_subs: + for key, value in custom_subs.items(): + _subs[key] = value + utf16_text = text.encode("utf-16-le") + html_text = "" + + def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): + upd_text = upd_text.decode("utf-16-le") + if subst_type == "text_mention": + subst_type = "text_link" + url = "tg://user?id={0}".format(user.id) + elif subst_type == "mention": + url = "https://t.me/{0}".format(upd_text[1:]) + upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") + if not subst_type or not _subs.get(subst_type): + return upd_text + subs = _subs.get(subst_type) + if subst_type == "custom_emoji": + return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) + return subs.format(text=upd_text, url=url) + + offset = 0 + start_index = 0 + end_index = 0 + for entity in entities: + if entity.offset > offset: + # when the offset is not 0: for example, a __b__ + # we need to add the text before the entity to the html_text + html_text += func(utf16_text[offset * 2: entity.offset * 2]) + offset = entity.offset + + new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string + offset += entity.length + end_index = len(html_text) + elif entity.offset == offset: + new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string + end_index = len(html_text) + offset += entity.length + else: + # Here we are processing nested entities. + # We shouldn't update offset, because they are the same as entity before. + # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, + # And we don't change it). + entity_string = html_text[start_index: end_index].encode("utf-16-le") + formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, + custom_emoji_id=entity.custom_emoji_id). \ + replace("&", "&").replace("<", "<").replace(">", ">") + html_text = html_text[:start_index] + formatted_string + html_text[end_index:] + end_index = len(html_text) + + if offset * 2 < len(utf16_text): + html_text += func(utf16_text[offset * 2:]) + + return html_text + + def mbold(content: str, escape: Optional[bool]=True) -> str: """ Returns a Markdown-formatted bold string. diff --git a/telebot/types.py b/telebot/types.py index b1630fd1b..7c2782788 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -14,6 +14,8 @@ import json from telebot import service_utils +from telebot.formatting import apply_html_entities + DISABLE_KEYLEN_ERROR = False @@ -1474,117 +1476,19 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso setattr(self, key, options[key]) self.json = json_string - - def __html_text(self, text, entities): - """ - Author: @sviat9440 - Updaters: @badiboy, @EgorKhabarov - Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" - - .. code-block:: python3 - :caption: Example: - - message.html_text - >> "Test parse formatting, url, text_mention and mention @username" - - Custom subs: - You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. - .. code-block:: python3 - :caption: Example: - - message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} - message.html_text - >> "Test parse formatting, url and text_mention and mention @username" - """ - - if not entities: - return text.replace("&", "&").replace("<", "<").replace(">", ">") - - _subs = { - "bold": "{text}", - "italic": "{text}", - "pre": "
{text}
", - "code": "{text}", - # "url": "{text}", # @badiboy plain URLs have no text and do not need tags - "text_link": "{text}", - "strikethrough": "{text}", - "underline": "{text}", - "spoiler": "{text}", - "custom_emoji": "{text}", - "blockquote": "
{text}
", - } - - if hasattr(self, "custom_subs"): - for key, value in self.custom_subs.items(): - _subs[key] = value - utf16_text = text.encode("utf-16-le") - html_text = "" - - def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): - upd_text = upd_text.decode("utf-16-le") - if subst_type == "text_mention": - subst_type = "text_link" - url = "tg://user?id={0}".format(user.id) - elif subst_type == "mention": - url = "https://t.me/{0}".format(upd_text[1:]) - upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") - if not subst_type or not _subs.get(subst_type): - return upd_text - subs = _subs.get(subst_type) - if subst_type == "custom_emoji": - return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) - return subs.format(text=upd_text, url=url) - - offset = 0 - start_index = 0 - end_index = 0 - for entity in entities: - if entity.offset > offset: - # when the offset is not 0: for example, a __b__ - # we need to add the text before the entity to the html_text - html_text += func(utf16_text[offset * 2 : entity.offset * 2]) - offset = entity.offset - - new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], subst_type=entity.type, url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) - start_index = len(html_text) - html_text += new_string - offset += entity.length - end_index = len(html_text) - elif entity.offset == offset: - new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], subst_type=entity.type, url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) - start_index = len(html_text) - html_text += new_string - end_index = len(html_text) - offset += entity.length - else: - # Here we are processing nested entities. - # We shouldn't update offset, because they are the same as entity before. - # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, - # And we don't change it). - entity_string = html_text[start_index : end_index].encode("utf-16-le") - formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id).\ - replace("&", "&").replace("<", "<").replace(">",">") - html_text = html_text[:start_index] + formatted_string + html_text[end_index:] - end_index = len(html_text) - - if offset * 2 < len(utf16_text): - html_text += func(utf16_text[offset * 2:]) - - return html_text - @property def html_text(self): """ Returns html-rendered text. """ - return self.__html_text(self.text, self.entities) + return apply_html_entities(self.text, self.entities, getattr(self, "custom_subs", None)) @property def html_caption(self): """ Returns html-rendered caption. """ - return self.__html_text(self.caption, self.caption_entities) + return apply_html_entities(self.caption, self.caption_entities, getattr(self, "custom_subs", None)) @property def voice_chat_scheduled(self): @@ -8988,109 +8892,12 @@ def __init__(self, text: str, position: int, self.position: Optional[int] = position self.is_manual: Optional[bool] = is_manual - def __html_text(self, text, entities): - """ - Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" - - .. code-block:: python3 - :caption: Example: - - message.quote.html_text - >> "Test parse formatting, url, text_mention and mention @username" - - Custom subs: - You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. - .. code-block:: python3 - :caption: Example: - - message.custom_subs = {"bold": "{text}", "italic": "{text}", "mention": "{text}"} - message.html_text - >> "Test parse formatting, url and text_mention and mention @username" - """ - - if not entities: - return text.replace("&", "&").replace("<", "<").replace(">", ">") - - _subs = { - "bold": "{text}", - "italic": "{text}", - "pre": "
{text}
", - "code": "{text}", - "text_link": "{text}", - "strikethrough": "{text}", - "underline": "{text}", - "spoiler": "{text}", - "custom_emoji": "{text}", - "blockquote": "
{text}
", - } - - if hasattr(self, "custom_subs"): - for key, value in self.custom_subs.items(): - _subs[key] = value - utf16_text = text.encode("utf-16-le") - html_text = "" - - def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): - upd_text = upd_text.decode("utf-16-le") - if subst_type == "text_mention": - subst_type = "text_link" - url = "tg://user?id={0}".format(user.id) - elif subst_type == "mention": - url = "https://t.me/{0}".format(upd_text[1:]) - upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") - if not subst_type or not _subs.get(subst_type): - return upd_text - subs = _subs.get(subst_type) - if subst_type == "custom_emoji": - return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) - return subs.format(text=upd_text, url=url) - - offset = 0 - start_index = 0 - end_index = 0 - for entity in entities: - if entity.offset > offset: - # when the offset is not 0: for example, a __b__ - # we need to add the text before the entity to the html_text - html_text += func(utf16_text[offset * 2: entity.offset * 2]) - offset = entity.offset - - new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, - url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) - start_index = len(html_text) - html_text += new_string - offset += entity.length - end_index = len(html_text) - elif entity.offset == offset: - new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, - url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) - start_index = len(html_text) - html_text += new_string - end_index = len(html_text) - offset += entity.length - else: - # Here we are processing nested entities. - # We shouldn't update offset, because they are the same as entity before. - # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, - # And we don't change it). - entity_string = html_text[start_index: end_index].encode("utf-16-le") - formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, - custom_emoji_id=entity.custom_emoji_id). \ - replace("&", "&").replace("<", "<").replace(">", ">") - html_text = html_text[:start_index] + formatted_string + html_text[end_index:] - end_index = len(html_text) - - if offset * 2 < len(utf16_text): - html_text += func(utf16_text[offset * 2:]) - - return html_text - @property def html_text(self): """ Returns html-rendered text. """ - return self.__html_text(self.text, self.entities) + return apply_html_entities(self.text, self.entities, getattr(self, "custom_subs", None)) class ReplyParameters(JsonDeserializable, Dictionaryable, JsonSerializable): From 870f5e190e26155c68bb8d96e87cdc87995d448f Mon Sep 17 00:00:00 2001 From: qeez Date: Fri, 12 Apr 2024 22:09:00 +0300 Subject: [PATCH 1532/1808] Fix grammar error --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d24a025dd..d023568f8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1018,7 +1018,7 @@ def check_regexp_input(regexp, method_name): def message_handler(self, commands=None, regexp=None, func=None, content_types=None, chat_types=None, **kwargs): """ - Handles ew incoming message of any kind - text, photo, sticker, etc. + Handles new incoming message of any kind - text, photo, sticker, etc. As a parameter to the decorator function, it passes :class:`telebot.types.Message` object. All message handlers are tested in the order they were added. From ebb9a7ff13fa5d5132f79905820648730721c4c8 Mon Sep 17 00:00:00 2001 From: chiri <109767616+chiri1488@users.noreply.github.com> Date: Wed, 17 Apr 2024 10:57:49 +0300 Subject: [PATCH 1533/1808] update: copyright date updating the copyright date from 2023 to 2024 --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 423df2324..f0502dfa6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -18,7 +18,7 @@ # -- Project information ----------------------------------------------------- project = 'pyTelegramBotAPI Documentation' -copyright = '2022-2023, coder2020official' +copyright = '2022-2024, coder2020official' author = 'coder2020official' # The full version, including alpha/beta/rc tags From 01e921e7ccce5e62b91a8703eeac9a305c1f02e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:36:11 +0000 Subject: [PATCH 1534/1808] Bump aiohttp from 3.9.0 to 3.9.4 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.0 to 3.9.4. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.0...v3.9.4) --- updated-dependencies: - dependency-name: aiohttp dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 049b84f84..32cb38aa1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest requests==2.31.0 wheel==0.38.1 -aiohttp==3.9.0 +aiohttp==3.9.4 From dc82acc6bcac72ecdf2428109b102fd2346b13ce Mon Sep 17 00:00:00 2001 From: chiri <109767616+chirizxc@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:22:56 +0300 Subject: [PATCH 1535/1808] automatic updates of copyright year in lib --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f0502dfa6..f2ab5ac09 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,12 +13,12 @@ # import os # import sys # sys.path.insert(0, os.path.abspath('.')) - +from datetime import datetime # -- Project information ----------------------------------------------------- project = 'pyTelegramBotAPI Documentation' -copyright = '2022-2024, coder2020official' +copyright = f'2022-{datetime.now().year}, coder2020official' author = 'coder2020official' # The full version, including alpha/beta/rc tags From e0e5cbb4f4dd232334120793bebe146752b1cd3e Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Fri, 19 Apr 2024 15:53:48 +0400 Subject: [PATCH 1536/1808] Moved the apply_html_entities function to the end of the formatting.py --- telebot/formatting.py | 210 +++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 3ececdd65..a491d9404 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -60,111 +60,7 @@ def escape_markdown(content: str) -> str: parse = re.sub(r"([_*\[\]()~`>\#\+\-=|\.!\{\}\\])", r"\\\1", content) reparse = re.sub(r"\\\\([_*\[\]()~`>\#\+\-=|\.!\{\}\\])", r"\1", parse) - return reparse - - -def apply_html_entities(text: str, entities: Optional[List], custom_subs: Optional[dict[str, str]]) -> str: - """ - Author: @sviat9440 - Updaters: @badiboy, @EgorKhabarov - Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" - - .. code-block:: python3 - :caption: Example: - - apply_html_entities(text, entities) - >> "Test parse formatting, url, text_mention and mention @username" - - Custom subs: - You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. - .. code-block:: python3 - :caption: Example: - - apply_html_entities( - text, - entities, - {"bold": "{text}", "italic": "{text}", "mention": "{text}"}, - ) - >> "Test parse formatting, url and text_mention and mention @username" - """ - - if not entities: - return text.replace("&", "&").replace("<", "<").replace(">", ">") - - _subs = { - "bold": "{text}", - "italic": "{text}", - "pre": "
{text}
", - "code": "{text}", - # "url": "{text}", # @badiboy plain URLs have no text and do not need tags - "text_link": "{text}", - "strikethrough": "{text}", - "underline": "{text}", - "spoiler": "{text}", - "custom_emoji": "{text}", - "blockquote": "
{text}
", - } - - if custom_subs: - for key, value in custom_subs.items(): - _subs[key] = value - utf16_text = text.encode("utf-16-le") - html_text = "" - - def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): - upd_text = upd_text.decode("utf-16-le") - if subst_type == "text_mention": - subst_type = "text_link" - url = "tg://user?id={0}".format(user.id) - elif subst_type == "mention": - url = "https://t.me/{0}".format(upd_text[1:]) - upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") - if not subst_type or not _subs.get(subst_type): - return upd_text - subs = _subs.get(subst_type) - if subst_type == "custom_emoji": - return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) - return subs.format(text=upd_text, url=url) - - offset = 0 - start_index = 0 - end_index = 0 - for entity in entities: - if entity.offset > offset: - # when the offset is not 0: for example, a __b__ - # we need to add the text before the entity to the html_text - html_text += func(utf16_text[offset * 2: entity.offset * 2]) - offset = entity.offset - - new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, - url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) - start_index = len(html_text) - html_text += new_string - offset += entity.length - end_index = len(html_text) - elif entity.offset == offset: - new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, - url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) - start_index = len(html_text) - html_text += new_string - end_index = len(html_text) - offset += entity.length - else: - # Here we are processing nested entities. - # We shouldn't update offset, because they are the same as entity before. - # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, - # And we don't change it). - entity_string = html_text[start_index: end_index].encode("utf-16-le") - formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, - custom_emoji_id=entity.custom_emoji_id). \ - replace("&", "&").replace("<", "<").replace(">", ">") - html_text = html_text[:start_index] + formatted_string + html_text[end_index:] - end_index = len(html_text) - - if offset * 2 < len(utf16_text): - html_text += func(utf16_text[offset * 2:]) - - return html_text + return reparse def mbold(content: str, escape: Optional[bool]=True) -> str: @@ -459,3 +355,107 @@ def hcite(content: str, escape: Optional[bool]=True) -> str: :rtype: :obj:`str` """ return '
{}
'.format(escape_html(content) if escape else content) + + +def apply_html_entities(text: str, entities: Optional[List], custom_subs: Optional[dict[str, str]]) -> str: + """ + Author: @sviat9440 + Updaters: @badiboy, @EgorKhabarov + Message: "*Test* parse _formatting_, [url](https://example.com), [text_mention](tg://user?id=123456) and mention @username" + + .. code-block:: python3 + :caption: Example: + + apply_html_entities(text, entities) + >> "Test parse formatting, url, text_mention and mention @username" + + Custom subs: + You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity. + .. code-block:: python3 + :caption: Example: + + apply_html_entities( + text, + entities, + {"bold": "{text}", "italic": "{text}", "mention": "{text}"}, + ) + >> "Test parse formatting, url and text_mention and mention @username" + """ + + if not entities: + return text.replace("&", "&").replace("<", "<").replace(">", ">") + + _subs = { + "bold": "{text}", + "italic": "{text}", + "pre": "
{text}
", + "code": "{text}", + # "url": "{text}", # @badiboy plain URLs have no text and do not need tags + "text_link": "{text}", + "strikethrough": "{text}", + "underline": "{text}", + "spoiler": "{text}", + "custom_emoji": "{text}", + "blockquote": "
{text}
", + } + + if custom_subs: + for key, value in custom_subs.items(): + _subs[key] = value + utf16_text = text.encode("utf-16-le") + html_text = "" + + def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): + upd_text = upd_text.decode("utf-16-le") + if subst_type == "text_mention": + subst_type = "text_link" + url = "tg://user?id={0}".format(user.id) + elif subst_type == "mention": + url = "https://t.me/{0}".format(upd_text[1:]) + upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") + if not subst_type or not _subs.get(subst_type): + return upd_text + subs = _subs.get(subst_type) + if subst_type == "custom_emoji": + return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) + return subs.format(text=upd_text, url=url) + + offset = 0 + start_index = 0 + end_index = 0 + for entity in entities: + if entity.offset > offset: + # when the offset is not 0: for example, a __b__ + # we need to add the text before the entity to the html_text + html_text += func(utf16_text[offset * 2: entity.offset * 2]) + offset = entity.offset + + new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string + offset += entity.length + end_index = len(html_text) + elif entity.offset == offset: + new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + start_index = len(html_text) + html_text += new_string + end_index = len(html_text) + offset += entity.length + else: + # Here we are processing nested entities. + # We shouldn't update offset, because they are the same as entity before. + # And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered, + # And we don't change it). + entity_string = html_text[start_index: end_index].encode("utf-16-le") + formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, + custom_emoji_id=entity.custom_emoji_id). \ + replace("&", "&").replace("<", "<").replace(">", ">") + html_text = html_text[:start_index] + formatted_string + html_text[end_index:] + end_index = len(html_text) + + if offset * 2 < len(utf16_text): + html_text += func(utf16_text[offset * 2:]) + + return html_text From 189da012092839b611233c5354de6ba39e985c85 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Apr 2024 14:29:01 +0300 Subject: [PATCH 1537/1808] Pass default parse mode to Media items --- telebot/__init__.py | 6 ++++++ telebot/async_telebot.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 823324359..237b8a3f2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3091,6 +3091,12 @@ def send_media_group( :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ + if media: + # Pass default parse mode to Media items + for media_item in media: + if media_item.parse_mode is None: + media_item.parse_mode = self.parse_mode + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d24a025dd..ace5c1f8e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4502,6 +4502,11 @@ async def send_media_group( :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ + if media: + # Pass default parse mode to Media items + for media_item in media: + if media_item.parse_mode is None: + media_item.parse_mode = self.parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content From 2e3b77590afbee435c1fe1fd60f0729b0518765f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Apr 2024 14:34:23 +0300 Subject: [PATCH 1538/1808] Hide token in exception messages --- telebot/__init__.py | 8 +++++++- telebot/async_telebot.py | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 237b8a3f2..85387fc33 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1083,9 +1083,15 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: + e = str(e) + if self.token in e: + e = e.replace(self.token, 'TOKEN') logger.error("Infinity polling exception: %s", str(e)) if logger_level and logger_level >= logging.DEBUG: - logger.error("Exception traceback:\n%s", traceback.format_exc()) + e = traceback.format_exc() + if self.token in e: + e = e.replace(self.token, 'TOKEN') + logger.error("Exception traceback:\n%s", e) time.sleep(3) continue if logger_level and logger_level >= logging.INFO: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ace5c1f8e..975f2d314 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -367,9 +367,15 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: + e = str(e) + if self.token in e: + e = e.replace(self.token, 'TOKEN') logger.error("Infinity polling exception: %s", str(e)) if logger_level and logger_level >= logging.DEBUG: - logger.error("Exception traceback:\n%s", traceback.format_exc()) + e = traceback.format_exc() + if self.token in e: + e = e.replace(self.token, 'TOKEN') + logger.error("Exception traceback:\n%s", e) await asyncio.sleep(3) continue if logger_level and logger_level >= logging.INFO: From 717bcf9e83e8600d2aa6f070541e91f400a8562e Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Sun, 21 Apr 2024 17:32:50 +0400 Subject: [PATCH 1539/1808] Fixed issue in types for Python 3.8 --- telebot/formatting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index a491d9404..dd7309416 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -6,7 +6,7 @@ import re import html -from typing import Optional, List +from typing import Optional, List, Dict def format_text(*args, separator="\n"): @@ -357,7 +357,7 @@ def hcite(content: str, escape: Optional[bool]=True) -> str: return '
{}
'.format(escape_html(content) if escape else content) -def apply_html_entities(text: str, entities: Optional[List], custom_subs: Optional[dict[str, str]]) -> str: +def apply_html_entities(text: str, entities: Optional[List], custom_subs: Optional[Dict[str, str]]) -> str: """ Author: @sviat9440 Updaters: @badiboy, @EgorKhabarov From 2e2240a7fdddb1174b4b98616fae60d3e71dc6df Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Apr 2024 18:31:45 +0300 Subject: [PATCH 1540/1808] Replace token part with asterisks --- telebot/__init__.py | 48 +++++++++++++++++++++++----------------- telebot/async_telebot.py | 13 +++++++---- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 85387fc33..ca48caae9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1030,6 +1030,11 @@ def _setup_change_detector(self, path_to_watch: str): self.event_observer.schedule(self.event_handler, path, recursive=True) self.event_observer.start() + def __hide_token(self, message: str) -> str: + if self.token in message: + code = self.token.split(':')[1] + return message.replace(code, "*" * len(code)) + def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, long_polling_timeout: Optional[int]=20, logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, restart_on_change: Optional[bool]=False, path_to_watch: Optional[str]=None, *args, **kwargs): @@ -1083,15 +1088,9 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: - e = str(e) - if self.token in e: - e = e.replace(self.token, 'TOKEN') - logger.error("Infinity polling exception: %s", str(e)) + logger.error("Infinity polling exception: %s", self.__hide_token(str(e))) if logger_level and logger_level >= logging.DEBUG: - e = traceback.format_exc() - if self.token in e: - e = e.replace(self.token, 'TOKEN') - logger.error("Exception traceback:\n%s", e) + logger.error("Exception traceback:\n%s", self.__hide_token(traceback.format_exc())) time.sleep(3) continue if logger_level and logger_level >= logging.INFO: @@ -6807,7 +6806,7 @@ def register_message_handler(self, callback: Callable, content_types: Optional[L :param commands: list of commands :type commands: :obj:`list` of :obj:`str` - :param regexp: + :param regexp: Regular expression :type regexp: :obj:`str` :param func: Function executed as a filter @@ -7965,6 +7964,7 @@ def register_removed_chat_boost_handler( handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_removed_chat_boost_handler(handler_dict) + def business_connection_handler(self, func=None, **kwargs): """ Handles new incoming business connection state. @@ -7981,7 +7981,8 @@ def decorator(handler): return handler return decorator - + + def add_business_connection_handler(self, handler_dict): """ Adds a business_connection handler. @@ -7994,6 +7995,7 @@ def add_business_connection_handler(self, handler_dict): """ self.business_connection_handlers.append(handler_dict) + def register_business_connection_handler( self, callback: Callable, func: Optional[Callable]=None, pass_bot:Optional[bool]=False, **kwargs): """ @@ -8015,6 +8017,7 @@ def register_business_connection_handler( handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_business_connection_handler(handler_dict) + def business_message_handler( self, commands: Optional[List[str]]=None, @@ -8096,7 +8099,8 @@ def decorator(handler): return handler return decorator - + + def add_business_message_handler(self, handler_dict): """ Adds a business_message handler. @@ -8109,6 +8113,7 @@ def add_business_message_handler(self, handler_dict): """ self.business_message_handlers.append(handler_dict) + def register_business_message_handler(self, callback: Callable, commands: Optional[List[str]]=None, @@ -8122,11 +8127,17 @@ def register_business_message_handler(self, :param callback: function to be called :type callback: :obj:`function` + :param commands: list of commands + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) - :type pass_bot: :obj:`bool` + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` :param kwargs: Optional keyword arguments(custom filters) @@ -8135,8 +8146,6 @@ def register_business_message_handler(self, handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, **kwargs) self.add_business_message_handler(handler_dict) - - def edited_business_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ @@ -8253,7 +8262,7 @@ def register_edited_business_message_handler(self, callback: Callable, content_t **kwargs) self.add_edited_business_message_handler(handler_dict) - + def deleted_business_messages_handler(self, func=None, **kwargs): """ Handles new incoming deleted messages state. @@ -8272,7 +8281,8 @@ def decorator(handler): return handler return decorator - + + def add_deleted_business_messages_handler(self, handler_dict): """ Adds a deleted_business_messages handler. @@ -8282,6 +8292,7 @@ def add_deleted_business_messages_handler(self, handler_dict): """ self.deleted_business_messages_handlers.append(handler_dict) + def register_deleted_business_messages_handler(self, callback: Callable, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ Registers deleted business messages handler. @@ -8304,9 +8315,6 @@ def register_deleted_business_messages_handler(self, callback: Callable, func: O self.add_deleted_business_messages_handler(handler_dict) - - - def add_custom_filter(self, custom_filter: Union[SimpleCustomFilter, AdvancedCustomFilter]): """ Create custom filter. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 975f2d314..01a8a1171 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -393,6 +393,11 @@ async def _handle_exception(self, exception: Exception) -> bool: handled = self.exception_handler.handle(exception) # noqa return handled + def __hide_token(self, message: str) -> str: + if self.token in message: + code = self.token.split(':')[1] + return message.replace(code, "*" * len(code)) + async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, request_timeout: int=None, allowed_updates: Optional[List[str]]=None): """ @@ -442,8 +447,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: except asyncio_helper.RequestTimeout as e: handled = await self._handle_exception(e) if not handled: - logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) - logger.debug(traceback.format_exc()) + logger.error('Unhandled exception (full traceback for debug level): %s', self.__hide_token(str(e))) + logger.debug(self.__hide_token(traceback.format_exc())) if non_stop or handled: await asyncio.sleep(2) @@ -453,8 +458,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: except asyncio_helper.ApiException as e: handled = await self._handle_exception(e) if not handled: - logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) - logger.debug(traceback.format_exc()) + logger.error('Unhandled exception (full traceback for debug level): %s', self.__hide_token(str(e))) + logger.debug(self.__hide_token(traceback.format_exc())) if non_stop or handled: continue From b3053405b3206fe7822e46b4a974971e7fe5db0b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 21 Apr 2024 19:08:49 +0300 Subject: [PATCH 1541/1808] Replace token part with asterisks N2 --- telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 11 +++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ca48caae9..067d24d76 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1214,9 +1214,9 @@ def __threaded_polling(self, non_stop = False, interval = 0, timeout = None, lon handled = self._handle_exception(e) if not handled: if logger_level and logger_level >= logging.ERROR: - logger.error("Threaded polling exception: %s", str(e)) + logger.error("Threaded polling exception: %s", self.__hide_token(str(e))) if logger_level and logger_level >= logging.DEBUG: - logger.error("Exception traceback:\n%s", traceback.format_exc()) + logger.error("Exception traceback:\n%s", self.__hide_token(traceback.format_exc())) if not non_stop: self.__stop_polling.set() # if logger_level and logger_level >= logging.INFO: # enable in future releases. Change output to logger.error diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 01a8a1171..bc3ba8d3b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -367,15 +367,9 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option allowed_updates=allowed_updates, *args, **kwargs) except Exception as e: if logger_level and logger_level >= logging.ERROR: - e = str(e) - if self.token in e: - e = e.replace(self.token, 'TOKEN') - logger.error("Infinity polling exception: %s", str(e)) + logger.error("Infinity polling exception: %s", self.__hide_token(str(e))) if logger_level and logger_level >= logging.DEBUG: - e = traceback.format_exc() - if self.token in e: - e = e.replace(self.token, 'TOKEN') - logger.error("Exception traceback:\n%s", e) + logger.error("Exception traceback:\n%s", self.__hide_token(traceback.format_exc())) await asyncio.sleep(3) continue if logger_level and logger_level >= logging.INFO: @@ -437,6 +431,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout, request_timeout=request_timeout) if updates: self.offset = updates[-1].update_id + 1 + # noinspection PyAsyncCall asyncio.create_task(self.process_new_updates(updates)) # Seperate task for processing updates if interval: await asyncio.sleep(interval) From 66236eff0a1ab490d3466f966fc4f3940bf54be9 Mon Sep 17 00:00:00 2001 From: YouKnow Date: Wed, 24 Apr 2024 18:55:20 +0330 Subject: [PATCH 1542/1808] Removed the `_convert_entites` fucntion and used `json.dumps(types.MessageEntity.to_list_of_dicts(...))` method for caption entities --- telebot/apihelper.py | 13 +------------ telebot/asyncio_helper.py | 14 +------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3f537065c..8941b88ae 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -432,7 +432,7 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m if parse_mode: payload['parse_mode'] = parse_mode if caption_entities is not None: - payload['caption_entities'] = _convert_entites(caption_entities) + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_parameters is not None: @@ -1998,17 +1998,6 @@ def _convert_markup(markup): return markup -def _convert_entites(entites): - if entites is None: - return None - elif len(entites) == 0: - return [] - elif isinstance(entites[0], types.JsonSerializable): - return [entity.to_json() for entity in entites] - else: - return entites - - def _convert_poll_options(poll_options): if poll_options is None: return None diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3aadccc65..9c8f6128c 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -422,7 +422,7 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p if parse_mode: payload['parse_mode'] = parse_mode if caption_entities is not None: - payload['caption_entities'] = await _convert_entites(caption_entities) + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if disable_notification is not None: payload['disable_notification'] = disable_notification if reply_parameters is not None: @@ -1975,18 +1975,6 @@ async def _convert_list_json_serializable(results): return '[' + ret + ']' - -async def _convert_entites(entites): - if entites is None: - return None - elif len(entites) == 0: - return [] - elif isinstance(entites[0], types.JsonSerializable): - return [entity.to_json() for entity in entites] - else: - return entites - - async def _convert_poll_options(poll_options): if poll_options is None: return None From 316825be94b7f72524fcbfd1bd31198344467fad Mon Sep 17 00:00:00 2001 From: chiri <109767616+chirizxc@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:33:52 +0300 Subject: [PATCH 1543/1808] Removed the tautology in the documentation title --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f2ab5ac09..aa49bceb2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -17,9 +17,9 @@ # -- Project information ----------------------------------------------------- -project = 'pyTelegramBotAPI Documentation' -copyright = f'2022-{datetime.now().year}, coder2020official' +project = 'pyTelegramBotAPI' author = 'coder2020official' +copyright = f'2022-{datetime.now().year}, {author}' # The full version, including alpha/beta/rc tags release = '4.17.0' From e26ee25de0c8a8eece049ffac6ae7bad2c7eb5f4 Mon Sep 17 00:00:00 2001 From: chiri <109767616+chirizxc@users.noreply.github.com> Date: Thu, 25 Apr 2024 20:06:37 +0300 Subject: [PATCH 1544/1808] Update async echo_bot example --- examples/asynchronous_telebot/echo_bot.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/asynchronous_telebot/echo_bot.py b/examples/asynchronous_telebot/echo_bot.py index cd2353755..aed26675e 100644 --- a/examples/asynchronous_telebot/echo_bot.py +++ b/examples/asynchronous_telebot/echo_bot.py @@ -2,19 +2,18 @@ # This is a simple echo bot using the decorator mechanism. # It echoes any incoming text messages. +import asyncio from telebot.async_telebot import AsyncTeleBot -bot = AsyncTeleBot('TOKEN') +bot = AsyncTeleBot('TOKEN') # Handle '/start' and '/help' @bot.message_handler(commands=['help', 'start']) async def send_welcome(message): - await bot.reply_to(message, """\ -Hi there, I am EchoBot. -I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\ -""") + text = 'Hi, I am EchoBot.\nJust write me something and I will repeat it!' + await bot.reply_to(message, text) # Handle all other messages with content_type 'text' (content_types defaults to ['text']) @@ -23,5 +22,4 @@ async def echo_message(message): await bot.reply_to(message, message.text) -import asyncio asyncio.run(bot.polling()) From 1b6a57381cd14cc90f0030a014869f3cbac28d92 Mon Sep 17 00:00:00 2001 From: YouKnow Date: Thu, 25 Apr 2024 22:40:54 +0330 Subject: [PATCH 1545/1808] added some of `_convert_entities` checks to `to_list_of_dicts` --- telebot/types.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 7c2782788..51db25ab5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1612,10 +1612,12 @@ def to_list_of_dicts(entity_list) -> Union[List[Dict], None]: """ Converts a list of MessageEntity objects to a list of dictionaries. """ - res = [] - for e in entity_list: - res.append(MessageEntity.to_dict(e)) - return res or None + if entity_list is None or len(entity_list) == 0: + return None + elif isinstance(entity_list[0], MessageEntity): + return [MessageEntity.to_dict(e) for e in entity_list] + else: + return entity_list @classmethod def de_json(cls, json_string): From 776bd53cc1544108887378367746c7b365f4b4ec Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Fri, 26 Apr 2024 16:14:10 +0400 Subject: [PATCH 1546/1808] Fixed a bug with hiding the token --- telebot/__init__.py | 2 ++ telebot/async_telebot.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 067d24d76..3da8952be 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1034,6 +1034,8 @@ def __hide_token(self, message: str) -> str: if self.token in message: code = self.token.split(':')[1] return message.replace(code, "*" * len(code)) + else: + return message def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[bool]=False, long_polling_timeout: Optional[int]=20, logger_level: Optional[int]=logging.ERROR, allowed_updates: Optional[List[str]]=None, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 36b6d007b..fa4a0a7eb 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -391,6 +391,8 @@ def __hide_token(self, message: str) -> str: if self.token in message: code = self.token.split(':')[1] return message.replace(code, "*" * len(code)) + else: + return message async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, request_timeout: int=None, allowed_updates: Optional[List[str]]=None): From 44531cded17aa82d8618154b9ede558a2f826225 Mon Sep 17 00:00:00 2001 From: Shakhzhakhan Maxudbek Date: Sun, 28 Apr 2024 00:17:25 +0500 Subject: [PATCH 1547/1808] Added Flask Google Cloud bot's example --- .../flask_google_cloud_bot/.gcloudignore | 1 + .../flask_google_cloud_bot/app.yaml | 4 ++ .../serverless/flask_google_cloud_bot/main.py | 65 +++++++++++++++++++ .../flask_google_cloud_bot/requirements.txt | 14 ++++ 4 files changed, 84 insertions(+) create mode 100644 examples/serverless/flask_google_cloud_bot/.gcloudignore create mode 100644 examples/serverless/flask_google_cloud_bot/app.yaml create mode 100644 examples/serverless/flask_google_cloud_bot/main.py create mode 100644 examples/serverless/flask_google_cloud_bot/requirements.txt diff --git a/examples/serverless/flask_google_cloud_bot/.gcloudignore b/examples/serverless/flask_google_cloud_bot/.gcloudignore new file mode 100644 index 000000000..ae412d6a0 --- /dev/null +++ b/examples/serverless/flask_google_cloud_bot/.gcloudignore @@ -0,0 +1 @@ +env/ \ No newline at end of file diff --git a/examples/serverless/flask_google_cloud_bot/app.yaml b/examples/serverless/flask_google_cloud_bot/app.yaml new file mode 100644 index 000000000..59ad3ad6f --- /dev/null +++ b/examples/serverless/flask_google_cloud_bot/app.yaml @@ -0,0 +1,4 @@ +runtime: python38 + +env_variables: + BUCKET_NAME: "your-google-application" \ No newline at end of file diff --git a/examples/serverless/flask_google_cloud_bot/main.py b/examples/serverless/flask_google_cloud_bot/main.py new file mode 100644 index 000000000..67f479ff6 --- /dev/null +++ b/examples/serverless/flask_google_cloud_bot/main.py @@ -0,0 +1,65 @@ +''' +Simple bot for Google cloud deployment. + +Docs: +https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service + +1. Receive your bot's token from https://t.me/BotFather + +2. Create a Google Cloud project. https://cloud.google.com/resource-manager/docs/creating-managing-projects + +3. Install the Google Cloud CLI. https://cloud.google.com/sdk/docs/install + +4. Move to telegram_google_cloud_bot folder + +cd telegram_google_cloud_bot/ + +5. Initialize the gcloud CLI: + +gcloud init + +6. To set the default project for your Cloud Run service: + +gcloud config set project PROJECT_ID + +7. Deploy: + +gcloud run deploy +''' + +import os + +from flask import Flask, request + +import telebot + +TOKEN = 'token_from_botfather' + +bot = telebot.TeleBot(TOKEN) + +app = Flask(__name__) + + +@bot.message_handler(commands=['start']) +def start(message): + bot.reply_to(message, 'Hello, ' + message.from_user.first_name) + + +@bot.message_handler(func=lambda message: True, content_types=['text']) +def echo_message(message): + bot.reply_to(message, message.text) + + +@app.route('/' + TOKEN, methods=['POST']) +def getMessage(): + json_string = request.get_data().decode('utf-8') + update = telebot.types.Update.de_json(json_string) + bot.process_new_updates([update]) + return '!', 200 + + +@app.route('/') +def webhook(): + bot.remove_webhook() + bot.set_webhook(url='https://mydomain.com/' + TOKEN) + return '!', 200 \ No newline at end of file diff --git a/examples/serverless/flask_google_cloud_bot/requirements.txt b/examples/serverless/flask_google_cloud_bot/requirements.txt new file mode 100644 index 000000000..6fca88a25 --- /dev/null +++ b/examples/serverless/flask_google_cloud_bot/requirements.txt @@ -0,0 +1,14 @@ +certifi==2022.12.7 +charset-normalizer==3.1.0 +click==8.0.3 +Flask==2.0.2 +idna==3.4 +importlib-metadata==6.4.1 +itsdangerous==2.0.1 +Jinja2==3.0.3 +MarkupSafe==2.0.1 +pyTelegramBotAPI==4.11.0 +requests==2.28.2 +urllib3==1.26.15 +Werkzeug==2.0.2 +zipp==3.15.0 From 5641674ac9d0b682bb2a39f62b9220c5c7ef1dce Mon Sep 17 00:00:00 2001 From: smartwa Date: Sun, 28 Apr 2024 18:42:35 +0300 Subject: [PATCH 1548/1808] Add pytgpt-bot as bot using this library --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 20b062e70..3f11bf8f9 100644 --- a/README.md +++ b/README.md @@ -890,5 +890,6 @@ Here are some examples of template: * [Best Instagram Downloader Bot](https://t.me/Best_Instagram_Downloader_Bot) ([source](https://github.com/arashnm80/best-instagram-downloader)) by [Arashnm80](https://github.com/arashnm80). Free and open source telegram bot to download posts and reels from Instagram. * [4K YouTube Downloader](https://github.com/hansanaD/TelegramYTDLBot/) - Youtube Downloader with upto 4K resolution support. * [DrinkGenius-Bot](https://t.me/cocktail_recommendation_bot) ([source](https://github.com/Povladarchik/DrinkGenius-Bot)) by [Povladarchik](https://github.com/Povladarchik). Your personal assistant in the world of cocktails. +* [Pytgpt-Bot](https://t.me/pytgpt_bot) ([source](https://github.com/Simatwa/pytgpt-bot)) by [Smartwa](https://github.com/Simatwa). AI powered bot for chatting, text-to-image and text-to-speech conversions. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From c82e3db719c639255e10759458482c5d1d402165 Mon Sep 17 00:00:00 2001 From: Shakhzhakhan Maxudbek <153020509+xinitd@users.noreply.github.com> Date: Mon, 29 Apr 2024 00:18:34 +0500 Subject: [PATCH 1549/1808] Update requirements.txt. Fix dependencies --- .../flask_google_cloud_bot/requirements.txt | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/examples/serverless/flask_google_cloud_bot/requirements.txt b/examples/serverless/flask_google_cloud_bot/requirements.txt index 6fca88a25..b4bb7413e 100644 --- a/examples/serverless/flask_google_cloud_bot/requirements.txt +++ b/examples/serverless/flask_google_cloud_bot/requirements.txt @@ -1,14 +1,4 @@ -certifi==2022.12.7 -charset-normalizer==3.1.0 -click==8.0.3 -Flask==2.0.2 -idna==3.4 -importlib-metadata==6.4.1 -itsdangerous==2.0.1 -Jinja2==3.0.3 -MarkupSafe==2.0.1 pyTelegramBotAPI==4.11.0 -requests==2.28.2 -urllib3==1.26.15 -Werkzeug==2.0.2 -zipp==3.15.0 +Flask==3.0.0 +gunicorn==22.0.0 +Werkzeug==3.0.1 From a7379a6a55a2b9b4f58b793c3f9cf4c03492d3d0 Mon Sep 17 00:00:00 2001 From: vehlwn <36479428+vehlwn@users.noreply.github.com> Date: Wed, 1 May 2024 18:26:28 +0400 Subject: [PATCH 1550/1808] Add type hints to SimpleCustomFilter --- telebot/asyncio_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 72695e90b..c580883ee 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -27,7 +27,7 @@ def check(self, message): key: str = None - async def check(self, message): + async def check(self, message: types.Message) -> bool: """ Perform a check. """ From 25f97213793818662ad80104a202ff6c12e2fe59 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 1 May 2024 20:44:04 +0300 Subject: [PATCH 1551/1808] Revert add type hints to SimpleCustomFilter --- telebot/asyncio_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index c580883ee..da794b77b 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -27,7 +27,7 @@ def check(self, message): key: str = None - async def check(self, message: types.Message) -> bool: + async def check(self, message) -> bool: """ Perform a check. """ From 374fe91068a66a6fa1d91668f821a9565dd68066 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 19:42:20 +0000 Subject: [PATCH 1552/1808] Bump werkzeug in /examples/serverless/flask_google_cloud_bot Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.0.1 to 3.0.3. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/werkzeug/compare/3.0.1...3.0.3) --- updated-dependencies: - dependency-name: werkzeug dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- examples/serverless/flask_google_cloud_bot/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/serverless/flask_google_cloud_bot/requirements.txt b/examples/serverless/flask_google_cloud_bot/requirements.txt index b4bb7413e..ed0491b0a 100644 --- a/examples/serverless/flask_google_cloud_bot/requirements.txt +++ b/examples/serverless/flask_google_cloud_bot/requirements.txt @@ -1,4 +1,4 @@ pyTelegramBotAPI==4.11.0 Flask==3.0.0 gunicorn==22.0.0 -Werkzeug==3.0.1 +Werkzeug==3.0.3 From be440a155ac416f75632a962cbfd65773ef659e9 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 10 May 2024 11:39:44 +0300 Subject: [PATCH 1553/1808] 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 1554/1808] 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 1555/1808] 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 1556/1808] 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 1557/1808] 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 1558/1808] 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 1559/1808] 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 1560/1808] 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 1561/1808] 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 1562/1808] 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 1563/1808] 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 1564/1808] 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 1565/1808] 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 1566/1808] 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 1567/1808] 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 1568/1808] 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): """ From 852b096ecd57785092dadf9a41ca9314a6ea988c Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 15 May 2024 10:41:13 +0300 Subject: [PATCH 1569/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f11bf8f9..73880b554 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.2! +##

Supported Bot API version: 7.3!

Official documentation

Official ru documentation

From e2aa7a7132fc47da541c97c5fa82811eec54c8b4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 15 May 2024 14:21:50 +0300 Subject: [PATCH 1570/1808] Bugfix in typing --- docs/source/conf.py | 2 +- pyproject.toml | 2 +- telebot/async_telebot.py | 2 +- telebot/version.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index bb40d90ab..7e46d02c3 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.18.0' +release = '4.18.1' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 6666fee8b..7eae1722e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.18.0" +version = "4.18.1" description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1b6c09413..992625641 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[InputPollOption], + 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, diff --git a/telebot/version.py b/telebot/version.py index 4ee703d3d..830950124 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.18.0' +__version__ = '4.18.1' From d4f7f8301035979eb88eafb70fdedffc67553cea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 05:41:29 +0000 Subject: [PATCH 1571/1808] --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 32cb38aa1..09014549b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest -requests==2.31.0 +requests==2.32.0 wheel==0.38.1 aiohttp==3.9.4 From 5f7a8626b02f49b2aae371ad9b2d5dab53cf6062 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 16:42:29 +0400 Subject: [PATCH 1572/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73880b554..770186d5c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.3! +##

Supported Bot API version: 7.4!

Official documentation

Official ru documentation

From 6a7c34f6b6b65aae98e9e7110f50208eb6dec81b Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 16:59:10 +0400 Subject: [PATCH 1573/1808] Added the method refundStarPayment. --- telebot/__init__.py | 16 ++++++++++++++++ telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 17 +++++++++++++++++ telebot/asyncio_helper.py | 6 ++++++ 4 files changed, 44 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2d327ab5a..6c1853e16 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5375,7 +5375,23 @@ def answer_pre_checkout_query( """ return apihelper.answer_pre_checkout_query( self.token, pre_checkout_query_id, ok, error_message=error_message) + + def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> bool: + """ + Refunds a successful payment in Telegram Stars. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#refundstarpayment + + :param user_id: Identifier of the user whose payment will be refunded + :type user_id: :obj:`int` + :param telegram_payment_charge_id: Telegram payment identifier + :type telegram_payment_charge_id: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.refund_star_payment(self.token, user_id, telegram_payment_charge_id) def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 82a1e31e6..0ef0d2211 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1616,6 +1616,11 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No payload['error_message'] = error_message return _make_request(token, method_url, params=payload) +def refund_star_payment(token, user_id, telegram_payment_charge_id): + method_url = 'refundStarPayment' + payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id} + return _make_request(token, method_url, params=payload, method='post') + def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 992625641..f5eda28c0 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6711,6 +6711,23 @@ async def answer_pre_checkout_query( :rtype: :obj:`bool` """ return await asyncio_helper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) + + async def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> bool: + """ + Refunds a successful payment in Telegram Stars. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#refundstarpayment + + :param user_id: Identifier of the user whose payment will be refunded + :type user_id: :obj:`int` + + :param telegram_payment_charge_id: Telegram payment identifier + :type telegram_payment_charge_id: :obj:`str` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.refund_star_payment(self.token, user_id, telegram_payment_charge_id) async def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 1a56348cf..d63ec5773 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1601,6 +1601,12 @@ async def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_mess return await _process_request(token, method_url, params=payload) +async def refund_star_payment(token, user_id, telegram_payment_charge_id): + method_url = 'refundStarPayment' + payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id} + return await _process_request(token, method_url, params=payload, method='post') + + async def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' payload = {'chat_id': chat_id} From 83e2c440a341a9640426889eeaa5e0cb7edf7890 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 17:03:11 +0400 Subject: [PATCH 1574/1808] Added the field effect_id to the class Message. --- telebot/types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index cfbe447fd..f5380e084 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -951,6 +951,9 @@ class Message(JsonDeserializable): if it is a text message and link preview options were changed :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :param effect_id: Optional. Unique identifier of the message effect added to the message + :type effect_id: :obj:`str` + :param animation: Optional. Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set :type animation: :class:`telebot.types.Animation` @@ -1370,6 +1373,8 @@ def de_json(cls, json_string): opts['business_connection_id'] = obj['business_connection_id'] if 'is_from_offline' in obj: opts['is_from_offline'] = obj['is_from_offline'] + if 'effect_id' in obj: + opts['effect_id'] = obj['effect_id'] @@ -1479,6 +1484,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.sender_business_bot: Optional[User] = None self.business_connection_id: Optional[str] = None self.is_from_offline: Optional[bool] = None + self.effect_id: Optional[str] = None for key in options: setattr(self, key, options[key]) From 249ef2e57f64d594e51c32bf7755257ff0ae9a50 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 17:28:00 +0400 Subject: [PATCH 1575/1808] Added the parameter message_effect_id to the methods sendMessage, sendPhoto, sendVideo, sendAnimation, sendAudio, sendDocument, sendSticker, sendVideoNote, sendVoice, sendLocation, sendVenue, sendContact, sendPoll, sendDice, sendInvoice, sendGame, and sendMediaGroup. --- telebot/__init__.py | 149 ++++++++++++++++++++++++++++++--------- telebot/async_telebot.py | 139 +++++++++++++++++++++++++++--------- 2 files changed, 220 insertions(+), 68 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6c1853e16..ffc3b485b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1649,7 +1649,8 @@ def send_message( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, link_preview_options : Optional[types.LinkPreviewOptions]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send text messages. @@ -1705,6 +1706,9 @@ def send_message( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1752,7 +1756,8 @@ def send_message( self.token, chat_id, text, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, entities=entities, protect_content=protect_content, message_thread_id=message_thread_id, - reply_parameters=reply_parameters, link_preview_options=link_preview_options, business_connection_id=business_connection_id)) + reply_parameters=reply_parameters, link_preview_options=link_preview_options, business_connection_id=business_connection_id, + message_effect_id=message_effect_id)) def forward_message( @@ -2040,7 +2045,8 @@ def send_dice( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -2082,6 +2088,9 @@ def send_dice( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2110,7 +2119,8 @@ def send_dice( apihelper.send_dice( self.token, chat_id, emoji=emoji, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id)) @@ -2127,7 +2137,8 @@ def send_photo( message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2181,6 +2192,9 @@ def send_photo( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2212,7 +2226,8 @@ def send_photo( self.token, chat_id, photo, caption=caption, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, - message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) + message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id)) @@ -2232,7 +2247,8 @@ def send_audio( message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2305,6 +2321,9 @@ def send_audio( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2339,7 +2358,8 @@ def send_audio( self.token, chat_id, audio, caption=caption, duration=duration, performer=performer, title=title, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id)) ) @@ -2356,7 +2376,8 @@ def send_voice( 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, + message_effect_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, 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. @@ -2410,6 +2431,9 @@ def send_voice( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2439,7 +2463,8 @@ def send_voice( self.token, chat_id, voice, caption=caption, duration=duration, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id) ) @@ -2460,7 +2485,8 @@ def send_document( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send general files. @@ -2526,6 +2552,9 @@ def send_document( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2565,7 +2594,8 @@ def send_document( reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption=caption, thumbnail=thumbnail, caption_entities=caption_entities, disable_content_type_detection=disable_content_type_detection, visible_file_name=visible_file_name, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, + business_connection_id=business_connection_id, message_effect_id=message_effect_id) ) @@ -2582,7 +2612,8 @@ def send_sticker( message_thread_id: Optional[int]=None, emoji: Optional[str]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2631,6 +2662,9 @@ def send_sticker( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2664,7 +2698,8 @@ def send_sticker( self.token, chat_id, sticker, 'sticker', reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, - reply_parameters=reply_parameters, business_connection_id=business_connection_id) + reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id) ) @@ -2689,7 +2724,8 @@ def send_video( has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2763,6 +2799,9 @@ def send_video( :param business_connection_id: Identifier of a business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Identifier of a message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2803,7 +2842,7 @@ def send_video( supports_streaming=supports_streaming, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, height=height, width=width, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, - reply_parameters=reply_parameters, business_connection_id=business_connection_id) + reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id) ) @@ -2826,7 +2865,8 @@ def send_animation( has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2899,6 +2939,9 @@ def send_animation( :param business_connection_id: Identifier of a business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2934,7 +2977,7 @@ def send_animation( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, width=width, height=height, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - has_spoiler=has_spoiler, business_connection_id=business_connection_id) + has_spoiler=has_spoiler, business_connection_id=business_connection_id, message_effect_id=message_effect_id) ) @@ -2952,7 +2995,8 @@ def send_video_note( message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -3010,6 +3054,9 @@ def send_video_note( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3042,7 +3089,8 @@ def send_video_note( apihelper.send_video_note( self.token, chat_id, data, duration=duration, length=length, reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, + business_connection_id=business_connection_id, message_effect_id=message_effect_id) ) @@ -3058,7 +3106,8 @@ def send_media_group( allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> List[types.Message]: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -3095,6 +3144,9 @@ def send_media_group( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -3128,7 +3180,7 @@ def send_media_group( result = apihelper.send_media_group( self.token, chat_id, media, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id) + business_connection_id=business_connection_id, message_effect_id=message_effect_id) return [types.Message.de_json(msg) for msg in result] @@ -3147,7 +3199,8 @@ def send_location( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -3203,6 +3256,9 @@ def send_location( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :parameter message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3232,7 +3288,8 @@ def send_location( self.token, chat_id, latitude, longitude, live_period=live_period, reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id) ) @@ -3354,7 +3411,8 @@ def send_venue( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -3417,6 +3475,9 @@ def send_venue( :param business_connection_id: Identifier of a business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3446,7 +3507,8 @@ def send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id=foursquare_id, foursquare_type=foursquare_type, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, google_place_id=google_place_id, google_place_type=google_place_type, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id) ) @@ -3461,7 +3523,8 @@ def send_contact( allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -3511,6 +3574,9 @@ def send_contact( :param business_connection_id: Identifier of a business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3539,7 +3605,8 @@ def send_contact( apihelper.send_contact( self.token, chat_id, phone_number, first_name, last_name=last_name, vcard=vcard, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, - protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, + business_connection_id=business_connection_id, message_effect_id=message_effect_id) ) @@ -4707,7 +4774,8 @@ def send_game( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Used to send the game. @@ -4746,6 +4814,9 @@ def send_game( :param business_connection_id: Unique identifier of the business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -4777,7 +4848,8 @@ def send_game( apihelper.send_game( self.token, chat_id, game_short_name, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, - message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, + message_effect_id=message_effect_id) ) @@ -4881,7 +4953,8 @@ def send_invoice( suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Sends invoice. @@ -4986,6 +5059,9 @@ def send_invoice( :param reply_parameters: Required if the message is a reply. Additional interface options. :type reply_parameters: :obj:`types.ReplyParameters` + :param message_effect_id: The identifier of a message effect, which will be applied to the sent message + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5024,7 +5100,8 @@ def send_invoice( disable_notification=disable_notification, reply_markup=reply_markup, provider_data=provider_data, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - max_tip_amount=max_tip_amount, suggested_tip_amounts=suggested_tip_amounts) + max_tip_amount=max_tip_amount, suggested_tip_amounts=suggested_tip_amounts, + message_effect_id=message_effect_id) ) def create_invoice_link(self, @@ -5155,7 +5232,7 @@ def send_poll( business_connection_id: Optional[str]=None, question_parse_mode: Optional[str] = None, question_entities: Optional[List[types.MessageEntity]] = None, - ) -> types.Message: + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -5234,6 +5311,9 @@ def send_poll( :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` + :param message_effect_id: Unique identifier of the message effect to apply to the sent message + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5288,7 +5368,8 @@ def send_poll( 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, - question_parse_mode=question_parse_mode, question_entities=question_entities) + question_parse_mode=question_parse_mode, question_entities=question_entities, + message_effect_id=message_effect_id) ) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f5eda28c0..ae080aea8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3061,7 +3061,8 @@ async def send_message( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, link_preview_options: Optional[types.LinkPreviewOptions]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send text messages. @@ -3117,6 +3118,9 @@ async def send_message( :param business_connection_id: Unique identifier for the target business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier for the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3171,7 +3175,8 @@ async def send_message( await asyncio_helper.send_message( self.token, chat_id, text, reply_markup, parse_mode, disable_notification, timeout, - entities, protect_content, message_thread_id, reply_parameters, link_preview_options, business_connection_id)) + entities, protect_content, message_thread_id, reply_parameters, link_preview_options, business_connection_id, + message_effect_id=message_effect_id)) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -3445,7 +3450,8 @@ async def send_dice( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -3487,6 +3493,9 @@ async def send_dice( :param business_connection_id: Unique identifier for the target business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier for the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3517,7 +3526,7 @@ async def send_dice( return types.Message.de_json( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, - reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id)) + reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_photo( @@ -3533,7 +3542,8 @@ async def send_photo( message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -3587,6 +3597,9 @@ async def send_photo( :param business_connection_id: Unique identifier for the target business connection :type business_connection_id: :obj:`str` + + :param message_effect_id: Unique identifier for the message effect + :type message_effect_id: :obj:`str` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3620,7 +3633,7 @@ async def send_photo( await asyncio_helper.send_photo( self.token, chat_id, photo, caption, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id)) + protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -3638,7 +3651,8 @@ async def send_audio( message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -3711,6 +3725,9 @@ async def send_audio( :param business_connection_id: Unique identifier for the target business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier for the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3747,7 +3764,7 @@ async def send_audio( await asyncio_helper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id)) + caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -3762,7 +3779,8 @@ async def send_voice( 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, + message_effect_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, 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. @@ -3816,6 +3834,9 @@ async def send_voice( :param business_connection_id: Unique identifier for the target business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier for the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -3847,7 +3868,7 @@ async def send_voice( await asyncio_helper.send_voice( self.token, chat_id, voice, caption, duration, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, reply_parameters, business_connection_id)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], @@ -3867,7 +3888,8 @@ async def send_document( message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send general files. @@ -3933,6 +3955,9 @@ async def send_document( :param business_connection_id: Unique identifier for the target business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier for the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3977,7 +4002,7 @@ async def send_document( disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content, - message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) + message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id)) async def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], @@ -3991,7 +4016,8 @@ async def send_sticker( message_thread_id: Optional[int]=None, emoji: Optional[str]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -4040,6 +4066,9 @@ async def send_sticker( :param business_connection_id: Unique identifier for the target business connection :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier for the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4078,7 +4107,7 @@ async def send_sticker( reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, - message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id)) + message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id)) async def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -4101,7 +4130,8 @@ async def send_video( has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -4175,6 +4205,9 @@ async def send_video( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4216,7 +4249,7 @@ async def send_video( await asyncio_helper.send_video( self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, - caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id)) + caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -4237,7 +4270,8 @@ async def send_animation( has_spoiler: Optional[bool]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -4310,6 +4344,9 @@ async def send_animation( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4346,7 +4383,8 @@ async def send_animation( await asyncio_helper.send_animation( self.token, chat_id, animation, duration, caption, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id)) + caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, + message_effect_id=message_effect_id)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -4362,7 +4400,8 @@ async def send_video_note( message_thread_id: Optional[int]=None, thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> types.Message: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -4420,6 +4459,9 @@ async def send_video_note( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4454,7 +4496,7 @@ async def send_video_note( return types.Message.de_json( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_markup, - disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id)) + disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_media_group( self, chat_id: Union[int, str], @@ -4468,7 +4510,8 @@ async def send_media_group( allow_sending_without_reply: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - business_connection_id: Optional[str]=None) -> List[types.Message]: + business_connection_id: Optional[str]=None, + message_effect_id: Optional[str]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -4505,6 +4548,9 @@ async def send_media_group( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -4538,7 +4584,7 @@ async def send_media_group( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = await asyncio_helper.send_media_group( - self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id) + self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -4556,7 +4602,8 @@ async def send_location( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -4612,6 +4659,9 @@ async def send_location( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4644,7 +4694,7 @@ async def send_location( self.token, chat_id, latitude, longitude, live_period, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - protect_content, message_thread_id, reply_parameters, business_connection_id)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def edit_message_live_location( self, latitude: float, longitude: float, @@ -4760,7 +4810,8 @@ async def send_venue( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -4824,6 +4875,9 @@ async def send_venue( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4855,7 +4909,7 @@ async def send_venue( await asyncio_helper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_markup, timeout, - google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id)) + google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_contact( @@ -4870,7 +4924,8 @@ async def send_contact( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -4921,6 +4976,9 @@ async def send_contact( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param message_effect_id: Unique identifier of the message effect + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4952,7 +5010,7 @@ async def send_contact( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters, business_connection_id)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) async def send_chat_action( @@ -6063,7 +6121,8 @@ async def send_game( 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, + message_effect_id: Optional[str]=None) -> types.Message: """ Used to send the game. @@ -6102,6 +6161,9 @@ async def send_game( :param business_connection_id: Identifier of the business connection. :type business_connection_id: :obj:`str` + :param message_effect_id: Identifier of the message effect. + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6132,7 +6194,7 @@ async def send_game( result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters, business_connection_id) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id) return types.Message.de_json(result) async def set_game_score( @@ -6230,7 +6292,8 @@ async def send_invoice( suggested_tip_amounts: Optional[List[int]]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.Message: + reply_parameters: Optional[types.ReplyParameters]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Sends invoice. @@ -6335,6 +6398,9 @@ async def send_invoice( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + :param message_effect_id: The identifier of a message effect to be applied to the message + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6368,7 +6434,8 @@ async def send_invoice( photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, reply_markup, provider_data, timeout, - max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters) + max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters, + message_effect_id=message_effect_id) return types.Message.de_json(result) @@ -6498,7 +6565,7 @@ async def send_poll( business_connection_id: Optional[str]=None, question_parse_mode: Optional[str] = None, question_entities: Optional[List[types.MessageEntity]] = None, - ) -> types.Message: + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -6581,6 +6648,9 @@ async def send_poll( :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` + :param message_effect_id: Identifier of the message effect to apply to the sent message + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6632,7 +6702,8 @@ async def send_poll( 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, question_parse_mode=question_parse_mode, question_entities=question_entities)) + business_connection_id, question_parse_mode=question_parse_mode, question_entities=question_entities, + message_effect_id=message_effect_id)) async def stop_poll( self, chat_id: Union[int, str], message_id: int, From 236518ee535240b3475d8d7bf05b0ed4836f9341 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 17:29:42 +0400 Subject: [PATCH 1576/1808] Update __init__.py --- telebot/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ffc3b485b..56a24e0c7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2360,7 +2360,6 @@ def send_audio( timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id)) - ) def send_voice( From a5090263dd8303ce20114b59b68be12bbba0c6b2 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 17:49:48 +0400 Subject: [PATCH 1577/1808] Added the field show_caption_above_media to the classes Message, InputMediaAnimation, InputMediaPhoto, InputMediaVideo, InlineQueryResultGif, InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo, InlineQueryResultCachedGif, InlineQueryResultCachedMpeg4Gif, InlineQueryResultCachedPhoto, and InlineQueryResultCachedVideo. --- telebot/types.py | 91 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f5380e084..23146cffe 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -989,6 +989,9 @@ class Message(JsonDeserializable): commands, etc. that appear in the caption :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + :param show_caption_above_media: Optional. True, if the caption must be shown above the message media + :type show_caption_above_media: :obj:`bool` + :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation :type has_media_spoiler: :obj:`bool` @@ -1375,6 +1378,8 @@ def de_json(cls, json_string): opts['is_from_offline'] = obj['is_from_offline'] if 'effect_id' in obj: opts['effect_id'] = obj['effect_id'] + if 'show_caption_above_media' in obj: + opts['show_caption_above_media'] = obj['show_caption_above_media'] @@ -1485,6 +1490,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.business_connection_id: Optional[str] = None self.is_from_offline: Optional[bool] = None self.effect_id: Optional[str] = None + self.show_caption_above_media: Optional[bool] = None for key in options: setattr(self, key, options[key]) @@ -4454,11 +4460,15 @@ class InlineQueryResultPhoto(InlineQueryResultBase): :param input_message_content: Optional. Content of the message to be sent instead of the photo :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. If true, a caption is shown over the photo or video + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultPhoto` """ def __init__(self, id, photo_url, thumbnail_url, photo_width=None, photo_height=None, title=None, - description=None, caption=None, caption_entities=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, + show_caption_above_media=None): 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) @@ -4467,6 +4477,7 @@ def __init__(self, id, photo_url, thumbnail_url, photo_width=None, photo_height= self.photo_width = photo_width self.photo_height = photo_height self.description = description + self.show_caption_above_media: Optional[bool] = show_caption_above_media @property def thumb_url(self): @@ -4483,6 +4494,8 @@ def to_dict(self): json_dict['photo_height'] = self.photo_height if self.description: json_dict['description'] = self.description + if self.show_caption_above_media: + json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -4537,13 +4550,16 @@ class InlineQueryResultGif(InlineQueryResultBase): :param input_message_content: Optional. Content of the message to be sent instead of the GIF animation :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. If true, a caption is shown over the photo or video + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultGif` """ def __init__(self, id, gif_url, thumbnail_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, - thumbnail_mime_type=None): + thumbnail_mime_type=None, show_caption_above_media=None): 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) @@ -4553,6 +4569,7 @@ def __init__(self, id, gif_url, thumbnail_url, gif_width=None, gif_height=None, self.thumbnail_url = thumbnail_url self.gif_duration = gif_duration self.thumbnail_mime_type = thumbnail_mime_type + self.show_caption_above_media: Optional[bool] = show_caption_above_media @property def thumb_url(self): @@ -4576,6 +4593,8 @@ def to_dict(self): json_dict['gif_duration'] = self.gif_duration if self.thumbnail_mime_type: json_dict['thumbnail_mime_type'] = self.thumbnail_mime_type + if self.show_caption_above_media: + json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -4630,13 +4649,16 @@ class InlineQueryResultMpeg4Gif(InlineQueryResultBase): :param input_message_content: Optional. Content of the message to be sent instead of the video animation :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. If true, a caption is shown over the photo or video + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultMpeg4Gif` """ def __init__(self, id, mpeg4_url, thumbnail_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, - thumbnail_mime_type=None): + thumbnail_mime_type=None, show_caption_above_media=None): 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) @@ -4646,6 +4668,7 @@ def __init__(self, id, mpeg4_url, thumbnail_url, mpeg4_width=None, mpeg4_height= self.thumbnail_url = thumbnail_url self.mpeg4_duration = mpeg4_duration self.thumbnail_mime_type = thumbnail_mime_type + self.show_caption_above_media: Optional[bool] = show_caption_above_media @property def thumb_url(self): @@ -4669,6 +4692,8 @@ def to_dict(self): json_dict['mpeg4_duration '] = self.mpeg4_duration if self.thumbnail_mime_type: json_dict['thumbnail_mime_type'] = self.thumbnail_mime_type + if self.show_caption_above_media: + json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -4727,13 +4752,16 @@ class InlineQueryResultVideo(InlineQueryResultBase): required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. If true, a caption is shown over the video + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultVideo` """ def __init__(self, id, video_url, mime_type, thumbnail_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): + description=None, reply_markup=None, input_message_content=None, show_caption_above_media=None): 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) @@ -4744,6 +4772,7 @@ def __init__(self, id, video_url, mime_type, thumbnail_url, self.video_height = video_height self.video_duration = video_duration self.description = description + self.show_caption_above_media: Optional[bool] = show_caption_above_media @property def thumb_url(self): @@ -4761,6 +4790,8 @@ def to_dict(self): json_dict['video_duration'] = self.video_duration if self.description: json_dict['description'] = self.description + if self.show_caption_above_media: + json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -5330,6 +5361,7 @@ def __init__(self): self.parse_mode = None self.caption_entities = None self.payload_dic = {} + self.show_caption_above_media = None def to_json(self): json_dict = self.payload_dic @@ -5349,6 +5381,8 @@ def to_json(self): json_dict['parse_mode'] = self.parse_mode if self.caption_entities: json_dict['caption_entities'] = MessageEntity.to_list_of_dicts(self.caption_entities) + if self.show_caption_above_media: + json_dict['show_caption_above_media'] = self.show_caption_above_media return json.dumps(json_dict) @@ -5391,12 +5425,15 @@ class InlineQueryResultCachedPhoto(InlineQueryResultCachedBase): :param input_message_content: Optional. Content of the message to be sent instead of the photo :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. Pass True, if a caption is not required for the media + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedPhoto` """ 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): + reply_markup=None, input_message_content=None, show_caption_above_media=None): InlineQueryResultCachedBase.__init__(self) self.type = 'photo' self.id = id @@ -5409,6 +5446,7 @@ def __init__(self, id, photo_file_id, title=None, description=None, self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['photo_file_id'] = photo_file_id + self.show_caption_above_media: Optional[bool] = show_caption_above_media # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -5446,12 +5484,15 @@ class InlineQueryResultCachedGif(InlineQueryResultCachedBase): :param input_message_content: Optional. Content of the message to be sent instead of the GIF animation :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. Pass True, if a caption is not required for the media + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedGif` """ 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): + reply_markup=None, input_message_content=None, show_caption_above_media=None): InlineQueryResultCachedBase.__init__(self) self.type = 'gif' self.id = id @@ -5464,6 +5505,7 @@ def __init__(self, id, gif_file_id, title=None, description=None, self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['gif_file_id'] = gif_file_id + self.show_caption_above_media: Optional[bool] = show_caption_above_media # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -5501,12 +5543,15 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResultCachedBase): :param input_message_content: Optional. Content of the message to be sent instead of the video animation :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. Pass True, if caption should be shown above the media + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedMpeg4Gif` """ 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): + reply_markup=None, input_message_content=None, show_caption_above_media=None): InlineQueryResultCachedBase.__init__(self) self.type = 'mpeg4_gif' self.id = id @@ -5519,7 +5564,7 @@ def __init__(self, id, mpeg4_file_id, title=None, description=None, self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['mpeg4_file_id'] = mpeg4_file_id - + self.show_caption_above_media: Optional[bool] = show_caption_above_media # noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedSticker(InlineQueryResultCachedBase): @@ -5654,13 +5699,16 @@ class InlineQueryResultCachedVideo(InlineQueryResultCachedBase): :param input_message_content: Optional. Content of the message to be sent instead of the video :type input_message_content: :class:`telebot.types.InputMessageContent` + :param show_caption_above_media: Optional. Pass True, if a caption is not required for the media + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedVideo` """ 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): + input_message_content=None, show_caption_above_media=None): InlineQueryResultCachedBase.__init__(self) self.type = 'video' self.id = id @@ -5673,6 +5721,7 @@ def __init__(self, id, video_file_id, title, description=None, self.input_message_content = input_message_content self.parse_mode = parse_mode self.payload_dic['video_file_id'] = video_file_id + self.show_caption_above_media: Optional[bool] = show_caption_above_media # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -6575,10 +6624,13 @@ class InputMediaPhoto(InputMedia): :param has_spoiler: Optional. True, if the uploaded photo is a spoiler :type has_spoiler: :obj:`bool` + :param show_caption_above_media: Optional. True, if the caption should be shown above the photo + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InputMediaPhoto` """ - def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, has_spoiler=None): + def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, has_spoiler=None, show_caption_above_media=None): if service_utils.is_pil_image(media): media = service_utils.pil_image_to_file(media) @@ -6586,11 +6638,14 @@ def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, type="photo", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.has_spoiler: Optional[bool] = has_spoiler + self.show_caption_above_media: Optional[bool] = show_caption_above_media def to_dict(self): ret = super(InputMediaPhoto, self).to_dict() if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler + if self.show_caption_above_media is not None: + ret['show_caption_above_media'] = self.show_caption_above_media return ret @@ -6638,11 +6693,14 @@ class InputMediaVideo(InputMedia): :param has_spoiler: Optional. True, if the uploaded video is a spoiler :type has_spoiler: :obj:`bool` + :param show_caption_above_media: Optional. True, if the caption should be shown above the video + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InputMediaVideo` """ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, - width=None, height=None, duration=None, supports_streaming=None, has_spoiler=None): + width=None, height=None, duration=None, supports_streaming=None, has_spoiler=None, show_caption_above_media=None): super(InputMediaVideo, self).__init__( type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumbnail = thumbnail @@ -6651,6 +6709,7 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption self.duration = duration self.supports_streaming = supports_streaming self.has_spoiler: Optional[bool] = has_spoiler + self.show_caption_above_media: Optional[bool] = show_caption_above_media @property def thumb(self): @@ -6671,6 +6730,8 @@ def to_dict(self): ret['supports_streaming'] = self.supports_streaming if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler + if self.show_caption_above_media is not None: + ret['show_caption_above_media'] = self.show_caption_above_media return ret @@ -6715,11 +6776,14 @@ class InputMediaAnimation(InputMedia): :param has_spoiler: Optional. True, if the uploaded animation is a spoiler :type has_spoiler: :obj:`bool` + :param show_caption_above_media: Optional. True, if the caption should be shown above the animation + :type show_caption_above_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAnimation` """ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, - width=None, height=None, duration=None, has_spoiler=None): + width=None, height=None, duration=None, has_spoiler=None, show_caption_above_media=None): super(InputMediaAnimation, self).__init__( type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumbnail = thumbnail @@ -6727,6 +6791,7 @@ def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption self.height = height self.duration = duration self.has_spoiler: Optional[bool] = has_spoiler + self.show_caption_above_media: Optional[bool] = show_caption_above_media @property def thumb(self): @@ -6745,6 +6810,8 @@ def to_dict(self): ret['duration'] = self.duration if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler + if self.show_caption_above_media is not None: + ret['show_caption_above_media'] = self.show_caption_above_media return ret From d66028285be722d5d21d63b6b57ffc10ef892efa Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 18:03:23 +0400 Subject: [PATCH 1578/1808] attempt 2: Added the parameter message_effect_id to the methods sendMessage, sendPhoto, sendVideo, sendAnimation, sendAudio, sendDocument, sendSticker, sendVideoNote, sendVoice, sendLocation, sendVenue, sendContact, sendPoll, sendDice, sendInvoice, sendGame, and sendMediaGroup. --- telebot/apihelper.py | 71 ++++++++++++++++++++++++++++++--------- telebot/asyncio_helper.py | 68 ++++++++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0ef0d2211..d230639c7 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -245,7 +245,7 @@ def send_message( parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, link_preview_options=None, - business_connection_id=None): + business_connection_id=None, message_effect_id=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -268,6 +268,8 @@ def send_message( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, method='post') @@ -454,7 +456,7 @@ def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, - business_connection_id=None): + business_connection_id=None, message_effect_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -473,6 +475,8 @@ def send_dice( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) @@ -481,7 +485,7 @@ def send_photo( caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -513,13 +517,16 @@ def send_photo( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_media_group( token, chat_id, media, disable_notification=None, - timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): + timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -535,6 +542,8 @@ def send_media_group( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -547,7 +556,8 @@ def send_location( reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, - message_thread_id=None, reply_parameters=None, business_connection_id=None): + message_thread_id=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -572,6 +582,8 @@ def send_location( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) @@ -623,7 +635,8 @@ def send_venue( token, chat_id, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, disable_notification=None, reply_markup=None, timeout=None, google_place_id=None, - google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): + google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -648,13 +661,16 @@ def send_venue( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): + protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -675,6 +691,8 @@ def send_contact( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) @@ -694,7 +712,7 @@ def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=Non def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -740,6 +758,8 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, files=files, method='post') @@ -748,7 +768,7 @@ def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None, - has_spoiler=None, business_connection_id=None): + has_spoiler=None, business_connection_id=None, message_effect_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -792,12 +812,15 @@ def send_animation( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): + protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -827,12 +850,14 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup= payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, - message_thread_id=None, reply_parameters=None,business_connection_id=None): + message_thread_id=None, reply_parameters=None,business_connection_id=None, message_effect_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -868,12 +893,15 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, - caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): + caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -915,13 +943,16 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, - protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None, business_connection_id=None): + protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -964,6 +995,8 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1417,7 +1450,7 @@ def delete_message(token, chat_id, message_id, timeout=None): def send_game( token, chat_id, game_short_name, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None): + protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1434,6 +1467,8 @@ def send_game( payload['reply_parameters'] = reply_parameters.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) @@ -1499,7 +1534,7 @@ def send_invoice( send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None, reply_parameters=None): + protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1579,6 +1614,8 @@ def send_invoice( payload['message_thread_id'] = message_thread_id if reply_parameters is not None: payload['reply_parameters'] = reply_parameters.to_json() + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) @@ -1827,7 +1864,7 @@ 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, question_parse_mode=None, question_entities=None): + reply_parameters=None, business_connection_id=None, question_parse_mode=None, question_entities=None, message_effect_id=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1876,6 +1913,8 @@ def send_poll( payload['question_parse_mode'] = question_parse_mode if question_entities: payload['question_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(question_entities)) + if message_effect_id: + payload['message_effect_id'] = message_effect_id 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/asyncio_helper.py b/telebot/asyncio_helper.py index d63ec5773..aa6a5cd68 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -281,7 +281,7 @@ async def send_message( reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, - message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None): + message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None, message_effect_id=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -304,6 +304,8 @@ async def send_message( params['message_thread_id'] = message_thread_id if business_connection_id: params['business_connection_id'] = business_connection_id + if message_effect_id: + params['message_effect_id'] = message_effect_id return await _process_request(token, method_name, params=params) @@ -442,7 +444,7 @@ async def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, - message_thread_id=None,reply_parameters=None, business_connection_id=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -461,6 +463,8 @@ async def send_dice( payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) @@ -470,7 +474,7 @@ async def send_photo( parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, - business_connection_id=None): + business_connection_id=None, message_effect_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -502,13 +506,15 @@ async def send_photo( payload['has_spoiler'] = has_spoiler if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_media_group( token, chat_id, media, disable_notification=None, - timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): + timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendMediaGroup' media_json, files = await convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -524,6 +530,8 @@ async def send_media_group( payload['reply_parameters'] = json.dumps(reply_parameters.to_dict()) if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request( token, method_url, params=payload, method='post' if files else 'get', @@ -535,7 +543,8 @@ async def send_location( live_period=None, reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, - proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): + proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -560,6 +569,8 @@ async def send_location( payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) @@ -612,7 +623,8 @@ async def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_markup=None, timeout=None, google_place_id=None, - google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): + google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -637,13 +649,15 @@ async def send_venue( payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): + protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -664,6 +678,8 @@ async def send_contact( payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) @@ -682,7 +698,8 @@ async def send_chat_action(token, chat_id, action, timeout=None, message_thread_ async def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, - protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None): + protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -728,6 +745,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m payload['has_spoiler'] = has_spoiler if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -735,7 +754,7 @@ async def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, width=None, height=None, protect_content=None, message_thread_id=None, - has_spoiler=None,reply_parameters=None, business_connection_id=None): + has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -779,12 +798,14 @@ async def send_animation( payload['has_spoiler'] = has_spoiler if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None): + protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None, message_effect_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -814,12 +835,14 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_ payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, - message_thread_id=None,reply_parameters=None, business_connection_id=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -855,12 +878,15 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, rep payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, - caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): + caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, + message_effect_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -902,13 +928,15 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, - message_thread_id=None, emoji=None,reply_parameters=None, business_connection_id=None): + message_thread_id=None, emoji=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -951,6 +979,8 @@ async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_m payload['emoji'] = emoji if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1400,7 +1430,7 @@ async def delete_message(token, chat_id, message_id, timeout=None): async def send_game( token, chat_id, game_short_name, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None): + protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1417,6 +1447,8 @@ async def send_game( payload['message_thread_id'] = message_thread_id if business_connection_id: payload['business_connection_id'] = business_connection_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) @@ -1482,7 +1514,7 @@ async def send_invoice( send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None, reply_parameters=None): + protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1563,6 +1595,8 @@ async def send_invoice( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) @@ -1809,7 +1843,7 @@ async def send_poll( 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, question_parse_mode=None, question_entities=None): + reply_parameters=None,business_connection_id=None, question_parse_mode=None, question_entities=None, message_effect_id=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -1859,6 +1893,8 @@ async def send_poll( payload['question_parse_mode'] = question_parse_mode if question_entities: payload['question_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(question_entities)) + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) From 4830720ce8c28d8f5ff52ce736b60a37f91d15c5 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 18:15:32 +0400 Subject: [PATCH 1579/1808] Added the parameter show_caption_above_media to the methods sendAnimation, sendPhoto, sendVideo, copyMessage, and editMessageCaption. --- telebot/__init__.py | 41 ++++++++++++++++++++++++++++---------- telebot/apihelper.py | 22 +++++++++++++++----- telebot/async_telebot.py | 42 +++++++++++++++++++++++++++++---------- telebot/asyncio_helper.py | 20 ++++++++++++++----- 4 files changed, 95 insertions(+), 30 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 56a24e0c7..9c23526eb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1818,7 +1818,7 @@ def copy_message( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.MessageID: + reply_parameters: Optional[types.ReplyParameters]=None, """ Use this method to copy messages of any kind. @@ -1866,6 +1866,9 @@ def copy_message( :param reply_parameters: Additional parameters for replies to messages :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -1896,7 +1899,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, show_caption_above_media=show_caption_above_media)) def delete_message(self, chat_id: Union[int, str], message_id: int, @@ -2138,7 +2141,8 @@ def send_photo( has_spoiler: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + show_caption_above_media: Optional[bool]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2195,6 +2199,9 @@ def send_photo( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2227,7 +2234,7 @@ def send_photo( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id)) + message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media)) @@ -2724,7 +2731,8 @@ def send_video( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + show_caption_above_media: Optional[bool]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2801,6 +2809,9 @@ def send_video( :param message_effect_id: Identifier of a message effect :type message_effect_id: :obj:`str` + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2841,7 +2852,8 @@ def send_video( supports_streaming=supports_streaming, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, height=height, width=width, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, - reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id) + reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media) ) @@ -2865,7 +2877,8 @@ def send_animation( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + show_caption_above_media: Optional[bool]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2941,6 +2954,9 @@ def send_animation( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2976,7 +2992,8 @@ def send_animation( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, width=width, height=height, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - has_spoiler=has_spoiler, business_connection_id=business_connection_id, message_effect_id=message_effect_id) + has_spoiler=has_spoiler, business_connection_id=business_connection_id, message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media) ) @@ -5479,7 +5496,8 @@ def edit_message_caption( inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + show_caption_above_media: Optional[bool]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. @@ -5506,6 +5524,9 @@ def edit_message_caption( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`InlineKeyboardMarkup` + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` | :obj:`bool` """ @@ -5513,7 +5534,7 @@ def edit_message_caption( result = apihelper.edit_message_caption( self.token, caption, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - parse_mode=parse_mode, caption_entities=caption_entities, reply_markup=reply_markup) + parse_mode=parse_mode, caption_entities=caption_entities, reply_markup=reply_markup, show_caption_above_media=show_caption_above_media) if type(result) == bool: return result diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d230639c7..78dee41a8 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -485,7 +485,8 @@ def send_photo( caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, + show_caption_above_media=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -519,6 +520,8 @@ def send_photo( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return _make_request(token, method_url, params=payload, files=files, method='post') @@ -712,7 +715,8 @@ def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=Non def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, + show_caption_above_media=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -760,6 +764,8 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return _make_request(token, method_url, params=payload, files=files, method='post') @@ -768,7 +774,7 @@ def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None, - has_spoiler=None, business_connection_id=None, message_effect_id=None): + has_spoiler=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -814,6 +820,8 @@ def send_animation( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1390,7 +1398,7 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, caption_entities=None,reply_markup=None): + parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -1405,6 +1413,8 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return _make_request(token, method_url, params=payload, method='post') @@ -2016,7 +2026,7 @@ def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notifica return _make_request(token, method_url, params=payload) def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, - message_thread_id=None, protect_content=None, remove_caption=None): + message_thread_id=None, protect_content=None, remove_caption=None, show_caption_above_media=None): method_url = 'copyMessages' payload = { 'chat_id': chat_id, @@ -2031,6 +2041,8 @@ def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notificatio payload['protect_content'] = protect_content if remove_caption is not None: payload['remove_caption'] = remove_caption + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return _make_request(token, method_url, params=payload) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ae080aea8..abd45a3c1 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3234,7 +3234,8 @@ async def copy_message( reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - reply_parameters: Optional[types.ReplyParameters]=None) -> types.MessageID: + reply_parameters: Optional[types.ReplyParameters]=None, + show_caption_above_media: Optional[bool]=None) -> types.MessageID: """ Use this method to copy messages of any kind. @@ -3282,6 +3283,9 @@ async def copy_message( :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -3313,7 +3317,7 @@ async def copy_message( return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_markup, - timeout, protect_content, message_thread_id, reply_parameters)) + timeout, protect_content, message_thread_id, reply_parameters, show_caption_above_media=show_caption_above_media)) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -3543,7 +3547,8 @@ async def send_photo( has_spoiler: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + show_caption_above_media: Optional[bool]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -3600,6 +3605,9 @@ async def send_photo( :param message_effect_id: Unique identifier for the message effect :type message_effect_id: :obj:`str` + + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3633,7 +3641,8 @@ async def send_photo( await asyncio_helper.send_photo( self.token, chat_id, photo, caption, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -4131,7 +4140,8 @@ async def send_video( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + show_caption_above_media: Optional[bool]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -4208,6 +4218,9 @@ async def send_video( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4249,7 +4262,8 @@ async def send_video( await asyncio_helper.send_video( self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, - caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -4271,7 +4285,8 @@ async def send_animation( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + show_caption_above_media: Optional[bool]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -4347,6 +4362,9 @@ async def send_animation( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4384,7 +4402,7 @@ async def send_animation( self.token, chat_id, animation, duration, caption, reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, - message_effect_id=message_effect_id)) + message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -6806,7 +6824,8 @@ async def edit_message_caption( inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + show_caption_above_media: Optional[bool]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. @@ -6833,13 +6852,16 @@ async def edit_message_caption( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`InlineKeyboardMarkup` + :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. + :type show_caption_above_media: :obj:`bool` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` | :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode result = await asyncio_helper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, caption_entities, reply_markup) + parse_mode, caption_entities, reply_markup, show_caption_above_media=show_caption_above_media) if type(result) == bool: return result return types.Message.de_json(result) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index aa6a5cd68..630a351a9 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -416,7 +416,7 @@ async def forward_message( async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, - reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None): + reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -437,6 +437,8 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return await _process_request(token, method_url, params=payload) @@ -474,7 +476,7 @@ async def send_photo( parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, - business_connection_id=None, message_effect_id=None): + business_connection_id=None, message_effect_id=None, show_caption_above_media=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -508,6 +510,8 @@ async def send_photo( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -699,7 +703,7 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, show_caption_above_media=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -747,6 +751,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -754,7 +760,7 @@ async def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, width=None, height=None, protect_content=None, message_thread_id=None, - has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): + has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -800,6 +806,8 @@ async def send_animation( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1370,7 +1378,7 @@ async def edit_message_text(token, text, chat_id=None, message_id=None, inline_m async def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, caption_entities=None,reply_markup=None): + parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -1385,6 +1393,8 @@ async def edit_message_caption(token, caption, chat_id=None, message_id=None, in payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return await _process_request(token, method_url, params=payload, method='post') From a95d100b23d1213d63b2954a3615fd11ed64d58f Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 28 May 2024 18:24:17 +0400 Subject: [PATCH 1580/1808] entity fixes --- telebot/__init__.py | 4 ++++ telebot/formatting.py | 2 ++ telebot/types.py | 12 ++++++------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9c23526eb..263802de0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1819,6 +1819,7 @@ def copy_message( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, + show_caption_above_media: Optional[bool]=None) -> types.MessageID: """ Use this method to copy messages of any kind. @@ -1829,6 +1830,7 @@ def copy_message( :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) :type from_chat_id: :obj:`int` or :obj:`str` + :param message_id: Message identifier in the chat specified in from_chat_id :type message_id: :obj:`int` @@ -1872,7 +1874,9 @@ def copy_message( :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` + """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification parse_mode = self.parse_mode if (parse_mode is None) else parse_mode protect_content = self.protect_content if (protect_content is None) else protect_content diff --git a/telebot/formatting.py b/telebot/formatting.py index dd7309416..09687ec9f 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -397,6 +397,8 @@ def apply_html_entities(text: str, entities: Optional[List], custom_subs: Option "spoiler": "{text}", "custom_emoji": "{text}", "blockquote": "
{text}
", + "expandable_blockquote": "
{text}
", + } if custom_subs: diff --git a/telebot/types.py b/telebot/types.py index 23146cffe..6479f3468 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1598,12 +1598,12 @@ class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#messageentity - :param type: Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), “cashtag” - ($USD), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” - (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), - “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “code” - (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users - without usernames), “custom_emoji” (for inline custom emoji stickers) + :param type: Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), “cashtag” ($USD), + “bot_command” (/start@jobs_bot),“url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), + “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), + “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), + “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), + “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers) :type type: :obj:`str` :param offset: Offset in UTF-16 code units to the start of the entity From 90714218761d87194121c82774197a745f3c84d8 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 31 May 2024 14:54:47 +0400 Subject: [PATCH 1581/1808] Fix #2287 Fix #2287 --- telebot/types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index cfbe447fd..1438cf762 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9899,7 +9899,6 @@ 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): From 2e4f92d96982021dce8dbea77562130388c8c264 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 31 May 2024 19:24:09 +0400 Subject: [PATCH 1582/1808] Fixes to bot API 7.4 --- telebot/__init__.py | 10 ++++++---- telebot/apihelper.py | 12 ++++++++---- telebot/async_telebot.py | 10 ++++++---- telebot/asyncio_helper.py | 14 +++++++++----- telebot/types.py | 10 +++++----- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 263802de0..99c492954 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4954,7 +4954,7 @@ def get_game_high_scores( def send_invoice( self, chat_id: Union[int, str], title: str, description: str, - invoice_payload: str, provider_token: str, currency: str, + invoice_payload: str, provider_token: Union[str, None], currency: str, prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, photo_url: Optional[str]=None, photo_size: Optional[int]=None, photo_width: Optional[int]=None, photo_height: Optional[int]=None, @@ -4993,7 +4993,8 @@ def send_invoice( use for your internal processes. :type invoice_payload: :obj:`str` - :param provider_token: Payments provider token, obtained via @Botfather + :param provider_token: Payments provider token, obtained via @Botfather; Pass None to omit the parameter + to use "XTR" currency :type provider_token: :obj:`str` :param currency: Three-letter ISO 4217 currency code, @@ -5125,7 +5126,7 @@ def send_invoice( ) def create_invoice_link(self, - title: str, description: str, payload:str, provider_token: str, + title: str, description: str, payload:str, provider_token: Union[str, None], currency: str, prices: List[types.LabeledPrice], max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, @@ -5159,7 +5160,8 @@ def create_invoice_link(self, use for your internal processes. :type payload: :obj:`str` - :param provider_token: Payments provider token, obtained via @Botfather + :param provider_token: Payments provider token, obtained via @Botfather; Pass None to omit the parameter + to use "XTR" currency :type provider_token: :obj:`str` :param currency: Three-letter ISO 4217 currency code, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 78dee41a8..4d984dcf1 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1580,7 +1580,7 @@ def send_invoice( """ method_url = r'sendInvoice' payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, - 'provider_token': provider_token, 'currency': currency, + 'currency': currency, 'prices': _convert_list_json_serializable(prices)} if start_parameter: payload['start_parameter'] = start_parameter @@ -1626,6 +1626,8 @@ def send_invoice( payload['reply_parameters'] = reply_parameters.to_json() if message_effect_id: payload['message_effect_id'] = message_effect_id + if provider_token is not None: + payload['provider_token'] = provider_token return _make_request(token, method_url, params=payload) @@ -1666,13 +1668,13 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No def refund_star_payment(token, user_id, telegram_payment_charge_id): method_url = 'refundStarPayment' payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id} - return _make_request(token, method_url, params=payload, method='post') + return _make_request(token, method_url, params=payload) def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' payload = {'chat_id': chat_id} - return _make_request(token, method_url, params=payload, method='post') + return _make_request(token, method_url, params=payload) # InlineQuery @@ -1835,7 +1837,7 @@ def create_invoice_link(token, title, description, payload, provider_token, need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, send_email_to_provider=None, is_flexible=None): method_url = r'createInvoiceLink' - payload = {'title': title, 'description': description, 'payload': payload, 'provider_token': provider_token, + payload = {'title': title, 'description': description, 'payload': payload, 'currency': currency, 'prices': _convert_list_json_serializable(prices)} if max_tip_amount: payload['max_tip_amount'] = max_tip_amount @@ -1865,6 +1867,8 @@ def create_invoice_link(token, title, description, payload, provider_token, payload['send_email_to_provider'] = send_email_to_provider if is_flexible is not None: payload['is_flexible'] = is_flexible + if provider_token is not None: + payload['provider_token'] = provider_token return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index abd45a3c1..be3135739 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6291,7 +6291,7 @@ async def get_game_high_scores( async def send_invoice( self, chat_id: Union[int, str], title: str, description: str, - invoice_payload: str, provider_token: str, currency: str, + invoice_payload: str, provider_token: Union[str, None], currency: str, prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, photo_url: Optional[str]=None, photo_size: Optional[int]=None, photo_width: Optional[int]=None, photo_height: Optional[int]=None, @@ -6330,7 +6330,8 @@ async def send_invoice( use for your internal processes. :type invoice_payload: :obj:`str` - :param provider_token: Payments provider token, obtained via @Botfather + :param provider_token: Payments provider token, obtained via @Botfather; Pass None to omit the parameter + to use "XTR" currency :type provider_token: :obj:`str` :param currency: Three-letter ISO 4217 currency code, @@ -6458,7 +6459,7 @@ async def send_invoice( async def create_invoice_link(self, - title: str, description: str, payload:str, provider_token: str, + title: str, description: str, payload:str, provider_token: Union[str, None], currency: str, prices: List[types.LabeledPrice], max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, @@ -6492,7 +6493,8 @@ async def create_invoice_link(self, use for your internal processes. :type payload: :obj:`str` - :param provider_token: Payments provider token, obtained via @Botfather + :param provider_token: Payments provider token, obtained via @Botfather; Pass None to omit the parameter + to use "XTR" currency :type provider_token: :obj:`str` :param currency: Three-letter ISO 4217 currency code, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 630a351a9..e7838fbb4 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1561,8 +1561,8 @@ async def send_invoice( """ method_url = r'sendInvoice' payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload, - 'provider_token': provider_token, 'currency': currency, - 'prices': await _convert_list_json_serializable(prices)} + 'currency': currency, + 'prices': await _convert_list_json_serializable(prices)} if start_parameter: payload['start_parameter'] = start_parameter if photo_url: @@ -1607,6 +1607,8 @@ async def send_invoice( payload['message_thread_id'] = message_thread_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if provider_token is not None: + payload['provider_token'] = provider_token return await _process_request(token, method_url, params=payload) @@ -1648,13 +1650,13 @@ async def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_mess async def refund_star_payment(token, user_id, telegram_payment_charge_id): method_url = 'refundStarPayment' payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id} - return await _process_request(token, method_url, params=payload, method='post') + return await _process_request(token, method_url, params=payload) async def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' payload = {'chat_id': chat_id} - return await _process_request(token, method_url, params=payload, method='post') + return await _process_request(token, method_url, params=payload) # InlineQuery @@ -1812,7 +1814,7 @@ async def create_invoice_link(token, title, description, payload, provider_token need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, send_email_to_provider=None, is_flexible=None): method_url = r'createInvoiceLink' - payload = {'title': title, 'description': description, 'payload': payload, 'provider_token': provider_token, + payload = {'title': title, 'description': description, 'payload': payload, 'currency': currency, 'prices': await _convert_list_json_serializable(prices)} if max_tip_amount: payload['max_tip_amount'] = max_tip_amount @@ -1842,6 +1844,8 @@ async def create_invoice_link(token, title, description, payload, provider_token payload['send_email_to_provider'] = send_email_to_provider if is_flexible is not None: payload['is_flexible'] = is_flexible + if provider_token is not None: + payload['provider_token'] = provider_token return await _process_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 6479f3468..c36cb8131 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -4494,7 +4494,7 @@ def to_dict(self): json_dict['photo_height'] = self.photo_height if self.description: json_dict['description'] = self.description - if self.show_caption_above_media: + if self.show_caption_above_media is not None: json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -4593,7 +4593,7 @@ def to_dict(self): json_dict['gif_duration'] = self.gif_duration if self.thumbnail_mime_type: json_dict['thumbnail_mime_type'] = self.thumbnail_mime_type - if self.show_caption_above_media: + if self.show_caption_above_media is not None: json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -4692,7 +4692,7 @@ def to_dict(self): json_dict['mpeg4_duration '] = self.mpeg4_duration if self.thumbnail_mime_type: json_dict['thumbnail_mime_type'] = self.thumbnail_mime_type - if self.show_caption_above_media: + if self.show_caption_above_media is not None: json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -4790,7 +4790,7 @@ def to_dict(self): json_dict['video_duration'] = self.video_duration if self.description: json_dict['description'] = self.description - if self.show_caption_above_media: + if self.show_caption_above_media is not None: json_dict['show_caption_above_media'] = self.show_caption_above_media return json_dict @@ -5381,7 +5381,7 @@ def to_json(self): json_dict['parse_mode'] = self.parse_mode if self.caption_entities: json_dict['caption_entities'] = MessageEntity.to_list_of_dicts(self.caption_entities) - if self.show_caption_above_media: + if self.show_caption_above_media is not None: json_dict['show_caption_above_media'] = self.show_caption_above_media return json.dumps(json_dict) From eb3b6998c4638394f273ce04620759aac719fc61 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 1 Jun 2024 20:44:07 +0300 Subject: [PATCH 1583/1808] Bump version to 4.19.0... and fix Bump version to 4.19.0 and fix MessageEntity.to_dict/to_json --- docs/source/conf.py | 2 +- pyproject.toml | 2 +- telebot/types.py | 4 ++-- telebot/version.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 7e46d02c3..ec7735312 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.18.1' +release = '4.19.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 7eae1722e..ad0f75a97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.18.1" +version = "4.19.0" description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} diff --git a/telebot/types.py b/telebot/types.py index 1a0b1efec..3358965b5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1665,8 +1665,8 @@ def to_dict(self): "offset": self.offset, "length": self.length, "url": self.url, - "user": self.user, - "language": self.language, + "user": self.user.to_dict(), + "language": self.language, "custom_emoji_id": self.custom_emoji_id} diff --git a/telebot/version.py b/telebot/version.py index 830950124..d9652d819 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.18.1' +__version__ = '4.19.0' From 67230b8ed26f14cd3818f18328d953c519cd768b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 2 Jun 2024 21:26:21 +0300 Subject: [PATCH 1584/1808] Fix MessageEntity.user if None --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 3358965b5..6ec9d8d4d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1665,7 +1665,7 @@ def to_dict(self): "offset": self.offset, "length": self.length, "url": self.url, - "user": self.user.to_dict(), + "user": self.user.to_dict() if self.user else None, "language": self.language, "custom_emoji_id": self.custom_emoji_id} From 9d9988668d3b186f6257d2ed780bbc711908db79 Mon Sep 17 00:00:00 2001 From: Moshe <62907797+moshe-coh@users.noreply.github.com> Date: Fri, 7 Jun 2024 01:25:39 +0300 Subject: [PATCH 1585/1808] fixed copy_message in apihelper.py --- telebot/apihelper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4d984dcf1..4c642e8f5 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -428,7 +428,7 @@ def forward_message( def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, - reply_parameters=None): + reply_parameters=None, show_caption_above_media=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -449,6 +449,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['protect_content'] = protect_content if message_thread_id is not None: payload['message_thread_id'] = message_thread_id + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media return _make_request(token, method_url, params=payload) From a3f7e307b0b9b8a07c637b30f4a4871ab45fd449 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 8 Jun 2024 20:55:10 +0300 Subject: [PATCH 1586/1808] Bump version for bugfix release --- docs/source/conf.py | 2 +- pyproject.toml | 2 +- telebot/__init__.py | 19 ++++++++++--------- telebot/apihelper.py | 8 ++++---- telebot/version.py | 2 +- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ec7735312..da36cc2fd 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.19.0' +release = '4.19.1' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index ad0f75a97..4cc5b3af0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.19.0" +version = "4.19.1" description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} diff --git a/telebot/__init__.py b/telebot/__init__.py index 99c492954..b70016506 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1874,7 +1874,6 @@ def copy_message( :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` - """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification @@ -1903,7 +1902,8 @@ 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, show_caption_above_media=show_caption_above_media)) + message_thread_id=message_thread_id, reply_parameters=reply_parameters, + show_caption_above_media=show_caption_above_media)) def delete_message(self, chat_id: Union[int, str], message_id: int, @@ -1950,13 +1950,13 @@ def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): :type message_ids: :obj:`list` of :obj:`int` :return: Returns True on success. - """ return apihelper.delete_messages(self.token, chat_id, message_ids) - def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, - message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: + def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], + disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, + protect_content: Optional[bool]=None) -> List[types.MessageID]: """ Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content can't be forwarded. Album grouping is kept for forwarded messages. @@ -2237,9 +2237,9 @@ def send_photo( self.token, chat_id, photo, caption=caption, reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, - message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media)) - + message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, + business_connection_id=business_connection_id, message_effect_id=message_effect_id, + show_caption_above_media=show_caption_above_media)) def send_audio( @@ -5540,7 +5540,8 @@ def edit_message_caption( result = apihelper.edit_message_caption( self.token, caption, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - parse_mode=parse_mode, caption_entities=caption_entities, reply_markup=reply_markup, show_caption_above_media=show_caption_above_media) + parse_mode=parse_mode, caption_entities=caption_entities, reply_markup=reply_markup, + show_caption_above_media=show_caption_above_media) if type(result) == bool: return result diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 4c642e8f5..cb0c77467 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -487,8 +487,8 @@ def send_photo( caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, - show_caption_above_media=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None, show_caption_above_media=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -717,8 +717,8 @@ def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=Non def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, - message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, - show_caption_above_media=None): + message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, + message_effect_id=None, show_caption_above_media=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None diff --git a/telebot/version.py b/telebot/version.py index d9652d819..248d79ff7 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.19.0' +__version__ = '4.19.1' From 5de4b1758e917cb6604c5795788f1ce29a9e738b Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 10 Jun 2024 19:58:37 +0300 Subject: [PATCH 1587/1808] UsersShared fix --- telebot/types.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 6ec9d8d4d..768775f5b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9121,13 +9121,8 @@ class UsersShared(JsonDeserializable): :param request_id: Identifier of the request :type request_id: :obj:`int` - :param user_ids: Array of :obj:`types.SharedUser` of the shared users. These numbers may have more than 32 significant bits - and some programming languages may have difficulty/silent defects in interpreting them. - But they have at most 52 significant bits, so 64-bit integers or double-precision float - types are safe for storing these identifiers. The bot may not have access to the users and - could be unable to use these identifiers unless the users are already known to the bot by - some other means. - :type user_ids: :obj:`list` of :obj:`types.SharedUser` + :param users: Information about users shared with the bot + :type users: :obj:`list` of :obj:`types.SharedUser` :return: Instance of the class :rtype: :class:`UsersShared` @@ -9136,20 +9131,23 @@ class UsersShared(JsonDeserializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - obj['user_ids'] = [SharedUser.de_json(user) for user in obj['user_ids']] - + obj['users'] = [SharedUser.de_json(user) for user in obj['users']] return cls(**obj) - def __init__(self, request_id, user_ids: SharedUser, **kwargs): + def __init__(self, request_id, users: List[SharedUser], **kwargs): self.request_id = request_id - self.user_ids = user_ids + self.users = users @property def user_id(self): logger.warning('The parameter "user_id" is deprecated, use "user_ids" instead') return None - + + @property + def user_ids(self): + logger.warning('The parameter "user_ids" is deprecated, use "users" instead') + return self.users + class ChatBoostUpdated(JsonDeserializable): """ From 2e32b412ab966a598f53091b7e05cfeb87562d28 Mon Sep 17 00:00:00 2001 From: Dieter van der Westhuizen Date: Fri, 14 Jun 2024 14:23:47 +0200 Subject: [PATCH 1588/1808] Update README.md Fixed spelling error in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 770186d5c..5eb40ce3a 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ Handle shipping queries `@bot.shipping_query_handler() # <- passes a ShippingQuery type object to your function` #### Pre Checkout Query Handler -Handle pre checkoupt queries +Handle pre checkout queries `@bot.pre_checkout_query_handler() # <- passes a PreCheckoutQuery type object to your function` #### Poll Handler From 6fe6f38c6ea6c44421fb02c066ea4bdd97212114 Mon Sep 17 00:00:00 2001 From: Dieter van der Westhuizen Date: Fri, 14 Jun 2024 14:35:35 +0200 Subject: [PATCH 1589/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5eb40ce3a..5d13646bf 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ def query_text(inline_query): #### Chosen Inline handler -Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback +Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forget to add the /setinlinefeedback command for @Botfather. More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback) From ab6374ba62bcb5bb388b77b43c74a9edf3d9b487 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 15 Jun 2024 20:21:11 +0500 Subject: [PATCH 1590/1808] Fix 1: fixed message_thread_id for copy_messages --- telebot/async_telebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index be3135739..4f7593d03 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3440,8 +3440,8 @@ async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, """ disable_notification = self.disable_notification if disable_notification is None else disable_notification protect_content = self.protect_content if protect_content is None else protect_content - result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, - protect_content, message_thread_id, remove_caption) + result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, + protect_content, remove_caption) return [types.MessageID.de_json(message_id) for message_id in result] async def send_dice( From bd36b09c5ddb789a5ff6fdbfbb0a41d228928d2d Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 15 Jun 2024 20:24:49 +0500 Subject: [PATCH 1591/1808] Fix bug in async middlewares --- telebot/async_telebot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4f7593d03..6a7c5fe08 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -523,7 +523,6 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up if isinstance(middleware_result, CancelUpdate): return elif isinstance(middleware_result, SkipHandler): - await middleware.post_process(message, data, handler_error) skip_handlers = True if handlers and not(skip_handlers): From 5c50facc066280ee639dbaabce14c238191018cd Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 16 Jun 2024 23:38:05 +0300 Subject: [PATCH 1592/1808] Bump version for bugfix release --- 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 da36cc2fd..34f930a66 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.19.1' +release = '4.19.2' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 4cc5b3af0..b43d71dd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.19.1" +version = "4.19.2" 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 248d79ff7..405f70f1a 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.19.1' +__version__ = '4.19.2' From 76f8b790e0a685c120558cf0e7463de9a9137cd9 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 18 Jun 2024 16:36:01 +0500 Subject: [PATCH 1593/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d13646bf..10694e4b0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.4! +##

Supported Bot API version: 7.5!

Official documentation

Official ru documentation

From eec78c931b1b0cae506fc467779d1282f1534df1 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 18 Jun 2024 16:52:58 +0500 Subject: [PATCH 1594/1808] Added the classes StarTransactions, StarTransaction, TransactionPartner and RevenueWithdrawalState, containing information about Telegram Star transactions involving the bot. --- telebot/types.py | 242 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 1438cf762..588e38919 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10005,3 +10005,245 @@ def de_json(cls, json_string): def __init__(self, type, **kwargs): self.type: BackgroundType = type + + +class RevenueWithdrawalState(JsonDeserializable): + """ + This object describes the state of a revenue withdrawal operation. Currently, it can be one of + RevenueWithdrawalStatePending + RevenueWithdrawalStateSucceeded + RevenueWithdrawalStateFailed + + Telegram documentation: https://core.telegram.org/bots/api#revenuewithdrawalstate + + :param type: Type of the state, always “pending” or “succeeded” or “failed” + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`RevenueWithdrawalStatePending` or :class:`RevenueWithdrawalStateSucceeded` or :class:`RevenueWithdrawalStateFailed` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if obj["type"] == "pending": + return RevenueWithdrawalStatePending.de_json(obj) + elif obj["type"] == "succeeded": + return RevenueWithdrawalStateSucceeded.de_json(obj) + elif obj["type"] == "failed": + return RevenueWithdrawalStateFailed.de_json(obj) + return None + + +class RevenueWithdrawalStatePending(RevenueWithdrawalState): + """ + The withdrawal is in progress. + + Telegram documentation: https://core.telegram.org/bots/api#revenuewithdrawalstatepending + + :param type: Type of the state, always “pending” + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`RevenueWithdrawalStatePending` + """ + + def __init__(self, type, **kwargs): + self.type: str = type + + +class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState): + """ + The withdrawal succeeded. + + Telegram documentation: https://core.telegram.org/bots/api#revenuewithdrawalstatesucceeded + + :param type: Type of the state, always “succeeded” + :type type: :obj:`str` + + :param date: Date the withdrawal was completed in Unix time + :type date: :obj:`int` + + :param url: An HTTPS URL that can be used to see transaction details + :type url: :obj:`str` + + :return: Instance of the class + :rtype: :class:`RevenueWithdrawalStateSucceeded` + """ + + def __init__(self, type, date, url, **kwargs): + self.type: str = type + self.date: int = date + self.url: str = url + + +class RevenueWithdrawalStateFailed(RevenueWithdrawalState): + """ + The withdrawal failed and the transaction was refunded. + + Telegram documentation: https://core.telegram.org/bots/api#revenuewithdrawalstatefailed + + :param type: Type of the state, always “failed” + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`RevenueWithdrawalStateFailed` + """ + + def __init__(self, type, **kwargs): + self.type: str = type + + +class TransactionPartner(JsonDeserializable): + """ + This object describes the source of a transaction, or its recipient for outgoing transactions. Currently, it can be one of + TransactionPartnerFragment + TransactionPartnerUser + TransactionPartnerOther + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartner + + :param type: Type of the transaction partner + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerFragment` or :class:`TransactionPartnerUser` or :class:`TransactionPartnerOther` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if obj["type"] == "fragment": + return TransactionPartnerFragment.de_json(obj) + elif obj["type"] == "user": + return TransactionPartnerUser.de_json(obj) + elif obj["type"] == "other": + return TransactionPartnerOther.de_json(obj) + +class TransactionPartnerFragment(TransactionPartner): + """ + Describes a withdrawal transaction with Fragment. + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartnerfragment + + :param type: Type of the transaction partner, always “fragment” + :type type: :obj:`str` + + :param withdrawal_state: Optional. State of the transaction if the transaction is outgoing + :type withdrawal_state: :class:`RevenueWithdrawalState` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerFragment` + + """ + + def __init__(self, type, withdrawal_state=None, **kwargs): + self.type: str = type + self.withdrawal_state: Optional[RevenueWithdrawalState] = withdrawal_state + + +class TransactionPartnerUser(TransactionPartner): + """ + Describes a transaction with a user. + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartneruser + + :param type: Type of the transaction partner, always “user” + :type type: :obj:`str` + + :param user: Information about the user + :type user: :class:`User` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerUser` + """ + + def __init__(self, type, user, **kwargs): + self.type: str = type + self.user: User = user + +class TransactionPartnerOther(TransactionPartner): + """ + Describes a transaction with an unknown source or recipient. + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartnerother + + :param type: Type of the transaction partner, always “other” + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerOther` + """ + + def __init__(self, type, **kwargs): + self.type: str = type + + +class StarTransaction(JsonDeserializable): + """ + Describes a Telegram Star transaction. + + Telegram documentation: https://core.telegram.org/bots/api#startransaction + + :param id: Unique identifier of the transaction. Coincides with the identifer of the original transaction for refund transactions. Coincides with SuccessfulPayment.telegram_payment_charge_id for successful incoming payments from users. + :type id: :obj:`str` + + :param amount: Number of Telegram Stars transferred by the transaction + :type amount: :obj:`int` + + :param date: Date the transaction was created in Unix time + :type date: :obj:`int` + + :param source: Optional. Source of an incoming transaction (e.g., a user purchasing goods or services, Fragment refunding a failed withdrawal). Only for incoming transactions + :type source: :class:`TransactionPartner` + + :param receiver: Optional. Receiver of an outgoing transaction (e.g., a user for a purchase refund, Fragment for a withdrawal). Only for outgoing transactions + :type receiver: :class:`TransactionPartner` + + :return: Instance of the class + :rtype: :class:`StarTransaction` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'source' in obj: + obj['source'] = TransactionPartner.de_json(obj['source']) + if 'receiver' in obj: + obj['receiver'] = TransactionPartner.de_json(obj['receiver']) + return cls(**obj) + + def __init__(self, id, amount, date, source=None, receiver=None, **kwargs): + self.id: str = id + self.amount: int = amount + self.date: int = date + self.source: Optional[TransactionPartner] = source + self.receiver: Optional[TransactionPartner] = receiver + + +class StarTransactions(JsonDeserializable): + """ + Contains a list of Telegram Star transactions. + + Telegram documentation: https://core.telegram.org/bots/api#startransactions + + :param transactions: The list of transactions + :type transactions: :obj:`list` of :class:`StarTransaction` + + :return: Instance of the class + :rtype: :class:`StarTransactions` + + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['transactions'] = [StarTransaction.de_json(transaction) for transaction in obj['transactions']] + return cls(**obj) + + def __init__(self, transactions, **kwargs): + self.transactions: List[StarTransaction] = transactions From dbe9e3232b04242c5da099885d7e3638867a00d1 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 18 Jun 2024 16:59:24 +0500 Subject: [PATCH 1595/1808] Added the method getStarTransactions that can be used to get the list of all Telegram Star transactions for the bot. --- telebot/__init__.py | 21 +++++++++++++++++++++ telebot/apihelper.py | 10 ++++++++++ telebot/async_telebot.py | 19 +++++++++++++++++++ telebot/asyncio_helper.py | 8 ++++++++ 4 files changed, 58 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index b70016506..b43ea89c8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5478,6 +5478,27 @@ def answer_pre_checkout_query( """ return apihelper.answer_pre_checkout_query( self.token, pre_checkout_query_id, ok, error_message=error_message) + + + def get_star_transactions(self, offset: Optional[int]=None, limit: Optional[int]=None) -> types.StarTransactions: + """ + Returns the bot's Telegram Star transactions in chronological order. On success, returns a StarTransactions object. + + Telegram documentation: https://core.telegram.org/bots/api#getstartransactions + + :param offset: Number of transactions to skip in the response + :type offset: :obj:`int` + + :param limit: The maximum number of transactions to be retrieved. Values between 1-100 are accepted. Defaults to 100. + :type limit: :obj:`int` + + :return: On success, returns a StarTransactions object. + :rtype: :obj:`types.StarTransactions` + """ + return types.StarTransactions.de_json( + apihelper.get_star_transactions(self.token, offset=offset, limit=limit) + ) + def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index cb0c77467..59039ce56 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1667,6 +1667,16 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No payload['error_message'] = error_message return _make_request(token, method_url, params=payload) +def get_star_transactions(token, offset=None, limit=None): + method_url = 'getStarTransactions' + payload = {} + if offset: + payload['offset'] = offset + if limit: + payload['limit'] = limit + return _make_request(token, method_url, params=payload) + + def refund_star_payment(token, user_id, telegram_payment_charge_id): method_url = 'refundStarPayment' payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6a7c5fe08..7fe6e48af 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6802,6 +6802,25 @@ async def answer_pre_checkout_query( """ return await asyncio_helper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) + + async def get_star_transactions(self, offset: Optional[int]=None, limit: Optional[int]=None) -> types.StarTransactions: + """ + Returns the bot's Telegram Star transactions in chronological order. + + Telegram documentation: https://core.telegram.org/bots/api#getstartransactions + + :param offset: Number of transactions to skip in the response + :type offset: :obj:`int` + + :param limit: The maximum number of transactions to be retrieved. Values between 1-100 are accepted. Defaults to 100. + :type limit: :obj:`int` + + :return: On success, returns a StarTransactions object. + :rtype: :obj:`types.StarTransactions` + """ + + return types.StarTransactions.de_json(await asyncio_helper.get_star_transactions(self.token, offset, limit)) + async def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> bool: """ Refunds a successful payment in Telegram Stars. Returns True on success. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index e7838fbb4..077ed3e61 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1646,6 +1646,14 @@ async def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_mess payload['error_message'] = error_message return await _process_request(token, method_url, params=payload) +async def get_star_transactions(token, offset=None, limit=None): + method_url = 'getStarTransactions' + payload = {} + if offset: + payload['offset'] = offset + if limit: + payload['limit'] = limit + return await _process_request(token, method_url, params=payload) async def refund_star_payment(token, user_id, telegram_payment_charge_id): method_url = 'refundStarPayment' From 67e2ec5fa0f75c05a1c76552969989aa4cf9d234 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 18 Jun 2024 17:10:46 +0500 Subject: [PATCH 1596/1808] Added the parameter business_connection_id to the methods editMessageText, editMessageMedia, editMessageCaption, editMessageLiveLocation, stopMessageLiveLocation and editMessageReplyMarkup, allowing the bot to edit business messages. --- telebot/__init__.py | 47 ++++++++++++++++++++++++++++++--------- telebot/apihelper.py | 24 +++++++++++++++----- telebot/async_telebot.py | 46 +++++++++++++++++++++++++++++--------- telebot/asyncio_helper.py | 24 +++++++++++++++----- 4 files changed, 107 insertions(+), 34 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b43ea89c8..369ce77a3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3324,6 +3324,7 @@ def edit_message_live_location( heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, live_period: Optional[int]=None, + business_connection_id: Optional[str]=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 @@ -3366,6 +3367,9 @@ def edit_message_live_location( :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` + :param business_connection_id: Identifier of a business connection + :type business_connection_id: :obj:`str` + :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 """ @@ -3373,7 +3377,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, live_period=live_period) + proximity_alert_radius=proximity_alert_radius, live_period=live_period, business_connection_id=business_connection_id) ) @@ -3382,7 +3386,8 @@ def stop_message_live_location( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - timeout: Optional[int]=None) -> types.Message or bool: + timeout: Optional[int]=None, + business_connection_id: Optional[str]=None) -> types.Message or bool: """ Use this method to stop updating a live location message before live_period expires. On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. @@ -3405,13 +3410,16 @@ def stop_message_live_location( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param business_connection_id: Identifier of a business connection + :type business_connection_id: :obj:`str` + :return: On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. :rtype: :class:`telebot.types.Message` or bool """ return types.Message.de_json( apihelper.stop_message_live_location( self.token, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - reply_markup=reply_markup, timeout=timeout) + reply_markup=reply_markup, timeout=timeout, business_connection_id=business_connection_id) ) @@ -4645,7 +4653,8 @@ def edit_message_text( entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[types.InlineKeyboardMarkup]=None, - link_preview_options : Optional[types.LinkPreviewOptions]=None) -> Union[types.Message, bool]: + link_preview_options : Optional[types.LinkPreviewOptions]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -4678,6 +4687,9 @@ def edit_message_text( :param link_preview_options: A JSON-serialized object for options used to automatically generate previews for links. :type link_preview_options: :obj:`LinkPreviewOptions` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ @@ -4704,7 +4716,8 @@ def edit_message_text( result = apihelper.edit_message_text( self.token, text, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - parse_mode=parse_mode, entities=entities, reply_markup=reply_markup, link_preview_options=link_preview_options) + parse_mode=parse_mode, entities=entities, reply_markup=reply_markup, link_preview_options=link_preview_options, + business_connection_id=business_connection_id) if type(result) == bool: # if edit inline message return is bool not Message. return result @@ -4715,7 +4728,8 @@ def edit_message_media( self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -4738,12 +4752,15 @@ def edit_message_media( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`telebot.types.InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ result = apihelper.edit_message_media( self.token, media, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - reply_markup=reply_markup) + reply_markup=reply_markup, business_connection_id=business_connection_id) if type(result) == bool: # if edit inline message return is bool not Message. return result @@ -4754,7 +4771,8 @@ def edit_message_reply_markup( self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. @@ -4772,12 +4790,15 @@ def edit_message_reply_markup( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ result = apihelper.edit_message_reply_markup( self.token, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - reply_markup=reply_markup) + reply_markup=reply_markup, business_connection_id=business_connection_id) if type(result) == bool: return result @@ -5524,7 +5545,8 @@ def edit_message_caption( parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - show_caption_above_media: Optional[bool]=None) -> Union[types.Message, bool]: + show_caption_above_media: Optional[bool]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. @@ -5554,6 +5576,9 @@ def edit_message_caption( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + :param business_connection_id: Identifier of the business connection to use for the message + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` | :obj:`bool` """ @@ -5562,7 +5587,7 @@ def edit_message_caption( result = apihelper.edit_message_caption( self.token, caption, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, parse_mode=parse_mode, caption_entities=caption_entities, reply_markup=reply_markup, - show_caption_above_media=show_caption_above_media) + show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id) if type(result) == bool: return result diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 59039ce56..3d5d89bf4 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -594,7 +594,7 @@ 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, live_period=None): + timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, live_period=None, business_connection_id=None): method_url = r'editMessageLiveLocation' payload = {'latitude': latitude, 'longitude': longitude} if chat_id: @@ -615,12 +615,14 @@ def edit_message_live_location( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) def stop_message_live_location( token, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None): + inline_message_id=None, reply_markup=None, timeout=None, business_connection_id=None): method_url = r'stopMessageLiveLocation' payload = {} if chat_id: @@ -633,6 +635,8 @@ def stop_message_live_location( payload['reply_markup'] = _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) @@ -1379,7 +1383,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, - entities = None, reply_markup=None, link_preview_options=None): + entities = None, reply_markup=None, link_preview_options=None, business_connection_id=None): method_url = r'editMessageText' payload = {'text': text} if chat_id: @@ -1396,11 +1400,13 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message payload['reply_markup'] = _convert_markup(reply_markup) if link_preview_options is not None: payload['link_preview_options'] = link_preview_options.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None): + parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None, business_connection_id=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -1417,10 +1423,12 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m payload['reply_markup'] = _convert_markup(reply_markup) if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') -def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): +def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): method_url = r'editMessageMedia' media_json, file = convert_input_media(media) payload = {'media': media_json} @@ -1432,10 +1440,12 @@ def edit_message_media(token, media, chat_id=None, message_id=None, inline_messa payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, files=file, method='post' if file else 'get') -def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): +def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): method_url = r'editMessageReplyMarkup' payload = {} if chat_id: @@ -1446,6 +1456,8 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7fe6e48af..a6517a7c7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4724,6 +4724,7 @@ async def edit_message_live_location( heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, live_period: Optional[int]=None, + business_connection_id: Optional[str]=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 @@ -4766,6 +4767,9 @@ async def edit_message_live_location( :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` + :param business_connection_id: Identifier of a business connection, in which the message will be edited + :type business_connection_id: :obj:`str` + :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 """ @@ -4773,7 +4777,7 @@ 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, live_period=live_period) + horizontal_accuracy, heading, proximity_alert_radius, live_period=live_period, business_connection_id=business_connection_id) ) async def stop_message_live_location( @@ -4781,7 +4785,8 @@ async def stop_message_live_location( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - timeout: Optional[int]=None) -> types.Message: + timeout: Optional[int]=None, + business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to stop updating a live location message before live_period expires. On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. @@ -4804,12 +4809,15 @@ async def stop_message_live_location( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` + :param business_connection_id: Identifier of a business connection, in which the message will be edited + :type business_connection_id: :obj:`str` + :return: On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. :rtype: :class:`telebot.types.Message` or bool """ return types.Message.de_json( await asyncio_helper.stop_message_live_location( - self.token, chat_id, message_id, inline_message_id, reply_markup, timeout)) + self.token, chat_id, message_id, inline_message_id, reply_markup, timeout, business_connection_id)) async def send_venue( self, chat_id: Union[int, str], @@ -6000,7 +6008,8 @@ async def edit_message_text( entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - link_preview_options: Optional[types.LinkPreviewOptions]=None) -> Union[types.Message, bool]: + link_preview_options: Optional[types.LinkPreviewOptions]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -6033,6 +6042,9 @@ async def edit_message_text( :param link_preview_options: A JSON-serialized object for options used to automatically generate Telegram link previews for messages. :type link_preview_options: :obj:`LinkPreviewOptions` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ @@ -6058,7 +6070,7 @@ async def edit_message_text( link_preview_options = types.LinkPreviewOptions(is_disabled=self.disable_web_page_preview) result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - entities, reply_markup, link_preview_options) + entities, reply_markup, link_preview_options, business_connection_id) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -6067,7 +6079,8 @@ async def edit_message_media( self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -6090,10 +6103,13 @@ async def edit_message_media( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`telebot.types.InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ - result = await asyncio_helper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup) + result = await asyncio_helper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup, business_connection_id) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -6102,7 +6118,8 @@ async def edit_message_reply_markup( self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> Union[types.Message, bool]: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. @@ -6120,10 +6137,13 @@ async def edit_message_reply_markup( :param reply_markup: A JSON-serialized object for an inline keyboard. :type reply_markup: :obj:`InlineKeyboardMarkup` or :obj:`ReplyKeyboardMarkup` or :obj:`ReplyKeyboardRemove` or :obj:`ForceReply` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ - result = await asyncio_helper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup) + result = await asyncio_helper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup, business_connection_id) if type(result) == bool: return result return types.Message.de_json(result) @@ -6845,7 +6865,8 @@ async def edit_message_caption( parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - show_caption_above_media: Optional[bool]=None) -> Union[types.Message, bool]: + show_caption_above_media: Optional[bool]=None, + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. @@ -6875,13 +6896,16 @@ async def edit_message_caption( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + :param business_connection_id: Identifier of the business connection to send the message through + :type business_connection_id: :obj:`str` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` | :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode result = await asyncio_helper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, caption_entities, reply_markup, show_caption_above_media=show_caption_above_media) + parse_mode, caption_entities, reply_markup, show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id) if type(result) == bool: return result return types.Message.de_json(result) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 077ed3e61..0d09a052a 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -580,7 +580,7 @@ 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, live_period=None): + timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, live_period=None, business_connection_id=None): method_url = r'editMessageLiveLocation' payload = {'latitude': latitude, 'longitude': longitude} if chat_id: @@ -601,12 +601,14 @@ async def edit_message_live_location( payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) async def stop_message_live_location( token, chat_id=None, message_id=None, - inline_message_id=None, reply_markup=None, timeout=None): + inline_message_id=None, reply_markup=None, timeout=None, business_connection_id=None): method_url = r'stopMessageLiveLocation' payload = {} if chat_id: @@ -619,6 +621,8 @@ async def stop_message_live_location( payload['reply_markup'] = await _convert_markup(reply_markup) if timeout: payload['timeout'] = timeout + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) @@ -1357,7 +1361,7 @@ async def unpin_all_chat_messages(token, chat_id): # Updating messages async def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, - entities = None, reply_markup=None, link_preview_options=None): + entities = None, reply_markup=None, link_preview_options=None, business_connection_id=None): method_url = r'editMessageText' payload = {'text': text} if chat_id: @@ -1374,11 +1378,13 @@ async def edit_message_text(token, text, chat_id=None, message_id=None, inline_m payload['reply_markup'] = await _convert_markup(reply_markup) if link_preview_options is not None: payload['link_preview_options'] = link_preview_options.to_json() + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, method='post') async def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None): + parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None, business_connection_id=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -1395,10 +1401,12 @@ async def edit_message_caption(token, caption, chat_id=None, message_id=None, in payload['reply_markup'] = await _convert_markup(reply_markup) if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, method='post') -async def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): +async def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): method_url = r'editMessageMedia' media_json, file = await convert_input_media(media) payload = {'media': media_json} @@ -1410,10 +1418,12 @@ async def edit_message_media(token, media, chat_id=None, message_id=None, inline payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, files=file, method='post' if file else 'get') -async def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None): +async def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): method_url = r'editMessageReplyMarkup' payload = {} if chat_id: @@ -1424,6 +1434,8 @@ async def edit_message_reply_markup(token, chat_id=None, message_id=None, inline payload['inline_message_id'] = inline_message_id if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, method='post') From c7130d5572128fa35a7f94c9bf8406466ae2f348 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 18 Jun 2024 17:12:38 +0500 Subject: [PATCH 1597/1808] Added the parameter business_connection_id to the method stopPoll, allowing the bot to stop polls it sent on behalf of a business account. --- telebot/__init__.py | 8 ++++++-- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 8 ++++++-- telebot/asyncio_helper.py | 4 +++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 369ce77a3..07cda06ae 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5418,7 +5418,8 @@ def send_poll( def stop_poll( self, chat_id: Union[int, str], message_id: int, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Poll: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + business_connection_id: Optional[str]=None) -> types.Poll: """ Use this method to stop a poll which was sent by the bot. On success, the stopped Poll is returned. @@ -5433,11 +5434,14 @@ def stop_poll( :param reply_markup: A JSON-serialized object for a new message markup. :type reply_markup: :obj:`InlineKeyboardMarkup` + :param business_connection_id: Identifier of the business connection to use for the poll + :type business_connection_id: :obj:`str` + :return: On success, the stopped Poll is returned. :rtype: :obj:`types.Poll` """ return types.Poll.de_json( - apihelper.stop_poll(self.token, chat_id, message_id, reply_markup=reply_markup) + apihelper.stop_poll(self.token, chat_id, message_id, reply_markup=reply_markup, business_connection_id=business_connection_id) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3d5d89bf4..d6a372cba 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1997,11 +1997,13 @@ def get_forum_topic_icon_stickers(token): method_url = r'getForumTopicIconStickers' return _make_request(token, method_url) -def stop_poll(token, chat_id, message_id, reply_markup=None): +def stop_poll(token, chat_id, message_id, reply_markup=None, business_connection_id=None): method_url = r'stopPoll' payload = {'chat_id': str(chat_id), 'message_id': message_id} if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload) def edit_general_forum_topic(token, chat_id, name): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a6517a7c7..af677e5ef 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6746,7 +6746,8 @@ async def send_poll( async def stop_poll( self, chat_id: Union[int, str], message_id: int, - reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Poll: + reply_markup: Optional[types.InlineKeyboardMarkup]=None, + business_connection_id: Optional[str]=None) -> types.Poll: """ Use this method to stop a poll which was sent by the bot. On success, the stopped Poll is returned. @@ -6761,10 +6762,13 @@ async def stop_poll( :param reply_markup: A JSON-serialized object for a new message markup. :type reply_markup: :obj:`InlineKeyboardMarkup` + :param business_connection_id: Identifier of the business connection to send the message through + :type business_connection_id: :obj:`str` + :return: On success, the stopped Poll is returned. :rtype: :obj:`types.Poll` """ - return types.Poll.de_json(await asyncio_helper.stop_poll(self.token, chat_id, message_id, reply_markup)) + return types.Poll.de_json(await asyncio_helper.stop_poll(self.token, chat_id, message_id, reply_markup, business_connection_id)) async def answer_shipping_query( self, shipping_query_id: str, ok: bool, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0d09a052a..ef755630f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2084,11 +2084,13 @@ def wrapper(key, val): return wrapper -async def stop_poll(token, chat_id, message_id, reply_markup=None): +async def stop_poll(token, chat_id, message_id, reply_markup=None, business_connection_id=None): method_url = r'stopPoll' payload = {'chat_id': str(chat_id), 'message_id': message_id} if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload) # exceptions From e256a716965ca13a77d6805a842e7d07417bc48d Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 20 Jun 2024 20:05:31 +0500 Subject: [PATCH 1598/1808] Added de_json classes for types --- telebot/types.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index fe92a6ff0..5d69af0ff 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10123,6 +10123,12 @@ class RevenueWithdrawalStatePending(RevenueWithdrawalState): def __init__(self, type, **kwargs): self.type: str = type + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState): """ @@ -10148,6 +10154,13 @@ def __init__(self, type, date, url, **kwargs): self.date: int = date self.url: str = url + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + class RevenueWithdrawalStateFailed(RevenueWithdrawalState): """ @@ -10165,6 +10178,12 @@ class RevenueWithdrawalStateFailed(RevenueWithdrawalState): def __init__(self, type, **kwargs): self.type: str = type + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + class TransactionPartner(JsonDeserializable): """ @@ -10214,6 +10233,15 @@ def __init__(self, type, withdrawal_state=None, **kwargs): self.type: str = type self.withdrawal_state: Optional[RevenueWithdrawalState] = withdrawal_state + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'withdrawal_state' in obj: + obj['withdrawal_state'] = RevenueWithdrawalState.de_json(obj['withdrawal_state']) + return cls(**obj) + + class TransactionPartnerUser(TransactionPartner): """ @@ -10234,6 +10262,14 @@ class TransactionPartnerUser(TransactionPartner): def __init__(self, type, user, **kwargs): self.type: str = type self.user: User = user + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + class TransactionPartnerOther(TransactionPartner): """ @@ -10251,6 +10287,13 @@ class TransactionPartnerOther(TransactionPartner): def __init__(self, type, **kwargs): self.type: str = type + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + class StarTransaction(JsonDeserializable): """ From b1166a35af8b2ded26812234e06566da631baebf Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 22 Jun 2024 13:15:33 +0300 Subject: [PATCH 1599/1808] Bump version to 4.20.0 --- 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 34f930a66..f2b1deb6b 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.19.2' +release = '4.20.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index b43d71dd1..75b695f44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.19.2" +version = "4.20.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 405f70f1a..96c6fbd20 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.19.2' +__version__ = '4.20.0' From 167e602907f72e7b50f24e10647334194b8d6627 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 22 Jun 2024 13:35:03 +0300 Subject: [PATCH 1600/1808] Add "timeout" parameter to edit_xxx functions --- telebot/__init__.py | 33 +++++++++++++++++++------ telebot/apihelper.py | 27 ++++++++++++++++----- telebot/async_telebot.py | 51 ++++++++++++++++++++++++++++++--------- telebot/asyncio_helper.py | 27 ++++++++++++++++----- 4 files changed, 106 insertions(+), 32 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 07cda06ae..4100e26a0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4654,7 +4654,8 @@ def edit_message_text( disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility reply_markup: Optional[types.InlineKeyboardMarkup]=None, link_preview_options : Optional[types.LinkPreviewOptions]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -4690,6 +4691,9 @@ def edit_message_text( :param business_connection_id: Unique identifier of the business connection :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ @@ -4717,7 +4721,7 @@ def edit_message_text( result = apihelper.edit_message_text( self.token, text, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, parse_mode=parse_mode, entities=entities, reply_markup=reply_markup, link_preview_options=link_preview_options, - business_connection_id=business_connection_id) + business_connection_id=business_connection_id, timeout=timeout) if type(result) == bool: # if edit inline message return is bool not Message. return result @@ -4729,7 +4733,8 @@ def edit_message_media( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -4755,12 +4760,15 @@ def edit_message_media( :param business_connection_id: Unique identifier of the business connection :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ result = apihelper.edit_message_media( self.token, media, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - reply_markup=reply_markup, business_connection_id=business_connection_id) + reply_markup=reply_markup, business_connection_id=business_connection_id, timeout=timeout) if type(result) == bool: # if edit inline message return is bool not Message. return result @@ -4772,7 +4780,8 @@ def edit_message_reply_markup( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. @@ -4793,12 +4802,15 @@ def edit_message_reply_markup( :param business_connection_id: Unique identifier of the business connection :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ result = apihelper.edit_message_reply_markup( self.token, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - reply_markup=reply_markup, business_connection_id=business_connection_id) + reply_markup=reply_markup, business_connection_id=business_connection_id, timeout=timeout) if type(result) == bool: return result @@ -5550,7 +5562,8 @@ def edit_message_caption( caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, show_caption_above_media: Optional[bool]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. @@ -5583,6 +5596,9 @@ def edit_message_caption( :param business_connection_id: Identifier of the business connection to use for the message :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` | :obj:`bool` """ @@ -5591,7 +5607,8 @@ def edit_message_caption( result = apihelper.edit_message_caption( self.token, caption, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, parse_mode=parse_mode, caption_entities=caption_entities, reply_markup=reply_markup, - show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id) + show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id, + timeout=timeout) if type(result) == bool: return result diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d6a372cba..54ce9f691 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1382,8 +1382,9 @@ 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, - entities = None, reply_markup=None, link_preview_options=None, business_connection_id=None): +def edit_message_text( + token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, entities = None, + reply_markup=None, link_preview_options=None, business_connection_id=None, timeout=None): method_url = r'editMessageText' payload = {'text': text} if chat_id: @@ -1402,11 +1403,14 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message payload['link_preview_options'] = link_preview_options.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return _make_request(token, method_url, params=payload, method='post') -def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None, business_connection_id=None): +def edit_message_caption( + token, caption, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, caption_entities=None, + reply_markup=None, show_caption_above_media=None, business_connection_id=None, timeout=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -1425,10 +1429,14 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m payload['show_caption_above_media'] = show_caption_above_media if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return _make_request(token, method_url, params=payload, method='post') -def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): +def edit_message_media( + token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, + business_connection_id=None, timeout=None): method_url = r'editMessageMedia' media_json, file = convert_input_media(media) payload = {'media': media_json} @@ -1442,10 +1450,14 @@ def edit_message_media(token, media, chat_id=None, message_id=None, inline_messa payload['reply_markup'] = _convert_markup(reply_markup) if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return _make_request(token, method_url, params=payload, files=file, method='post' if file else 'get') -def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): +def edit_message_reply_markup( + token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None, + timeout=None): method_url = r'editMessageReplyMarkup' payload = {} if chat_id: @@ -1458,6 +1470,8 @@ def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_messa payload['reply_markup'] = _convert_markup(reply_markup) if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return _make_request(token, method_url, params=payload, method='post') @@ -1590,6 +1604,7 @@ def send_invoice( :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. + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :return: """ method_url = r'sendInvoice' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index af677e5ef..2a338722a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2362,11 +2362,17 @@ def register_business_message_handler(self, :param callback: function to be called :type callback: :obj:`function` + :param commands: list of commands + :type commands: :obj:`list` of :obj:`str` + + :param regexp: Regular expression + :type regexp: :obj:`str` + :param func: Function executed as a filter :type func: :obj:`function` - :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) - :type pass_bot: :obj:`bool` + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. + :type content_types: :obj:`list` of :obj:`str` :param kwargs: Optional keyword arguments(custom filters) @@ -6009,7 +6015,8 @@ async def edit_message_text( disable_web_page_preview: Optional[bool]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, link_preview_options: Optional[types.LinkPreviewOptions]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit text and game messages. @@ -6045,6 +6052,9 @@ async def edit_message_text( :param business_connection_id: Unique identifier of the business connection :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ @@ -6069,8 +6079,9 @@ async def edit_message_text( # create a LinkPreviewOptions object link_preview_options = types.LinkPreviewOptions(is_disabled=self.disable_web_page_preview) - result = await asyncio_helper.edit_message_text(self.token, text, chat_id, message_id, inline_message_id, parse_mode, - entities, reply_markup, link_preview_options, business_connection_id) + result = await asyncio_helper.edit_message_text( + self.token, text, chat_id, message_id, inline_message_id, parse_mode, entities, reply_markup, + link_preview_options, business_connection_id, timeout) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -6080,7 +6091,8 @@ async def edit_message_media( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -6106,10 +6118,14 @@ async def edit_message_media( :param business_connection_id: Unique identifier of the business connection :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ - result = await asyncio_helper.edit_message_media(self.token, media, chat_id, message_id, inline_message_id, reply_markup, business_connection_id) + result = await asyncio_helper.edit_message_media( + self.token, media, chat_id, message_id, inline_message_id, reply_markup, business_connection_id, timeout) if type(result) == bool: # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -6119,7 +6135,8 @@ async def edit_message_reply_markup( message_id: Optional[int]=None, inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit only the reply markup of messages. @@ -6140,10 +6157,14 @@ async def edit_message_reply_markup( :param business_connection_id: Unique identifier of the business connection :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` or :obj:`bool` """ - result = await asyncio_helper.edit_message_reply_markup(self.token, chat_id, message_id, inline_message_id, reply_markup, business_connection_id) + result = await asyncio_helper.edit_message_reply_markup( + self.token, chat_id, message_id, inline_message_id, reply_markup, business_connection_id, timeout) if type(result) == bool: return result return types.Message.de_json(result) @@ -6870,7 +6891,8 @@ async def edit_message_caption( caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, show_caption_above_media: Optional[bool]=None, - business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: + business_connection_id: Optional[str]=None, + timeout: Optional[int]=None) -> Union[types.Message, bool]: """ Use this method to edit captions of messages. @@ -6903,13 +6925,18 @@ async def edit_message_caption( :param business_connection_id: Identifier of the business connection to send the message through :type business_connection_id: :obj:`str` + :param timeout: Timeout in seconds for the request. + :type timeout: :obj:`int` + :return: On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. :rtype: :obj:`types.Message` | :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - result = await asyncio_helper.edit_message_caption(self.token, caption, chat_id, message_id, inline_message_id, - parse_mode, caption_entities, reply_markup, show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id) + result = await asyncio_helper.edit_message_caption( + self.token, caption, chat_id, message_id, inline_message_id, parse_mode, caption_entities, reply_markup, + show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id, + timeout=timeout) if type(result) == bool: return result return types.Message.de_json(result) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index ef755630f..0a484f3b4 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1360,8 +1360,9 @@ async def unpin_all_chat_messages(token, chat_id): # Updating messages -async def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, - entities = None, reply_markup=None, link_preview_options=None, business_connection_id=None): +async def edit_message_text( + token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, entities = None, + reply_markup=None, link_preview_options=None, business_connection_id=None, timeout=None): method_url = r'editMessageText' payload = {'text': text} if chat_id: @@ -1380,11 +1381,14 @@ async def edit_message_text(token, text, chat_id=None, message_id=None, inline_m payload['link_preview_options'] = link_preview_options.to_json() if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return await _process_request(token, method_url, params=payload, method='post') -async def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None, - parse_mode=None, caption_entities=None,reply_markup=None, show_caption_above_media=None, business_connection_id=None): +async def edit_message_caption( + token, caption, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None, caption_entities=None, + reply_markup=None, show_caption_above_media=None, business_connection_id=None, timeout=None): method_url = r'editMessageCaption' payload = {'caption': caption} if chat_id: @@ -1403,10 +1407,14 @@ async def edit_message_caption(token, caption, chat_id=None, message_id=None, in payload['show_caption_above_media'] = show_caption_above_media if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return await _process_request(token, method_url, params=payload, method='post') -async def edit_message_media(token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): +async def edit_message_media( + token, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, + business_connection_id=None, timeout=None): method_url = r'editMessageMedia' media_json, file = await convert_input_media(media) payload = {'media': media_json} @@ -1420,10 +1428,14 @@ async def edit_message_media(token, media, chat_id=None, message_id=None, inline payload['reply_markup'] = await _convert_markup(reply_markup) if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return await _process_request(token, method_url, params=payload, files=file, method='post' if file else 'get') -async def edit_message_reply_markup(token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None): +async def edit_message_reply_markup( + token, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None, business_connection_id=None, + timeout=None): method_url = r'editMessageReplyMarkup' payload = {} if chat_id: @@ -1436,6 +1448,8 @@ async def edit_message_reply_markup(token, chat_id=None, message_id=None, inline payload['reply_markup'] = await _convert_markup(reply_markup) if business_connection_id: payload['business_connection_id'] = business_connection_id + if timeout: + payload['timeout'] = timeout return await _process_request(token, method_url, params=payload, method='post') @@ -1569,6 +1583,7 @@ async def send_invoice( :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. + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :return: """ method_url = r'sendInvoice' From ab2dca8d03abb2f6f5901a99c00dccc38935191e Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 22 Jun 2024 18:01:36 +0500 Subject: [PATCH 1601/1808] Add pass_bot to register_business_message (#2316) * Fix pass_bot in business message handler register --- telebot/__init__.py | 7 ++++++- telebot/async_telebot.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4100e26a0..d2bcdc307 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -8336,6 +8336,7 @@ def register_business_message_handler(self, regexp: Optional[str]=None, func: Optional[Callable]=None, content_types: Optional[List[str]]=None, + pass_bot: Optional[bool]=False, **kwargs): """ Registers business connection handler. @@ -8355,11 +8356,15 @@ def register_business_message_handler(self, :param content_types: Supported message content types. Must be a list. Defaults to ['text']. :type content_types: :obj:`list` of :obj:`str` + :param pass_bot: True, if bot instance should be passed to handler + :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) :return: None """ - handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot, **kwargs) self.add_business_message_handler(handler_dict) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2a338722a..224856416 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2355,6 +2355,7 @@ def register_business_message_handler(self, regexp: Optional[str]=None, func: Optional[Callable]=None, content_types: Optional[List[str]]=None, + pass_bot: Optional[bool]=False, **kwargs): """ Registers business connection handler. @@ -2374,11 +2375,15 @@ def register_business_message_handler(self, :param content_types: Supported message content types. Must be a list. Defaults to ['text']. :type content_types: :obj:`list` of :obj:`str` + :param pass_bot: True, if bot instance should be passed to handler + :type pass_bot: :obj:`bool` + :param kwargs: Optional keyword arguments(custom filters) :return: None """ - handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, **kwargs) + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, + pass_bot=pass_bot,**kwargs) self.add_business_message_handler(handler_dict) From 1815fa50e22c6087bfad67eccd01a64ce7ff2ea4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 22 Jun 2024 18:46:47 +0300 Subject: [PATCH 1602/1808] Pypy 3.8 removed from tests --- .github/workflows/setup_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 5518ff2f3..670251511 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10'] + python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.9', 'pypy-3.10'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 From fce1c3d1710dad4e4d2a7f683ac1188116b16372 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 23 Jun 2024 15:34:05 +0500 Subject: [PATCH 1603/1808] Added statesv2 --- telebot/__init__.py | 113 +++++++++++++++++--- telebot/custom_filters.py | 16 ++- telebot/storage/base_storage.py | 40 ++++++- telebot/storage/memory_storage.py | 168 ++++++++++++++++++++---------- 4 files changed, 263 insertions(+), 74 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d2bcdc307..8fe414252 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7,7 +7,7 @@ import threading import time import traceback -from typing import Any, Callable, List, Optional, Union +from typing import Any, Callable, List, Optional, Union, Dict # these imports are used to avoid circular import error import telebot.util @@ -168,7 +168,8 @@ def __init__( disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, allow_sending_without_reply: Optional[bool]=None, - colorful_logs: Optional[bool]=False + colorful_logs: Optional[bool]=False, + token_check: Optional[bool]=True ): # update-related @@ -186,6 +187,11 @@ def __init__( self.webhook_listener = None self._user = None + # token check + if token_check: + self._user = self.get_me() + self.bot_id = self._user.id + # logs-related if colorful_logs: try: @@ -280,6 +286,8 @@ def __init__( self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(self, num_threads=num_threads) + + @property def user(self) -> types.User: @@ -6572,7 +6580,9 @@ def setup_middleware(self, middleware: BaseMiddleware): self.middlewares.append(middleware) - def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None) -> None: + def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None) -> None: """ Sets a new state of a user. @@ -6591,14 +6601,29 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Option :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: None """ if chat_id is None: chat_id = user_id - self.current_states.set_state(chat_id, user_id, state) + if bot_id is None: + bot_id = self.bot_id + self.current_states.set_state( + chat_id=chat_id, user_id=user_id, state=state, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def reset_data(self, user_id: int, chat_id: Optional[int]=None): + def reset_data(self, user_id: int, chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: """ Reset data for a user in chat. @@ -6608,14 +6633,27 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None): :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: None """ if chat_id is None: chat_id = user_id - self.current_states.reset_data(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + self.current_states.reset_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def delete_state(self, user_id: int, chat_id: Optional[int]=None) -> None: + def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: """ Delete the current state of a user. @@ -6629,10 +6667,14 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None) -> None: """ if chat_id is None: chat_id = user_id - self.current_states.delete_state(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + self.current_states.delete_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def retrieve_data(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Any]: + def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Optional[Dict[str, Any]]: """ Returns context manager with data for a user in chat. @@ -6642,15 +6684,30 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None) -> Optional[A :param chat_id: Chat's unique identifier, defaults to user_id :type chat_id: int, optional + :param bot_id: Bot's identifier + :type bot_id: int, optional + + :param business_connection_id: Business identifier + :type business_connection_id: str, optional + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: int, optional + :return: Context manager with data for a user in chat :rtype: Optional[Any] """ if chat_id is None: chat_id = user_id - return self.current_states.get_interactive_data(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + return self.current_states.get_interactive_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id) - def get_state(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Union[int, str, State]]: + def get_state(self, user_id: int, chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Union[int, str]: """ Gets current state of a user. Not recommended to use this method. But it is ok for debugging. @@ -6661,15 +6718,31 @@ def get_state(self, user_id: int, chat_id: Optional[int]=None) -> Optional[Union :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: state of a user :rtype: :obj:`int` or :obj:`str` or :class:`telebot.types.State` """ if chat_id is None: chat_id = user_id - return self.current_states.get_state(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + return self.current_states.get_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): + def add_data(self, user_id: int, chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None, + **kwargs) -> None: """ Add data to states. @@ -6679,13 +6752,25 @@ def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :param kwargs: Data to add :return: None """ if chat_id is None: chat_id = user_id + if bot_id is None: + bot_id = self.bot_id for key, value in kwargs.items(): - self.current_states.set_data(chat_id, user_id, key, value) + self.current_states.set_data(chat_id=chat_id, user_id=user_id, key=key, value=value, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) def register_next_step_handler_by_chat_id( diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index d4d4ffeca..41a7c780c 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -7,6 +7,7 @@ + class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -417,8 +418,6 @@ def check(self, message, text): user_id = message.from_user.id message = message.message - - if isinstance(text, list): new_text = [] @@ -430,7 +429,11 @@ def check(self, message, text): text = text.name if message.chat.type in ['group', 'supergroup']: - group_state = self.bot.current_states.get_state(chat_id, user_id) + group_state = self.bot.current_states.get_state(chat_id=chat_id, user_id=user_id, business_connection_id=message.business_connection_id, bot_id=self.bot._user.id, + message_thread_id=message.message_thread_id) + if group_state is None and not message.is_topic_message: # needed for general topic and group messages + group_state = self.bot.current_states.get_state(chat_id=chat_id, user_id=user_id, business_connection_id=message.business_connection_id, bot_id=self.bot._user.id) + if group_state == text: return True elif type(text) is list and group_state in text: @@ -438,7 +441,12 @@ def check(self, message, text): else: - user_state = self.bot.current_states.get_state(chat_id, user_id) + user_state = self.bot.current_states.get_state( + chat_id=chat_id, + user_id=user_id, + business_connection_id=message.business_connection_id, + bot_id=self.bot._user.id + ) if user_state == text: return True elif type(text) is list and user_state in text: diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py index 92b31ba85..36545a77c 100644 --- a/telebot/storage/base_storage.py +++ b/telebot/storage/base_storage.py @@ -47,22 +47,56 @@ def get_interactive_data(self, chat_id, user_id): def save(self, chat_id, user_id, data): raise NotImplementedError + + def convert_params_to_key( + self, + chat_id: int, + user_id: int, + prefix: str, + separator: str, + business_connection_id: str=None, + message_thread_id: int=None, + bot_id: int=None + ) -> str: + """ + Convert parameters to a key. + """ + params = [prefix] + if bot_id: + params.append(str(bot_id)) + if business_connection_id: + params.append(business_connection_id) + if message_thread_id: + params.append(str(message_thread_id)) + params.append(str(chat_id)) + params.append(str(user_id)) + return separator.join(params) + + + + class StateContext: """ Class for data. """ - def __init__(self , obj, chat_id, user_id) -> None: + def __init__(self , obj, chat_id, user_id, business_connection_id=None, message_thread_id=None, bot_id=None, ): self.obj = obj - self.data = copy.deepcopy(obj.get_data(chat_id, user_id)) + res = obj.get_data(chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, + message_thread_id=message_thread_id, bot_id=bot_id) + self.data = copy.deepcopy(res) self.chat_id = chat_id self.user_id = user_id + self.bot_id = bot_id + self.business_connection_id = business_connection_id + self.message_thread_id = message_thread_id + def __enter__(self): return self.data def __exit__(self, exc_type, exc_val, exc_tb): - return self.obj.save(self.chat_id, self.user_id, self.data) \ No newline at end of file + return self.obj.save(self.chat_id, self.user_id, self.data, self.business_connection_id, self.message_thread_id, self.bot_id) \ No newline at end of file diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 7d71c7ccd..fbf9eebda 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -1,69 +1,131 @@ from telebot.storage.base_storage import StateStorageBase, StateContext - +from typing import Optional, Union class StateMemoryStorage(StateStorageBase): - def __init__(self) -> None: - super().__init__() - self.data = {} - # - # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} - - - def set_state(self, chat_id, user_id, state): - if hasattr(state, 'name'): + def __init__(self, + separator: Optional[str]=":", + prefix: Optional[str]="telebot" + ) -> None: + self.separator = separator + self.prefix = prefix + if not self.prefix: + raise ValueError("Prefix cannot be empty") + + self.data = {} # key: telebot:bot_id:business_connection_id:message_thread_id:chat_id:user_id + + def set_state( + self, chat_id: int, user_id: int, state: str, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + + ) -> bool: + if hasattr(state, "name"): state = state.name - if chat_id in self.data: - if user_id in self.data[chat_id]: - self.data[chat_id][user_id]['state'] = state - return True - else: - self.data[chat_id][user_id] = {'state': state, 'data': {}} - return True - self.data[chat_id] = {user_id: {'state': state, 'data': {}}} + + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + if self.data.get(_key) is None: + self.data[_key] = {"state": state, "data": {}} + else: + self.data[_key]["state"] = state + return True - def delete_state(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - del self.data[chat_id][user_id] - if chat_id == user_id: - del self.data[chat_id] - - return True + def get_state( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> Union[str, None]: - return False + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + if self.data.get(_key) is None: + return None + + return self.data[_key]["state"] - def get_state(self, chat_id, user_id): + def delete_state( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> bool: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + if self.data.get(_key) is None: + return False + + del self.data[_key] + return True + + + def set_data( + self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], + business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None) -> bool: + + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['state'] + if self.data.get(_key) is None: + return False + self.data[_key]["data"][key] = value + return True - return None - def get_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['data'] + + def get_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> dict: - return None + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) - def reset_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'] = {} - return True - return False + return self.data.get(_key, {}).get("data", None) + + def reset_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> bool: + + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) - def set_data(self, chat_id, user_id, key, value): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'][key] = value - return True - raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + if self.data.get(_key) is None: + return False + self.data[_key]["data"] = {} + return True + + def get_interactive_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> Optional[dict]: + return StateContext( + self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, + message_thread_id=message_thread_id, bot_id=bot_id + ) + + def save( + self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> bool: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, + message_thread_id, bot_id + ) - def get_interactive_data(self, chat_id, user_id): - return StateContext(self, chat_id, user_id) + if self.data.get(_key) is None: + return False + self.data[_key]["data"] = data + return True + + def __str__(self) -> str: + return f"" + + - def save(self, chat_id, user_id, data): - self.data[chat_id][user_id]['data'] = data \ No newline at end of file From 004f7dec172c8fc0854d54f39880b6f6c1b18aca Mon Sep 17 00:00:00 2001 From: Zaid _ Date: Thu, 27 Jun 2024 14:27:10 +0300 Subject: [PATCH 1604/1808] fix comparing types --- telebot/__init__.py | 10 +++++----- telebot/async_telebot.py | 10 +++++----- telebot/custom_filters.py | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d2bcdc307..408a11902 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4723,7 +4723,7 @@ def edit_message_text( parse_mode=parse_mode, entities=entities, reply_markup=reply_markup, link_preview_options=link_preview_options, business_connection_id=business_connection_id, timeout=timeout) - if type(result) == bool: # if edit inline message return is bool not Message. + if isinstance(result, bool): # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -4770,7 +4770,7 @@ def edit_message_media( self.token, media, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, reply_markup=reply_markup, business_connection_id=business_connection_id, timeout=timeout) - if type(result) == bool: # if edit inline message return is bool not Message. + if isinstance(result, bool): # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -4812,7 +4812,7 @@ def edit_message_reply_markup( self.token, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, reply_markup=reply_markup, business_connection_id=business_connection_id, timeout=timeout) - if type(result) == bool: + if isinstance(result, bool): return result return types.Message.de_json(result) @@ -4946,7 +4946,7 @@ def set_game_score( self.token, user_id, score, force=force, disable_edit_message=disable_edit_message, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id) - if type(result) == bool: + if isinstance(result, bool): return result return types.Message.de_json(result) @@ -5610,7 +5610,7 @@ def edit_message_caption( show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id, timeout=timeout) - if type(result) == bool: + if isinstance(result, bool): return result return types.Message.de_json(result) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 224856416..4ad59c4da 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6087,7 +6087,7 @@ async def edit_message_text( result = await asyncio_helper.edit_message_text( self.token, text, chat_id, message_id, inline_message_id, parse_mode, entities, reply_markup, link_preview_options, business_connection_id, timeout) - if type(result) == bool: # if edit inline message return is bool not Message. + if isinstance(result, bool): # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -6131,7 +6131,7 @@ async def edit_message_media( """ result = await asyncio_helper.edit_message_media( self.token, media, chat_id, message_id, inline_message_id, reply_markup, business_connection_id, timeout) - if type(result) == bool: # if edit inline message return is bool not Message. + if isinstance(result, bool): # if edit inline message return is bool not Message. return result return types.Message.de_json(result) @@ -6170,7 +6170,7 @@ async def edit_message_reply_markup( """ result = await asyncio_helper.edit_message_reply_markup( self.token, chat_id, message_id, inline_message_id, reply_markup, business_connection_id, timeout) - if type(result) == bool: + if isinstance(result, bool): return result return types.Message.de_json(result) @@ -6298,7 +6298,7 @@ async def set_game_score( """ result = await asyncio_helper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, message_id, inline_message_id) - if type(result) == bool: + if isinstance(result, bool): return result return types.Message.de_json(result) @@ -6942,7 +6942,7 @@ async def edit_message_caption( self.token, caption, chat_id, message_id, inline_message_id, parse_mode, caption_entities, reply_markup, show_caption_above_media=show_caption_above_media, business_connection_id=business_connection_id, timeout=timeout) - if type(result) == bool: + if isinstance(result, bool): return result return types.Message.de_json(result) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index d4d4ffeca..0c4ca614e 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -215,7 +215,7 @@ def check(self, message, text): """ if isinstance(text, TextFilter): return text.check(message) - elif type(text) is list: + elif isinstance(text, list): return message.text in text else: return text == message.text @@ -353,7 +353,7 @@ def check(self, message, text): """ :meta private: """ - if type(text) is list: + if isinstance(text, list): return message.from_user.language_code in text else: return message.from_user.language_code == text @@ -433,7 +433,7 @@ def check(self, message, text): group_state = self.bot.current_states.get_state(chat_id, user_id) if group_state == text: return True - elif type(text) is list and group_state in text: + elif isinstance(text, list) and group_state in text: return True @@ -441,7 +441,7 @@ def check(self, message, text): user_state = self.bot.current_states.get_state(chat_id, user_id) if user_state == text: return True - elif type(text) is list and user_state in text: + elif isinstance(text, list) and user_state in text: return True From 9feb9be827837e3641e27738c7526c799d263c91 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 22:28:30 +0500 Subject: [PATCH 1605/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10694e4b0..22be0a24c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.5! +##

Supported Bot API version: 7.6!

Official documentation

Official ru documentation

From 915b90fd24c82f7b1b65acfcb2b0300c59a6db3b Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 22:36:41 +0500 Subject: [PATCH 1606/1808] Added the classes PaidMedia, PaidMediaInfo, PaidMediaPreview, PaidMediaPhoto and PaidMediaVideo, containing information about paid media. --- telebot/types.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 5d69af0ff..b17ae48cb 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10361,3 +10361,122 @@ def de_json(cls, json_string): def __init__(self, transactions, **kwargs): self.transactions: List[StarTransaction] = transactions + + +class PaidMedia(JsonDeserializable): + """ + This object describes paid media. Currently, it can be one of + + PaidMediaPreview + PaidMediaPhoto + PaidMediaVideo + + Telegram documentation: https://core.telegram.org/bots/api#paidmedia + + :return: Instance of the class + :rtype: :class:`PaidMediaPreview` or :class:`PaidMediaPhoto` or :class:`PaidMediaVideo` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if obj["type"] == "preview": + return PaidMediaPreview.de_json(obj) + elif obj["type"] == "photo": + return PaidMediaPhoto.de_json(obj) + elif obj["type"] == "video": + return PaidMediaVideo.de_json(obj) + +class PaidMediaPreview(PaidMedia): + """ + The paid media isn't available before the payment. + + Telegram documentation: https://core.telegram.org/bots/api#paidmediapreview + + :param type: Type of the paid media, always “preview” + :type type: :obj:`str` + + :param width: Optional. Media width as defined by the sender + :type width: :obj:`int` + + :param height: Optional. Media height as defined by the sender + :type height: :obj:`int` + + :param duration: Optional. Duration of the media in seconds as defined by the sender + :type duration: :obj:`int` + + :return: Instance of the class + :rtype: :class:`PaidMediaPreview` + """ + + def __init__(self, type, width=None, height=None, duration=None, **kwargs): + self.type: str = type + self.width: Optional[int] = width + self.height: Optional[int] = height + self.duration: Optional[int] = duration + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + +class PaidMediaPhoto(PaidMedia): + """ + The paid media is a photo. + + Telegram documentation: https://core.telegram.org/bots/api#paidmediaphoto + + :param type: Type of the paid media, always “photo” + :type type: :obj:`str` + + :param photo: The photo + :type photo: :obj:`list` of :class:`PhotoSize` + + :return: Instance of the class + :rtype: :class:`PaidMediaPhoto` + + """ + + def __init__(self, type, photo, **kwargs): + self.type: str = type + self.photo: List[PhotoSize] = photo + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + + obj['photo'] = [PhotoSize.de_json(photo) for photo in obj['photo']] + return cls(**obj) + + +class PaidMediaVideo(PaidMedia): + """ + The paid media is a video. + + Telegram documentation: https://core.telegram.org/bots/api#paidmediavideo + + :param type: Type of the paid media, always “video” + :type type: :obj:`str` + + :param video: The video + :type video: :class:`Video` + + :return: Instance of the class + :rtype: :class:`PaidMediaVideo` + """ + + def __init__(self, type, video, **kwargs): + self.type: str = type + self.video: Video = video + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['video'] = Video.de_json(obj['video']) + return cls(**obj) + \ No newline at end of file From 1d8239bf003afa179368ed04921229024983d8d3 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 22:48:38 +0500 Subject: [PATCH 1607/1808] Added the method sendPaidMedia and the classes InputPaidMedia, InputPaidMediaPhoto and InputPaidMediaVideo, to support sending paid media. --- telebot/__init__.py | 55 +++++++++++++++++++ telebot/apihelper.py | 28 ++++++++++ telebot/async_telebot.py | 55 +++++++++++++++++++ telebot/asyncio_helper.py | 27 ++++++++++ telebot/types.py | 111 +++++++++++++++++++++++++++++++++++++- 5 files changed, 274 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 408a11902..4d3a7398b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3112,6 +3112,61 @@ def send_video_note( protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id) ) + + def send_paid_media( + self, chat_id: Union[int, str], star_count: int, media: List[types.InputPaidMedia], + caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, + show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Message: + """ + Use this method to send paid media to channel chats. On success, the sent Message is returned. + + Telegram documentation: https://core.telegram.org/bots/api#sendpaidmedia + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param star_count: The number of Telegram Stars that must be paid to buy access to the media + :type star_count: :obj:`int` + + :param media: A JSON-serialized array describing the media to be sent; up to 10 items + :type media: :obj:`list` of :class:`telebot.types.InputPaidMedia` + + :param caption: Media caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the media caption + :type parse_mode: :obj:`str` + + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param show_caption_above_media: Pass True, if the caption must be shown above the message media + :type show_caption_above_media: :obj:`bool` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_parameters: Description of the message to reply to + :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` or :class:`telebot.types.ForceReply` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` + """ + return types.Message.de_json( + apihelper.send_paid_media( + self.token, chat_id, star_count, media, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, + disable_notification=disable_notification, protect_content=protect_content, + reply_parameters=reply_parameters, reply_markup=reply_markup) + ) def send_media_group( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 54ce9f691..ad6d593d9 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -525,6 +525,34 @@ def send_photo( if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media return _make_request(token, method_url, params=payload, files=files, method='post') + +def send_paid_media( + token, chat_id, star_count, media, + caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, + disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None): + method_url = r'sendPaidMedia' + media_json, files = convert_input_media_array(media) + payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} + if caption: + payload['caption'] = caption + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if protect_content is not None: + payload['protect_content'] = protect_content + if reply_parameters is not None: + payload['reply_parameters'] = reply_parameters.to_json() + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request( + token, method_url, params=payload, + method='post' if files else 'get', + files=files if files else None) def send_media_group( diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4ad59c4da..5f424fc72 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4526,6 +4526,61 @@ async def send_video_note( self.token, chat_id, data, duration, length, reply_markup, disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + async def send_paid_media( + self, chat_id: Union[int, str], star_count: int, media: List[types.InputPaidMedia], + caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, + show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Message: + """ + Use this method to send paid media to channel chats. On success, the sent Message is returned. + + Telegram documentation: https://core.telegram.org/bots/api#sendpaidmedia + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param star_count: The number of Telegram Stars that must be paid to buy access to the media + :type star_count: :obj:`int` + + :param media: A JSON-serialized array describing the media to be sent; up to 10 items + :type media: :obj:`list` of :class:`telebot.types.InputPaidMedia` + + :param caption: Media caption, 0-1024 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the media caption + :type parse_mode: :obj:`str` + + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param show_caption_above_media: Pass True, if the caption must be shown above the message media + :type show_caption_above_media: :obj:`bool` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param reply_parameters: Description of the message to reply to + :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` or :class:`telebot.types.ForceReply` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` + """ + return types.Message.de_json( + await asyncio_helper.send_paid_media( + self.token, chat_id, star_count, media, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, + disable_notification=disable_notification, protect_content=protect_content, + reply_parameters=reply_parameters, reply_markup=reply_markup) + ) + async def send_media_group( self, chat_id: Union[int, str], media: List[Union[ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0a484f3b4..b6248e1a1 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -514,6 +514,33 @@ async def send_photo( payload['show_caption_above_media'] = show_caption_above_media return await _process_request(token, method_url, params=payload, files=files, method='post') +async def send_paid_media( + token, chat_id, star_count, media, + caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, + disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None): + method_url = r'sendPaidMedia' + media_json, files = convert_input_media_array(media) + payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} + if caption: + payload['caption'] = caption + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if show_caption_above_media is not None: + payload['show_caption_above_media'] = show_caption_above_media + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if protect_content is not None: + payload['protect_content'] = protect_content + if reply_parameters is not None: + payload['reply_parameters'] = reply_parameters.to_json() + if reply_markup: + payload['reply_markup'] = _convert_markup(reply_markup) + return await _process_request( + token, method_url, params=payload, + method='post' if files else 'get', + files=files if files else None) async def send_media_group( token, chat_id, media, diff --git a/telebot/types.py b/telebot/types.py index b17ae48cb..e1a7fc933 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10361,7 +10361,7 @@ def de_json(cls, json_string): def __init__(self, transactions, **kwargs): self.transactions: List[StarTransaction] = transactions - + class PaidMedia(JsonDeserializable): """ @@ -10479,4 +10479,111 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['video'] = Video.de_json(obj['video']) return cls(**obj) - \ No newline at end of file + + +class InputPaidMedia(JsonSerializable): + """ + This object describes the paid media to be sent. Currently, it can be one of + InputPaidMediaPhoto + InputPaidMediaVideo + + Telegram documentation: https://core.telegram.org/bots/api#inputpaidmedia + + :return: Instance of the class + :rtype: :class:`InputPaidMediaPhoto` or :class:`InputPaidMediaVideo` + """ + + def __init__(self, type, media, **kwargs): + self.type = type + self.media = media + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = {} + data['type'] = str(self.type) + data['media'] = str(self.media) + return data + +class InputPaidMediaPhoto(InputPaidMedia): + """ + The paid media to send is a photo. + + Telegram documentation: https://core.telegram.org/bots/api#inputpaidmediaphoto + + :param type: Type of the media, must be photo + :type type: :obj:`str` + + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for + Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data + under name. More information on Sending Files » + :type media: :obj:`str` + + :return: Instance of the class + :rtype: :class:`InputPaidMediaPhoto` + """ + + def __init__(self, media, **kwargs): + super().__init__(type='photo', media=media) + +class InputPaidMediaVideo(InputPaidMedia): + """ + The paid media to send is a video. + + Telegram documentation: https://core.telegram.org/bots/api#inputpaidmediavideo + + :param type: Type of the media, must be video + :type type: :obj:`str` + + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for + Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data + under name. More information on Sending Files » + :type media: :obj:`str` + + :param thumbnail: Optional. 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 . + More information on Sending Files » + :type thumbnail: :class:`InputFile` + + :param width: Optional. Video width + :type width: :obj:`int` + + :param height: Optional. Video height + :type height: :obj:`int` + + :param duration: Optional. Video duration in seconds + :type duration: :obj:`int` + + :param supports_streaming: Optional. Pass True if the uploaded video is suitable for streaming + :type supports_streaming: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`InputPaidMediaVideo` + + """ + + def __init__(self, media, thumbnail=None, width=None, height=None, duration=None, supports_streaming=None, **kwargs): + super().__init__(type='video', media=media) + self.thumbnail = thumbnail + self.width = width + self.height = height + self.duration = duration + self.supports_streaming = supports_streaming + + def to_dict(self): + data = super().to_dict() + if self.thumbnail: + data['thumbnail'] = self.thumbnail.to_dict() + if self.width: + data['width'] = self.width + if self.height: + data['height'] = self.height + if self.duration: + data['duration'] = self.duration + if self.supports_streaming: + data['supports_streaming'] = self.supports_streaming + return data + From ca9ab83091d4f19b0511bb9b6a3df694ab91e0a0 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 22:52:30 +0500 Subject: [PATCH 1608/1808] Documented that the methods copyMessage and copyMessages cannot be used to copy paid media. --- telebot/__init__.py | 63 +++++++++++++++++++++------------------- telebot/async_telebot.py | 61 ++++++++++++++++++++------------------ 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4d3a7398b..eef715af2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1822,6 +1822,9 @@ def copy_message( show_caption_above_media: Optional[bool]=None) -> types.MessageID: """ Use this method to copy messages of any kind. + Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. + A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous to the method + forwardMessage, but the copied message doesn't have a link to the original message. Returns the MessageId of the sent message on success. Telegram documentation: https://core.telegram.org/bots/api#copymessage @@ -1999,47 +2002,47 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: - """ - Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. - Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. - A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. - The method is analogous to the method forwardMessages, but the copied messages don't have a link to the original message. - Album grouping is kept for copied messages. On success, an array of MessageId of the sent messages is returned. + """ + Use this method to copy messages of any kind. + If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, + and invoice messages can't be copied. A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous + to the method forwardMessages, but the copied messages don't have a link to the original message. Album grouping is kept for copied messages. On success, an array + of MessageId of the sent messages is returned. - Telegram documentation: https://core.telegram.org/bots/api#copymessages + Telegram documentation: https://core.telegram.org/bots/api#copymessages - :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :type chat_id: :obj:`int` or :obj:`str` + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` - :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) - :type from_chat_id: :obj:`int` or :obj:`str` + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` - :param message_ids: Message identifiers in the chat specified in from_chat_id - :type message_ids: :obj:`list` of :obj:`int` + :param message_ids: Message identifiers in the chat specified in from_chat_id + :type message_ids: :obj:`list` of :obj:`int` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound - :type disable_notification: :obj:`bool` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` - :param message_thread_id: Identifier of a message thread, in which the messages will be sent - :type message_thread_id: :obj:`int` + :param message_thread_id: Identifier of a message thread, in which the messages will be sent + :type message_thread_id: :obj:`int` - :param protect_content: Protects the contents of the forwarded message from forwarding and saving - :type protect_content: :obj:`bool` + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :type protect_content: :obj:`bool` - :param remove_caption: Pass True to copy the messages without their captions - :type remove_caption: :obj:`bool` + :param remove_caption: Pass True to copy the messages without their captions + :type remove_caption: :obj:`bool` - :return: On success, an array of MessageId of the sent messages is returned. - :rtype: :obj:`list` of :class:`telebot.types.MessageID` - """ - disable_notification = self.disable_notification if disable_notification is None else disable_notification - protect_content = self.protect_content if protect_content is None else protect_content + :return: On success, an array of MessageId of the sent messages is returned. + :rtype: :obj:`list` of :class:`telebot.types.MessageID` + """ + disable_notification = self.disable_notification if disable_notification is None else disable_notification + protect_content = self.protect_content if protect_content is None else protect_content - result = apihelper.copy_messages( - self.token, chat_id, from_chat_id, message_ids, disable_notification=disable_notification, - message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption) - return [types.MessageID.de_json(message_id) for message_id in result] + result = apihelper.copy_messages( + self.token, chat_id, from_chat_id, message_ids, disable_notification=disable_notification, + message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption) + return [types.MessageID.de_json(message_id) for message_id in result] def send_dice( diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 5f424fc72..75ebe6791 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3248,6 +3248,10 @@ async def copy_message( show_caption_above_media: Optional[bool]=None) -> types.MessageID: """ Use this method to copy messages of any kind. + If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, + and invoice messages can't be copied. A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous + to the method forwardMessages, but the copied messages don't have a link to the original message. Album grouping is kept for copied messages. On success, an array + of MessageId of the sent messages is returned. Telegram documentation: https://core.telegram.org/bots/api#copymessage @@ -3415,44 +3419,43 @@ async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[s async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: - """ - Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. - Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied - only if the value of the field correct_option_id is known to the bot. The method is analogous to the method forwardMessages, but - the copied messages don't have a link to the original message. Album grouping is kept for copied messages. - On success, an array of MessageId of the sent messages is returned. + """ + Use this method to copy messages of any kind. + Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. + A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous to the method + forwardMessage, but the copied message doesn't have a link to the original message. Returns the MessageId of the sent message on success. - Telegram documentation: https://core.telegram.org/bots/api#copymessages + Telegram documentation: https://core.telegram.org/bots/api#copymessages - :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) - :type chat_id: :obj:`int` or :obj:`str` + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` - :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) - :type from_chat_id: :obj:`int` or :obj:`str` + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) + :type from_chat_id: :obj:`int` or :obj:`str` - :param message_ids: Message identifiers in the chat specified in from_chat_id - :type message_ids: :obj:`list` of :obj:`int` + :param message_ids: Message identifiers in the chat specified in from_chat_id + :type message_ids: :obj:`list` of :obj:`int` - :param disable_notification: Sends the message silently. Users will receive a notification with no sound - :type disable_notification: :obj:`bool` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound + :type disable_notification: :obj:`bool` - :param message_thread_id: Identifier of a message thread, in which the messages will be sent - :type message_thread_id: :obj:`int` + :param message_thread_id: Identifier of a message thread, in which the messages will be sent + :type message_thread_id: :obj:`int` - :param protect_content: Protects the contents of the forwarded message from forwarding and saving - :type protect_content: :obj:`bool` + :param protect_content: Protects the contents of the forwarded message from forwarding and saving + :type protect_content: :obj:`bool` - :param remove_caption: Pass True to copy the messages without their captions - :type remove_caption: :obj:`bool` + :param remove_caption: Pass True to copy the messages without their captions + :type remove_caption: :obj:`bool` - :return: On success, an array of MessageId of the sent messages is returned. - :rtype: :obj:`list` of :class:`telebot.types.MessageID` - """ - disable_notification = self.disable_notification if disable_notification is None else disable_notification - protect_content = self.protect_content if protect_content is None else protect_content - result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, - protect_content, remove_caption) - return [types.MessageID.de_json(message_id) for message_id in result] + :return: On success, an array of MessageId of the sent messages is returned. + :rtype: :obj:`list` of :class:`telebot.types.MessageID` + """ + disable_notification = self.disable_notification if disable_notification is None else disable_notification + protect_content = self.protect_content if protect_content is None else protect_content + result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, + protect_content, remove_caption) + return [types.MessageID.de_json(message_id) for message_id in result] async def send_dice( self, chat_id: Union[int, str], From 01e92b1a49c3441b4c32f5c6b71600809fd1759a Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 22:57:24 +0500 Subject: [PATCH 1609/1808] Added the field can_send_paid_media to the class ChatFullInfo. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e1a7fc933..51b3f0e56 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -707,6 +707,10 @@ class ChatFullInfo(JsonDeserializable): :param location: Optional. For supergroups, the location to which the supergroup is connected. Returned only in getChat. :type location: :class:`telebot.types.ChatLocation` + :param can_send_paid_media: Optional. True, if paid media messages can be sent or forwarded to the channel chat. + The field is available only for channel chats. + :type can_send_paid_media: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ChatFullInfo` """ @@ -748,7 +752,8 @@ def __init__(self, id, type, title=None, username=None, first_name=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, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, - business_opening_hours=None, personal_chat=None, birthdate=None, **kwargs): + business_opening_hours=None, personal_chat=None, birthdate=None, + can_send_paid_media=None, **kwargs): self.id: int = id self.type: str = type self.title: str = title @@ -792,6 +797,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.business_opening_hours: BusinessOpeningHours = business_opening_hours self.personal_chat: Chat = personal_chat self.birthdate: Birthdate = birthdate + self.can_send_paid_media: bool = can_send_paid_media class Chat(ChatFullInfo): From 1d18a2672353e496ed2b23ec7b696b6014382186 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 23:04:44 +0500 Subject: [PATCH 1610/1808] Added the field paid_media to the classes Message and ExternalReplyInfo. --- telebot/types.py | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 51b3f0e56..728d31167 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -970,6 +970,9 @@ class Message(JsonDeserializable): :param document: Optional. Message is a general file, information about the file :type document: :class:`telebot.types.Document` + :param paid_media: Optional. Message contains paid media; information about the paid media + :type paid_media: :class:`telebot.types.PaidMediaInfo` + :param photo: Optional. Message is a photo, available sizes of the photo :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` @@ -1386,7 +1389,8 @@ def de_json(cls, json_string): opts['effect_id'] = obj['effect_id'] if 'show_caption_above_media' in obj: opts['show_caption_above_media'] = obj['show_caption_above_media'] - + if 'paid_media' in obj: + opts['paid_media'] = PaidMediaInfo.de_json(obj['paid_media']) return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1497,6 +1501,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.is_from_offline: Optional[bool] = None self.effect_id: Optional[str] = None self.show_caption_above_media: Optional[bool] = None + self.paid_media : Optional[PaidMediaInfo] = None for key in options: setattr(self, key, options[key]) @@ -8517,6 +8522,9 @@ class ExternalReplyInfo(JsonDeserializable): :param document: Optional. Message is a general file, information about the file :type document: :class:`Document` + :param paid_media: Optional. Message is a paid media content + :type paid_media: :class:`PaidMedia` + :param photo: Optional. Message is a photo, available sizes of the photo :type photo: :obj:`list` of :class:`PhotoSize` @@ -8625,7 +8633,7 @@ def __init__( dice: Optional[Dice]=None, game: Optional[Game]=None, giveaway: Optional[Giveaway]=None, giveaway_winners: Optional[GiveawayWinners]=None, invoice: Optional[Invoice]=None, location: Optional[Location]=None, poll: Optional[Poll]=None, - venue: Optional[Venue]=None, **kwargs) -> None: + venue: Optional[Venue]=None, paid_media: Optional[PaidMediaInfo]=None, **kwargs) -> None: self.origin: MessageOrigin = origin self.chat: Optional[Chat] = chat self.message_id: Optional[int] = message_id @@ -8649,6 +8657,7 @@ def __init__( self.location: Optional[Location] = location self.poll: Optional[Poll] = poll self.venue: Optional[Venue] = venue + self.paid_media: Optional[PaidMediaInfo] = paid_media # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -10487,6 +10496,34 @@ def de_json(cls, json_string): return cls(**obj) +class PaidMediaInfo(JsonDeserializable): + """ + Describes the paid media added to a message. + + Telegram documentation: https://core.telegram.org/bots/api#paidmediainfo + + :param star_count: The number of Telegram Stars that must be paid to buy access to the media + :type star_count: :obj:`int` + + :param paid_media: Information about the paid media + :type paid_media: :obj:`list` of :class:`PaidMedia` + + :return: Instance of the class + :rtype: :class:`PaidMediaInfo` + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['paid_media'] = [PaidMedia.de_json(media) for media in obj['paid_media']] + return cls(**obj) + + def __init__(self, star_count, paid_media, **kwargs): + self.star_count: int = star_count + self.paid_media: List[PaidMedia] = paid_media + + class InputPaidMedia(JsonSerializable): """ This object describes the paid media to be sent. Currently, it can be one of From 4ec6b402f3e637fb5d91cd8cf75b8e2266b6421e Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 23:07:37 +0500 Subject: [PATCH 1611/1808] Added the class TransactionPartnerTelegramAds, containing information about Telegram Star transactions involving the Telegram Ads Platform. --- telebot/types.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 728d31167..245f37cd8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10224,6 +10224,8 @@ def de_json(cls, json_string): return TransactionPartnerFragment.de_json(obj) elif obj["type"] == "user": return TransactionPartnerUser.de_json(obj) + elif obj["type"] == "telegram_ads": + return TransactionPartnerTelegramAds.de_json(obj) elif obj["type"] == "other": return TransactionPartnerOther.de_json(obj) @@ -10285,6 +10287,27 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) return cls(**obj) +class TransactionPartnerTelegramAds(TransactionPartner): + """ + Describes a transaction with Telegram Ads. + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartnertelegramads + + :param type: Type of the transaction partner, always “telegram_ads” + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerTelegramAds` + """ + + def __init__(self, type, **kwargs): + self.type: str = type + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + class TransactionPartnerOther(TransactionPartner): """ @@ -10630,3 +10653,4 @@ def to_dict(self): data['supports_streaming'] = self.supports_streaming return data + \ No newline at end of file From ecde9547aa2d2cd97057f4bc2f85705a1e76f6f6 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 23:12:51 +0500 Subject: [PATCH 1612/1808] Added the field invoice_payload to the class TransactionPartnerUser, containing the bot-specified invoice payload. --- telebot/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 245f37cd8..9e4cf5f1d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10272,13 +10272,17 @@ class TransactionPartnerUser(TransactionPartner): :param user: Information about the user :type user: :class:`User` + :param invoice_payload: Optional, Bot-specified invoice payload + :type invoice_payload: :obj:`str` + :return: Instance of the class :rtype: :class:`TransactionPartnerUser` """ - def __init__(self, type, user, **kwargs): + def __init__(self, type, user, invoice_payload=None, **kwargs): self.type: str = type self.user: User = user + self.invoice_payload: Optional[str] = invoice_payload @classmethod def de_json(cls, json_string): From 5117807ea11ae97658dbfba4f772f8685ff58ef8 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Jul 2024 23:13:51 +0500 Subject: [PATCH 1613/1808] Added support for launching Web Apps via t.me link in the class MenuButtonWebApp. --- telebot/types.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 9e4cf5f1d..2a0bc65d5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7543,7 +7543,9 @@ class MenuButtonWebApp(MenuButton): :type text: :obj:`str` :param web_app: Description of the Web App that will be launched when the user presses the button. The Web App will be - able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. + able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Alternatively, a t.me link + to a Web App of the bot can be specified in the object instead of the Web App's URL, in which case the Web App will be + opened as if the user pressed the link. :type web_app: :class:`telebot.types.WebAppInfo` :return: Instance of the class From f558724c5f117b58bb43656520a48a24c0c3ab37 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 3 Jul 2024 16:44:30 +0500 Subject: [PATCH 1614/1808] Error intervals to stop flooding logs & set non_stop to true by default --- telebot/async_telebot.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4ad59c4da..2f8d0db27 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -246,7 +246,7 @@ def _setup_change_detector(self, path_to_watch: str) -> None: self.event_observer.schedule(self.event_handler, path, recursive=True) self.event_observer.start() - async def polling(self, non_stop: bool=False, skip_pending=False, interval: int=0, timeout: int=20, + async def polling(self, non_stop: bool=True, skip_pending=False, interval: int=0, timeout: int=20, request_timeout: Optional[int]=None, allowed_updates: Optional[List[str]]=None, none_stop: Optional[bool]=None, restart_on_change: Optional[bool]=False, path_to_watch: Optional[str]=None): """ @@ -257,11 +257,6 @@ async def polling(self, non_stop: bool=False, skip_pending=False, interval: int= Always gets updates. - .. note:: - - Set non_stop=True if you want your bot to continue receiving updates - if there is an error. - .. note:: Install watchdog and psutil before using restart_on_change option. @@ -393,6 +388,15 @@ def __hide_token(self, message: str) -> str: return message.replace(code, "*" * len(code)) else: return message + + async def _handle_error_interval(self, error_interval: float): + logger.debug('Waiting for %s seconds before retrying', error_interval) + await asyncio.sleep(error_interval) + if error_interval * 2 < 60: # same logic as sync + error_interval *= 2 + else: + error_interval = 60 + return error_interval async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: int=20, request_timeout: int=None, allowed_updates: Optional[List[str]]=None): @@ -426,16 +430,18 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: self._polling = True + error_interval = 0.25 + try: while self._polling: try: - updates = await self.get_updates(offset=self.offset, allowed_updates=allowed_updates, timeout=timeout, request_timeout=request_timeout) if updates: self.offset = updates[-1].update_id + 1 # noinspection PyAsyncCall asyncio.create_task(self.process_new_updates(updates)) # Seperate task for processing updates if interval: await asyncio.sleep(interval) + error_interval = 0.25 # drop error_interval if no errors except KeyboardInterrupt: return @@ -446,9 +452,10 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', self.__hide_token(str(e))) logger.debug(self.__hide_token(traceback.format_exc())) + error_interval = await self._handle_error_interval(error_interval) if non_stop or handled: - await asyncio.sleep(2) + #await asyncio.sleep(2) # used error_interval instead continue else: return @@ -457,6 +464,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', self.__hide_token(str(e))) logger.debug(self.__hide_token(traceback.format_exc())) + error_interval = await self._handle_error_interval(error_interval) if non_stop or handled: continue @@ -467,6 +475,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) logger.debug(traceback.format_exc()) + error_interval = await self._handle_error_interval(error_interval) if non_stop or handled: continue From 040e23634bc346887e255bf929b4900576a855e9 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 3 Jul 2024 18:51:11 +0500 Subject: [PATCH 1615/1808] inputfile fix #2320 --- telebot/__init__.py | 4 ++++ telebot/apihelper.py | 2 +- telebot/async_telebot.py | 4 ++++ telebot/asyncio_helper.py | 2 ++ telebot/types.py | 14 ++++++++++++-- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 408a11902..e1ba557b7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2598,6 +2598,10 @@ def send_document( logger.warning('The parameter "thumb" is deprecated. Use "thumbnail" instead.') thumbnail = thumb + if isinstance(document, types.InputFile) and visible_file_name: + # inputfile name ignored, warn + logger.warning('Cannot use both InputFile and visible_file_name. InputFile name will be ignored.') + return types.Message.de_json( apihelper.send_data( self.token, chat_id, document, 'document', diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 54ce9f691..a1eb0b221 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -94,7 +94,7 @@ def _make_request(token, method_name, method='get', params=None, files=None): # process types.InputFile for key, value in files_copy.items(): if isinstance(value, types.InputFile): - files[key] = value.file + files[key] = (value.file_name, value.file) elif isinstance(value, tuple) and (len(value) == 2) and isinstance(value[1], types.InputFile): files[key] = (value[0], value[1].file) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2f8d0db27..ab13b2f60 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4023,6 +4023,10 @@ async def send_document( if reply_parameters and (reply_parameters.allow_sending_without_reply is None): reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply + if isinstance(document, types.InputFile) and visible_file_name: + # inputfile name ignored, warn + logger.warning('Cannot use both InputFile and visible_file_name. InputFile name will be ignored.') + return types.Message.de_json( await asyncio_helper.send_data( self.token, chat_id, document, 'document', diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0a484f3b4..db0641899 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -130,6 +130,8 @@ def _prepare_data(params=None, files=None): if isinstance(f, tuple): if len(f) == 2: file_name, file = f + if isinstance(file, types.InputFile): + file = file.file else: raise ValueError('Tuple must have exactly 2 elements: filename, fileobj') elif isinstance(f, types.InputFile): diff --git a/telebot/types.py b/telebot/types.py index 5d69af0ff..f8744525e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7735,8 +7735,11 @@ class InputFile: InputFile(pathlib.Path('/path/to/file/file.txt')) ) """ - def __init__(self, file) -> None: - self._file, self.file_name = self._resolve_file(file) + def __init__(self, file: Union[str, IOBase, Path], file_name: Optional[str] = None): + self._file, self._file_name = self._resolve_file(file) + if file_name: + self._file_name = file_name + @staticmethod def _resolve_file(file): @@ -7757,6 +7760,13 @@ def file(self): File object. """ return self._file + + @property + def file_name(self): + """ + File name. + """ + return self._file_name class ForumTopicCreated(JsonDeserializable): From 01f9a28cf156750ff4e636ef1bc8211f6b99f948 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 4 Jul 2024 11:05:03 +0500 Subject: [PATCH 1616/1808] Fixes from review --- telebot/apihelper.py | 2 +- telebot/asyncio_helper.py | 2 +- telebot/types.py | 20 ++++++++++++++------ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index ad6d593d9..8c170ec2a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2145,7 +2145,7 @@ def convert_input_media_array(array): media = [] files = {} for input_media in array: - if isinstance(input_media, types.InputMedia): + if isinstance(input_media, types.InputMedia) or isinstance(input_media, types.InputPaidMedia): media_dict = input_media.to_dict() if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index b6248e1a1..79f09b96b 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2108,7 +2108,7 @@ async def convert_input_media_array(array): media = [] files = {} for input_media in array: - if isinstance(input_media, types.InputMedia): + if isinstance(input_media, types.InputMedia) or isinstance(input_media, types.InputPaidMedia): media_dict = input_media.to_dict() if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') diff --git a/telebot/types.py b/telebot/types.py index 2a0bc65d5..abbc69bbd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6737,7 +6737,7 @@ def to_dict(self): ret['height'] = self.height if self.duration: ret['duration'] = self.duration - if self.supports_streaming: + if self.supports_streaming is not None: ret['supports_streaming'] = self.supports_streaming if self.has_spoiler is not None: ret['has_spoiler'] = self.has_spoiler @@ -10569,13 +10569,21 @@ def __init__(self, type, media, **kwargs): self.type = type self.media = media + if service_utils.is_string(self.media): + self._media_name = '' + self._media_dic = self.media + else: + self._media_name = service_utils.generate_random_token() + self._media_dic = 'attach://{0}'.format(self._media_name) + def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - data = {} - data['type'] = str(self.type) - data['media'] = str(self.media) + data = { + 'type': self.type, + 'media': self._media_dic + } return data class InputPaidMediaPhoto(InputPaidMedia): @@ -10648,14 +10656,14 @@ def __init__(self, media, thumbnail=None, width=None, height=None, duration=None def to_dict(self): data = super().to_dict() if self.thumbnail: - data['thumbnail'] = self.thumbnail.to_dict() + data['thumbnail'] = self.thumbnail if self.width: data['width'] = self.width if self.height: data['height'] = self.height if self.duration: data['duration'] = self.duration - if self.supports_streaming: + if self.supports_streaming is not None: data['supports_streaming'] = self.supports_streaming return data From 08c08942451efc4e86a8026572bd2078c986486b Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 4 Jul 2024 23:41:49 +0500 Subject: [PATCH 1617/1808] Fix --- telebot/async_telebot.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ab13b2f60..1fc13276b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -452,10 +452,11 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', self.__hide_token(str(e))) logger.debug(self.__hide_token(traceback.format_exc())) + + if non_stop: error_interval = await self._handle_error_interval(error_interval) if non_stop or handled: - #await asyncio.sleep(2) # used error_interval instead continue else: return @@ -464,6 +465,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', self.__hide_token(str(e))) logger.debug(self.__hide_token(traceback.format_exc())) + + if non_stop: error_interval = await self._handle_error_interval(error_interval) if non_stop or handled: @@ -475,6 +478,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if not handled: logger.error('Unhandled exception (full traceback for debug level): %s', str(e)) logger.debug(traceback.format_exc()) + + if non_stop: error_interval = await self._handle_error_interval(error_interval) if non_stop or handled: From b45edee76e81b425c5919ad3090ad172bc7ff683 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 4 Jul 2024 22:23:00 +0300 Subject: [PATCH 1618/1808] Bump version --- 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 f2b1deb6b..cfa926801 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.20.0' +release = '4.21.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 75b695f44..0095c3f21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.20.0" +version = "4.21.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 96c6fbd20..5016764c6 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.20.0' +__version__ = '4.21.0' From 2d56fa87ee7455c623111a45cb817b987d732e5f Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 7 Jul 2024 18:36:29 +0500 Subject: [PATCH 1619/1808] bot api 7.7 --- README.md | 2 +- telebot/types.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 22be0a24c..36f0e7a43 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.6! +##

Supported Bot API version: 7.6!

Official documentation

Official ru documentation

diff --git a/telebot/types.py b/telebot/types.py index d0d3819bb..05a45f0de 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1080,6 +1080,9 @@ class Message(JsonDeserializable): the payment. More about payments » :type successful_payment: :class:`telebot.types.SuccessfulPayment` + :param refunded_payment: Optional. Message is a service message about a refunded payment, information about the payment. More about payments » + :type refunded_payment: :class:`telebot.types.RefundedPayment` + :param users_shared: Optional. Service message: a user was shared with the bot :type users_shared: :class:`telebot.types.UsersShared` @@ -1391,7 +1394,8 @@ def de_json(cls, json_string): opts['show_caption_above_media'] = obj['show_caption_above_media'] if 'paid_media' in obj: opts['paid_media'] = PaidMediaInfo.de_json(obj['paid_media']) - + if 'refunded_payment' in obj: + opts['refunded_payment'] = RefundedPayment.de_json(obj['refunded_payment']) return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1502,6 +1506,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.effect_id: Optional[str] = None self.show_caption_above_media: Optional[bool] = None self.paid_media : Optional[PaidMediaInfo] = None + self.refunded_payment : Optional[RefundedPayment] = None for key in options: setattr(self, key, options[key]) @@ -10676,5 +10681,43 @@ def to_dict(self): if self.supports_streaming is not None: data['supports_streaming'] = self.supports_streaming return data + +class RefundedPayment(JsonDeserializable): + """ + This object contains basic information about a refunded payment. + + Telegram documentation: https://core.telegram.org/bots/api#refundedpayment + + :param currency: Three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars. Currently, always “XTR” + :type currency: :obj:`str` + + :param total_amount: Total refunded price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45, total_amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). + :type total_amount: :obj:`int` + + :param invoice_payload: Bot-specified invoice payload + :type invoice_payload: :obj:`str` + + :param telegram_payment_charge_id: Telegram payment identifier + :type telegram_payment_charge_id: :obj:`str` + + :param provider_payment_charge_id: Optional. Provider payment identifier + :type provider_payment_charge_id: :obj:`str` + + :return: Instance of the class + :rtype: :class:`RefundedPayment` + """ + + def __init__(self, currency, total_amount, invoice_payload, telegram_payment_charge_id, provider_payment_charge_id=None, **kwargs): + self.currency: str = currency + self.total_amount: int = total_amount + self.invoice_payload: str = invoice_payload + self.telegram_payment_charge_id: str = telegram_payment_charge_id + self.provider_payment_charge_id: Optional[str] = provider_payment_charge_id + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) \ No newline at end of file From 7e5a044b104527489c84e5579396d24e7c2a8115 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 8 Jul 2024 17:11:37 +0500 Subject: [PATCH 1620/1808] Sync states v2 early version --- telebot/handler_backends.py | 43 +-------- telebot/states/__init__.py | 43 +++++++++ telebot/states/aio/__init__.py | 0 telebot/states/sync/__init__.py | 7 ++ telebot/states/sync/context.py | 153 ++++++++++++++++++++++++++++++ telebot/states/sync/middleware.py | 17 ++++ 6 files changed, 223 insertions(+), 40 deletions(-) create mode 100644 telebot/states/__init__.py create mode 100644 telebot/states/aio/__init__.py create mode 100644 telebot/states/sync/__init__.py create mode 100644 telebot/states/sync/context.py create mode 100644 telebot/states/sync/middleware.py diff --git a/telebot/handler_backends.py b/telebot/handler_backends.py index b95861e0b..12f8c89bf 100644 --- a/telebot/handler_backends.py +++ b/telebot/handler_backends.py @@ -9,6 +9,8 @@ except: redis_installed = False +# backward compatibility +from telebot.states import State, StatesGroup class HandlerBackend(object): """ @@ -160,45 +162,6 @@ def get_handlers(self, handler_group_id): return handlers -class State: - """ - Class representing a state. - - .. code-block:: python3 - - class MyStates(StatesGroup): - my_state = State() # returns my_state:State string. - """ - def __init__(self) -> None: - self.name = None - def __str__(self) -> str: - return self.name - - -class StatesGroup: - """ - Class representing common states. - - .. code-block:: python3 - - class MyStates(StatesGroup): - my_state = State() # returns my_state:State string. - """ - def __init_subclass__(cls) -> None: - state_list = [] - for name, value in cls.__dict__.items(): - if not name.startswith('__') and not callable(value) and isinstance(value, State): - # change value of that variable - value.name = ':'.join((cls.__name__, name)) - value.group = cls - state_list.append(value) - cls._state_list = state_list - - @classmethod - def state_list(self): - return self._state_list - - class BaseMiddleware: """ Base class for middleware. @@ -292,4 +255,4 @@ def start2(message): """ def __init__(self) -> None: - pass + pass \ No newline at end of file diff --git a/telebot/states/__init__.py b/telebot/states/__init__.py new file mode 100644 index 000000000..0a45f17e1 --- /dev/null +++ b/telebot/states/__init__.py @@ -0,0 +1,43 @@ +""" +Contains classes for states and state groups. +""" + + +class State: + """ + Class representing a state. + + .. code-block:: python3 + + class MyStates(StatesGroup): + my_state = State() # returns my_state:State string. + """ + def __init__(self) -> None: + self.name: str = None + self.group: StatesGroup = None + def __str__(self) -> str: + return f"<{self.group.__name__}:{self.name}>" + + +class StatesGroup: + """ + Class representing common states. + + .. code-block:: python3 + + class MyStates(StatesGroup): + my_state = State() # returns my_state:State string. + """ + def __init_subclass__(cls) -> None: + state_list = [] + for name, value in cls.__dict__.items(): + if not name.startswith('__') and not callable(value) and isinstance(value, State): + # change value of that variable + value.name = ':'.join((cls.__name__, name)) + value.group = cls + state_list.append(value) + cls._state_list = state_list + + @classmethod + def state_list(self): + return self._state_list diff --git a/telebot/states/aio/__init__.py b/telebot/states/aio/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/telebot/states/sync/__init__.py b/telebot/states/sync/__init__.py new file mode 100644 index 000000000..60f9ea2b7 --- /dev/null +++ b/telebot/states/sync/__init__.py @@ -0,0 +1,7 @@ +from .context import StateContext +from .middleware import StateMiddleware + +__all__ = [ + 'StateContext', + 'StateMiddleware', +] \ No newline at end of file diff --git a/telebot/states/sync/context.py b/telebot/states/sync/context.py new file mode 100644 index 000000000..f0a395d01 --- /dev/null +++ b/telebot/states/sync/context.py @@ -0,0 +1,153 @@ +from telebot.states import State, StatesGroup +from telebot.types import CallbackQuery, Message +from telebot import TeleBot + +from typing import Union + + +class StateContext(): + """ + Class representing a state context. + + Passed through a middleware to provide easy way to set states. + + .. code-block:: python3 + + @bot.message_handler(commands=['start']) + def start_ex(message: types.Message, state_context: StateContext): + state_context.set(MyStates.name) + bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id) + # also, state_context.data(), .add_data(), .reset_data(), .delete() methods available. + """ + + def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None: + self.message: Union[Message, CallbackQuery] = message + self.bot: TeleBot = bot + self.bot_id = self.bot.bot_id + + def _resolve_context(self) -> Union[Message, CallbackQuery]: + chat_id = None + user_id = None + business_connection_id = self.message.business_connection_id + bot_id = self.bot_id + message_thread_id = None + + if isinstance(self.message, Message): + chat_id = self.message.chat.id + user_id = self.message.from_user.id + message_thread_id = self.message.message_thread_id if self.message.is_topic_message else None + elif isinstance(self.message, CallbackQuery): + chat_id = self.message.message.chat.id + user_id = self.message.from_user.id + message_thread_id = self.message.message.message_thread_id if self.message.message.is_topic_message else None + + return chat_id, user_id, business_connection_id, bot_id, message_thread_id + + def set(self, state: Union[State, str]) -> None: + """ + Set state for current user. + + :param state: State object or state name. + :type state: Union[State, str] + + .. code-block:: python3 + + @bot.message_handler(commands=['start']) + def start_ex(message: types.Message, state_context: StateContext): + state_context.set(MyStates.name) + bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id) + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + if isinstance(state, State): + state = state.name + return self.bot.set_state( + chat_id=chat_id, + user_id=user_id, + state=state, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + def get(self) -> str: + """ + Get current state for current user. + + :return: Current state name. + :rtype: str + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + return self.bot.get_state( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + def reset_data(self) -> None: + """ + Reset data for current user. + State will not be changed. + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + return self.bot.reset_data( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + def delete(self) -> None: + """ + Deletes state and data for current user. + """ + chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + return self.bot.delete_state( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + def data(self) -> dict: + """ + Get data for current user. + + .. code-block:: python3 + + with state_context.data() as data: + print(data) + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + return self.bot.retrieve_data( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + def add_data(self, **kwargs) -> None: + """ + Add data for current user. + + :param kwargs: Data to add. + :type kwargs: dict + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + return self.bot.add_data( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id, + **kwargs + ) \ No newline at end of file diff --git a/telebot/states/sync/middleware.py b/telebot/states/sync/middleware.py new file mode 100644 index 000000000..e1c74cc23 --- /dev/null +++ b/telebot/states/sync/middleware.py @@ -0,0 +1,17 @@ +from telebot.handler_backends import BaseMiddleware +from telebot import TeleBot +from telebot.states.sync.context import StateContext + + +class StateMiddleware(BaseMiddleware): + + def __init__(self, bot: TeleBot) -> None: + self.update_sensitive = False + self.update_types = ['message', 'edited_message', 'callback_query'] #TODO: support other types + self.bot: TeleBot = bot + + def pre_process(self, message, data): + data['state_context'] = StateContext(message, self.bot) + + def post_process(self, message, data, exception): + pass \ No newline at end of file From 4a7bc5d5006b664848feff84b5200c361b3e170b Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 8 Jul 2024 21:47:38 +0500 Subject: [PATCH 1621/1808] all update types are supported for states(theoretically) --- telebot/custom_filters.py | 48 +++++++++---------------------- telebot/states/__init__.py | 41 +++++++++++++++++++++++++- telebot/states/sync/context.py | 37 +++++++----------------- telebot/states/sync/middleware.py | 6 ++-- tests/test_handler_backends.py | 2 +- 5 files changed, 69 insertions(+), 65 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 41a7c780c..1c5bd0b40 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -4,7 +4,7 @@ from telebot import types - +from telebot.states import resolve_context @@ -407,17 +407,7 @@ def check(self, message, text): """ if text == '*': return True - # needs to work with callbackquery - if isinstance(message, types.Message): - chat_id = message.chat.id - user_id = message.from_user.id - - if isinstance(message, types.CallbackQuery): - - chat_id = message.message.chat.id - user_id = message.from_user.id - message = message.message - + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot._user.id) if isinstance(text, list): new_text = [] @@ -428,29 +418,17 @@ def check(self, message, text): elif isinstance(text, State): text = text.name - if message.chat.type in ['group', 'supergroup']: - group_state = self.bot.current_states.get_state(chat_id=chat_id, user_id=user_id, business_connection_id=message.business_connection_id, bot_id=self.bot._user.id, - message_thread_id=message.message_thread_id) - if group_state is None and not message.is_topic_message: # needed for general topic and group messages - group_state = self.bot.current_states.get_state(chat_id=chat_id, user_id=user_id, business_connection_id=message.business_connection_id, bot_id=self.bot._user.id) - - if group_state == text: - return True - elif type(text) is list and group_state in text: - return True - - - else: - user_state = self.bot.current_states.get_state( - chat_id=chat_id, - user_id=user_id, - business_connection_id=message.business_connection_id, - bot_id=self.bot._user.id - ) - if user_state == text: - return True - elif type(text) is list and user_state in text: - return True + user_state = self.bot.current_states.get_state( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + if user_state == text: + return True + elif type(text) is list and user_state in text: + return True class IsDigitFilter(SimpleCustomFilter): diff --git a/telebot/states/__init__.py b/telebot/states/__init__.py index 0a45f17e1..b8efe3bbe 100644 --- a/telebot/states/__init__.py +++ b/telebot/states/__init__.py @@ -1,7 +1,7 @@ """ Contains classes for states and state groups. """ - +from telebot import types class State: """ @@ -41,3 +41,42 @@ def __init_subclass__(cls) -> None: @classmethod def state_list(self): return self._state_list + +def resolve_context(message, bot_id: int) -> tuple: + # chat_id, user_id, business_connection_id, bot_id, message_thread_id + + # message, edited_message, channel_post, edited_channel_post, business_message, edited_business_message + if isinstance(message, types.Message): + return (message.chat.id, message.from_user.id, message.business_connection_id, bot_id, + message.message_thread_id if message.is_topic_message else None) + elif isinstance(message, types.CallbackQuery): # callback_query + return (message.message.chat.id, message.from_user.id, message.message.business_connection_id, bot_id, + message.message.message_thread_id if message.message.is_topic_message else None) + elif isinstance(message, types.BusinessConnection): # business_connection + return (message.user_chat_id, message.user.id, message.id, bot_id, None) + elif isinstance(message, types.BusinessMessagesDeleted): # deleted_business_messages + return (message.chat.id, message.chat.id, message.business_connection_id, bot_id, None) + elif isinstance(message, types.MessageReactionUpdated): # message_reaction + return (message.chat.id, message.user.id, None, bot_id, None) + elif isinstance(message, types.MessageReactionCountUpdated): # message_reaction_count + return (message.chat.id, None, None, bot_id, None) + elif isinstance(message, types.InlineQuery): # inline_query + return (None, message.from_user.id, None, bot_id, None) + elif isinstance(message, types.ChosenInlineResult): # chosen_inline_result + return (None, message.from_user.id, None, bot_id, None) + elif isinstance(message, types.ShippingQuery): # shipping_query + return (None, message.from_user.id, None, bot_id, None) + elif isinstance(message, types.PreCheckoutQuery): # pre_checkout_query + return (None, message.from_user.id, None, bot_id, None) + elif isinstance(message, types.PollAnswer): # poll_answer + return (None, message.user.id, None, bot_id, None) + elif isinstance(message, types.ChatMemberUpdated): # chat_member # my_chat_member + return (message.chat.id, message.from_user.id, None, bot_id, None) + elif isinstance(message, types.ChatJoinRequest): # chat_join_request + return (message.chat.id, message.from_user.id, None, bot_id, None) + elif isinstance(message, types.ChatBoostRemoved): # removed_chat_boost + return (message.chat.id, message.source.user.id if message.source else None, None, bot_id, None) + elif isinstance(message, types.ChatBoostUpdated): # chat_boost + return (message.chat.id, message.boost.source.user.id if message.boost.source else None, None, bot_id, None) + else: + pass # not yet supported :( \ No newline at end of file diff --git a/telebot/states/sync/context.py b/telebot/states/sync/context.py index f0a395d01..c8009007a 100644 --- a/telebot/states/sync/context.py +++ b/telebot/states/sync/context.py @@ -1,10 +1,12 @@ from telebot.states import State, StatesGroup from telebot.types import CallbackQuery, Message -from telebot import TeleBot +from telebot import TeleBot, types +from telebot.states import resolve_context from typing import Union + class StateContext(): """ Class representing a state context. @@ -25,24 +27,6 @@ def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None: self.bot: TeleBot = bot self.bot_id = self.bot.bot_id - def _resolve_context(self) -> Union[Message, CallbackQuery]: - chat_id = None - user_id = None - business_connection_id = self.message.business_connection_id - bot_id = self.bot_id - message_thread_id = None - - if isinstance(self.message, Message): - chat_id = self.message.chat.id - user_id = self.message.from_user.id - message_thread_id = self.message.message_thread_id if self.message.is_topic_message else None - elif isinstance(self.message, CallbackQuery): - chat_id = self.message.message.chat.id - user_id = self.message.from_user.id - message_thread_id = self.message.message.message_thread_id if self.message.message.is_topic_message else None - - return chat_id, user_id, business_connection_id, bot_id, message_thread_id - def set(self, state: Union[State, str]) -> None: """ Set state for current user. @@ -58,7 +42,7 @@ def start_ex(message: types.Message, state_context: StateContext): bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id) """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) if isinstance(state, State): state = state.name return self.bot.set_state( @@ -78,7 +62,7 @@ def get(self) -> str: :rtype: str """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) return self.bot.get_state( chat_id=chat_id, user_id=user_id, @@ -93,7 +77,7 @@ def reset_data(self) -> None: State will not be changed. """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) return self.bot.reset_data( chat_id=chat_id, user_id=user_id, @@ -106,7 +90,7 @@ def delete(self) -> None: """ Deletes state and data for current user. """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) return self.bot.delete_state( chat_id=chat_id, user_id=user_id, @@ -125,7 +109,7 @@ def data(self) -> dict: print(data) """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) return self.bot.retrieve_data( chat_id=chat_id, user_id=user_id, @@ -142,7 +126,7 @@ def add_data(self, **kwargs) -> None: :type kwargs: dict """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = self._resolve_context() + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) return self.bot.add_data( chat_id=chat_id, user_id=user_id, @@ -150,4 +134,5 @@ def add_data(self, **kwargs) -> None: bot_id=bot_id, message_thread_id=message_thread_id, **kwargs - ) \ No newline at end of file + ) + \ No newline at end of file diff --git a/telebot/states/sync/middleware.py b/telebot/states/sync/middleware.py index e1c74cc23..b85f795da 100644 --- a/telebot/states/sync/middleware.py +++ b/telebot/states/sync/middleware.py @@ -1,17 +1,19 @@ from telebot.handler_backends import BaseMiddleware from telebot import TeleBot from telebot.states.sync.context import StateContext +from telebot.util import update_types +from telebot import types class StateMiddleware(BaseMiddleware): def __init__(self, bot: TeleBot) -> None: self.update_sensitive = False - self.update_types = ['message', 'edited_message', 'callback_query'] #TODO: support other types + self.update_types = update_types self.bot: TeleBot = bot def pre_process(self, message, data): data['state_context'] = StateContext(message, self.bot) def post_process(self, message, data, exception): - pass \ No newline at end of file + pass diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index bb541bf8a..f57200c1c 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -19,7 +19,7 @@ @pytest.fixture() def telegram_bot(): - return telebot.TeleBot('', threaded=False) + return telebot.TeleBot('', threaded=False, token_check=False) @pytest.fixture From 805b1669cfe39e6d506dcbc044deef7a2c7399f8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 8 Jul 2024 22:59:56 +0300 Subject: [PATCH 1622/1808] Suppress warnings and fix forward_origin --- telebot/custom_filters.py | 4 +--- telebot/types.py | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 0c4ca614e..a1c0aba14 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -5,8 +5,6 @@ from telebot import types - - class SimpleCustomFilter(ABC): """ Simple Custom Filter base class. @@ -311,7 +309,7 @@ def check(self, message): """ :meta private: """ - return message.forward_date is not None + return message.forward_origin is not None class IsReplyFilter(SimpleCustomFilter): diff --git a/telebot/types.py b/telebot/types.py index 05a45f0de..6913452be 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10111,6 +10111,7 @@ def __init__(self, type, **kwargs): class RevenueWithdrawalState(JsonDeserializable): + # noinspection PyUnresolvedReferences """ This object describes the state of a revenue withdrawal operation. Currently, it can be one of RevenueWithdrawalStatePending @@ -10152,6 +10153,7 @@ class RevenueWithdrawalStatePending(RevenueWithdrawalState): :rtype: :class:`RevenueWithdrawalStatePending` """ + # noinspection PyPackageRequirements def __init__(self, type, **kwargs): self.type: str = type @@ -10181,6 +10183,7 @@ class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState): :rtype: :class:`RevenueWithdrawalStateSucceeded` """ + # noinspection PyPackageRequirements def __init__(self, type, date, url, **kwargs): self.type: str = type self.date: int = date @@ -10207,6 +10210,7 @@ class RevenueWithdrawalStateFailed(RevenueWithdrawalState): :rtype: :class:`RevenueWithdrawalStateFailed` """ + # noinspection PyPackageRequirements def __init__(self, type, **kwargs): self.type: str = type @@ -10218,6 +10222,7 @@ def de_json(cls, json_string): class TransactionPartner(JsonDeserializable): + # noinspection PyUnresolvedReferences """ This object describes the source of a transaction, or its recipient for outgoing transactions. Currently, it can be one of TransactionPartnerFragment @@ -10263,6 +10268,7 @@ class TransactionPartnerFragment(TransactionPartner): """ + # noinspection PyPackageRequirements def __init__(self, type, withdrawal_state=None, **kwargs): self.type: str = type self.withdrawal_state: Optional[RevenueWithdrawalState] = withdrawal_state From 08295ffce73fe6e92a317ce7a30f3905001000ba Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 8 Jul 2024 23:08:12 +0300 Subject: [PATCH 1623/1808] API version 7.7 in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36f0e7a43..98f8c389a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.6! +##

Supported Bot API version: 7.7!

Official documentation

Official ru documentation

From 73645302fefb828edc35ef4e02e4d78943e80ae3 Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Wed, 10 Jul 2024 16:49:00 +0200 Subject: [PATCH 1624/1808] Added the ability to make blockquote expandable --- telebot/formatting.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index dd7309416..e536e1bb0 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -341,7 +341,7 @@ def mcite(content: str, escape: Optional[bool]=True) -> str: return content -def hcite(content: str, escape: Optional[bool]=True) -> str: +def hcite(content: str, escape: Optional[bool] = True, expandable: Optional[bool] = False) -> str: """ Returns a html-formatted block-quotation string. @@ -350,11 +350,17 @@ def hcite(content: str, escape: Optional[bool]=True) -> str: :param escape: True if you need to escape special characters. Defaults to True. :type escape: :obj:`bool` - + + :param expandable: True if you need the quote to be expandable. Defaults to False. + :type expandable: :obj:`bool` + :return: The formatted string. :rtype: :obj:`str` """ - return '
{}
'.format(escape_html(content) if escape else content) + return "{}".format( + " expandable" if expandable else "", + escape_html(content) if escape else content, + ) def apply_html_entities(text: str, entities: Optional[List], custom_subs: Optional[Dict[str, str]]) -> str: From 77e3f130936f9e5751ef84018c7b3f7496681c5a Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Wed, 10 Jul 2024 17:47:31 +0200 Subject: [PATCH 1625/1808] Added the ability to make blockquote expandable --- telebot/formatting.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 51869a2a0..24299b51a 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -323,7 +323,7 @@ def hide_link(url: str) -> str: return f'' -def mcite(content: str, escape: Optional[bool]=True) -> str: +def mcite(content: str, escape: Optional[bool] = True, expandable: Optional[bool] = False) -> str: """ Returns a Markdown-formatted block-quotation string. @@ -333,11 +333,16 @@ def mcite(content: str, escape: Optional[bool]=True) -> str: :param escape: True if you need to escape special characters. Defaults to True. :type escape: :obj:`bool` + :param expandable: True if you need the quote to be expandable. Defaults to False. + :type expandable: :obj:`bool` + :return: The formatted string. :rtype: :obj:`str` """ content = escape_markdown(content) if escape else content - content = '\n'.join(['>' + line for line in content.split('\n')]) + content = "\n".join([">" + line for line in content.split("\n")]) + if expandable: + return f"**{content}||" return content From c901c75b468c3cbefa6bd2e486c73487c2ce8803 Mon Sep 17 00:00:00 2001 From: Hiddify <114227601+hiddify-com@users.noreply.github.com> Date: Thu, 11 Jul 2024 23:38:53 +0000 Subject: [PATCH 1626/1808] chg: allow callback_query_handler to do not have func arg. --- 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 630edb29e..b154d04ca 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7700,7 +7700,7 @@ def register_chosen_inline_handler(self, callback: Callable, func: Callable, pas self.add_chosen_inline_handler(handler_dict) - def callback_query_handler(self, func, **kwargs): + def callback_query_handler(self, func=None, **kwargs): """ Handles new incoming callback query. As a parameter to the decorator function, it passes :class:`telebot.types.CallbackQuery` object. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d8dd91e05..d50cbf7e0 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1700,7 +1700,7 @@ def register_chosen_inline_handler(self, callback: Callable[[Any], Awaitable], f handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_chosen_inline_handler(handler_dict) - def callback_query_handler(self, func, **kwargs): + def callback_query_handler(self, func=None, **kwargs): """ Handles new incoming callback query. As a parameter to the decorator function, it passes :class:`telebot.types.CallbackQuery` object. From 676597cf6c8353b4569d17ddc29e235049f05cf7 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Jul 2024 17:16:10 +0500 Subject: [PATCH 1627/1808] added redis support(not fully tested) --- telebot/custom_filters.py | 4 + telebot/storage/redis_storage.py | 300 ++++++++++++++----------------- 2 files changed, 136 insertions(+), 168 deletions(-) diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index dbba91d9c..e53c669a1 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -407,6 +407,9 @@ def check(self, message, text): chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot._user.id) + if chat_id is None: + chat_id = user_id # May change in future + if isinstance(text, list): new_text = [] for i in text: @@ -423,6 +426,7 @@ def check(self, message, text): bot_id=bot_id, message_thread_id=message_thread_id ) + if user_state == text: return True elif type(text) is list and user_state in text: diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 3fac57c46..9c52a3fa1 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -1,183 +1,147 @@ -from telebot.storage.base_storage import StateStorageBase, StateContext import json - -redis_installed = True -try: - from redis import Redis, ConnectionPool - -except: - redis_installed = False +import redis +from telebot.storage.base_storage import StateStorageBase, StateContext +from typing import Optional, Union class StateRedisStorage(StateStorageBase): - """ - This class is for Redis storage. - This will work only for states. - To use it, just pass this class to: - TeleBot(storage=StateRedisStorage()) - """ - def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_', redis_url=None): - super().__init__() + def __init__(self, host='localhost', port=6379, db=0, password=None, + prefix='telebot', + redis_url=None, + connection_pool: redis.ConnectionPool=None, + separator: Optional[str]=":", + ) -> None: + self.separator = separator + self.prefix = prefix + if not self.prefix: + raise ValueError("Prefix cannot be empty") + if redis_url: - self.redis = ConnectionPool.from_url(redis_url) + self.redis = redis.Redis.from_url(redis_url) + elif connection_pool: + self.redis = redis.Redis(connection_pool=connection_pool) else: - self.redis = ConnectionPool(host=host, port=port, db=db, password=password) - #self.con = Redis(connection_pool=self.redis) -> use this when necessary - # - # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} - self.prefix = prefix - if not redis_installed: - raise Exception("Redis is not installed. Install it via 'pip install redis'") + self.redis = redis.Redis(host=host, port=port, db=db, password=password) - def get_record(self, key): - """ - Function to get record from database. - It has nothing to do with states. - Made for backward compatibility - """ - connection = Redis(connection_pool=self.redis) - result = connection.get(self.prefix+str(key)) - connection.close() - if result: return json.loads(result) - return - - def set_record(self, key, value): - """ - Function to set record to database. - It has nothing to do with states. - Made for backward compatibility - """ - connection = Redis(connection_pool=self.redis) - connection.set(self.prefix+str(key), json.dumps(value)) - connection.close() + + def set_state( + self, chat_id: int, user_id: int, state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + ) -> bool: + if hasattr(state, "name"): + state = state.name + + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + def set_state_action(pipe): + pipe.multi() + #pipe.hset(_key, mapping={"state": state, "data": "{}"}) + pipe.hset(_key, "state", state) + + self.redis.transaction(set_state_action, _key) return True - def delete_record(self, key): - """ - Function to delete record from database. - It has nothing to do with states. - Made for backward compatibility - """ - connection = Redis(connection_pool=self.redis) - connection.delete(self.prefix+str(key)) - connection.close() + def get_state( + self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + ) -> Union[str, None]: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + state_bytes = self.redis.hget(_key, "state") + return state_bytes.decode('utf-8') if state_bytes else None + + def delete_state( + self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + ) -> bool: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + return self.redis.delete(_key) > 0 + + def set_data( + self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None + ) -> bool: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + def set_data_action(pipe): + pipe.multi() + data = pipe.hget(_key, "data") + data = data.execute()[0] + if data is None: + pipe.hset(_key, "data", json.dumps({key: value})) + else: + data = json.loads(data) + data[key] = value + pipe.hset(_key, "data", json.dumps(data)) + + self.redis.transaction(set_data_action, _key) return True - def set_state(self, chat_id, user_id, state): - """ - Set state for a particular user in a chat. - """ - response = self.get_record(chat_id) - user_id = str(user_id) - if hasattr(state, 'name'): - state = state.name + def get_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + ) -> dict: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = self.redis.hget(_key, "data") + return json.loads(data) if data else {} + + def reset_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + ) -> bool: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + def reset_data_action(pipe): + pipe.multi() + if pipe.exists(_key): + pipe.hset(_key, "data", "{}") + else: + return False + + self.redis.transaction(reset_data_action, _key) + return True - if response: - if user_id in response: - response[user_id]['state'] = state + def get_interactive_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + ) -> Optional[dict]: + return StateContext( + self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, + message_thread_id=message_thread_id, bot_id=bot_id + ) + + def save( + self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + ) -> bool: + _key = self.convert_params_to_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, + message_thread_id, bot_id + ) + + def save_action(pipe): + pipe.multi() + if pipe.exists(_key): + pipe.hset(_key, "data", json.dumps(data)) else: - response[user_id] = {'state': state, 'data': {}} - else: - response = {user_id: {'state': state, 'data': {}}} - self.set_record(chat_id, response) + return False + self.redis.transaction(save_action, _key) return True - - def delete_state(self, chat_id, user_id): - """ - Delete state for a particular user in a chat. - """ - response = self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - del response[user_id] - if user_id == str(chat_id): - self.delete_record(chat_id) - return True - else: self.set_record(chat_id, response) - return True - return False - - - def get_value(self, chat_id, user_id, key): - """ - Get value for a data of a user in a chat. - """ - response = self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - if key in response[user_id]['data']: - return response[user_id]['data'][key] - return None - - def get_state(self, chat_id, user_id): - """ - Get state of a user in a chat. - """ - response = self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - return response[user_id]['state'] - - return None - - - def get_data(self, chat_id, user_id): - """ - Get data of particular user in a particular chat. - """ - response = self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - return response[user_id]['data'] - return None - - - def reset_data(self, chat_id, user_id): - """ - Reset data of a user in a chat. - """ - response = self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - response[user_id]['data'] = {} - self.set_record(chat_id, response) - return True - - - - - def set_data(self, chat_id, user_id, key, value): - """ - Set data without interactive data. - """ - response = self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - response[user_id]['data'][key] = value - self.set_record(chat_id, response) - return True - return False - - def get_interactive_data(self, chat_id, user_id): - """ - Get Data in interactive way. - You can use with() with this function. - """ - return StateContext(self, chat_id, user_id) - - def save(self, chat_id, user_id, data): - response = self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - response[user_id]['data'] = data - self.set_record(chat_id, response) - return True - + def __str__(self) -> str: + keys = self.redis.keys(f"{self.prefix}{self.separator}*") + data = {key.decode(): self.redis.hgetall(key) for key in keys} + return f"" From 5bd42715582f3ebee6edd1da0dccd89dca0b1981 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Jul 2024 17:28:14 +0500 Subject: [PATCH 1628/1808] Added redis dependency check --- telebot/storage/redis_storage.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 9c52a3fa1..d32ab8f8e 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -1,8 +1,13 @@ import json -import redis from telebot.storage.base_storage import StateStorageBase, StateContext from typing import Optional, Union +redis_installed = True +try: + import redis +except ImportError: + redis_installed = False + class StateRedisStorage(StateStorageBase): def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot', @@ -10,6 +15,10 @@ def __init__(self, host='localhost', port=6379, db=0, password=None, connection_pool: redis.ConnectionPool=None, separator: Optional[str]=":", ) -> None: + + if not redis_installed: + raise ImportError("Redis is not installed. Please install it via pip install redis") + self.separator = separator self.prefix = prefix if not self.prefix: From a79fd77cba36073f7d70bcbff5240c0231ee8f3c Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Jul 2024 17:29:56 +0500 Subject: [PATCH 1629/1808] fix test --- telebot/storage/redis_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index d32ab8f8e..39c75a49b 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -12,7 +12,7 @@ class StateRedisStorage(StateStorageBase): def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot', redis_url=None, - connection_pool: redis.ConnectionPool=None, + connection_pool: 'redis.ConnectionPool'=None, separator: Optional[str]=":", ) -> None: From c29bf0e5358da3b21c519007fea64038745cdd2a Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 12 Jul 2024 21:51:34 +0500 Subject: [PATCH 1630/1808] Added pickle and renamed method --- telebot/storage/base_storage.py | 2 +- telebot/storage/memory_storage.py | 14 +-- telebot/storage/pickle_storage.py | 195 ++++++++++++++++-------------- telebot/storage/redis_storage.py | 14 +-- 4 files changed, 121 insertions(+), 104 deletions(-) diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py index 36545a77c..2358e93c9 100644 --- a/telebot/storage/base_storage.py +++ b/telebot/storage/base_storage.py @@ -48,7 +48,7 @@ def get_interactive_data(self, chat_id, user_id): def save(self, chat_id, user_id, data): raise NotImplementedError - def convert_params_to_key( + def _get_key( self, chat_id: int, user_id: int, diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index fbf9eebda..547445d0c 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -21,7 +21,7 @@ def set_state( if hasattr(state, "name"): state = state.name - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -37,7 +37,7 @@ def get_state( message_thread_id: Optional[int]=None, bot_id: Optional[int]=None ) -> Union[str, None]: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -50,7 +50,7 @@ def delete_state( self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None ) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -66,7 +66,7 @@ def set_data( business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -81,7 +81,7 @@ def get_data( message_thread_id: Optional[int]=None, bot_id: Optional[int]=None ) -> dict: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -92,7 +92,7 @@ def reset_data( message_thread_id: Optional[int]=None, bot_id: Optional[int]=None ) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -114,7 +114,7 @@ def save( self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None ) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 68c9fbed5..64ed7ce85 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -1,34 +1,26 @@ -from telebot.storage.base_storage import StateStorageBase, StateContext import os - import pickle - +import threading +from typing import Optional, Union +from telebot.storage.base_storage import StateStorageBase, StateContext class StatePickleStorage(StateStorageBase): - def __init__(self, file_path="./.state-save/states.pkl") -> None: - super().__init__() + def __init__(self, file_path: str="./.state-save/states.pkl", + prefix='telebot', separator: Optional[str]=":") -> None: self.file_path = file_path + self.prefix = prefix + self.separator = separator + self.lock = threading.Lock() + self.create_dir() - self.data = self.read() - def convert_old_to_new(self): - """ - Use this function to convert old storage to new storage. - This function is for people who was using pickle storage - that was in version <=4.3.1. - """ - # old looks like: - # {1: {'state': 'start', 'data': {'name': 'John'}} - # we should update old version pickle to new. - # new looks like: - # {1: {2: {'state': 'start', 'data': {'name': 'John'}}}} - new_data = {} - for key, value in self.data.items(): - # this returns us id and dict with data and state - new_data[key] = {key: value} # convert this to new - # pass it to global data - self.data = new_data - self.update_data() # update data in file + def _read_from_file(self) -> dict: + with open(self.file_path, 'rb') as f: + return pickle.load(f) + + def _write_to_file(self, data: dict) -> None: + with open(self.file_path, 'wb') as f: + pickle.dump(data, f) def create_dir(self): """ @@ -40,77 +32,102 @@ def create_dir(self): with open(self.file_path,'wb') as file: pickle.dump({}, file) - def read(self): - file = open(self.file_path, 'rb') - data = pickle.load(file) - file.close() - return data - - def update_data(self): - file = open(self.file_path, 'wb+') - pickle.dump(self.data, file, protocol=pickle.HIGHEST_PROTOCOL) - file.close() - - def set_state(self, chat_id, user_id, state): - if hasattr(state, 'name'): - state = state.name - if chat_id in self.data: - if user_id in self.data[chat_id]: - self.data[chat_id][user_id]['state'] = state - self.update_data() - return True + def set_state(self, chat_id: int, user_id: int, state: str, + business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + with self.lock: + data = self._read_from_file() + if _key not in data: + data[_key] = {"state": state, "data": {}} else: - self.data[chat_id][user_id] = {'state': state, 'data': {}} - self.update_data() - return True - self.data[chat_id] = {user_id: {'state': state, 'data': {}}} - self.update_data() + data[_key]["state"] = state + self._write_to_file(data) return True - - def delete_state(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - del self.data[chat_id][user_id] - if chat_id == user_id: - del self.data[chat_id] - self.update_data() - return True - - return False - - def get_state(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['state'] - - return None - def get_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['data'] + def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Union[str, None]: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + with self.lock: + data = self._read_from_file() + return data.get(_key, {}).get("state") - return None - - def reset_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'] = {} - self.update_data() + def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + with self.lock: + data = self._read_from_file() + if _key in data: + del data[_key] + self._write_to_file(data) return True - return False + return False - def set_data(self, chat_id, user_id, key, value): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'][key] = value - self.update_data() + def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], + business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + with self.lock: + data = self._read_from_file() + state_data = data.get(_key, {}) + state_data["data"][key] = value + if _key not in data: + data[_key] = {"state": None, "data": state_data} + else: + data[_key]["data"][key] = value + self._write_to_file(data) + return True + + def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> dict: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + with self.lock: + data = self._read_from_file() + return data.get(_key, {}).get("data", {}) + + def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + with self.lock: + data = self._read_from_file() + if _key in data: + data[_key]["data"] = {} + self._write_to_file(data) return True - raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + return False - def get_interactive_data(self, chat_id, user_id): - return StateContext(self, chat_id, user_id) + def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Optional[dict]: + return StateContext( + self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, + message_thread_id=message_thread_id, bot_id=bot_id + ) - def save(self, chat_id, user_id, data): - self.data[chat_id][user_id]['data'] = data - self.update_data() + def save(self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + with self.lock: + data = self._read_from_file() + data[_key]["data"] = data + self._write_to_file(data) + return True + + def __str__(self) -> str: + with self.lock: + with open(self.file_path, 'rb') as f: + data = pickle.load(f) + return f"" diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 39c75a49b..b16ea3016 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -40,7 +40,7 @@ def set_state( if hasattr(state, "name"): state = state.name - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -56,7 +56,7 @@ def get_state( self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, bot_id: Optional[int] = None ) -> Union[str, None]: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) state_bytes = self.redis.hget(_key, "state") @@ -66,7 +66,7 @@ def delete_state( self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, bot_id: Optional[int] = None ) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) return self.redis.delete(_key) > 0 @@ -76,7 +76,7 @@ def set_data( business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, bot_id: Optional[int] = None ) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -98,7 +98,7 @@ def get_data( self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, bot_id: Optional[int] = None ) -> dict: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) data = self.redis.hget(_key, "data") @@ -108,7 +108,7 @@ def reset_data( self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, bot_id: Optional[int] = None ) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) @@ -135,7 +135,7 @@ def save( self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, bot_id: Optional[int] = None ) -> bool: - _key = self.convert_params_to_key( + _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) From 15bced9b67b27b2c54a44f1f060ac552d141d4ca Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 17 Jul 2024 16:31:24 +0500 Subject: [PATCH 1631/1808] Fixed bugs, renamed StateContext for retrieve_data to StateDataContext to avoid conflicts, added support of statesv2 to async(only memory storage) --- telebot/__init__.py | 9 +- telebot/async_telebot.py | 137 +++++++++++++++--- telebot/asyncio_filters.py | 44 +++--- telebot/asyncio_handler_backends.py | 41 +----- telebot/asyncio_storage/__init__.py | 4 +- telebot/asyncio_storage/base_storage.py | 55 ++++++- telebot/asyncio_storage/memory_storage.py | 169 +++++++++++++++------- telebot/asyncio_storage/pickle_storage.py | 4 +- telebot/asyncio_storage/redis_storage.py | 4 +- telebot/states/__init__.py | 2 +- telebot/states/aio/__init__.py | 7 + telebot/states/aio/context.py | 138 ++++++++++++++++++ telebot/states/aio/middleware.py | 19 +++ telebot/storage/__init__.py | 4 +- telebot/storage/base_storage.py | 2 +- telebot/storage/memory_storage.py | 4 +- telebot/storage/pickle_storage.py | 4 +- telebot/storage/redis_storage.py | 4 +- 18 files changed, 485 insertions(+), 166 deletions(-) create mode 100644 telebot/states/aio/context.py create mode 100644 telebot/states/aio/middleware.py diff --git a/telebot/__init__.py b/telebot/__init__.py index 31370d3e3..08ae9fc6f 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1180,6 +1180,9 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F if restart_on_change: self._setup_change_detector(path_to_watch) + if not self._user: + self._user = self.get_me() + logger.info('Starting your bot with username: [@%s]', self.user.username) if self.threaded: @@ -6678,7 +6681,7 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Option chat_id = user_id if bot_id is None: bot_id = self.bot_id - self.current_states.set_state( + return self.current_states.set_state( chat_id=chat_id, user_id=user_id, state=state, bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) @@ -6710,7 +6713,7 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None, chat_id = user_id if bot_id is None: bot_id = self.bot_id - self.current_states.reset_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + return self.current_states.reset_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) @@ -6731,7 +6734,7 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_conne chat_id = user_id if bot_id is None: bot_id = self.bot_id - self.current_states.delete_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + return self.current_states.delete_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d8dd91e05..2d443cb5b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4,7 +4,7 @@ import logging import re import traceback -from typing import Any, Awaitable, Callable, List, Optional, Union +from typing import Any, Awaitable, Callable, List, Optional, Union, Dict import sys # this imports are used to avoid circular import error @@ -18,7 +18,7 @@ from inspect import signature, iscoroutinefunction -from telebot import util, types, asyncio_helper +from telebot import util, types, asyncio_helper, apihelper # have to use sync import asyncio from telebot import asyncio_filters @@ -117,6 +117,8 @@ class AsyncTeleBot: :param colorful_logs: Outputs colorful logs :type colorful_logs: :obj:`bool`, optional + :param token_check: Check token on start + :type token_check: :obj:`bool`, optional, defaults to True """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[int]=None, @@ -126,7 +128,8 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, allow_sending_without_reply: Optional[bool]=None, - colorful_logs: Optional[bool]=False) -> None: + colorful_logs: Optional[bool]=False, + token_check: Optional[bool]=True) -> None: # update-related self.token = token @@ -183,6 +186,14 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.middlewares = [] self._user = None # set during polling + self.bot_id = None + + if token_check: + result = apihelper.get_me(token) + self._user = types.User.de_json(result) + self.bot_id = self._user.id + + @property def user(self): @@ -424,7 +435,8 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: # show warning logger.warning("Setting non_stop to False will stop polling on API and system exceptions.") - self._user = await self.get_me() + if not self._user: + self._user = await self.get_me() logger.info('Starting your bot with username: [@%s]', self.user.username) @@ -7831,7 +7843,10 @@ async def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: """ return await asyncio_helper.get_forum_topic_icon_stickers(self.token) - async def set_state(self, user_id: int, state: Union[State, int, str], chat_id: Optional[int]=None): + + async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None) -> None: """ Sets a new state of a user. @@ -7850,13 +7865,29 @@ async def set_state(self, user_id: int, state: Union[State, int, str], chat_id: :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: None """ - if not chat_id: + if chat_id is None: chat_id = user_id - await self.current_states.set_state(chat_id, user_id, state) + if bot_id is None: + bot_id = self.bot_id + return await self.current_states.set_state( + chat_id=chat_id, user_id=user_id, state=state, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) - async def reset_data(self, user_id: int, chat_id: Optional[int]=None): + + async def reset_data(self, user_id: int, chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: """ Reset data for a user in chat. @@ -7866,13 +7897,27 @@ async def reset_data(self, user_id: int, chat_id: Optional[int]=None): :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: None """ if chat_id is None: chat_id = user_id - await self.current_states.reset_data(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + return await self.current_states.reset_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) + - async def delete_state(self, user_id: int, chat_id: Optional[int]=None): + async def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: """ Delete the current state of a user. @@ -7884,11 +7929,16 @@ async def delete_state(self, user_id: int, chat_id: Optional[int]=None): :return: None """ - if not chat_id: + if chat_id is None: chat_id = user_id - await self.current_states.delete_state(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + return await self.current_states.delete_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) + - def retrieve_data(self, user_id: int, chat_id: Optional[int]=None): + def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Optional[Dict[str, Any]]: """ Returns context manager with data for a user in chat. @@ -7898,14 +7948,30 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None): :param chat_id: Chat's unique identifier, defaults to user_id :type chat_id: int, optional + :param bot_id: Bot's identifier + :type bot_id: int, optional + + :param business_connection_id: Business identifier + :type business_connection_id: str, optional + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: int, optional + :return: Context manager with data for a user in chat :rtype: Optional[Any] """ - if not chat_id: + if chat_id is None: chat_id = user_id - return self.current_states.get_interactive_data(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + return self.current_states.get_interactive_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id) + - async def get_state(self, user_id, chat_id: Optional[int]=None): + async def get_state(self, user_id: int, chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Union[int, str]: """ Gets current state of a user. Not recommended to use this method. But it is ok for debugging. @@ -7916,14 +7982,31 @@ async def get_state(self, user_id, chat_id: Optional[int]=None): :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: state of a user :rtype: :obj:`int` or :obj:`str` or :class:`telebot.types.State` """ - if not chat_id: + if chat_id is None: chat_id = user_id - return await self.current_states.get_state(chat_id, user_id) + if bot_id is None: + bot_id = self.bot_id + return await self.current_states.get_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) + - async def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): + async def add_data(self, user_id: int, chat_id: Optional[int]=None, + business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None, + **kwargs) -> None: """ Add data to states. @@ -7933,10 +8016,22 @@ async def add_data(self, user_id: int, chat_id: Optional[int]=None, **kwargs): :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :param kwargs: Data to add :return: None """ - if not chat_id: + if chat_id is None: chat_id = user_id + if bot_id is None: + bot_id = self.bot_id for key, value in kwargs.items(): - await self.current_states.set_data(chat_id, user_id, key, value) + await self.current_states.set_data(chat_id=chat_id, user_id=user_id, key=key, value=value, bot_id=bot_id, + business_connection_id=business_connection_id, message_thread_id=message_thread_id) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index da794b77b..389ce1643 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -3,6 +3,7 @@ from telebot.asyncio_handler_backends import State from telebot import types +from telebot.states import resolve_context class SimpleCustomFilter(ABC): @@ -397,18 +398,11 @@ async def check(self, message, text): :meta private: """ if text == '*': return True + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot._user.id) - # needs to work with callbackquery - if isinstance(message, types.Message): - chat_id = message.chat.id - user_id = message.from_user.id - - if isinstance(message, types.CallbackQuery): - - chat_id = message.message.chat.id - user_id = message.from_user.id - message = message.message - + if chat_id is None: + chat_id = user_id # May change in future if isinstance(text, list): new_text = [] @@ -418,21 +412,19 @@ async def check(self, message, text): text = new_text elif isinstance(text, State): text = text.name - - if message.chat.type in ['group', 'supergroup']: - group_state = await self.bot.current_states.get_state(chat_id, user_id) - if group_state == text: - return True - elif type(text) is list and group_state in text: - return True - - - else: - user_state = await self.bot.current_states.get_state(chat_id, user_id) - if user_state == text: - return True - elif type(text) is list and user_state in text: - return True + + user_state = await self.bot.current_states.get_state( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + if user_state == text: + return True + elif type(text) is list and user_state in text: + return True class IsDigitFilter(SimpleCustomFilter): diff --git a/telebot/asyncio_handler_backends.py b/telebot/asyncio_handler_backends.py index 0861a9893..6c96cc2d7 100644 --- a/telebot/asyncio_handler_backends.py +++ b/telebot/asyncio_handler_backends.py @@ -1,6 +1,7 @@ """ File with all middleware classes, states. """ +from telebot.states import State, StatesGroup class BaseMiddleware: @@ -48,46 +49,6 @@ async def post_process(self, message, data, exception): raise NotImplementedError -class State: - """ - Class representing a state. - - .. code-block:: python3 - - class MyStates(StatesGroup): - my_state = State() # returns my_state:State string. - """ - def __init__(self) -> None: - self.name = None - - def __str__(self) -> str: - return self.name - - -class StatesGroup: - """ - Class representing common states. - - .. code-block:: python3 - - class MyStates(StatesGroup): - my_state = State() # returns my_state:State string. - """ - def __init_subclass__(cls) -> None: - state_list = [] - for name, value in cls.__dict__.items(): - if not name.startswith('__') and not callable(value) and isinstance(value, State): - # change value of that variable - value.name = ':'.join((cls.__name__, name)) - value.group = cls - state_list.append(value) - cls._state_list = state_list - - @classmethod - def state_list(self): - return self._state_list - - class SkipHandler: """ Class for skipping handlers. diff --git a/telebot/asyncio_storage/__init__.py b/telebot/asyncio_storage/__init__.py index 892f0af94..1f9d51650 100644 --- a/telebot/asyncio_storage/__init__.py +++ b/telebot/asyncio_storage/__init__.py @@ -1,13 +1,13 @@ from telebot.asyncio_storage.memory_storage import StateMemoryStorage from telebot.asyncio_storage.redis_storage import StateRedisStorage from telebot.asyncio_storage.pickle_storage import StatePickleStorage -from telebot.asyncio_storage.base_storage import StateContext,StateStorageBase +from telebot.asyncio_storage.base_storage import StateDataContext, StateStorageBase __all__ = [ - 'StateStorageBase', 'StateContext', + 'StateStorageBase', 'StateDataContext', 'StateMemoryStorage', 'StateRedisStorage', 'StatePickleStorage' ] \ No newline at end of file diff --git a/telebot/asyncio_storage/base_storage.py b/telebot/asyncio_storage/base_storage.py index 38615c4c2..6d06e7bfc 100644 --- a/telebot/asyncio_storage/base_storage.py +++ b/telebot/asyncio_storage/base_storage.py @@ -1,6 +1,5 @@ import copy - class StateStorageBase: def __init__(self) -> None: pass @@ -42,27 +41,67 @@ async def reset_data(self, chat_id, user_id): async def get_state(self, chat_id, user_id): raise NotImplementedError - + + def get_interactive_data(self, chat_id, user_id): + """ + Should be sync, but should provide a context manager + with __aenter__ and __aexit__ methods. + """ + raise NotImplementedError + async def save(self, chat_id, user_id, data): raise NotImplementedError + + def _get_key( + self, + chat_id: int, + user_id: int, + prefix: str, + separator: str, + business_connection_id: str=None, + message_thread_id: int=None, + bot_id: int=None + ) -> str: + """ + Convert parameters to a key. + """ + params = [prefix] + if bot_id: + params.append(str(bot_id)) + if business_connection_id: + params.append(business_connection_id) + if message_thread_id: + params.append(str(message_thread_id)) + params.append(str(chat_id)) + params.append(str(user_id)) + + return separator.join(params) + + + + -class StateContext: +class StateDataContext: """ Class for data. """ - - def __init__(self, obj, chat_id, user_id): + def __init__(self , obj, chat_id, user_id, business_connection_id=None, message_thread_id=None, bot_id=None, ): self.obj = obj self.data = None self.chat_id = chat_id self.user_id = user_id + self.bot_id = bot_id + self.business_connection_id = business_connection_id + self.message_thread_id = message_thread_id + - async def __aenter__(self): - self.data = copy.deepcopy(await self.obj.get_data(self.chat_id, self.user_id)) + data = await self.obj.get_data(chat_id=self.chat_id, user_id=self.user_id, business_connection_id=self.business_connection_id, + message_thread_id=self.message_thread_id, bot_id=self.bot_id) + self.data = copy.deepcopy(data) return self.data async def __aexit__(self, exc_type, exc_val, exc_tb): - return await self.obj.save(self.chat_id, self.user_id, self.data) \ No newline at end of file + return await self.obj.save(self.chat_id, self.user_id, self.data, self.business_connection_id, self.message_thread_id, self.bot_id) \ No newline at end of file diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py index 45c2ad914..e65ed74d4 100644 --- a/telebot/asyncio_storage/memory_storage.py +++ b/telebot/asyncio_storage/memory_storage.py @@ -1,66 +1,131 @@ -from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext +from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext +from typing import Optional, Union class StateMemoryStorage(StateStorageBase): - def __init__(self) -> None: - self.data = {} - # - # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} - - - async def set_state(self, chat_id, user_id, state): - if hasattr(state, 'name'): + def __init__(self, + separator: Optional[str]=":", + prefix: Optional[str]="telebot" + ) -> None: + self.separator = separator + self.prefix = prefix + if not self.prefix: + raise ValueError("Prefix cannot be empty") + + self.data = {} # key: telebot:bot_id:business_connection_id:message_thread_id:chat_id:user_id + + async def set_state( + self, chat_id: int, user_id: int, state: str, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + + ) -> bool: + if hasattr(state, "name"): state = state.name - if chat_id in self.data: - if user_id in self.data[chat_id]: - self.data[chat_id][user_id]['state'] = state - return True - else: - self.data[chat_id][user_id] = {'state': state, 'data': {}} - return True - self.data[chat_id] = {user_id: {'state': state, 'data': {}}} + + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + if self.data.get(_key) is None: + self.data[_key] = {"state": state, "data": {}} + else: + self.data[_key]["state"] = state + return True - async def delete_state(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - del self.data[chat_id][user_id] - if chat_id == user_id: - del self.data[chat_id] - - return True + async def get_state( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> Union[str, None]: - return False + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + if self.data.get(_key) is None: + return None + + return self.data[_key]["state"] - async def get_state(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['state'] + async def delete_state( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + if self.data.get(_key) is None: + return False + + del self.data[_key] + return True + + + async def set_data( + self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], + business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, + bot_id: Optional[int]=None) -> bool: + + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + + if self.data.get(_key) is None: + return False + self.data[_key]["data"][key] = value + return True - return None - async def get_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['data'] + + async def get_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> dict: - return None + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) - async def reset_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'] = {} - return True - return False + return self.data.get(_key, {}).get("data", None) + + async def reset_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> bool: + + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) - async def set_data(self, chat_id, user_id, key, value): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'][key] = value - return True - raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + if self.data.get(_key) is None: + return False + self.data[_key]["data"] = {} + return True + + def get_interactive_data( + self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> Optional[dict]: + return StateDataContext( + self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, + message_thread_id=message_thread_id, bot_id=bot_id + ) + + async def save( + self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + ) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, + message_thread_id, bot_id + ) - def get_interactive_data(self, chat_id, user_id): - return StateContext(self, chat_id, user_id) + if self.data.get(_key) is None: + return False + self.data[_key]["data"] = data + return True + + def __str__(self) -> str: + return f"" + + - async def save(self, chat_id, user_id, data): - self.data[chat_id][user_id]['data'] = data \ No newline at end of file diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index cf446d85b..fcffbb289 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -1,4 +1,4 @@ -from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext +from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext import os import pickle @@ -103,7 +103,7 @@ async def set_data(self, chat_id, user_id, key, value): raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) def get_interactive_data(self, chat_id, user_id): - return StateContext(self, chat_id, user_id) + return StateDataContext(self, chat_id, user_id) async def save(self, chat_id, user_id, data): self.data[chat_id][user_id]['data'] = data diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 84db253e5..86fde3716 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,4 +1,4 @@ -from telebot.asyncio_storage.base_storage import StateStorageBase, StateContext +from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext import json redis_installed = True @@ -167,7 +167,7 @@ def get_interactive_data(self, chat_id, user_id): Get Data in interactive way. You can use with() with this function. """ - return StateContext(self, chat_id, user_id) + return StateDataContext(self, chat_id, user_id) async def save(self, chat_id, user_id, data): response = await self.get_record(chat_id) diff --git a/telebot/states/__init__.py b/telebot/states/__init__.py index b8efe3bbe..015f135e4 100644 --- a/telebot/states/__init__.py +++ b/telebot/states/__init__.py @@ -16,7 +16,7 @@ def __init__(self) -> None: self.name: str = None self.group: StatesGroup = None def __str__(self) -> str: - return f"<{self.group.__name__}:{self.name}>" + return f"<{self.name}>" class StatesGroup: diff --git a/telebot/states/aio/__init__.py b/telebot/states/aio/__init__.py index e69de29bb..60f9ea2b7 100644 --- a/telebot/states/aio/__init__.py +++ b/telebot/states/aio/__init__.py @@ -0,0 +1,7 @@ +from .context import StateContext +from .middleware import StateMiddleware + +__all__ = [ + 'StateContext', + 'StateMiddleware', +] \ No newline at end of file diff --git a/telebot/states/aio/context.py b/telebot/states/aio/context.py new file mode 100644 index 000000000..431eba93c --- /dev/null +++ b/telebot/states/aio/context.py @@ -0,0 +1,138 @@ +from telebot.states import State, StatesGroup +from telebot.types import CallbackQuery, Message +from telebot.async_telebot import AsyncTeleBot +from telebot.states import resolve_context + +from typing import Union + + + +class StateContext(): + """ + Class representing a state context. + + Passed through a middleware to provide easy way to set states. + + .. code-block:: python3 + + @bot.message_handler(commands=['start']) + async def start_ex(message: types.Message, state_context: StateContext): + await state_context.set(MyStates.name) + await bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id) + # also, state_context.data(), .add_data(), .reset_data(), .delete() methods available. + """ + + def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None: + self.message: Union[Message, CallbackQuery] = message + self.bot: AsyncTeleBot = bot + self.bot_id = self.bot.bot_id + + async def set(self, state: Union[State, str]) -> None: + """ + Set state for current user. + + :param state: State object or state name. + :type state: Union[State, str] + + .. code-block:: python3 + + @bot.message_handler(commands=['start']) + async def start_ex(message: types.Message, state_context: StateContext): + await state_context.set(MyStates.name) + await bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id) + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + if isinstance(state, State): + state = state.name + return await self.bot.set_state( + chat_id=chat_id, + user_id=user_id, + state=state, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + async def get(self) -> str: + """ + Get current state for current user. + + :return: Current state name. + :rtype: str + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + return await self.bot.get_state( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + async def reset_data(self) -> None: + """ + Reset data for current user. + State will not be changed. + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + return await self.bot.reset_data( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + async def delete(self) -> None: + """ + Deletes state and data for current user. + """ + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + return await self.bot.delete_state( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + def data(self) -> dict: + """ + Get data for current user. + + .. code-block:: python3 + + with state_context.data() as data: + print(data) + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + return self.bot.retrieve_data( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id + ) + + async def add_data(self, **kwargs) -> None: + """ + Add data for current user. + + :param kwargs: Data to add. + :type kwargs: dict + """ + + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + return await self.bot.add_data( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + bot_id=bot_id, + message_thread_id=message_thread_id, + **kwargs + ) + \ No newline at end of file diff --git a/telebot/states/aio/middleware.py b/telebot/states/aio/middleware.py new file mode 100644 index 000000000..66283dc82 --- /dev/null +++ b/telebot/states/aio/middleware.py @@ -0,0 +1,19 @@ +from telebot.asyncio_handler_backends import BaseMiddleware +from telebot.async_telebot import AsyncTeleBot +from telebot.states.sync.context import StateContext +from telebot.util import update_types +from telebot import types + + +class StateMiddleware(BaseMiddleware): + + def __init__(self, bot: AsyncTeleBot) -> None: + self.update_sensitive = False + self.update_types = update_types + self.bot: AsyncTeleBot = bot + + async def pre_process(self, message, data): + data['state_context'] = StateContext(message, self.bot) + + async def post_process(self, message, data, exception): + pass diff --git a/telebot/storage/__init__.py b/telebot/storage/__init__.py index 59e2b058c..954c1b31d 100644 --- a/telebot/storage/__init__.py +++ b/telebot/storage/__init__.py @@ -1,13 +1,13 @@ from telebot.storage.memory_storage import StateMemoryStorage from telebot.storage.redis_storage import StateRedisStorage from telebot.storage.pickle_storage import StatePickleStorage -from telebot.storage.base_storage import StateContext,StateStorageBase +from telebot.storage.base_storage import StateDataContext,StateStorageBase __all__ = [ - 'StateStorageBase', 'StateContext', + 'StateStorageBase', 'StateDataContext', 'StateMemoryStorage', 'StateRedisStorage', 'StatePickleStorage' ] \ No newline at end of file diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py index 2358e93c9..25a6c6b04 100644 --- a/telebot/storage/base_storage.py +++ b/telebot/storage/base_storage.py @@ -78,7 +78,7 @@ def _get_key( -class StateContext: +class StateDataContext: """ Class for data. """ diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 547445d0c..82142430a 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -1,4 +1,4 @@ -from telebot.storage.base_storage import StateStorageBase, StateContext +from telebot.storage.base_storage import StateStorageBase, StateDataContext from typing import Optional, Union class StateMemoryStorage(StateStorageBase): @@ -105,7 +105,7 @@ def get_interactive_data( self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None ) -> Optional[dict]: - return StateContext( + return StateDataContext( self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id, bot_id=bot_id ) diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 64ed7ce85..917491814 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -2,7 +2,7 @@ import pickle import threading from typing import Optional, Union -from telebot.storage.base_storage import StateStorageBase, StateContext +from telebot.storage.base_storage import StateStorageBase, StateDataContext class StatePickleStorage(StateStorageBase): def __init__(self, file_path: str="./.state-save/states.pkl", @@ -110,7 +110,7 @@ def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optiona def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Optional[dict]: - return StateContext( + return StateDataContext( self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id, bot_id=bot_id ) diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index b16ea3016..0aa7c43ea 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -1,5 +1,5 @@ import json -from telebot.storage.base_storage import StateStorageBase, StateContext +from telebot.storage.base_storage import StateStorageBase, StateDataContext from typing import Optional, Union redis_installed = True @@ -126,7 +126,7 @@ def get_interactive_data( self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, bot_id: Optional[int] = None ) -> Optional[dict]: - return StateContext( + return StateDataContext( self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id, bot_id=bot_id ) From ff6485d66570e497038f6a1cc9fa6ac10ee05f18 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 19 Jul 2024 21:26:33 +0500 Subject: [PATCH 1632/1808] Async version fully supported, partially tested. --- telebot/asyncio_storage/pickle_storage.py | 206 +++++++++------- telebot/asyncio_storage/redis_storage.py | 274 +++++++++------------- telebot/storage/pickle_storage.py | 5 +- telebot/storage/redis_storage.py | 4 +- 4 files changed, 237 insertions(+), 252 deletions(-) diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index fcffbb289..0c7da7eb1 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -1,28 +1,43 @@ -from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext -import os +try: + import aiofiles +except ImportError: + aiofiles_installed = False + +import os import pickle +import asyncio +from typing import Optional, Union, Callable, Any +from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext + +def with_lock(func: Callable) -> Callable: + async def wrapper(self, *args, **kwargs): + async with self.lock: + return await func(self, *args, **kwargs) + return wrapper class StatePickleStorage(StateStorageBase): - def __init__(self, file_path="./.state-save/states.pkl") -> None: + def __init__(self, file_path: str = "./.state-save/states.pkl", + prefix='telebot', separator: Optional[str] = ":") -> None: + + if not aiofiles_installed: + raise ImportError("Please install aiofiles using `pip install aiofiles`") + self.file_path = file_path + self.prefix = prefix + self.separator = separator + self.lock = asyncio.Lock() self.create_dir() - self.data = self.read() - - async def convert_old_to_new(self): - # old looks like: - # {1: {'state': 'start', 'data': {'name': 'John'}} - # we should update old version pickle to new. - # new looks like: - # {1: {2: {'state': 'start', 'data': {'name': 'John'}}}} - new_data = {} - for key, value in self.data.items(): - # this returns us id and dict with data and state - new_data[key] = {key: value} # convert this to new - # pass it to global data - self.data = new_data - self.update_data() # update data in file + + async def _read_from_file(self) -> dict: + async with aiofiles.open(self.file_path, 'rb') as f: + data = await f.read() + return pickle.loads(data) + + async def _write_to_file(self, data: dict) -> None: + async with aiofiles.open(self.file_path, 'wb') as f: + await f.write(pickle.dumps(data)) def create_dir(self): """ @@ -34,77 +49,100 @@ def create_dir(self): with open(self.file_path,'wb') as file: pickle.dump({}, file) - def read(self): - file = open(self.file_path, 'rb') - data = pickle.load(file) - file.close() - return data - - def update_data(self): - file = open(self.file_path, 'wb+') - pickle.dump(self.data, file, protocol=pickle.HIGHEST_PROTOCOL) - file.close() - - async def set_state(self, chat_id, user_id, state): - if hasattr(state, 'name'): - state = state.name - if chat_id in self.data: - if user_id in self.data[chat_id]: - self.data[chat_id][user_id]['state'] = state - self.update_data() - return True - else: - self.data[chat_id][user_id] = {'state': state, 'data': {}} - self.update_data() - return True - self.data[chat_id] = {user_id: {'state': state, 'data': {}}} - self.update_data() + + @with_lock + async def set_state(self, chat_id: int, user_id: int, state: str, + business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = await self._read_from_file() + if _key not in data: + data[_key] = {"state": state, "data": {}} + else: + data[_key]["state"] = state + await self._write_to_file(data) return True - - async def delete_state(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - del self.data[chat_id][user_id] - if chat_id == user_id: - del self.data[chat_id] - self.update_data() - return True + @with_lock + async def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Union[str, None]: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = await self._read_from_file() + return data.get(_key, {}).get("state") + + @with_lock + async def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = await self._read_from_file() + if _key in data: + del data[_key] + await self._write_to_file(data) + return True return False - - async def get_state(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['state'] - - return None - async def get_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - return self.data[chat_id][user_id]['data'] - - return None - - async def reset_data(self, chat_id, user_id): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'] = {} - self.update_data() - return True + @with_lock + async def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = await self._read_from_file() + state_data = data.get(_key, {}) + state_data["data"][key] = value + if _key not in data: + data[_key] = {"state": None, "data": state_data} + else: + data[_key]["data"][key] = value + await self._write_to_file(data) + return True + + @with_lock + async def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> dict: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = await self._read_from_file() + return data.get(_key, {}).get("data", {}) + + @with_lock + async def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = await self._read_from_file() + if _key in data: + data[_key]["data"] = {} + await self._write_to_file(data) + return True return False - async def set_data(self, chat_id, user_id, key, value): - if self.data.get(chat_id): - if self.data[chat_id].get(user_id): - self.data[chat_id][user_id]['data'][key] = value - self.update_data() - return True - raise RuntimeError('chat_id {} and user_id {} does not exist'.format(chat_id, user_id)) + def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Optional[dict]: + return StateDataContext( + self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, + message_thread_id=message_thread_id, bot_id=bot_id + ) - def get_interactive_data(self, chat_id, user_id): - return StateDataContext(self, chat_id, user_id) + @with_lock + async def save(self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + _key = self._get_key( + chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + ) + data = await self._read_from_file() + data[_key]["data"] = data + await self._write_to_file(data) + return True - async def save(self, chat_id, user_id, data): - self.data[chat_id][user_id]['data'] = data - self.update_data() \ No newline at end of file + def __str__(self) -> str: + return f"StatePickleStorage({self.file_path}, {self.prefix})" diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 86fde3716..a6b19780d 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,179 +1,131 @@ -from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext -import json -redis_installed = True -is_actual_aioredis = False try: - import aioredis - is_actual_aioredis = True + import redis + from redis.asyncio import Redis, ConnectionPool except ImportError: - try: - from redis import asyncio as aioredis - except ImportError: - redis_installed = False + redis_installed = False + +import json +from typing import Optional, Union, Callable, Coroutine +import asyncio + +from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext + + +def async_with_lock(func: Callable[..., Coroutine]) -> Callable[..., Coroutine]: + async def wrapper(self, *args, **kwargs): + async with self.lock: + return await func(self, *args, **kwargs) + return wrapper +def async_with_pipeline(func: Callable[..., Coroutine]) -> Callable[..., Coroutine]: + async def wrapper(self, *args, **kwargs): + async with self.redis.pipeline() as pipe: + pipe.multi() + result = await func(self, pipe, *args, **kwargs) + await pipe.execute() + return result + return wrapper class StateRedisStorage(StateStorageBase): - """ - This class is for Redis storage. - This will work only for states. - To use it, just pass this class to: - TeleBot(storage=StateRedisStorage()) - """ - def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot_', redis_url=None): + def __init__(self, host='localhost', port=6379, db=0, password=None, + prefix='telebot', + redis_url=None, + connection_pool: 'ConnectionPool'=None, + separator: Optional[str] = ":", + ) -> None: + if not redis_installed: - raise ImportError('AioRedis is not installed. Install it via "pip install aioredis"') + raise ImportError("Please install redis using `pip install redis`") + + self.separator = separator + self.prefix = prefix + if not self.prefix: + raise ValueError("Prefix cannot be empty") - if is_actual_aioredis: - aioredis_version = tuple(map(int, aioredis.__version__.split(".")[0])) - if aioredis_version < (2,): - raise ImportError('Invalid aioredis version. Aioredis version should be >= 2.0.0') if redis_url: - self.redis = aioredis.Redis.from_url(redis_url) + self.redis = redis.asyncio.from_url(redis_url) + elif connection_pool: + self.redis = Redis(connection_pool=connection_pool) else: - self.redis = aioredis.Redis(host=host, port=port, db=db, password=password) + self.redis = Redis(host=host, port=port, db=db, password=password) + + self.lock = asyncio.Lock() + + @async_with_lock + @async_with_pipeline + async def set_state(self, pipe, chat_id: int, user_id: int, state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + if hasattr(state, "name"): + state = state.name - self.prefix = prefix - #self.con = Redis(connection_pool=self.redis) -> use this when necessary - # - # {chat_id: {user_id: {'state': None, 'data': {}}, ...}, ...} - - async def get_record(self, key): - """ - Function to get record from database. - It has nothing to do with states. - Made for backward compatibility - """ - result = await self.redis.get(self.prefix+str(key)) - if result: return json.loads(result) - return - - async def set_record(self, key, value): - """ - Function to set record to database. - It has nothing to do with states. - Made for backward compatibility - """ - - await self.redis.set(self.prefix+str(key), json.dumps(value)) + _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + await pipe.hset(_key, "state", state) return True - async def delete_record(self, key): - """ - Function to delete record from database. - It has nothing to do with states. - Made for backward compatibility - """ - await self.redis.delete(self.prefix+str(key)) + async def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Union[str, None]: + _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + state_bytes = await self.redis.hget(_key, "state") + return state_bytes.decode('utf-8') if state_bytes else None + + async def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + result = await self.redis.delete(_key) + return result > 0 + + @async_with_lock + @async_with_pipeline + async def set_data(self, pipe, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None) -> bool: + _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + data = await pipe.hget(_key, "data") + data = await pipe.execute() + data = data[0] + if data is None: + await pipe.hset(_key, "data", json.dumps({key: value})) + else: + data = json.loads(data) + data[key] = value + await pipe.hset(_key, "data", json.dumps(data)) return True - async def set_state(self, chat_id, user_id, state): - """ - Set state for a particular user in a chat. - """ - response = await self.get_record(chat_id) - user_id = str(user_id) - if hasattr(state, 'name'): - state = state.name - if response: - if user_id in response: - response[user_id]['state'] = state - else: - response[user_id] = {'state': state, 'data': {}} + async def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> dict: + _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + data = await self.redis.hget(_key, "data") + return json.loads(data) if data else {} + + @async_with_lock + @async_with_pipeline + async def reset_data(self, pipe, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + if await pipe.exists(_key): + await pipe.hset(_key, "data", "{}") else: - response = {user_id: {'state': state, 'data': {}}} - await self.set_record(chat_id, response) + return False + return True + def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Optional[dict]: + return StateDataContext(self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, + message_thread_id=message_thread_id, bot_id=bot_id) + + @async_with_lock + @async_with_pipeline + async def save(self, pipe, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + if await pipe.exists(_key): + await pipe.hset(_key, "data", json.dumps(data)) + else: + return False return True - - async def delete_state(self, chat_id, user_id): - """ - Delete state for a particular user in a chat. - """ - response = await self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - del response[user_id] - if user_id == str(chat_id): - await self.delete_record(chat_id) - return True - else: await self.set_record(chat_id, response) - return True - return False - - async def get_value(self, chat_id, user_id, key): - """ - Get value for a data of a user in a chat. - """ - response = await self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - if key in response[user_id]['data']: - return response[user_id]['data'][key] - return None - - async def get_state(self, chat_id, user_id): - """ - Get state of a user in a chat. - """ - response = await self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - return response[user_id]['state'] - - return None - - async def get_data(self, chat_id, user_id): - """ - Get data of particular user in a particular chat. - """ - response = await self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - return response[user_id]['data'] - return None - - async def reset_data(self, chat_id, user_id): - """ - Reset data of a user in a chat. - """ - response = await self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - response[user_id]['data'] = {} - await self.set_record(chat_id, response) - return True - - async def set_data(self, chat_id, user_id, key, value): - """ - Set data without interactive data. - """ - response = await self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - response[user_id]['data'][key] = value - await self.set_record(chat_id, response) - return True - return False - - def get_interactive_data(self, chat_id, user_id): - """ - Get Data in interactive way. - You can use with() with this function. - """ - return StateDataContext(self, chat_id, user_id) - - async def save(self, chat_id, user_id, data): - response = await self.get_record(chat_id) - user_id = str(user_id) - if response: - if user_id in response: - response[user_id]['data'] = data - await self.set_record(chat_id, response) - return True + + def __str__(self) -> str: + # include some connection info + return f"StateRedisStorage({self.redis})" diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 917491814..32a9653c7 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -127,7 +127,4 @@ def save(self, chat_id: int, user_id: int, data: dict, business_connection_id: O return True def __str__(self) -> str: - with self.lock: - with open(self.file_path, 'rb') as f: - data = pickle.load(f) - return f"" + return f"StatePickleStorage({self.file_path}, {self.prefix})" diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 0aa7c43ea..d41da05c8 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -151,6 +151,4 @@ def save_action(pipe): return True def __str__(self) -> str: - keys = self.redis.keys(f"{self.prefix}{self.separator}*") - data = {key.decode(): self.redis.hgetall(key) for key in keys} - return f"" + return f"StateRedisStorage({self.redis})" From 1a47edf9db5e25b91bbc52c77ada19e32236ec12 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 21 Jul 2024 18:27:45 +0500 Subject: [PATCH 1633/1808] Fix #2339 --- telebot/asyncio_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index da794b77b..f4e594a7a 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -301,7 +301,7 @@ async def check(self, message): """ :meta private: """ - return message.forward_date is not None + return message.forward_origin is not None class IsReplyFilter(SimpleCustomFilter): From af79db6ad3528d01be167bec02cc6799dfe67526 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 21 Jul 2024 20:15:57 +0500 Subject: [PATCH 1634/1808] rewrote pickle sync with lock decorators, few changes to storages to return empty dict on get_data & raise runtimeerror on key not existing --- telebot/asyncio_storage/memory_storage.py | 4 +- telebot/asyncio_storage/pickle_storage.py | 2 +- telebot/asyncio_storage/redis_storage.py | 2 +- telebot/storage/memory_storage.py | 4 +- telebot/storage/pickle_storage.py | 124 ++++++++++++---------- telebot/storage/redis_storage.py | 2 +- 6 files changed, 72 insertions(+), 66 deletions(-) diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py index e65ed74d4..661cc35e9 100644 --- a/telebot/asyncio_storage/memory_storage.py +++ b/telebot/asyncio_storage/memory_storage.py @@ -71,7 +71,7 @@ async def set_data( ) if self.data.get(_key) is None: - return False + raise RuntimeError(f"MemoryStorage: key {_key} does not exist.") self.data[_key]["data"][key] = value return True @@ -85,7 +85,7 @@ async def get_data( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - return self.data.get(_key, {}).get("data", None) + return self.data.get(_key, {}).get("data", {}) async def reset_data( self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index 0c7da7eb1..9a8c9eead 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -98,7 +98,7 @@ async def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, state_data = data.get(_key, {}) state_data["data"][key] = value if _key not in data: - data[_key] = {"state": None, "data": state_data} + raise RuntimeError(f"StatePickleStorage: key {_key} does not exist.") else: data[_key]["data"][key] = value await self._write_to_file(data) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index a6b19780d..b07bd4159 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -86,7 +86,7 @@ async def set_data(self, pipe, chat_id: int, user_id: int, key: str, value: Unio data = await pipe.execute() data = data[0] if data is None: - await pipe.hset(_key, "data", json.dumps({key: value})) + raise RuntimeError(f"StateRedisStorage: key {_key} does not exist.") else: data = json.loads(data) data[key] = value diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 82142430a..11acdc119 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -71,7 +71,7 @@ def set_data( ) if self.data.get(_key) is None: - return False + raise RuntimeError(f"StateMemoryStorage: key {_key} does not exist.") self.data[_key]["data"][key] = value return True @@ -85,7 +85,7 @@ def get_data( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - return self.data.get(_key, {}).get("data", None) + return self.data.get(_key, {}).get("data", {}) def reset_data( self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index 32a9653c7..b449c17f4 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -1,12 +1,18 @@ import os import pickle import threading -from typing import Optional, Union +from typing import Optional, Union, Callable from telebot.storage.base_storage import StateStorageBase, StateDataContext +def with_lock(func: Callable) -> Callable: + def wrapper(self, *args, **kwargs): + with self.lock: + return func(self, *args, **kwargs) + return wrapper + class StatePickleStorage(StateStorageBase): - def __init__(self, file_path: str="./.state-save/states.pkl", - prefix='telebot', separator: Optional[str]=":") -> None: + def __init__(self, file_path: str = "./.state-save/states.pkl", + prefix='telebot', separator: Optional[str] = ":") -> None: self.file_path = file_path self.prefix = prefix self.separator = separator @@ -32,98 +38,98 @@ def create_dir(self): with open(self.file_path,'wb') as file: pickle.dump({}, file) + @with_lock def set_state(self, chat_id: int, user_id: int, state: str, - business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, - bot_id: Optional[int]=None) -> bool: + business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None) -> bool: _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - with self.lock: - data = self._read_from_file() - if _key not in data: - data[_key] = {"state": state, "data": {}} - else: - data[_key]["state"] = state - self._write_to_file(data) + data = self._read_from_file() + if _key not in data: + data[_key] = {"state": state, "data": {}} + else: + data[_key]["state"] = state + self._write_to_file(data) return True - def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Union[str, None]: + @with_lock + def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Union[str, None]: _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - with self.lock: - data = self._read_from_file() - return data.get(_key, {}).get("state") + data = self._read_from_file() + return data.get(_key, {}).get("state") - def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: + @with_lock + def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - with self.lock: - data = self._read_from_file() - if _key in data: - del data[_key] - self._write_to_file(data) - return True - return False + data = self._read_from_file() + if _key in data: + del data[_key] + self._write_to_file(data) + return True + return False + @with_lock def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], - business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, - bot_id: Optional[int]=None) -> bool: + business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None) -> bool: _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - with self.lock: - data = self._read_from_file() - state_data = data.get(_key, {}) - state_data["data"][key] = value - if _key not in data: - data[_key] = {"state": None, "data": state_data} - else: - data[_key]["data"][key] = value - self._write_to_file(data) + data = self._read_from_file() + state_data = data.get(_key, {}) + state_data["data"][key] = value + + if _key not in data: + raise RuntimeError(f"PickleStorage: key {_key} does not exist.") + + self._write_to_file(data) return True - def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> dict: + @with_lock + def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> dict: _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - with self.lock: - data = self._read_from_file() - return data.get(_key, {}).get("data", {}) + data = self._read_from_file() + return data.get(_key, {}).get("data", {}) - def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: + @with_lock + def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - with self.lock: - data = self._read_from_file() - if _key in data: - data[_key]["data"] = {} - self._write_to_file(data) - return True - return False + data = self._read_from_file() + if _key in data: + data[_key]["data"] = {} + self._write_to_file(data) + return True + return False - def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Optional[dict]: + def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Optional[dict]: return StateDataContext( self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id, bot_id=bot_id ) - def save(self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: + @with_lock + def save(self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: _key = self._get_key( chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id ) - with self.lock: - data = self._read_from_file() - data[_key]["data"] = data - self._write_to_file(data) + data = self._read_from_file() + data[_key]["data"] = data + self._write_to_file(data) return True def __str__(self) -> str: diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index d41da05c8..f21d50fe1 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -85,7 +85,7 @@ def set_data_action(pipe): data = pipe.hget(_key, "data") data = data.execute()[0] if data is None: - pipe.hset(_key, "data", json.dumps({key: value})) + raise RuntimeError(f"RedisStorage: key {_key} does not exist.") else: data = json.loads(data) data[key] = value From 1adca1375b6901b0963fd020f697825032b551d5 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 21 Jul 2024 22:27:08 +0500 Subject: [PATCH 1635/1808] Improved docstrings, fixed bugs, allow accessing statecontext via state name --- telebot/__init__.py | 52 ++++++++++++++++------- telebot/async_telebot.py | 43 +++++++++++++------ telebot/asyncio_storage/memory_storage.py | 17 ++++++++ telebot/asyncio_storage/pickle_storage.py | 24 +++++++++++ telebot/asyncio_storage/redis_storage.py | 34 +++++++++++++++ telebot/states/aio/context.py | 23 ++++++---- telebot/states/aio/middleware.py | 4 +- telebot/states/sync/context.py | 25 ++++++----- telebot/states/sync/middleware.py | 4 +- telebot/storage/memory_storage.py | 16 +++++++ telebot/storage/pickle_storage.py | 24 +++++++++++ telebot/storage/redis_storage.py | 33 ++++++++++++++ 12 files changed, 248 insertions(+), 51 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 08ae9fc6f..07ae33181 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6645,9 +6645,9 @@ def setup_middleware(self, middleware: BaseMiddleware): self.middlewares.append(middleware) - def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None, + def set_state(self, user_id: int, state: Union[str, State], chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, - bot_id: Optional[int]=None) -> None: + bot_id: Optional[int]=None) -> bool: """ Sets a new state of a user. @@ -6657,16 +6657,24 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Option Otherwise, if you only set user_id, chat_id will equal to user_id, this means that state will be set for the user in his private chat with a bot. + .. versionchanged:: 4.22.0 + + Added additional parameters to support topics, business connections, and message threads. + + .. seealso:: + + For more details, visit the `custom_states.py example `_. + :param user_id: User's identifier :type user_id: :obj:`int` - :param state: new state. can be string, integer, or :class:`telebot.types.State` + :param state: new state. can be string, or :class:`telebot.types.State` :type state: :obj:`int` or :obj:`str` or :class:`telebot.types.State` :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier @@ -6675,7 +6683,8 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Option :param message_thread_id: Identifier of the message thread :type message_thread_id: :obj:`int` - :return: None + :return: True on success + :rtype: :obj:`bool` """ if chat_id is None: chat_id = user_id @@ -6688,9 +6697,9 @@ def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Option def reset_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ - Reset data for a user in chat. + Reset data for a user in chat: sets the 'data' fieldi to an empty dictionary. :param user_id: User's identifier :type user_id: :obj:`int` @@ -6698,7 +6707,7 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None, :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier @@ -6707,7 +6716,8 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None, :param message_thread_id: Identifier of the message thread :type message_thread_id: :obj:`int` - :return: None + :return: True on success + :rtype: :obj:`bool` """ if chat_id is None: chat_id = user_id @@ -6718,9 +6728,13 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None, def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ - Delete the current state of a user. + Fully deletes the storage record of a user in chat. + + .. warning:: + + This does NOT set state to None, but deletes the object from storage. :param user_id: User's identifier :type user_id: :obj:`int` @@ -6728,7 +6742,8 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_conne :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :return: None + :return: True on success + :rtype: :obj:`bool` """ if chat_id is None: chat_id = user_id @@ -6749,7 +6764,7 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn :param chat_id: Chat's unique identifier, defaults to user_id :type chat_id: int, optional - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: int, optional :param business_connection_id: Business identifier @@ -6772,18 +6787,23 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn def get_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Union[int, str]: + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> str: """ Gets current state of a user. Not recommended to use this method. But it is ok for debugging. + .. warning:: + + Even if you are using :class:`telebot.types.State`, this method will return a string. + When comparing(not recommended), you should compare this string with :class:`telebot.types.State`.name + :param user_id: User's identifier :type user_id: :obj:`int` :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier @@ -6817,7 +6837,7 @@ def add_data(self, user_id: int, chat_id: Optional[int]=None, :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2d443cb5b..508aa6cb4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7846,7 +7846,7 @@ async def get_forum_topic_icon_stickers(self) -> List[types.Sticker]: async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, - bot_id: Optional[int]=None) -> None: + bot_id: Optional[int]=None) -> bool: """ Sets a new state of a user. @@ -7856,16 +7856,24 @@ async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Otherwise, if you only set user_id, chat_id will equal to user_id, this means that state will be set for the user in his private chat with a bot. + .. versionchanged:: 4.22.0 + + Added additional parameters to support topics, business connections, and message threads. + + .. seealso:: + + For more details, visit the `custom_states.py example `_. + :param user_id: User's identifier :type user_id: :obj:`int` - :param state: new state. can be string, integer, or :class:`telebot.types.State` + :param state: new state. can be string, or :class:`telebot.types.State` :type state: :obj:`int` or :obj:`str` or :class:`telebot.types.State` :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier @@ -7874,7 +7882,8 @@ async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: :param message_thread_id: Identifier of the message thread :type message_thread_id: :obj:`int` - :return: None + :return: True on success + :rtype: :obj:`bool` """ if chat_id is None: chat_id = user_id @@ -7887,7 +7896,7 @@ async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: async def reset_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ Reset data for a user in chat. @@ -7897,7 +7906,7 @@ async def reset_data(self, user_id: int, chat_id: Optional[int]=None, :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier @@ -7906,7 +7915,8 @@ async def reset_data(self, user_id: int, chat_id: Optional[int]=None, :param message_thread_id: Identifier of the message thread :type message_thread_id: :obj:`int` - :return: None + :return: True on success + :rtype: :obj:`bool` """ if chat_id is None: chat_id = user_id @@ -7917,7 +7927,7 @@ async def reset_data(self, user_id: int, chat_id: Optional[int]=None, async def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> None: + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ Delete the current state of a user. @@ -7948,7 +7958,7 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn :param chat_id: Chat's unique identifier, defaults to user_id :type chat_id: int, optional - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: int, optional :param business_connection_id: Business identifier @@ -7958,7 +7968,7 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn :type message_thread_id: int, optional :return: Context manager with data for a user in chat - :rtype: Optional[Any] + :rtype: :obj:`dict` """ if chat_id is None: chat_id = user_id @@ -7971,18 +7981,23 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn async def get_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> Union[int, str]: + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> str: """ Gets current state of a user. Not recommended to use this method. But it is ok for debugging. + .. warning:: + + Even if you are using :class:`telebot.types.State`, this method will return a string. + When comparing(not recommended), you should compare this string with :class:`telebot.types.State`.name + :param user_id: User's identifier :type user_id: :obj:`int` :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier @@ -7992,7 +8007,7 @@ async def get_state(self, user_id: int, chat_id: Optional[int]=None, :type message_thread_id: :obj:`int` :return: state of a user - :rtype: :obj:`int` or :obj:`str` or :class:`telebot.types.State` + :rtype: :obj:`str` """ if chat_id is None: chat_id = user_id @@ -8016,7 +8031,7 @@ async def add_data(self, user_id: int, chat_id: Optional[int]=None, :param chat_id: Chat's identifier :type chat_id: :obj:`int` - :param bot_id: Bot's identifier + :param bot_id: Bot's identifier, defaults to current bot id :type bot_id: :obj:`int` :param business_connection_id: Business identifier diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py index 661cc35e9..87449cace 100644 --- a/telebot/asyncio_storage/memory_storage.py +++ b/telebot/asyncio_storage/memory_storage.py @@ -2,6 +2,23 @@ from typing import Optional, Union class StateMemoryStorage(StateStorageBase): + """ + Memory storage for states. + + Stores states in memory as a dictionary. + + .. code-block:: python3 + + storage = StateMemoryStorage() + bot = AsyncTeleBot(token, storage=storage) + + :param separator: Separator for keys, default is ":". + :type separator: Optional[str] + + :param prefix: Prefix for keys, default is "telebot". + :type prefix: Optional[str] + """ + def __init__(self, separator: Optional[str]=":", prefix: Optional[str]="telebot" diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index 9a8c9eead..0506d4703 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -1,4 +1,5 @@ +aiofiles_installed = True try: import aiofiles except ImportError: @@ -18,6 +19,29 @@ async def wrapper(self, *args, **kwargs): return wrapper class StatePickleStorage(StateStorageBase): + """ + State storage based on pickle file. + + .. warning:: + + This storage is not recommended for production use. + Data may be corrupted. If you face a case where states do not work as expected, + try to use another storage. + + .. code-block:: python3 + + storage = StatePickleStorage() + bot = AsyncTeleBot(token, storage=storage) + + :param file_path: Path to file where states will be stored. + :type file_path: str + + :param prefix: Prefix for keys, default is "telebot". + :type prefix: Optional[str] + + :param separator: Separator for keys, default is ":". + :type separator: Optional[str] + """ def __init__(self, file_path: str = "./.state-save/states.pkl", prefix='telebot', separator: Optional[str] = ":") -> None: diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index b07bd4159..dea214daa 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,4 +1,5 @@ +redis_installed = True try: import redis from redis.asyncio import Redis, ConnectionPool @@ -28,6 +29,39 @@ async def wrapper(self, *args, **kwargs): return wrapper class StateRedisStorage(StateStorageBase): + """ + State storage based on Redis. + + .. code-block:: python3 + + storage = StateRedisStorage(...) + bot = AsyncTeleBot(token, storage=storage) + + :param host: Redis host, default is "localhost". + :type host: str + + :param port: Redis port, default is 6379. + :type port: int + + :param db: Redis database, default is 0. + :type db: int + + :param password: Redis password, default is None. + :type password: Optional[str] + + :param prefix: Prefix for keys, default is "telebot". + :type prefix: Optional[str] + + :param redis_url: Redis URL, default is None. + :type redis_url: Optional[str] + + :param connection_pool: Redis connection pool, default is None. + :type connection_pool: Optional[ConnectionPool] + + :param separator: Separator for keys, default is ":". + :type separator: Optional[str] + + """ def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot', redis_url=None, diff --git a/telebot/states/aio/context.py b/telebot/states/aio/context.py index 431eba93c..956ca6837 100644 --- a/telebot/states/aio/context.py +++ b/telebot/states/aio/context.py @@ -27,7 +27,7 @@ def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None: self.bot: AsyncTeleBot = bot self.bot_id = self.bot.bot_id - async def set(self, state: Union[State, str]) -> None: + async def set(self, state: Union[State, str]) -> bool: """ Set state for current user. @@ -71,14 +71,16 @@ async def get(self) -> str: message_thread_id=message_thread_id ) - async def reset_data(self) -> None: - """ - Reset data for current user. - State will not be changed. + async def delete(self) -> bool: """ + Deletes state and data for current user. + .. warning:: + + This method deletes state and associated data for current user. + """ chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) - return await self.bot.reset_data( + return await self.bot.delete_state( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, @@ -86,12 +88,14 @@ async def reset_data(self) -> None: message_thread_id=message_thread_id ) - async def delete(self) -> None: + async def reset_data(self) -> bool: """ - Deletes state and data for current user. + Reset data for current user. + State will not be changed. """ + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) - return await self.bot.delete_state( + return await self.bot.reset_data( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, @@ -107,6 +111,7 @@ def data(self) -> dict: with state_context.data() as data: print(data) + data['name'] = 'John' """ chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) diff --git a/telebot/states/aio/middleware.py b/telebot/states/aio/middleware.py index 66283dc82..546b29067 100644 --- a/telebot/states/aio/middleware.py +++ b/telebot/states/aio/middleware.py @@ -13,7 +13,9 @@ def __init__(self, bot: AsyncTeleBot) -> None: self.bot: AsyncTeleBot = bot async def pre_process(self, message, data): - data['state_context'] = StateContext(message, self.bot) + state_context = StateContext(message, self.bot) + data['state_context'] = state_context + data['state'] = state_context # 2 ways to access state context async def post_process(self, message, data, exception): pass diff --git a/telebot/states/sync/context.py b/telebot/states/sync/context.py index c8009007a..c0611f6bb 100644 --- a/telebot/states/sync/context.py +++ b/telebot/states/sync/context.py @@ -27,7 +27,7 @@ def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None: self.bot: TeleBot = bot self.bot_id = self.bot.bot_id - def set(self, state: Union[State, str]) -> None: + def set(self, state: Union[State, str]) -> bool: """ Set state for current user. @@ -71,27 +71,31 @@ def get(self) -> str: message_thread_id=message_thread_id ) - def reset_data(self) -> None: - """ - Reset data for current user. - State will not be changed. + def delete(self) -> bool: """ + Deletes state and data for current user. + .. warning:: + + This method deletes state and associated data for current user. + """ chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) - return self.bot.reset_data( + return self.bot.delete_state( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, bot_id=bot_id, message_thread_id=message_thread_id ) - - def delete(self) -> None: + + def reset_data(self) -> bool: """ - Deletes state and data for current user. + Reset data for current user. + State will not be changed. """ + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) - return self.bot.delete_state( + return self.bot.reset_data( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, @@ -107,6 +111,7 @@ def data(self) -> dict: with state_context.data() as data: print(data) + data['name'] = 'John' """ chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) diff --git a/telebot/states/sync/middleware.py b/telebot/states/sync/middleware.py index b85f795da..4a252158b 100644 --- a/telebot/states/sync/middleware.py +++ b/telebot/states/sync/middleware.py @@ -13,7 +13,9 @@ def __init__(self, bot: TeleBot) -> None: self.bot: TeleBot = bot def pre_process(self, message, data): - data['state_context'] = StateContext(message, self.bot) + state_context = StateContext(message, self.bot) + data['state_context'] = state_context + data['state'] = state_context # 2 ways to access state context def post_process(self, message, data, exception): pass diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index 11acdc119..dc88bcebc 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -2,6 +2,22 @@ from typing import Optional, Union class StateMemoryStorage(StateStorageBase): + """ + Memory storage for states. + + Stores states in memory as a dictionary. + + .. code-block:: python3 + + storage = StateMemoryStorage() + bot = TeleBot(token, storage=storage) + + :param separator: Separator for keys, default is ":". + :type separator: Optional[str] + + :param prefix: Prefix for keys, default is "telebot". + :type prefix: Optional[str] + """ def __init__(self, separator: Optional[str]=":", prefix: Optional[str]="telebot" diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index b449c17f4..fc8dc3289 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -11,6 +11,30 @@ def wrapper(self, *args, **kwargs): return wrapper class StatePickleStorage(StateStorageBase): + """ + State storage based on pickle file. + + .. warning:: + + This storage is not recommended for production use. + Data may be corrupted. If you face a case where states do not work as expected, + try to use another storage. + + .. code-block:: python3 + + storage = StatePickleStorage() + bot = TeleBot(token, storage=storage) + + :param file_path: Path to file where states will be stored. + :type file_path: str + + :param prefix: Prefix for keys, default is "telebot". + :type prefix: Optional[str] + + :param separator: Separator for keys, default is ":". + :type separator: Optional[str] + """ + def __init__(self, file_path: str = "./.state-save/states.pkl", prefix='telebot', separator: Optional[str] = ":") -> None: self.file_path = file_path diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index f21d50fe1..146ec2f63 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -9,6 +9,39 @@ redis_installed = False class StateRedisStorage(StateStorageBase): + """ + State storage based on Redis. + + .. code-block:: python3 + + storage = StateRedisStorage(...) + bot = TeleBot(token, storage=storage) + + :param host: Redis host, default is "localhost". + :type host: str + + :param port: Redis port, default is 6379. + :type port: int + + :param db: Redis database, default is 0. + :type db: int + + :param password: Redis password, default is None. + :type password: Optional[str] + + :param prefix: Prefix for keys, default is "telebot". + :type prefix: Optional[str] + + :param redis_url: Redis URL, default is None. + :type redis_url: Optional[str] + + :param connection_pool: Redis connection pool, default is None. + :type connection_pool: Optional[ConnectionPool] + + :param separator: Separator for keys, default is ":". + :type separator: Optional[str] + + """ def __init__(self, host='localhost', port=6379, db=0, password=None, prefix='telebot', redis_url=None, From adae7a9ba4b137a3ed46db48a8a52cf0b089b692 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 21 Jul 2024 22:33:08 +0500 Subject: [PATCH 1636/1808] Removed aioredis from optional dependencies, breaking change made for states="*" --- pyproject.toml | 1 - telebot/asyncio_filters.py | 6 +++++- telebot/custom_filters.py | 6 +++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0095c3f21..d5289361b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,6 @@ Issues = "https://github.com/eternnoir/pyTelegramBotAPI/issues" json = ["ujson"] PIL = ["Pillow"] redis = ["redis>=3.4.1"] -aioredis = ["aioredis"] aiohttp = ["aiohttp"] fastapi = ["fastapi"] uvicorn = ["uvicorn"] diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 389ce1643..f8212f934 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -397,7 +397,6 @@ async def check(self, message, text): """ :meta private: """ - if text == '*': return True chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot._user.id) @@ -421,10 +420,15 @@ async def check(self, message, text): message_thread_id=message_thread_id ) + # CHANGED BEHAVIOUR + if text == "*" and user_state is not None: + return True + if user_state == text: return True elif type(text) is list and user_state in text: return True + return False class IsDigitFilter(SimpleCustomFilter): diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index e53c669a1..2e91e55ea 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -403,7 +403,6 @@ def check(self, message, text): """ :meta private: """ - if text == '*': return True chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot._user.id) @@ -427,10 +426,15 @@ def check(self, message, text): message_thread_id=message_thread_id ) + # CHANGED BEHAVIOUR + if text == "*" and user_state is not None: + return True + if user_state == text: return True elif type(text) is list and user_state in text: return True + return False class IsDigitFilter(SimpleCustomFilter): """ From d33db8532f1505d240dc53c79c9eeb5995a7fb95 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 21 Jul 2024 22:55:57 +0500 Subject: [PATCH 1637/1808] Updated examples --- .../asynchronous_telebot/custom_states.py | 169 +++++++++------- examples/custom_states.py | 186 +++++++++--------- 2 files changed, 191 insertions(+), 164 deletions(-) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index aefad9809..eefc6ebbb 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -1,91 +1,112 @@ -from telebot import asyncio_filters -from telebot.async_telebot import AsyncTeleBot - -# list of storages, you can use any storage +from telebot import async_telebot +from telebot import asyncio_filters, types +from telebot.states import State, StatesGroup +from telebot.states.aio.context import StateContext from telebot.asyncio_storage import StateMemoryStorage -# new feature for states. -from telebot.asyncio_handler_backends import State, StatesGroup - -# default state storage is statememorystorage -bot = AsyncTeleBot('TOKEN', state_storage=StateMemoryStorage()) - +# Initialize the bot +state_storage = StateMemoryStorage() # don't use this in production; switch to redis +bot = async_telebot.AsyncTeleBot("TOKEN", state_storage=state_storage) -# Just create different statesgroup +# Define states class MyStates(StatesGroup): - name = State() # statesgroup should contain states - surname = State() + name = State() age = State() + color = State() + hobby = State() - - -# set_state -> sets a new state -# delete_state -> delets state if exists -# get_state -> returns state if exists - - +# Start command handler @bot.message_handler(commands=['start']) -async def start_ex(message): - """ - Start command. Here we are starting state - """ - await bot.set_state(message.from_user.id, MyStates.name, message.chat.id) - await bot.send_message(message.chat.id, 'Hi, write me a name') - - - -@bot.message_handler(state="*", commands='cancel') -async def any_state(message): - """ - Cancel state - """ - await bot.send_message(message.chat.id, "Your state was cancelled.") - await bot.delete_state(message.from_user.id, message.chat.id) +async def start_ex(message: types.Message, state: StateContext): + await state.set(MyStates.name) + await bot.send_message(message.chat.id, 'Hello! What is your first name?', reply_to_message_id=message.message_id) + +# Cancel command handler +@bot.message_handler(state="*", commands=['cancel']) +async def any_state(message: types.Message, state: StateContext): + await state.delete() + await bot.send_message(message.chat.id, 'Your information has been cleared. Type /start to begin again.', reply_to_message_id=message.message_id) +# Handler for name input @bot.message_handler(state=MyStates.name) -async def name_get(message): - """ - State 1. Will process when user's state is MyStates.name. - """ - await bot.send_message(message.chat.id, f'Now write me a surname') - await bot.set_state(message.from_user.id, MyStates.surname, message.chat.id) - async with bot.retrieve_data(message.from_user.id, message.chat.id) as data: - data['name'] = message.text - - -@bot.message_handler(state=MyStates.surname) -async def ask_age(message): - """ - State 2. Will process when user's state is MyStates.surname. - """ - await bot.send_message(message.chat.id, "What is your age?") - await bot.set_state(message.from_user.id, MyStates.age, message.chat.id) - async with bot.retrieve_data(message.from_user.id, message.chat.id) as data: - data['surname'] = message.text - -# result +async def name_get(message: types.Message, state: StateContext): + await state.set(MyStates.age) + await bot.send_message(message.chat.id, "How old are you?", reply_to_message_id=message.message_id) + await state.add_data(name=message.text) + +# Handler for age input @bot.message_handler(state=MyStates.age, is_digit=True) -async def ready_for_answer(message): - """ - State 3. Will process when user's state is MyStates.age. - """ - async with bot.retrieve_data(message.from_user.id, message.chat.id) as data: - await bot.send_message(message.chat.id, "Ready, take a look:\nName: {name}\nSurname: {surname}\nAge: {age}".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html") - await bot.delete_state(message.from_user.id, message.chat.id) - -#incorrect number +async def ask_color(message: types.Message, state: StateContext): + await state.set(MyStates.color) + await state.add_data(age=message.text) + + # Define reply keyboard for color selection + keyboard = types.ReplyKeyboardMarkup(row_width=2) + colors = ["Red", "Green", "Blue", "Yellow", "Purple", "Orange", "Other"] + buttons = [types.KeyboardButton(color) for color in colors] + keyboard.add(*buttons) + + await bot.send_message(message.chat.id, "What is your favorite color? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + +# Handler for color input +@bot.message_handler(state=MyStates.color) +async def ask_hobby(message: types.Message, state: StateContext): + await state.set(MyStates.hobby) + await state.add_data(color=message.text) + + # Define reply keyboard for hobby selection + keyboard = types.ReplyKeyboardMarkup(row_width=2) + hobbies = ["Reading", "Traveling", "Gaming", "Cooking"] + buttons = [types.KeyboardButton(hobby) for hobby in hobbies] + keyboard.add(*buttons) + + await bot.send_message(message.chat.id, "What is one of your hobbies? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + +# Handler for hobby input; use filters to ease validation +@bot.message_handler(state=MyStates.hobby, text=['Reading', 'Traveling', 'Gaming', 'Cooking']) +async def finish(message: types.Message, state: StateContext): + async with state.data() as data: + name = data.get('name') + age = data.get('age') + color = data.get('color') + hobby = message.text # Get the hobby from the message text + + # Provide a fun fact based on color + color_facts = { + "Red": "Red is often associated with excitement and passion.", + "Green": "Green is the color of nature and tranquility.", + "Blue": "Blue is known for its calming and serene effects.", + "Yellow": "Yellow is a cheerful color often associated with happiness.", + "Purple": "Purple signifies royalty and luxury.", + "Orange": "Orange is a vibrant color that stimulates enthusiasm.", + "Other": "Colors have various meanings depending on context." + } + color_fact = color_facts.get(color, "Colors have diverse meanings, and yours is unique!") + + msg = (f"Thank you for sharing! Here is a summary of your information:\n" + f"First Name: {name}\n" + f"Age: {age}\n" + f"Favorite Color: {color}\n" + f"Fun Fact about your color: {color_fact}\n" + f"Favorite Hobby: {hobby}") + + await bot.send_message(message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id) + await state.delete() + +# Handler for incorrect age input @bot.message_handler(state=MyStates.age, is_digit=False) -async def age_incorrect(message): - """ - Will process for wrong input when state is MyState.age - """ - await bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') - -# register filters +async def age_incorrect(message: types.Message): + await bot.send_message(message.chat.id, 'Please enter a valid number for age.', reply_to_message_id=message.message_id) +# Add custom filters bot.add_custom_filter(asyncio_filters.StateFilter(bot)) bot.add_custom_filter(asyncio_filters.IsDigitFilter()) +bot.add_custom_filter(asyncio_filters.TextMatchFilter()) +# necessary for state parameter in handlers. +from telebot.states.aio.middleware import StateMiddleware +bot.setup_middleware(StateMiddleware(bot)) +# Start polling import asyncio -asyncio.run(bot.polling()) \ No newline at end of file +asyncio.run(bot.polling()) diff --git a/examples/custom_states.py b/examples/custom_states.py index a02761286..a488664b2 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -1,106 +1,112 @@ -import telebot # telebot - -from telebot import custom_filters -from telebot.handler_backends import State, StatesGroup #States - -# States storage +import telebot +from telebot import custom_filters, types +from telebot.states import State, StatesGroup +from telebot.states.sync.context import StateContext from telebot.storage import StateMemoryStorage +# Initialize the bot +state_storage = StateMemoryStorage() # don't use this in production; switch to redis +bot = telebot.TeleBot("TOKEN", state_storage=state_storage, + use_class_middlewares=True) -# Starting from version 4.4.0+, we support storages. -# StateRedisStorage -> Redis-based storage. -# StatePickleStorage -> Pickle-based storage. -# For redis, you will need to install redis. -# Pass host, db, password, or anything else, -# if you need to change config for redis. -# Pickle requires path. Default path is in folder .state-saves. -# If you were using older version of pytba for pickle, -# you need to migrate from old pickle to new by using -# StatePickleStorage().convert_old_to_new() - - - -# Now, you can pass storage to bot. -state_storage = StateMemoryStorage() # you can init here another storage - -bot = telebot.TeleBot("TOKEN", -state_storage=state_storage) - - -# States group. +# Define states class MyStates(StatesGroup): - # Just name variables differently - name = State() # creating instances of State class is enough from now - surname = State() + name = State() age = State() + color = State() + hobby = State() - - - +# Start command handler @bot.message_handler(commands=['start']) -def start_ex(message): - """ - Start command. Here we are starting state - """ - bot.set_state(message.from_user.id, MyStates.name, message.chat.id) - bot.send_message(message.chat.id, 'Hi, write me a name') - - -# Any state +def start_ex(message: types.Message, state: StateContext): + state.set(MyStates.name) + bot.send_message(message.chat.id, 'Hello! What is your first name?', reply_to_message_id=message.message_id) + +# Cancel command handler @bot.message_handler(state="*", commands=['cancel']) -def any_state(message): - """ - Cancel state - """ - bot.send_message(message.chat.id, "Your state was cancelled.") - bot.delete_state(message.from_user.id, message.chat.id) +def any_state(message: types.Message, state: StateContext): + state.delete() + bot.send_message(message.chat.id, 'Your information has been cleared. Type /start to begin again.', reply_to_message_id=message.message_id) +# Handler for name input @bot.message_handler(state=MyStates.name) -def name_get(message): - """ - State 1. Will process when user's state is MyStates.name. - """ - bot.send_message(message.chat.id, 'Now write me a surname') - bot.set_state(message.from_user.id, MyStates.surname, message.chat.id) - with bot.retrieve_data(message.from_user.id, message.chat.id) as data: - data['name'] = message.text - - -@bot.message_handler(state=MyStates.surname) -def ask_age(message): - """ - State 2. Will process when user's state is MyStates.surname. - """ - bot.send_message(message.chat.id, "What is your age?") - bot.set_state(message.from_user.id, MyStates.age, message.chat.id) - with bot.retrieve_data(message.from_user.id, message.chat.id) as data: - data['surname'] = message.text - -# result +def name_get(message: types.Message, state: StateContext): + state.set(MyStates.age) + bot.send_message(message.chat.id, "How old are you?", reply_to_message_id=message.message_id) + state.add_data(name=message.text) + +# Handler for age input @bot.message_handler(state=MyStates.age, is_digit=True) -def ready_for_answer(message): - """ - State 3. Will process when user's state is MyStates.age. - """ - with bot.retrieve_data(message.from_user.id, message.chat.id) as data: - msg = ("Ready, take a look:\n" - f"Name: {data['name']}\n" - f"Surname: {data['surname']}\n" - f"Age: {message.text}") - bot.send_message(message.chat.id, msg, parse_mode="html") - bot.delete_state(message.from_user.id, message.chat.id) - -#incorrect number +def ask_color(message: types.Message, state: StateContext): + state.set(MyStates.color) + state.add_data(age=message.text) + + # Define reply keyboard for color selection + keyboard = types.ReplyKeyboardMarkup(row_width=2) + colors = ["Red", "Green", "Blue", "Yellow", "Purple", "Orange", "Other"] + buttons = [types.KeyboardButton(color) for color in colors] + keyboard.add(*buttons) + + bot.send_message(message.chat.id, "What is your favorite color? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + +# Handler for color input +@bot.message_handler(state=MyStates.color) +def ask_hobby(message: types.Message, state: StateContext): + state.set(MyStates.hobby) + state.add_data(color=message.text) + + # Define reply keyboard for hobby selection + keyboard = types.ReplyKeyboardMarkup(row_width=2) + hobbies = ["Reading", "Traveling", "Gaming", "Cooking"] + buttons = [types.KeyboardButton(hobby) for hobby in hobbies] + keyboard.add(*buttons) + + bot.send_message(message.chat.id, "What is one of your hobbies? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + +# Handler for hobby input +@bot.message_handler(state=MyStates.hobby, text=['Reading', 'Traveling', 'Gaming', 'Cooking']) +def finish(message: types.Message, state: StateContext): + with state.data() as data: + name = data.get('name') + age = data.get('age') + color = data.get('color') + hobby = message.text # Get the hobby from the message text + + # Provide a fun fact based on color + color_facts = { + "Red": "Red is often associated with excitement and passion.", + "Green": "Green is the color of nature and tranquility.", + "Blue": "Blue is known for its calming and serene effects.", + "Yellow": "Yellow is a cheerful color often associated with happiness.", + "Purple": "Purple signifies royalty and luxury.", + "Orange": "Orange is a vibrant color that stimulates enthusiasm.", + "Other": "Colors have various meanings depending on context." + } + color_fact = color_facts.get(color, "Colors have diverse meanings, and yours is unique!") + + msg = (f"Thank you for sharing! Here is a summary of your information:\n" + f"First Name: {name}\n" + f"Age: {age}\n" + f"Favorite Color: {color}\n" + f"Fun Fact about your color: {color_fact}\n" + f"Favorite Hobby: {hobby}") + + bot.send_message(message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id) + state.delete() + +# Handler for incorrect age input @bot.message_handler(state=MyStates.age, is_digit=False) -def age_incorrect(message): - """ - Wrong response for MyStates.age - """ - bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number') - -# register filters +def age_incorrect(message: types.Message): + bot.send_message(message.chat.id, 'Please enter a valid number for age.', reply_to_message_id=message.message_id) +# Add custom filters bot.add_custom_filter(custom_filters.StateFilter(bot)) bot.add_custom_filter(custom_filters.IsDigitFilter()) +bot.add_custom_filter(custom_filters.TextMatchFilter()) + +# necessary for state parameter in handlers. +from telebot.states.sync.middleware import StateMiddleware +bot.setup_middleware(StateMiddleware(bot)) -bot.infinity_polling(skip_pending=True) +# Start polling +bot.infinity_polling() From 536ffa21f419ce91323ebe0723dc8bc26f70d145 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 25 Jul 2024 18:09:19 +0500 Subject: [PATCH 1638/1808] Added migrate_format to migrate from old format of states to new. Only redis as it should be used for production. --- telebot/asyncio_storage/redis_storage.py | 41 ++++++++++++++++++++++++ telebot/storage/redis_storage.py | 41 ++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index dea214daa..5c0c1bb0a 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -159,6 +159,47 @@ async def save(self, pipe, chat_id: int, user_id: int, data: dict, business_conn else: return False return True + + def migrate_format(self, bot_id: int, + prefix: Optional[str]="telebot_"): + """ + Migrate from old to new format of keys. + Run this function once to migrate all redis existing keys to new format. + + Starting from version 4.22.0, the format of keys has been changed: + :value + - Old format: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} + - New format: + {prefix}{separator}{bot_id}{separator}{business_connection_id}{separator}{message_thread_id}{separator}{chat_id}{separator}{user_id}: {'state': ..., 'data': {}} + + This function will help you to migrate from the old format to the new one in order to avoid data loss. + + :param bot_id: Bot ID; To get it, call a getMe request and grab the id from the response. + :type bot_id: int + + :param prefix: Prefix for keys, default is "telebot_"(old default value) + :type prefix: Optional[str] + """ + keys = self.redis.keys(f"{prefix}*") + + for key in keys: + old_key = key.decode('utf-8') + # old: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} + value = self.redis.get(old_key) + value = json.loads(value) + + chat_id = old_key[len(prefix):] + user_id = list(value.keys())[0] + state = value[user_id]['state'] + state_data = value[user_id]['data'] + + # set new format + new_key = self._get_key(int(chat_id), int(user_id), self.prefix, self.separator, bot_id=bot_id) + self.redis.hset(new_key, "state", state) + self.redis.hset(new_key, "data", json.dumps(state_data)) + + # delete old key + self.redis.delete(old_key) def __str__(self) -> str: # include some connection info diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 146ec2f63..3a81ac033 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -182,6 +182,47 @@ def save_action(pipe): self.redis.transaction(save_action, _key) return True + + def migrate_format(self, bot_id: int, + prefix: Optional[str]="telebot_"): + """ + Migrate from old to new format of keys. + Run this function once to migrate all redis existing keys to new format. + + Starting from version 4.22.0, the format of keys has been changed: + :value + - Old format: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} + - New format: + {prefix}{separator}{bot_id}{separator}{business_connection_id}{separator}{message_thread_id}{separator}{chat_id}{separator}{user_id}: {'state': ..., 'data': {}} + + This function will help you to migrate from the old format to the new one in order to avoid data loss. + + :param bot_id: Bot ID; To get it, call a getMe request and grab the id from the response. + :type bot_id: int + + :param prefix: Prefix for keys, default is "telebot_"(old default value) + :type prefix: Optional[str] + """ + keys = self.redis.keys(f"{prefix}*") + + for key in keys: + old_key = key.decode('utf-8') + # old: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} + value = self.redis.get(old_key) + value = json.loads(value) + + chat_id = old_key[len(prefix):] + user_id = list(value.keys())[0] + state = value[user_id]['state'] + state_data = value[user_id]['data'] + + # set new format + new_key = self._get_key(int(chat_id), int(user_id), self.prefix, self.separator, bot_id=bot_id) + self.redis.hset(new_key, "state", state) + self.redis.hset(new_key, "data", json.dumps(state_data)) + + # delete old key + self.redis.delete(old_key) def __str__(self) -> str: return f"StateRedisStorage({self.redis})" From d2485bf42827795a20f40cd3c3ff96e52df86f74 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 26 Jul 2024 11:57:52 +0500 Subject: [PATCH 1639/1808] Another approach to bot id --- telebot/__init__.py | 22 +++++++++++++--------- telebot/async_telebot.py | 20 +++++++++++--------- telebot/util.py | 18 +++++++++++++++++- tests/test_handler_backends.py | 2 +- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 07ae33181..d0ab64d5a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -152,9 +152,14 @@ class TeleBot: :param allow_sending_without_reply: Default value for allow_sending_without_reply, defaults to None :type allow_sending_without_reply: :obj:`bool`, optional - :param colorful_logs: Outputs colorful logs :type colorful_logs: :obj:`bool`, optional + + :param validate_token: Validate token, defaults to True; + :type validate_token: :obj:`bool`, optional + + :raises ImportError: If coloredlogs module is not installed and colorful_logs is True + :raises ValueError: If token is invalid """ def __init__( @@ -169,7 +174,7 @@ def __init__( protect_content: Optional[bool]=None, allow_sending_without_reply: Optional[bool]=None, colorful_logs: Optional[bool]=False, - token_check: Optional[bool]=True + validate_token: Optional[bool]=True ): # update-related @@ -186,11 +191,12 @@ def __init__( self.allow_sending_without_reply = allow_sending_without_reply self.webhook_listener = None self._user = None + self.bot_id: int = None - # token check - if token_check: - self._user = self.get_me() - self.bot_id = self._user.id + if validate_token: + util.validate_token(self.token) + + self.bot_id = util.extract_bot_id(self.token) # subject to change in future, unspecified # logs-related if colorful_logs: @@ -286,9 +292,7 @@ def __init__( self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(self, num_threads=num_threads) - - - + @property def user(self) -> types.User: """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 508aa6cb4..d60a4a090 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -117,8 +117,11 @@ class AsyncTeleBot: :param colorful_logs: Outputs colorful logs :type colorful_logs: :obj:`bool`, optional - :param token_check: Check token on start - :type token_check: :obj:`bool`, optional, defaults to True + :param validate_token: Validate token, defaults to True; + :type validate_token: :obj:`bool`, optional + + :raises ImportError: If coloredlogs module is not installed and colorful_logs is True + :raises ValueError: If token is invalid """ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[int]=None, @@ -129,7 +132,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ protect_content: Optional[bool]=None, allow_sending_without_reply: Optional[bool]=None, colorful_logs: Optional[bool]=False, - token_check: Optional[bool]=True) -> None: + validate_token: Optional[bool]=True) -> None: # update-related self.token = token @@ -186,15 +189,14 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.middlewares = [] self._user = None # set during polling - self.bot_id = None + self.bot_id: int = None - if token_check: - result = apihelper.get_me(token) - self._user = types.User.de_json(result) - self.bot_id = self._user.id + if validate_token: + util.validate_token(self.token) + + self.bot_id: int = util.extract_bot_id(self.token) # subject to change, unspecified - @property def user(self): return self._user diff --git a/telebot/util.py b/telebot/util.py index 295c0d1aa..c8ef526c5 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -686,6 +686,22 @@ def validate_web_app_data(token: str, raw_init_data: str): return hmac.new(secret_key.digest(), data_check_string.encode(), sha256).hexdigest() == init_data_hash +def validate_token(token) -> bool: + if any(char.isspace() for char in token): + raise ValueError('Token must not contain spaces') + + if ':' not in token: + raise ValueError('Token must contain a colon') + + if len(token.split(':')) != 2: + raise ValueError('Token must contain exactly 2 parts separated by a colon') + + return True + +def extract_bot_id(token) -> str: + return int(token.split(':')[0]) + + __all__ = ( "content_type_media", "content_type_service", "update_types", "WorkerThread", "AsyncTask", "CustomRequestResponse", @@ -696,5 +712,5 @@ def validate_web_app_data(token: str, raw_init_data: str): "split_string", "smart_split", "escape", "user_link", "quick_markup", "antiflood", "parse_web_app_data", "validate_web_app_data", "or_set", "or_clear", "orify", "OrEvent", "per_thread", - "webhook_google_functions" + "webhook_google_functions", "validate_token", "extract_bot_id" ) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index f57200c1c..ad6ccaaad 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -19,7 +19,7 @@ @pytest.fixture() def telegram_bot(): - return telebot.TeleBot('', threaded=False, token_check=False) + return telebot.TeleBot('', threaded=False, validate_token=False) @pytest.fixture From dd0dfa98aa9a977827f62b078232941f981e372a Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 26 Jul 2024 11:59:26 +0500 Subject: [PATCH 1640/1808] fix tests --- tests/test_handler_backends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index ad6ccaaad..b74d6fad0 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -19,7 +19,7 @@ @pytest.fixture() def telegram_bot(): - return telebot.TeleBot('', threaded=False, validate_token=False) + return telebot.TeleBot('1234:test', threaded=False) @pytest.fixture From 7f99176c034cd5e356f009dd2664a03cf67edf25 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 26 Jul 2024 13:00:39 +0500 Subject: [PATCH 1641/1808] fixed redis data bug on set_state --- telebot/asyncio_storage/redis_storage.py | 7 +++++++ telebot/storage/redis_storage.py | 12 ++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 5c0c1bb0a..69b18d5a1 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -95,7 +95,14 @@ async def set_state(self, pipe, chat_id: int, user_id: int, state: str, state = state.name _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + pipe.hget(_key, "data") + result = await pipe.execute() + data = result[0] + if data is None: + pipe.hset(_key, "data", json.dumps({})) + await pipe.hset(_key, "state", state) + return True async def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 3a81ac033..1d1413fa2 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -79,9 +79,17 @@ def set_state( def set_state_action(pipe): pipe.multi() - #pipe.hset(_key, mapping={"state": state, "data": "{}"}) + + data = pipe.hget(_key, "data") + result = pipe.execute() + data = result[0] + if data is None: + # If data is None, set it to an empty dictionary + data = {} + pipe.hset(_key, "data", json.dumps(data)) + pipe.hset(_key, "state", state) - + self.redis.transaction(set_state_action, _key) return True From 5c0ca1864b8921d2f6bdbcf71d9fbb3d5973dbb9 Mon Sep 17 00:00:00 2001 From: nypava Date: Sat, 27 Jul 2024 12:58:44 +0300 Subject: [PATCH 1642/1808] Add webapp example --- examples/webapp/bot.py | 19 +++++++++++ examples/webapp/webapp.html | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 examples/webapp/bot.py create mode 100644 examples/webapp/webapp.html diff --git a/examples/webapp/bot.py b/examples/webapp/bot.py new file mode 100644 index 000000000..310e25c5c --- /dev/null +++ b/examples/webapp/bot.py @@ -0,0 +1,19 @@ +from telebot import TeleBot +from telebot.types import ReplyKeyboardMarkup, KeyboardButton, WebAppInfo + +BOT_TOKEN = '' +WEB_URL = 'https://pytelegrambotminiapp.vercel.app/' # https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/webapp/webapp.html + +bot = TeleBot(BOT_TOKEN) + +@bot.message_handler(commands=["start"]) +def start(message): + reply_markup = ReplyKeyboardMarkup(resize_keyboard=True) + reply_markup.row(KeyboardButton("Start MiniApp", web_app=WebAppInfo(WEB_URL))) + bot.reply_to(message, "Click the button to start MiniApp", reply_markup=reply_markup) + +@bot.message_handler(content_types=['web_app_data']) +def web_app(message): + bot.reply_to(message, f'Your message is "{message.web_app_data.data}"') + +bot.infinity_polling() diff --git a/examples/webapp/webapp.html b/examples/webapp/webapp.html new file mode 100644 index 000000000..e2e953825 --- /dev/null +++ b/examples/webapp/webapp.html @@ -0,0 +1,63 @@ + + + + + + MiniApp example + + + + +
+ + +
+ + + + + + + From 3fec98c070f91b098766bef4968b283e26ce1556 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 27 Jul 2024 15:07:45 +0500 Subject: [PATCH 1643/1808] Code cleanup --- telebot/asyncio_storage/__init__.py | 12 +- telebot/asyncio_storage/base_storage.py | 66 ++++--- telebot/asyncio_storage/memory_storage.py | 183 +++++++++++++----- telebot/asyncio_storage/pickle_storage.py | 174 +++++++++++++---- telebot/asyncio_storage/redis_storage.py | 226 +++++++++++++++++----- telebot/states/__init__.py | 96 ++++++--- telebot/states/aio/__init__.py | 6 +- telebot/states/aio/context.py | 56 +++--- telebot/states/aio/middleware.py | 4 +- telebot/storage/__init__.py | 14 +- telebot/storage/base_storage.py | 66 ++++--- telebot/storage/memory_storage.py | 184 +++++++++++++----- telebot/storage/pickle_storage.py | 173 +++++++++++++---- telebot/storage/redis_storage.py | 186 +++++++++++++----- 14 files changed, 1054 insertions(+), 392 deletions(-) diff --git a/telebot/asyncio_storage/__init__.py b/telebot/asyncio_storage/__init__.py index 1f9d51650..82a8c817a 100644 --- a/telebot/asyncio_storage/__init__.py +++ b/telebot/asyncio_storage/__init__.py @@ -4,10 +4,10 @@ from telebot.asyncio_storage.base_storage import StateDataContext, StateStorageBase - - - __all__ = [ - 'StateStorageBase', 'StateDataContext', - 'StateMemoryStorage', 'StateRedisStorage', 'StatePickleStorage' -] \ No newline at end of file + "StateStorageBase", + "StateDataContext", + "StateMemoryStorage", + "StateRedisStorage", + "StatePickleStorage", +] diff --git a/telebot/asyncio_storage/base_storage.py b/telebot/asyncio_storage/base_storage.py index 6d06e7bfc..20d0b0587 100644 --- a/telebot/asyncio_storage/base_storage.py +++ b/telebot/asyncio_storage/base_storage.py @@ -1,5 +1,6 @@ import copy + class StateStorageBase: def __init__(self) -> None: pass @@ -15,30 +16,30 @@ async def get_data(self, chat_id, user_id): Get data for a user in a particular chat. """ raise NotImplementedError - + async def set_state(self, chat_id, user_id, state): """ Set state for a particular user. - ! Note that you should create a - record if it does not exist, and + ! Note that you should create a + record if it does not exist, and if a record with state already exists, you need to update a record. """ raise NotImplementedError - + async def delete_state(self, chat_id, user_id): """ Delete state for a particular user. """ raise NotImplementedError - + async def reset_data(self, chat_id, user_id): """ Reset data for a particular user in a chat. """ raise NotImplementedError - + async def get_state(self, chat_id, user_id): raise NotImplementedError @@ -51,16 +52,16 @@ def get_interactive_data(self, chat_id, user_id): async def save(self, chat_id, user_id, data): raise NotImplementedError - + def _get_key( - self, - chat_id: int, - user_id: int, - prefix: str, - separator: str, - business_connection_id: str=None, - message_thread_id: int=None, - bot_id: int=None + self, + chat_id: int, + user_id: int, + prefix: str, + separator: str, + business_connection_id: str = None, + message_thread_id: int = None, + bot_id: int = None, ) -> str: """ Convert parameters to a key. @@ -78,15 +79,20 @@ def _get_key( return separator.join(params) - - - - class StateDataContext: """ Class for data. """ - def __init__(self , obj, chat_id, user_id, business_connection_id=None, message_thread_id=None, bot_id=None, ): + + def __init__( + self, + obj, + chat_id, + user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): self.obj = obj self.data = None self.chat_id = chat_id @@ -95,13 +101,23 @@ def __init__(self , obj, chat_id, user_id, business_connection_id=None, message_ self.business_connection_id = business_connection_id self.message_thread_id = message_thread_id - - async def __aenter__(self): - data = await self.obj.get_data(chat_id=self.chat_id, user_id=self.user_id, business_connection_id=self.business_connection_id, - message_thread_id=self.message_thread_id, bot_id=self.bot_id) + data = await self.obj.get_data( + chat_id=self.chat_id, + user_id=self.user_id, + business_connection_id=self.business_connection_id, + message_thread_id=self.message_thread_id, + bot_id=self.bot_id, + ) self.data = copy.deepcopy(data) return self.data async def __aexit__(self, exc_type, exc_val, exc_tb): - return await self.obj.save(self.chat_id, self.user_id, self.data, self.business_connection_id, self.message_thread_id, self.bot_id) \ No newline at end of file + return await self.obj.save( + self.chat_id, + self.user_id, + self.data, + self.business_connection_id, + self.message_thread_id, + self.bot_id, + ) diff --git a/telebot/asyncio_storage/memory_storage.py b/telebot/asyncio_storage/memory_storage.py index 87449cace..e17d5fe8c 100644 --- a/telebot/asyncio_storage/memory_storage.py +++ b/telebot/asyncio_storage/memory_storage.py @@ -1,6 +1,7 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext from typing import Optional, Union + class StateMemoryStorage(StateStorageBase): """ Memory storage for states. @@ -19,72 +20,114 @@ class StateMemoryStorage(StateStorageBase): :type prefix: Optional[str] """ - def __init__(self, - separator: Optional[str]=":", - prefix: Optional[str]="telebot" - ) -> None: + def __init__( + self, separator: Optional[str] = ":", prefix: Optional[str] = "telebot" + ) -> None: self.separator = separator self.prefix = prefix if not self.prefix: raise ValueError("Prefix cannot be empty") - - self.data = {} # key: telebot:bot_id:business_connection_id:message_thread_id:chat_id:user_id - async def set_state( - self, chat_id: int, user_id: int, state: str, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self.data = ( + {} + ) # key: telebot:bot_id:business_connection_id:message_thread_id:chat_id:user_id + async def set_state( + self, + chat_id: int, + user_id: int, + state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: if hasattr(state, "name"): state = state.name _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: self.data[_key] = {"state": state, "data": {}} else: self.data[_key]["state"] = state - + return True - + async def get_state( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> Union[str, None]: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: return None - + return self.data[_key]["state"] - + async def delete_state( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) - + if self.data.get(_key) is None: return False - + del self.data[_key] return True - - + async def set_data( - self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], - business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, - bot_id: Optional[int]=None) -> bool: - + self, + chat_id: int, + user_id: int, + key: str, + value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: + _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: @@ -92,57 +135,91 @@ async def set_data( self.data[_key]["data"][key] = value return True - async def get_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> dict: - + _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) return self.data.get(_key, {}).get("data", {}) - + async def reset_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: - + _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: return False self.data[_key]["data"] = {} return True - + def get_interactive_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> Optional[dict]: return StateDataContext( - self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, - message_thread_id=message_thread_id, bot_id=bot_id + self, + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id, + bot_id=bot_id, ) - + async def save( - self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + data: dict, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, - message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: return False self.data[_key]["data"] = data return True - + def __str__(self) -> str: return f"" - - - diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index 0506d4703..723672034 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -1,4 +1,3 @@ - aiofiles_installed = True try: import aiofiles @@ -12,12 +11,15 @@ from telebot.asyncio_storage.base_storage import StateStorageBase, StateDataContext + def with_lock(func: Callable) -> Callable: async def wrapper(self, *args, **kwargs): async with self.lock: return await func(self, *args, **kwargs) + return wrapper + class StatePickleStorage(StateStorageBase): """ State storage based on pickle file. @@ -27,7 +29,7 @@ class StatePickleStorage(StateStorageBase): This storage is not recommended for production use. Data may be corrupted. If you face a case where states do not work as expected, try to use another storage. - + .. code-block:: python3 storage = StatePickleStorage() @@ -42,9 +44,14 @@ class StatePickleStorage(StateStorageBase): :param separator: Separator for keys, default is ":". :type separator: Optional[str] """ - def __init__(self, file_path: str = "./.state-save/states.pkl", - prefix='telebot', separator: Optional[str] = ":") -> None: - + + def __init__( + self, + file_path: str = "./.state-save/states.pkl", + prefix="telebot", + separator: Optional[str] = ":", + ) -> None: + if not aiofiles_installed: raise ImportError("Please install aiofiles using `pip install aiofiles`") @@ -55,12 +62,12 @@ def __init__(self, file_path: str = "./.state-save/states.pkl", self.create_dir() async def _read_from_file(self) -> dict: - async with aiofiles.open(self.file_path, 'rb') as f: + async with aiofiles.open(self.file_path, "rb") as f: data = await f.read() return pickle.loads(data) async def _write_to_file(self, data: dict) -> None: - async with aiofiles.open(self.file_path, 'wb') as f: + async with aiofiles.open(self.file_path, "wb") as f: await f.write(pickle.dumps(data)) def create_dir(self): @@ -70,16 +77,27 @@ def create_dir(self): dirs, filename = os.path.split(self.file_path) os.makedirs(dirs, exist_ok=True) if not os.path.isfile(self.file_path): - with open(self.file_path,'wb') as file: + with open(self.file_path, "wb") as file: pickle.dump({}, file) - @with_lock - async def set_state(self, chat_id: int, user_id: int, state: str, - business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, - bot_id: Optional[int] = None) -> bool: + async def set_state( + self, + chat_id: int, + user_id: int, + state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = await self._read_from_file() if _key not in data: @@ -90,19 +108,43 @@ async def set_state(self, chat_id: int, user_id: int, state: str, return True @with_lock - async def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Union[str, None]: + async def get_state( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> Union[str, None]: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = await self._read_from_file() return data.get(_key, {}).get("state") @with_lock - async def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + async def delete_state( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = await self._read_from_file() if _key in data: @@ -112,11 +154,24 @@ async def delete_state(self, chat_id: int, user_id: int, business_connection_id: return False @with_lock - async def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], - business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, - bot_id: Optional[int] = None) -> bool: + async def set_data( + self, + chat_id: int, + user_id: int, + key: str, + value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = await self._read_from_file() state_data = data.get(_key, {}) @@ -129,19 +184,43 @@ async def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, return True @with_lock - async def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> dict: + async def get_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> dict: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = await self._read_from_file() return data.get(_key, {}).get("data", {}) @with_lock - async def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + async def reset_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = await self._read_from_file() if _key in data: @@ -150,18 +229,41 @@ async def reset_data(self, chat_id: int, user_id: int, business_connection_id: O return True return False - def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Optional[dict]: + def get_interactive_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> Optional[dict]: return StateDataContext( - self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, - message_thread_id=message_thread_id, bot_id=bot_id + self, + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id, + bot_id=bot_id, ) @with_lock - async def save(self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + async def save( + self, + chat_id: int, + user_id: int, + data: dict, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = await self._read_from_file() data[_key]["data"] = data diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index 69b18d5a1..eccff5c30 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -1,4 +1,3 @@ - redis_installed = True try: import redis @@ -17,8 +16,10 @@ def async_with_lock(func: Callable[..., Coroutine]) -> Callable[..., Coroutine]: async def wrapper(self, *args, **kwargs): async with self.lock: return await func(self, *args, **kwargs) + return wrapper + def async_with_pipeline(func: Callable[..., Coroutine]) -> Callable[..., Coroutine]: async def wrapper(self, *args, **kwargs): async with self.redis.pipeline() as pipe: @@ -26,8 +27,10 @@ async def wrapper(self, *args, **kwargs): result = await func(self, pipe, *args, **kwargs) await pipe.execute() return result + return wrapper + class StateRedisStorage(StateStorageBase): """ State storage based on Redis. @@ -62,16 +65,22 @@ class StateRedisStorage(StateStorageBase): :type separator: Optional[str] """ - def __init__(self, host='localhost', port=6379, db=0, password=None, - prefix='telebot', - redis_url=None, - connection_pool: 'ConnectionPool'=None, - separator: Optional[str] = ":", - ) -> None: - + + def __init__( + self, + host="localhost", + port=6379, + db=0, + password=None, + prefix="telebot", + redis_url=None, + connection_pool: "ConnectionPool" = None, + separator: Optional[str] = ":", + ) -> None: + if not redis_installed: raise ImportError("Please install redis using `pip install redis`") - + self.separator = separator self.prefix = prefix if not self.prefix: @@ -83,46 +92,105 @@ def __init__(self, host='localhost', port=6379, db=0, password=None, self.redis = Redis(connection_pool=connection_pool) else: self.redis = Redis(host=host, port=port, db=db, password=password) - + self.lock = asyncio.Lock() @async_with_lock @async_with_pipeline - async def set_state(self, pipe, chat_id: int, user_id: int, state: str, - business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + async def set_state( + self, + pipe, + chat_id: int, + user_id: int, + state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: if hasattr(state, "name"): state = state.name - _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + _key = self._get_key( + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, + ) pipe.hget(_key, "data") result = await pipe.execute() data = result[0] if data is None: pipe.hset(_key, "data", json.dumps({})) - + await pipe.hset(_key, "state", state) - + return True - async def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Union[str, None]: - _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + async def get_state( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> Union[str, None]: + _key = self._get_key( + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, + ) state_bytes = await self.redis.hget(_key, "state") - return state_bytes.decode('utf-8') if state_bytes else None - - async def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: - _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + return state_bytes.decode("utf-8") if state_bytes else None + + async def delete_state( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: + _key = self._get_key( + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, + ) result = await self.redis.delete(_key) return result > 0 @async_with_lock @async_with_pipeline - async def set_data(self, pipe, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], - business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, - bot_id: Optional[int] = None) -> bool: - _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + async def set_data( + self, + pipe, + chat_id: int, + user_id: int, + key: str, + value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: + _key = self._get_key( + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, + ) data = await pipe.hget(_key, "data") data = await pipe.execute() data = data[0] @@ -134,41 +202,97 @@ async def set_data(self, pipe, chat_id: int, user_id: int, key: str, value: Unio await pipe.hset(_key, "data", json.dumps(data)) return True - async def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> dict: - _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + async def get_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> dict: + _key = self._get_key( + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, + ) data = await self.redis.hget(_key, "data") return json.loads(data) if data else {} @async_with_lock @async_with_pipeline - async def reset_data(self, pipe, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: - _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + async def reset_data( + self, + pipe, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: + _key = self._get_key( + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, + ) if await pipe.exists(_key): await pipe.hset(_key, "data", "{}") else: return False return True - def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Optional[dict]: - return StateDataContext(self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, - message_thread_id=message_thread_id, bot_id=bot_id) + def get_interactive_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> Optional[dict]: + return StateDataContext( + self, + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id, + bot_id=bot_id, + ) @async_with_lock @async_with_pipeline - async def save(self, pipe, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: - _key = self._get_key(chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id) + async def save( + self, + pipe, + chat_id: int, + user_id: int, + data: dict, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: + _key = self._get_key( + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, + ) if await pipe.exists(_key): await pipe.hset(_key, "data", json.dumps(data)) else: return False return True - - def migrate_format(self, bot_id: int, - prefix: Optional[str]="telebot_"): + + def migrate_format(self, bot_id: int, prefix: Optional[str] = "telebot_"): """ Migrate from old to new format of keys. Run this function once to migrate all redis existing keys to new format. @@ -188,20 +312,22 @@ def migrate_format(self, bot_id: int, :type prefix: Optional[str] """ keys = self.redis.keys(f"{prefix}*") - + for key in keys: - old_key = key.decode('utf-8') + old_key = key.decode("utf-8") # old: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} value = self.redis.get(old_key) value = json.loads(value) - chat_id = old_key[len(prefix):] + chat_id = old_key[len(prefix) :] user_id = list(value.keys())[0] - state = value[user_id]['state'] - state_data = value[user_id]['data'] + state = value[user_id]["state"] + state_data = value[user_id]["data"] # set new format - new_key = self._get_key(int(chat_id), int(user_id), self.prefix, self.separator, bot_id=bot_id) + new_key = self._get_key( + int(chat_id), int(user_id), self.prefix, self.separator, bot_id=bot_id + ) self.redis.hset(new_key, "state", state) self.redis.hset(new_key, "data", json.dumps(state_data)) diff --git a/telebot/states/__init__.py b/telebot/states/__init__.py index 015f135e4..2491608ee 100644 --- a/telebot/states/__init__.py +++ b/telebot/states/__init__.py @@ -1,8 +1,10 @@ """ Contains classes for states and state groups. """ + from telebot import types + class State: """ Class representing a state. @@ -12,9 +14,11 @@ class State: class MyStates(StatesGroup): my_state = State() # returns my_state:State string. """ + def __init__(self) -> None: self.name: str = None self.group: StatesGroup = None + def __str__(self) -> str: return f"<{self.name}>" @@ -28,12 +32,17 @@ class StatesGroup: class MyStates(StatesGroup): my_state = State() # returns my_state:State string. """ + def __init_subclass__(cls) -> None: state_list = [] for name, value in cls.__dict__.items(): - if not name.startswith('__') and not callable(value) and isinstance(value, State): + if ( + not name.startswith("__") + and not callable(value) + and isinstance(value, State) + ): # change value of that variable - value.name = ':'.join((cls.__name__, name)) + value.name = ":".join((cls.__name__, name)) value.group = cls state_list.append(value) cls._state_list = state_list @@ -42,41 +51,78 @@ def __init_subclass__(cls) -> None: def state_list(self): return self._state_list + def resolve_context(message, bot_id: int) -> tuple: # chat_id, user_id, business_connection_id, bot_id, message_thread_id - + # message, edited_message, channel_post, edited_channel_post, business_message, edited_business_message if isinstance(message, types.Message): - return (message.chat.id, message.from_user.id, message.business_connection_id, bot_id, - message.message_thread_id if message.is_topic_message else None) - elif isinstance(message, types.CallbackQuery): # callback_query - return (message.message.chat.id, message.from_user.id, message.message.business_connection_id, bot_id, - message.message.message_thread_id if message.message.is_topic_message else None) - elif isinstance(message, types.BusinessConnection): # business_connection + return ( + message.chat.id, + message.from_user.id, + message.business_connection_id, + bot_id, + message.message_thread_id if message.is_topic_message else None, + ) + elif isinstance(message, types.CallbackQuery): # callback_query + return ( + message.message.chat.id, + message.from_user.id, + message.message.business_connection_id, + bot_id, + ( + message.message.message_thread_id + if message.message.is_topic_message + else None + ), + ) + elif isinstance(message, types.BusinessConnection): # business_connection return (message.user_chat_id, message.user.id, message.id, bot_id, None) - elif isinstance(message, types.BusinessMessagesDeleted): # deleted_business_messages - return (message.chat.id, message.chat.id, message.business_connection_id, bot_id, None) - elif isinstance(message, types.MessageReactionUpdated): # message_reaction + elif isinstance( + message, types.BusinessMessagesDeleted + ): # deleted_business_messages + return ( + message.chat.id, + message.chat.id, + message.business_connection_id, + bot_id, + None, + ) + elif isinstance(message, types.MessageReactionUpdated): # message_reaction return (message.chat.id, message.user.id, None, bot_id, None) - elif isinstance(message, types.MessageReactionCountUpdated): # message_reaction_count + elif isinstance( + message, types.MessageReactionCountUpdated + ): # message_reaction_count return (message.chat.id, None, None, bot_id, None) - elif isinstance(message, types.InlineQuery): # inline_query + elif isinstance(message, types.InlineQuery): # inline_query return (None, message.from_user.id, None, bot_id, None) - elif isinstance(message, types.ChosenInlineResult): # chosen_inline_result + elif isinstance(message, types.ChosenInlineResult): # chosen_inline_result return (None, message.from_user.id, None, bot_id, None) - elif isinstance(message, types.ShippingQuery): # shipping_query + elif isinstance(message, types.ShippingQuery): # shipping_query return (None, message.from_user.id, None, bot_id, None) - elif isinstance(message, types.PreCheckoutQuery): # pre_checkout_query + elif isinstance(message, types.PreCheckoutQuery): # pre_checkout_query return (None, message.from_user.id, None, bot_id, None) - elif isinstance(message, types.PollAnswer): # poll_answer + elif isinstance(message, types.PollAnswer): # poll_answer return (None, message.user.id, None, bot_id, None) - elif isinstance(message, types.ChatMemberUpdated): # chat_member # my_chat_member + elif isinstance(message, types.ChatMemberUpdated): # chat_member # my_chat_member return (message.chat.id, message.from_user.id, None, bot_id, None) - elif isinstance(message, types.ChatJoinRequest): # chat_join_request + elif isinstance(message, types.ChatJoinRequest): # chat_join_request return (message.chat.id, message.from_user.id, None, bot_id, None) - elif isinstance(message, types.ChatBoostRemoved): # removed_chat_boost - return (message.chat.id, message.source.user.id if message.source else None, None, bot_id, None) - elif isinstance(message, types.ChatBoostUpdated): # chat_boost - return (message.chat.id, message.boost.source.user.id if message.boost.source else None, None, bot_id, None) + elif isinstance(message, types.ChatBoostRemoved): # removed_chat_boost + return ( + message.chat.id, + message.source.user.id if message.source else None, + None, + bot_id, + None, + ) + elif isinstance(message, types.ChatBoostUpdated): # chat_boost + return ( + message.chat.id, + message.boost.source.user.id if message.boost.source else None, + None, + bot_id, + None, + ) else: - pass # not yet supported :( \ No newline at end of file + pass # not yet supported :( diff --git a/telebot/states/aio/__init__.py b/telebot/states/aio/__init__.py index 60f9ea2b7..14ef19536 100644 --- a/telebot/states/aio/__init__.py +++ b/telebot/states/aio/__init__.py @@ -2,6 +2,6 @@ from .middleware import StateMiddleware __all__ = [ - 'StateContext', - 'StateMiddleware', -] \ No newline at end of file + "StateContext", + "StateMiddleware", +] diff --git a/telebot/states/aio/context.py b/telebot/states/aio/context.py index 956ca6837..4c9ad61a3 100644 --- a/telebot/states/aio/context.py +++ b/telebot/states/aio/context.py @@ -6,8 +6,7 @@ from typing import Union - -class StateContext(): +class StateContext: """ Class representing a state context. @@ -21,7 +20,7 @@ async def start_ex(message: types.Message, state_context: StateContext): await bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id) # also, state_context.data(), .add_data(), .reset_data(), .delete() methods available. """ - + def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None: self.message: Union[Message, CallbackQuery] = message self.bot: AsyncTeleBot = bot @@ -42,7 +41,9 @@ async def start_ex(message: types.Message, state_context: StateContext): await bot.send_message(message.chat.id, 'Hi, write me a name', reply_to_message_id=message.message_id) """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = ( + resolve_context(self.message, self.bot.bot_id) + ) if isinstance(state, State): state = state.name return await self.bot.set_state( @@ -51,9 +52,9 @@ async def start_ex(message: types.Message, state_context: StateContext): state=state, business_connection_id=business_connection_id, bot_id=bot_id, - message_thread_id=message_thread_id + message_thread_id=message_thread_id, ) - + async def get(self) -> str: """ Get current state for current user. @@ -62,67 +63,75 @@ async def get(self) -> str: :rtype: str """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = ( + resolve_context(self.message, self.bot.bot_id) + ) return await self.bot.get_state( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, bot_id=bot_id, - message_thread_id=message_thread_id + message_thread_id=message_thread_id, ) - + async def delete(self) -> bool: """ Deletes state and data for current user. .. warning:: - + This method deletes state and associated data for current user. """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = ( + resolve_context(self.message, self.bot.bot_id) + ) return await self.bot.delete_state( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, bot_id=bot_id, - message_thread_id=message_thread_id + message_thread_id=message_thread_id, ) - + async def reset_data(self) -> bool: """ - Reset data for current user. + Reset data for current user. State will not be changed. """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = ( + resolve_context(self.message, self.bot.bot_id) + ) return await self.bot.reset_data( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, bot_id=bot_id, - message_thread_id=message_thread_id + message_thread_id=message_thread_id, ) - + def data(self) -> dict: """ Get data for current user. .. code-block:: python3 - + with state_context.data() as data: print(data) data['name'] = 'John' """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = ( + resolve_context(self.message, self.bot.bot_id) + ) return self.bot.retrieve_data( chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, bot_id=bot_id, - message_thread_id=message_thread_id + message_thread_id=message_thread_id, ) - + async def add_data(self, **kwargs) -> None: """ Add data for current user. @@ -131,7 +140,9 @@ async def add_data(self, **kwargs) -> None: :type kwargs: dict """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(self.message, self.bot.bot_id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = ( + resolve_context(self.message, self.bot.bot_id) + ) return await self.bot.add_data( chat_id=chat_id, user_id=user_id, @@ -140,4 +151,3 @@ async def add_data(self, **kwargs) -> None: message_thread_id=message_thread_id, **kwargs ) - \ No newline at end of file diff --git a/telebot/states/aio/middleware.py b/telebot/states/aio/middleware.py index 546b29067..675b7b462 100644 --- a/telebot/states/aio/middleware.py +++ b/telebot/states/aio/middleware.py @@ -14,8 +14,8 @@ def __init__(self, bot: AsyncTeleBot) -> None: async def pre_process(self, message, data): state_context = StateContext(message, self.bot) - data['state_context'] = state_context - data['state'] = state_context # 2 ways to access state context + data["state_context"] = state_context + data["state"] = state_context # 2 ways to access state context async def post_process(self, message, data, exception): pass diff --git a/telebot/storage/__init__.py b/telebot/storage/__init__.py index 954c1b31d..bb3dd7f07 100644 --- a/telebot/storage/__init__.py +++ b/telebot/storage/__init__.py @@ -1,13 +1,13 @@ from telebot.storage.memory_storage import StateMemoryStorage from telebot.storage.redis_storage import StateRedisStorage from telebot.storage.pickle_storage import StatePickleStorage -from telebot.storage.base_storage import StateDataContext,StateStorageBase - - - +from telebot.storage.base_storage import StateDataContext, StateStorageBase __all__ = [ - 'StateStorageBase', 'StateDataContext', - 'StateMemoryStorage', 'StateRedisStorage', 'StatePickleStorage' -] \ No newline at end of file + "StateStorageBase", + "StateDataContext", + "StateMemoryStorage", + "StateRedisStorage", + "StatePickleStorage", +] diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py index 25a6c6b04..9956c7ab9 100644 --- a/telebot/storage/base_storage.py +++ b/telebot/storage/base_storage.py @@ -1,5 +1,6 @@ import copy + class StateStorageBase: def __init__(self) -> None: pass @@ -15,30 +16,30 @@ def get_data(self, chat_id, user_id): Get data for a user in a particular chat. """ raise NotImplementedError - + def set_state(self, chat_id, user_id, state): """ Set state for a particular user. - ! Note that you should create a - record if it does not exist, and + ! Note that you should create a + record if it does not exist, and if a record with state already exists, you need to update a record. """ raise NotImplementedError - + def delete_state(self, chat_id, user_id): """ Delete state for a particular user. """ raise NotImplementedError - + def reset_data(self, chat_id, user_id): """ Reset data for a particular user in a chat. """ raise NotImplementedError - + def get_state(self, chat_id, user_id): raise NotImplementedError @@ -47,16 +48,16 @@ def get_interactive_data(self, chat_id, user_id): def save(self, chat_id, user_id, data): raise NotImplementedError - + def _get_key( - self, - chat_id: int, - user_id: int, - prefix: str, - separator: str, - business_connection_id: str=None, - message_thread_id: int=None, - bot_id: int=None + self, + chat_id: int, + user_id: int, + prefix: str, + separator: str, + business_connection_id: str = None, + message_thread_id: int = None, + bot_id: int = None, ) -> str: """ Convert parameters to a key. @@ -74,18 +75,28 @@ def _get_key( return separator.join(params) - - - - class StateDataContext: """ Class for data. """ - def __init__(self , obj, chat_id, user_id, business_connection_id=None, message_thread_id=None, bot_id=None, ): + + def __init__( + self, + obj, + chat_id, + user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): self.obj = obj - res = obj.get_data(chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, - message_thread_id=message_thread_id, bot_id=bot_id) + res = obj.get_data( + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id, + bot_id=bot_id, + ) self.data = copy.deepcopy(res) self.chat_id = chat_id self.user_id = user_id @@ -93,10 +104,15 @@ def __init__(self , obj, chat_id, user_id, business_connection_id=None, message_ self.business_connection_id = business_connection_id self.message_thread_id = message_thread_id - - def __enter__(self): return self.data def __exit__(self, exc_type, exc_val, exc_tb): - return self.obj.save(self.chat_id, self.user_id, self.data, self.business_connection_id, self.message_thread_id, self.bot_id) \ No newline at end of file + return self.obj.save( + self.chat_id, + self.user_id, + self.data, + self.business_connection_id, + self.message_thread_id, + self.bot_id, + ) diff --git a/telebot/storage/memory_storage.py b/telebot/storage/memory_storage.py index dc88bcebc..21266632c 100644 --- a/telebot/storage/memory_storage.py +++ b/telebot/storage/memory_storage.py @@ -1,6 +1,7 @@ from telebot.storage.base_storage import StateStorageBase, StateDataContext from typing import Optional, Union + class StateMemoryStorage(StateStorageBase): """ Memory storage for states. @@ -18,72 +19,115 @@ class StateMemoryStorage(StateStorageBase): :param prefix: Prefix for keys, default is "telebot". :type prefix: Optional[str] """ - def __init__(self, - separator: Optional[str]=":", - prefix: Optional[str]="telebot" - ) -> None: + + def __init__( + self, separator: Optional[str] = ":", prefix: Optional[str] = "telebot" + ) -> None: self.separator = separator self.prefix = prefix if not self.prefix: raise ValueError("Prefix cannot be empty") - - self.data = {} # key: telebot:bot_id:business_connection_id:message_thread_id:chat_id:user_id - def set_state( - self, chat_id: int, user_id: int, state: str, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self.data = ( + {} + ) # key: telebot:bot_id:business_connection_id:message_thread_id:chat_id:user_id + def set_state( + self, + chat_id: int, + user_id: int, + state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: if hasattr(state, "name"): state = state.name _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: self.data[_key] = {"state": state, "data": {}} else: self.data[_key]["state"] = state - + return True - + def get_state( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> Union[str, None]: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: return None - + return self.data[_key]["state"] - + def delete_state( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) - + if self.data.get(_key) is None: return False - + del self.data[_key] return True - - + def set_data( - self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], - business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, - bot_id: Optional[int]=None) -> bool: - + self, + chat_id: int, + user_id: int, + key: str, + value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: + _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: @@ -91,57 +135,91 @@ def set_data( self.data[_key]["data"][key] = value return True - def get_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> dict: - + _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) return self.data.get(_key, {}).get("data", {}) - + def reset_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: - + _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: return False self.data[_key]["data"] = {} return True - + def get_interactive_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> Optional[dict]: return StateDataContext( - self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, - message_thread_id=message_thread_id, bot_id=bot_id + self, + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id, + bot_id=bot_id, ) - + def save( - self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, bot_id: Optional[int]=None + self, + chat_id: int, + user_id: int, + data: dict, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, - message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) if self.data.get(_key) is None: return False self.data[_key]["data"] = data return True - + def __str__(self) -> str: return f"" - - - diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index fc8dc3289..e11a05043 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -4,12 +4,15 @@ from typing import Optional, Union, Callable from telebot.storage.base_storage import StateStorageBase, StateDataContext + def with_lock(func: Callable) -> Callable: def wrapper(self, *args, **kwargs): with self.lock: return func(self, *args, **kwargs) + return wrapper + class StatePickleStorage(StateStorageBase): """ State storage based on pickle file. @@ -19,7 +22,7 @@ class StatePickleStorage(StateStorageBase): This storage is not recommended for production use. Data may be corrupted. If you face a case where states do not work as expected, try to use another storage. - + .. code-block:: python3 storage = StatePickleStorage() @@ -35,8 +38,12 @@ class StatePickleStorage(StateStorageBase): :type separator: Optional[str] """ - def __init__(self, file_path: str = "./.state-save/states.pkl", - prefix='telebot', separator: Optional[str] = ":") -> None: + def __init__( + self, + file_path: str = "./.state-save/states.pkl", + prefix="telebot", + separator: Optional[str] = ":", + ) -> None: self.file_path = file_path self.prefix = prefix self.separator = separator @@ -45,11 +52,11 @@ def __init__(self, file_path: str = "./.state-save/states.pkl", self.create_dir() def _read_from_file(self) -> dict: - with open(self.file_path, 'rb') as f: + with open(self.file_path, "rb") as f: return pickle.load(f) def _write_to_file(self, data: dict) -> None: - with open(self.file_path, 'wb') as f: + with open(self.file_path, "wb") as f: pickle.dump(data, f) def create_dir(self): @@ -59,15 +66,27 @@ def create_dir(self): dirs, filename = os.path.split(self.file_path) os.makedirs(dirs, exist_ok=True) if not os.path.isfile(self.file_path): - with open(self.file_path,'wb') as file: + with open(self.file_path, "wb") as file: pickle.dump({}, file) @with_lock - def set_state(self, chat_id: int, user_id: int, state: str, - business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, - bot_id: Optional[int] = None) -> bool: + def set_state( + self, + chat_id: int, + user_id: int, + state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self._read_from_file() if _key not in data: @@ -78,19 +97,43 @@ def set_state(self, chat_id: int, user_id: int, state: str, return True @with_lock - def get_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Union[str, None]: + def get_state( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> Union[str, None]: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self._read_from_file() return data.get(_key, {}).get("state") - + @with_lock - def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + def delete_state( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self._read_from_file() if _key in data: @@ -100,11 +143,24 @@ def delete_state(self, chat_id: int, user_id: int, business_connection_id: Optio return False @with_lock - def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], - business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, - bot_id: Optional[int] = None) -> bool: + def set_data( + self, + chat_id: int, + user_id: int, + key: str, + value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self._read_from_file() state_data = data.get(_key, {}) @@ -117,19 +173,43 @@ def set_data(self, chat_id: int, user_id: int, key: str, value: Union[str, int, return True @with_lock - def get_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> dict: + def get_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> dict: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self._read_from_file() return data.get(_key, {}).get("data", {}) @with_lock - def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + def reset_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self._read_from_file() if _key in data: @@ -138,23 +218,46 @@ def reset_data(self, chat_id: int, user_id: int, business_connection_id: Optiona return True return False - def get_interactive_data(self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> Optional[dict]: + def get_interactive_data( + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> Optional[dict]: return StateDataContext( - self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, - message_thread_id=message_thread_id, bot_id=bot_id + self, + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id, + bot_id=bot_id, ) @with_lock - def save(self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None) -> bool: + def save( + self, + chat_id: int, + user_id: int, + data: dict, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, + ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self._read_from_file() data[_key]["data"] = data self._write_to_file(data) return True - + def __str__(self) -> str: return f"StatePickleStorage({self.file_path}, {self.prefix})" diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 1d1413fa2..19f5c2cd5 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -8,6 +8,7 @@ except ImportError: redis_installed = False + class StateRedisStorage(StateStorageBase): """ State storage based on Redis. @@ -42,39 +43,56 @@ class StateRedisStorage(StateStorageBase): :type separator: Optional[str] """ - def __init__(self, host='localhost', port=6379, db=0, password=None, - prefix='telebot', - redis_url=None, - connection_pool: 'redis.ConnectionPool'=None, - separator: Optional[str]=":", - ) -> None: - + + def __init__( + self, + host="localhost", + port=6379, + db=0, + password=None, + prefix="telebot", + redis_url=None, + connection_pool: "redis.ConnectionPool" = None, + separator: Optional[str] = ":", + ) -> None: + if not redis_installed: - raise ImportError("Redis is not installed. Please install it via pip install redis") + raise ImportError( + "Redis is not installed. Please install it via pip install redis" + ) self.separator = separator self.prefix = prefix if not self.prefix: raise ValueError("Prefix cannot be empty") - + if redis_url: self.redis = redis.Redis.from_url(redis_url) elif connection_pool: self.redis = redis.Redis(connection_pool=connection_pool) else: self.redis = redis.Redis(host=host, port=port, db=db, password=password) - def set_state( - self, chat_id: int, user_id: int, state: str, - business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + state: str, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: if hasattr(state, "name"): state = state.name _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) def set_state_action(pipe): @@ -89,36 +107,67 @@ def set_state_action(pipe): pipe.hset(_key, "data", json.dumps(data)) pipe.hset(_key, "state", state) - + self.redis.transaction(set_state_action, _key) return True def get_state( - self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> Union[str, None]: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) state_bytes = self.redis.hget(_key, "state") - return state_bytes.decode('utf-8') if state_bytes else None + return state_bytes.decode("utf-8") if state_bytes else None def delete_state( - self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) return self.redis.delete(_key) > 0 def set_data( - self, chat_id: int, user_id: int, key: str, value: Union[str, int, float, dict], - business_connection_id: Optional[str] = None, message_thread_id: Optional[int] = None, - bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + key: str, + value: Union[str, int, float, dict], + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) def set_data_action(pipe): @@ -136,21 +185,41 @@ def set_data_action(pipe): return True def get_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> dict: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) data = self.redis.hget(_key, "data") return json.loads(data) if data else {} def reset_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) def reset_data_action(pipe): @@ -164,21 +233,39 @@ def reset_data_action(pipe): return True def get_interactive_data( - self, chat_id: int, user_id: int, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> Optional[dict]: return StateDataContext( - self, chat_id=chat_id, user_id=user_id, business_connection_id=business_connection_id, - message_thread_id=message_thread_id, bot_id=bot_id + self, + chat_id=chat_id, + user_id=user_id, + business_connection_id=business_connection_id, + message_thread_id=message_thread_id, + bot_id=bot_id, ) def save( - self, chat_id: int, user_id: int, data: dict, business_connection_id: Optional[str] = None, - message_thread_id: Optional[int] = None, bot_id: Optional[int] = None + self, + chat_id: int, + user_id: int, + data: dict, + business_connection_id: Optional[str] = None, + message_thread_id: Optional[int] = None, + bot_id: Optional[int] = None, ) -> bool: _key = self._get_key( - chat_id, user_id, self.prefix, self.separator, business_connection_id, - message_thread_id, bot_id + chat_id, + user_id, + self.prefix, + self.separator, + business_connection_id, + message_thread_id, + bot_id, ) def save_action(pipe): @@ -190,9 +277,8 @@ def save_action(pipe): self.redis.transaction(save_action, _key) return True - - def migrate_format(self, bot_id: int, - prefix: Optional[str]="telebot_"): + + def migrate_format(self, bot_id: int, prefix: Optional[str] = "telebot_"): """ Migrate from old to new format of keys. Run this function once to migrate all redis existing keys to new format. @@ -212,20 +298,22 @@ def migrate_format(self, bot_id: int, :type prefix: Optional[str] """ keys = self.redis.keys(f"{prefix}*") - + for key in keys: - old_key = key.decode('utf-8') + old_key = key.decode("utf-8") # old: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} value = self.redis.get(old_key) value = json.loads(value) - chat_id = old_key[len(prefix):] + chat_id = old_key[len(prefix) :] user_id = list(value.keys())[0] - state = value[user_id]['state'] - state_data = value[user_id]['data'] + state = value[user_id]["state"] + state_data = value[user_id]["data"] # set new format - new_key = self._get_key(int(chat_id), int(user_id), self.prefix, self.separator, bot_id=bot_id) + new_key = self._get_key( + int(chat_id), int(user_id), self.prefix, self.separator, bot_id=bot_id + ) self.redis.hset(new_key, "state", state) self.redis.hset(new_key, "data", json.dumps(state_data)) From dbfa514fc389d527ed368cf4630e40b957d88d6e Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 27 Jul 2024 15:12:27 +0500 Subject: [PATCH 1644/1808] code cleanuop & renamed aio -> asyncio --- .../asynchronous_telebot/custom_states.py | 98 +++++++++++++------ examples/custom_states.py | 91 ++++++++++++----- telebot/states/{aio => asyncio}/__init__.py | 0 telebot/states/{aio => asyncio}/context.py | 0 telebot/states/{aio => asyncio}/middleware.py | 0 5 files changed, 136 insertions(+), 53 deletions(-) rename telebot/states/{aio => asyncio}/__init__.py (100%) rename telebot/states/{aio => asyncio}/context.py (100%) rename telebot/states/{aio => asyncio}/middleware.py (100%) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index eefc6ebbb..f0111e27e 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -1,13 +1,13 @@ -from telebot import async_telebot -from telebot import asyncio_filters, types -from telebot.states import State, StatesGroup -from telebot.states.aio.context import StateContext +from telebot import async_telebot, asyncio_filters, types from telebot.asyncio_storage import StateMemoryStorage +from telebot.states import State, StatesGroup +from telebot.states.asyncio.context import StateContext # Initialize the bot state_storage = StateMemoryStorage() # don't use this in production; switch to redis bot = async_telebot.AsyncTeleBot("TOKEN", state_storage=state_storage) + # Define states class MyStates(StatesGroup): name = State() @@ -15,25 +15,39 @@ class MyStates(StatesGroup): color = State() hobby = State() + # Start command handler -@bot.message_handler(commands=['start']) +@bot.message_handler(commands=["start"]) async def start_ex(message: types.Message, state: StateContext): await state.set(MyStates.name) - await bot.send_message(message.chat.id, 'Hello! What is your first name?', reply_to_message_id=message.message_id) + await bot.send_message( + message.chat.id, + "Hello! What is your first name?", + reply_to_message_id=message.message_id, + ) + # Cancel command handler -@bot.message_handler(state="*", commands=['cancel']) +@bot.message_handler(state="*", commands=["cancel"]) async def any_state(message: types.Message, state: StateContext): await state.delete() - await bot.send_message(message.chat.id, 'Your information has been cleared. Type /start to begin again.', reply_to_message_id=message.message_id) + await bot.send_message( + message.chat.id, + "Your information has been cleared. Type /start to begin again.", + reply_to_message_id=message.message_id, + ) + # Handler for name input @bot.message_handler(state=MyStates.name) async def name_get(message: types.Message, state: StateContext): await state.set(MyStates.age) - await bot.send_message(message.chat.id, "How old are you?", reply_to_message_id=message.message_id) + await bot.send_message( + message.chat.id, "How old are you?", reply_to_message_id=message.message_id + ) await state.add_data(name=message.text) + # Handler for age input @bot.message_handler(state=MyStates.age, is_digit=True) async def ask_color(message: types.Message, state: StateContext): @@ -46,7 +60,13 @@ async def ask_color(message: types.Message, state: StateContext): buttons = [types.KeyboardButton(color) for color in colors] keyboard.add(*buttons) - await bot.send_message(message.chat.id, "What is your favorite color? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + await bot.send_message( + message.chat.id, + "What is your favorite color? Choose from the options below.", + reply_markup=keyboard, + reply_to_message_id=message.message_id, + ) + # Handler for color input @bot.message_handler(state=MyStates.color) @@ -60,15 +80,23 @@ async def ask_hobby(message: types.Message, state: StateContext): buttons = [types.KeyboardButton(hobby) for hobby in hobbies] keyboard.add(*buttons) - await bot.send_message(message.chat.id, "What is one of your hobbies? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + await bot.send_message( + message.chat.id, + "What is one of your hobbies? Choose from the options below.", + reply_markup=keyboard, + reply_to_message_id=message.message_id, + ) + # Handler for hobby input; use filters to ease validation -@bot.message_handler(state=MyStates.hobby, text=['Reading', 'Traveling', 'Gaming', 'Cooking']) +@bot.message_handler( + state=MyStates.hobby, text=["Reading", "Traveling", "Gaming", "Cooking"] +) async def finish(message: types.Message, state: StateContext): async with state.data() as data: - name = data.get('name') - age = data.get('age') - color = data.get('color') + name = data.get("name") + age = data.get("age") + color = data.get("color") hobby = message.text # Get the hobby from the message text # Provide a fun fact based on color @@ -79,24 +107,36 @@ async def finish(message: types.Message, state: StateContext): "Yellow": "Yellow is a cheerful color often associated with happiness.", "Purple": "Purple signifies royalty and luxury.", "Orange": "Orange is a vibrant color that stimulates enthusiasm.", - "Other": "Colors have various meanings depending on context." + "Other": "Colors have various meanings depending on context.", } - color_fact = color_facts.get(color, "Colors have diverse meanings, and yours is unique!") - - msg = (f"Thank you for sharing! Here is a summary of your information:\n" - f"First Name: {name}\n" - f"Age: {age}\n" - f"Favorite Color: {color}\n" - f"Fun Fact about your color: {color_fact}\n" - f"Favorite Hobby: {hobby}") - - await bot.send_message(message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id) + color_fact = color_facts.get( + color, "Colors have diverse meanings, and yours is unique!" + ) + + msg = ( + f"Thank you for sharing! Here is a summary of your information:\n" + f"First Name: {name}\n" + f"Age: {age}\n" + f"Favorite Color: {color}\n" + f"Fun Fact about your color: {color_fact}\n" + f"Favorite Hobby: {hobby}" + ) + + await bot.send_message( + message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id + ) await state.delete() + # Handler for incorrect age input @bot.message_handler(state=MyStates.age, is_digit=False) async def age_incorrect(message: types.Message): - await bot.send_message(message.chat.id, 'Please enter a valid number for age.', reply_to_message_id=message.message_id) + await bot.send_message( + message.chat.id, + "Please enter a valid number for age.", + reply_to_message_id=message.message_id, + ) + # Add custom filters bot.add_custom_filter(asyncio_filters.StateFilter(bot)) @@ -104,9 +144,11 @@ async def age_incorrect(message: types.Message): bot.add_custom_filter(asyncio_filters.TextMatchFilter()) # necessary for state parameter in handlers. -from telebot.states.aio.middleware import StateMiddleware +from telebot.states.asyncio.middleware import StateMiddleware + bot.setup_middleware(StateMiddleware(bot)) # Start polling import asyncio + asyncio.run(bot.polling()) diff --git a/examples/custom_states.py b/examples/custom_states.py index a488664b2..131dd1d42 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -6,8 +6,8 @@ # Initialize the bot state_storage = StateMemoryStorage() # don't use this in production; switch to redis -bot = telebot.TeleBot("TOKEN", state_storage=state_storage, - use_class_middlewares=True) +bot = telebot.TeleBot("TOKEN", state_storage=state_storage, use_class_middlewares=True) + # Define states class MyStates(StatesGroup): @@ -16,25 +16,39 @@ class MyStates(StatesGroup): color = State() hobby = State() + # Start command handler -@bot.message_handler(commands=['start']) +@bot.message_handler(commands=["start"]) def start_ex(message: types.Message, state: StateContext): state.set(MyStates.name) - bot.send_message(message.chat.id, 'Hello! What is your first name?', reply_to_message_id=message.message_id) + bot.send_message( + message.chat.id, + "Hello! What is your first name?", + reply_to_message_id=message.message_id, + ) + # Cancel command handler -@bot.message_handler(state="*", commands=['cancel']) +@bot.message_handler(state="*", commands=["cancel"]) def any_state(message: types.Message, state: StateContext): state.delete() - bot.send_message(message.chat.id, 'Your information has been cleared. Type /start to begin again.', reply_to_message_id=message.message_id) + bot.send_message( + message.chat.id, + "Your information has been cleared. Type /start to begin again.", + reply_to_message_id=message.message_id, + ) + # Handler for name input @bot.message_handler(state=MyStates.name) def name_get(message: types.Message, state: StateContext): state.set(MyStates.age) - bot.send_message(message.chat.id, "How old are you?", reply_to_message_id=message.message_id) + bot.send_message( + message.chat.id, "How old are you?", reply_to_message_id=message.message_id + ) state.add_data(name=message.text) + # Handler for age input @bot.message_handler(state=MyStates.age, is_digit=True) def ask_color(message: types.Message, state: StateContext): @@ -47,7 +61,13 @@ def ask_color(message: types.Message, state: StateContext): buttons = [types.KeyboardButton(color) for color in colors] keyboard.add(*buttons) - bot.send_message(message.chat.id, "What is your favorite color? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + bot.send_message( + message.chat.id, + "What is your favorite color? Choose from the options below.", + reply_markup=keyboard, + reply_to_message_id=message.message_id, + ) + # Handler for color input @bot.message_handler(state=MyStates.color) @@ -61,15 +81,23 @@ def ask_hobby(message: types.Message, state: StateContext): buttons = [types.KeyboardButton(hobby) for hobby in hobbies] keyboard.add(*buttons) - bot.send_message(message.chat.id, "What is one of your hobbies? Choose from the options below.", reply_markup=keyboard, reply_to_message_id=message.message_id) + bot.send_message( + message.chat.id, + "What is one of your hobbies? Choose from the options below.", + reply_markup=keyboard, + reply_to_message_id=message.message_id, + ) + # Handler for hobby input -@bot.message_handler(state=MyStates.hobby, text=['Reading', 'Traveling', 'Gaming', 'Cooking']) +@bot.message_handler( + state=MyStates.hobby, text=["Reading", "Traveling", "Gaming", "Cooking"] +) def finish(message: types.Message, state: StateContext): with state.data() as data: - name = data.get('name') - age = data.get('age') - color = data.get('color') + name = data.get("name") + age = data.get("age") + color = data.get("color") hobby = message.text # Get the hobby from the message text # Provide a fun fact based on color @@ -80,24 +108,36 @@ def finish(message: types.Message, state: StateContext): "Yellow": "Yellow is a cheerful color often associated with happiness.", "Purple": "Purple signifies royalty and luxury.", "Orange": "Orange is a vibrant color that stimulates enthusiasm.", - "Other": "Colors have various meanings depending on context." + "Other": "Colors have various meanings depending on context.", } - color_fact = color_facts.get(color, "Colors have diverse meanings, and yours is unique!") - - msg = (f"Thank you for sharing! Here is a summary of your information:\n" - f"First Name: {name}\n" - f"Age: {age}\n" - f"Favorite Color: {color}\n" - f"Fun Fact about your color: {color_fact}\n" - f"Favorite Hobby: {hobby}") - - bot.send_message(message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id) + color_fact = color_facts.get( + color, "Colors have diverse meanings, and yours is unique!" + ) + + msg = ( + f"Thank you for sharing! Here is a summary of your information:\n" + f"First Name: {name}\n" + f"Age: {age}\n" + f"Favorite Color: {color}\n" + f"Fun Fact about your color: {color_fact}\n" + f"Favorite Hobby: {hobby}" + ) + + bot.send_message( + message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id + ) state.delete() + # Handler for incorrect age input @bot.message_handler(state=MyStates.age, is_digit=False) def age_incorrect(message: types.Message): - bot.send_message(message.chat.id, 'Please enter a valid number for age.', reply_to_message_id=message.message_id) + bot.send_message( + message.chat.id, + "Please enter a valid number for age.", + reply_to_message_id=message.message_id, + ) + # Add custom filters bot.add_custom_filter(custom_filters.StateFilter(bot)) @@ -106,6 +146,7 @@ def age_incorrect(message: types.Message): # necessary for state parameter in handlers. from telebot.states.sync.middleware import StateMiddleware + bot.setup_middleware(StateMiddleware(bot)) # Start polling diff --git a/telebot/states/aio/__init__.py b/telebot/states/asyncio/__init__.py similarity index 100% rename from telebot/states/aio/__init__.py rename to telebot/states/asyncio/__init__.py diff --git a/telebot/states/aio/context.py b/telebot/states/asyncio/context.py similarity index 100% rename from telebot/states/aio/context.py rename to telebot/states/asyncio/context.py diff --git a/telebot/states/aio/middleware.py b/telebot/states/asyncio/middleware.py similarity index 100% rename from telebot/states/aio/middleware.py rename to telebot/states/asyncio/middleware.py From 303d11adf8d9c24e27239cbcd72fd1665a4c362b Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 27 Jul 2024 18:52:00 +0500 Subject: [PATCH 1645/1808] Improved typehints for types.py --- telebot/types.py | 1243 ++++++++++++++++++++++++---------------------- 1 file changed, 656 insertions(+), 587 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 6913452be..7cb7070a9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -5,7 +5,7 @@ import logging import os from pathlib import Path -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, Any, Tuple from abc import ABC try: @@ -26,7 +26,6 @@ class JsonSerializable(object): """ Subclasses of this class are guaranteed to be able to be converted to JSON format. All subclasses of this class must override to_json. - """ def to_json(self): @@ -45,7 +44,6 @@ class Dictionaryable(object): """ Subclasses of this class are guaranteed to be able to be converted to dictionary. All subclasses of this class must override to_dict. - """ def to_dict(self): @@ -236,29 +234,29 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan 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 - self.channel_post = channel_post - self.edited_channel_post = edited_channel_post - self.inline_query = inline_query - self.chosen_inline_result = chosen_inline_result - self.callback_query = callback_query - self.shipping_query = shipping_query - self.pre_checkout_query = pre_checkout_query - self.poll = poll - self.poll_answer = poll_answer - self.my_chat_member = my_chat_member - self.chat_member = chat_member - self.chat_join_request = chat_join_request - self.message_reaction = message_reaction - self.message_reaction_count = message_reaction_count - self.removed_chat_boost = removed_chat_boost - self.chat_boost = chat_boost - self.business_connection = business_connection - self.business_message = business_message - self.edited_business_message = edited_business_message - self.deleted_business_messages = deleted_business_messages + self.update_id: int = update_id + self.message: Optional[Message] = message + self.edited_message: Optional[Message] = edited_message + self.channel_post: Optional[Message] = channel_post + self.edited_channel_post: Optional[Message] = edited_channel_post + self.inline_query: Optional[InlineQuery] = inline_query + self.chosen_inline_result: Optional[ChosenInlineResult] = chosen_inline_result + self.callback_query: Optional[CallbackQuery] = callback_query + self.shipping_query: Optional[ShippingQuery] = shipping_query + self.pre_checkout_query: Optional[PreCheckoutQuery] = pre_checkout_query + self.poll: Optional[Poll] = poll + self.poll_answer: Optional[PollAnswer] = poll_answer + self.my_chat_member: Optional[ChatMemberUpdated] = my_chat_member + self.chat_member: Optional[ChatMemberUpdated] = chat_member + self.chat_join_request: Optional[ChatJoinRequest] = chat_join_request + self.message_reaction: Optional[MessageReactionUpdated] = message_reaction + self.message_reaction_count: Optional[MessageReactionCountUpdated] = message_reaction_count + self.removed_chat_boost: Optional[ChatBoostRemoved] = removed_chat_boost + self.chat_boost: Optional[ChatBoostUpdated] = chat_boost + self.business_connection: Optional[BusinessConnection] = business_connection + self.business_message: Optional[Message] = business_message + self.edited_business_message: Optional[Message] = edited_business_message + self.deleted_business_messages: Optional[BusinessMessagesDeleted] = deleted_business_messages @@ -384,8 +382,8 @@ def __init__(self, chat, from_user, user_chat_id, date, bio=None, invite_link=No self.chat: Chat = chat self.from_user: User = from_user self.date: str = date - self.bio: str = bio - self.invite_link: ChatInviteLink = invite_link + self.bio: Optional[str] = bio + self.invite_link: Optional[ChatInviteLink] = invite_link self.user_chat_id: int = user_chat_id class WebhookInfo(JsonDeserializable): @@ -438,15 +436,15 @@ def de_json(cls, json_string): def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, last_error_date=None, last_error_message=None, last_synchronization_error_date=None, max_connections=None, allowed_updates=None, **kwargs): - self.url = url - self.has_custom_certificate = has_custom_certificate - self.pending_update_count = pending_update_count - self.ip_address = ip_address - self.last_error_date = last_error_date - self.last_error_message = last_error_message - self.last_synchronization_error_date = last_synchronization_error_date - self.max_connections = max_connections - self.allowed_updates = allowed_updates + self.url: str = url + self.has_custom_certificate: bool = has_custom_certificate + self.pending_update_count: int = pending_update_count + self.ip_address: Optional[str] = ip_address + self.last_error_date: Optional[int] = last_error_date + self.last_error_message: Optional[str] = last_error_message + self.last_synchronization_error_date: Optional[int] = last_synchronization_error_date + self.max_connections: Optional[int] = max_connections + self.allowed_updates: Optional[int] = allowed_updates class User(JsonDeserializable, Dictionaryable, JsonSerializable): @@ -510,18 +508,18 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.id: int = id self.is_bot: bool = is_bot self.first_name: str = first_name - self.username: str = username - self.last_name: str = last_name - self.language_code: str = language_code - self.can_join_groups: bool = can_join_groups - self.can_read_all_group_messages: bool = can_read_all_group_messages - self.supports_inline_queries: bool = supports_inline_queries - self.is_premium: bool = is_premium - self.added_to_attachment_menu: bool = added_to_attachment_menu - self.can_connect_to_business: bool = can_connect_to_business + self.username: Optional[str] = username + self.last_name: Optional[str] = last_name + self.language_code: Optional[str] = language_code + self.can_join_groups: Optional[bool] = can_join_groups + self.can_read_all_group_messages: Optional[bool] = can_read_all_group_messages + self.supports_inline_queries: Optional[bool] = supports_inline_queries + self.is_premium: Optional[bool] = is_premium + self.added_to_attachment_menu: Optional[bool] = added_to_attachment_menu + self.can_connect_to_business: Optional[bool] = can_connect_to_business @property - def full_name(self): + def full_name(self) -> str: """ :return: User's full name """ @@ -756,48 +754,48 @@ def __init__(self, id, type, title=None, username=None, first_name=None, can_send_paid_media=None, **kwargs): self.id: int = id self.type: str = type - self.title: str = title - self.username: str = username - 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 - self.join_by_request: bool = join_by_request - self.has_private_forwards: bool = has_private_forwards - self.has_restricted_voice_and_video_messages: bool = has_restricted_voice_and_video_messages - self.description: str = description - self.invite_link: str = invite_link - self.pinned_message: Message = pinned_message - self.permissions: ChatPermissions = permissions - self.slow_mode_delay: int = slow_mode_delay - self.message_auto_delete_time: int = message_auto_delete_time - self.has_protected_content: bool = has_protected_content - self.sticker_set_name: str = sticker_set_name - self.can_set_sticker_set: bool = can_set_sticker_set - self.linked_chat_id: int = linked_chat_id - self.location: ChatLocation = location - self.active_usernames: List[str] = active_usernames - self.emoji_status_custom_emoji_id: str = emoji_status_custom_emoji_id - self.has_hidden_members: bool = has_hidden_members - self.has_aggressive_anti_spam_enabled: bool = has_aggressive_anti_spam_enabled - self.emoji_status_expiration_date: int = emoji_status_expiration_date - self.available_reactions: List[ReactionType] = available_reactions - self.accent_color_id: int = accent_color_id - self.background_custom_emoji_id: str = background_custom_emoji_id - self.profile_accent_color_id: int = profile_accent_color_id - self.profile_background_custom_emoji_id: str = profile_background_custom_emoji_id - self.has_visible_history: bool = has_visible_history - self.unrestrict_boost_count: int = unrestrict_boost_count - self.custom_emoji_sticker_set_name: str = custom_emoji_sticker_set_name - self.business_intro: BusinessIntro = business_intro - self.business_location: BusinessLocation = business_location - self.business_opening_hours: BusinessOpeningHours = business_opening_hours - self.personal_chat: Chat = personal_chat - self.birthdate: Birthdate = birthdate - self.can_send_paid_media: bool = can_send_paid_media + self.title: Optional[str] = title + self.username: Optional[str] = username + self.first_name: Optional[str] = first_name + self.last_name: Optional[str] = last_name + self.is_forum: Optional[bool] = is_forum + self.max_reaction_count: Optional[int] = max_reaction_count + self.photo: Optional[ChatPhoto] = photo + self.bio: Optional[str] = bio + self.join_to_send_messages: Optional[bool] = join_to_send_messages + self.join_by_request: Optional[bool] = join_by_request + self.has_private_forwards: Optional[bool] = has_private_forwards + self.has_restricted_voice_and_video_messages: Optional[bool] = has_restricted_voice_and_video_messages + self.description: Optional[str] = description + self.invite_link: Optional[str] = invite_link + self.pinned_message: Optional[Message] = pinned_message + self.permissions: Optional[ChatPermissions] = permissions + self.slow_mode_delay: Optional[int] = slow_mode_delay + self.message_auto_delete_time: Optional[int] = message_auto_delete_time + self.has_protected_content: Optional[bool] = has_protected_content + self.sticker_set_name: Optional[str] = sticker_set_name + self.can_set_sticker_set: Optional[bool] = can_set_sticker_set + self.linked_chat_id: Optional[int] = linked_chat_id + self.location: Optional[ChatLocation] = location + self.active_usernames: Optional[List[str]] = active_usernames + self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id + self.has_hidden_members: Optional[bool] = has_hidden_members + self.has_aggressive_anti_spam_enabled: Optional[bool] = has_aggressive_anti_spam_enabled + self.emoji_status_expiration_date: Optional[int] = emoji_status_expiration_date + self.available_reactions: Optional[List[ReactionType]] = available_reactions + self.accent_color_id: Optional[int] = accent_color_id + self.background_custom_emoji_id: Optional[str] = background_custom_emoji_id + self.profile_accent_color_id: Optional[int] = profile_accent_color_id + self.profile_background_custom_emoji_id: Optional[str] = profile_background_custom_emoji_id + self.has_visible_history: Optional[bool] = has_visible_history + self.unrestrict_boost_count: Optional[int] = unrestrict_boost_count + self.custom_emoji_sticker_set_name: Optional[str] = custom_emoji_sticker_set_name + self.business_intro: Optional[BusinessIntro] = business_intro + self.business_location: Optional[BusinessLocation] = business_location + self.business_opening_hours: Optional[BusinessOpeningHours] = business_opening_hours + self.personal_chat: Optional[Chat] = personal_chat + self.birthdate: Optional[Birthdate] = birthdate + self.can_send_paid_media: Optional[bool] = can_send_paid_media class Chat(ChatFullInfo): @@ -831,7 +829,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, message_id, **kwargs): - self.message_id = message_id + self.message_id: int = message_id class WebAppData(JsonDeserializable, Dictionaryable): @@ -858,8 +856,8 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, data, button_text, **kwargs): - self.data = data - self.button_text = button_text + self.data: str = data + self.button_text: str = button_text def to_dict(self): return {'data': self.data, 'button_text': self.button_text} @@ -1400,7 +1398,7 @@ def de_json(cls, json_string): return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod - def parse_chat(cls, chat): + def parse_chat(cls, chat) -> Union[User, GroupChat]: """ Parses chat. """ @@ -1410,7 +1408,7 @@ def parse_chat(cls, chat): return User.de_json(chat) @classmethod - def parse_photo(cls, photo_size_array): + def parse_photo(cls, photo_size_array) -> List[PhotoSize]: """ Parses photo array. """ @@ -1420,7 +1418,7 @@ def parse_photo(cls, photo_size_array): return ret @classmethod - def parse_entities(cls, message_entity_array): + def parse_entities(cls, message_entity_array) -> List[MessageEntity]: """ Parses message entity array. """ @@ -1433,7 +1431,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.content_type: str = content_type self.id: int = message_id # Lets fix the telegram usability ####up with ID in Message :) self.message_id: int = message_id - self.from_user: User = from_user + self.from_user: Optional[User] = from_user self.date: int = date self.chat: Chat = chat self.sender_chat: Optional[Chat] = None @@ -1507,20 +1505,28 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.show_caption_above_media: Optional[bool] = None self.paid_media : Optional[PaidMediaInfo] = None self.refunded_payment : Optional[RefundedPayment] = None + self.proximity_alert_triggered: Optional[ProximityAlertTriggered] = None + self.video_chat_scheduled: Optional[VideoChatScheduled] = None + self.video_chat_started: Optional[VideoChatStarted] = None + self.video_chat_ended: Optional[VideoChatEnded] = None + self.video_chat_participants_invited: Optional[VideoChatParticipantsInvited] = None + self.web_app_data: Optional[WebAppData] = None + self.message_auto_delete_timer_changed: Optional[MessageAutoDeleteTimerChanged] = None + for key in options: setattr(self, key, options[key]) self.json = json_string @property - def html_text(self): + def html_text(self) -> str: """ Returns html-rendered text. """ return apply_html_entities(self.text, self.entities, getattr(self, "custom_subs", None)) @property - def html_caption(self): + def html_caption(self) -> str: """ Returns html-rendered caption. """ @@ -1529,25 +1535,21 @@ def html_caption(self): @property def voice_chat_scheduled(self): logger.warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') - # noinspection PyUnresolvedReferences return self.video_chat_scheduled @property def voice_chat_started(self): logger.warning('The parameter "voice_chat_started" is deprecated, use "video_chat_started" instead') - # noinspection PyUnresolvedReferences return self.video_chat_started @property def voice_chat_ended(self): logger.warning('The parameter "voice_chat_ended" is deprecated, use "video_chat_ended" instead') - # noinspection PyUnresolvedReferences return self.video_chat_ended @property def voice_chat_participants_invited(self): logger.warning('The parameter "voice_chat_participants_invited" is deprecated, use "video_chat_participants_invited" instead') - # noinspection PyUnresolvedReferences return self.video_chat_participants_invited @property @@ -1671,7 +1673,7 @@ def __init__(self, type, offset, length, url=None, user=None, language=None, cus self.url: str = url self.user: User = user self.language: str = language - self.custom_emoji_id: str = custom_emoji_id + self.custom_emoji_id: Optional[str] = custom_emoji_id def to_json(self): return json.dumps(self.to_dict()) @@ -1755,7 +1757,7 @@ def __init__(self, file_id, file_unique_id, width, height, file_size=None, **kwa self.file_unique_id: str = file_unique_id self.width: int = width self.height: int = height - self.file_size: int = file_size + self.file_size: Optional[int] = file_size class Audio(JsonDeserializable): @@ -1812,15 +1814,15 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.duration: int = duration - self.performer: str = performer - self.title: str = title - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size - self.thumbnail: PhotoSize = thumbnail + self.performer: Optional[str] = performer + self.title: Optional[str] = title + self.file_name: Optional[str] = file_name + self.mime_type: Optional[str] = mime_type + self.file_size: Optional[int] = file_size + self.thumbnail: Optional[PhotoSize] = thumbnail @property - def thumb(self): + def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -1862,8 +1864,8 @@ def __init__(self, file_id, file_unique_id, duration, mime_type=None, file_size= self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.duration: int = duration - self.mime_type: str = mime_type - self.file_size: int = file_size + self.mime_type: Optional[str] = mime_type + self.file_size: Optional[int] = file_size class Document(JsonDeserializable): @@ -1909,13 +1911,13 @@ def de_json(cls, json_string): def __init__(self, file_id, file_unique_id, thumbnail=None, file_name=None, mime_type=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id - self.thumbnail: PhotoSize = thumbnail - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size + self.thumbnail: Optional[PhotoSize] = thumbnail + self.file_name: Optional[str] = file_name + self.mime_type: Optional[str] = mime_type + self.file_size: Optional[int] = file_size @property - def thumb(self): + def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -1974,12 +1976,12 @@ def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=N self.height: int = height self.duration: int = duration self.thumbnail: PhotoSize = thumbnail - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size + self.file_name: Optional[str] = file_name + self.mime_type: Optional[str] = mime_type + self.file_size: Optional[int] = file_size @property - def thumb(self): + def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -2025,11 +2027,11 @@ def __init__(self, file_id, file_unique_id, length, duration, thumbnail=None, fi self.file_unique_id: str = file_unique_id self.length: int = length self.duration: int = duration - self.thumbnail: PhotoSize = thumbnail - self.file_size: int = file_size + self.thumbnail: Optional[PhotoSize] = thumbnail + self.file_size: Optional[int] = file_size @property - def thumb(self): + def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -2069,9 +2071,9 @@ def de_json(cls, json_string): def __init__(self, phone_number, first_name, last_name=None, user_id=None, vcard=None, **kwargs): self.phone_number: str = phone_number self.first_name: str = first_name - self.last_name: str = last_name - self.user_id: int = user_id - self.vcard: str = vcard + self.last_name: Optional[str] = last_name + self.user_id: Optional[int] = user_id + self.vcard: Optional[str] = vcard class Location(JsonDeserializable, JsonSerializable, Dictionaryable): @@ -2113,10 +2115,10 @@ def __init__(self, longitude, latitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None, **kwargs): self.longitude: float = longitude self.latitude: float = latitude - self.horizontal_accuracy: float = horizontal_accuracy - self.live_period: int = live_period - self.heading: int = heading - self.proximity_alert_radius: int = proximity_alert_radius + self.horizontal_accuracy: Optional[float] = horizontal_accuracy + self.live_period: Optional[int] = live_period + self.heading: Optional[int] = heading + self.proximity_alert_radius: Optional[int] = proximity_alert_radius def to_json(self): return json.dumps(self.to_dict()) @@ -2175,10 +2177,10 @@ def __init__(self, location, title, address, foursquare_id=None, foursquare_type self.location: Location = location self.title: str = title self.address: str = address - self.foursquare_id: str = foursquare_id - self.foursquare_type: str = foursquare_type - self.google_place_id: str = google_place_id - self.google_place_type: str = google_place_type + self.foursquare_id: Optional[str] = foursquare_id + self.foursquare_type: Optional[str] = foursquare_type + self.google_place_id: Optional[str] = google_place_id + self.google_place_type: Optional[str] = google_place_type class UserProfilePhotos(JsonDeserializable): @@ -2244,8 +2246,8 @@ def de_json(cls, json_string): def __init__(self, file_id, file_unique_id, file_size=None, file_path=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id - self.file_size: int = file_size - self.file_path: str = file_path + self.file_size: Optional[int] = file_size + self.file_path: Optional[str] = file_path # noinspection PyUnresolvedReferences @@ -2272,8 +2274,8 @@ class ForceReply(JsonSerializable): :rtype: :class:`telebot.types.ForceReply` """ def __init__(self, selective: Optional[bool]=None, input_field_placeholder: Optional[str]=None): - self.selective: bool = selective - self.input_field_placeholder: str = input_field_placeholder + self.selective: Optional[bool] = selective + self.input_field_placeholder: Optional[str] = input_field_placeholder def to_json(self): json_dict = {'force_reply': True} @@ -2307,8 +2309,8 @@ class ReplyKeyboardRemove(JsonSerializable): :return: Instance of the class :rtype: :class:`telebot.types.ReplyKeyboardRemove` """ - def __init__(self, selective=None): - self.selective: bool = selective + def __init__(self, selective: Optional[bool]=None): + self.selective: Optional[bool] = selective def to_json(self): json_dict = {'remove_keyboard': True} @@ -2408,15 +2410,15 @@ def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Opti logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - self.resize_keyboard: bool = resize_keyboard - self.one_time_keyboard: bool = one_time_keyboard - self.selective: bool = selective - self.row_width: int = row_width - self.input_field_placeholder: str = input_field_placeholder + self.resize_keyboard: Optional[bool] = resize_keyboard + self.one_time_keyboard: Optional[bool] = one_time_keyboard + self.selective: Optional[bool] = selective + self.row_width: Optional[int] = row_width + self.input_field_placeholder: Optional[str] = input_field_placeholder self.keyboard: List[List[KeyboardButton]] = [] - self.is_persistent: bool = is_persistent + self.is_persistent: Optional[bool] = is_persistent - def add(self, *args, row_width=None): + def add(self, *args, row_width=None) -> 'ReplyKeyboardMarkup': """ This function adds strings to the keyboard, while not exceeding row_width. E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} @@ -2455,7 +2457,7 @@ def add(self, *args, row_width=None): return self - def row(self, *args): + def row(self, *args) -> 'ReplyKeyboardMarkup': """ Adds a list of KeyboardButton to the keyboard. This function does not consider row_width. ReplyKeyboardMarkup#row("A")#row("B", "C")#to_json() outputs '{keyboard: [["A"], ["B", "C"]]}' @@ -2713,12 +2715,12 @@ def __init__(self, text: str, request_contact: Optional[bool]=None, web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUser]=None, request_chat: Optional[KeyboardButtonRequestChat]=None, request_users: Optional[KeyboardButtonRequestUsers]=None): self.text: str = text - self.request_contact: bool = request_contact - self.request_location: bool = request_location - self.request_poll: KeyboardButtonPollType = request_poll - self.web_app: WebAppInfo = web_app - self.request_chat: KeyboardButtonRequestChat = request_chat - self.request_users: KeyboardButtonRequestUsers = request_users + self.request_contact: Optional[bool] = request_contact + self.request_location: Optional[bool] = request_location + self.request_poll: Optional[KeyboardButtonPollType] = request_poll + self.web_app: Optional[WebAppInfo] = web_app + self.request_chat: Optional[KeyboardButtonRequestChat] = request_chat + self.request_users: Optional[KeyboardButtonRequestUsers] = request_users if request_user is not None: logger.warning('The parameter "request_user" is deprecated, use "request_users" instead') if self.request_users is None: @@ -2795,7 +2797,7 @@ def __init__(self, keyboard=None, row_width=3): self.row_width: int = row_width self.keyboard: List[List[InlineKeyboardButton]] = keyboard or [] - def add(self, *args, row_width=None): + def add(self, *args, row_width=None) -> 'InlineKeyboardMarkup': """ This method adds buttons to the keyboard without exceeding row_width. @@ -2829,7 +2831,7 @@ def add(self, *args, row_width=None): return self - def row(self, *args): + def row(self, *args) -> 'InlineKeyboardMarkup': """ Adds a list of InlineKeyboardButton to the keyboard. This method does not consider row_width. @@ -2922,18 +2924,20 @@ def de_json(cls, json_string): return cls(**obj) - def __init__(self, text, url=None, callback_data=None, web_app=None, switch_inline_query=None, - switch_inline_query_current_chat=None, switch_inline_query_chosen_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): + def __init__(self, text: str, url: Optional[str]=None, callback_data: Optional[str]=None, web_app: Optional[WebAppInfo]=None, + switch_inline_query: Optional[str]=None, switch_inline_query_current_chat: Optional[str]=None, + switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat]=None, callback_game=None, pay: Optional[bool]=None, + login_url: Optional[LoginUrl]=None, **kwargs): self.text: str = text - self.url: str = url - self.callback_data: str = callback_data - self.web_app: WebAppInfo = web_app - self.switch_inline_query: str = switch_inline_query - self.switch_inline_query_current_chat: str = switch_inline_query_current_chat - self.switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat = switch_inline_query_chosen_chat + self.url: Optional[str] = url + self.callback_data: Optional[str] = callback_data + self.web_app: Optional[WebAppInfo] = web_app + self.switch_inline_query: Optional[str] = switch_inline_query + self.switch_inline_query_current_chat: Optional[str] = switch_inline_query_current_chat + self.switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat] = switch_inline_query_chosen_chat self.callback_game = callback_game # Not Implemented - self.pay: bool = pay - self.login_url: LoginUrl = login_url + self.pay: Optional[bool] = pay + self.login_url: Optional[LoginUrl] = login_url def to_json(self): return json.dumps(self.to_dict()) @@ -2995,11 +2999,12 @@ def de_json(cls, json_string): obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) - def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None, **kwargs): + def __init__(self, url: str, forward_text: Optional[str]=None, bot_username: Optional[str]=None, request_write_access: Optional[bool]=None, + **kwargs): self.url: str = url - self.forward_text: str = forward_text - self.bot_username: str = bot_username - self.request_write_access: bool = request_write_access + self.forward_text: Optional[str] = forward_text + self.bot_username: Optional[str] = bot_username + self.request_write_access: Optional[bool] = request_write_access def to_json(self): return json.dumps(self.to_dict()) @@ -3073,10 +3078,10 @@ def __init__( self.id: int = id self.from_user: User = from_user self.message: Union[Message, InaccessibleMessage] = message - self.inline_message_id: str = inline_message_id - self.chat_instance: str = chat_instance - self.data: str = data - self.game_short_name: str = game_short_name + self.inline_message_id: Optional[str] = inline_message_id + self.chat_instance: Optional[str] = chat_instance + self.data: Optional[str] = data + self.game_short_name: Optional[str] = game_short_name self.json = json_string @@ -3510,31 +3515,31 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send can_add_web_page_previews=None, can_change_info=None, can_invite_users=None, can_pin_messages=None, can_manage_topics=None, **kwargs): - self.can_send_messages: bool = can_send_messages - self.can_send_polls: bool = can_send_polls - self.can_send_other_messages: bool = can_send_other_messages - self.can_add_web_page_previews: bool = can_add_web_page_previews - self.can_change_info: bool = can_change_info - self.can_invite_users: bool = can_invite_users - self.can_pin_messages: bool = can_pin_messages - self.can_manage_topics: bool = can_manage_topics - self.can_send_audios: bool = can_send_audios - self.can_send_documents: bool = can_send_documents - self.can_send_photos: bool = can_send_photos - self.can_send_videos: bool = can_send_videos - self.can_send_video_notes: bool = can_send_video_notes - self.can_send_voice_notes: bool = can_send_voice_notes + self.can_send_messages: Optional[bool] = can_send_messages + self.can_send_polls: Optional[bool] = can_send_polls + self.can_send_other_messages: Optional[bool] = can_send_other_messages + self.can_add_web_page_previews: Optional[bool] = can_add_web_page_previews + self.can_change_info: Optional[bool] = can_change_info + self.can_invite_users: Optional[bool] = can_invite_users + self.can_pin_messages: Optional[bool] = can_pin_messages + self.can_manage_topics: Optional[bool] = can_manage_topics + self.can_send_audios: Optional[bool] = can_send_audios + self.can_send_documents: Optional[bool] = can_send_documents + self.can_send_photos: Optional[bool] = can_send_photos + self.can_send_videos: Optional[bool] = can_send_videos + self.can_send_video_notes: Optional[bool] = can_send_video_notes + self.can_send_voice_notes: Optional[bool] = can_send_voice_notes if kwargs.get("de_json", False) and can_send_media_messages is not None: # Telegram passes can_send_media_messages in Chat.permissions. Temporary created parameter "de_json" allows avoid # deprection warning and individual parameters overriding. logger.warning('The parameter "can_send_media_messages" is deprecated. Use individual parameters like "can_send_audios", "can_send_documents" etc.') - self.can_send_audios = can_send_media_messages - self.can_send_documents = can_send_media_messages - self.can_send_photos = can_send_media_messages - self.can_send_videos = can_send_media_messages - self.can_send_video_notes = can_send_media_messages - self.can_send_voice_notes = can_send_media_messages + self.can_send_audios: Optional[bool] = can_send_media_messages + self.can_send_documents: Optional[bool] = can_send_media_messages + self.can_send_photos: Optional[bool] = can_send_media_messages + self.can_send_videos: Optional[bool] = can_send_media_messages + self.can_send_video_notes: Optional[bool] = can_send_media_messages + self.can_send_voice_notes: Optional[bool] = can_send_media_messages def to_json(self): @@ -3765,7 +3770,7 @@ class BotCommandScopeChat(BotCommandScope): :return: Instance of the class :rtype: :class:`telebot.types.BotCommandScopeChat` """ - def __init__(self, chat_id=None): + def __init__(self, chat_id: Optional[Union[str, int]]=None): super(BotCommandScopeChat, self).__init__(type='chat', chat_id=chat_id) @@ -3786,7 +3791,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): :return: Instance of the class :rtype: :class:`telebot.types.BotCommandScopeChatAdministrators` """ - def __init__(self, chat_id=None): + def __init__(self, chat_id: Optional[Union[str, int]]=None): super(BotCommandScopeChatAdministrators, self).__init__(type='chat_administrators', chat_id=chat_id) @@ -3810,7 +3815,7 @@ class BotCommandScopeChatMember(BotCommandScope): :return: Instance of the class :rtype: :class:`telebot.types.BotCommandScopeChatMember` """ - def __init__(self, chat_id=None, user_id=None): + def __init__(self, chat_id: Optional[Union[str, int]]=None, user_id: Optional[Union[str, int]]=None): super(BotCommandScopeChatMember, self).__init__(type='chat_member', chat_id=chat_id, user_id=user_id) @@ -3861,8 +3866,8 @@ def __init__(self, id, from_user, query, offset, chat_type=None, location=None, self.from_user: User = from_user self.query: str = query self.offset: str = offset - self.chat_type: str = chat_type - self.location: Location = location + self.chat_type: Optional[str] = chat_type + self.location: Optional[Location] = location class InputTextMessageContent(Dictionaryable): @@ -3891,20 +3896,19 @@ class InputTextMessageContent(Dictionaryable): :return: Instance of the class :rtype: :class:`telebot.types.InputTextMessageContent` """ - def __init__( - self, message_text, parse_mode=None, entities=None, disable_web_page_preview=None, - link_preview_options=None): + def __init__(self, message_text: str, parse_mode: Optional[str] = None, entities: Optional[List[MessageEntity]] = None, + disable_web_page_preview: Optional[bool] = None, link_preview_options: Optional[LinkPreviewOptions] = None): self.message_text: str = message_text - self.parse_mode: str = parse_mode - self.entities: List[MessageEntity] = entities - self.link_preview_options: LinkPreviewOptions = link_preview_options + self.parse_mode: Optional[str] = parse_mode + self.entities: Optional[List[MessageEntity]] = entities + self.link_preview_options: Optional[LinkPreviewOptions] = link_preview_options if disable_web_page_preview is not None: logger.warning('The parameter "disable_web_page_preview" is deprecated. Use "link_preview_options" instead.') if link_preview_options: logger.warning('Both "link_preview_options" and "disable_web_page_preview" parameters are set: conflicting, "disable_web_page_preview" is deprecated') else: - self.link_preview_options: LinkPreviewOptions = LinkPreviewOptions(is_disabled=disable_web_page_preview) + self.link_preview_options: Optional[LinkPreviewOptions] = LinkPreviewOptions(is_disabled=disable_web_page_preview) def to_dict(self): json_dict = {'message_text': self.message_text} @@ -3947,10 +3951,10 @@ class InputLocationMessageContent(Dictionaryable): def __init__(self, latitude, longitude, horizontal_accuracy=None, live_period=None, heading=None, proximity_alert_radius=None): self.latitude: float = latitude self.longitude: float = longitude - self.horizontal_accuracy: float = horizontal_accuracy - self.live_period: int = live_period - self.heading: int = heading - self.proximity_alert_radius: int = proximity_alert_radius + self.horizontal_accuracy: Optional[float] = horizontal_accuracy + self.live_period: Optional[int] = live_period + self.heading: Optional[int] = heading + self.proximity_alert_radius: Optional[int] = proximity_alert_radius def to_dict(self): json_dict = {'latitude': self.latitude, 'longitude': self.longitude} @@ -4005,10 +4009,10 @@ def __init__(self, latitude, longitude, title, address, foursquare_id=None, four self.longitude: float = longitude self.title: str = title self.address: str = address - self.foursquare_id: str = foursquare_id - self.foursquare_type: str = foursquare_type - self.google_place_id: str = google_place_id - self.google_place_type: str = google_place_type + self.foursquare_id: Optional[str] = foursquare_id + self.foursquare_type: Optional[str] = foursquare_type + self.google_place_id: Optional[str] = google_place_id + self.google_place_type: Optional[str] = google_place_type def to_dict(self): json_dict = { @@ -4052,8 +4056,8 @@ class InputContactMessageContent(Dictionaryable): def __init__(self, phone_number, first_name, last_name=None, vcard=None): self.phone_number: str = phone_number self.first_name: str = first_name - self.last_name: str = last_name - self.vcard: str = vcard + self.last_name: Optional[str] = last_name + self.vcard: Optional[str] = vcard def to_dict(self): json_dict = {'phone_number': self.phone_number, 'first_name': self.first_name} @@ -4080,8 +4084,9 @@ class InputInvoiceMessageContent(Dictionaryable): internal processes. :type payload: :obj:`str` - :param provider_token: Payment provider token, obtained via @BotFather - :type provider_token: :obj:`str` + :param provider_token: Payment provider token, obtained via @BotFather; To create invoice in stars, + provide an empty token. + :type provider_token: :obj:`str` or :obj:`None` :param currency: Three-letter ISO 4217 currency code, see more on currencies :type currency: :obj:`str` @@ -4143,16 +4148,16 @@ class InputInvoiceMessageContent(Dictionaryable): :return: Instance of the class :rtype: :class:`telebot.types.InputInvoiceMessageContent` """ - def __init__(self, title, description, payload, provider_token, currency, prices, - max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, - photo_url=None, photo_size=None, photo_width=None, photo_height=None, - need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, - send_phone_number_to_provider=None, send_email_to_provider=None, - is_flexible=None): + + def __init__(self, title: str, description: str, payload: str, provider_token: Optional[str], currency: str, prices: List[LabeledPrice], + max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]] = None, provider_data: Optional[str] = None, + photo_url: Optional[str] = None, photo_size: Optional[int] = None, photo_width: Optional[int] = None, photo_height: Optional[int] = None, + need_name: Optional[bool] = None, need_phone_number: Optional[bool] = None, need_email: Optional[bool] = None, need_shipping_address: Optional[bool] = None, + send_phone_number_to_provider: Optional[bool] = None, send_email_to_provider: Optional[bool] = None, is_flexible: Optional[bool] = None): self.title: str = title self.description: str = description self.payload: str = payload - self.provider_token: str = provider_token + self.provider_token: Optional[str] = provider_token self.currency: str = currency self.prices: List[LabeledPrice] = prices self.max_tip_amount: Optional[int] = max_tip_amount @@ -4209,6 +4214,7 @@ def to_dict(self): json_dict['is_flexible'] = self.is_flexible return json_dict +InputMessageContent = Union[InputTextMessageContent, InputLocationMessageContent, InputVenueMessageContent, InputContactMessageContent, InputInvoiceMessageContent] class ChosenInlineResult(JsonDeserializable): """ @@ -4247,8 +4253,8 @@ def de_json(cls, json_string): def __init__(self, result_id, from_user, query, location=None, inline_message_id=None, **kwargs): self.result_id: str = result_id self.from_user: User = from_user - self.location: Location = location - self.inline_message_id: str = inline_message_id + self.location: Optional[Location] = location + self.inline_message_id: Optional[str] = inline_message_id self.query: str = query @@ -4279,17 +4285,17 @@ class InlineQueryResultBase(ABC, Dictionaryable, JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresult """ - # 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 __init__(self, type: str, id: str, title: Optional[str] = None, caption: Optional[str] = None, input_message_content: Optional[InputMessageContent] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None): + self.type: str = type + self.id: str = id + self.title: Optional[str] = title + self.caption: Optional[str] = caption + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.parse_mode: Optional[str] = parse_mode def to_json(self): return json.dumps(self.to_dict()) @@ -4334,7 +4340,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, inline_message_id=None, **kwargs): - self.inline_message_id = inline_message_id + self.inline_message_id: Optional[str] = inline_message_id def to_dict(self): json_dict = {} @@ -4386,28 +4392,32 @@ class InlineQueryResultArticle(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultArticle` """ - def __init__(self, id, title, input_message_content, reply_markup=None, - url=None, hide_url=None, description=None, thumbnail_url=None, thumbnail_width=None, thumbnail_height=None): + + def __init__(self, id: str, title: str, input_message_content: InputMessageContent, reply_markup: Optional[InlineKeyboardMarkup] = None, + url: Optional[str] = None, hide_url: Optional[bool] = None, description: Optional[str] = None, + thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = None): + 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 - self.thumbnail_url = thumbnail_url - self.thumbnail_width = thumbnail_width - self.thumbnail_height = thumbnail_height + self.url: Optional[str] = url + self.hide_url: Optional[bool] = hide_url + self.description: Optional[str] = description + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height + @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property - def thumb_width(self): + def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property - def thumb_height(self): + def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height @@ -4482,21 +4492,23 @@ class InlineQueryResultPhoto(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultPhoto` """ - def __init__(self, id, photo_url, thumbnail_url, photo_width=None, photo_height=None, title=None, - description=None, caption=None, caption_entities=None, parse_mode=None, reply_markup=None, input_message_content=None, - show_caption_above_media=None): + def __init__(self, id: str, photo_url: str, thumbnail_url: str, photo_width: Optional[int] = None, photo_height: Optional[int] = None, + title: Optional[str] = None, description: Optional[str] = None, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, + show_caption_above_media: Optional[bool] = None): 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.thumbnail_url = thumbnail_url - self.photo_width = photo_width - self.photo_height = photo_height - self.description = description + self.photo_url: str = photo_url + self.thumbnail_url: str = thumbnail_url + self.photo_width: Optional[int] = photo_width + self.photo_height: Optional[int] = photo_height + self.description: Optional[str] = description self.show_caption_above_media: Optional[bool] = show_caption_above_media + @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @@ -4572,28 +4584,30 @@ class InlineQueryResultGif(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultGif` """ - def __init__(self, id, gif_url, thumbnail_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, - thumbnail_mime_type=None, show_caption_above_media=None): + def __init__(self, id: str, gif_url: str, thumbnail_url: str, gif_width: Optional[int] = None, gif_height: Optional[int] = None, + title: Optional[str] = None, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, + gif_duration: Optional[int] = None, parse_mode: Optional[str] = None, thumbnail_mime_type: Optional[str] = None, + show_caption_above_media: Optional[bool] = None): + 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.thumbnail_url = thumbnail_url - self.gif_duration = gif_duration - self.thumbnail_mime_type = thumbnail_mime_type + self.gif_url: str = gif_url + self.thumbnail_url: str = thumbnail_url + self.gif_width: Optional[int] = gif_width + self.gif_height: Optional[int] = gif_height + self.gif_duration: Optional[int] = gif_duration + self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type self.show_caption_above_media: Optional[bool] = show_caption_above_media @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property - def thumb_mime_type(self): + def thumb_mime_type(self) -> str: logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') return self.thumbnail_mime_type @@ -4671,28 +4685,29 @@ class InlineQueryResultMpeg4Gif(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultMpeg4Gif` """ - def __init__(self, id, mpeg4_url, thumbnail_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, - thumbnail_mime_type=None, show_caption_above_media=None): + def __init__(self, id: str, mpeg4_url: str, thumbnail_url: str, mpeg4_width: Optional[int] = None, mpeg4_height: Optional[int] = None, + title: Optional[str] = None, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, + mpeg4_duration: Optional[int] = None, thumbnail_mime_type: Optional[str] = None, show_caption_above_media: Optional[bool] = None): + 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.thumbnail_url = thumbnail_url - self.mpeg4_duration = mpeg4_duration - self.thumbnail_mime_type = thumbnail_mime_type + self.mpeg4_url: str = mpeg4_url + self.thumbnail_url: str = thumbnail_url + self.mpeg4_width: Optional[int] = mpeg4_width + self.mpeg4_height: Optional[int] = mpeg4_height + self.mpeg4_duration: Optional[int] = mpeg4_duration + self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type self.show_caption_above_media: Optional[bool] = show_caption_above_media @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property - def thumb_mime_type(self): + def thumb_mime_type(self) -> str: logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') return self.thumbnail_mime_type @@ -4774,24 +4789,26 @@ class InlineQueryResultVideo(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultVideo` """ - def __init__(self, id, video_url, mime_type, thumbnail_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, show_caption_above_media=None): + def __init__(self, id: str, video_url: str, mime_type: str, thumbnail_url: str, title: str, + caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, video_width: Optional[int] = None, video_height: Optional[int] = None, + video_duration: Optional[int] = None, description: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): + 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.thumbnail_url = thumbnail_url - self.video_width = video_width - self.video_height = video_height - self.video_duration = video_duration - self.description = description + self.video_url: str = video_url + self.mime_type: str = mime_type + self.thumbnail_url: str = thumbnail_url + self.video_width: Optional[int] = video_width + self.video_height: Optional[int] = video_height + self.video_duration: Optional[int] = video_duration + self.description: Optional[str] = description self.show_caption_above_media: Optional[bool] = show_caption_above_media @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @@ -4856,15 +4873,16 @@ class InlineQueryResultAudio(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultAudio` """ - 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): + def __init__(self, id: str, audio_url: str, title: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, performer: Optional[str] = None, audio_duration: Optional[int] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = 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.performer = performer - self.audio_duration = audio_duration + self.audio_url: str = audio_url + self.performer: Optional[str] = performer + self.audio_duration: Optional[int] = audio_duration def to_dict(self): json_dict = super().to_dict() @@ -4918,13 +4936,15 @@ class InlineQueryResultVoice(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultVoice` """ - 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): + def __init__(self, id: str, voice_url: str, title: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, voice_duration: Optional[int] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = 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.voice_duration = voice_duration + self.voice_url: str = voice_url + self.voice_duration: Optional[int] = voice_duration def to_dict(self): json_dict = super().to_dict() @@ -4988,31 +5008,34 @@ class InlineQueryResultDocument(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultDocument` """ - 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, - thumbnail_url=None, thumbnail_width=None, thumbnail_height=None): + def __init__(self, id: str, title: str, document_url: str, mime_type: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, description: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = None, thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, + thumbnail_height: Optional[int] = 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.description = description - self.thumbnail_url = thumbnail_url - self.thumbnail_width = thumbnail_width - self.thumbnail_height = thumbnail_height + self.document_url: str = document_url + self.mime_type: str = mime_type + self.description: Optional[str] = description + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height + @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property - def thumb_width(self): + def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property - def thumb_height(self): + def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height @@ -5083,32 +5106,35 @@ class InlineQueryResultLocation(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultLocation` """ - def __init__(self, id, title, latitude, longitude, horizontal_accuracy, live_period=None, reply_markup=None, - input_message_content=None, thumbnail_url=None, thumbnail_width=None, thumbnail_height=None, heading=None, proximity_alert_radius = None): + def __init__(self, id: str, title: str, latitude: float, longitude: float, horizontal_accuracy: float, live_period: Optional[int] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, + thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = None, + heading: Optional[int] = None, proximity_alert_radius: Optional[int] = 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.heading: int = heading - self.proximity_alert_radius: int = proximity_alert_radius - self.thumbnail_url = thumbnail_url - self.thumbnail_width = thumbnail_width - self.thumbnail_height = thumbnail_height + self.latitude: float = latitude + self.longitude: float = longitude + self.horizontal_accuracy: float = horizontal_accuracy + self.live_period: Optional[int] = live_period + self.heading: Optional[int] = heading + self.proximity_alert_radius: Optional[int] = proximity_alert_radius + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property - def thumb_width(self): + def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property - def thumb_height(self): + def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height @@ -5189,34 +5215,36 @@ class InlineQueryResultVenue(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultVenue` """ - def __init__(self, id, title, latitude, longitude, address, foursquare_id=None, foursquare_type=None, - reply_markup=None, input_message_content=None, thumbnail_url=None, - thumbnail_width=None, thumbnail_height=None, google_place_id=None, google_place_type=None): + def __init__(self, id: str, title: str, latitude: float, longitude: float, address: str, foursquare_id: Optional[str] = None, + foursquare_type: Optional[str] = None, google_place_id: Optional[str] = None, google_place_type: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, + thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = None): + 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.google_place_id = google_place_id - self.google_place_type = google_place_type - self.thumbnail_url = thumbnail_url - self.thumbnail_width = thumbnail_width - self.thumbnail_height = thumbnail_height + self.latitude: float = latitude + self.longitude: float = longitude + self.address: str = address + self.foursquare_id: Optional[str] = foursquare_id + self.foursquare_type: Optional[str] = foursquare_type + self.google_place_id: Optional[str] = google_place_id + self.google_place_type: Optional[str] = google_place_type + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property - def thumb_width(self): + def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property - def thumb_height(self): + def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height @@ -5285,31 +5313,33 @@ class InlineQueryResultContact(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultContact` """ - def __init__(self, id, phone_number, first_name, last_name=None, vcard=None, - reply_markup=None, input_message_content=None, - thumbnail_url=None, thumbnail_width=None, thumbnail_height=None): + + def __init__(self, id: str, phone_number: str, first_name: str, last_name: Optional[str] = None, vcard: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, + thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = 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.thumbnail_url = thumbnail_url - self.thumbnail_width = thumbnail_width - self.thumbnail_height = thumbnail_height + self.phone_number: str = phone_number + self.first_name: str = first_name + self.last_name: Optional[str] = last_name + self.vcard: Optional[str] = vcard + self.thumbnail_url: Optional[str] = thumbnail_url + self.thumbnail_width: Optional[int] = thumbnail_width + self.thumbnail_height: Optional[int] = thumbnail_height + @property - def thumb_url(self): + def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property - def thumb_width(self): + def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property - def thumb_height(self): + def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height @@ -5352,9 +5382,9 @@ class InlineQueryResultGame(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultGame` """ - def __init__(self, id, game_short_name, reply_markup=None): + def __init__(self, id: str, game_short_name: str, reply_markup: Optional[InlineKeyboardMarkup] = None): super().__init__('game', id, reply_markup = reply_markup) - self.game_short_name = game_short_name + self.game_short_name: str = game_short_name def to_dict(self): json_dict = super().to_dict() @@ -5367,17 +5397,17 @@ class InlineQueryResultCachedBase(ABC, JsonSerializable): Base class of all InlineQueryResultCached* classes. """ def __init__(self): - self.type = None - self.id = None - self.title = None - self.description = None - self.caption = None - self.reply_markup = None - self.input_message_content = None - self.parse_mode = None - self.caption_entities = None - self.payload_dic = {} - self.show_caption_above_media = None + self.type: str = None + self.id: str = None + self.title: Optional[str] = None + self.description: Optional[str] = None + self.caption: Optional[str] = None + self.reply_markup: Optional[InlineKeyboardMarkup] = None + self.input_message_content: Optional[InputMessageContent] = None + self.parse_mode: Optional[str] = None + self.caption_entities: Optional[List[MessageEntity]] = None + self.payload_dic: Dict[str] = {} + self.show_caption_above_media: Optional[bool] = None def to_json(self): json_dict = self.payload_dic @@ -5447,24 +5477,27 @@ class InlineQueryResultCachedPhoto(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedPhoto` """ - 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, show_caption_above_media=None): + def __init__(self, id: str, photo_file_id: str, title: Optional[str] = None, description: Optional[str] = None, + caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): + 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.type: str = 'photo' + self.id: str = id + self.photo_file_id: str = photo_file_id + self.title: Optional[str] = title + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.parse_mode: Optional[str] = parse_mode self.payload_dic['photo_file_id'] = photo_file_id self.show_caption_above_media: Optional[bool] = show_caption_above_media + # noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedGif(InlineQueryResultCachedBase): """ @@ -5506,20 +5539,22 @@ class InlineQueryResultCachedGif(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedGif` """ - 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, show_caption_above_media=None): + def __init__(self, id: str, gif_file_id: str, title: Optional[str] = None, description: Optional[str] = None, + caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = 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.type: str = 'gif' + self.id: str = id + self.gif_file_id: str = gif_file_id + self.title: Optional[str] = title + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.parse_mode: Optional[str] = parse_mode self.payload_dic['gif_file_id'] = gif_file_id self.show_caption_above_media: Optional[bool] = show_caption_above_media @@ -5565,20 +5600,22 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedMpeg4Gif` """ - 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, show_caption_above_media=None): + def __init__(self, id: str, mpeg4_file_id: str, title: Optional[str] = None, description: Optional[str] = None, + caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): + 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.type: str = 'mpeg4_gif' + self.id: str = id + self.mpeg4_file_id: str = mpeg4_file_id + self.title: Optional[str] = title + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.parse_mode: Optional[str] = parse_mode self.payload_dic['mpeg4_file_id'] = mpeg4_file_id self.show_caption_above_media: Optional[bool] = show_caption_above_media @@ -5607,16 +5644,18 @@ class InlineQueryResultCachedSticker(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedSticker` """ - def __init__(self, id, sticker_file_id, reply_markup=None, input_message_content=None): + def __init__(self, id: str, sticker_file_id: str, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = None): InlineQueryResultCachedBase.__init__(self) - self.type = 'sticker' - self.id = id - self.sticker_file_id = sticker_file_id - self.reply_markup = reply_markup - self.input_message_content = input_message_content + self.type: str = 'sticker' + self.id: str = id + self.sticker_file_id: str = sticker_file_id + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content self.payload_dic['sticker_file_id'] = sticker_file_id + # noinspection PyUnresolvedReferences,PyShadowingBuiltins class InlineQueryResultCachedDocument(InlineQueryResultCachedBase): """ @@ -5659,20 +5698,23 @@ class InlineQueryResultCachedDocument(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedDocument` """ - 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): + + def __init__(self, id: str, document_file_id: str, title: str, description: Optional[str] = None, + caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = 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.type: str = 'document' + self.id: str = id + self.title: str = title + self.document_file_id: str = document_file_id + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.parse_mode: Optional[str] = parse_mode self.payload_dic['document_file_id'] = document_file_id @@ -5721,21 +5763,23 @@ class InlineQueryResultCachedVideo(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedVideo` """ - 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, show_caption_above_media=None): + + def __init__(self, id: str, video_file_id: str, title: str, description: Optional[str] = None, + caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): + 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.type: str = 'video' + self.id: str = id + self.video_file_id: str = video_file_id + self.title: str = title + self.description: Optional[str] = description + self.caption: Optional[str] = caption + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.parse_mode: Optional[str] = parse_mode self.payload_dic['video_file_id'] = video_file_id self.show_caption_above_media: Optional[bool] = show_caption_above_media @@ -5779,19 +5823,22 @@ class InlineQueryResultCachedVoice(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedVoice` """ - def __init__(self, id, voice_file_id, title, caption=None, caption_entities = None, - parse_mode=None, reply_markup=None, input_message_content=None): + + def __init__(self, id: str, voice_file_id: str, title: str, caption: Optional[str] = None, + caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, + reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = 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.type: str = 'voice' + self.id: str = id + self.voice_file_id: str = voice_file_id + self.title: str = title + self.caption: Optional[str] = caption + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.parse_mode: Optional[str] = parse_mode self.payload_dic['voice_file_id'] = voice_file_id + # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -5830,17 +5877,19 @@ class InlineQueryResultCachedAudio(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedAudio` """ - def __init__(self, id, audio_file_id, caption=None, caption_entities = None, - parse_mode=None, reply_markup=None, input_message_content=None): + + def __init__(self, id: str, audio_file_id: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, + input_message_content: Optional[InputMessageContent] = 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 + self.type: str = 'audio' + self.id: str = id + self.audio_file_id: str = audio_file_id + self.caption: Optional[str] = caption + self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup + self.input_message_content: Optional[InputMessageContent] = input_message_content + self.parse_mode: Optional[str] = parse_mode self.payload_dic['audio_file_id'] = audio_file_id @@ -5887,7 +5936,7 @@ def de_json(cls, json_string): return cls(**obj) @classmethod - def parse_photo(cls, photo_size_array): + def parse_photo(cls, photo_size_array) -> List[PhotoSize]: """ Parse the photo array into a list of PhotoSize objects """ @@ -5897,7 +5946,7 @@ def parse_photo(cls, photo_size_array): return ret @classmethod - def parse_entities(cls, message_entity_array): + def parse_entities(cls, message_entity_array) -> List[MessageEntity]: """ Parse the message entity array into a list of MessageEntity objects """ @@ -5968,16 +6017,16 @@ def __init__(self, file_id, file_unique_id, width=None, height=None, duration=No thumbnail=None, file_name=None, mime_type=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id - self.width: int = width - self.height: int = height - self.duration: int = duration - self.thumbnail: PhotoSize = thumbnail - self.file_name: str = file_name - self.mime_type: str = mime_type - self.file_size: int = file_size + self.width: Optional[int] = width + self.height: Optional[int] = height + self.duration: Optional[int] = duration + self.thumbnail: Optional[PhotoSize] = thumbnail + self.file_name: Optional[str] = file_name + self.mime_type: Optional[str] = mime_type + self.file_size: Optional[int] = file_size @property - def thumb(self): + def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6007,7 +6056,7 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) return cls(**obj) - def __init__(self, position, user, score, **kwargs): + def __init__(self, position: int, user: User, score: int, **kwargs): self.position: int = position self.user: User = user self.score: int = score @@ -6158,8 +6207,8 @@ def de_json(cls, json_string): def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs): self.name: str = name self.phone_number: str = phone_number - self.email: str = email - self.shipping_address: ShippingAddress = shipping_address + self.email: Optional[str] = email + self.shipping_address: Optional[ShippingAddress] = shipping_address # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -6186,7 +6235,7 @@ def __init__(self, id, title): self.title: str = title self.prices: List[LabeledPrice] = [] - def add_price(self, *args): + def add_price(self, *args) -> 'ShippingOption': """ Add LabeledPrice to ShippingOption @@ -6341,8 +6390,8 @@ def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipp self.currency: str = currency self.total_amount: int = total_amount self.invoice_payload: str = invoice_payload - self.shipping_option_id: str = shipping_option_id - self.order_info: OrderInfo = order_info + self.shipping_option_id: Optional[str] = shipping_option_id + self.order_info: Optional[OrderInfo] = order_info # Stickers @@ -6390,15 +6439,15 @@ def __init__(self, name, title, sticker_type, stickers, thumbnail=None, **kwargs self.title: str = title self.sticker_type: str = sticker_type self.stickers: List[Sticker] = stickers - self.thumbnail: PhotoSize = thumbnail + self.thumbnail: Optional[PhotoSize] = thumbnail @property - def thumb(self): + def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @property - def contains_masks(self): + def contains_masks(self) -> bool: """ Deprecated since Bot API 6.2, use sticker_type instead. """ @@ -6406,7 +6455,7 @@ def contains_masks(self): return self.sticker_type == 'mask' @property - def is_animated(self): + def is_animated(self) -> bool: """ Deprecated since Bot API 7.2. Stickers can be mixed now. """ @@ -6414,7 +6463,7 @@ def is_animated(self): return False @property - def is_video(self): + def is_video(self) -> bool: """ Deprecated since Bot API 7.2. Stickers can be mixed now. """ @@ -6506,17 +6555,17 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, self.height: int = height self.is_animated: bool = is_animated self.is_video: bool = is_video - self.thumbnail: PhotoSize = thumbnail - self.emoji: str = emoji - self.set_name: str = set_name - self.mask_position: MaskPosition = mask_position - self.file_size: int = file_size - self.premium_animation: File = premium_animation - self.custom_emoji_id: int = custom_emoji_id - self.needs_repainting: bool = needs_repainting + self.thumbnail: Optional[PhotoSize] = thumbnail + self.emoji: Optional[str] = emoji + self.set_name: Optional[str] = set_name + self.mask_position: Optional[MaskPosition] = mask_position + self.file_size: Optional[int] = file_size + self.premium_animation: Optional[File] = premium_animation + self.custom_emoji_id: Optional[str] = custom_emoji_id + self.needs_repainting: Optional[bool] = needs_repainting @property - def thumb(self): + def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6646,7 +6695,9 @@ class InputMediaPhoto(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaPhoto` """ - def __init__(self, media, caption=None, parse_mode=None, caption_entities=None, has_spoiler=None, show_caption_above_media=None): + def __init__(self, media: Union[str, InputFile], caption: Optional[str] = None, + parse_mode: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, + has_spoiler: Optional[bool] = None, show_caption_above_media: Optional[bool] = None): if service_utils.is_pil_image(media): media = service_utils.pil_image_to_file(media) @@ -6715,20 +6766,24 @@ class InputMediaVideo(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaVideo` """ - def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, - width=None, height=None, duration=None, supports_streaming=None, has_spoiler=None, show_caption_above_media=None): + def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, InputFile]] = None, + caption: Optional[str] = None, parse_mode: Optional[str] = None, + caption_entities: Optional[List[MessageEntity]] = None, width: Optional[int] = None, + height: Optional[int] = None, duration: Optional[int] = None, + supports_streaming: Optional[bool] = None, has_spoiler: Optional[bool] = None, + show_caption_above_media: Optional[bool] = None): super(InputMediaVideo, self).__init__( type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail = thumbnail - self.width = width - self.height = height - self.duration = duration - self.supports_streaming = supports_streaming + self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + self.width: Optional[int] = width + self.height: Optional[int] = height + self.duration: Optional[int] = duration + self.supports_streaming: Optional[bool] = supports_streaming self.has_spoiler: Optional[bool] = has_spoiler self.show_caption_above_media: Optional[bool] = show_caption_above_media @property - def thumb(self): + def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6798,19 +6853,24 @@ class InputMediaAnimation(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAnimation` """ - def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, - width=None, height=None, duration=None, has_spoiler=None, show_caption_above_media=None): + + def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, InputFile]] = None, + caption: Optional[str] = None, parse_mode: Optional[str] = None, + caption_entities: Optional[List[MessageEntity]] = None, width: Optional[int] = None, + height: Optional[int] = None, duration: Optional[int] = None, + has_spoiler: Optional[bool] = None, show_caption_above_media: Optional[bool] = None): super(InputMediaAnimation, self).__init__( type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail = thumbnail - self.width = width - self.height = height - self.duration = duration + self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + self.width: Optional[int] = width + self.height: Optional[int] = height + self.duration: Optional[int] = duration self.has_spoiler: Optional[bool] = has_spoiler self.show_caption_above_media: Optional[bool] = show_caption_above_media + @property - def thumb(self): + def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6872,17 +6932,19 @@ class InputMediaAudio(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaAudio` """ - def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, - duration=None, performer=None, title=None): + def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, InputFile]] = None, + caption: Optional[str] = None, parse_mode: Optional[str] = None, + caption_entities: Optional[List[MessageEntity]] = None, duration: Optional[int] = None, + performer: Optional[str] = None, title: Optional[str] = None): super(InputMediaAudio, self).__init__( type="audio", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail = thumbnail - self.duration = duration - self.performer = performer - self.title = title + self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + self.duration: Optional[int] = duration + self.performer: Optional[str] = performer + self.title: Optional[str] = title @property - def thumb(self): + def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6935,15 +6997,17 @@ class InputMediaDocument(InputMedia): :return: Instance of the class :rtype: :class:`telebot.types.InputMediaDocument` """ - def __init__(self, media, thumbnail=None, caption=None, parse_mode=None, caption_entities=None, - disable_content_type_detection=None): + def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, InputFile]] = None, + caption: Optional[str] = None, parse_mode: Optional[str] = None, + caption_entities: Optional[List[MessageEntity]] = None, + disable_content_type_detection: Optional[bool] = None): super(InputMediaDocument, self).__init__( type="document", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail = thumbnail - self.disable_content_type_detection = disable_content_type_detection + self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + self.disable_content_type_detection: Optional[bool] = disable_content_type_detection @property - def thumb(self): + def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6985,7 +7049,7 @@ def de_json(cls, json_string): 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 + self.text_entities: Optional[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 @@ -7010,10 +7074,11 @@ class InputPollOption(JsonSerializable): :return: Instance of the class :rtype: :class:`telebot.types.PollOption` """ - def __init__(self, text, text_parse_mode=None, text_entities=None, **kwargs): + def __init__(self, text: str, text_parse_mode: Optional[str] = None, text_entities: Optional[List[MessageEntity]] = None, + **kwargs): self.text: str = text self.text_parse_mode: Optional[str] = text_parse_mode - self.text_entities: List[MessageEntity] = text_entities + self.text_entities: Optional[List[MessageEntity]] = text_entities def to_json(self): return json.dumps(self.to_dict()) @@ -7095,13 +7160,13 @@ def de_json(cls, json_string): obj['question_entities'] = Message.parse_entities(obj['question_entities']) return cls(**obj) - # noinspection PyShadowingBuiltins def __init__( self, - 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, question_entities=None, + question: str, options: List[PollOption], + poll_id: str = None, total_voter_count: int = None, is_closed: bool = None, is_anonymous: bool = None, + type: str = None, allows_multiple_answers: bool = None, correct_option_id: int = None, + explanation: str = None, explanation_entities: List[MessageEntity] = None, open_period: int = None, + close_date: int = None, poll_type: str = None, question_entities: List[MessageEntity] = None, **kwargs): self.id: str = poll_id self.question: str = question @@ -7167,10 +7232,10 @@ def de_json(cls, json_string): obj['voter_chat'] = Chat.de_json(obj['voter_chat']) return cls(**obj) - def __init__(self, poll_id, option_ids, user=None, voter_chat=None, **kwargs): + def __init__(self, poll_id: str, option_ids: List[int], user: Optional[User] = None, voter_chat: Optional[Chat] = None, **kwargs): self.poll_id: str = poll_id self.user: Optional[User] = user - self.option_ids: List[int] = option_ids + self.option_ids: Optional[List[int]] = option_ids self.voter_chat: Optional[Chat] = voter_chat @@ -7213,7 +7278,7 @@ def de_json(cls, json_string): obj['location'] = Location.de_json(obj['location']) return cls(**obj) - def __init__(self, location, address, **kwargs): + def __init__(self, location: Location, address: str, **kwargs): self.location: Location = location self.address: str = address @@ -7272,17 +7337,18 @@ def de_json(cls, json_string): obj['creator'] = User.de_json(obj['creator']) return cls(**obj) - def __init__(self, invite_link, creator, creates_join_request , is_primary, is_revoked, - name=None, expire_date=None, member_limit=None, pending_join_request_count=None, **kwargs): + def __init__(self, invite_link: str, creator: User, creates_join_request: bool, is_primary: bool, is_revoked: bool, + name: Optional[str] = None, expire_date: Optional[int] = None, member_limit: Optional[int] = None, + pending_join_request_count: Optional[int] = None, **kwargs): self.invite_link: str = invite_link self.creator: User = creator self.creates_join_request: bool = creates_join_request self.is_primary: bool = is_primary self.is_revoked: bool = is_revoked - self.name: str = name - self.expire_date: int = expire_date - self.member_limit: int = member_limit - self.pending_join_request_count: int = pending_join_request_count + self.name: Optional[str] = name + self.expire_date: Optional[int] = expire_date + self.member_limit: Optional[int] = member_limit + self.pending_join_request_count: Optional[int] = pending_join_request_count def to_json(self): return json.dumps(self.to_dict()) @@ -7472,7 +7538,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, message_auto_delete_time, **kwargs): - self.message_auto_delete_time = message_auto_delete_time + self.message_auto_delete_time: int = message_auto_delete_time class MenuButton(JsonDeserializable, JsonSerializable, Dictionaryable): @@ -7524,7 +7590,7 @@ class MenuButtonCommands(MenuButton): :rtype: :class:`telebot.types.MenuButtonCommands` """ - def __init__(self, type = None, **kwargs): + def __init__(self, type: str = None, **kwargs): self.type: str = "commands" def to_dict(self): @@ -7557,7 +7623,7 @@ class MenuButtonWebApp(MenuButton): :rtype: :class:`telebot.types.MenuButtonWebApp` """ - def __init__(self, type, text, web_app, **kwargs): + def __init__(self, type: str, text: str, web_app: WebAppInfo, **kwargs): self.type: str = "web_app" self.text: str = text self.web_app: WebAppInfo = web_app @@ -7582,7 +7648,7 @@ class MenuButtonDefault(MenuButton): :return: Instance of the class :rtype: :class:`telebot.types.MenuButtonDefault` """ - def __init__(self, type = None, **kwargs): + def __init__(self, type: str = None, **kwargs): self.type: str = "default" def to_dict(self): @@ -7661,10 +7727,10 @@ def de_json(cls, json_string): def __init__(self, is_anonymous: bool, can_manage_chat: bool, can_delete_messages: bool, can_manage_video_chats: bool, can_restrict_members: bool, can_promote_members: bool, can_change_info: bool, can_invite_users: bool, - can_post_messages: bool=None, can_edit_messages: bool=None, - can_pin_messages: bool=None, can_manage_topics: bool=None, - can_post_stories: bool=None, can_edit_stories: bool=None, - can_delete_stories: bool=None, **kwargs + can_post_messages: Optional[bool]=None, can_edit_messages: Optional[bool]=None, + can_pin_messages: Optional[bool]=None, can_manage_topics: Optional[bool]=None, + can_post_stories: Optional[bool]=None, can_edit_stories: Optional[bool]=None, + can_delete_stories: Optional[bool]=None, **kwargs ) -> None: self.is_anonymous: bool = is_anonymous @@ -7675,13 +7741,13 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, self.can_promote_members: bool = can_promote_members self.can_change_info: bool = can_change_info self.can_invite_users: bool = can_invite_users - self.can_post_messages: bool = can_post_messages - self.can_edit_messages: bool = can_edit_messages - self.can_pin_messages: bool = can_pin_messages - self.can_manage_topics: bool = can_manage_topics - self.can_post_stories: bool = can_post_stories - self.can_edit_stories: bool = can_edit_stories - self.can_delete_stories: bool = can_delete_stories + self.can_post_messages: Optional[bool] = can_post_messages + self.can_edit_messages: Optional[bool] = can_edit_messages + self.can_pin_messages: Optional[bool] = can_pin_messages + self.can_manage_topics: Optional[bool] = can_manage_topics + self.can_post_stories: Optional[bool] = can_post_stories + self.can_edit_stories: Optional[bool] = can_edit_stories + self.can_delete_stories: Optional[bool] = can_delete_stories def to_dict(self): json_dict = { @@ -7773,14 +7839,14 @@ def _resolve_file(file): raise TypeError("File must be a string or a file-like object(pathlib.Path, io.IOBase).") @property - def file(self): + def file(self) -> Union[IOBase, str]: """ File object. """ return self._file @property - def file_name(self): + def file_name(self) -> str: """ File name. """ @@ -7965,9 +8031,9 @@ def de_json(cls, json_string): def __init__(self, from_request: Optional[bool]=None, web_app_name: Optional[str]=None, from_attachment_menu: Optional[bool]=None, **kwargs) -> None: - self.web_app_name: str = web_app_name - self.from_request: bool = from_request - self.from_attachment_menu: bool = from_attachment_menu + self.web_app_name: Optional[str] = web_app_name + self.from_request: Optional[bool] = from_request + self.from_attachment_menu: Optional[bool] = from_attachment_menu class ChatShared(JsonDeserializable): @@ -8126,7 +8192,7 @@ def to_dict(self) -> dict: def to_json(self) -> str: return json.dumps(self.to_dict()) - def convert_input_sticker(self): + def convert_input_sticker(self) -> Tuple[dict, Optional[dict]]: if service_utils.is_string(self.sticker): return self.to_json(), None @@ -8167,13 +8233,14 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, query=None, allow_user_chats=None, allow_bot_chats=None, allow_group_chats=None, - allow_channel_chats=None, **kwargs): - self.query: str = query - self.allow_user_chats: bool = allow_user_chats - self.allow_bot_chats: bool = allow_bot_chats - self.allow_group_chats: bool = allow_group_chats - self.allow_channel_chats: bool = allow_channel_chats + def __init__(self, query: Optional[str]=None, allow_user_chats: Optional[bool]=None, allow_bot_chats: Optional[bool]=None, + allow_group_chats: Optional[bool]=None, allow_channel_chats: Optional[bool]=None) -> None: + self.query: Optional[str] = query + self.allow_user_chats: Optional[bool] = allow_user_chats + self.allow_bot_chats: Optional[bool] = allow_bot_chats + self.allow_group_chats: Optional[bool] = allow_group_chats + self.allow_channel_chats: Optional[bool] = allow_channel_chats + def to_dict(self): json_dict = {} @@ -9166,17 +9233,17 @@ def de_json(cls, json_string): obj['users'] = [SharedUser.de_json(user) for user in obj['users']] return cls(**obj) - def __init__(self, request_id, users: List[SharedUser], **kwargs): - self.request_id = request_id - self.users = users + def __init__(self, request_id: int, users: List[SharedUser], **kwargs): + self.request_id: int = request_id + self.users: List[SharedUser] = users @property - def user_id(self): + def user_id(self) -> int: logger.warning('The parameter "user_id" is deprecated, use "user_ids" instead') return None @property - def user_ids(self): + def user_ids(self) -> List[int]: logger.warning('The parameter "user_ids" is deprecated, use "users" instead') return self.users @@ -10586,9 +10653,9 @@ class InputPaidMedia(JsonSerializable): :rtype: :class:`InputPaidMediaPhoto` or :class:`InputPaidMediaVideo` """ - def __init__(self, type, media, **kwargs): - self.type = type - self.media = media + def __init__(self, type: str, media: Union[str, InputFile], **kwargs): + self.type: str = type + self.media: Union[str, InputFile] = media if service_utils.is_string(self.media): self._media_name = '' @@ -10625,7 +10692,7 @@ class InputPaidMediaPhoto(InputPaidMedia): :rtype: :class:`InputPaidMediaPhoto` """ - def __init__(self, media, **kwargs): + def __init__(self, media: Union[str, InputFile], **kwargs): super().__init__(type='photo', media=media) class InputPaidMediaVideo(InputPaidMedia): @@ -10665,14 +10732,16 @@ class InputPaidMediaVideo(InputPaidMedia): :rtype: :class:`InputPaidMediaVideo` """ - - def __init__(self, media, thumbnail=None, width=None, height=None, duration=None, supports_streaming=None, **kwargs): + def __init__(self, media: Union[str, InputFile], thumbnail: Optional[InputFile] = None, width: Optional[int] = None, + height: Optional[int] = None, duration: Optional[int] = None, supports_streaming: Optional[bool] = None, + **kwargs): super().__init__(type='video', media=media) - self.thumbnail = thumbnail - self.width = width - self.height = height - self.duration = duration - self.supports_streaming = supports_streaming + self.thumbnail: Optional[Union[str,InputFile]] = thumbnail + self.width: Optional[int] = width + self.height: Optional[int] = height + self.duration: Optional[int] = duration + self.supports_streaming: Optional[bool] = supports_streaming + def to_dict(self): data = super().to_dict() From 2ae6b9308e05dff5632f2e6bf91301b74d5cb7fc Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 27 Jul 2024 18:57:30 +0500 Subject: [PATCH 1646/1808] Use deepcopy for sync --- telebot/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index b154d04ca..671c5631b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -20,6 +20,7 @@ # random module to generate random string import random import string +import copy import ssl @@ -8749,7 +8750,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return else: - data_copy = data.copy() + data_copy = copy.deepcopy(data) for key in list(data_copy): # remove data from data_copy if handler does not accept it if key not in params: From 74019d03afe7f0e0f4248c0cdf34655617256045 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 27 Jul 2024 18:58:07 +0500 Subject: [PATCH 1647/1808] Use deepcopy for async --- telebot/async_telebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d50cbf7e0..27b861ead 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -32,7 +32,7 @@ import string import random import ssl - +import copy """ Module : telebot @@ -558,7 +558,7 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return else: - data_copy = data.copy() + data_copy = copy.deepcopy(data) for key in list(data_copy): # remove data from data_copy if handler does not accept it if key not in params: From 2dbf19004c74a7f8e73f37a460484fdec023ccb3 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 28 Jul 2024 14:26:58 +0500 Subject: [PATCH 1648/1808] Remove apihelper following the validate_token not using getMe --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d60a4a090..705012f77 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -18,7 +18,7 @@ from inspect import signature, iscoroutinefunction -from telebot import util, types, asyncio_helper, apihelper # have to use sync +from telebot import util, types, asyncio_helper import asyncio from telebot import asyncio_filters From b10e8d749440cc596fdba05b1c5d1a33e4b86196 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 28 Jul 2024 14:48:12 +0500 Subject: [PATCH 1649/1808] Reverted changes regarding self._user, fixed validate_token=False causing error when extracting a bot id --- telebot/__init__.py | 6 +----- telebot/async_telebot.py | 6 ++---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index d0ab64d5a..19f85f2d2 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -195,8 +195,7 @@ def __init__( if validate_token: util.validate_token(self.token) - - self.bot_id = util.extract_bot_id(self.token) # subject to change in future, unspecified + self.bot_id = util.extract_bot_id(self.token) # subject to change in future, unspecified # logs-related if colorful_logs: @@ -1184,9 +1183,6 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F if restart_on_change: self._setup_change_detector(path_to_watch) - if not self._user: - self._user = self.get_me() - logger.info('Starting your bot with username: [@%s]', self.user.username) if self.threaded: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 705012f77..5302000ff 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -193,8 +193,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ if validate_token: util.validate_token(self.token) - - self.bot_id: int = util.extract_bot_id(self.token) # subject to change, unspecified + self.bot_id: int = util.extract_bot_id(self.token) # subject to change, unspecified @property @@ -437,8 +436,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: # show warning logger.warning("Setting non_stop to False will stop polling on API and system exceptions.") - if not self._user: - self._user = await self.get_me() + self._user = await self.get_me() logger.info('Starting your bot with username: [@%s]', self.user.username) From 6108e358134177d07d90f5fe5907217140c51a40 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 28 Jul 2024 14:49:00 +0500 Subject: [PATCH 1650/1808] fix docstring --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 19f85f2d2..74c08a4b8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6699,7 +6699,7 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ - Reset data for a user in chat: sets the 'data' fieldi to an empty dictionary. + Reset data for a user in chat: sets the 'data' field to an empty dictionary. :param user_id: User's identifier :type user_id: :obj:`int` From 9a8ca9d51e395569f81412449674ce218505f740 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 29 Jul 2024 00:02:37 +0300 Subject: [PATCH 1651/1808] Fix html_text for None --- telebot/types.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 6913452be..bd9688cb7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1517,6 +1517,8 @@ def html_text(self): """ Returns html-rendered text. """ + if self.text is None: + return None return apply_html_entities(self.text, self.entities, getattr(self, "custom_subs", None)) @property @@ -9067,6 +9069,8 @@ def html_text(self): """ Returns html-rendered text. """ + if self.text is None: + return None return apply_html_entities(self.text, self.entities, getattr(self, "custom_subs", None)) From cd987101b3cab121d1d7c08be13b59b39f8618e3 Mon Sep 17 00:00:00 2001 From: EgorKhabarov Date: Mon, 29 Jul 2024 02:10:55 +0400 Subject: [PATCH 1652/1808] Fix html_caption for None. Return None instead if exception. --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index bd9688cb7..9055091e9 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1526,6 +1526,8 @@ def html_caption(self): """ Returns html-rendered caption. """ + if self.caption is None: + return None return apply_html_entities(self.caption, self.caption_entities, getattr(self, "custom_subs", None)) @property From 30ebe756ac98010380a83114d84525ae968f0086 Mon Sep 17 00:00:00 2001 From: _run Date: Tue, 30 Jul 2024 15:48:26 +0500 Subject: [PATCH 1653/1808] make extract_bot_id return None in case validation fails --- telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 4 ++-- telebot/util.py | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 74c08a4b8..fc7f3cdc3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -191,11 +191,11 @@ def __init__( self.allow_sending_without_reply = allow_sending_without_reply self.webhook_listener = None self._user = None - self.bot_id: int = None if validate_token: util.validate_token(self.token) - self.bot_id = util.extract_bot_id(self.token) # subject to change in future, unspecified + + self.bot_id: Union[int, None] = util.extract_bot_id(self.token) # subject to change in future, unspecified # logs-related if colorful_logs: diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 5302000ff..ec782ff91 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -189,11 +189,11 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.middlewares = [] self._user = None # set during polling - self.bot_id: int = None if validate_token: util.validate_token(self.token) - self.bot_id: int = util.extract_bot_id(self.token) # subject to change, unspecified + + self.bot_id: Union[int, None] = util.extract_bot_id(self.token) # subject to change, unspecified @property diff --git a/telebot/util.py b/telebot/util.py index c8ef526c5..0448893e1 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -698,7 +698,11 @@ def validate_token(token) -> bool: return True -def extract_bot_id(token) -> str: +def extract_bot_id(token) -> Union[int, None]: + try: + validate_token(token) + except ValueError: + return None return int(token.split(':')[0]) From f0c3e5aec97fb2c86efaa84104ff5c3cb599d176 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Jul 2024 21:46:05 +0500 Subject: [PATCH 1654/1808] Bot api 7.8 --- README.md | 2 +- telebot/__init__.py | 15 +++++++++++---- telebot/apihelper.py | 8 ++++++-- telebot/async_telebot.py | 14 ++++++++++---- telebot/asyncio_helper.py | 8 ++++++-- telebot/types.py | 10 ++++++++-- 6 files changed, 42 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 98f8c389a..10d75993b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.7! +##

Supported Bot API version: 7.8!

Official documentation

Official ru documentation

diff --git a/telebot/__init__.py b/telebot/__init__.py index b154d04ca..70d59ddde 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4640,7 +4640,7 @@ def set_chat_description(self, chat_id: Union[int, str], description: Optional[s def pin_chat_message( self, chat_id: Union[int, str], message_id: int, - disable_notification: Optional[bool]=False) -> bool: + disable_notification: Optional[bool]=False, business_connection_id: Optional[str]=None) -> bool: """ Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -4659,15 +4659,19 @@ def pin_chat_message( to all group members about the new pinned message :type disable_notification: :obj:`bool` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: True on success. :rtype: :obj:`bool` """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification - return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification=disable_notification) + return apihelper.pin_chat_message(self.token, chat_id, message_id, disable_notification=disable_notification, + business_connection_id=business_connection_id) - def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: + def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None, business_connection_id: Optional[str]=None) -> bool: """ Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -4682,10 +4686,13 @@ def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int] :param message_id: Int: Identifier of a message to unpin :type message_id: :obj:`int` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: True on success. :rtype: :obj:`bool` """ - return apihelper.unpin_chat_message(self.token, chat_id, message_id) + return apihelper.unpin_chat_message(self.token, chat_id, message_id, business_connection_id=business_connection_id) def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0bcb80a96..2ee86abab 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1386,19 +1386,23 @@ def set_chat_description(token, chat_id, description): return _make_request(token, method_url, params=payload, method='post') -def pin_chat_message(token, chat_id, message_id, disable_notification=None): +def pin_chat_message(token, chat_id, message_id, disable_notification=None, business_connection_id=None): method_url = 'pinChatMessage' payload = {'chat_id': chat_id, 'message_id': message_id} if disable_notification is not None: payload['disable_notification'] = disable_notification + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') -def unpin_chat_message(token, chat_id, message_id): +def unpin_chat_message(token, chat_id, message_id, business_connection_id=None): method_url = 'unpinChatMessage' payload = {'chat_id': chat_id} if message_id: payload['message_id'] = message_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d50cbf7e0..e702487f3 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6023,7 +6023,7 @@ async def set_chat_description(self, chat_id: Union[int, str], description: Opti async def pin_chat_message( self, chat_id: Union[int, str], message_id: int, - disable_notification: Optional[bool]=False) -> bool: + disable_notification: Optional[bool]=False, business_connection_id: Optional[str]=None) -> bool: """ Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -6042,14 +6042,17 @@ async def pin_chat_message( to all group members about the new pinned message :type disable_notification: :obj:`bool` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: True on success. :rtype: :obj:`bool` """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification - return await asyncio_helper.pin_chat_message(self.token, chat_id, message_id, disable_notification) + return await asyncio_helper.pin_chat_message(self.token, chat_id, message_id, disable_notification, business_connection_id) - async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None) -> bool: + async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optional[int]=None, business_connection_id: Optional[str]=None) -> bool: """ Use this method to unpin specific pinned message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -6064,10 +6067,13 @@ async def unpin_chat_message(self, chat_id: Union[int, str], message_id: Optiona :param message_id: Int: Identifier of a message to unpin :type message_id: :obj:`int` + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + :return: True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.unpin_chat_message(self.token, chat_id, message_id) + return await asyncio_helper.unpin_chat_message(self.token, chat_id, message_id, business_connection_id) async def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index b70508a2a..fa14f6123 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1365,19 +1365,23 @@ async def set_chat_description(token, chat_id, description): return await _process_request(token, method_url, params=payload, method='post') -async def pin_chat_message(token, chat_id, message_id, disable_notification=None): +async def pin_chat_message(token, chat_id, message_id, disable_notification=None, business_connection_id=None): method_url = 'pinChatMessage' payload = {'chat_id': chat_id, 'message_id': message_id} if disable_notification is not None: payload['disable_notification'] = disable_notification + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, method='post') -async def unpin_chat_message(token, chat_id, message_id): +async def unpin_chat_message(token, chat_id, message_id, business_connection_id=None): method_url = 'unpinChatMessage' payload = {'chat_id': chat_id} if message_id: payload['message_id'] = message_id + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, method='post') diff --git a/telebot/types.py b/telebot/types.py index 9055091e9..c5abb13d4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -494,6 +494,9 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): :param can_connect_to_business: Optional. True, if the bot can be connected to a Telegram Business account to receive its messages. Returned only in getMe. :type can_connect_to_business: :obj:`bool` + :param has_main_web_app: Optional. True, if the bot has a main Web App. Returned only in getMe. + :type has_main_web_app: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.User` """ @@ -506,7 +509,8 @@ def de_json(cls, json_string): # 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): + is_premium=None, added_to_attachment_menu=None, can_connect_to_business=None, + has_main_web_app=None, **kwargs): self.id: int = id self.is_bot: bool = is_bot self.first_name: str = first_name @@ -519,6 +523,7 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.is_premium: bool = is_premium self.added_to_attachment_menu: bool = added_to_attachment_menu self.can_connect_to_business: bool = can_connect_to_business + self.has_main_web_app: bool = has_main_web_app @property def full_name(self): @@ -545,7 +550,8 @@ def to_dict(self): 'supports_inline_queries': self.supports_inline_queries, 'is_premium': self.is_premium, 'added_to_attachment_menu': self.added_to_attachment_menu, - 'can_connect_to_business': self.can_connect_to_business} + 'can_connect_to_business': self.can_connect_to_business, + 'has_main_web_app': self.has_main_web_app} # noinspection PyShadowingBuiltins From a0b0b9b58c328216acb849317809e2bf46206c69 Mon Sep 17 00:00:00 2001 From: 2ei Date: Thu, 1 Aug 2024 22:00:23 +0300 Subject: [PATCH 1655/1808] Add Bot API badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98f8c389a..76f8f487e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: 7.7! +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From d059c648e0f8f37199b9c743ae387fd572607673 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 4 Aug 2024 10:20:53 +0300 Subject: [PATCH 1656/1808] Bump version --- 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 cfa926801..1b8c80931 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.21.0' +release = '4.22.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 0095c3f21..a45a8dd3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.21.0" +version = "4.22.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 5016764c6..3bbf77eb0 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.21.0' +__version__ = '4.22.0' From adbf425e0253233a2b265b33dc2b61dbad50d056 Mon Sep 17 00:00:00 2001 From: nypava Date: Sun, 4 Aug 2024 15:00:22 +0300 Subject: [PATCH 1657/1808] add features to webapp --- examples/webapp/bot.py | 23 +- examples/webapp/webapp.html | 63 ------ examples/webapp/webapp/index.html | 163 ++++++++++++++ examples/webapp/webapp/script.js | 318 ++++++++++++++++++++++++++++ examples/webapp/webapp/style.css | 338 ++++++++++++++++++++++++++++++ 5 files changed, 836 insertions(+), 69 deletions(-) delete mode 100644 examples/webapp/webapp.html create mode 100644 examples/webapp/webapp/index.html create mode 100644 examples/webapp/webapp/script.js create mode 100644 examples/webapp/webapp/style.css diff --git a/examples/webapp/bot.py b/examples/webapp/bot.py index 310e25c5c..7fa106f09 100644 --- a/examples/webapp/bot.py +++ b/examples/webapp/bot.py @@ -1,16 +1,27 @@ from telebot import TeleBot -from telebot.types import ReplyKeyboardMarkup, KeyboardButton, WebAppInfo +from telebot.types import ( + ReplyKeyboardMarkup, + KeyboardButton, + WebAppInfo, + InlineKeyboardMarkup, + InlineKeyboardButton +) -BOT_TOKEN = '' -WEB_URL = 'https://pytelegrambotminiapp.vercel.app/' # https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/webapp/webapp.html +BOT_TOKEN = "" +WEB_URL = "https://pytelegrambotminiapp.vercel.app" # https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/webapp/webapp bot = TeleBot(BOT_TOKEN) @bot.message_handler(commands=["start"]) def start(message): - reply_markup = ReplyKeyboardMarkup(resize_keyboard=True) - reply_markup.row(KeyboardButton("Start MiniApp", web_app=WebAppInfo(WEB_URL))) - bot.reply_to(message, "Click the button to start MiniApp", reply_markup=reply_markup) + reply_keyboard_markup = ReplyKeyboardMarkup(resize_keyboard=True) + reply_keyboard_markup.row(KeyboardButton("Start MiniApp", web_app=WebAppInfo(WEB_URL))) + + inline_keyboard_markup = InlineKeyboardMarkup() + inline_keyboard_markup.row(InlineKeyboardButton('Start MiniApp', web_app=WebAppInfo(WEB_URL))) + + bot.reply_to(message, "Click the bottom inline button to start MiniApp", reply_markup=inline_keyboard_markup) + bot.reply_to(message, "Click keyboard button to start MiniApp", reply_markup=reply_keyboard_markup) @bot.message_handler(content_types=['web_app_data']) def web_app(message): diff --git a/examples/webapp/webapp.html b/examples/webapp/webapp.html deleted file mode 100644 index e2e953825..000000000 --- a/examples/webapp/webapp.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - MiniApp example - - - - -
- - -
- - - - - - - diff --git a/examples/webapp/webapp/index.html b/examples/webapp/webapp/index.html new file mode 100644 index 000000000..6f7d79ff7 --- /dev/null +++ b/examples/webapp/webapp/index.html @@ -0,0 +1,163 @@ + + + + + + + PyTelegramBotApi MiniApp + + + +
+ + +
+ +

client buttons

+
+ + + +
+

hapticFeedback (notification)

+ + +

tweak color [Bot API 6.1+]

+
+
+

Header color:

+
+
+
+
+
+
+
+
+

Background color:

+
+
+
+
+
+
+
+
+ +

popup and alert [Bot API 6.2+]

+ + +

request [Bot API 6.9+]

+
+ + + +
+ +

story [Bot API 7.8+]

+
+ + + +
+ +

cloud storage [Bot API 6.9+]

+
+
+ + +
+ + + + + + + + + + +
key value
- -
+
+ + +
+
+

data from telegram

+
+
+

initDataUnsafe

+
+
+

ThemeParams

+
+
+

Biometrics Manager

+ + + + + +
supportUnsupported in this platform
+
+
+ +

event from telegram

+
+
+
+
+ +

vertical scroll behaviour [Bot API 7.7+]

+
+

close the miniapp when scroll vertically

+
+ + +
+
+ +

closing confirmation [Bot API 6.2+]

+
+

Confimation dialog when closing mini app

+
+ + +
+
+ +

links

+ + +

other

+
+ + + +
+ +
+
 version: 
+
 platform: 
+
 color scheme: 
+
+ + + diff --git a/examples/webapp/webapp/script.js b/examples/webapp/webapp/script.js new file mode 100644 index 000000000..42889b055 --- /dev/null +++ b/examples/webapp/webapp/script.js @@ -0,0 +1,318 @@ +const telegram = Telegram.WebApp; +telegram.ready(); + +class ModifyStyle{ + constructor(element) { + this.element = element; + }; + disableButton() { + this.element.style.backgroundColor = 'transparent'; + this.element.style.color = 'var(--tg-theme-button-color)'; + this.element.style.border = "solid 1px var(--tg-theme-button-color)"; + }; + enableButton() { + this.element.style.backgroundColor = "var(--tg-theme-button-color)"; + this.element.style.color = 'var(--tg-theme-text-color)'; + }; +}; + +class TableModify{ + constructor(element) { + this.element = element; + } + addElements(element_1, element_2) { + this.element.innerHTML += ` ${element_1} ${element_2} `; + } + removeElement(id) { + document.getElementById(id).remove(); + } +}; + +const button = { + send_message : document.getElementById('send-message-button'), + back : document.getElementById('back-button'), + main: document.getElementById('main-button'), + settings: document.getElementById('settings-button'), + success: document.getElementById('success-button'), + warning: document.getElementById('warning-button'), + error: document.getElementById('error-button'), + popup: document.getElementById('popup-button'), + alert: document.getElementById('alert-button'), + confirm: document.getElementById('confirm-button'), + contact: document.getElementById('contact-button'), + write_access: document.getElementById('write-access-button'), + biometrics: document.getElementById('biometrics-button'), + cloud_add: document.getElementById('cloud-add-button'), + cloud_remove: document.getElementById('cloud-remove-button'), + vertical_scroll_enable: document.getElementById('enable-scroll-button'), + vertical_scroll_disable: document.getElementById('disable-scroll-button'), + close_confirm_enable: document.getElementById('enable-close-confirm-button'), + close_confirm_disable: document.getElementById('disable-close-confirm-button'), + post_story_photo: document.getElementById('post-story-photo-button'), + post_story_video: document.getElementById('post-story-video-button'), + post_story_with_link: document.getElementById('post-story-with-link-button'), + show_QR: document.getElementById('show-QR-button') , + expand: document.getElementById('expand-button'), + close: document.getElementById('close-button'), +}; + +const input = { + message: document.querySelector('.send-message__input'), + cloud_add_key: document.querySelector('.cloud-storage__input_add-key'), + cloud_add_value: document.querySelector('.cloud-storage__input_add-value'), + cloud_remove_key: document.querySelector('.cloud-storage__input_remove'), +}; + +const text = { + telegram_link: document.getElementById('telegram-link'), + internal_link: document.getElementById('internal-link'), + version: document.getElementById('version-text'), + platform: document.getElementById('platform-text'), + color_scheme: document.getElementById('color-scheme-text') +}; + +const table = { + cloud_table: document.querySelector(".cloud-storage__table"), + biometrics_table: document.getElementById("biometrics-table") +} + +const colors_tweaks = [ + document.getElementById('header-yellow'), + document.getElementById('header-button_color'), + document.getElementById('header-red'), + document.getElementById('header-default'), + document.getElementById('background-yellow'), + document.getElementById('background-button_color'), + document.getElementById('background-red'), + document.getElementById('background-default'), +]; + +telegram.MainButton.setParams({text: 'Close', color: '#ba4d47'}); +telegram.MainButton.onClick(() => {telegram.close()}); +telegram.MainButton.show(); +telegram.enableVerticalSwipes(); + +telegram.CloudStorage.getKeys((error, keys) => { + let table_modify = new TableModify(table.cloud_table); + if (keys.length) {table_modify.removeElement("null-storage-row")}; + keys.forEach((key) => { + telegram.CloudStorage.getItem(key, (error, item) => { + table_modify.addElements(key, item) + }); + }); +}); + +document.getElementById('initDataUnsafe-block').innerHTML += `
 ${JSON.stringify(telegram.initDataUnsafe, null, 2)} 
`; +document.getElementById('themeParams-block').innerHTML += `
 ${JSON.stringify(telegram.themeParams, null, 2)} 
`; + +let biometrics_table_modify = new TableModify(table.biometrics_table); + +telegram.BiometricManager.init(() => {['isInited', 'isBiometricAvailable', 'biometricType', 'isAccessRequested', 'isAccessGranted', 'isBiometricTokenSaved', 'deviceId'].forEach((field) => { + biometrics_table_modify.addElements(field, eval(`telegram.BiometricManager.${field}`)); +}); biometrics_table_modify.removeElement('null-biometrics-row') }); + +['themeChanged', 'viewportChanged', 'mainButtonClicked', 'backButtonClicked', 'settingsButtonClicked', 'invoiceClosed', 'popupClosed', 'qrTextReceived', 'scanQrPopupClosed', +'clipboardTextReceived', 'writeAccessRequested', 'contactRequested', 'biometricManagerUpdated', 'biometricAuthRequested', 'biometricTokenUpdated'].forEach((element) => { + telegram.onEvent(element, () => { + document.getElementById('event-block').innerHTML += `

${element}

`; + }); +}); + +text.color_scheme.innerText += telegram.colorScheme; +text.version.innerText += telegram.version; +text.platform.innerText += telegram.platform; + +button.send_message.addEventListener('click', () => { + telegram.sendData(input.message.value); + if (telegram.initDataUnsafe) telegram.showAlert('SendData only works in webapp that opened with reply keyboard.') + input.message.value = '' +}); + +button.main.addEventListener('click', (e) => { + const modify_button = new ModifyStyle(e.currentTarget); + if (e.currentTarget.value == 'enable') { + e.currentTarget.value = 'disable'; + modify_button.disableButton(); + telegram.MainButton.hide(); + } else { + e.currentTarget.value = 'enable'; + modify_button.enableButton(); + telegram.MainButton.show(); + } +}); + +button.back.addEventListener('click', (e) => { + const modify_button = new ModifyStyle(e.currentTarget); + if (e.currentTarget.value == 'enable') { + e.currentTarget.value = 'disable'; + modify_button.disableButton(); + telegram.BackButton.hide(); + } else { + e.currentTarget.value = 'enable'; + modify_button.enableButton(); + telegram.BackButton.show(); + } +}); + +button.settings.addEventListener('click', (e) => { + const modify_button = new ModifyStyle(e.currentTarget); + if (e.currentTarget.value == 'enable') { + e.currentTarget.value = 'disable'; + modify_button.disableButton(); + telegram.SettingsButton.hide(); + } else { + e.currentTarget.value = 'enable'; + modify_button.enableButton(); + telegram.SettingsButton.show(); + } +}); + +button.success.addEventListener('click', () => { + telegram.HapticFeedback.notificationOccurred('success'); +}); + +button.warning.addEventListener('click', () => { + telegram.HapticFeedback.notificationOccurred('warning'); +}); + +button.error.addEventListener('click', () => { + telegram.HapticFeedback.notificationOccurred('error'); +}); + +button.popup.addEventListener('click', () => { + telegram.showPopup({title: 'popup', message: 'this is popup message', buttons: [{ text: 'roger'}]}); +}); + +button.alert.addEventListener('click', () => { + telegram.showAlert('This is alert message'); +}); + +button.confirm.addEventListener('click', () => { + telegram.showConfirm('This is confirm message'); +}); + +button.contact.addEventListener('click', () => { + telegram.requestContact(); +}); + +button.write_access.addEventListener('click', () => { + telegram.requestWriteAccess(); +}); + +button.biometrics.addEventListener('click', () => { + telegram.BiometricManager.requestAccess({'reseaon': 'this is biometrics request'}); +}); + +button.vertical_scroll_enable.addEventListener('click', (e) => { + const modify_scroll_enable = new ModifyStyle(e.currentTarget); + const modify_scroll_disable = new ModifyStyle(button.vertical_scroll_disable); + if (e.currentTarget.value == 'disable') { + e.currentTarget.value = 'enable'; + modify_scroll_enable.enableButton(); + modify_scroll_disable.disableButton(); + button.vertical_scroll_disable.value = 'disable'; + telegram.enableVerticalSwipes(); + }; +}); + +button.vertical_scroll_disable.addEventListener('click', (e) => { + const modify_scroll_disable = new ModifyStyle(e.currentTarget); + const modify_scroll_enable = new ModifyStyle(button.vertical_scroll_enable); + if (e.currentTarget.value == 'disable') { + e.currentTarget.value = 'enable'; + modify_scroll_disable.enableButton(); + modify_scroll_enable.disableButton(); + telegram.disableVerticalSwipes(); + button.vertical_scroll_enable.value = 'disable'; + }; +}); + +button.close_confirm_enable.addEventListener('click', (e) => { + const modify_close_confirm_enable = new ModifyStyle(e.currentTarget); + const modify_close_confirm_disable = new ModifyStyle(button.close_confirm_disable); + if (e.currentTarget.value == 'disable') { + e.currentTarget.value = 'enable'; + modify_close_confirm_enable.enableButton(); + modify_close_confirm_disable.disableButton(); + button.close_confirm_disable.value = 'disable'; + telegram.enableClosingConfirmation(); + }; +}); + +button.close_confirm_disable.addEventListener('click', (e) => { + const modify_close_confirm_disable= new ModifyStyle(e.currentTarget); + const modify_close_confirm_enable= new ModifyStyle(button.close_confirm_enable); + if (e.currentTarget.value == 'disable') { + e.currentTarget.id = 'enable'; + modify_close_confirm_enable.disableButton(); + modify_close_confirm_disable.enableButton(); + button.close_confirm_enable.value = 'disable'; + telegram.disableClosingConfirmation(); + }; +}); + +button.show_QR.addEventListener('click', () => { + telegram.showScanQrPopup({text: "this is QR scanner"}); +}); + +button.expand.addEventListener('click', () => { + telegram.expand(); +}); + +button.close.addEventListener('click', () => { + telegram.close(); +}); + +button.cloud_add.addEventListener('click', () => { + let table_modify = new TableModify(table.cloud_table); + telegram.CloudStorage.setItem(input.cloud_add_key.value, input.cloud_add_value.value, (error, is_stored) => { + if (!is_stored) telegram.showAlert(error); + else { + table_modify.addElements(input.cloud_add_key.value, input.cloud_add_value.value); + new TableModify(table.cloud_table).removeElement('null-storage-row'); + }; + input.cloud_add_key.value = ''; + input.cloud_add_value.value = ''; + }); +}); + +button.cloud_remove.addEventListener('click', () => { + let table_modify = new TableModify(table.cloud_table); + telegram.CloudStorage.removeItem(input.cloud_remove_key.value, (error, is_removed) => { + if (!is_removed) telegram.showAlert(error); + else table_modify.removeElement(input.cloud_remove_key.value); + input.cloud_remove_key.value = ''; + }); +}); + +button.post_story_photo.addEventListener('click', () => { + telegram.shareToStory('https://telegra.ph/file/e194a37aed103485469b4.jpg', {text: 'This is photo story'}); +}); + +button.post_story_video.addEventListener('click', () => { + telegram.shareToStory('https://telegra.ph/file/16ffd9385a017b59f458e.mp4', {text: 'This is video story'}); +}); + +button.post_story_with_link.addEventListener('click', () => { + telegram.shareToStory('https://telegra.ph/file/e194a37aed103485469b4.jpg', {text: 'This is story with link', widget_link: {url: 'https://t.me/joinchat/Bn4ixj84FIZVkwhk2jag6A'}}); +}); + +colors_tweaks.forEach((element) => { + element.addEventListener('click', (e) => { + const color = window.getComputedStyle(e.currentTarget).backgroundColor; + if (e.currentTarget.id.includes('header')){ + telegram.setHeaderColor(color); + } else { + telegram.setBackgroundColor(color); + document.querySelector('.body').style.backgroundColor = telegram.backgroundColor; + }; + }); +}); + +text.telegram_link.addEventListener('click', () => { + telegram.openTelegramLink("https://t.me/joinchat/Bn4ixj84FIZVkwhk2jag6A"); +}); + +text.internal_link.addEventListener('click', () => { + telegram.openLink('https://telegram.org', {try_instant_view: false}); +}); diff --git a/examples/webapp/webapp/style.css b/examples/webapp/webapp/style.css new file mode 100644 index 000000000..30fad466c --- /dev/null +++ b/examples/webapp/webapp/style.css @@ -0,0 +1,338 @@ +:root { + --red-color: #ba4d47; + --yellow-color: #bab05b; +} + +.body { + background-color: (--tg-theme-secondary-bg-color); +} + +* { + font-family: Arial, Helvetica, sans-serif; + margin: 0; + padding: 0; +} + +input { + color: var(--tg-theme-text-color); +} + +table { + width: 93vw; + border-collapse: collapse; + border: solid 0px var(--tg-theme-hint-color); + border-radius: 5px; +} + +th { + font-weight: bold; +} + +td { + font-weight: lighter; + max-width: 46.5vw; + overflow-x: hidden; +} + + +th, td { + padding: 3px; + font-size: 14px; + color: var(--tg-theme-text-color); + text-align: center; + border: solid 1px var(--tg-theme-hint-color); +} + +input:focus { + outline: none; +} + +::placeholder { + color: var(--tg-theme-hint-color); +} + +.content-block { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 9.24px; + background-color: var(--tg-theme-bg-color); + height: 70px; +} + +.content-block_send-message { + flex-direction: column; + height: 160px; +} + +.content-block_tweak-color { + flex-direction: column; + align-items: start; + justify-content: center; + gap: 17px; + height: 96px; +} + +.content-block_cloud-storage { + flex-direction: column; + height: 320px; + gap: 9.6px; +} + +.content-block_link { + height: 104px; + flex-direction: column; +} + +.content-block_event{ + height: 220px; +} + +.content-block_scroll-behaviour, .content-block_close-confirmation { + height: 100px; + align-items: flex-start; + align-self: flex-start; + flex-direction: column; + padding-left: 4.2vw; +} + +.content-block_data { + height: 100%; + flex-direction: column; + align-items: flex-start; + padding:5vh 3vw 5vh 3vw ; + overflow: hidden; +} + +.scroll-behaviour__toggle-box, .content-block__toggle-box { + display: flex; + gap: 9.6px; +} + +.link__content { + display: flex; + flex-direction: row; +} + +.tweak-color__header, .tweak-color__background { + display: flex; + flex-direction: row; + justify-content: flex-end; + gap: 9.6px; + width: 65vw; +} + +.tweak-color__circle-block { + display: flex; + flex-direction: row; + gap: 2vw; +} + +.event__content { + overflow-y: auto; + height: 170px; + width: 88vw; + border: solid 1px var(--tg-theme-hint-color); + border-radius: 5px; + padding: 10px; +} + +.data__content { + padding-top: 10px; + padding-bottom: 10px; +} + +.cloud-storage__input-block { + width: 93vw; + display: flex; + flex-direction: row; + gap: 0px; +} + +.cloud-storage__input-block_remove { + width: 94vw; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.description-block { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin: 10px; + gap: 3px; +} + +.send-message__button{ + border: none; + color: var(--tg-theme-text-color); + background-color: var(--tg-theme-button-color); + border-radius: 5px; + width: 70vw; + height: 40px; +} + +.default-button { + color: var(--tg-theme-text-color); + background-color: var(--tg-theme-button-color); + border: none; + border-radius: 5px; + width: 29vw;; + height: 35px; +} + +.default-button_yellow { + background-color: var(--yellow-color); +} + +.default-button_red { + background-color: var(--red-color); +} + +.default-button_disable { + background: transparent; + color: var(--tg-theme-button-color); + border: solid 1px var(--tg-theme-button-color); +} + +.cloud-storage__button { + border: none; + border-radius: 5px; + color: var(--button_text_color); + width: 93vw; + height: 38px; +} + +.cloud-storage__button_add { + color: var(--tg-theme-text-color); + background-color: var(--tg-theme-button-color); + margin-bottom: 10px; +} + +.cloud-storage__button_remove { + color: var(--tg-theme-text-color); + background-color: var(--red-color); +} + +.header-text { + font-size: 14px; + color: var(--tg-theme-hint-color); + margin-top: 13px; + margin-bottom: 5px; + margin-left: 6px; +} + +.content-block__text { + color: var(--tg-theme-text-color); + font-size: 14px; + padding-bottom: 3px; +} + +.tweak-color__text { + font-size: 13px; + color: var(--tg-theme-text-color); + } + +.data__text { + color: var(--tg-theme-text-color); + font-weight: 300; + font-size: 15px; +} + +.link__text { + font-size: 14px; + color: var(--tg-theme-hint-color); + padding: 2px; +} + +.link__text_blue { + color: var(--tg-theme-link-color); +} + +.event__text { + color: var(--tg-theme-text-color); + font-weight: lighter; + font-size: 13px; +} + + +.description__text { + color: var(--tg-theme-hint-color); + font-size: 13px; + font-weight: 300; +} + +.send-message__input { + border: solid var(--tg-theme-hint-color) 1px; + background-color: transparent; + border-radius: 5px; + width: 67vw; + height: 40px; + padding-left: 10px; +} + +.cloud-storage__input { + border: solid 1px var(--tg-theme-hint-color); + background-color: transparent; + height: 30px; + padding-left: 10px; +} + +.cloud-storage__input_add-key { + border-radius: 5px 0px 0px 5px; + width: 35%; +} + +.cloud-storage__input_add-value { + border-radius: 0px 5px 5px 0px; + width: 65%; +} + +.cloud-storage__input_remove { + width: 90vw; + border: solid 1px var(--tg-theme-hint-color); + border-radius: 5px; + margin-bottom: 10px; +} + +.tweak-color__circle { + border-radius: 100%; + width: 20px; + height: 20px; +} + +.tweak-color__circle_yellow { + background-color: var(--yellow-color); +} + +.tweak-color__circle_red { + background-color: var(--red-color); +} + +.tweak-color__circle_button { + background-color: var(--tg-theme-button-color); +} + +.tweak-color__circle_header { + border: solid 1px var(--tg-theme-hint-color); + background-color: var(--tg-theme-header-bg-color); +} + +.tweak-color__circle_background { + border: solid 1px var(--tg-theme-hint-color); + background-color: var(--tg-theme-secondary-bg-color); +} + +.cloud-storage__table { + margin-bottom: 10px; +} + +.link__list { + color: var(--tg-theme-hint-color); + width: 88vw; +} + From 1bcb2a1d935527faead4e9c3dbea0935f7608443 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 11 Aug 2024 16:36:34 +0500 Subject: [PATCH 1658/1808] Revert "Use deepcopy for async" This reverts commit 74019d03afe7f0e0f4248c0cdf34655617256045. --- telebot/async_telebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 27b861ead..d50cbf7e0 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -32,7 +32,7 @@ import string import random import ssl -import copy + """ Module : telebot @@ -558,7 +558,7 @@ async def _run_middlewares_and_handlers(self, message, handlers, middlewares, up logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return else: - data_copy = copy.deepcopy(data) + data_copy = data.copy() for key in list(data_copy): # remove data from data_copy if handler does not accept it if key not in params: From c79e63e3b300508f3e99b2ad841cc1889f9c6bc2 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 11 Aug 2024 16:36:41 +0500 Subject: [PATCH 1659/1808] Revert "Use deepcopy for sync" This reverts commit 2ae6b9308e05dff5632f2e6bf91301b74d5cb7fc. --- telebot/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 671c5631b..b154d04ca 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -20,7 +20,6 @@ # random module to generate random string import random import string -import copy import ssl @@ -8750,7 +8749,7 @@ def _run_middlewares_and_handler(self, message, handlers, middlewares, update_ty logger.error("It is not allowed to pass data and values inside data to the handler. Check your handler: {}".format(handler['function'])) return else: - data_copy = copy.deepcopy(data) + data_copy = data.copy() for key in list(data_copy): # remove data from data_copy if handler does not accept it if key not in params: From d44ebce95916f845b0493fdf395cd3f8b70ee1d0 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 11 Aug 2024 16:57:42 +0500 Subject: [PATCH 1660/1808] Fix versions --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- telebot/asyncio_storage/redis_storage.py | 2 +- telebot/storage/redis_storage.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index fc7f3cdc3..79db7c8d9 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6657,7 +6657,7 @@ def set_state(self, user_id: int, state: Union[str, State], chat_id: Optional[in Otherwise, if you only set user_id, chat_id will equal to user_id, this means that state will be set for the user in his private chat with a bot. - .. versionchanged:: 4.22.0 + .. versionchanged:: 4.23.0 Added additional parameters to support topics, business connections, and message threads. diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ec782ff91..92bf2e127 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7856,7 +7856,7 @@ async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Otherwise, if you only set user_id, chat_id will equal to user_id, this means that state will be set for the user in his private chat with a bot. - .. versionchanged:: 4.22.0 + .. versionchanged:: 4.23.0 Added additional parameters to support topics, business connections, and message threads. diff --git a/telebot/asyncio_storage/redis_storage.py b/telebot/asyncio_storage/redis_storage.py index eccff5c30..08e255809 100644 --- a/telebot/asyncio_storage/redis_storage.py +++ b/telebot/asyncio_storage/redis_storage.py @@ -297,7 +297,7 @@ def migrate_format(self, bot_id: int, prefix: Optional[str] = "telebot_"): Migrate from old to new format of keys. Run this function once to migrate all redis existing keys to new format. - Starting from version 4.22.0, the format of keys has been changed: + Starting from version 4.23.0, the format of keys has been changed: :value - Old format: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} - New format: diff --git a/telebot/storage/redis_storage.py b/telebot/storage/redis_storage.py index 19f5c2cd5..c9935ac5e 100644 --- a/telebot/storage/redis_storage.py +++ b/telebot/storage/redis_storage.py @@ -283,7 +283,7 @@ def migrate_format(self, bot_id: int, prefix: Optional[str] = "telebot_"): Migrate from old to new format of keys. Run this function once to migrate all redis existing keys to new format. - Starting from version 4.22.0, the format of keys has been changed: + Starting from version 4.23.0, the format of keys has been changed: :value - Old format: {prefix}chat_id: {user_id: {'state': None, 'data': {}}, ...} - New format: From 1540dd03832b4d35e81331d38cf00c18f2a65720 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Aug 2024 11:59:22 +0000 Subject: [PATCH 1661/1808] Bump aiohttp from 3.9.4 to 3.10.2 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.4 to 3.10.2. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.4...v3.10.2) --- updated-dependencies: - dependency-name: aiohttp dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 09014549b..2abfd54bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest requests==2.32.0 wheel==0.38.1 -aiohttp==3.9.4 +aiohttp==3.10.2 From b198c3ab844585b1e327def2e374b96d92535baf Mon Sep 17 00:00:00 2001 From: nypava Date: Tue, 13 Aug 2024 15:15:57 +0300 Subject: [PATCH 1662/1808] async support & file renames --- examples/asynchronous_telebot/mini_app.py | 33 +++++++++++++++++++ examples/{webapp/bot.py => mini_app.py} | 4 ++- .../webapp => mini_app_web}/index.html | 2 +- .../{webapp/webapp => mini_app_web}/script.js | 2 +- .../{webapp/webapp => mini_app_web}/style.css | 0 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 examples/asynchronous_telebot/mini_app.py rename examples/{webapp/bot.py => mini_app.py} (82%) rename examples/{webapp/webapp => mini_app_web}/index.html (99%) rename examples/{webapp/webapp => mini_app_web}/script.js (99%) rename examples/{webapp/webapp => mini_app_web}/style.css (100%) diff --git a/examples/asynchronous_telebot/mini_app.py b/examples/asynchronous_telebot/mini_app.py new file mode 100644 index 000000000..3cdb811cb --- /dev/null +++ b/examples/asynchronous_telebot/mini_app.py @@ -0,0 +1,33 @@ +# The source of the "https://pytelegrambotminiapp.vercel.app" can be found in https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/mini_app_web + +import asyncio +from telebot.async_telebot import AsyncTeleBot +from telebot.types import ( + ReplyKeyboardMarkup, + KeyboardButton, + WebAppInfo, + InlineKeyboardMarkup, + InlineKeyboardButton +) + +BOT_TOKEN = "" +WEB_URL = "https://pytelegrambotminiapp.vercel.app" + +bot = AsyncTeleBot(BOT_TOKEN) + +@bot.message_handler(commands=["start"]) +async def start(message): + reply_keyboard_markup = ReplyKeyboardMarkup(resize_keyboard=True) + reply_keyboard_markup.row(KeyboardButton("Start MiniApp", web_app=WebAppInfo(WEB_URL))) + + inline_keyboard_markup = InlineKeyboardMarkup() + inline_keyboard_markup.row(InlineKeyboardButton('Start MiniApp', web_app=WebAppInfo(WEB_URL))) + + await bot.reply_to(message, "Click the bottom inline button to start MiniApp", reply_markup=inline_keyboard_markup) + await bot.reply_to(message, "Click keyboard button to start MiniApp", reply_markup=reply_keyboard_markup) + +@bot.message_handler(content_types=['web_app_data']) +async def web_app(message): + await bot.reply_to(message, f'Your message is "{message.web_app_data.data}"') + +asyncio.run(bot.polling()) diff --git a/examples/webapp/bot.py b/examples/mini_app.py similarity index 82% rename from examples/webapp/bot.py rename to examples/mini_app.py index 7fa106f09..735acd38c 100644 --- a/examples/webapp/bot.py +++ b/examples/mini_app.py @@ -1,3 +1,5 @@ +# The source of the "https://pytelegrambotminiapp.vercel.app" can be found in https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/mini_app_web + from telebot import TeleBot from telebot.types import ( ReplyKeyboardMarkup, @@ -8,7 +10,7 @@ ) BOT_TOKEN = "" -WEB_URL = "https://pytelegrambotminiapp.vercel.app" # https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/webapp/webapp +WEB_URL = "https://pytelegrambotminiapp.vercel.app" bot = TeleBot(BOT_TOKEN) diff --git a/examples/webapp/webapp/index.html b/examples/mini_app_web/index.html similarity index 99% rename from examples/webapp/webapp/index.html rename to examples/mini_app_web/index.html index 6f7d79ff7..22bd86ec9 100644 --- a/examples/webapp/webapp/index.html +++ b/examples/mini_app_web/index.html @@ -127,7 +127,7 @@

closing confirmation [Bot API 6.2+]

-

Confimation dialog when closing mini app

+

Confimation dialog when closing miniapp

diff --git a/examples/webapp/webapp/script.js b/examples/mini_app_web/script.js similarity index 99% rename from examples/webapp/webapp/script.js rename to examples/mini_app_web/script.js index 42889b055..4022ed5f0 100644 --- a/examples/webapp/webapp/script.js +++ b/examples/mini_app_web/script.js @@ -124,7 +124,7 @@ text.platform.innerText += telegram.platform; button.send_message.addEventListener('click', () => { telegram.sendData(input.message.value); - if (telegram.initDataUnsafe) telegram.showAlert('SendData only works in webapp that opened with reply keyboard.') + if (telegram.initDataUnsafe) telegram.showAlert('SendData only works in miniapp that opened with reply keyboard.') input.message.value = '' }); diff --git a/examples/webapp/webapp/style.css b/examples/mini_app_web/style.css similarity index 100% rename from examples/webapp/webapp/style.css rename to examples/mini_app_web/style.css From 407982da37415c9cc2f743679a6ca69c1959834f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Thu, 15 Aug 2024 12:02:01 +0300 Subject: [PATCH 1663/1808] Bump version for intermediate release --- 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 1b8c80931..f6cdb5283 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.22.0' +release = '4.22.1' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index a45a8dd3b..20b08e9f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.22.0" +version = "4.22.1" 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 3bbf77eb0..86d088045 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.22.0' +__version__ = '4.22.1' From 77c802c1ad7f82daeb1a40bd11cdd7787baf5cc2 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 15 Aug 2024 14:57:57 +0500 Subject: [PATCH 1664/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76f8f487e..1739e7595 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From 737e7e4e190acc7dfb2180493236359fe052bf80 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 15 Aug 2024 14:58:12 +0500 Subject: [PATCH 1665/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1739e7595..5f70cad2f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From 80cddf018e82570d31102d4782f32a471dbd6adf Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 15 Aug 2024 15:05:28 +0500 Subject: [PATCH 1666/1808] Added the field paid_media to the class TransactionPartnerUser for transactions involving paid media. --- telebot/__init__.py | 8 ++++++-- telebot/apihelper.py | 5 ++++- telebot/async_telebot.py | 8 +++++--- telebot/asyncio_helper.py | 7 +++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 33a353de7..a84c6660d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3136,7 +3136,8 @@ def send_paid_media( caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Message: + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, + ) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -3175,6 +3176,9 @@ def send_paid_media( :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` or :class:`telebot.types.ForceReply` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3183,7 +3187,7 @@ def send_paid_media( self.token, chat_id, star_count, media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, - reply_parameters=reply_parameters, reply_markup=reply_markup) + reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 2ee86abab..96a686b2e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -529,7 +529,8 @@ def send_photo( def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, - disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None): + disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, + business_connection_id=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -549,6 +550,8 @@ def send_paid_media( payload['reply_parameters'] = reply_parameters.to_json() if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request( token, method_url, params=payload, method='post' if files else 'get', diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8647897b6..3cc008daf 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4564,7 +4564,7 @@ async def send_paid_media( caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None) -> types.Message: + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -4603,6 +4603,9 @@ async def send_paid_media( :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardMarkup` or :class:`telebot.types.ReplyKeyboardRemove` or :class:`telebot.types.ForceReply` + :param business_connection_id: Identifier of a business connection, in which the message will be sent + :type business_connection_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4611,8 +4614,7 @@ async def send_paid_media( self.token, chat_id, star_count, media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, - reply_parameters=reply_parameters, reply_markup=reply_markup) - ) + reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id)) async def send_media_group( self, chat_id: Union[int, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index fa14f6123..94af21eb8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -309,7 +309,7 @@ async def send_message( if message_effect_id: params['message_effect_id'] = message_effect_id - return await _process_request(token, method_name, params=params) + return await _process_request(token, method_name, params=params, method='post') # methods @@ -519,7 +519,8 @@ async def send_photo( async def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, - disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None): + disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, + business_connection_id=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -539,6 +540,8 @@ async def send_paid_media( payload['reply_parameters'] = reply_parameters.to_json() if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request( token, method_url, params=payload, method='post' if files else 'get', From 496cc7c61ec377d572f2ca71756b6c029cfde00e Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 15 Aug 2024 15:08:12 +0500 Subject: [PATCH 1667/1808] Added the field paid_media to the class TransactionPartnerUser for transactions involving paid media. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 21480e31d..ade17eb6e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10377,20 +10377,26 @@ class TransactionPartnerUser(TransactionPartner): :param invoice_payload: Optional, Bot-specified invoice payload :type invoice_payload: :obj:`str` + :param paid_media: Optional. Information about the paid media bought by the user + :type paid_media: :obj:`list` of :class:`PaidMedia` + :return: Instance of the class :rtype: :class:`TransactionPartnerUser` """ - def __init__(self, type, user, invoice_payload=None, **kwargs): + def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, **kwargs): self.type: str = type self.user: User = user self.invoice_payload: Optional[str] = invoice_payload + self.paid_media: Optional[List[PaidMedia]] = paid_media @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) + if 'paid_media' in obj: + obj['paid_media'] = [PaidMedia.de_json(media) for media in obj['paid_media']] return cls(**obj) class TransactionPartnerTelegramAds(TransactionPartner): From 80a5b94505d2bd6b6730eb99b8d876f66c3d6210 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 15 Aug 2024 15:19:00 +0500 Subject: [PATCH 1668/1808] added 2 methods --- telebot/__init__.py | 56 ++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 20 ++++++++++++++ telebot/async_telebot.py | 57 +++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 22 +++++++++++++++ 4 files changed, 155 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index a84c6660d..658f70e58 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4199,6 +4199,62 @@ def edit_chat_invite_link( apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request) ) + def create_chat_subscription_invite_link( + self, chat_id: Union[int, str], subscription_period: int, subscription_price: int, + name: Optional[str]=None) -> types.ChatInviteLink: + """ + Use this method to create a subscription invite link for a channel chat. The bot must have the can_invite_users administrator rights. + The link can be edited using the method editChatSubscriptionInviteLink or revoked using the method revokeChatInviteLink. + Returns the new invite link as a ChatInviteLink object. + + Telegram documentation: https://core.telegram.org/bots/api#createchatsubscriptioninvitelink + + :param chat_id: Unique identifier for the target channel chat or username of the target channel + (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + + :param subscription_period: The number of seconds the subscription will be active for before the next payment. + Currently, it must always be 2592000 (30 days). + :type subscription_period: :obj:`int` + + :param subscription_price: The amount of Telegram Stars a user must pay initially and after each subsequent + subscription period to be a member of the chat; 1-2500 + :type subscription_price: :obj:`int` + + :return: Returns the new invite link as a ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` + """ + return types.ChatInviteLink.de_json( + apihelper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name) + ) + + def edit_chat_subscription_invite_link( + self, chat_id: Union[int, str], invite_link: str, name: Optional[str]=None) -> types.ChatInviteLink: + """ + Use this method to edit a subscription invite link created by the bot. The bot must have the can_invite_users administrator rights. + Returns the edited invite link as a ChatInviteLink object. + + Telegram documentation: https://core.telegram.org/bots/api#editchatsubscriptioninvitelink + + :param chat_id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param invite_link: The invite link to edit + :type invite_link: :obj:`str` + + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + + :return: Returns the edited invite link as a ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` + """ + return types.ChatInviteLink.de_json( + apihelper.edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name) + ) def revoke_chat_invite_link( self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 96a686b2e..495fa293d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1205,6 +1205,26 @@ def edit_chat_invite_link(token, chat_id, invite_link, name, expire_date, member return _make_request(token, method_url, params=payload, method='post') +def create_chat_subscription_invite_link(token, chat_id, subscription_period, subscription_price, name=None): + method_url = 'createChatSubscriptionInviteLink' + payload = { + 'chat_id': chat_id, + 'subscription_period': subscription_period, + 'subscription_price': subscription_price + } + if name: + payload['name'] = name + return _make_request(token, method_url, params=payload, method='post') + +def edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name=None): + method_url = 'editChatSubscriptionInviteLink' + payload = { + 'chat_id': chat_id, + 'invite_link': invite_link + } + if name: + payload['name'] = name + return _make_request(token, method_url, params=payload, method='post') def revoke_chat_invite_link(token, chat_id, invite_link): method_url = 'revokeChatInviteLink' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3cc008daf..5757a68bc 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5605,6 +5605,63 @@ async def edit_chat_invite_link( return types.ChatInviteLink.de_json( await asyncio_helper.edit_chat_invite_link(self.token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request) ) + + async def create_chat_subscription_invite_link( + self, chat_id: Union[int, str], subscription_period: int, subscription_price: int, + name: Optional[str]=None) -> types.ChatInviteLink: + """ + Use this method to create a subscription invite link for a channel chat. The bot must have the can_invite_users administrator rights. + The link can be edited using the method editChatSubscriptionInviteLink or revoked using the method revokeChatInviteLink. + Returns the new invite link as a ChatInviteLink object. + + Telegram documentation: https://core.telegram.org/bots/api#createchatsubscriptioninvitelink + + :param chat_id: Unique identifier for the target channel chat or username of the target channel + (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + + :param subscription_period: The number of seconds the subscription will be active for before the next payment. + Currently, it must always be 2592000 (30 days). + :type subscription_period: :obj:`int` + + :param subscription_price: The amount of Telegram Stars a user must pay initially and after each subsequent + subscription period to be a member of the chat; 1-2500 + :type subscription_price: :obj:`int` + + :return: Returns the new invite link as a ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name) + ) + + async def edit_chat_subscription_invite_link( + self, chat_id: Union[int, str], invite_link: str, name: Optional[str]=None) -> types.ChatInviteLink: + """ + Use this method to edit a subscription invite link created by the bot. The bot must have the can_invite_users administrator rights. + Returns the edited invite link as a ChatInviteLink object. + + Telegram documentation: https://core.telegram.org/bots/api#editchatsubscriptioninvitelink + + :param chat_id: Unique identifier for the target chat or username of the target channel + (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param invite_link: The invite link to edit + :type invite_link: :obj:`str` + + :param name: Invite link name; 0-32 characters + :type name: :obj:`str` + + :return: Returns the edited invite link as a ChatInviteLink object. + :rtype: :class:`telebot.types.ChatInviteLink` + """ + return types.ChatInviteLink.de_json( + await asyncio_helper.edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name) + ) async def revoke_chat_invite_link( self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 94af21eb8..c31bf53da 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1185,6 +1185,28 @@ async def edit_chat_invite_link(token, chat_id, invite_link, name, expire_date, return await _process_request(token, method_url, params=payload, method='post') +async def create_chat_subscription_invite_link(token, chat_id, subscription_period, subscription_price, name=None): + method_url = 'createChatSubscriptionInviteLink' + payload = { + 'chat_id': chat_id, + 'subscription_period': subscription_period, + 'subscription_price': subscription_price + } + if name: + payload['name'] = name + return await _process_request(token, method_url, params=payload, method='post') + +async def edit_chat_subscription_invite_link(token, chat_id, invite_link, name=None): + method_url = 'editChatSubscriptionInviteLink' + payload = { + 'chat_id': chat_id, + 'invite_link': invite_link + } + if name: + payload['name'] = name + return await _process_request(token, method_url, params=payload, method='post') + + async def revoke_chat_invite_link(token, chat_id, invite_link): method_url = 'revokeChatInviteLink' payload = { From d6216479b48fd029accdefed799fb4deb83be7eb Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 15 Aug 2024 15:20:27 +0500 Subject: [PATCH 1669/1808] fix --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 495fa293d..bc15e9316 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1216,7 +1216,7 @@ def create_chat_subscription_invite_link(token, chat_id, subscription_period, su payload['name'] = name return _make_request(token, method_url, params=payload, method='post') -def edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name=None): +def edit_chat_subscription_invite_link(token, chat_id, invite_link, name=None): method_url = 'editChatSubscriptionInviteLink' payload = { 'chat_id': chat_id, From 6c4599254422ef82925d7f1dfe46d751a2e6d356 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 15 Aug 2024 15:23:46 +0500 Subject: [PATCH 1670/1808] added reactiontypepaid --- telebot/types.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index ade17eb6e..1f85b14b5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8465,6 +8465,27 @@ def to_dict(self) -> dict: return json_dict +class ReactionTypePaid(ReactionType): + """ + This object represents a paid reaction type. + + Telegram documentation: https://core.telegram.org/bots/api#reactiontypepaid + + :param type: Type of the reaction, must be paid + :type type: :obj:`str` + + :return: Instance of the class + :rtype: :class:`ReactionTypePaid` + """ + + def __init__(self, **kwargs) -> None: + super().__init__('paid') + + def to_dict(self) -> dict: + return super().to_dict() + + + class MessageReactionUpdated(JsonDeserializable): """ This object represents a service message about a change in the list of the current user's reactions to a message. From b5f8fe8ea5583789ee6a91a8ecee28982d502a6b Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 16 Aug 2024 10:49:40 +0500 Subject: [PATCH 1671/1808] Fix states for webhooks --- telebot/asyncio_filters.py | 2 +- telebot/custom_filters.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 20abe5464..77c0559a6 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -398,7 +398,7 @@ async def check(self, message, text): :meta private: """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot._user.id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot.bot_id) if chat_id is None: chat_id = user_id # May change in future diff --git a/telebot/custom_filters.py b/telebot/custom_filters.py index 2e91e55ea..f56535a57 100644 --- a/telebot/custom_filters.py +++ b/telebot/custom_filters.py @@ -404,7 +404,7 @@ def check(self, message, text): :meta private: """ - chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot._user.id) + chat_id, user_id, business_connection_id, bot_id, message_thread_id = resolve_context(message, self.bot.bot_id) if chat_id is None: chat_id = user_id # May change in future From 31a73ff14faec4b4158e204177bc1363a5060393 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Aug 2024 12:45:27 +0300 Subject: [PATCH 1672/1808] Fix some type hints and warnings --- telebot/types.py | 66 +++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 21480e31d..28ea9eca4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1525,7 +1525,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.json = json_string @property - def html_text(self) -> str: + def html_text(self) -> Optional[str]: """ Returns html-rendered text. """ @@ -1534,7 +1534,7 @@ def html_text(self) -> str: return apply_html_entities(self.text, self.entities, getattr(self, "custom_subs", None)) @property - def html_caption(self) -> str: + def html_caption(self) -> Optional[str]: """ Returns html-rendered caption. """ @@ -2734,6 +2734,7 @@ def __init__(self, text: str, request_contact: Optional[bool]=None, if request_user is not None: logger.warning('The parameter "request_user" is deprecated, use "request_users" instead') if self.request_users is None: + # noinspection PyTypeChecker self.request_users = request_user @@ -4268,6 +4269,7 @@ def __init__(self, result_id, from_user, query, location=None, inline_message_id self.query: str = query +# noinspection PyShadowingBuiltins class InlineQueryResultBase(ABC, Dictionaryable, JsonSerializable): """ This object represents one result of an inline query. Telegram clients currently support results of the following 20 types: @@ -5407,8 +5409,8 @@ class InlineQueryResultCachedBase(ABC, JsonSerializable): Base class of all InlineQueryResultCached* classes. """ def __init__(self): - self.type: str = None - self.id: str = None + self.type: str = "" + self.id: str = "" self.title: Optional[str] = None self.description: Optional[str] = None self.caption: Optional[str] = None @@ -5416,6 +5418,7 @@ def __init__(self): self.input_message_content: Optional[InputMessageContent] = None self.parse_mode: Optional[str] = None self.caption_entities: Optional[List[MessageEntity]] = None + # noinspection PyTypeChecker self.payload_dic: Dict[str] = {} self.show_caption_above_media: Optional[bool] = None @@ -7104,6 +7107,7 @@ def to_dict(self): return json_dict +# noinspection PyShadowingBuiltins class Poll(JsonDeserializable): """ This object contains information about a poll. @@ -7586,7 +7590,7 @@ def to_dict(self): raise NotImplementedError -# noinspection PyUnusedLocal +# noinspection PyUnusedLocal,PyShadowingBuiltins class MenuButtonCommands(MenuButton): """ Represents a menu button, which opens the bot's list of commands. @@ -7610,7 +7614,7 @@ def to_json(self): return json.dumps(self.to_dict()) -# noinspection PyUnusedLocal +# noinspection PyUnusedLocal,PyShadowingBuiltins class MenuButtonWebApp(MenuButton): """ Represents a menu button, which launches a Web App. @@ -7645,7 +7649,7 @@ def to_json(self): return json.dumps(self.to_dict()) -# noinspection PyUnusedLocal +# noinspection PyUnusedLocal,PyShadowingBuiltins class MenuButtonDefault(MenuButton): """ Describes that no specific value for the menu button was set. @@ -8202,7 +8206,7 @@ def to_dict(self) -> dict: def to_json(self) -> str: return json.dumps(self.to_dict()) - def convert_input_sticker(self) -> Tuple[dict, Optional[dict]]: + def convert_input_sticker(self) -> Tuple[str, Optional[dict]]: if service_utils.is_string(self.sticker): return self.to_json(), None @@ -8343,6 +8347,7 @@ def to_json(self) -> str: return json.dumps(self.to_dict()) +# noinspection PyShadowingBuiltins class Story(JsonDeserializable): """ This object represents a story. @@ -9250,12 +9255,12 @@ def __init__(self, request_id: int, users: List[SharedUser], **kwargs): self.users: List[SharedUser] = users @property - def user_id(self) -> int: + def user_id(self) -> None: logger.warning('The parameter "user_id" is deprecated, use "user_ids" instead') return None @property - def user_ids(self) -> List[int]: + def user_ids(self) -> List[SharedUser]: logger.warning('The parameter "user_ids" is deprecated, use "users" instead') return self.users @@ -10217,8 +10222,9 @@ def de_json(cls, json_string): elif obj["type"] == "failed": return RevenueWithdrawalStateFailed.de_json(obj) return None - + +# noinspection PyShadowingBuiltins class RevenueWithdrawalStatePending(RevenueWithdrawalState): """ The withdrawal is in progress. @@ -10232,7 +10238,6 @@ class RevenueWithdrawalStatePending(RevenueWithdrawalState): :rtype: :class:`RevenueWithdrawalStatePending` """ - # noinspection PyPackageRequirements def __init__(self, type, **kwargs): self.type: str = type @@ -10243,6 +10248,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState): """ The withdrawal succeeded. @@ -10262,7 +10268,6 @@ class RevenueWithdrawalStateSucceeded(RevenueWithdrawalState): :rtype: :class:`RevenueWithdrawalStateSucceeded` """ - # noinspection PyPackageRequirements def __init__(self, type, date, url, **kwargs): self.type: str = type self.date: int = date @@ -10273,9 +10278,9 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - - + +# noinspection PyShadowingBuiltins class RevenueWithdrawalStateFailed(RevenueWithdrawalState): """ The withdrawal failed and the transaction was refunded. @@ -10289,7 +10294,6 @@ class RevenueWithdrawalStateFailed(RevenueWithdrawalState): :rtype: :class:`RevenueWithdrawalStateFailed` """ - # noinspection PyPackageRequirements def __init__(self, type, **kwargs): self.type: str = type @@ -10329,7 +10333,9 @@ def de_json(cls, json_string): return TransactionPartnerTelegramAds.de_json(obj) elif obj["type"] == "other": return TransactionPartnerOther.de_json(obj) - + + +# noinspection PyShadowingBuiltins class TransactionPartnerFragment(TransactionPartner): """ Describes a withdrawal transaction with Fragment. @@ -10347,7 +10353,6 @@ class TransactionPartnerFragment(TransactionPartner): """ - # noinspection PyPackageRequirements def __init__(self, type, withdrawal_state=None, **kwargs): self.type: str = type self.withdrawal_state: Optional[RevenueWithdrawalState] = withdrawal_state @@ -10359,9 +10364,9 @@ def de_json(cls, json_string): if 'withdrawal_state' in obj: obj['withdrawal_state'] = RevenueWithdrawalState.de_json(obj['withdrawal_state']) return cls(**obj) - +# noinspection PyShadowingBuiltins class TransactionPartnerUser(TransactionPartner): """ Describes a transaction with a user. @@ -10392,7 +10397,9 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) return cls(**obj) - + + +# noinspection PyShadowingBuiltins class TransactionPartnerTelegramAds(TransactionPartner): """ Describes a transaction with Telegram Ads. @@ -10413,8 +10420,10 @@ def __init__(self, type, **kwargs): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - - + return obj + + +# noinspection PyShadowingBuiltins class TransactionPartnerOther(TransactionPartner): """ Describes a transaction with an unknown source or recipient. @@ -10436,9 +10445,9 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - +# noinspection PyShadowingBuiltins class StarTransaction(JsonDeserializable): """ Describes a Telegram Star transaction. @@ -10531,7 +10540,9 @@ def de_json(cls, json_string): return PaidMediaPhoto.de_json(obj) elif obj["type"] == "video": return PaidMediaVideo.de_json(obj) - + + +# noinspection PyShadowingBuiltins class PaidMediaPreview(PaidMedia): """ The paid media isn't available before the payment. @@ -10565,8 +10576,9 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - + +# noinspection PyShadowingBuiltins class PaidMediaPhoto(PaidMedia): """ The paid media is a photo. @@ -10595,8 +10607,9 @@ def de_json(cls, json_string): obj['photo'] = [PhotoSize.de_json(photo) for photo in obj['photo']] return cls(**obj) - + +# noinspection PyShadowingBuiltins class PaidMediaVideo(PaidMedia): """ The paid media is a video. @@ -10653,6 +10666,7 @@ def __init__(self, star_count, paid_media, **kwargs): self.paid_media: List[PaidMedia] = paid_media +# noinspection PyShadowingBuiltins class InputPaidMedia(JsonSerializable): """ This object describes the paid media to be sent. Currently, it can be one of From 2d41d2a516ed5cf513d5ab7ae30a67f32c9a55d7 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Aug 2024 13:23:07 +0300 Subject: [PATCH 1673/1808] any_text / any_entities to Message --- telebot/types.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 28ea9eca4..e972696b1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1618,6 +1618,14 @@ def user_shared(self): logger.warning('The parameter "user_shared" is deprecated, use "users_shared" instead') return self.users_shared + @property + def any_text(self): + return self.caption if (self.caption is not None) else self.text + + @property + def any_entities(self): + return self.caption_entities if (self.caption_entities is not None) else self.entities + # noinspection PyShadowingBuiltins class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): From 8dcfdc5e6bfb5d5633c8c55e1ac3f84030526140 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Aug 2024 19:08:25 +0300 Subject: [PATCH 1674/1808] any_xxx typehints added --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index e972696b1..badd99a75 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1619,11 +1619,11 @@ def user_shared(self): return self.users_shared @property - def any_text(self): + def any_text(self) -> Optional[str]: return self.caption if (self.caption is not None) else self.text @property - def any_entities(self): + def any_entities(self) -> Optional[List[MessageEntity]]: return self.caption_entities if (self.caption_entities is not None) else self.entities From 25266a17a624f1e40f448fc7c2626f8f79fc4390 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 17 Aug 2024 22:36:58 +0500 Subject: [PATCH 1675/1808] use named parameters for creating and editing chat subcription links --- telebot/__init__.py | 4 ++-- telebot/async_telebot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 658f70e58..bddcda75b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4228,7 +4228,7 @@ def create_chat_subscription_invite_link( :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( - apihelper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name) + apihelper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name=name) ) def edit_chat_subscription_invite_link( @@ -4253,7 +4253,7 @@ def edit_chat_subscription_invite_link( :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( - apihelper.edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name) + apihelper.edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name=name) ) def revoke_chat_invite_link( diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 5757a68bc..e2b77b0e6 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5635,7 +5635,7 @@ async def create_chat_subscription_invite_link( :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( - await asyncio_helper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name) + await asyncio_helper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name=name) ) async def edit_chat_subscription_invite_link( @@ -5660,7 +5660,7 @@ async def edit_chat_subscription_invite_link( :rtype: :class:`telebot.types.ChatInviteLink` """ return types.ChatInviteLink.de_json( - await asyncio_helper.edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name) + await asyncio_helper.edit_chat_subscription_invite_link(self.token, chat_id, invite_link, name=name) ) async def revoke_chat_invite_link( From dd30e78dd45c53165f579d089c5cb9e76d213a27 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 17 Aug 2024 22:46:45 +0300 Subject: [PATCH 1676/1808] Bump version --- 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 f6cdb5283..87d25810f 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.22.1' +release = '4.23.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 72f0a2962..07f2ce727 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.22.1" +version = "4.23.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 86d088045..bf02d6515 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.22.1' +__version__ = '4.23.0' From 85c471970e1b722a55fb36a417471f9047077016 Mon Sep 17 00:00:00 2001 From: GautamKumar Date: Wed, 21 Aug 2024 02:56:27 +0530 Subject: [PATCH 1677/1808] Fix: Correct order of parameters in forward_messages --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e2b77b0e6..fcb0df2d1 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3439,7 +3439,7 @@ async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[s disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - result = await asyncio_helper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, protect_content, message_thread_id) + result = await asyncio_helper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, protect_content) return [types.MessageID.de_json(message_id) for message_id in result] async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], From 60d830391ffb80a8df754651a98c6f9dee834542 Mon Sep 17 00:00:00 2001 From: Emil Relecto Date: Wed, 21 Aug 2024 14:09:56 +0200 Subject: [PATCH 1678/1808] set answer_pre_checkout_query query_id argument type to str As per the documentation - https://core.telegram.org/bots/api#answerprecheckoutquery --- telebot/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index bddcda75b..95b6cca46 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5627,7 +5627,7 @@ def answer_shipping_query( def answer_pre_checkout_query( - self, pre_checkout_query_id: int, ok: bool, + self, pre_checkout_query_id: str, ok: bool, error_message: Optional[str]=None) -> bool: """ Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the From 99d3243ee13156d53b96327e29fcba1a9d51f320 Mon Sep 17 00:00:00 2001 From: Emil Relecto Date: Wed, 21 Aug 2024 14:16:00 +0200 Subject: [PATCH 1679/1808] set answer_pre_checkout_query query id type to string. in async_telebot.py --- telebot/async_telebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fcb0df2d1..4a71772a4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6977,7 +6977,7 @@ async def answer_shipping_query( return await asyncio_helper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) async def answer_pre_checkout_query( - self, pre_checkout_query_id: int, ok: bool, + self, pre_checkout_query_id: str, ok: bool, error_message: Optional[str]=None) -> bool: """ Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the From 377636774d7d5eaa1d5b49ebbd094c2d96361103 Mon Sep 17 00:00:00 2001 From: pnwclw <45664035+pnwclw@users.noreply.github.com> Date: Mon, 2 Sep 2024 22:38:59 +0200 Subject: [PATCH 1680/1808] Fixed typing for `request_name` parameter --- telebot/types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 70907aa1f..8b4c30469 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2561,13 +2561,13 @@ class KeyboardButtonRequestUsers(Dictionaryable): """ def __init__( self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, - max_quantity: Optional[int]=None, request_name: Optional[str]=None, request_username: Optional[bool]=None, + max_quantity: Optional[int]=None, request_name: Optional[bool]=None, request_username: Optional[bool]=None, request_photo: Optional[bool]=None) -> None: self.request_id: int = request_id self.user_is_bot: Optional[bool] = user_is_bot self.user_is_premium: Optional[bool] = user_is_premium self.max_quantity: Optional[int] = max_quantity - self.request_name: Optional[str] = request_name + self.request_name: Optional[bool] = request_name self.request_username: Optional[bool] = request_username self.request_photo: Optional[bool] = request_photo @@ -10856,4 +10856,4 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - \ No newline at end of file + From 6ebe88dd310fd22a526044d3e207a3017737623f Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 6 Sep 2024 17:58:05 +0400 Subject: [PATCH 1681/1808] added purchased paid media --- telebot/__init__.py | 66 +++++++++++++++++++++++++++++++++++++--- telebot/async_telebot.py | 63 ++++++++++++++++++++++++++++++++++++++ telebot/types.py | 36 ++++++++++++++++++++-- 3 files changed, 159 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 95b6cca46..a7071438e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -248,6 +248,7 @@ def __init__( self.business_message_handlers = [] self.edited_business_message_handlers = [] self.deleted_business_messages_handlers = [] + self.purchased_paid_media_handlers = [] self.custom_filters = {} self.state_handlers = [] @@ -724,6 +725,7 @@ def process_new_updates(self, updates: List[types.Update]): new_business_messages = None new_edited_business_messages = None new_deleted_business_messages = None + new_purchased_paid_media = None for update in updates: if apihelper.ENABLE_MIDDLEWARE and not self.use_class_middlewares: @@ -805,8 +807,10 @@ def process_new_updates(self, updates: List[types.Update]): if update.deleted_business_messages: if new_deleted_business_messages is None: new_deleted_business_messages = [] new_deleted_business_messages.append(update.deleted_business_messages) - - + if update.purchased_paid_media: + if new_purchased_paid_media is None: new_purchased_paid_media = [] + new_purchased_paid_media.append(update.purchased_paid_media) + if new_messages: self.process_new_messages(new_messages) if new_edited_messages: @@ -851,6 +855,8 @@ def process_new_updates(self, updates: List[types.Update]): self.process_new_edited_business_message(new_edited_business_messages) if new_deleted_business_messages: self.process_new_deleted_business_messages(new_deleted_business_messages) + if new_purchased_paid_media: + self.process_new_purchased_paid_media(new_purchased_paid_media) def process_new_messages(self, new_messages): """ @@ -987,8 +993,11 @@ def process_new_deleted_business_messages(self, new_deleted_business_messages): """ self._notify_command_handlers(self.deleted_business_messages_handlers, new_deleted_business_messages, 'deleted_business_messages') - - + def process_new_purchased_paid_media(self, new_purchased_paid_media): + """ + :meta private: + """ + self._notify_command_handlers(self.purchased_paid_media_handlers, new_purchased_paid_media, 'purchased_paid_media') def process_middlewares(self, update): """ @@ -8035,6 +8044,55 @@ def register_pre_checkout_query_handler(self, callback: Callable, func: Callable handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_pre_checkout_query_handler(handler_dict) + def purchased_paid_media_handler(self, func=None, **kwargs): + """ + Handles new incoming purchased paid media. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_purchased_paid_media_handler(handler_dict) + return handler + + return decorator + + def add_purchased_paid_media_handler(self, handler_dict): + """ + Adds a purchased paid media handler + Note that you should use register_purchased_paid_media_handler to add purchased_paid_media_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.purchased_paid_media_handlers.append(handler_dict) + + def register_purchased_paid_media_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers purchased paid media handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_purchased_paid_media_handler(handler_dict) def poll_handler(self, func, **kwargs): """ diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4a71772a4..eff7fb931 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -183,6 +183,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ self.business_message_handlers = [] self.edited_business_message_handlers = [] self.deleted_business_messages_handlers = [] + self.purchased_paid_media_handlers = [] self.custom_filters = {} self.state_handlers = [] @@ -636,6 +637,7 @@ async def process_new_updates(self, updates: List[types.Update]): new_business_messages = None new_edited_business_messages = None new_deleted_business_messages = None + new_purchased_paid_media = None for update in updates: @@ -706,6 +708,9 @@ async def process_new_updates(self, updates: List[types.Update]): if update.deleted_business_messages: if new_deleted_business_messages is None: new_deleted_business_messages = [] new_deleted_business_messages.append(update.deleted_business_messages) + if update.purchased_paid_media: + if new_purchased_paid_media is None: new_purchased_paid_media = [] + new_purchased_paid_media.append(update.purchased_paid_media) if new_messages: @@ -750,6 +755,8 @@ async def process_new_updates(self, updates: List[types.Update]): await self.process_new_edited_business_message(new_edited_business_messages) if new_deleted_business_messages: await self.process_new_deleted_business_messages(new_deleted_business_messages) + if new_purchased_paid_media: + await self.process_new_purchased_paid_media(new_purchased_paid_media) async def process_new_messages(self, new_messages): """ @@ -884,6 +891,12 @@ async def process_new_deleted_business_messages(self, new_deleted_business_messa """ await self._process_updates(self.deleted_business_messages_handlers, new_deleted_business_messages, 'deleted_business_messages') + async def process_new_purchased_paid_media(self, new_purchased_paid_media): + """ + :meta private: + """ + await self._process_updates(self.purchased_paid_media_handlers, new_purchased_paid_media, 'purchased_paid_media') + async def _get_middlewares(self, update_type): """ :meta private: @@ -1867,6 +1880,56 @@ def register_pre_checkout_query_handler(self, callback: Callable[[Any], Awaitabl handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) self.add_pre_checkout_query_handler(handler_dict) + def purchased_paid_media_handler(self, func=None, **kwargs): + """ + Handles new incoming purchased paid media. + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + def decorator(handler): + handler_dict = self._build_handler_dict(handler, func=func, **kwargs) + self.add_purchased_paid_media_handler(handler_dict) + return handler + + return decorator + + def add_purchased_paid_media_handler(self, handler_dict): + """ + Adds a purchased paid media handler + Note that you should use register_purchased_paid_media_handler to add purchased_paid_media_handler to the bot. + + :meta private: + + :param handler_dict: + :return: + """ + self.purchased_paid_media_handlers.append(handler_dict) + + def register_purchased_paid_media_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): + """ + Registers purchased paid media handler. + + :param callback: function to be called + :type callback: :obj:`function` + + :param func: Function executed as a filter + :type func: :obj:`function` + + :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) + :type pass_bot: :obj:`bool` + + :param kwargs: Optional keyword arguments(custom filters) + + :return: None + """ + handler_dict = self._build_handler_dict(callback, func=func, pass_bot=pass_bot, **kwargs) + self.add_purchased_paid_media_handler(handler_dict) + def poll_handler(self, func, **kwargs): """ Handles new state of a poll. Bots receive only updates about stopped polls and polls, which are sent by the bot diff --git a/telebot/types.py b/telebot/types.py index 8b4c30469..df2959e6a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -153,6 +153,9 @@ class Update(JsonDeserializable): checkout :type pre_checkout_query: :class:`telebot.types.PreCheckoutQuery` + :purchased_paid_media: Optional. A user purchased paid media with a non-empty payload sent by the bot in a non-channel chat + :type purchased_paid_media: :class:`telebot.types.PaidMediaPurchased` + :param poll: Optional. New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot :type poll: :class:`telebot.types.Poll` @@ -222,18 +225,19 @@ def de_json(cls, json_string): business_message = Message.de_json(obj.get('business_message')) edited_business_message = Message.de_json(obj.get('edited_business_message')) deleted_business_messages = BusinessMessagesDeleted.de_json(obj.get('deleted_business_messages')) + purchased_paid_media = PaidMediaPurchased.de_json(obj.get('purchased_paid_media')) 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) + deleted_business_messages, purchased_paid_media) 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): + deleted_business_messages, purchased_paid_media): self.update_id: int = update_id self.message: Optional[Message] = message self.edited_message: Optional[Message] = edited_message @@ -257,6 +261,7 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.business_message: Optional[Message] = business_message self.edited_business_message: Optional[Message] = edited_business_message self.deleted_business_messages: Optional[BusinessMessagesDeleted] = deleted_business_messages + self.purchased_paid_media: Optional[PaidMediaPurchased] = purchased_paid_media @@ -10857,3 +10862,30 @@ def de_json(cls, json_string): return cls(**obj) +class PaidMediaPurchased(JsonDeserializable): + """ + This object contains information about a paid media purchase. + + Telegram documentation: https://core.telegram.org/bots/api#paidmediapurchased + + :param from_user: User who purchased the media + :type from_user: :class:`User` + + :param paid_media_payload: Bot-specified paid media payload + :type paid_media_payload: :obj:`str` + + :return: Instance of the class + :rtype: :class:`PaidMediaPurchased` + """ + + def __init__(self, from_user, paid_media_payload, **kwargs): + self.from_user: User = from_user + self.paid_media_payload: str = paid_media_payload + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['from_user'] = User.de_json(obj['from_user']) + return cls(**obj) + From e3081fb62d4db8d5fa8cb4ec468b0e6a361309d5 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 6 Sep 2024 18:04:57 +0400 Subject: [PATCH 1682/1808] added payload to sendpaidmedia --- telebot/__init__.py | 7 ++++++- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 9 +++++++-- telebot/asyncio_helper.py | 5 ++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a7071438e..6a874ea9e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3146,6 +3146,7 @@ def send_paid_media( show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, + payload: Optional[str]=None ) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -3188,6 +3189,9 @@ def send_paid_media( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param payload: Bot-defined paid media payload, 0-128 bytes. This will not be displayed to the user, use it for your internal processes. + :type payload: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3196,7 +3200,8 @@ def send_paid_media( self.token, chat_id, star_count, media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, - reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id) + reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, + media_payload=payload) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index bc15e9316..1f553deef 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -530,7 +530,7 @@ def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None): + business_connection_id=None, media_payload=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -552,6 +552,8 @@ def send_paid_media( payload['reply_markup'] = _convert_markup(reply_markup) if business_connection_id: payload['business_connection_id'] = business_connection_id + if media_payload: + payload['payload'] = media_payload return _make_request( token, method_url, params=payload, method='post' if files else 'get', diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index eff7fb931..dd44c2417 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4627,7 +4627,8 @@ async def send_paid_media( caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None) -> types.Message: + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, + payload: Optional[str]=None) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -4669,6 +4670,9 @@ async def send_paid_media( :param business_connection_id: Identifier of a business connection, in which the message will be sent :type business_connection_id: :obj:`str` + :param payload: Bot-defined paid media payload, 0-128 bytes. This will not be displayed to the user, use it for your internal processes. + :type payload: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4677,7 +4681,8 @@ async def send_paid_media( self.token, chat_id, star_count, media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, - reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id)) + reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, + media_payload=payload)) async def send_media_group( self, chat_id: Union[int, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index c31bf53da..f4c27d71f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -520,7 +520,7 @@ async def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None): + business_connection_id=None, media_payload=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -542,6 +542,9 @@ async def send_paid_media( payload['reply_markup'] = _convert_markup(reply_markup) if business_connection_id: payload['business_connection_id'] = business_connection_id + if media_payload: + payload['payload'] = media_payload + return await _process_request( token, method_url, params=payload, method='post' if files else 'get', From 155608e58b5959df8df03a1dd7d4262e95640081 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 6 Sep 2024 20:32:23 +0400 Subject: [PATCH 1683/1808] prize_star_count and everything related to star giveaways --- telebot/types.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index df2959e6a..0aa302012 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8994,6 +8994,9 @@ class Giveaway(JsonDeserializable): :param country_codes: Optional. A list of two-letter ISO 3166-1 alpha-2 country codes indicating the countries from which eligible users for the giveaway must come. If empty, then all users can participate in the giveaway. :type country_codes: :obj:`list` of :obj:`str` + :param prize_star_count: Optional. The number of Telegram Stars to be split between giveaway winners; for Telegram Star giveaways only + :type prize_star_count: :obj:`int` + :param premium_subscription_month_count: Optional. The number of months the Telegram Premium subscription won from the giveaway will be active for :type premium_subscription_month_count: :obj:`int` @@ -9011,7 +9014,7 @@ def de_json(cls, json_string): def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: int, only_new_members: Optional[bool] = None, has_public_winners: Optional[bool] = None, prize_description: Optional[str] = None, country_codes: Optional[List[str]] = None, - premium_subscription_month_count: Optional[int] = None, **kwargs) -> None: + premium_subscription_month_count: Optional[int] = None, prize_star_count: Optional[int] = None, **kwargs) -> None: self.chats: List[Chat] = chats self.winners_selection_date: int = winners_selection_date self.winner_count: int = winner_count @@ -9020,6 +9023,7 @@ def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: self.prize_description: Optional[str] = prize_description self.country_codes: Optional[List[str]] = country_codes self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count + self.prize_star_count: Optional[int] = prize_star_count class GiveawayWinners(JsonDeserializable): @@ -9061,6 +9065,9 @@ class GiveawayWinners(JsonDeserializable): :param prize_description: Optional. Description of additional giveaway prize :type prize_description: :obj:`str` + :param prize_star_count: Optional. The number of Telegram Stars to be split between giveaway winners; for Telegram Star giveaways only + :type prize_star_count: :obj:`int` + :return: Instance of the class :rtype: :class:`GiveawayWinners` """ @@ -9077,7 +9084,7 @@ def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: winners: List[User], additional_chat_count: Optional[int] = None, premium_subscription_month_count: Optional[int] = None, unclaimed_prize_count: Optional[int] = None, only_new_members: Optional[bool] = None, was_refunded: Optional[bool] = None, - prize_description: Optional[str] = None, **kwargs) -> None: + prize_description: Optional[str] = None, prize_star_count: Optional[int] = None, **kwargs) -> None: self.chat: Chat = chat self.giveaway_message_id: int = giveaway_message_id self.winners_selection_date: int = winners_selection_date @@ -9089,6 +9096,7 @@ def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: self.only_new_members: Optional[bool] = only_new_members self.was_refunded: Optional[bool] = was_refunded self.prize_description: Optional[str] = prize_description + self.prize_star_count: Optional[int] = prize_star_count class GiveawayCompleted(JsonDeserializable): @@ -9106,6 +9114,9 @@ class GiveawayCompleted(JsonDeserializable): :param giveaway_message: Optional. Message with the giveaway that was completed, if it wasn't deleted :type giveaway_message: :class:`Message` + :param is_star_giveaway: Optional. True, if the giveaway was a Telegram Star giveaway + :type is_star_giveaway: :obj:`bool` + :return: Instance of the class :rtype: :class:`GiveawayCompleted` """ @@ -9119,15 +9130,21 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, winner_count: int, unclaimed_prize_count: Optional[int] = None, - giveaway_message: Optional[Message] = None, **kwargs) -> None: + giveaway_message: Optional[Message] = None, is_star_giveaway: Optional[bool] = None, **kwargs) -> None: self.winner_count: int = winner_count self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count self.giveaway_message: Optional[Message] = giveaway_message + self.is_star_giveaway: Optional[bool] = is_star_giveaway class GiveawayCreated(JsonDeserializable): """ - This object represents a service message about the creation of a scheduled giveaway. Currently holds no information. + This object represents a service message about the creation of a scheduled giveaway. + + :prize_star_count: Optional. The number of Telegram Stars to be split between giveaway winners; for Telegram Star giveaways only + :type prize_star_count: :obj:`int` + + :return: Instance of the class """ @classmethod @@ -9136,8 +9153,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, **kwargs) -> None: - pass + def __init__(self, prize_star_count=None, **kwargs) -> None: + self.prize_star_count: Optional[str] = prize_star_count class TextQuote(JsonDeserializable): @@ -9468,6 +9485,9 @@ class ChatBoostSourceGiveaway(ChatBoostSource): :param is_unclaimed: True, if the giveaway was completed, but there was no user to win the prize :type is_unclaimed: :obj:`bool` + :param prize_star_count: Optional. The number of Telegram Stars to be split between giveaway winners; for Telegram Star giveaways only + :type prize_star_count: :obj:`int` + :return: Instance of the class :rtype: :class:`ChatBoostSourceGiveaway` """ @@ -9479,11 +9499,12 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj.get('user')) return cls(**obj) - def __init__(self, source, giveaway_message_id, user=None, is_unclaimed=None, **kwargs): + def __init__(self, source, giveaway_message_id, user=None, is_unclaimed=None, prize_star_count=None, **kwargs): self.source: str = source self.giveaway_message_id: int = giveaway_message_id self.user: User = user self.is_unclaimed: bool = is_unclaimed + self.prize_star_count: Optional[int] = prize_star_count class ChatBoost(JsonDeserializable): From ad70e7f0e11f3ca7a59d31df4a1593a80aa6dcdb Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 6 Sep 2024 20:35:53 +0400 Subject: [PATCH 1684/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f70cad2f..1959744fa 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From f116f925a432544d7a6d2ba3bbb5dc2fac9fce6d Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 6 Sep 2024 20:44:26 +0400 Subject: [PATCH 1685/1808] fix tests --- tests/test_handler_backends.py | 4 ++-- tests/test_telebot.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_handler_backends.py b/tests/test_handler_backends.py index b74d6fad0..4df760ebc 100644 --- a/tests/test_handler_backends.py +++ b/tests/test_handler_backends.py @@ -71,7 +71,7 @@ def update_type(message): chat_boost_removed = None return types.Update(1001234038283, 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, chat_boost, chat_boost_removed, None, None, None, None) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed, None, None, None, None, None) @pytest.fixture() @@ -95,7 +95,7 @@ def reply_to_message_update_type(reply_to_message): chat_boost_removed = None return types.Update(1001234038284, reply_to_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, chat_boost, chat_boost_removed, None, None, None, None) + poll, poll_answer, my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed, None, None, None, None, None) def next_handler(message): diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 52adbc2fe..8b04ac402 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -546,9 +546,11 @@ def create_message_update(text): message_reaction_count = None chat_boost = None chat_boost_removed = None + purchased_paid_media = None return types.Update(-1001234038283, 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, chat_boost, chat_boost_removed) + my_chat_member, chat_member, chat_join_request, message_reaction, message_reaction_count, chat_boost, chat_boost_removed, + purchased_paid_media) def test_is_string_unicode(self): s1 = u'string' From 3759a95218875fdc7a3cae7626a4e8f730ef0811 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 16 Sep 2024 20:34:30 +0400 Subject: [PATCH 1686/1808] Fixes from review --- telebot/__init__.py | 2 +- telebot/apihelper.py | 28 ++++++++++++++-------------- telebot/async_telebot.py | 2 +- telebot/asyncio_helper.py | 29 +++++++++++++++-------------- telebot/types.py | 4 ++-- telebot/util.py | 2 +- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6a874ea9e..a79c2ea50 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3201,7 +3201,7 @@ def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - media_payload=payload) + payload=payload) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1f553deef..c46b108c8 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -530,32 +530,32 @@ def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, media_payload=None): + business_connection_id=None, payload=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) - payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} + _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} if caption: - payload['caption'] = caption + _payload['caption'] = caption if parse_mode: - payload['parse_mode'] = parse_mode + _payload['parse_mode'] = parse_mode if caption_entities: - payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + _payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if show_caption_above_media is not None: - payload['show_caption_above_media'] = show_caption_above_media + _payload['show_caption_above_media'] = show_caption_above_media if disable_notification is not None: - payload['disable_notification'] = disable_notification + _payload['disable_notification'] = disable_notification if protect_content is not None: - payload['protect_content'] = protect_content + _payload['protect_content'] = protect_content if reply_parameters is not None: - payload['reply_parameters'] = reply_parameters.to_json() + _payload['reply_parameters'] = reply_parameters.to_json() if reply_markup: - payload['reply_markup'] = _convert_markup(reply_markup) + _payload['reply_markup'] = _convert_markup(reply_markup) if business_connection_id: - payload['business_connection_id'] = business_connection_id - if media_payload: - payload['payload'] = media_payload + _payload['business_connection_id'] = business_connection_id + if payload: + _payload['payload'] = payload return _make_request( - token, method_url, params=payload, + token, method_url, params=_payload, method='post' if files else 'get', files=files if files else None) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index dd44c2417..ef35d109a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4682,7 +4682,7 @@ async def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - media_payload=payload)) + payload=payload)) async def send_media_group( self, chat_id: Union[int, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index f4c27d71f..0bd447f18 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -520,33 +520,34 @@ async def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, media_payload=None): + business_connection_id=None, payload=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) - payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} + _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} + # USE _payload for request payload if caption: - payload['caption'] = caption + _payload['caption'] = caption if parse_mode: - payload['parse_mode'] = parse_mode + _payload['parse_mode'] = parse_mode if caption_entities: - payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + _payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if show_caption_above_media is not None: - payload['show_caption_above_media'] = show_caption_above_media + _payload['show_caption_above_media'] = show_caption_above_media if disable_notification is not None: - payload['disable_notification'] = disable_notification + _payload['disable_notification'] = disable_notification if protect_content is not None: - payload['protect_content'] = protect_content + _payload['protect_content'] = protect_content if reply_parameters is not None: - payload['reply_parameters'] = reply_parameters.to_json() + _payload['reply_parameters'] = reply_parameters.to_json() if reply_markup: - payload['reply_markup'] = _convert_markup(reply_markup) + _payload['reply_markup'] = _convert_markup(reply_markup) if business_connection_id: - payload['business_connection_id'] = business_connection_id - if media_payload: - payload['payload'] = media_payload + _payload['business_connection_id'] = business_connection_id + if payload: + _payload['payload'] = payload return await _process_request( - token, method_url, params=payload, + token, method_url, params=_payload, method='post' if files else 'get', files=files if files else None) diff --git a/telebot/types.py b/telebot/types.py index 0aa302012..043473aee 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9502,8 +9502,8 @@ def de_json(cls, json_string): def __init__(self, source, giveaway_message_id, user=None, is_unclaimed=None, prize_star_count=None, **kwargs): self.source: str = source self.giveaway_message_id: int = giveaway_message_id - self.user: User = user - self.is_unclaimed: bool = is_unclaimed + self.user: Optional[User] = user + self.is_unclaimed: Optional[bool] = is_unclaimed self.prize_star_count: Optional[int] = prize_star_count diff --git a/telebot/util.py b/telebot/util.py index 0448893e1..bed0e8c19 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -48,7 +48,7 @@ "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", "chat_boost", "removed_chat_boost", - "business_connection", "business_message", "edited_business_message", "deleted_business_messages" + "business_connection", "business_message", "edited_business_message", "deleted_business_messages", "purchased_paid_media" ] From ce0c525973244fb21358c374a3f739fb04964925 Mon Sep 17 00:00:00 2001 From: uak <4626956-uak@users.noreply.gitlab.com> Date: Mon, 30 Sep 2024 08:55:39 +0300 Subject: [PATCH 1687/1808] Replaced depreicated reply_to_message_id --- examples/asynchronous_telebot/custom_states.py | 17 ++++++++++------- examples/custom_states.py | 17 ++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/examples/asynchronous_telebot/custom_states.py b/examples/asynchronous_telebot/custom_states.py index f0111e27e..f3437660f 100644 --- a/examples/asynchronous_telebot/custom_states.py +++ b/examples/asynchronous_telebot/custom_states.py @@ -2,6 +2,7 @@ from telebot.asyncio_storage import StateMemoryStorage from telebot.states import State, StatesGroup from telebot.states.asyncio.context import StateContext +from telebot.types import ReplyParameters # Initialize the bot state_storage = StateMemoryStorage() # don't use this in production; switch to redis @@ -23,7 +24,7 @@ async def start_ex(message: types.Message, state: StateContext): await bot.send_message( message.chat.id, "Hello! What is your first name?", - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -34,7 +35,7 @@ async def any_state(message: types.Message, state: StateContext): await bot.send_message( message.chat.id, "Your information has been cleared. Type /start to begin again.", - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -43,7 +44,8 @@ async def any_state(message: types.Message, state: StateContext): async def name_get(message: types.Message, state: StateContext): await state.set(MyStates.age) await bot.send_message( - message.chat.id, "How old are you?", reply_to_message_id=message.message_id + message.chat.id, "How old are you?", + reply_parameters=ReplyParameters(message_id=message.message_id), ) await state.add_data(name=message.text) @@ -64,7 +66,7 @@ async def ask_color(message: types.Message, state: StateContext): message.chat.id, "What is your favorite color? Choose from the options below.", reply_markup=keyboard, - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -84,7 +86,7 @@ async def ask_hobby(message: types.Message, state: StateContext): message.chat.id, "What is one of your hobbies? Choose from the options below.", reply_markup=keyboard, - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -123,7 +125,8 @@ async def finish(message: types.Message, state: StateContext): ) await bot.send_message( - message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id + message.chat.id, msg, parse_mode="html", + reply_parameters=ReplyParameters(message_id=message.message_id), ) await state.delete() @@ -134,7 +137,7 @@ async def age_incorrect(message: types.Message): await bot.send_message( message.chat.id, "Please enter a valid number for age.", - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) diff --git a/examples/custom_states.py b/examples/custom_states.py index 131dd1d42..e1bf13295 100644 --- a/examples/custom_states.py +++ b/examples/custom_states.py @@ -3,6 +3,7 @@ from telebot.states import State, StatesGroup from telebot.states.sync.context import StateContext from telebot.storage import StateMemoryStorage +from telebot.types import ReplyParameters # Initialize the bot state_storage = StateMemoryStorage() # don't use this in production; switch to redis @@ -24,7 +25,7 @@ def start_ex(message: types.Message, state: StateContext): bot.send_message( message.chat.id, "Hello! What is your first name?", - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -35,7 +36,7 @@ def any_state(message: types.Message, state: StateContext): bot.send_message( message.chat.id, "Your information has been cleared. Type /start to begin again.", - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -44,7 +45,8 @@ def any_state(message: types.Message, state: StateContext): def name_get(message: types.Message, state: StateContext): state.set(MyStates.age) bot.send_message( - message.chat.id, "How old are you?", reply_to_message_id=message.message_id + message.chat.id, "How old are you?", + reply_parameters=ReplyParameters(message_id=message.message_id), ) state.add_data(name=message.text) @@ -65,7 +67,7 @@ def ask_color(message: types.Message, state: StateContext): message.chat.id, "What is your favorite color? Choose from the options below.", reply_markup=keyboard, - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -85,7 +87,7 @@ def ask_hobby(message: types.Message, state: StateContext): message.chat.id, "What is one of your hobbies? Choose from the options below.", reply_markup=keyboard, - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) @@ -124,7 +126,8 @@ def finish(message: types.Message, state: StateContext): ) bot.send_message( - message.chat.id, msg, parse_mode="html", reply_to_message_id=message.message_id + message.chat.id, msg, parse_mode="html", + reply_parameters=ReplyParameters(message_id=message.message_id), ) state.delete() @@ -135,7 +138,7 @@ def age_incorrect(message: types.Message): bot.send_message( message.chat.id, "Please enter a valid number for age.", - reply_to_message_id=message.message_id, + reply_parameters=ReplyParameters(message_id=message.message_id), ) From 57938f7762fae96edcabe3399beffa5c8c233b80 Mon Sep 17 00:00:00 2001 From: thebocher <63579888+thebocher@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:24:58 +0200 Subject: [PATCH 1688/1808] Update pickle_storage.py fixed typo in StatePickleStorage.save, which didn't allow to save user state, but instead copied the whole content of state file inside "data" --- telebot/storage/pickle_storage.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/storage/pickle_storage.py b/telebot/storage/pickle_storage.py index e11a05043..b4c44e8fa 100644 --- a/telebot/storage/pickle_storage.py +++ b/telebot/storage/pickle_storage.py @@ -254,9 +254,9 @@ def save( message_thread_id, bot_id, ) - data = self._read_from_file() - data[_key]["data"] = data - self._write_to_file(data) + file_data = self._read_from_file() + file_data[_key]["data"] = data + self._write_to_file(file_data) return True def __str__(self) -> str: From 344e52fac5d7cb870cfd683dc9ff67b7443633e9 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 10 Oct 2024 17:51:38 +0400 Subject: [PATCH 1689/1808] Fix pickle issue with saving data on async --- telebot/asyncio_storage/pickle_storage.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/asyncio_storage/pickle_storage.py b/telebot/asyncio_storage/pickle_storage.py index 723672034..1a63e6961 100644 --- a/telebot/asyncio_storage/pickle_storage.py +++ b/telebot/asyncio_storage/pickle_storage.py @@ -265,9 +265,9 @@ async def save( message_thread_id, bot_id, ) - data = await self._read_from_file() - data[_key]["data"] = data - await self._write_to_file(data) + file_data = await self._read_from_file() + file_data[_key]["data"] = data + await self._write_to_file(file_data) return True def __str__(self) -> str: From 58356c8d7dac1d7a102ed4216e0b2bb20ed1e0d3 Mon Sep 17 00:00:00 2001 From: Maksim Kulis <37274042+maksimkulis@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:57:12 +0300 Subject: [PATCH 1690/1808] Fix KeyboardButtonRequestChat request_title typing --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 043473aee..7efc260cf 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2655,7 +2655,7 @@ def __init__(self, request_id: int, chat_is_channel: bool, chat_is_forum: Option chat_has_username: Optional[bool]=None, chat_is_created: Optional[bool]=None, user_administrator_rights: Optional[ChatAdministratorRights]=None, bot_administrator_rights: Optional[ChatAdministratorRights]=None, bot_is_member: Optional[bool]=None, - request_title: Optional[str]=None, request_photo: Optional[bool]=None, request_username: Optional[bool]=None): + request_title: Optional[bool]=None, request_photo: Optional[bool]=None, request_username: Optional[bool]=None): self.request_id: int = request_id self.chat_is_channel: bool = chat_is_channel self.chat_is_forum: Optional[bool] = chat_is_forum @@ -2664,7 +2664,7 @@ def __init__(self, request_id: int, chat_is_channel: bool, chat_is_forum: Option self.user_administrator_rights: Optional[ChatAdministratorRights] = user_administrator_rights self.bot_administrator_rights: Optional[ChatAdministratorRights] = bot_administrator_rights self.bot_is_member: Optional[bool] = bot_is_member - self.request_title: Optional[str] = request_title + self.request_title: Optional[bool] = request_title self.request_photo: Optional[bool] = request_photo self.request_username: Optional[bool] = request_username From 309f6a267f0a2d0fa451877f19ad15a25b5b4fc4 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 20 Oct 2024 18:24:21 +0300 Subject: [PATCH 1691/1808] Enable call stack logging for deprecation warnings (optional) --- telebot/types.py | 139 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 12 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 7efc260cf..4bba24cf7 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -4,6 +4,7 @@ from io import IOBase import logging import os +import traceback from pathlib import Path from typing import Dict, List, Optional, Union, Any, Tuple from abc import ABC @@ -19,6 +20,7 @@ DISABLE_KEYLEN_ERROR = False +DEPRECATION_STACK_SHOW_DEPTH = 0 logger = logging.getLogger('TeleBot') @@ -1550,31 +1552,43 @@ def html_caption(self) -> Optional[str]: @property def voice_chat_scheduled(self): logger.warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.video_chat_scheduled @property def voice_chat_started(self): logger.warning('The parameter "voice_chat_started" is deprecated, use "video_chat_started" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.video_chat_started @property def voice_chat_ended(self): logger.warning('The parameter "voice_chat_ended" is deprecated, use "video_chat_ended" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.video_chat_ended @property def voice_chat_participants_invited(self): logger.warning('The parameter "voice_chat_participants_invited" is deprecated, use "video_chat_participants_invited" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.video_chat_participants_invited @property def new_chat_member(self): logger.warning('The parameter "new_chat_member" is deprecated, use "new_chat_members" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return None @property def forward_from(self): logger.warning('The parameter "forward_from" is deprecated, use "forward_origin" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if self.forward_origin and isinstance(self.forward_origin, MessageOriginUser): return self.forward_origin.sender_user return None @@ -1582,6 +1596,8 @@ def forward_from(self): @property def forward_from_chat(self): logger.warning('The parameter "forward_from_chat" is deprecated, use "forward_origin" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if self.forward_origin and isinstance(self.forward_origin, MessageOriginChat): return self.forward_origin.sender_chat elif self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): @@ -1591,6 +1607,8 @@ def forward_from_chat(self): @property def forward_from_message_id(self): logger.warning('The parameter "forward_from_message_id" is deprecated, use "forward_origin" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): return self.forward_origin.message_id return None @@ -1598,6 +1616,8 @@ def forward_from_message_id(self): @property def forward_signature(self): logger.warning('The parameter "forward_signature" is deprecated, use "forward_origin" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if self.forward_origin and isinstance(self.forward_origin, MessageOriginChat): return self.forward_origin.author_signature elif self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): @@ -1607,6 +1627,8 @@ def forward_signature(self): @property def forward_sender_name(self): logger.warning('The parameter "forward_sender_name" is deprecated, use "forward_origin" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if self.forward_origin and isinstance(self.forward_origin, MessageOriginHiddenUser): return self.forward_origin.sender_user_name return None @@ -1614,6 +1636,8 @@ def forward_sender_name(self): @property def forward_date(self): logger.warning('The parameter "forward_date" is deprecated, use "forward_origin" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if self.forward_origin: return self.forward_origin.date return None @@ -1621,6 +1645,8 @@ def forward_date(self): @property def user_shared(self): logger.warning('The parameter "user_shared" is deprecated, use "users_shared" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.users_shared @property @@ -1847,6 +1873,8 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None @property def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail @@ -1942,6 +1970,8 @@ def __init__(self, file_id, file_unique_id, thumbnail=None, file_name=None, mime @property def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail @@ -2006,6 +2036,8 @@ def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=N @property def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail @@ -2056,6 +2088,8 @@ def __init__(self, file_id, file_unique_id, length, duration, thumbnail=None, fi @property def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail @@ -2598,7 +2632,9 @@ class KeyboardButtonRequestUser(KeyboardButtonRequestUsers): def __init__( self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, max_quantity: Optional[int]=None) -> None: - logger.warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') + logger.warning('The class "KeyboardButtonRequestUser" is deprecated, use "KeyboardButtonRequestUsers" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) super().__init__(request_id, user_is_bot=user_is_bot, user_is_premium=user_is_premium, max_quantity=max_quantity) @@ -2746,6 +2782,8 @@ def __init__(self, text: str, request_contact: Optional[bool]=None, self.request_users: Optional[KeyboardButtonRequestUsers] = request_users if request_user is not None: logger.warning('The parameter "request_user" is deprecated, use "request_users" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if self.request_users is None: # noinspection PyTypeChecker self.request_users = request_user @@ -3233,6 +3271,8 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed @property def can_manage_voice_chats(self): logger.warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.can_manage_video_chats @@ -3558,6 +3598,8 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send # Telegram passes can_send_media_messages in Chat.permissions. Temporary created parameter "de_json" allows avoid # deprection warning and individual parameters overriding. logger.warning('The parameter "can_send_media_messages" is deprecated. Use individual parameters like "can_send_audios", "can_send_documents" etc.') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) self.can_send_audios: Optional[bool] = can_send_media_messages self.can_send_documents: Optional[bool] = can_send_media_messages self.can_send_photos: Optional[bool] = can_send_media_messages @@ -3928,7 +3970,9 @@ def __init__(self, message_text: str, parse_mode: Optional[str] = None, entities self.link_preview_options: Optional[LinkPreviewOptions] = link_preview_options if disable_web_page_preview is not None: logger.warning('The parameter "disable_web_page_preview" is deprecated. Use "link_preview_options" instead.') - + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + if link_preview_options: logger.warning('Both "link_preview_options" and "disable_web_page_preview" parameters are set: conflicting, "disable_web_page_preview" is deprecated') else: @@ -4434,16 +4478,22 @@ def __init__(self, id: str, title: str, input_message_content: InputMessageConte @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url @property def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_width @property def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_height def to_dict(self): @@ -4535,6 +4585,8 @@ def __init__(self, id: str, photo_url: str, thumbnail_url: str, photo_width: Opt @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url def to_dict(self): @@ -4629,11 +4681,15 @@ def __init__(self, id: str, gif_url: str, thumbnail_url: str, gif_width: Optiona @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url @property def thumb_mime_type(self) -> str: logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_mime_type def to_dict(self): @@ -4729,11 +4785,15 @@ def __init__(self, id: str, mpeg4_url: str, thumbnail_url: str, mpeg4_width: Opt @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url @property def thumb_mime_type(self) -> str: logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_mime_type def to_dict(self): @@ -4835,6 +4895,8 @@ def __init__(self, id: str, video_url: str, mime_type: str, thumbnail_url: str, @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url def to_dict(self): @@ -5052,16 +5114,22 @@ def __init__(self, id: str, title: str, document_url: str, mime_type: str, capti @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url @property def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_width @property def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_height def to_dict(self): @@ -5151,16 +5219,22 @@ def __init__(self, id: str, title: str, latitude: float, longitude: float, horiz @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url @property def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_width @property def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_height def to_dict(self): @@ -5261,16 +5335,22 @@ def __init__(self, id: str, title: str, latitude: float, longitude: float, addre @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url @property def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_width @property def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_height def to_dict(self): @@ -5356,16 +5436,22 @@ def __init__(self, id: str, phone_number: str, first_name: str, last_name: Optio @property def thumb_url(self) -> str: logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_url @property def thumb_width(self) -> int: logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_width @property def thumb_height(self) -> int: logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail_height def to_dict(self): @@ -6054,6 +6140,8 @@ def __init__(self, file_id, file_unique_id, width=None, height=None, duration=No @property def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail @@ -6470,30 +6558,29 @@ def __init__(self, name, title, sticker_type, stickers, thumbnail=None, **kwargs @property def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail @property def contains_masks(self) -> bool: - """ - Deprecated since Bot API 6.2, use sticker_type instead. - """ logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type instead"') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.sticker_type == 'mask' @property def is_animated(self) -> bool: - """ - Deprecated since Bot API 7.2. Stickers can be mixed now. - """ logger.warning('The parameter "is_animated" is deprecated since Bot API 7.2. Stickers can now be mixed') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return False @property def is_video(self) -> bool: - """ - Deprecated since Bot API 7.2. Stickers can be mixed now. - """ logger.warning('The parameter "is_video" is deprecated since Bot API 7.2. Stickers can now be mixed') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return False @@ -6593,6 +6680,8 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, @property def thumb(self) -> Optional[PhotoSize]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail @@ -6811,6 +6900,8 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail def to_dict(self): @@ -6898,6 +6989,8 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail def to_dict(self): @@ -6972,6 +7065,8 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail def to_dict(self): @@ -7035,6 +7130,8 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.thumbnail def to_dict(self): @@ -7204,6 +7301,8 @@ def __init__( self.type: str = type if poll_type is not None: logger.warning("Poll: poll_type parameter is deprecated. Use type instead.") + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) if type is None: self.type: str = poll_type self.allows_multiple_answers: bool = allows_multiple_answers @@ -7447,6 +7546,8 @@ class VoiceChatStarted(VideoChatStarted): def __init__(self): logger.warning('VoiceChatStarted is deprecated. Use VideoChatStarted instead.') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) super().__init__() @@ -7479,6 +7580,8 @@ class VoiceChatScheduled(VideoChatScheduled): """ def __init__(self, *args, **kwargs): logger.warning('VoiceChatScheduled is deprecated. Use VideoChatScheduled instead.') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) super().__init__(*args, **kwargs) @@ -7510,6 +7613,8 @@ class VoiceChatEnded(VideoChatEnded): """ def __init__(self, *args, **kwargs): logger.warning('VoiceChatEnded is deprecated. Use VideoChatEnded instead.') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) super().__init__(*args, **kwargs) @@ -7543,6 +7648,8 @@ class VoiceChatParticipantsInvited(VideoChatParticipantsInvited): """ def __init__(self, *args, **kwargs): logger.warning('VoiceChatParticipantsInvited is deprecated. Use VideoChatParticipantsInvited instead.') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) super().__init__(*args, **kwargs) @@ -8190,6 +8297,8 @@ def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], forma if not self.format: logger.warning("Deprecation warning. 'format' parameter is required in InputSticker. Setting format to 'static'.") + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) self.format = "static" if service_utils.is_string(self.sticker): @@ -9308,11 +9417,15 @@ def __init__(self, request_id: int, users: List[SharedUser], **kwargs): @property def user_id(self) -> None: logger.warning('The parameter "user_id" is deprecated, use "user_ids" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return None @property def user_ids(self) -> List[SharedUser]: logger.warning('The parameter "user_ids" is deprecated, use "users" instead') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return self.users @@ -9613,7 +9726,9 @@ def __init__(self, chat, message_id, date, **kwargs): @staticmethod def __universal_deprecation(property_name): - logger.warning(f'Deprecation warning: the filed "{property_name}" is not accessible for InaccessibleMessage. You should check if your object is Message instance before access.') + logger.warning(f'Deprecation warning: the field "{property_name}" is not accessible for InaccessibleMessage. You should check if your object is Message instance before access.') + if DEPRECATION_STACK_SHOW_DEPTH: + logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) return None def __getattr__(self, item): From b6db6a9ad66aca8402e176ca9a94019858e9ec0d Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Nov 2024 15:34:54 +0400 Subject: [PATCH 1692/1808] Added the class CopyTextButton and the field copy_text in the class InlineKeyboardButton allowing bots to send and receive inline buttons that copy arbitrary text. --- telebot/types.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 7efc260cf..e882e9d71 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -2932,6 +2932,9 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable) the first row and can only be used in invoice messages. :type pay: :obj:`bool` + :param copy_text: Optional. Description of the button that copies the specified text to the clipboard. + :type copy_text: :class:`telebot.types.CopyTextButton` + :return: Instance of the class :rtype: :class:`telebot.types.InlineKeyboardButton` """ @@ -2945,13 +2948,15 @@ def de_json(cls, json_string): obj['web_app'] = WebAppInfo.de_json(obj.get('web_app')) if 'switch_inline_query_chosen_chat' in obj: obj['switch_inline_query_chosen_chat'] = SwitchInlineQueryChosenChat.de_json(obj.get('switch_inline_query_chosen_chat')) + if 'copy_text' in obj: + obj['copy_text'] = CopyTextButton.de_json(obj.get('copy_text')) return cls(**obj) def __init__(self, text: str, url: Optional[str]=None, callback_data: Optional[str]=None, web_app: Optional[WebAppInfo]=None, switch_inline_query: Optional[str]=None, switch_inline_query_current_chat: Optional[str]=None, switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat]=None, callback_game=None, pay: Optional[bool]=None, - login_url: Optional[LoginUrl]=None, **kwargs): + login_url: Optional[LoginUrl]=None, copy_text: Optional[CopyTextButton]=None, **kwargs): self.text: str = text self.url: Optional[str] = url self.callback_data: Optional[str] = callback_data @@ -2962,6 +2967,7 @@ def __init__(self, text: str, url: Optional[str]=None, callback_data: Optional[s self.callback_game = callback_game # Not Implemented self.pay: Optional[bool] = pay self.login_url: Optional[LoginUrl] = login_url + self.copy_text: Optional[CopyTextButton] = copy_text def to_json(self): return json.dumps(self.to_dict()) @@ -2986,6 +2992,8 @@ def to_dict(self): json_dict['login_url'] = self.login_url.to_dict() if self.switch_inline_query_chosen_chat is not None: json_dict['switch_inline_query_chosen_chat'] = self.switch_inline_query_chosen_chat.to_dict() + if self.copy_text is not None: + json_dict['copy_text'] = self.copy_text.to_dict() return json_dict @@ -10910,3 +10918,33 @@ def de_json(cls, json_string): obj['from_user'] = User.de_json(obj['from_user']) return cls(**obj) + +class CopyTextButton(JsonSerializable, JsonDeserializable): + """ + This object represents an inline keyboard button that copies specified text to the clipboard. + + Telegram documentation: https://core.telegram.org/bots/api#copytextbutton + + :param text: The text to be copied to the clipboard; 1-256 characters + :type text: :obj:`str` + + :return: Instance of the class + :rtype: :class:`CopyTextButton` + """ + def __init__(self, text: str, **kwargs): + self.text: str = text + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = { + 'text': self.text + } + return data + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) From f9cc905ef46d0e905c46378112ffc2cffe669901 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Nov 2024 16:18:53 +0400 Subject: [PATCH 1693/1808] Added the parameter allow_paid_broadcast to the methods sendMessage, sendPhoto, sendVideo, sendAnimation, sendAudio, sendDocument, sendPaidMedia, sendSticker, sendVideoNote, sendVoice, sendLocation, sendVenue, sendContact, sendPoll, sendDice, sendInvoice, sendGame, sendMediaGroup and copyMessage. --- telebot/__init__.py | 170 ++++++++++++++++++++++++++++-------- telebot/apihelper.py | 78 +++++++++++++---- telebot/async_telebot.py | 176 ++++++++++++++++++++++++++++++-------- telebot/asyncio_helper.py | 82 ++++++++++++++---- 4 files changed, 394 insertions(+), 112 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index a79c2ea50..59de38db5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1670,7 +1670,8 @@ def send_message( reply_parameters: Optional[types.ReplyParameters]=None, link_preview_options : Optional[types.LinkPreviewOptions]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send text messages. @@ -1729,6 +1730,10 @@ def send_message( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1777,7 +1782,7 @@ def send_message( reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, entities=entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, link_preview_options=link_preview_options, business_connection_id=business_connection_id, - message_effect_id=message_effect_id)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) def forward_message( @@ -1839,7 +1844,8 @@ def copy_message( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - show_caption_above_media: Optional[bool]=None) -> types.MessageID: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.MessageID: """ Use this method to copy messages of any kind. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. @@ -1894,6 +1900,10 @@ def copy_message( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -1926,7 +1936,7 @@ def copy_message( 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, - show_caption_above_media=show_caption_above_media)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) def delete_message(self, chat_id: Union[int, str], message_id: int, @@ -2076,7 +2086,8 @@ def send_dice( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -2121,6 +2132,10 @@ def send_dice( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2150,7 +2165,7 @@ def send_dice( self.token, chat_id, emoji=emoji, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) @@ -2169,7 +2184,8 @@ def send_photo( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - show_caption_above_media: Optional[bool]=None) -> types.Message: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2229,6 +2245,10 @@ def send_photo( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2262,7 +2282,7 @@ def send_photo( caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) def send_audio( @@ -2282,7 +2302,8 @@ def send_audio( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2358,6 +2379,10 @@ def send_audio( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2393,7 +2418,7 @@ def send_audio( reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) def send_voice( @@ -2410,7 +2435,8 @@ def send_voice( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=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, 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. @@ -2467,6 +2493,10 @@ def send_voice( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2497,7 +2527,7 @@ def send_voice( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) @@ -2519,7 +2549,8 @@ def send_document( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send general files. @@ -2588,6 +2619,10 @@ def send_document( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2632,7 +2667,7 @@ def send_document( timeout=timeout, caption=caption, thumbnail=thumbnail, caption_entities=caption_entities, disable_content_type_detection=disable_content_type_detection, visible_file_name=visible_file_name, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) @@ -2650,7 +2685,8 @@ def send_sticker( emoji: Optional[str]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2702,6 +2738,10 @@ def send_sticker( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2736,7 +2776,7 @@ def send_sticker( reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) @@ -2763,7 +2803,8 @@ def send_video( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - show_caption_above_media: Optional[bool]=None) -> types.Message: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2843,6 +2884,10 @@ def send_video( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2884,7 +2929,7 @@ def send_video( thumbnail=thumbnail, height=height, width=width, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast) ) @@ -2909,7 +2954,8 @@ def send_animation( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - show_caption_above_media: Optional[bool]=None) -> types.Message: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -2988,6 +3034,10 @@ def send_animation( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3024,7 +3074,7 @@ def send_animation( thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, width=width, height=height, message_thread_id=message_thread_id, reply_parameters=reply_parameters, has_spoiler=has_spoiler, business_connection_id=business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast) ) @@ -3043,7 +3093,8 @@ def send_video_note( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -3104,6 +3155,10 @@ def send_video_note( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3137,7 +3192,7 @@ def send_video_note( self.token, chat_id, data, duration=duration, length=length, reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) def send_paid_media( @@ -3146,7 +3201,7 @@ def send_paid_media( show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, - payload: Optional[str]=None + payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None ) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -3192,6 +3247,10 @@ def send_paid_media( :param payload: Bot-defined paid media payload, 0-128 bytes. This will not be displayed to the user, use it for your internal processes. :type payload: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3201,7 +3260,7 @@ def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - payload=payload) + payload=payload, allow_paid_broadcast=allow_paid_broadcast) ) @@ -3218,7 +3277,8 @@ def send_media_group( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> List[types.Message]: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -3258,6 +3318,10 @@ def send_media_group( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -3291,7 +3355,7 @@ def send_media_group( result = apihelper.send_media_group( self.token, chat_id, media, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) return [types.Message.de_json(msg) for msg in result] @@ -3311,7 +3375,8 @@ def send_location( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -3370,6 +3435,10 @@ def send_location( :parameter message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3400,7 +3469,7 @@ def send_location( disable_notification=disable_notification, timeout=timeout, horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) @@ -3531,7 +3600,8 @@ def send_venue( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -3597,6 +3667,10 @@ def send_venue( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3627,7 +3701,7 @@ def send_venue( foursquare_type=foursquare_type, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, google_place_id=google_place_id, google_place_type=google_place_type, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) @@ -3643,7 +3717,8 @@ def send_contact( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -3696,6 +3771,10 @@ def send_contact( :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3725,7 +3804,7 @@ def send_contact( self.token, chat_id, phone_number, first_name, last_name=last_name, vcard=vcard, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) @@ -4982,7 +5061,8 @@ def send_game( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Used to send the game. @@ -5024,6 +5104,10 @@ def send_game( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5056,7 +5140,7 @@ def send_game( self.token, chat_id, game_short_name, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) @@ -5161,7 +5245,8 @@ def send_invoice( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Sends invoice. @@ -5270,6 +5355,10 @@ def send_invoice( :param message_effect_id: The identifier of a message effect, which will be applied to the sent message :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5309,7 +5398,7 @@ def send_invoice( provider_data=provider_data, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, max_tip_amount=max_tip_amount, suggested_tip_amounts=suggested_tip_amounts, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) def create_invoice_link(self, @@ -5441,7 +5530,8 @@ def send_poll( business_connection_id: Optional[str]=None, question_parse_mode: Optional[str] = None, question_entities: Optional[List[types.MessageEntity]] = None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -5523,6 +5613,10 @@ def send_poll( :param message_effect_id: Unique identifier of the message effect to apply to the sent message :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5578,7 +5672,7 @@ def send_poll( protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, question_parse_mode=question_parse_mode, question_entities=question_entities, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c46b108c8..92d419c45 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -245,7 +245,7 @@ def send_message( parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, link_preview_options=None, - business_connection_id=None, message_effect_id=None): + business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -270,6 +270,8 @@ def send_message( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, method='post') @@ -428,7 +430,7 @@ def forward_message( def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, - reply_parameters=None, show_caption_above_media=None): + reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -451,6 +453,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['message_thread_id'] = message_thread_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload) @@ -458,7 +462,7 @@ def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, - business_connection_id=None, message_effect_id=None): + business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -479,6 +483,8 @@ def send_dice( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload) @@ -488,7 +494,7 @@ def send_photo( parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, show_caption_above_media=None): + message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -524,13 +530,15 @@ def send_photo( payload['message_effect_id'] = message_effect_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, files=files, method='post') def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, payload=None): + business_connection_id=None, payload=None, allow_paid_broadcast=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -554,6 +562,8 @@ def send_paid_media( _payload['business_connection_id'] = business_connection_id if payload: _payload['payload'] = payload + if allow_paid_broadcast is not None: + _payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request( token, method_url, params=_payload, method='post' if files else 'get', @@ -564,7 +574,7 @@ def send_media_group( token, chat_id, media, disable_notification=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -582,6 +592,8 @@ def send_media_group( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -595,7 +607,7 @@ def send_location( timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -622,6 +634,8 @@ def send_location( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload) @@ -678,7 +692,7 @@ def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_markup=None, timeout=None, google_place_id=None, google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -705,6 +719,8 @@ def send_venue( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload) @@ -712,7 +728,7 @@ def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -735,6 +751,8 @@ def send_contact( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload) @@ -755,7 +773,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, show_caption_above_media=None): + message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -805,6 +823,8 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['message_effect_id'] = message_effect_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, files=files, method='post') @@ -813,7 +833,8 @@ def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None, - has_spoiler=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None): + has_spoiler=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, + allow_paid_broadcast=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -861,13 +882,15 @@ def send_animation( payload['message_effect_id'] = message_effect_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -899,12 +922,15 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup= payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, - message_thread_id=None, reply_parameters=None,business_connection_id=None, message_effect_id=None): + message_thread_id=None, reply_parameters=None,business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -942,13 +968,15 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -992,6 +1020,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, files=files, method='post') @@ -999,7 +1029,7 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -1044,6 +1074,8 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1545,7 +1577,8 @@ def delete_message(token, chat_id, message_id, timeout=None): def send_game( token, chat_id, game_short_name, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, message_effect_id=None): + protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1564,6 +1597,8 @@ def send_game( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload) @@ -1629,7 +1664,8 @@ def send_invoice( send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None): + protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None, + allow_paid_broadcast=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1662,6 +1698,7 @@ def send_invoice( :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. :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :param allow_paid_broadcast: :return: """ method_url = r'sendInvoice' @@ -1714,6 +1751,8 @@ def send_invoice( payload['message_effect_id'] = message_effect_id if provider_token is not None: payload['provider_token'] = provider_token + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return _make_request(token, method_url, params=payload) @@ -1974,7 +2013,8 @@ 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, question_parse_mode=None, question_entities=None, message_effect_id=None): + reply_parameters=None, business_connection_id=None, question_parse_mode=None, question_entities=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -2025,6 +2065,8 @@ def send_poll( payload['question_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(question_entities)) if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast 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 ef35d109a..3874429ad 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3161,7 +3161,8 @@ async def send_message( reply_parameters: Optional[types.ReplyParameters]=None, link_preview_options: Optional[types.LinkPreviewOptions]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send text messages. @@ -3220,6 +3221,10 @@ async def send_message( :param message_effect_id: Unique identifier for the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3275,7 +3280,7 @@ async def send_message( self.token, chat_id, text, reply_markup, parse_mode, disable_notification, timeout, entities, protect_content, message_thread_id, reply_parameters, link_preview_options, business_connection_id, - message_effect_id=message_effect_id)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -3334,7 +3339,8 @@ async def copy_message( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - show_caption_above_media: Optional[bool]=None) -> types.MessageID: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.MessageID: """ Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, @@ -3389,6 +3395,10 @@ async def copy_message( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -3420,7 +3430,8 @@ async def copy_message( return types.MessageID.de_json( await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, disable_notification, reply_markup, - timeout, protect_content, message_thread_id, reply_parameters, show_caption_above_media=show_caption_above_media)) + timeout, protect_content, message_thread_id, reply_parameters, show_caption_above_media=show_caption_above_media, + allow_paid_broadcast=allow_paid_broadcast)) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -3557,7 +3568,8 @@ async def send_dice( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -3602,6 +3614,10 @@ async def send_dice( :param message_effect_id: Unique identifier for the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3632,7 +3648,8 @@ async def send_dice( return types.Message.de_json( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, - reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, + allow_paid_broadcast=allow_paid_broadcast)) async def send_photo( @@ -3650,7 +3667,8 @@ async def send_photo( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - show_caption_above_media: Optional[bool]=None) -> types.Message: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -3710,6 +3728,10 @@ async def send_photo( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3744,7 +3766,7 @@ async def send_photo( self.token, chat_id, photo, caption, reply_markup, parse_mode, disable_notification, timeout, caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -3763,7 +3785,8 @@ async def send_audio( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -3839,6 +3862,10 @@ async def send_audio( :param message_effect_id: Unique identifier for the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3875,7 +3902,7 @@ async def send_audio( await asyncio_helper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -3891,7 +3918,8 @@ async def send_voice( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=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, 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. @@ -3948,6 +3976,10 @@ async def send_voice( :param message_effect_id: Unique identifier for the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -3979,7 +4011,8 @@ async def send_voice( await asyncio_helper.send_voice( self.token, chat_id, voice, caption, duration, reply_markup, parse_mode, disable_notification, timeout, caption_entities, - protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, + allow_paid_broadcast=allow_paid_broadcast)) async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], @@ -4000,7 +4033,8 @@ async def send_document( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send general files. @@ -4069,6 +4103,10 @@ async def send_document( :param message_effect_id: Unique identifier for the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4117,7 +4155,7 @@ async def send_document( disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content, - message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id)) + message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], @@ -4132,7 +4170,8 @@ async def send_sticker( emoji: Optional[str]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -4184,6 +4223,10 @@ async def send_sticker( :param message_effect_id: Unique identifier for the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4222,7 +4265,7 @@ async def send_sticker( reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, - message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id)) + message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -4247,7 +4290,8 @@ async def send_video( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - show_caption_above_media: Optional[bool]=None) -> types.Message: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -4327,6 +4371,10 @@ async def send_video( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4369,7 +4417,7 @@ async def send_video( self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -4392,7 +4440,8 @@ async def send_animation( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - show_caption_above_media: Optional[bool]=None) -> types.Message: + show_caption_above_media: Optional[bool]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -4471,6 +4520,10 @@ async def send_animation( :param show_caption_above_media: Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages. :type show_caption_above_media: :obj:`bool` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4508,7 +4561,7 @@ async def send_animation( self.token, chat_id, animation, duration, caption, reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, - message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media)) + message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -4525,7 +4578,8 @@ async def send_video_note( thumb: Optional[Union[Any, str]]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -4586,6 +4640,10 @@ async def send_video_note( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4620,7 +4678,8 @@ async def send_video_note( return types.Message.de_json( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_markup, - disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, + allow_paid_broadcast=allow_paid_broadcast)) async def send_paid_media( self, chat_id: Union[int, str], star_count: int, media: List[types.InputPaidMedia], @@ -4628,7 +4687,7 @@ async def send_paid_media( show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, - payload: Optional[str]=None) -> types.Message: + payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -4673,6 +4732,10 @@ async def send_paid_media( :param payload: Bot-defined paid media payload, 0-128 bytes. This will not be displayed to the user, use it for your internal processes. :type payload: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4682,7 +4745,7 @@ async def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - payload=payload)) + payload=payload, allow_paid_broadcast=allow_paid_broadcast)) async def send_media_group( self, chat_id: Union[int, str], @@ -4697,7 +4760,8 @@ async def send_media_group( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> List[types.Message]: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -4737,6 +4801,10 @@ async def send_media_group( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -4770,7 +4838,8 @@ async def send_media_group( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply result = await asyncio_helper.send_media_group( - self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id) + self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, + allow_paid_broadcast=allow_paid_broadcast) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -4789,7 +4858,8 @@ async def send_location( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -4848,6 +4918,10 @@ async def send_location( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4880,7 +4954,7 @@ async def send_location( self.token, chat_id, latitude, longitude, live_period, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def edit_message_live_location( self, latitude: float, longitude: float, @@ -5005,7 +5079,8 @@ async def send_venue( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -5071,6 +5146,10 @@ async def send_venue( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -5103,7 +5182,8 @@ async def send_venue( await asyncio_helper.send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_markup, timeout, - google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, + allow_paid_broadcast=allow_paid_broadcast)) async def send_contact( @@ -5119,7 +5199,8 @@ async def send_contact( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -5173,6 +5254,10 @@ async def send_contact( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -5204,7 +5289,7 @@ async def send_contact( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def send_chat_action( @@ -6406,7 +6491,8 @@ async def send_game( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Used to send the game. @@ -6448,6 +6534,10 @@ async def send_game( :param message_effect_id: Identifier of the message effect. :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6478,7 +6568,7 @@ async def send_game( result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) return types.Message.de_json(result) async def set_game_score( @@ -6577,7 +6667,8 @@ async def send_invoice( protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Sends invoice. @@ -6686,6 +6777,10 @@ async def send_invoice( :param message_effect_id: The identifier of a message effect to be applied to the message :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6720,7 +6815,7 @@ async def send_invoice( send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, reply_markup, provider_data, timeout, max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters, - message_effect_id=message_effect_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) return types.Message.de_json(result) @@ -6851,7 +6946,8 @@ async def send_poll( business_connection_id: Optional[str]=None, question_parse_mode: Optional[str] = None, question_entities: Optional[List[types.MessageEntity]] = None, - message_effect_id: Optional[str]=None) -> types.Message: + message_effect_id: Optional[str]=None, + allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send a native poll. On success, the sent Message is returned. @@ -6937,6 +7033,10 @@ async def send_poll( :param message_effect_id: Identifier of the message effect to apply to the sent message :type message_effect_id: :obj:`str` + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee + of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance + :type allow_paid_broadcast: :obj:`bool` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6989,7 +7089,7 @@ async def send_poll( disable_notification, 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, - message_effect_id=message_effect_id)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) 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 0bd447f18..b21b7f9f0 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -283,7 +283,8 @@ async def send_message( reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, - message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None, message_effect_id=None): + message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -308,6 +309,8 @@ async def send_message( params['business_connection_id'] = business_connection_id if message_effect_id: params['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + params['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_name, params=params, method='post') @@ -418,7 +421,8 @@ async def forward_message( async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, - reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None): + reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, + allow_paid_broadcast=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -441,6 +445,8 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['message_thread_id'] = message_thread_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) @@ -448,7 +454,7 @@ async def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, - message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -469,6 +475,8 @@ async def send_dice( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) @@ -478,7 +486,7 @@ async def send_photo( parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, - business_connection_id=None, message_effect_id=None, show_caption_above_media=None): + business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -514,13 +522,15 @@ async def send_photo( payload['message_effect_id'] = message_effect_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, payload=None): + business_connection_id=None, payload=None, allow_paid_broadcast=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -545,6 +555,8 @@ async def send_paid_media( _payload['business_connection_id'] = business_connection_id if payload: _payload['payload'] = payload + if allow_paid_broadcast is not None: + _payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request( token, method_url, params=_payload, @@ -554,7 +566,8 @@ async def send_paid_media( async def send_media_group( token, chat_id, media, disable_notification=None, - timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): + timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendMediaGroup' media_json, files = await convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -572,6 +585,8 @@ async def send_media_group( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request( token, method_url, params=payload, method='post' if files else 'get', @@ -584,7 +599,7 @@ async def send_location( reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -611,6 +626,8 @@ async def send_location( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) @@ -668,7 +685,7 @@ async def send_venue( reply_markup=None, timeout=None, google_place_id=None, google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -695,13 +712,16 @@ async def send_venue( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): + protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -724,6 +744,8 @@ async def send_contact( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) @@ -743,7 +765,7 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, show_caption_above_media=None): + message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -793,6 +815,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m payload['message_effect_id'] = message_effect_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -800,7 +824,8 @@ async def send_animation( token, chat_id, data, duration=None, caption=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, width=None, height=None, protect_content=None, message_thread_id=None, - has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None): + has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, + allow_paid_broadcast=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -848,12 +873,15 @@ async def send_animation( payload['message_effect_id'] = message_effect_id if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, - protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None, message_effect_id=None): + protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -885,12 +913,14 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_ payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, - message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -928,13 +958,15 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, rep payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None): + message_effect_id=None, allow_paid_broadcast=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -978,13 +1010,16 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, - message_thread_id=None, emoji=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): + message_thread_id=None, emoji=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -1029,6 +1064,8 @@ async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_m payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1528,7 +1565,8 @@ async def delete_message(token, chat_id, message_id, timeout=None): async def send_game( token, chat_id, game_short_name, disable_notification=None, reply_markup=None, timeout=None, - protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None): + protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendGame' payload = {'chat_id': chat_id, 'game_short_name': game_short_name} if disable_notification is not None: @@ -1547,6 +1585,8 @@ async def send_game( payload['business_connection_id'] = business_connection_id if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) @@ -1612,7 +1652,7 @@ async def send_invoice( send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None): + protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None, allow_paid_broadcast=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1646,6 +1686,7 @@ async def send_invoice( :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. :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :param allow_paid_broadcast: :return: """ method_url = r'sendInvoice' @@ -1698,6 +1739,8 @@ async def send_invoice( payload['message_effect_id'] = message_effect_id if provider_token is not None: payload['provider_token'] = provider_token + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) @@ -1954,7 +1997,8 @@ async def send_poll( 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, question_parse_mode=None, question_entities=None, message_effect_id=None): + reply_parameters=None,business_connection_id=None, question_parse_mode=None, question_entities=None, message_effect_id=None, + allow_paid_broadcast=None): method_url = r'sendPoll' payload = { 'chat_id': str(chat_id), @@ -2006,6 +2050,8 @@ async def send_poll( payload['question_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(question_entities)) if message_effect_id: payload['message_effect_id'] = message_effect_id + if allow_paid_broadcast is not None: + payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) From be047fccc6a8d3d72f19def4e55fcb8579cb8e39 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Nov 2024 16:23:22 +0400 Subject: [PATCH 1694/1808] Added the class TransactionPartnerTelegramApi for transactions related to paid broadcasted messages. --- telebot/types.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index e882e9d71..e93f05685 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10394,6 +10394,8 @@ def de_json(cls, json_string): return TransactionPartnerUser.de_json(obj) elif obj["type"] == "telegram_ads": return TransactionPartnerTelegramAds.de_json(obj) + elif obj["type"] == "telegram_api": + return TransactionPartnerTelegramApi.de_json(obj) elif obj["type"] == "other": return TransactionPartnerOther.de_json(obj) @@ -10429,6 +10431,33 @@ def de_json(cls, json_string): return cls(**obj) +class TransactionPartnerTelegramApi(TransactionPartner): + """ + Describes a transaction with payment for paid broadcasting. + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartnertelegramapi + + :param type: Type of the transaction partner, always “telegram_api” + :type type: :obj:`str` + + :param request_count: The number of successful requests that exceeded regular limits and were therefore billed + :type request_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerTelegramApi` + """ + + def __init__(self, type, request_count, **kwargs): + self.type: str = type + self.request_count: int = request_count + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + # noinspection PyShadowingBuiltins class TransactionPartnerUser(TransactionPartner): """ From c16e264e4a281092aac6a5fa72cd33cb1b418619 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Nov 2024 16:24:01 +0400 Subject: [PATCH 1695/1808] Introduced the ability to add media to existing text messages using the method editMessageMedia --- telebot/__init__.py | 9 +++++---- telebot/async_telebot.py | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 59de38db5..62cae3f96 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4969,10 +4969,11 @@ def edit_message_media( business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: """ - Use this method to edit animation, audio, document, photo, or video messages. - If a message is a part of a message album, then it can be edited only to a photo or a video. - Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. - Use previously uploaded file via its file_id or specify a URL. + Use this method to edit animation, audio, document, photo, or video messages, or to add media to text messages. + If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. + When an inline message is edited, a new file can't be uploaded; use a previously uploaded file via its file_id or specify a URL. + On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. + Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3874429ad..75ce41346 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6405,10 +6405,11 @@ async def edit_message_media( business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: """ - Use this method to edit animation, audio, document, photo, or video messages. - If a message is a part of a message album, then it can be edited only to a photo or a video. - Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. - Use previously uploaded file via its file_id or specify a URL. + Use this method to edit animation, audio, document, photo, or video messages, or to add media to text messages. + If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. + When an inline message is edited, a new file can't be uploaded; use a previously uploaded file via its file_id or specify a URL. + On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. + Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia From cabd0cb587da8f8e94871aa3723f0de338f53718 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Nov 2024 16:33:20 +0400 Subject: [PATCH 1696/1808] Added support for hashtag and cashtag entities with a specified chat username that opens a search for the relevant tag within the specified chat. --- telebot/types.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index e93f05685..a4b676c03 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1639,11 +1639,10 @@ class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#messageentity - :param type: Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), “cashtag” ($USD), - “bot_command” (/start@jobs_bot),“url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), - “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), - “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), - “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), + :param type: Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag or #hashtag@chatusername), “cashtag” ($USD or $USD@chatusername), + “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), + “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), + “expandable_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers) :type type: :obj:`str` From 6044655f28828841a5ec4be1ec571d8d60763e44 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 1 Nov 2024 16:33:56 +0400 Subject: [PATCH 1697/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1959744fa..43676d2d1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From 822afdf23d5a931a6c0908367bf7d016b33363f3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 3 Nov 2024 16:51:51 +0300 Subject: [PATCH 1698/1808] Wrapped deprecation logging to a single function --- telebot/types.py | 256 +++++++++++++---------------------------------- 1 file changed, 72 insertions(+), 184 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 4bba24cf7..03a851620 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -24,6 +24,16 @@ logger = logging.getLogger('TeleBot') + +def log_deprecation_warning(warning_message, logging_level=logging.WARNING): + """ + Logs a deprecation warning message. + """ + logger.log(logging_level, warning_message) + if DEPRECATION_STACK_SHOW_DEPTH: + logger.log(logging_level, "".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + + class JsonSerializable(object): """ Subclasses of this class are guaranteed to be able to be converted to JSON format. @@ -1551,53 +1561,39 @@ def html_caption(self) -> Optional[str]: @property def voice_chat_scheduled(self): - logger.warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "voice_chat_scheduled" is deprecated, use "video_chat_scheduled" instead') return self.video_chat_scheduled @property def voice_chat_started(self): - logger.warning('The parameter "voice_chat_started" is deprecated, use "video_chat_started" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "voice_chat_started" is deprecated, use "video_chat_started" instead') return self.video_chat_started @property def voice_chat_ended(self): - logger.warning('The parameter "voice_chat_ended" is deprecated, use "video_chat_ended" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "voice_chat_ended" is deprecated, use "video_chat_ended" instead') return self.video_chat_ended @property def voice_chat_participants_invited(self): - logger.warning('The parameter "voice_chat_participants_invited" is deprecated, use "video_chat_participants_invited" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "voice_chat_participants_invited" is deprecated, use "video_chat_participants_invited" instead') return self.video_chat_participants_invited @property def new_chat_member(self): - logger.warning('The parameter "new_chat_member" is deprecated, use "new_chat_members" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "new_chat_member" is deprecated, use "new_chat_members" instead') return None @property def forward_from(self): - logger.warning('The parameter "forward_from" is deprecated, use "forward_origin" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "forward_from" is deprecated, use "forward_origin" instead') if self.forward_origin and isinstance(self.forward_origin, MessageOriginUser): return self.forward_origin.sender_user return None @property def forward_from_chat(self): - logger.warning('The parameter "forward_from_chat" is deprecated, use "forward_origin" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "forward_from_chat" is deprecated, use "forward_origin" instead') if self.forward_origin and isinstance(self.forward_origin, MessageOriginChat): return self.forward_origin.sender_chat elif self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): @@ -1606,18 +1602,14 @@ def forward_from_chat(self): @property def forward_from_message_id(self): - logger.warning('The parameter "forward_from_message_id" is deprecated, use "forward_origin" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "forward_from_message_id" is deprecated, use "forward_origin" instead') if self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): return self.forward_origin.message_id return None @property def forward_signature(self): - logger.warning('The parameter "forward_signature" is deprecated, use "forward_origin" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "forward_signature" is deprecated, use "forward_origin" instead') if self.forward_origin and isinstance(self.forward_origin, MessageOriginChat): return self.forward_origin.author_signature elif self.forward_origin and isinstance(self.forward_origin, MessageOriginChannel): @@ -1626,27 +1618,21 @@ def forward_signature(self): @property def forward_sender_name(self): - logger.warning('The parameter "forward_sender_name" is deprecated, use "forward_origin" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "forward_sender_name" is deprecated, use "forward_origin" instead') if self.forward_origin and isinstance(self.forward_origin, MessageOriginHiddenUser): return self.forward_origin.sender_user_name return None @property def forward_date(self): - logger.warning('The parameter "forward_date" is deprecated, use "forward_origin" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "forward_date" is deprecated, use "forward_origin" instead') if self.forward_origin: return self.forward_origin.date return None @property def user_shared(self): - logger.warning('The parameter "user_shared" is deprecated, use "users_shared" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "user_shared" is deprecated, use "users_shared" instead') return self.users_shared @property @@ -1872,9 +1858,7 @@ def __init__(self, file_id, file_unique_id, duration, performer=None, title=None @property def thumb(self) -> Optional[PhotoSize]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -1969,9 +1953,7 @@ def __init__(self, file_id, file_unique_id, thumbnail=None, file_name=None, mime @property def thumb(self) -> Optional[PhotoSize]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -2035,9 +2017,7 @@ def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=N @property def thumb(self) -> Optional[PhotoSize]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -2087,9 +2067,7 @@ def __init__(self, file_id, file_unique_id, length, duration, thumbnail=None, fi @property def thumb(self) -> Optional[PhotoSize]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -2632,9 +2610,7 @@ class KeyboardButtonRequestUser(KeyboardButtonRequestUsers): def __init__( self, request_id: int, user_is_bot: Optional[bool]=None, user_is_premium: Optional[bool]=None, max_quantity: Optional[int]=None) -> None: - logger.warning('The class "KeyboardButtonRequestUser" is deprecated, use "KeyboardButtonRequestUsers" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The class "KeyboardButtonRequestUser" is deprecated, use "KeyboardButtonRequestUsers" instead') super().__init__(request_id, user_is_bot=user_is_bot, user_is_premium=user_is_premium, max_quantity=max_quantity) @@ -2781,9 +2757,7 @@ def __init__(self, text: str, request_contact: Optional[bool]=None, self.request_chat: Optional[KeyboardButtonRequestChat] = request_chat self.request_users: Optional[KeyboardButtonRequestUsers] = request_users if request_user is not None: - logger.warning('The parameter "request_user" is deprecated, use "request_users" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "request_user" is deprecated, use "request_users" instead') if self.request_users is None: # noinspection PyTypeChecker self.request_users = request_user @@ -3270,9 +3244,7 @@ def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_ed @property def can_manage_voice_chats(self): - logger.warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') return self.can_manage_video_chats @@ -3597,9 +3569,7 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send if kwargs.get("de_json", False) and can_send_media_messages is not None: # Telegram passes can_send_media_messages in Chat.permissions. Temporary created parameter "de_json" allows avoid # deprection warning and individual parameters overriding. - logger.warning('The parameter "can_send_media_messages" is deprecated. Use individual parameters like "can_send_audios", "can_send_documents" etc.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "can_send_media_messages" is deprecated. Use individual parameters like "can_send_audios", "can_send_documents" etc.') self.can_send_audios: Optional[bool] = can_send_media_messages self.can_send_documents: Optional[bool] = can_send_media_messages self.can_send_photos: Optional[bool] = can_send_media_messages @@ -3969,12 +3939,10 @@ def __init__(self, message_text: str, parse_mode: Optional[str] = None, entities self.entities: Optional[List[MessageEntity]] = entities self.link_preview_options: Optional[LinkPreviewOptions] = link_preview_options if disable_web_page_preview is not None: - logger.warning('The parameter "disable_web_page_preview" is deprecated. Use "link_preview_options" instead.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "disable_web_page_preview" is deprecated. Use "link_preview_options" instead.') if link_preview_options: - logger.warning('Both "link_preview_options" and "disable_web_page_preview" parameters are set: conflicting, "disable_web_page_preview" is deprecated') + log_deprecation_warning('Both "link_preview_options" and "disable_web_page_preview" parameters are set: conflicting, "disable_web_page_preview" is deprecated') else: self.link_preview_options: Optional[LinkPreviewOptions] = LinkPreviewOptions(is_disabled=disable_web_page_preview) @@ -4477,23 +4445,17 @@ def __init__(self, id: str, title: str, input_message_content: InputMessageConte @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property def thumb_width(self) -> int: - logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property def thumb_height(self) -> int: - logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height def to_dict(self): @@ -4584,9 +4546,7 @@ def __init__(self, id: str, photo_url: str, thumbnail_url: str, photo_width: Opt @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url def to_dict(self): @@ -4680,16 +4640,12 @@ def __init__(self, id: str, gif_url: str, thumbnail_url: str, gif_width: Optiona @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property def thumb_mime_type(self) -> str: - logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') return self.thumbnail_mime_type def to_dict(self): @@ -4784,16 +4740,12 @@ def __init__(self, id: str, mpeg4_url: str, thumbnail_url: str, mpeg4_width: Opt @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property def thumb_mime_type(self) -> str: - logger.warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_mime_type" is deprecated, use "thumbnail_mime_type" instead') return self.thumbnail_mime_type def to_dict(self): @@ -4894,9 +4846,7 @@ def __init__(self, id: str, video_url: str, mime_type: str, thumbnail_url: str, @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url def to_dict(self): @@ -5113,23 +5063,17 @@ def __init__(self, id: str, title: str, document_url: str, mime_type: str, capti @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property def thumb_width(self) -> int: - logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property def thumb_height(self) -> int: - logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height def to_dict(self): @@ -5218,23 +5162,17 @@ def __init__(self, id: str, title: str, latitude: float, longitude: float, horiz @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property def thumb_width(self) -> int: - logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property def thumb_height(self) -> int: - logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height def to_dict(self): @@ -5334,23 +5272,17 @@ def __init__(self, id: str, title: str, latitude: float, longitude: float, addre @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property def thumb_width(self) -> int: - logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property def thumb_height(self) -> int: - logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height def to_dict(self): @@ -5435,23 +5367,17 @@ def __init__(self, id: str, phone_number: str, first_name: str, last_name: Optio @property def thumb_url(self) -> str: - logger.warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_url" is deprecated, use "thumbnail_url" instead') return self.thumbnail_url @property def thumb_width(self) -> int: - logger.warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_width" is deprecated, use "thumbnail_width" instead') return self.thumbnail_width @property def thumb_height(self) -> int: - logger.warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb_height" is deprecated, use "thumbnail_height" instead') return self.thumbnail_height def to_dict(self): @@ -6139,9 +6065,7 @@ def __init__(self, file_id, file_unique_id, width=None, height=None, duration=No @property def thumb(self) -> Optional[PhotoSize]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6557,30 +6481,22 @@ def __init__(self, name, title, sticker_type, stickers, thumbnail=None, **kwargs @property def thumb(self) -> Optional[PhotoSize]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @property def contains_masks(self) -> bool: - logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type instead"') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "contains_masks" is deprecated, use "sticker_type instead"') return self.sticker_type == 'mask' @property def is_animated(self) -> bool: - logger.warning('The parameter "is_animated" is deprecated since Bot API 7.2. Stickers can now be mixed') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "is_animated" is deprecated since Bot API 7.2. Stickers can now be mixed') return False @property def is_video(self) -> bool: - logger.warning('The parameter "is_video" is deprecated since Bot API 7.2. Stickers can now be mixed') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "is_video" is deprecated since Bot API 7.2. Stickers can now be mixed') return False @@ -6679,9 +6595,7 @@ def __init__(self, file_id, file_unique_id, type, width, height, is_animated, @property def thumb(self) -> Optional[PhotoSize]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail @@ -6899,9 +6813,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): @@ -6988,9 +6900,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): @@ -7064,9 +6974,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): @@ -7129,9 +7037,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, @property def thumb(self) -> Optional[Union[str, Any]]: - logger.warning('The parameter "thumb" is deprecated, use "thumbnail" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "thumb" is deprecated, use "thumbnail" instead') return self.thumbnail def to_dict(self): @@ -7300,9 +7206,7 @@ def __init__( self.is_anonymous: bool = is_anonymous self.type: str = type if poll_type is not None: - logger.warning("Poll: poll_type parameter is deprecated. Use type instead.") - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning("Poll: poll_type parameter is deprecated. Use type instead.") if type is None: self.type: str = poll_type self.allows_multiple_answers: bool = allows_multiple_answers @@ -7545,9 +7449,7 @@ class VoiceChatStarted(VideoChatStarted): """ def __init__(self): - logger.warning('VoiceChatStarted is deprecated. Use VideoChatStarted instead.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('VoiceChatStarted is deprecated. Use VideoChatStarted instead.') super().__init__() @@ -7579,9 +7481,7 @@ class VoiceChatScheduled(VideoChatScheduled): Deprecated, use :class:`VideoChatScheduled` instead. """ def __init__(self, *args, **kwargs): - logger.warning('VoiceChatScheduled is deprecated. Use VideoChatScheduled instead.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('VoiceChatScheduled is deprecated. Use VideoChatScheduled instead.') super().__init__(*args, **kwargs) @@ -7612,9 +7512,7 @@ class VoiceChatEnded(VideoChatEnded): Deprecated, use :class:`VideoChatEnded` instead. """ def __init__(self, *args, **kwargs): - logger.warning('VoiceChatEnded is deprecated. Use VideoChatEnded instead.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('VoiceChatEnded is deprecated. Use VideoChatEnded instead.') super().__init__(*args, **kwargs) @@ -7647,9 +7545,7 @@ class VoiceChatParticipantsInvited(VideoChatParticipantsInvited): Deprecated, use :class:`VideoChatParticipantsInvited` instead. """ def __init__(self, *args, **kwargs): - logger.warning('VoiceChatParticipantsInvited is deprecated. Use VideoChatParticipantsInvited instead.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('VoiceChatParticipantsInvited is deprecated. Use VideoChatParticipantsInvited instead.') super().__init__(*args, **kwargs) @@ -8296,9 +8192,7 @@ def __init__(self, sticker: Union[str, InputFile], emoji_list: List[str], forma self.format: str = format if not self.format: - logger.warning("Deprecation warning. 'format' parameter is required in InputSticker. Setting format to 'static'.") - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning("Deprecation warning. 'format' parameter is required in InputSticker. Setting format to 'static'.") self.format = "static" if service_utils.is_string(self.sticker): @@ -9416,16 +9310,12 @@ def __init__(self, request_id: int, users: List[SharedUser], **kwargs): @property def user_id(self) -> None: - logger.warning('The parameter "user_id" is deprecated, use "user_ids" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "user_id" is deprecated, use "user_ids" instead') return None @property def user_ids(self) -> List[SharedUser]: - logger.warning('The parameter "user_ids" is deprecated, use "users" instead') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning('The parameter "user_ids" is deprecated, use "users" instead') return self.users @@ -9726,9 +9616,7 @@ def __init__(self, chat, message_id, date, **kwargs): @staticmethod def __universal_deprecation(property_name): - logger.warning(f'Deprecation warning: the field "{property_name}" is not accessible for InaccessibleMessage. You should check if your object is Message instance before access.') - if DEPRECATION_STACK_SHOW_DEPTH: - logger.warning("".join(traceback.format_stack(limit=DEPRECATION_STACK_SHOW_DEPTH))) + log_deprecation_warning(f'Deprecation warning: the field "{property_name}" is not accessible for InaccessibleMessage. You should check if your object is Message instance before access.') return None def __getattr__(self, item): From d41e3904b9ba9b154e0a885ff7e13be33dc3860e Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 15 Nov 2024 18:26:39 +0300 Subject: [PATCH 1699/1808] Version bump and python upgrade --- .github/workflows/setup_python.yml | 2 +- .travis.yml | 2 +- README.md | 2 +- docs/source/conf.py | 2 +- pyproject.toml | 6 +++--- telebot/version.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/setup_python.yml b/.github/workflows/setup_python.yml index 670251511..603515ddc 100644 --- a/.github/workflows/setup_python.yml +++ b/.github/workflows/setup_python.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.9', 'pypy-3.10'] + python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.9', 'pypy-3.10'] name: ${{ matrix.python-version }} and tests steps: - uses: actions/checkout@v2 diff --git a/.travis.yml b/.travis.yml index 1fe9399d6..cc0a0c62e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: python python: - - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" + - "3.13" - "pypy3" install: "pip install -r requirements.txt" script: diff --git a/README.md b/README.md index 43676d2d1..97d55930d 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ ## Getting started -This API is tested with Python 3.8-3.12 and Pypy 3. +This API is tested with Python 3.9-3.13 and Pypy 3. There are two ways to install the library: * Installation using pip (a Python package manager): diff --git a/docs/source/conf.py b/docs/source/conf.py index 87d25810f..b45dfb263 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.23.0' +release = '4.24.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 07f2ce727..610ced195 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,21 +4,21 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.23.0" +version = "4.24.0" description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} readme = "README.md" -requires-python = ">=3.8" +requires-python = ">=3.9" keywords = ["telegram", "bot", "api", "tools"] classifiers = [ "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Environment :: Console", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)" ] diff --git a/telebot/version.py b/telebot/version.py index bf02d6515..bde2003e4 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.23.0' +__version__ = '4.24.0' From e17a26d1c6c95bdc7aea6695b28b7fc9eec312cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:30:52 +0000 Subject: [PATCH 1700/1808] Bump werkzeug in /examples/serverless/flask_google_cloud_bot Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.0.3 to 3.0.6. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/werkzeug/compare/3.0.3...3.0.6) --- updated-dependencies: - dependency-name: werkzeug dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- examples/serverless/flask_google_cloud_bot/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/serverless/flask_google_cloud_bot/requirements.txt b/examples/serverless/flask_google_cloud_bot/requirements.txt index ed0491b0a..e08389a50 100644 --- a/examples/serverless/flask_google_cloud_bot/requirements.txt +++ b/examples/serverless/flask_google_cloud_bot/requirements.txt @@ -1,4 +1,4 @@ pyTelegramBotAPI==4.11.0 Flask==3.0.0 gunicorn==22.0.0 -Werkzeug==3.0.3 +Werkzeug==3.0.6 From ff6db8b947619ee67c62798e7c0e5e968c3ff4df Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:08:42 +0400 Subject: [PATCH 1701/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97d55930d..119545779 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From c8d8ec7c2ca8f519c0a60084773d7df344bf8688 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:26:25 +0400 Subject: [PATCH 1702/1808] Added business connection id and subscription period for createinvoicelink --- telebot/__init__.py | 15 +++++++++++++-- telebot/apihelper.py | 6 +++++- telebot/async_telebot.py | 14 ++++++++++++-- telebot/asyncio_helper.py | 6 +++++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 62cae3f96..04c4d3ced 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5418,7 +5418,9 @@ def create_invoice_link(self, need_shipping_address: Optional[bool]=None, send_phone_number_to_provider: Optional[bool]=None, send_email_to_provider: Optional[bool]=None, - is_flexible: Optional[bool]=None) -> str: + is_flexible: Optional[bool]=None, + subscription_period: Optional[int]=None, + business_connection_id: Optional[str]=None) -> str: """ Use this method to create a link for an invoice. @@ -5427,6 +5429,9 @@ def create_invoice_link(self, Telegram documentation: https://core.telegram.org/bots/api#createinvoicelink + :param business_connection_id: Unique identifier of the business connection on behalf of which the link will be created + :type business_connection_id: :obj:`str` + :param title: Product name, 1-32 characters :type title: :obj:`str` @@ -5449,6 +5454,11 @@ def create_invoice_link(self, (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`list` of :obj:`types.LabeledPrice` + :subscription_period: The number of seconds the subscription will be active for before the next payment. + The currency must be set to “XTR” (Telegram Stars) if the parameter is used. Currently, it must always + be 2592000 (30 days) if specified. + :type subscription_period: :obj:`int` + :param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency :type max_tip_amount: :obj:`int` @@ -5505,7 +5515,8 @@ def create_invoice_link(self, photo_width=photo_width, photo_height=photo_height, need_name=need_name, need_phone_number=need_phone_number, need_email=need_email, need_shipping_address=need_shipping_address, send_phone_number_to_provider=send_phone_number_to_provider, - send_email_to_provider=send_email_to_provider, is_flexible=is_flexible) + send_email_to_provider=send_email_to_provider, is_flexible=is_flexible ,subscription_period=subscription_period, + business_connection_id=business_connection_id) # noinspection PyShadowingBuiltins diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 92d419c45..43e536e6f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1970,7 +1970,7 @@ def create_invoice_link(token, title, description, payload, provider_token, currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, - send_email_to_provider=None, is_flexible=None): + send_email_to_provider=None, is_flexible=None, subscription_period=None, business_connection_id=None): method_url = r'createInvoiceLink' payload = {'title': title, 'description': description, 'payload': payload, 'currency': currency, 'prices': _convert_list_json_serializable(prices)} @@ -2004,6 +2004,10 @@ def create_invoice_link(token, title, description, payload, provider_token, payload['is_flexible'] = is_flexible if provider_token is not None: payload['provider_token'] = provider_token + if subscription_period: + payload['subscription_period'] = subscription_period + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 75ce41346..61b2dee21 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6836,7 +6836,9 @@ async def create_invoice_link(self, need_shipping_address: Optional[bool]=None, send_phone_number_to_provider: Optional[bool]=None, send_email_to_provider: Optional[bool]=None, - is_flexible: Optional[bool]=None) -> str: + is_flexible: Optional[bool]=None, + subscription_period: Optional[int]=None, + business_connection_id: Optional[str]=None) -> str: """ Use this method to create a link for an invoice. @@ -6844,6 +6846,9 @@ async def create_invoice_link(self, Telegram documentation: https://core.telegram.org/bots/api#createinvoicelink + + :param business_connection_id: Unique identifier of the business connection on behalf of which the link will be created + :type business_connection_id: :obj:`str` :param title: Product name, 1-32 characters :type title: :obj:`str` @@ -6867,6 +6872,11 @@ async def create_invoice_link(self, (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`list` of :obj:`types.LabeledPrice` + :subscription_period: The number of seconds the subscription will be active for before the next payment. + The currency must be set to “XTR” (Telegram Stars) if the parameter is used. Currently, it must always + be 2592000 (30 days) if specified. + :type subscription_period: :obj:`int` + :param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency :type max_tip_amount: :obj:`int` @@ -6921,7 +6931,7 @@ async def create_invoice_link(self, currency, prices, max_tip_amount, suggested_tip_amounts, provider_data, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, - send_email_to_provider, is_flexible) + send_email_to_provider, is_flexible, subscription_period=subscription_period, business_connection_id=business_connection_id) return result # noinspection PyShadowingBuiltins diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index b21b7f9f0..46b70fe85 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1952,7 +1952,7 @@ async def create_invoice_link(token, title, description, payload, provider_token currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, - send_email_to_provider=None, is_flexible=None): + send_email_to_provider=None, is_flexible=None, subscription_period=None, business_connection_id=None): method_url = r'createInvoiceLink' payload = {'title': title, 'description': description, 'payload': payload, 'currency': currency, 'prices': await _convert_list_json_serializable(prices)} @@ -1986,6 +1986,10 @@ async def create_invoice_link(token, title, description, payload, provider_token payload['is_flexible'] = is_flexible if provider_token is not None: payload['provider_token'] = provider_token + if subscription_period: + payload['subscription_period'] = subscription_period + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, method='post') From 90b3c976f542d2a8bd350839834e6f9260966b69 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:35:01 +0400 Subject: [PATCH 1703/1808] Added the fields subscription_expiration_date, is_recurring and is_first_recurring to the class SuccessfulPayment. --- telebot/types.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 66ad984bc..e11e74ecf 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6318,6 +6318,15 @@ class SuccessfulPayment(JsonDeserializable): :param invoice_payload: Bot specified invoice payload :type invoice_payload: :obj:`str` + :param subscription_expiration_date: Optional. Expiration date of the subscription, in Unix time; for recurring payments only + :type subscription_expiration_date: :obj:`int` + + :param is_recurring: Optional. True, if the payment is a recurring payment, false otherwise + :type is_recurring: :obj:`bool` + + :param is_first_recurring: Optional. True, if the payment is the first payment for a subscription + :type is_first_recurring: :obj:`bool` + :param shipping_option_id: Optional. Identifier of the shipping option chosen by the user :type shipping_option_id: :obj:`str` @@ -6341,7 +6350,8 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, - telegram_payment_charge_id=None, provider_payment_charge_id=None, **kwargs): + telegram_payment_charge_id=None, provider_payment_charge_id=None, + subscription_expiration_date=None, is_recurring=None, is_first_recurring=None, **kwargs): self.currency: str = currency self.total_amount: int = total_amount self.invoice_payload: str = invoice_payload @@ -6349,6 +6359,9 @@ def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=N self.order_info: OrderInfo = order_info self.telegram_payment_charge_id: str = telegram_payment_charge_id self.provider_payment_charge_id: str = provider_payment_charge_id + self.subscription_expiration_date: Optional[int] = subscription_expiration_date + self.is_recurring: Optional[bool] = is_recurring + self.is_first_recurring: Optional[bool] = is_first_recurring # noinspection PyShadowingBuiltins From a12d8c2844c2ac5fecc85e6ae9832ee0aabdce79 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:38:42 +0400 Subject: [PATCH 1704/1808] Added the method editUserStarSubscription. --- telebot/__init__.py | 20 ++++++++++++++++++++ telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 20 ++++++++++++++++++++ telebot/asyncio_helper.py | 6 ++++++ 4 files changed, 51 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 04c4d3ced..142d71193 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5814,6 +5814,26 @@ def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> """ return apihelper.refund_star_payment(self.token, user_id, telegram_payment_charge_id) + def edit_user_star_subscription(self, user_id: int, telegram_payment_charge_id: str, is_canceled: bool) -> bool: + """ + Allows the bot to cancel or re-enable extension of a subscription paid in Telegram Stars. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#edituserstarsubscription + + :param user_id: Identifier of the user whose subscription will be edited + :type user_id: :obj:`int` + + :param telegram_payment_charge_id: Telegram payment identifier for the subscription + :type telegram_payment_charge_id: :obj:`str` + + :param is_canceled: Pass True to cancel extension of the user subscription; the subscription must be active up to the end of the current subscription period. Pass False to allow the user to re-enable a subscription that was previously canceled by the bot. + :type is_canceled: :obj:`bool` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.edit_user_star_subscription(self.token, user_id, telegram_payment_charge_id, is_canceled) + def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 43e536e6f..69fa5bf4f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1805,6 +1805,11 @@ def refund_star_payment(token, user_id, telegram_payment_charge_id): payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id} return _make_request(token, method_url, params=payload) +def edit_user_star_subscription(token, user_id, telegram_payment_charge_id, is_canceled): + method_url = 'editUserStarSubscription' + payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id, 'is_canceled': is_canceled} + return _make_request(token, method_url, params=payload) + def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 61b2dee21..f82c7238e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7220,6 +7220,26 @@ async def refund_star_payment(self, user_id: int, telegram_payment_charge_id: st """ return await asyncio_helper.refund_star_payment(self.token, user_id, telegram_payment_charge_id) + async def edit_user_star_subscription(self, user_id: int, telegram_payment_charge_id: str, is_canceled: bool) -> bool: + """ + Allows the bot to cancel or re-enable extension of a subscription paid in Telegram Stars. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#edituserstarsubscription + + :param user_id: Identifier of the user whose subscription will be edited + :type user_id: :obj:`int` + + :param telegram_payment_charge_id: Telegram payment identifier for the subscription + :type telegram_payment_charge_id: :obj:`str` + + :param is_canceled: Pass True to cancel extension of the user subscription; the subscription must be active up to the end of the current subscription period. Pass False to allow the user to re-enable a subscription that was previously canceled by the bot. + :type is_canceled: :obj:`bool` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.edit_user_star_subscription(self.token, user_id, telegram_payment_charge_id, is_canceled) + async def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 46b70fe85..3e1a68a1d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1793,6 +1793,12 @@ async def refund_star_payment(token, user_id, telegram_payment_charge_id): return await _process_request(token, method_url, params=payload) +async def edit_user_star_subscription(token, user_id, telegram_payment_charge_id, is_canceled): + method_url = 'editUserStarSubscription' + payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id, 'is_canceled': is_canceled} + return await _process_request(token, method_url, params=payload) + + async def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' payload = {'chat_id': chat_id} From 8eb5d1065b2cd2712430bb7928806919edf6e961 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:40:04 +0400 Subject: [PATCH 1705/1808] Added the field subscription_period to the class TransactionPartnerUser. --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e11e74ecf..76cf20b61 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10489,6 +10489,9 @@ class TransactionPartnerUser(TransactionPartner): :param invoice_payload: Optional, Bot-specified invoice payload :type invoice_payload: :obj:`str` + :param subscription_period: Optional. The duration of the paid subscription + :type subscription_period: :obj:`int` + :param paid_media: Optional. Information about the paid media bought by the user :type paid_media: :obj:`list` of :class:`PaidMedia` @@ -10496,11 +10499,13 @@ class TransactionPartnerUser(TransactionPartner): :rtype: :class:`TransactionPartnerUser` """ - def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, **kwargs): + def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, + subscription_period=None, **kwargs): self.type: str = type self.user: User = user self.invoice_payload: Optional[str] = invoice_payload self.paid_media: Optional[List[PaidMedia]] = paid_media + self.subscription_period: Optional[int] = subscription_period @classmethod def de_json(cls, json_string): From e55b0971f65f74738c17d264291398da178101a5 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:01:48 +0400 Subject: [PATCH 1706/1808] Added the method setUserEmojiStatus. The user must allow the bot to manage their emoji status. --- telebot/__init__.py | 20 ++++++++++++++++++++ telebot/apihelper.py | 10 ++++++++++ telebot/async_telebot.py | 20 ++++++++++++++++++++ telebot/asyncio_helper.py | 10 ++++++++++ 4 files changed, 60 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 142d71193..494c0ec5d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1512,6 +1512,26 @@ def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, apihelper.get_user_profile_photos(self.token, user_id, offset=offset, limit=limit) ) + def set_user_emoji_status(self, user_id: int, emoji_status_custom_emoji_id: Optional[str]=None, emoji_status_expiration_date: Optional[int]=None) -> bool: + """ + Changes the emoji status for a given user that previously allowed the bot to manage their emoji status via the Mini App method requestEmojiStatusAccess. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setuseremojistatus + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param emoji_status_custom_emoji_id: Custom emoji identifier of the emoji status to set. Pass an empty string to remove the status. + :type emoji_status_custom_emoji_id: :obj:`str` + + :param emoji_status_expiration_date: Expiration date of the emoji status, if any + :type emoji_status_expiration_date: :obj:`int` + + :return: :obj:`bool` + """ + return apihelper.set_user_emoji_status( + self.token, user_id, emoji_status_custom_emoji_id=emoji_status_custom_emoji_id, emoji_status_expiration_date=emoji_status_expiration_date) + def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 69fa5bf4f..68623922f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -341,6 +341,16 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None): payload['limit'] = limit return _make_request(token, method_url, params=payload) + +def set_user_emoji_status(token, user_id, emoji_status_custom_emoji_id=None, emoji_status_expiration_date=None): + method_url = r'setUserEmojiStatus' + payload = {'user_id': user_id} + if emoji_status_custom_emoji_id: + payload['emoji_status_custom_emoji_id'] = emoji_status_custom_emoji_id + if emoji_status_expiration_date: + payload['emoji_status_expiration_date'] = emoji_status_expiration_date + return _make_request(token, method_url, params=payload) + def set_message_reaction(token, chat_id, message_id, reaction=None, is_big=None): method_url = r'setMessageReaction' payload = {'chat_id': chat_id, 'message_id': message_id} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f82c7238e..6ab489756 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2987,6 +2987,26 @@ 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 set_user_emoji_status(self, user_id: int, emoji_status_custom_emoji_id: Optional[str]=None, emoji_status_expiration_date: Optional[int]=None) -> bool: + """ + Use this method to change the emoji status for a given user that previously allowed the bot to manage their emoji status via the Mini App method requestEmojiStatusAccess. + + Telegram documentation: https://core.telegram.org/bots/api#setuseremojistatus + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param emoji_status_custom_emoji_id: Custom emoji identifier of the emoji status to set. Pass an empty string to remove the status. + :type emoji_status_custom_emoji_id: :obj:`str`, optional + + :param emoji_status_expiration_date: Expiration date of the emoji status, if any + :type emoji_status_expiration_date: :obj:`int`, optional + + :return: :obj:`bool` + """ + result = await asyncio_helper.set_user_emoji_status(self.token, user_id, emoji_status_custom_emoji_id, emoji_status_expiration_date) + return result async def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3e1a68a1d..41d94d0bd 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -325,6 +325,16 @@ async def get_user_profile_photos(token, user_id, offset=None, limit=None): payload['limit'] = limit return await _process_request(token, method_url, params=payload) + +async def set_user_emoji_status(token, user_id, emoji_status_custom_emoji_id=None, emoji_status_expiration_date=None): + method_url = r'setUserEmojiStatus' + payload = {'user_id': user_id} + if emoji_status_custom_emoji_id: + payload['emoji_status_custom_emoji_id'] = emoji_status_custom_emoji_id + if emoji_status_expiration_date: + payload['emoji_status_expiration_date'] = emoji_status_expiration_date + return await _process_request(token, method_url, params=payload) + async def set_message_reaction(token, chat_id, message_id, reaction=None, is_big=None): method_url = r'setMessageReaction' payload = {'chat_id': chat_id, 'message_id': message_id} From bee6bab8213e1d22731146b9e92245fd1a42cdb5 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:10:08 +0400 Subject: [PATCH 1707/1808] Added the class PreparedInlineMessage and the method savePreparedInlineMessage, allowing bots to suggest users to send a specific message from a Mini App via the method shareMessage. --- telebot/__init__.py | 36 ++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 15 +++++++++++++++ telebot/async_telebot.py | 30 ++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 14 ++++++++++++++ telebot/types.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 494c0ec5d..586f44c9a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6769,6 +6769,42 @@ def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryR """ return apihelper.answer_web_app_query(self.token, web_app_query_id, result) + def save_prepared_inline_message( + self, user_id: int, result: types.InlineQueryResultBase, allow_user_chats: Optional[bool]=None, + allow_bot_chats: Optional[bool]=None, allow_group_chats: Optional[bool]=None, + allow_channel_chats: Optional[bool]=None) -> types.PreparedInlineMessage: + """ + Use this method to store a message that can be sent by a user of a Mini App. + Returns a PreparedInlineMessage object. + + Telegram Documentation: https://core.telegram.org/bots/api#savepreparedinlinemessage + + :param user_id: Unique identifier of the target user that can use the prepared message + :type user_id: :obj:`int` + + :param result: A JSON-serialized object describing the message to be sent + :type result: :class:`telebot.types.InlineQueryResultBase` + + :param allow_user_chats: Pass True if the message can be sent to private chats with users + :type allow_user_chats: :obj:`bool` + + :param allow_bot_chats: Pass True if the message can be sent to private chats with bots + :type allow_bot_chats: :obj:`bool` + + :param allow_group_chats: Pass True if the message can be sent to group and supergroup chats + :type allow_group_chats: :obj:`bool` + + :param allow_channel_chats: Pass True if the message can be sent to channel chats + :type allow_channel_chats: :obj:`bool` + + :return: On success, a PreparedInlineMessage object is returned. + :rtype: :class:`telebot.types.PreparedInlineMessage` + """ + return types.PreparedInlineMessage.de_json( + apihelper.save_prepared_inline_message( + self.token, user_id, result, allow_user_chats=allow_user_chats, allow_bot_chats=allow_bot_chats, + allow_group_chats=allow_group_chats, allow_channel_chats=allow_channel_chats) + ) def register_for_reply(self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 68623922f..01a1a77ba 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1981,6 +1981,21 @@ def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResul return _make_request(token, method_url, params=payload, method='post') +def save_prepared_inline_message(token, user_id, result: types.InlineQueryResultBase, allow_user_chats=None, + allow_bot_chats=None, allow_group_chats=None, allow_channel_chats=None): + method_url = 'savePreparedInlineMessage' + payload = {'user_id': user_id, 'result': result.to_json()} + if allow_user_chats is not None: + payload['allow_user_chats'] = allow_user_chats + if allow_bot_chats is not None: + payload['allow_bot_chats'] = allow_bot_chats + if allow_group_chats is not None: + payload['allow_group_chats'] = allow_group_chats + if allow_channel_chats is not None: + payload['allow_channel_chats'] = allow_channel_chats + return _make_request(token, method_url, params=payload, method='post') + + def create_invoice_link(token, title, description, payload, provider_token, currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6ab489756..659596f59 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3148,6 +3148,36 @@ async def answer_web_app_query(self, web_app_query_id: str, result: types.Inline return await asyncio_helper.answer_web_app_query(self.token, web_app_query_id, result) + async def save_prepared_inline_message(self, user_id: int, result: types.InlineQueryResultBase, allow_user_chats: Optional[bool]=None, + allow_bot_chats: Optional[bool]=None, allow_group_chats: Optional[bool]=None, allow_channel_chats: Optional[bool]=None) -> types.PreparedInlineMessage: + """ + Stores a message that can be sent by a user of a Mini App. Returns a PreparedInlineMessage object. + + Telegram Documentation: https://core.telegram.org/bots/api#savepreparedinlinemessage + + :param user_id: Unique identifier of the target user that can use the prepared message + :type user_id: :obj:`int` + + :param result: A JSON-serialized object describing the message to be sent + :type result: :class:`telebot.types.InlineQueryResultBase` + + :param allow_user_chats: Pass True if the message can be sent to private chats with users + :type allow_user_chats: :obj:`bool`, optional + + :param allow_bot_chats: Pass True if the message can be sent to private chats with bots + :type allow_bot_chats: :obj:`bool`, optional + + :param allow_group_chats: Pass True if the message can be sent to group and supergroup chats + :type allow_group_chats: :obj:`bool`, optional + + :param allow_channel_chats: Pass True if the message can be sent to channel chats + :type allow_channel_chats: :obj:`bool`, optional + + :return: :class:`telebot.types.PreparedInlineMessage` + """ + result = await asyncio_helper.save_prepared_inline_message(self.token, user_id, result, allow_user_chats, allow_bot_chats, allow_group_chats, allow_channel_chats) + return types.PreparedInlineMessage.de_json(result) + async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 41d94d0bd..4523e7173 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -406,6 +406,20 @@ async def answer_web_app_query(token, web_app_query_id, result: types.InlineQuer return await _process_request(token, method_url, params=payload, method='post') +async def save_prepared_inline_message(token, user_id, result: types.InlineQueryResultBase, allow_user_chats=None, allow_bot_chats=None, allow_group_chats=None, allow_channel_chats=None): + method_url = r'savePreparedInlineMessage' + payload = {'user_id': user_id, 'result': result.to_json()} + if allow_user_chats is not None: + payload['allow_user_chats'] = allow_user_chats + if allow_bot_chats is not None: + payload['allow_bot_chats'] = allow_bot_chats + if allow_group_chats is not None: + payload['allow_group_chats'] = allow_group_chats + if allow_channel_chats is not None: + payload['allow_channel_chats'] = allow_channel_chats + return await _process_request(token, method_url, params=payload) + + async def get_chat_member(token, chat_id, user_id): method_url = r'getChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} diff --git a/telebot/types.py b/telebot/types.py index 76cf20b61..904379dbc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10997,3 +10997,31 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) + + +class PreparedInlineMessage(JsonDeserializable): + """ + Describes an inline message to be sent by a user of a Mini App. + + Telegram documentation: https://core.telegram.org/bots/api#preparedinlinemessage + + :param id: Unique identifier of the prepared message + :type id: :obj:`str` + + :param expiration_date: Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used + :type expiration_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`PreparedInlineMessage` + """ + + def __init__(self, id, expiration_date, **kwargs): + self.id: str = id + self.expiration_date: int = expiration_date + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + From 3d740ffd97364ec5fd81c3eb3bba47d356500f60 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:18:37 +0400 Subject: [PATCH 1708/1808] Added the classes Gift and Gifts and the method getAvailableGifts, allowing bots to get all gifts available for sending. --- telebot/__init__.py | 12 ++++++++ telebot/apihelper.py | 5 ++++ telebot/async_telebot.py | 12 ++++++++ telebot/asyncio_helper.py | 4 +++ telebot/types.py | 63 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 586f44c9a..a274d2272 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6249,7 +6249,19 @@ def delete_sticker_set(self, name:str) -> bool: :rtype: :obj:`bool` """ return apihelper.delete_sticker_set(self.token, name) + + def get_available_gifts(self) -> types.Gifts: + """ + Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. + + Telegram documentation: https://core.telegram.org/bots/api#getavailablegifts + :return: On success, a Gifts object is returned. + :rtype: :class:`telebot.types.Gifts` + """ + return types.Gifts.de_json( + apihelper.get_available_gifts(self.token) + ) def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 01a1a77ba..83606f746 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1924,6 +1924,11 @@ def delete_sticker_set(token, name): return _make_request(token, method_url, params=payload, method='post') +def get_available_gifts(token): + method_url = 'getAvailableGifts' + return _make_request(token, method_url) + + def set_sticker_emoji_list(token, sticker, emoji_list): method_url = 'setStickerEmojiList' payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 659596f59..60719477d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7707,6 +7707,18 @@ async def delete_sticker_set(self, name:str) -> bool: return await asyncio_helper.delete_sticker_set(self.token, name) + async def get_available_gifts(self) -> types.Gifts: + """ + Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. + + Telegram documentation: https://core.telegram.org/bots/api#getavailablegifts + + :return: On success, a Gifts object is returned. + :rtype: :class:`telebot.types.Gifts` + """ + + return types.Gifts.de_json(await asyncio_helper.get_available_gifts(self.token)) + async def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: """ Use this method to replace an existing sticker in a sticker set with a new one. The method is equivalent to calling deleteStickerFromSet, then addStickerToSet, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 4523e7173..47a1da1dc 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1916,6 +1916,10 @@ async def delete_sticker_set(token, name): payload = {'name': name} return await _process_request(token, method_url, params=payload, method='post') +async def get_available_gifts(token): + method_url = 'getAvailableGifts' + return await _process_request(token, method_url) + async def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): method_url = 'setCustomEmojiStickerSetThumbnail' payload = {'name': name} diff --git a/telebot/types.py b/telebot/types.py index 904379dbc..c6386a61b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11025,3 +11025,66 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) + +class Gift(JsonDeserializable): + """ + This object represents a gift that can be sent by the bot. + + Telegram documentation: https://core.telegram.org/bots/api#gift + + :param id: Unique identifier of the gift + :type id: :obj:`str` + + :param sticker: The sticker that represents the gift + :type sticker: :class:`Sticker` + + :param star_count: The number of Telegram Stars that must be paid to send the sticker + :type star_count: :obj:`int` + + :param total_count: Optional. The total number of the gifts of this type that can be sent; for limited gifts only + :type total_count: :obj:`int` + + :param remaining_count: Optional. The number of remaining gifts of this type that can be sent; for limited gifts only + :type remaining_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`Gift` + """ + + def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, **kwargs): + self.id: str = id + self.sticker: Sticker = sticker + self.star_count: int = star_count + self.total_count: Optional[int] = total_count + self.remaining_count: Optional[int] = remaining_count + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['sticker'] = Sticker.de_json(obj['sticker']) + return cls(**obj) + +class Gifts(JsonDeserializable): + """ + This object represent a list of gifts. + + Telegram documentation: https://core.telegram.org/bots/api#gifts + + :param gifts: The list of gifts + :type gifts: :obj:`list` of :class:`Gift` + + :return: Instance of the class + :rtype: :class:`Gifts` + """ + + def __init__(self, gifts, **kwargs): + self.gifts: List[Gift] = gifts + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['gifts'] = [Gift.de_json(gift) for gift in obj['gifts']] + return cls(**obj) + \ No newline at end of file From a2d2a86d4f496babff9353bf672222c773bbe5be Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:22:31 +0400 Subject: [PATCH 1709/1808] Added the method sendGift, allowing bots to send gifts to users. --- telebot/__init__.py | 26 ++++++++++++++++++++++++++ telebot/apihelper.py | 11 +++++++++++ telebot/async_telebot.py | 26 ++++++++++++++++++++++++++ telebot/asyncio_helper.py | 11 +++++++++++ 4 files changed, 74 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index a274d2272..bb33f55b5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6249,6 +6249,32 @@ def delete_sticker_set(self, name:str) -> bool: :rtype: :obj:`bool` """ return apihelper.delete_sticker_set(self.token, name) + + def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + """ + Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#sendgift + + :param user_id: Unique identifier of the target user that will receive the gift + :type user_id: :obj:`int` + + :param gift_id: Identifier of the gift + :type gift_id: :obj:`str` + + :param text: Text that will be shown along with the gift; 0-255 characters + :type text: :obj:`str` + + :param text_parse_mode: Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_parse_mode: :obj:`str` + + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_entities: :obj:`list` of :obj:`types.MessageEntity` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 83606f746..5ea37c4fd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1929,6 +1929,17 @@ def get_available_gifts(token): return _make_request(token, method_url) +def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None): + method_url = 'sendGift' + payload = {'user_id': user_id, 'gift_id': gift_id} + if text: + payload['text'] = text + if text_parse_mode: + payload['text_parse_mode'] = text_parse_mode + if text_entities: + payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + return _make_request(token, method_url, params=payload, method='post') + def set_sticker_emoji_list(token, sticker, emoji_list): method_url = 'setStickerEmojiList' payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 60719477d..7f50b1c86 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7706,6 +7706,32 @@ async def delete_sticker_set(self, name:str) -> bool: """ return await asyncio_helper.delete_sticker_set(self.token, name) + + async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + """ + Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#sendgift + + :param user_id: Unique identifier of the target user that will receive the gift + :type user_id: :obj:`int` + + :param gift_id: Identifier of the gift + :type gift_id: :obj:`str` + + :param text: Text that will be shown along with the gift; 0-255 characters + :type text: :obj:`str` + + :param text_parse_mode: Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_parse_mode: :obj:`str` + + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_entities: :obj:`list` of :obj:`types.MessageEntity` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.send_gift(self.token, user_id, gift_id, text, text_parse_mode, text_entities) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 47a1da1dc..56604c750 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1916,6 +1916,17 @@ async def delete_sticker_set(token, name): payload = {'name': name} return await _process_request(token, method_url, params=payload, method='post') +async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None): + method_url = 'sendGift' + payload = {'user_id': user_id, 'gift_id': gift_id} + if text: + payload['text'] = text + if text_parse_mode: + payload['text_parse_mode'] = text_parse_mode + if text_entities: + payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + return await _process_request(token, method_url, params=payload, method='post') + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) From 9c7c47f3628f5997949f456b52aa18cb624832ff Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:23:46 +0400 Subject: [PATCH 1710/1808] Added the field gift to the class TransactionPartnerUser. --- telebot/types.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index c6386a61b..3a71887de 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10495,17 +10495,21 @@ class TransactionPartnerUser(TransactionPartner): :param paid_media: Optional. Information about the paid media bought by the user :type paid_media: :obj:`list` of :class:`PaidMedia` + :param gift: Optional. The gift sent to the user by the bot + :type gift: :class:`Gift` + :return: Instance of the class :rtype: :class:`TransactionPartnerUser` """ def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, - subscription_period=None, **kwargs): + subscription_period=None, gift: Optional[Gift] = None, **kwargs): self.type: str = type self.user: User = user self.invoice_payload: Optional[str] = invoice_payload self.paid_media: Optional[List[PaidMedia]] = paid_media self.subscription_period: Optional[int] = subscription_period + self.gift: Optional[Gift] = gift @classmethod def de_json(cls, json_string): @@ -10514,6 +10518,8 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) if 'paid_media' in obj: obj['paid_media'] = [PaidMedia.de_json(media) for media in obj['paid_media']] + if 'gift' in obj: + obj['gift'] = Gift.de_json(obj['gift']) return cls(**obj) @@ -11025,7 +11031,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - + class Gift(JsonDeserializable): """ This object represents a gift that can be sent by the bot. From e57136d6d72aec1b8af29ea11a026a6a1c8b8229 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:27:29 +0000 Subject: [PATCH 1711/1808] Bump aiohttp from 3.10.2 to 3.10.11 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.10.2 to 3.10.11. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.2...v3.10.11) --- updated-dependencies: - dependency-name: aiohttp dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2abfd54bd..55dd7e486 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest requests==2.32.0 wheel==0.38.1 -aiohttp==3.10.2 +aiohttp==3.10.11 From f9c857fe60724140ec9f5453f363e4fa69e3134d Mon Sep 17 00:00:00 2001 From: Andy Kluger Date: Tue, 26 Nov 2024 12:07:24 -0500 Subject: [PATCH 1712/1808] Update colorcodebot description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 119545779..eb59d8a74 100644 --- a/README.md +++ b/README.md @@ -830,7 +830,7 @@ Here are some examples of template: * [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot. * [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie* * [NeoBot](https://github.com/neoranger/NeoBot) by [@NeoRanger](https://github.com/neoranger) -* [ColorCodeBot](https://t.me/colorcodebot) ([source](https://github.com/andydecleyre/colorcodebot)) - Share code snippets as beautifully syntax-highlighted HTML and/or images. +* [ColorCodeBot](https://t.me/colorcodebot) ([source](https://github.com/andydecleyre/colorcodebot)) - Share code snippets as beautifully syntax-highlighted images. * [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall. * [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash * [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot) - DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch From 73c68c13bf7eba3e2364e3c0dd70b0495fa1f1c6 Mon Sep 17 00:00:00 2001 From: Samorezov V Date: Fri, 29 Nov 2024 22:45:08 +0300 Subject: [PATCH 1713/1808] Fix: change code for non-escape mode - return url --- telebot/formatting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 24299b51a..801a67ec1 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -240,7 +240,7 @@ def mlink(content: str, url: str, escape: Optional[bool]=True) -> str: :return: The formatted string. :rtype: :obj:`str` """ - return '[{}]({})'.format(escape_markdown(content), escape_markdown(url) if escape else content) + return '[{}]({})'.format(escape_markdown(content), escape_markdown(url) if escape else url) def hlink(content: str, url: str, escape: Optional[bool]=True) -> str: From b089ae1e744ff417b44d39c63596c43831ba2be7 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 4 Dec 2024 17:48:01 +0400 Subject: [PATCH 1714/1808] bot api 8.1 --- telebot/types.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 3a71887de..119e14a53 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10411,6 +10411,8 @@ def de_json(cls, json_string): return TransactionPartnerTelegramAds.de_json(obj) elif obj["type"] == "telegram_api": return TransactionPartnerTelegramApi.de_json(obj) + elif obj["type"] == "affiliate_program": + return TransactionPartnerAffiliateProgram.de_json(obj) elif obj["type"] == "other": return TransactionPartnerOther.de_json(obj) @@ -10486,6 +10488,9 @@ class TransactionPartnerUser(TransactionPartner): :param user: Information about the user :type user: :class:`User` + :param affiliate: Optional. Information about the affiliate that received a commission via this transaction + :type affiliate: :class:`AffiliateInfo` + :param invoice_payload: Optional, Bot-specified invoice payload :type invoice_payload: :obj:`str` @@ -10502,10 +10507,11 @@ class TransactionPartnerUser(TransactionPartner): :rtype: :class:`TransactionPartnerUser` """ - def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, + def __init__(self, type, user, affiliate=None, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, subscription_period=None, gift: Optional[Gift] = None, **kwargs): self.type: str = type self.user: User = user + self.affiliate: Optional[AffiliateInfo] = affiliate self.invoice_payload: Optional[str] = invoice_payload self.paid_media: Optional[List[PaidMedia]] = paid_media self.subscription_period: Optional[int] = subscription_period @@ -10584,6 +10590,9 @@ class StarTransaction(JsonDeserializable): :param amount: Number of Telegram Stars transferred by the transaction :type amount: :obj:`int` + :param nanostar_amount: Optional. The number of 1/1000000000 shares of Telegram Stars transferred by the transaction; from 0 to 999999999 + :type nanostar_amount: :obj:`int` + :param date: Date the transaction was created in Unix time :type date: :obj:`int` @@ -10607,12 +10616,13 @@ def de_json(cls, json_string): obj['receiver'] = TransactionPartner.de_json(obj['receiver']) return cls(**obj) - def __init__(self, id, amount, date, source=None, receiver=None, **kwargs): + def __init__(self, id, amount, date, source=None, receiver=None, nanostar_amount=None, **kwargs): self.id: str = id self.amount: int = amount self.date: int = date self.source: Optional[TransactionPartner] = source self.receiver: Optional[TransactionPartner] = receiver + self.nanostar_amount: Optional[int] = nanostar_amount class StarTransactions(JsonDeserializable): @@ -11093,4 +11103,82 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['gifts'] = [Gift.de_json(gift) for gift in obj['gifts']] return cls(**obj) - \ No newline at end of file + + +class TransactionPartnerAffiliateProgram(TransactionPartner): + """ + Describes the affiliate program that issued the affiliate commission received via this transaction. + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartneraffiliateprogram + + :param type: Type of the transaction partner, always “affiliate_program” + :type type: :obj:`str` + + :param sponsor_user: Optional. Information about the bot that sponsored the affiliate program + :type sponsor_user: :class:`User` + + :param commission_per_mille: The number of Telegram Stars received by the bot for each 1000 Telegram Stars received by the affiliate program sponsor from referred users + :type commission_per_mille: :obj:`int` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerAffiliateProgram` + """ + + def __init__(self, type, commission_per_mille, sponsor_user=None, **kwargs): + self.type: str = type + self.sponsor_user: Optional[User] = sponsor_user + self.commission_per_mille: int = commission_per_mille + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'sponsor_user' in obj: + obj['sponsor_user'] = User.de_json(obj['sponsor_user']) + + return cls(**obj) + + +class AffiliateInfo(JsonDeserializable): + """ + Contains information about the affiliate that received a commission via this transaction. + + Telegram documentation: https://core.telegram.org/bots/api#affiliateinfo + + :param affiliate_user: Optional. The bot or the user that received an affiliate commission if it was received by a bot or a user + :type affiliate_user: :class:`User` + + :param affiliate_chat: Optional. The chat that received an affiliate commission if it was received by a chat + :type affiliate_chat: :class:`Chat` + + :param commission_per_mille: The number of Telegram Stars received by the affiliate for each 1000 Telegram Stars received by the bot from referred users + :type commission_per_mille: :obj:`int` + + :param amount: Integer amount of Telegram Stars received by the affiliate from the transaction, rounded to 0; can be negative for refunds + :type amount: :obj:`int` + + :param nanostar_amount: Optional. The number of 1/1000000000 shares of Telegram Stars received by the affiliate; from -999999999 to 999999999; can be negative for refunds + :type nanostar_amount: :obj:`int` + + :return: Instance of the class + :rtype: :class:`AffiliateInfo` + """ + + def __init__(self, commission_per_mille, amount, affiliate_user=None, affiliate_chat=None, nanostar_amount=None, **kwargs): + self.affiliate_user: Optional[User] = affiliate_user + self.affiliate_chat: Optional[Chat] = affiliate_chat + self.commission_per_mille: int = commission_per_mille + self.amount: int = amount + self.nanostar_amount: Optional[int] = nanostar_amount + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'affiliate_user' in obj: + obj['affiliate_user'] = User.de_json(obj['affiliate_user']) + if 'affiliate_chat' in obj: + obj['affiliate_chat'] = Chat.de_json(obj['affiliate_chat']) + return cls(**obj) + + From e73703f3c11c842284aa7efc737c93786349a0ce Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 4 Dec 2024 17:49:09 +0400 Subject: [PATCH 1715/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb59d8a74..bbc6f1698 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From 15b03428de769044581115b51b9fbe79969b1d72 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 4 Dec 2024 18:14:39 +0400 Subject: [PATCH 1716/1808] fix --- telebot/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 119e14a53..e4dafaa7d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10526,6 +10526,8 @@ def de_json(cls, json_string): obj['paid_media'] = [PaidMedia.de_json(media) for media in obj['paid_media']] if 'gift' in obj: obj['gift'] = Gift.de_json(obj['gift']) + if 'affiliate' in obj: + obj['affiliate'] = AffiliateInfo.de_json(obj['affiliate']) return cls(**obj) From beef0e6d7b8692fb6f3f1a0eea93d1520efe3cf0 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 4 Dec 2024 17:43:21 +0300 Subject: [PATCH 1717/1808] Bump version --- 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 b45dfb263..fb559d15e 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.24.0' +release = '4.25.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 610ced195..ebb66f1c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.24.0" +version = "4.25.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 bde2003e4..9317bd5d7 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.24.0' +__version__ = '4.25.0' From 6ecfd1b00ce8135bf7a0194e3c39c745590b18ed Mon Sep 17 00:00:00 2001 From: Heck Lawert Date: Mon, 9 Dec 2024 10:02:49 +0100 Subject: [PATCH 1718/1808] Fix issue with thumbnail in InputMedia types --- telebot/apihelper.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 5ea37c4fd..3044b226d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2271,6 +2271,12 @@ def convert_input_media_array(array): if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media + if 'thumbnail' in media_dict: + thumbnail = media_dict['thumbnail'] + if isinstance(thumbnail, types.InputFile): + thumbnail_key = 'thumbnail_' + key + files[thumbnail_key] = thumbnail + media_dict['thumbnail'] = 'attach://' + thumbnail_key media.append(media_dict) return json.dumps(media), files From 4cc438abe6c161dc22f1ad2937fc7200cfe06abb Mon Sep 17 00:00:00 2001 From: Daniil Gilmutdinov Date: Tue, 10 Dec 2024 23:17:43 +0500 Subject: [PATCH 1719/1808] Fixed typo in method params --- 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 bb33f55b5..51eb63d4a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5474,7 +5474,7 @@ def create_invoice_link(self, (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`list` of :obj:`types.LabeledPrice` - :subscription_period: The number of seconds the subscription will be active for before the next payment. + :param subscription_period: The number of seconds the subscription will be active for before the next payment. The currency must be set to “XTR” (Telegram Stars) if the parameter is used. Currently, it must always be 2592000 (30 days) if specified. :type subscription_period: :obj:`int` diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7f50b1c86..0e298ace7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6922,7 +6922,7 @@ async def create_invoice_link(self, (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`list` of :obj:`types.LabeledPrice` - :subscription_period: The number of seconds the subscription will be active for before the next payment. + :param subscription_period: The number of seconds the subscription will be active for before the next payment. The currency must be set to “XTR” (Telegram Stars) if the parameter is used. Currently, it must always be 2592000 (30 days) if specified. :type subscription_period: :obj:`int` From 63ddb1537f760027043d225ef540ab49a8013728 Mon Sep 17 00:00:00 2001 From: Heck Lawert Date: Thu, 12 Dec 2024 16:09:23 +0100 Subject: [PATCH 1720/1808] Fix issue with thumbnail in InputMedia types --- telebot/asyncio_helper.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 56604c750..d11245d56 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2243,6 +2243,12 @@ async def convert_input_media_array(array): if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media + if 'thumbnail' in media_dict: + thumbnail = media_dict['thumbnail'] + if isinstance(thumbnail, types.InputFile): + thumbnail_key = 'thumbnail_' + key + files[thumbnail_key] = thumbnail + media_dict['thumbnail'] = 'attach://' + thumbnail_key media.append(media_dict) return json.dumps(media), files From 17e0aca55bcd2e554c68140bd20b3b5390c4a727 Mon Sep 17 00:00:00 2001 From: jakie Date: Wed, 18 Dec 2024 13:29:52 +0700 Subject: [PATCH 1721/1808] Added new example: stars payment bot This is a example how to make a payment using telegram stars. --- examples/stars_payment.py | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 examples/stars_payment.py diff --git a/examples/stars_payment.py b/examples/stars_payment.py new file mode 100644 index 000000000..e261a5e31 --- /dev/null +++ b/examples/stars_payment.py @@ -0,0 +1,64 @@ +# Import required libraries +import telebot +from telebot import types + +bot = telebot.TeleBot('TOKEN') + +# Product prices in stars +PRICES = { + '10 Stars': 10, # $1.00 - Entry level package + '50 Stars': 45, # $4.50 - Medium package with 10% discount + '100 Stars': 80, # $8.00 - Large package with 20% discount + '500 Stars': 350 # $35.00 - Bulk package with 30% discount +} + +# Handler for /start command - Then the bot will send amount stars to purchase +@bot.message_handler(commands=['start']) +def send_welcome(message): + # Create a custom keyboard with 2 buttons per row + markup = types.ReplyKeyboardMarkup(row_width=2) + # Generate buttons for each product in our PRICES dictionary + buttons = [types.KeyboardButton(product) for product in PRICES.keys()] + # Add all buttons to the markup + markup.add(*buttons) + + # Send welcome message with the custom keyboard + bot.reply_to(message, + "Welcome to Stars Payment Bot!\nPlease select amount of stars to purchase:", + reply_markup=markup) + +# Handler for when user selects a product from the keyboard +@bot.message_handler(func=lambda message: message.text in PRICES.keys()) +def handle_product_selection(message): + # Get selected product and its price + product = message.text + price = PRICES[product] + + # Create invoice with product details + prices = [types.LabeledPrice(label=product, amount=price)] + + # Send payment invoice to user + bot.send_invoice( + message.chat.id, # Chat ID to send invoice to + title=f"Purchase {product}", # Title of the invoice + description=f"Buy {product} for your account", # Description shown on invoice + provider_token='', # Payment provider token (empty for testing) + currency='XTR', # Currency code + prices=prices, # List of prices (we only have one item) + start_parameter='stars-payment', # Deep-linking parameter + invoice_payload=product # Payload to identify the product after payment + ) + +# Pre-checkout handler - Called before payment is completed +@bot.pre_checkout_query_handler(func=lambda query: True) +def process_pre_checkout_query(pre_checkout_query): + bot.answer_pre_checkout_query(pre_checkout_query.id, ok=True) + +# Handler for successful payments +@bot.message_handler(content_types=['successful_payment']) +def handle_successful_payment(message): + # Get the product that was purchased from the invoice payload + product = message.successful_payment.invoice_payload + bot.reply_to(message, + f"Payment for {product} successful!") +bot.polling() From b0afe2d2f735173db0c361dca1f827f30213118d Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 2 Jan 2025 10:58:44 +0500 Subject: [PATCH 1722/1808] Bot API 8.2 full support --- README.md | 2 +- telebot/__init__.py | 72 +++++++++++++++++++++++++++++++++++++-- telebot/apihelper.py | 38 +++++++++++++++++---- telebot/async_telebot.py | 72 +++++++++++++++++++++++++++++++++++++-- telebot/asyncio_helper.py | 34 ++++++++++++++---- telebot/types.py | 10 +++++- telebot/version.py | 2 +- 7 files changed, 209 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index bbc6f1698..ff1b5159f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

diff --git a/telebot/__init__.py b/telebot/__init__.py index 51eb63d4a..ae210f130 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6250,7 +6250,8 @@ def delete_sticker_set(self, name:str) -> bool: """ return apihelper.delete_sticker_set(self.token, name) - def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, + text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool: """ Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. @@ -6262,6 +6263,9 @@ def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_p :param gift_id: Identifier of the gift :type gift_id: :obj:`str` + :param pay_for_upgrade: Pass True to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver + :type pay_for_upgrade: :obj:`bool` + :param text: Text that will be shown along with the gift; 0-255 characters :type text: :obj:`str` @@ -6274,7 +6278,71 @@ def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_p :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities) + return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities, + pay_for_upgrade=pay_for_upgrade) + + def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool: + """ + Verifies a user on behalf of the organization which is represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#verifyuser + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description. + :type custom_description: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.verify_user(self.token, user_id, custom_description=custom_description) + + def verify_chat(self, chat_id: Union[int, str], custom_description: Optional[str]=None) -> bool: + """ + Verifies a chat on behalf of the organization which is represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#verifychat + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` | :obj:`str` + + :param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description. + :type custom_description: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.verify_chat(self.token, chat_id, custom_description=custom_description) + + def remove_user_verification(self, user_id: int) -> bool: + """ + Removes verification from a user who is currently verified on behalf of the organization represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#removeuserverification + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return apihelper.remove_user_verification(self.token, user_id) + + def remove_chat_verification(self, chat_id: Union[int, str]) -> bool: + """ + Removes verification from a chat that is currently verified on behalf of the organization represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#removechatverification + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` | :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.remove_chat_verification(self.token, chat_id) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 3044b226d..887f163db 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1929,7 +1929,7 @@ def get_available_gifts(token): return _make_request(token, method_url) -def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None): +def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None): method_url = 'sendGift' payload = {'user_id': user_id, 'gift_id': gift_id} if text: @@ -1938,6 +1938,36 @@ def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_ent payload['text_parse_mode'] = text_parse_mode if text_entities: payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + if pay_for_upgrade is not None: + payload['pay_for_upgrade'] = pay_for_upgrade + return _make_request(token, method_url, params=payload, method='post') + + +def verify_user(token, user_id, custom_description=None): + method_url = 'verifyUser' + payload = {'user_id': user_id} + if custom_description: + payload['custom_description'] = custom_description + return _make_request(token, method_url, params=payload, method='post') + + +def verify_chat(token, chat_id, custom_description=None): + method_url = 'verifyChat' + payload = {'chat_id': chat_id} + if custom_description: + payload['custom_description'] = custom_description + return _make_request(token, method_url, params=payload, method='post') + + +def remove_user_verification(token, user_id): + method_url = 'removeUserVerification' + payload = {'user_id': user_id} + return _make_request(token, method_url, params=payload, method='post') + + +def remove_chat_verification(token, chat_id): + method_url = 'removeChatVerification' + payload = {'chat_id': chat_id} return _make_request(token, method_url, params=payload, method='post') def set_sticker_emoji_list(token, sticker, emoji_list): @@ -2271,12 +2301,6 @@ def convert_input_media_array(array): if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media - if 'thumbnail' in media_dict: - thumbnail = media_dict['thumbnail'] - if isinstance(thumbnail, types.InputFile): - thumbnail_key = 'thumbnail_' + key - files[thumbnail_key] = thumbnail - media_dict['thumbnail'] = 'attach://' + thumbnail_key media.append(media_dict) return json.dumps(media), files diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0e298ace7..288b5f26e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7707,7 +7707,8 @@ async def delete_sticker_set(self, name:str) -> bool: return await asyncio_helper.delete_sticker_set(self.token, name) - async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, + text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool: """ Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. @@ -7719,6 +7720,9 @@ async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, :param gift_id: Identifier of the gift :type gift_id: :obj:`str` + :param pay_for_upgrade: Pass True to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver + :type pay_for_upgrade: :obj:`bool` + :param text: Text that will be shown along with the gift; 0-255 characters :type text: :obj:`str` @@ -7731,7 +7735,71 @@ async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, :return: Returns True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.send_gift(self.token, user_id, gift_id, text, text_parse_mode, text_entities) + return await asyncio_helper.send_gift(self.token, user_id, gift_id, text, text_parse_mode, text_entities, pay_for_upgrade=pay_for_upgrade) + + async def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool: + """ + Verifies a user on behalf of the organization which is represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#verifyuser + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description. + :type custom_description: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.verify_user(self.token, user_id, custom_description) + + async def verify_chat(self, chat_id: Union[int, str], custom_description: Optional[str]=None) -> bool: + """ + Verifies a chat on behalf of the organization which is represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#verifychat + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` | :obj:`str` + + :param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description. + :type custom_description: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + + return await asyncio_helper.verify_chat(self.token, chat_id, custom_description) + + async def remove_user_verification(self, user_id: int) -> bool: + """ + Removes verification from a user who is currently verified on behalf of the organization represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#removeuserverification + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + + """ + return await asyncio_helper.remove_user_verification(self.token, user_id) + + async def remove_chat_verification(self, chat_id: Union[int, str]) -> bool: + """ + Removes verification from a chat that is currently verified on behalf of the organization represented by the bot. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#removechatverification + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` | :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.remove_chat_verification(self.token, chat_id) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index d11245d56..8c5b9ace6 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1916,7 +1916,7 @@ async def delete_sticker_set(token, name): payload = {'name': name} return await _process_request(token, method_url, params=payload, method='post') -async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None): +async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None): method_url = 'sendGift' payload = {'user_id': user_id, 'gift_id': gift_id} if text: @@ -1925,6 +1925,32 @@ async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, te payload['text_parse_mode'] = text_parse_mode if text_entities: payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + if pay_for_upgrade is not None: + payload['pay_for_upgrade'] = pay_for_upgrade + return await _process_request(token, method_url, params=payload, method='post') + +async def verify_user(token, user_id, custom_description=None): + method_url = 'verifyUser' + payload = {'user_id': user_id} + if custom_description: + payload['custom_description'] = custom_description + return await _process_request(token, method_url, params=payload, method='post') + +async def verify_chat(token, chat_id, custom_description=None): + method_url = 'verifyChat' + payload = {'chat_id': chat_id} + if custom_description: + payload['custom_description'] = custom_description + return await _process_request(token, method_url, params=payload, method='post') + +async def remove_user_verification(token, user_id): + method_url = 'removeUserVerification' + payload = {'user_id': user_id} + return await _process_request(token, method_url, params=payload, method='post') + +async def remove_chat_verification(token, chat_id): + method_url = 'removeChatVerification' + payload = {'chat_id': chat_id} return await _process_request(token, method_url, params=payload, method='post') async def get_available_gifts(token): @@ -2243,12 +2269,6 @@ async def convert_input_media_array(array): if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media - if 'thumbnail' in media_dict: - thumbnail = media_dict['thumbnail'] - if isinstance(thumbnail, types.InputFile): - thumbnail_key = 'thumbnail_' + key - files[thumbnail_key] = thumbnail - media_dict['thumbnail'] = 'attach://' + thumbnail_key media.append(media_dict) return json.dumps(media), files diff --git a/telebot/types.py b/telebot/types.py index e4dafaa7d..301ae61a1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -4449,6 +4449,10 @@ def __init__(self, id: str, title: str, input_message_content: InputMessageConte self.thumbnail_width: Optional[int] = thumbnail_width self.thumbnail_height: Optional[int] = thumbnail_height + if hide_url: + log_deprecation_warning('The parameter "hide_url" is deprecated. Pass an empty string as url instead.') + self.url = '' + @property def thumb_url(self) -> str: @@ -11059,6 +11063,9 @@ class Gift(JsonDeserializable): :param star_count: The number of Telegram Stars that must be paid to send the sticker :type star_count: :obj:`int` + :param upgrade_star_count: Optional. The number of Telegram Stars that must be paid to upgrade the gift to a unique one + :type upgrade_star_count: :obj:`int` + :param total_count: Optional. The total number of the gifts of this type that can be sent; for limited gifts only :type total_count: :obj:`int` @@ -11069,12 +11076,13 @@ class Gift(JsonDeserializable): :rtype: :class:`Gift` """ - def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, **kwargs): + def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, **kwargs): self.id: str = id self.sticker: Sticker = sticker self.star_count: int = star_count self.total_count: Optional[int] = total_count self.remaining_count: Optional[int] = remaining_count + self.upgrade_star_count: Optional[int] = upgrade_star_count @classmethod def de_json(cls, json_string): diff --git a/telebot/version.py b/telebot/version.py index 9317bd5d7..bde2003e4 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.25.0' +__version__ = '4.24.0' From ebb291af1c2495a997a7e2f8515a98cf1ed1c37c Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 3 Jan 2025 19:21:04 +0500 Subject: [PATCH 1723/1808] fix issues --- telebot/apihelper.py | 6 ++++++ telebot/async_telebot.py | 4 ++-- telebot/asyncio_helper.py | 6 ++++++ telebot/version.py | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 887f163db..69f3f8e4f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2301,6 +2301,12 @@ def convert_input_media_array(array): if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media + if 'thumbnail' in media_dict: + thumbnail = media_dict['thumbnail'] + if isinstance(thumbnail, types.InputFile): + thumbnail_key = 'thumbnail_' + key + files[thumbnail_key] = thumbnail + media_dict['thumbnail'] = 'attach://' + thumbnail_key media.append(media_dict) return json.dumps(media), files diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 288b5f26e..a3f717dff 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7752,7 +7752,7 @@ async def verify_user(self, user_id: int, custom_description: Optional[str]=None :return: Returns True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.verify_user(self.token, user_id, custom_description) + return await asyncio_helper.verify_user(self.token, user_id, custom_description=custom_description) async def verify_chat(self, chat_id: Union[int, str], custom_description: Optional[str]=None) -> bool: """ @@ -7770,7 +7770,7 @@ async def verify_chat(self, chat_id: Union[int, str], custom_description: Option :rtype: :obj:`bool` """ - return await asyncio_helper.verify_chat(self.token, chat_id, custom_description) + return await asyncio_helper.verify_chat(self.token, chat_id, custom_description=custom_description) async def remove_user_verification(self, user_id: int) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 8c5b9ace6..ba51a7daa 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2269,6 +2269,12 @@ async def convert_input_media_array(array): if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media + if 'thumbnail' in media_dict: + thumbnail = media_dict['thumbnail'] + if isinstance(thumbnail, types.InputFile): + thumbnail_key = 'thumbnail_' + key + files[thumbnail_key] = thumbnail + media_dict['thumbnail'] = 'attach://' + thumbnail_key media.append(media_dict) return json.dumps(media), files diff --git a/telebot/version.py b/telebot/version.py index bde2003e4..9317bd5d7 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.24.0' +__version__ = '4.25.0' From ab7de46fdadacddb04dc1c36b9ab317f7bab7b0d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 5 Jan 2025 00:28:05 +0300 Subject: [PATCH 1724/1808] Bump version --- 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 fb559d15e..0ce35e8db 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.25.0' +release = '4.26.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index ebb66f1c4..471aaa45b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.25.0" +version = "4.26.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 9317bd5d7..b37f3e662 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.25.0' +__version__ = '4.26.0' From 198d92f8b3c1d5b075ab4aa83ed3624583e210b7 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 10 Feb 2025 20:43:30 +0400 Subject: [PATCH 1725/1808] Fix deprecation bug #2451 --- 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 ba51a7daa..e076b8e26 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -37,7 +37,7 @@ def __init__(self) -> None: async def create_session(self): self.session = aiohttp.ClientSession(connector=aiohttp.TCPConnector( limit=REQUEST_LIMIT, - ssl_context=self.ssl_context + ssl=self.ssl_context )) return self.session From 36b2753a1b5c12851d42a5e5c869a0b40949c2a3 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:06:45 +0400 Subject: [PATCH 1726/1808] added chat_id to send_gift, user_id made optional; --- telebot/__init__.py | 19 +++++++++++++------ telebot/apihelper.py | 7 ++++++- telebot/async_telebot.py | 21 +++++++++++++++------ telebot/asyncio_helper.py | 7 ++++++- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ae210f130..eadd5aae8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6250,19 +6250,23 @@ def delete_sticker_set(self, name:str) -> bool: """ return apihelper.delete_sticker_set(self.token, name) - def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, + def send_gift(self, gift_id: str, user_id: Optional[Union[str, int]] = None, chat_id: Optional[Union[str, int]] = None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool: """ Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#sendgift - :param user_id: Unique identifier of the target user that will receive the gift - :type user_id: :obj:`int` - :param gift_id: Identifier of the gift :type gift_id: :obj:`str` + :param user_id: Required if chat_id is not specified. Unique identifier of the target user who will receive the gift. + :type user_id::obj:`int` | :obj:`str` + + :param chat_id: Required if user_id is not specified. Unique identifier for the chat or username of the channel + (in the format @channelusername) that will receive the gift. + :type chat_id: :obj:`int` | :obj:`str` + :param pay_for_upgrade: Pass True to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver :type pay_for_upgrade: :obj:`bool` @@ -6278,8 +6282,11 @@ def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_p :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities, - pay_for_upgrade=pay_for_upgrade) + if user_id is None and chat_id is None: + raise ValueError("Either user_id or chat_id must be specified.") + + return apihelper.send_gift(self.token, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities, + pay_for_upgrade=pay_for_upgrade, chat_id=chat_id, user_id=user_id) def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 69f3f8e4f..899470031 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1929,7 +1929,8 @@ def get_available_gifts(token): return _make_request(token, method_url) -def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None): +def send_gift(token, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None, + chat_id=None, user_id=None): method_url = 'sendGift' payload = {'user_id': user_id, 'gift_id': gift_id} if text: @@ -1940,6 +1941,10 @@ def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_ent payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) if pay_for_upgrade is not None: payload['pay_for_upgrade'] = pay_for_upgrade + if chat_id: + payload['chat_id'] = chat_id + if user_id: + payload['user_id'] = user_id return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index a3f717dff..7d2008077 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7707,19 +7707,24 @@ async def delete_sticker_set(self, name:str) -> bool: return await asyncio_helper.delete_sticker_set(self.token, name) - async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, - text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool: + async def send_gift(self, gift_id: str, user_id: Optional[Union[str, int]] = None, chat_id: Optional[Union[str, int]] = None, + text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None, + pay_for_upgrade: Optional[bool]=None) -> bool: """ Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#sendgift - :param user_id: Unique identifier of the target user that will receive the gift - :type user_id: :obj:`int` - :param gift_id: Identifier of the gift :type gift_id: :obj:`str` + :param user_id: Required if chat_id is not specified. Unique identifier of the target user who will receive the gift. + :type user_id::obj:`int` | :obj:`str` + + :param chat_id: Required if user_id is not specified. Unique identifier for the chat or username of the channel + (in the format @channelusername) that will receive the gift. + :type chat_id: :obj:`int` | :obj:`str` + :param pay_for_upgrade: Pass True to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver :type pay_for_upgrade: :obj:`bool` @@ -7735,7 +7740,11 @@ async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, :return: Returns True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.send_gift(self.token, user_id, gift_id, text, text_parse_mode, text_entities, pay_for_upgrade=pay_for_upgrade) + if user_id is None and chat_id is None: + raise ValueError("Either user_id or chat_id must be specified.") + + return await asyncio_helper.send_gift(self.token, gift_id, text, text_parse_mode, text_entities, pay_for_upgrade=pay_for_upgrade, + chat_id=chat_id, user_id=user_id) async def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index e076b8e26..f0ca629c8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1916,7 +1916,8 @@ async def delete_sticker_set(token, name): payload = {'name': name} return await _process_request(token, method_url, params=payload, method='post') -async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None): +async def send_gift(token, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None, + user_id=None, chat_id=None): method_url = 'sendGift' payload = {'user_id': user_id, 'gift_id': gift_id} if text: @@ -1927,6 +1928,10 @@ async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, te payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) if pay_for_upgrade is not None: payload['pay_for_upgrade'] = pay_for_upgrade + if chat_id: + payload['chat_id'] = chat_id + if user_id: + payload['user_id'] = user_id return await _process_request(token, method_url, params=payload, method='post') async def verify_user(token, user_id, custom_description=None): From d7c2a326fa50b5d503a5b965214a093ce70c4640 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:08:39 +0400 Subject: [PATCH 1727/1808] Added can_send_gift to ChatFullInfo --- telebot/types.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 301ae61a1..dd0824f9f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -688,6 +688,13 @@ class ChatFullInfo(JsonDeserializable): :param permissions: Optional. Default chat member permissions, for groups and supergroups. Returned only in getChat. :type permissions: :class:`telebot.types.ChatPermissions` + :param can_send_gift: Optional. True, if gifts can be sent to the chat + :type can_send_gift: :obj:`bool` + + :param can_send_paid_media: Optional. True, if paid media messages can be sent or forwarded to the channel chat. + The field is available only for channel chats. + :type can_send_paid_media: :obj:`bool` + :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` @@ -728,10 +735,6 @@ class ChatFullInfo(JsonDeserializable): :param location: Optional. For supergroups, the location to which the supergroup is connected. Returned only in getChat. :type location: :class:`telebot.types.ChatLocation` - :param can_send_paid_media: Optional. True, if paid media messages can be sent or forwarded to the channel chat. - The field is available only for channel chats. - :type can_send_paid_media: :obj:`bool` - :return: Instance of the class :rtype: :class:`telebot.types.ChatFullInfo` """ @@ -774,7 +777,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, profile_background_custom_emoji_id=None, has_visible_history=None, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, business_opening_hours=None, personal_chat=None, birthdate=None, - can_send_paid_media=None, **kwargs): + can_send_paid_media=None, can_send_gift=None, **kwargs): self.id: int = id self.type: str = type self.title: Optional[str] = title @@ -819,6 +822,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.personal_chat: Optional[Chat] = personal_chat self.birthdate: Optional[Birthdate] = birthdate self.can_send_paid_media: Optional[bool] = can_send_paid_media + self.can_send_gift: Optional[bool] = can_send_gift class Chat(ChatFullInfo): From eb2cfd221d2b728200e9682d637f20298a10a683 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:10:31 +0400 Subject: [PATCH 1728/1808] Added TransactionPartnerChat --- telebot/types.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index dd0824f9f..5bdf918bf 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10423,6 +10423,8 @@ def de_json(cls, json_string): return TransactionPartnerAffiliateProgram.de_json(obj) elif obj["type"] == "other": return TransactionPartnerOther.de_json(obj) + elif obj["type"] == "chat": + return TransactionPartnerChat.de_json(obj) # noinspection PyShadowingBuiltins @@ -11196,3 +11198,36 @@ def de_json(cls, json_string): return cls(**obj) +class TransactionPartnerChat(TransactionPartner): + """ + Describes a transaction with a chat. + + Telegram documentation: https://core.telegram.org/bots/api#transactionpartnerchat + + :param type: Type of the transaction partner, always “chat” + :type type: :obj:`str` + + :param chat: Information about the chat + :type chat: :class:`Chat` + + :param gift: Optional. The gift sent to the chat by the bot + :type gift: :class:`Gift` + + :return: Instance of the class + :rtype: :class:`TransactionPartnerChat` + """ + + def __init__(self, type, chat, gift=None, **kwargs): + self.type: str = type + self.chat: Chat = chat + self.gift: Optional[Gift] = gift + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['chat'] = Chat.de_json(obj['chat']) + if 'gift' in obj: + obj['gift'] = Gift.de_json(obj['gift']) + return cls(**obj) + From 73f34d316ce3fab33120ccefd3524d2f3956595b Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:12:51 +0400 Subject: [PATCH 1729/1808] Added the fields cover and start_timestamp to the class Video, containing a message-specific cover and a start timestamp for the video. --- telebot/types.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 5bdf918bf..3ecfd0905 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1985,6 +1985,12 @@ class Video(JsonDeserializable): :param thumbnail: Optional. Video thumbnail :type thumbnail: :class:`telebot.types.PhotoSize` + :param cover: Optional. Available sizes of the cover of the video in the message + :type cover: List[:class:`telebot.types.PhotoSize`] + + :param start_timestamp: Optional. Timestamp in seconds from which the video will play in the message + :type start_timestamp: :obj:`int` + :param file_name: Optional. Original filename as defined by sender :type file_name: :obj:`str` @@ -2005,9 +2011,12 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) + if 'cover' in obj: + obj['cover'] = [PhotoSize.de_json(c) for c in obj['cover']] return cls(**obj) - def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=None, file_name=None, mime_type=None, file_size=None, **kwargs): + def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=None, file_name=None, mime_type=None, file_size=None, + cover=None, start_timestamp=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id self.width: int = width @@ -2017,6 +2026,8 @@ def __init__(self, file_id, file_unique_id, width, height, duration, thumbnail=N self.file_name: Optional[str] = file_name self.mime_type: Optional[str] = mime_type self.file_size: Optional[int] = file_size + self.cover: Optional[List[PhotoSize]] = cover + self.start_timestamp: Optional[int] = start_timestamp @property def thumb(self) -> Optional[PhotoSize]: From fcee398fe0f4e16e6c895b7f7657ba9f1e8cd81f Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:20:15 +0400 Subject: [PATCH 1730/1808] Added the parameters cover and start_timestamp to the method sendVideo, allowing bots to specify a cover and a start timestamp for the videos they send. --- telebot/__init__.py | 19 ++++++++++++++++--- telebot/apihelper.py | 13 ++++++++++++- telebot/async_telebot.py | 14 ++++++++++++-- telebot/asyncio_helper.py | 12 +++++++++++- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index eadd5aae8..3629f23a6 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2824,7 +2824,9 @@ def send_video( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + cover: Optional[Union[Any, str]]=None, + start_timestamp: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -2845,8 +2847,18 @@ def send_video( :param height: Video height :type height: :obj:`int` - :param thumbnail: 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 thumbnail: 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 . :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + + :param cover: Cover for the video in the message. Pass a file_id to send a file that exists on the Telegram servers (recommended), + pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under + name. More information on Sending Files » + :type cover: :obj:`str` or :class:`telebot.types.InputFile` + + :param start_timestamp: Start timestamp for the video in the message + :type start_timestamp: :obj:`int` :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -2949,7 +2961,8 @@ def send_video( thumbnail=thumbnail, height=height, width=width, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, + cover=cover, start_timestamp=start_timestamp) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 899470031..9f7325a08 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -783,7 +783,8 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): + message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, + cover=None, start_timestamp=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -835,6 +836,16 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if cover: + if not util.is_string(cover): + if files: + files['cover'] = cover + else: + files = {'cover': cover} + else: + payload['cover'] = cover + if start_timestamp: + payload['start_timestamp'] = start_timestamp return _make_request(token, method_url, params=payload, files=files, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7d2008077..2022d37c4 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -4341,7 +4341,9 @@ async def send_video( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + cover: Optional[Union[Any, str]]=None, + start_timestamp: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -4364,6 +4366,14 @@ async def send_video( :param thumbnail: 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 . :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` + + :param cover: Cover for the video in the message. Pass a file_id to send a file that exists on the Telegram servers (recommended), + pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under + name. More information on Sending Files » + :type cover: :obj:`str` or :class:`telebot.types.InputFile` + + :param start_timestamp: Start timestamp for the video in the message + :type start_timestamp: :obj:`int` :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -4467,7 +4477,7 @@ async def send_video( self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, cover=cover, start_timestamp=start_timestamp)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index f0ca629c8..360138bd8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -789,7 +789,7 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): + message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, cover=None, start_timestamp=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -841,6 +841,16 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if cover: + if not util.is_string(cover): + if files: + files['cover'] = cover + else: + files = {'cover': cover} + else: + payload['cover'] = cover + if start_timestamp: + payload['start_timestamp'] = start_timestamp return await _process_request(token, method_url, params=payload, files=files, method='post') From 1ccc96669a8e74770eb421a161280a06345a26c6 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:24:28 +0400 Subject: [PATCH 1731/1808] Added the fields cover and start_timestamp to the classes InputMediaVideo and InputPaidMediaVideo, allowing bots to edit video cover and start timestamp and specify them for videos in albums and paid media. --- telebot/types.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 3ecfd0905..af0d4cf74 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6802,6 +6802,14 @@ class InputMediaVideo(InputMedia): multipart/form-data under . More information on Sending Files » :type thumbnail: InputFile or :obj:`str` + :param cover: Cover for the video in the message. Pass a file_id to send a file that exists on the Telegram servers (recommended), + pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under + name. More information on Sending Files » + :type cover: :obj:`str` or :class:`telebot.types.InputFile` + + :param start_timestamp: Start timestamp for the video in the message + :type start_timestamp: :obj:`int` + :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -6839,7 +6847,8 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, caption_entities: Optional[List[MessageEntity]] = None, width: Optional[int] = None, height: Optional[int] = None, duration: Optional[int] = None, supports_streaming: Optional[bool] = None, has_spoiler: Optional[bool] = None, - show_caption_above_media: Optional[bool] = None): + show_caption_above_media: Optional[bool] = None, cover: Optional[Union[str, InputFile]] = None, + start_timestamp: Optional[int] = None): super(InputMediaVideo, self).__init__( type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) self.thumbnail: Optional[Union[str, InputFile]] = thumbnail @@ -6849,6 +6858,8 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, self.supports_streaming: Optional[bool] = supports_streaming self.has_spoiler: Optional[bool] = has_spoiler self.show_caption_above_media: Optional[bool] = show_caption_above_media + self.cover: Optional[str] = cover + self.start_timestamp: Optional[int] = start_timestamp @property def thumb(self) -> Optional[Union[str, Any]]: @@ -6871,6 +6882,10 @@ def to_dict(self): ret['has_spoiler'] = self.has_spoiler if self.show_caption_above_media is not None: ret['show_caption_above_media'] = self.show_caption_above_media + if self.cover: + ret['cover'] = self.cover + if self.start_timestamp: + ret['start_timestamp'] = self.start_timestamp return ret @@ -10899,6 +10914,14 @@ class InputPaidMediaVideo(InputPaidMedia): More information on Sending Files » :type thumbnail: :class:`InputFile` + :param cover: Cover for the video in the message. Pass a file_id to send a file that exists on the Telegram servers (recommended), + pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under + name. More information on Sending Files » + :type cover: :obj:`str` or :class:`telebot.types.InputFile` + + :param start_timestamp: Start timestamp for the video in the message + :type start_timestamp: :obj:`int` + :param width: Optional. Video width :type width: :obj:`int` @@ -10917,13 +10940,16 @@ class InputPaidMediaVideo(InputPaidMedia): """ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[InputFile] = None, width: Optional[int] = None, height: Optional[int] = None, duration: Optional[int] = None, supports_streaming: Optional[bool] = None, - **kwargs): + cover: Optional[Union[str,InputFile]] = None, start_timestamp: Optional[int] = None, **kwargs): super().__init__(type='video', media=media) self.thumbnail: Optional[Union[str,InputFile]] = thumbnail self.width: Optional[int] = width self.height: Optional[int] = height self.duration: Optional[int] = duration self.supports_streaming: Optional[bool] = supports_streaming + self.cover: Optional[Union[str,InputFile]] = cover + self.start_timestamp: Optional[int] = start_timestamp + def to_dict(self): @@ -10938,6 +10964,10 @@ def to_dict(self): data['duration'] = self.duration if self.supports_streaming is not None: data['supports_streaming'] = self.supports_streaming + if self.cover: + data['cover'] = self.cover + if self.start_timestamp: + data['start_timestamp'] = self.start_timestamp return data class RefundedPayment(JsonDeserializable): From 56d14dca89e7d72a8ecddda33a1363867558c50a Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:31:05 +0400 Subject: [PATCH 1732/1808] Added the parameter video_start_timestamp to the methods forwardMessage and copyMessage, allowing bots to change the start timestamp for forwarded and copied videos. --- telebot/__init__.py | 18 ++++++++++++++---- telebot/apihelper.py | 10 ++++++++-- telebot/async_telebot.py | 28 ++++++++++++++++++++-------- telebot/asyncio_helper.py | 8 ++++++-- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 3629f23a6..015997631 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1810,7 +1810,8 @@ def forward_message( message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + video_start_timestamp: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -1825,6 +1826,9 @@ def forward_message( :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) :type from_chat_id: :obj:`int` or :obj:`str` + :param video_start_timestamp: New start timestamp for the forwarded video in the message + :type video_start_timestamp: :obj:`int` + :param message_id: Message identifier in the chat specified in from_chat_id :type message_id: :obj:`int` @@ -1846,7 +1850,8 @@ def forward_message( return types.Message.de_json( apihelper.forward_message( self.token, chat_id, from_chat_id, message_id, disable_notification=disable_notification, - timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id)) + timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, + video_start_timestamp=video_start_timestamp)) def copy_message( @@ -1865,7 +1870,8 @@ def copy_message( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.MessageID: + allow_paid_broadcast: Optional[bool]=None, + video_start_timestamp: Optional[int]=None) -> types.MessageID: """ Use this method to copy messages of any kind. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. @@ -1883,6 +1889,9 @@ def copy_message( :param message_id: Message identifier in the chat specified in from_chat_id :type message_id: :obj:`int` + :param video_start_timestamp: New start timestamp for the copied video in the message + :type video_start_timestamp: :obj:`int` + :param caption: New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept :type caption: :obj:`str` @@ -1956,7 +1965,8 @@ def copy_message( 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, - show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, + video_start_timestamp=video_start_timestamp)) def delete_message(self, chat_id: Union[int, str], message_id: int, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9f7325a08..879dfdec5 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -424,7 +424,8 @@ def get_chat_member(token, chat_id, user_id): def forward_message( token, chat_id, from_chat_id, message_id, - disable_notification=None, timeout=None, protect_content=None, message_thread_id=None): + disable_notification=None, timeout=None, protect_content=None, message_thread_id=None, + video_start_timestamp=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -435,12 +436,15 @@ def forward_message( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if video_start_timestamp: + payload['video_start_timestamp'] = video_start_timestamp return _make_request(token, method_url, params=payload) def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, - reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None): + reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None, + video_start_timestamp=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -465,6 +469,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if video_start_timestamp: + payload['video_start_timestamp'] = video_start_timestamp return _make_request(token, method_url, params=payload) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 2022d37c4..3d9860054 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3337,7 +3337,8 @@ async def forward_message( message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, timeout: Optional[int]=None, - message_thread_id: Optional[int]=None) -> types.Message: + message_thread_id: Optional[int]=None, + video_start_timestamp: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -3355,6 +3356,9 @@ async def forward_message( :param message_id: Message identifier in the chat specified in from_chat_id :type message_id: :obj:`int` + :param video_start_timestamp: New start timestamp for the forwarded video in the message + :type video_start_timestamp: :obj:`int` + :param protect_content: Protects the contents of the forwarded message from forwarding and saving :type protect_content: :obj:`bool` @@ -3371,8 +3375,9 @@ async def forward_message( protect_content = self.protect_content if (protect_content is None) else protect_content return types.Message.de_json( - await asyncio_helper.forward_message(self.token, chat_id, from_chat_id, message_id, disable_notification, timeout, protect_content, - message_thread_id)) + await asyncio_helper.forward_message(self.token, chat_id=chat_id, from_chat_id=from_chat_id, message_id=message_id, + disable_notification=disable_notification, protect_content=protect_content, + timeout=timeout, message_thread_id=message_thread_id, video_start_timestamp=video_start_timestamp)) async def copy_message( self, chat_id: Union[int, str], @@ -3390,7 +3395,8 @@ async def copy_message( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.MessageID: + allow_paid_broadcast: Optional[bool]=None, + video_start_timestamp: Optional[bool]=None) -> types.MessageID: """ Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, @@ -3405,9 +3411,13 @@ async def copy_message( :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format @channelusername) :type from_chat_id: :obj:`int` or :obj:`str` + :param message_id: Message identifier in the chat specified in from_chat_id :type message_id: :obj:`int` + :param video_start_timestamp: New start timestamp for the forwarded video in the message + :type video_start_timestamp: :obj:`int` + :param caption: New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept :type caption: :obj:`str` @@ -3478,10 +3488,12 @@ async def copy_message( reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply return types.MessageID.de_json( - await asyncio_helper.copy_message(self.token, chat_id, from_chat_id, message_id, caption, parse_mode, caption_entities, - disable_notification, reply_markup, - timeout, protect_content, message_thread_id, reply_parameters, show_caption_above_media=show_caption_above_media, - allow_paid_broadcast=allow_paid_broadcast)) + await asyncio_helper.copy_message(self.token, chat_id=chat_id, from_chat_id=from_chat_id, message_id=message_id, + caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, + disable_notification=disable_notification, protect_content=protect_content, + reply_parameters=reply_parameters, reply_markup=reply_markup, timeout=timeout, + message_thread_id=message_thread_id, show_caption_above_media=show_caption_above_media, + allow_paid_broadcast=allow_paid_broadcast, video_start_timestamp=video_start_timestamp)) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 360138bd8..99937ff63 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -429,7 +429,7 @@ async def get_chat_member(token, chat_id, user_id): async def forward_message( token, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None, protect_content=None, - message_thread_id=None): + message_thread_id=None, video_start_timestamp=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -440,13 +440,15 @@ async def forward_message( payload['protect_content'] = protect_content if message_thread_id: payload['message_thread_id'] = message_thread_id + if video_start_timestamp: + payload['video_start_timestamp'] = video_start_timestamp return await _process_request(token, method_url, params=payload) async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, video_start_timestamp=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -471,6 +473,8 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if video_start_timestamp: + payload['video_start_timestamp'] = video_start_timestamp return await _process_request(token, method_url, params=payload) From d5c28da62506a033b5ee72685b47a1067e72a8e2 Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 13 Feb 2025 18:31:52 +0400 Subject: [PATCH 1733/1808] Update Bot API Version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff1b5159f..7431dc001 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From 7d82d8f835e1bf05831b97378274fbbc644a543d Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 15 Feb 2025 16:27:47 +0400 Subject: [PATCH 1734/1808] Ensure backward compatibility for send_gift --- telebot/__init__.py | 8 ++++++-- telebot/async_telebot.py | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 015997631..2ac86cf06 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6273,8 +6273,9 @@ def delete_sticker_set(self, name:str) -> bool: """ return apihelper.delete_sticker_set(self.token, name) - def send_gift(self, gift_id: str, user_id: Optional[Union[str, int]] = None, chat_id: Optional[Union[str, int]] = None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, - text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool: + def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, + text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None, + chat_id: Optional[Union[str, int]] = None) -> bool: """ Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. @@ -6308,6 +6309,9 @@ def send_gift(self, gift_id: str, user_id: Optional[Union[str, int]] = None, cha if user_id is None and chat_id is None: raise ValueError("Either user_id or chat_id must be specified.") + if gift_id is None: + raise ValueError("gift_id must be specified.") + return apihelper.send_gift(self.token, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities, pay_for_upgrade=pay_for_upgrade, chat_id=chat_id, user_id=user_id) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 3d9860054..bc251220a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7729,9 +7729,9 @@ async def delete_sticker_set(self, name:str) -> bool: return await asyncio_helper.delete_sticker_set(self.token, name) - async def send_gift(self, gift_id: str, user_id: Optional[Union[str, int]] = None, chat_id: Optional[Union[str, int]] = None, + async def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None, - pay_for_upgrade: Optional[bool]=None) -> bool: + pay_for_upgrade: Optional[bool]=None, chat_id: Optional[Union[str, int]] = None) -> bool: """ Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. @@ -7765,6 +7765,9 @@ async def send_gift(self, gift_id: str, user_id: Optional[Union[str, int]] = Non if user_id is None and chat_id is None: raise ValueError("Either user_id or chat_id must be specified.") + if gift_id is None: + raise ValueError("gift_id must be specified.") + return await asyncio_helper.send_gift(self.token, gift_id, text, text_parse_mode, text_entities, pay_for_upgrade=pay_for_upgrade, chat_id=chat_id, user_id=user_id) From fb0e9b32fec3898a9bac50daaba9bdb9f6426ce8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 03:21:16 +0000 Subject: [PATCH 1735/1808] Bump gunicorn in /examples/serverless/flask_google_cloud_bot Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 22.0.0 to 23.0.0. - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/22.0.0...23.0.0) --- updated-dependencies: - dependency-name: gunicorn dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- examples/serverless/flask_google_cloud_bot/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/serverless/flask_google_cloud_bot/requirements.txt b/examples/serverless/flask_google_cloud_bot/requirements.txt index e08389a50..a9c951e71 100644 --- a/examples/serverless/flask_google_cloud_bot/requirements.txt +++ b/examples/serverless/flask_google_cloud_bot/requirements.txt @@ -1,4 +1,4 @@ pyTelegramBotAPI==4.11.0 Flask==3.0.0 -gunicorn==22.0.0 +gunicorn==23.0.0 Werkzeug==3.0.6 From 19bf13815582140e25ca7ff540c839e282fc41db Mon Sep 17 00:00:00 2001 From: pplulee Date: Sat, 5 Apr 2025 23:22:22 +0800 Subject: [PATCH 1736/1808] Add support for processing language parameter in message entities --- telebot/formatting.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index 801a67ec1..aa24c2007 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -418,13 +418,16 @@ def apply_html_entities(text: str, entities: Optional[List], custom_subs: Option utf16_text = text.encode("utf-16-le") html_text = "" - def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): + def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None, language=None): upd_text = upd_text.decode("utf-16-le") if subst_type == "text_mention": subst_type = "text_link" url = "tg://user?id={0}".format(user.id) elif subst_type == "mention": url = "https://t.me/{0}".format(upd_text[1:]) + elif subst_type == "pre": + if language is not None: + return '
{1}
'.format(language, upd_text) upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") if not subst_type or not _subs.get(subst_type): return upd_text @@ -444,14 +447,16 @@ def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): offset = entity.offset new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, - url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id, + language=entity.language) start_index = len(html_text) html_text += new_string offset += entity.length end_index = len(html_text) elif entity.offset == offset: new_string = func(utf16_text[offset * 2: (offset + entity.length) * 2], subst_type=entity.type, - url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id) + url=entity.url, user=entity.user, custom_emoji_id=entity.custom_emoji_id, + language=entity.language) start_index = len(html_text) html_text += new_string end_index = len(html_text) @@ -463,7 +468,8 @@ def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None): # And we don't change it). entity_string = html_text[start_index: end_index].encode("utf-16-le") formatted_string = func(entity_string, subst_type=entity.type, url=entity.url, user=entity.user, - custom_emoji_id=entity.custom_emoji_id). \ + custom_emoji_id=entity.custom_emoji_id, + language=entity.language). \ replace("&", "&").replace("<", "<").replace(">", ">") html_text = html_text[:start_index] + formatted_string + html_text[end_index:] end_index = len(html_text) From e270371d0277af3a1b197826bc5a07f3035bd42e Mon Sep 17 00:00:00 2001 From: pplulee Date: Sun, 6 Apr 2025 12:11:06 +0800 Subject: [PATCH 1737/1808] Add test for HTML parsing --- tests/test_telebot.py | 80 +++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 8b04ac402..4c364d952 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -44,11 +44,11 @@ def test_message_listener(self): def listener(messages): assert len(messages) == 100 - tb = telebot.TeleBot('') + tb = telebot.TeleBot(TOKEN) tb.set_update_listener(listener) def test_message_handler(self): - tb = telebot.TeleBot('') + tb = telebot.TeleBot(TOKEN) msg = self.create_text_message('/help') @tb.message_handler(commands=['help', 'start']) @@ -60,7 +60,7 @@ def command_handler(message): assert msg.text == 'got' def test_message_handler_reg(self): - bot = telebot.TeleBot('') + bot = telebot.TeleBot(TOKEN) msg = self.create_text_message(r'https://web.telegram.org/') # noinspection PyUnusedLocal @@ -73,7 +73,7 @@ def command_url(message): assert msg.text == 'got' def test_message_handler_lambda(self): - bot = telebot.TeleBot('') + bot = telebot.TeleBot(TOKEN) msg = self.create_text_message(r'lambda_text') # noinspection PyUnusedLocal @@ -86,7 +86,7 @@ def command_url(message): assert msg.text == 'got' def test_message_handler_lambda_fail(self): - bot = telebot.TeleBot('') + bot = telebot.TeleBot(TOKEN) msg = self.create_text_message(r'text') # noinspection PyUnusedLocal @@ -99,7 +99,7 @@ def command_url(message): assert not msg.text == 'got' def test_message_handler_reg_fail(self): - bot = telebot.TeleBot('') + bot = telebot.TeleBot(TOKEN) msg = self.create_text_message(r'web.telegram.org/') # noinspection PyUnusedLocal @@ -147,6 +147,7 @@ def test_send_file_with_filename(self): ret_msg = tb.send_document(CHAT_ID, file_data) assert ret_msg.message_id + file_data.seek(0) ret_msg = tb.send_document(CHAT_ID, file_data, visible_file_name="test.jpg") assert ret_msg.message_id @@ -529,28 +530,31 @@ def create_message_update(text): params = {'text': text} chat = types.User(11, False, 'test') message = types.Message(1, None, None, chat, 'text', params, "") - edited_message = None - channel_post = None - edited_channel_post = None - inline_query = None - chosen_inline_result = None - callback_query = None - shipping_query = None - pre_checkout_query = None - poll = None - poll_answer = None - my_chat_member = None - chat_member = None - chat_join_request = None - message_reaction = None - message_reaction_count = None - chat_boost = None - chat_boost_removed = None - purchased_paid_media = None - return types.Update(-1001234038283, 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, chat_boost, chat_boost_removed, - purchased_paid_media) + return types.Update( + update_id=-1001234038283, + message=message, + edited_message=None, + channel_post=None, + edited_channel_post=None, + inline_query=None, + chosen_inline_result=None, + callback_query=None, + shipping_query=None, + pre_checkout_query=None, + poll=None, + poll_answer=None, + my_chat_member=None, + chat_member=None, + chat_join_request=None, + message_reaction=None, + message_reaction_count=None, + chat_boost=None, + removed_chat_boost=None, + purchased_paid_media=None, + business_message=None, + business_connection=None, + edited_business_message=None, + deleted_business_messages=None, ) def test_is_string_unicode(self): s1 = u'string' @@ -657,7 +661,7 @@ def test_typed_middleware_handler(self): apihelper.ENABLE_MIDDLEWARE = True - tb = telebot.TeleBot('') + tb = telebot.TeleBot(TOKEN) update = self.create_message_update('/help') # noinspection PyUnusedLocal @@ -678,7 +682,7 @@ def test_default_middleware_handler(self): apihelper.ENABLE_MIDDLEWARE = True - tb = telebot.TeleBot('') + tb = telebot.TeleBot(TOKEN) update = self.create_message_update('/help') # noinspection PyUnusedLocal @@ -703,3 +707,19 @@ def test_chat_permissions(self): #tb = telebot.TeleBot(TOKEN) #permissions = types.ChatPermissions(can_send_messages=True, can_send_polls=False) #msg = tb.set_chat_permissions(CHAT_ID, permissions) + + def test_apply_html_entities(self): + text = { + "*bold*": "bold", + "__italic__": "italic", + "~strikethrough~": "strikethrough", + "`inline code`": "inline code", + "```\ncode block```": "
code block
", + "```python\nprint('Hello, world!')\n```": "
print('Hello, world!')
", + "[link](http://example.com/)": "link", + ">blockquote": "
blockquote
", + } + tb = telebot.TeleBot(TOKEN) + for key, value in text.items(): + ret_msg = tb.send_message(CHAT_ID, text=key, parse_mode='MarkdownV2') + assert telebot.formatting.apply_html_entities(ret_msg.text, ret_msg.entities, None) == value From d25dae870fc88bad9ff9e62a90587b07bed45eb0 Mon Sep 17 00:00:00 2001 From: pplulee Date: Sun, 6 Apr 2025 22:03:01 +0800 Subject: [PATCH 1738/1808] Fix special character conversion --- telebot/formatting.py | 5 ++--- tests/test_telebot.py | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/formatting.py b/telebot/formatting.py index aa24c2007..0e300f7c1 100644 --- a/telebot/formatting.py +++ b/telebot/formatting.py @@ -425,15 +425,14 @@ def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None, l url = "tg://user?id={0}".format(user.id) elif subst_type == "mention": url = "https://t.me/{0}".format(upd_text[1:]) - elif subst_type == "pre": - if language is not None: - return '
{1}
'.format(language, upd_text) upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") if not subst_type or not _subs.get(subst_type): return upd_text subs = _subs.get(subst_type) if subst_type == "custom_emoji": return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id) + elif (subst_type == "pre") and language: + return "
{1}
".format(language, upd_text) return subs.format(text=upd_text, url=url) offset = 0 diff --git a/tests/test_telebot.py b/tests/test_telebot.py index 4c364d952..f395ff9c5 100644 --- a/tests/test_telebot.py +++ b/tests/test_telebot.py @@ -716,6 +716,7 @@ def test_apply_html_entities(self): "`inline code`": "inline code", "```\ncode block```": "
code block
", "```python\nprint('Hello, world!')\n```": "
print('Hello, world!')
", + "```python\nprint(1 < 2)\n```": "
print(1 < 2)
", "[link](http://example.com/)": "link", ">blockquote": "
blockquote
", } From 231d0d1294777442ff6130b38d24810a1d2d0b89 Mon Sep 17 00:00:00 2001 From: Timur Valiev Date: Wed, 9 Apr 2025 11:27:10 +0300 Subject: [PATCH 1739/1808] Issues 2458: missing `in` --- telebot/asyncio_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/asyncio_filters.py b/telebot/asyncio_filters.py index 77c0559a6..83aefefe3 100644 --- a/telebot/asyncio_filters.py +++ b/telebot/asyncio_filters.py @@ -372,7 +372,7 @@ async def check(self, message): """ if isinstance(message, types.CallbackQuery): result = await self._bot.get_chat_member(message.message.chat.id, message.from_user.id) - return result.status ('creator', 'administrator') + return result.status in ('creator', 'administrator') result = await self._bot.get_chat_member(message.chat.id, message.from_user.id) return result.status in ['creator', 'administrator'] From 350bc556da35d63c4620c315f19b4dbf0cd5c918 Mon Sep 17 00:00:00 2001 From: pplulee Date: Fri, 11 Apr 2025 22:40:24 +0900 Subject: [PATCH 1740/1808] Add BetterForward to bots list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7431dc001..a23b5c330 100644 --- a/README.md +++ b/README.md @@ -891,5 +891,6 @@ Here are some examples of template: * [4K YouTube Downloader](https://github.com/hansanaD/TelegramYTDLBot/) - Youtube Downloader with upto 4K resolution support. * [DrinkGenius-Bot](https://t.me/cocktail_recommendation_bot) ([source](https://github.com/Povladarchik/DrinkGenius-Bot)) by [Povladarchik](https://github.com/Povladarchik). Your personal assistant in the world of cocktails. * [Pytgpt-Bot](https://t.me/pytgpt_bot) ([source](https://github.com/Simatwa/pytgpt-bot)) by [Smartwa](https://github.com/Simatwa). AI powered bot for chatting, text-to-image and text-to-speech conversions. +* [BetterForward](https://github.com/SideCloudGroup/BetterForward) by [SideCloud](https://github.com/SideCloudGroup). Probably the most reliable Message Forwarding Bot in Telegram via the Topic feature. **Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.** From c448bba171f3e09a4e5901eba5b130fb360ebc94 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 12:18:38 +0400 Subject: [PATCH 1741/1808] Update types.py --- telebot/types.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index af0d4cf74..6b561505c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -9736,9 +9736,12 @@ class BusinessConnection(JsonDeserializable): :param date: Date the connection was established in Unix time :type date: :obj:`int` - :param can_reply: True, if the bot can act on behalf of the business account in chats that were active in the last 24 hours + :param can_reply: Deprecated, use :attr:`can_reply` instead. True, if the bot can reply to messages from the business account :type can_reply: :obj:`bool` + :param rights: Optional. Rights of the business bot + :type rights: :class:`BusinessBotRights` + :param is_enabled: True, if the connection is active :type is_enabled: :obj:`bool` @@ -9753,12 +9756,15 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) return cls(**obj) - def __init__(self, id, user, user_chat_id, date, can_reply, is_enabled, **kwargs): + def __init__(self, id, user, user_chat_id, date, can_reply, is_enabled, + rights=None, **kwargs): self.id: str = id self.user: User = user self.user_chat_id: int = user_chat_id self.date: int = date - self.can_reply: bool = can_reply + self.rights: Optional[BusinessBotRights] = rights + # Deprecated, use self.rights instead + self.can_reply: Optional[bool] = can_reply self.is_enabled: bool = is_enabled @@ -11272,3 +11278,80 @@ def de_json(cls, json_string): obj['gift'] = Gift.de_json(obj['gift']) return cls(**obj) + +class BusinessBotRights(JsonDeserializable): + """ + Represents the rights of a business bot. + + Telegram documentation: https://core.telegram.org/bots/api#businessbotrights + + :param can_reply: Optional. True, if the bot can send and edit messages in the private chats that had incoming messages in the last 24 hours + :type can_reply: :obj:`bool` + + :param can_read_messages: Optional. True, if the bot can mark incoming private messages as read + :type can_read_messages: :obj:`bool` + + :param can_delete_outgoing_messages: Optional. True, if the bot can delete messages sent by the bot + :type can_delete_outgoing_messages: :obj:`bool` + + :param can_delete_all_messages: Optional. True, if the bot can delete all private messages in managed chats + :type can_delete_all_messages: :obj:`bool` + + :param can_edit_name: Optional. True, if the bot can edit the first and last name of the business account + :type can_edit_name: :obj:`bool` + + :param can_edit_bio: Optional. True, if the bot can edit the bio of the business account + :type can_edit_bio: :obj:`bool` + + :param can_edit_profile_photo: Optional. True, if the bot can edit the profile photo of the business account + :type can_edit_profile_photo: :obj:`bool` + + :param can_edit_username: Optional. True, if the bot can edit the username of the business account + :type can_edit_username: :obj:`bool` + + :param can_change_gift_settings: Optional. True, if the bot can change the privacy settings pertaining to gifts for the business account + :type can_change_gift_settings: :obj:`bool` + + :param can_view_gifts_and_stars: Optional. True, if the bot can view gifts and the amount of Telegram Stars owned by the business account + :type can_view_gifts_and_stars: :obj:`bool` + + :param can_convert_gifts_to_stars: Optional. True, if the bot can convert regular gifts owned by the business account to Telegram Stars + :type can_convert_gifts_to_stars: :obj:`bool` + + :param can_transfer_and_upgrade_gifts: Optional. True, if the bot can transfer and upgrade gifts owned by the business account + :type can_transfer_and_upgrade_gifts: :obj:`bool` + + :param can_transfer_stars: Optional. True, if the bot can transfer Telegram Stars received by the business account to its own account, or use them to upgrade and transfer gifts + :type can_transfer_stars: :obj:`bool` + + :param can_manage_stories: Optional. True, if the bot can post, edit and delete stories on behalf of the business account + :type can_manage_stories: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`BusinessBotRights` + """ + def __init__(self, can_reply=None, can_read_messages=None, can_delete_outgoing_messages=None, can_delete_all_messages=None, + can_edit_name=None, can_edit_bio=None, can_edit_profile_photo=None, can_edit_username=None, + can_change_gift_settings=None, can_view_gifts_and_stars=None, can_convert_gifts_to_stars=None, + can_transfer_and_upgrade_gifts=None, can_transfer_stars=None, can_manage_stories=None, **kwargs): + self.can_reply: Optional[bool] = can_reply + self.can_read_messages: Optional[bool] = can_read_messages + self.can_delete_outgoing_messages: Optional[bool] = can_delete_outgoing_messages + self.can_delete_all_messages: Optional[bool] = can_delete_all_messages + self.can_edit_name: Optional[bool] = can_edit_name + self.can_edit_bio: Optional[bool] = can_edit_bio + self.can_edit_profile_photo: Optional[bool] = can_edit_profile_photo + self.can_edit_username: Optional[bool] = can_edit_username + self.can_change_gift_settings: Optional[bool] = can_change_gift_settings + self.can_view_gifts_and_stars: Optional[bool] = can_view_gifts_and_stars + self.can_convert_gifts_to_stars: Optional[bool] = can_convert_gifts_to_stars + self.can_transfer_and_upgrade_gifts: Optional[bool] = can_transfer_and_upgrade_gifts + self.can_transfer_stars: Optional[bool] = can_transfer_stars + self.can_manage_stories: Optional[bool] = can_manage_stories + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + From 805c78f7ae0482215c475f572cd3e7faf03f3b30 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 12:28:11 +0400 Subject: [PATCH 1742/1808] Add business account methods --- telebot/__init__.py | 92 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 33 ++++++++++++++ telebot/async_telebot.py | 92 +++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 33 ++++++++++++++ 4 files changed, 250 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 2ac86cf06..c1a99dacb 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6377,6 +6377,98 @@ def remove_chat_verification(self, chat_id: Union[int, str]) -> bool: :rtype: :obj:`bool` """ return apihelper.remove_chat_verification(self.token, chat_id) + + def read_business_message(self, business_connection_id: str, chat_id: Union[int, str], message_id: int) -> bool: + """ + Marks incoming message as read on behalf of a business account. Requires the can_read_messages business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#readbusinessmessage + + :param business_connection_id: Unique identifier of the business connection on behalf of which to read the message + :type business_connection_id: :obj:`str` + + :param chat_id: Unique identifier of the chat in which the message was received. The chat must have been active in the last 24 hours. + :type chat_id: :obj:`int` | :obj:`str` + + :param message_id: Unique identifier of the message to mark as read + :type message_id: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.read_business_message(self.token, business_connection_id, chat_id, message_id) + + def delete_business_messages(self, business_connection_id: str, message_ids: List[int]) -> bool: + """ + Delete messages on behalf of a business account. Requires the can_delete_outgoing_messages business bot right to delete messages sent by the bot itself, or the can_delete_all_messages business bot right to delete any message. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletebusinessmessages + + :param business_connection_id: Unique identifier of the business connection on behalf of which to delete the messages + :type business_connection_id: :obj:`str` + + :param message_ids: A JSON-serialized list of 1-100 identifiers of messages to delete. All messages must be from the same chat. See deleteMessage for limitations on which messages can be deleted + :type message_ids: :obj:`list` of :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.delete_business_messages(self.token, business_connection_id, message_ids) + + def set_business_account_name(self, business_connection_id: str, first_name: str, last_name: Optional[str]=None) -> bool: + """ + Changes the first and last name of a managed business account. Requires the can_change_name business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountname + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param first_name: The new value of the first name for the business account; 1-64 characters + :type first_name: :obj:`str` + + :param last_name: The new value of the last name for the business account; 0-64 characters + :type last_name: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.set_business_account_name(self.token, business_connection_id, first_name, last_name) + + def set_business_account_username(self, business_connection_id: str, username: Optional[str]=None) -> bool: + """ + Changes the username of a managed business account. Requires the can_change_username business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountusername + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param username: The new value of the username for the business account; 0-32 characters + :type username: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + + """ + return apihelper.set_business_account_username(self.token, business_connection_id, username) + + def set_business_account_bio(self, business_connection_id: str, bio: Optional[str]=None) -> bool: + """ + Changes the bio of a managed business account. Requires the can_change_bio business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountbio + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param bio: The new value of the bio for the business account; 0-140 characters + :type bio: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.set_business_account_bio(self.token, business_connection_id, bio) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 879dfdec5..2fcb6b729 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1992,6 +1992,39 @@ def remove_chat_verification(token, chat_id): payload = {'chat_id': chat_id} return _make_request(token, method_url, params=payload, method='post') + +def read_business_message(token, business_connection_id, chat_id, message_id): + method_url = 'readBusinessMessage' + payload = {'business_connection_id': business_connection_id, 'chat_id': chat_id, 'message_id': message_id} + return _make_request(token, method_url, params=payload, method='post') + + +def delete_business_messages(token, business_connection_id, message_ids): + method_url = 'deleteBusinessMessages' + payload = {'business_connection_id': business_connection_id, 'message_ids': json.dumps(message_ids)} + return _make_request(token, method_url, params=payload, method='post') + + +def set_business_account_name(token, business_connection_id, first_name, last_name=None): + method_url = 'setBusinessAccountName' + payload = {'business_connection_id': business_connection_id, 'first_name': first_name} + if last_name: + payload['last_name'] = last_name + return _make_request(token, method_url, params=payload, method='post') + + +def set_business_account_username(token, business_connection_id, username): + method_url = 'setBusinessAccountUsername' + payload = {'business_connection_id': business_connection_id, 'username': username} + return _make_request(token, method_url, params=payload, method='post') + + +def set_business_account_bio(token, business_connection_id, bio): + method_url = 'setBusinessAccountBio' + payload = {'business_connection_id': business_connection_id, 'bio': bio} + return _make_request(token, method_url, params=payload, method='post') + + def set_sticker_emoji_list(token, sticker, emoji_list): method_url = 'setStickerEmojiList' payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index bc251220a..e8815232a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7835,6 +7835,98 @@ async def remove_chat_verification(self, chat_id: Union[int, str]) -> bool: """ return await asyncio_helper.remove_chat_verification(self.token, chat_id) + async def read_business_message(self, business_connection_id: str, chat_id: Union[int, str], message_id: int) -> bool: + """ + Marks incoming message as read on behalf of a business account. Requires the can_read_messages business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#readbusinessmessage + + :param business_connection_id: Unique identifier of the business connection on behalf of which to read the message + :type business_connection_id: :obj:`str` + + :param chat_id: Unique identifier of the chat in which the message was received. The chat must have been active in the last 24 hours. + :type chat_id: :obj:`int` | :obj:`str` + + :param message_id: Unique identifier of the message to mark as read + :type message_id: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.read_business_message(self.token, business_connection_id, chat_id, message_id) + + async def delete_business_messages(self, business_connection_id: str, message_ids: List[int]) -> bool: + """ + Delete messages on behalf of a business account. Requires the can_delete_outgoing_messages business bot right to delete messages sent by the bot itself, or the can_delete_all_messages business bot right to delete any message. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletebusinessmessages + + :param business_connection_id: Unique identifier of the business connection on behalf of which to delete the messages + :type business_connection_id: :obj:`str` + + :param message_ids: A JSON-serialized list of 1-100 identifiers of messages to delete. All messages must be from the same chat. See deleteMessage for limitations on which messages can be deleted + :type message_ids: :obj:`list` of :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.delete_business_messages(self.token, business_connection_id, message_ids) + + async def set_business_account_name(self, business_connection_id: str, first_name: str, last_name: Optional[str]=None) -> bool: + """ + Changes the first and last name of a managed business account. Requires the can_change_name business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountname + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param first_name: The new value of the first name for the business account; 1-64 characters + :type first_name: :obj:`str` + + :param last_name: The new value of the last name for the business account; 0-64 characters + :type last_name: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.set_business_account_name(self.token, business_connection_id, first_name, last_name) + + async def set_business_account_username(self, business_connection_id: str, username: Optional[str]=None) -> bool: + """ + Changes the username of a managed business account. Requires the can_change_username business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountusername + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param username: The new value of the username for the business account; 0-32 characters + :type username: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + + """ + return await asyncio_helper.set_business_account_username(self.token, business_connection_id, username) + + async def set_business_account_bio(self, business_connection_id: str, bio: Optional[str]=None) -> bool: + """ + Changes the bio of a managed business account. Requires the can_change_bio business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountbio + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param bio: The new value of the bio for the business account; 0-140 characters + :type bio: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.set_business_account_bio(self.token, business_connection_id, bio) + async def get_available_gifts(self) -> types.Gifts: """ Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 99937ff63..c082509ae 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1972,6 +1972,39 @@ async def remove_chat_verification(token, chat_id): payload = {'chat_id': chat_id} return await _process_request(token, method_url, params=payload, method='post') + +async def read_business_message(token, business_connection_id, chat_id, message_id): + method_url = 'readBusinessMessage' + payload = {'business_connection_id': business_connection_id, 'chat_id': chat_id, 'message_id': message_id} + return await _process_request(token, method_url, params=payload, method='post') + + +async def delete_business_messages(token, business_connection_id, message_ids): + method_url = 'deleteBusinessMessages' + payload = {'business_connection_id': business_connection_id, 'message_ids': json.dumps(message_ids)} + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_business_account_name(token, business_connection_id, first_name, last_name=None): + method_url = 'setBusinessAccountName' + payload = {'business_connection_id': business_connection_id, 'first_name': first_name} + if last_name: + payload['last_name'] = last_name + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_business_account_username(token, business_connection_id, username): + method_url = 'setBusinessAccountUsername' + payload = {'business_connection_id': business_connection_id, 'username': username} + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_business_account_bio(token, business_connection_id, bio): + method_url = 'setBusinessAccountBio' + payload = {'business_connection_id': business_connection_id, 'bio': bio} + return await _process_request(token, method_url, params=payload, method='post') + + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) From 1d32718bd7e00ef53ae2ca32bd61c1e846df5768 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 13:11:38 +0400 Subject: [PATCH 1743/1808] Added AcceptedGiftTypes and set_business_account_gift_settings --- telebot/__init__.py | 22 ++++++++++++++++++ telebot/apihelper.py | 5 ++++ telebot/async_telebot.py | 22 ++++++++++++++++++ telebot/asyncio_helper.py | 5 ++++ telebot/types.py | 49 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index c1a99dacb..afb063588 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6469,6 +6469,28 @@ def set_business_account_bio(self, business_connection_id: str, bio: Optional[st :rtype: :obj:`bool` """ return apihelper.set_business_account_bio(self.token, business_connection_id, bio) + + def set_business_account_gift_settings( + self, business_connection_id: str, show_gift_button: bool, accepted_gift_types: types.AcceptedGiftTypes) -> bool: + """ + Changes the privacy settings pertaining to incoming gifts in a managed business account. Requires the can_change_gift_settings business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountgiftsettings + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param show_gift_button: Pass True, if a button for sending a gift to the user or by the business account must always be shown in the input field + :type show_gift_button: :obj:`bool` + + :param accepted_gift_types: Types of gifts accepted by the business account + :type accepted_gift_types: :class:`telebot.types.AcceptedGiftTypes` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.set_business_account_gift_settings(self.token, business_connection_id, show_gift_button, accepted_gift_types) + def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 2fcb6b729..979b5b7dc 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2025,6 +2025,11 @@ def set_business_account_bio(token, business_connection_id, bio): return _make_request(token, method_url, params=payload, method='post') +def set_business_account_gift_settings(token, business_connection_id, show_gift_button, accepted_gift_types): + method_url = 'setBusinessAccountGiftSettings' + payload = {'business_connection_id': business_connection_id, 'show_gift_button': show_gift_button, 'accepted_gift_types': json.dumps(accepted_gift_types)} + return _make_request(token, method_url, params=payload, method='post') + def set_sticker_emoji_list(token, sticker, emoji_list): method_url = 'setStickerEmojiList' payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index e8815232a..355d9791a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7927,6 +7927,27 @@ async def set_business_account_bio(self, business_connection_id: str, bio: Optio """ return await asyncio_helper.set_business_account_bio(self.token, business_connection_id, bio) + async def set_business_account_gift_settings( + self, business_connection_id: str, show_gift_button: bool, accepted_gift_types: types.AcceptedGiftTypes) -> bool: + """ + Changes the privacy settings pertaining to incoming gifts in a managed business account. Requires the can_change_gift_settings business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountgiftsettings + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param show_gift_button: Pass True, if a button for sending a gift to the user or by the business account must always be shown in the input field + :type show_gift_button: :obj:`bool` + + :param accepted_gift_types: Types of gifts accepted by the business account + :type accepted_gift_types: :class:`telebot.types.AcceptedGiftTypes` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.set_business_account_gift_settings(self.token, business_connection_id, show_gift_button, accepted_gift_types) + async def get_available_gifts(self) -> types.Gifts: """ Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. @@ -7938,6 +7959,7 @@ async def get_available_gifts(self) -> types.Gifts: """ return types.Gifts.de_json(await asyncio_helper.get_available_gifts(self.token)) + async def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index c082509ae..25d6658ee 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2004,6 +2004,11 @@ async def set_business_account_bio(token, business_connection_id, bio): payload = {'business_connection_id': business_connection_id, 'bio': bio} return await _process_request(token, method_url, params=payload, method='post') +async def set_business_account_gift_settings(token, business_connection_id, show_gift_button, accepted_gift_types): + method_url = 'setBusinessAccountGiftSettings' + payload = {'business_connection_id': business_connection_id, 'show_gift_button': show_gift_button, 'accepted_gift_types': json.dumps(accepted_gift_types)} + return await _process_request(token, method_url, params=payload, method='post') + async def get_available_gifts(token): method_url = 'getAvailableGifts' diff --git a/telebot/types.py b/telebot/types.py index 6b561505c..653d754e5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11354,4 +11354,53 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) + + +class AcceptedGiftTypes(JsonDeserializable, JsonSerializable): + """ + This object describes the types of gifts that can be gifted to a user or a chat. + + Telegram documentation: https://core.telegram.org/bots/api#acceptedgifttypes + + :param unlimited_gifts: True, if unlimited regular gifts are accepted + :type unlimited_gifts: :obj:`bool` + + :param limited_gifts: True, if limited regular gifts are accepted + :type limited_gifts: :obj:`bool` + + :param unique_gifts: True, if unique gifts or gifts that can be upgraded to unique for free are accepted + :type unique_gifts: :obj:`bool` + + :param premium_subscription: True, if a Telegram Premium subscription is accepted + :type premium_subscription: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`AcceptedGiftTypes` + """ + def __init__(self, unlimited_gifts: Optional[bool] = None, limited_gifts: Optional[bool] = None, + unique_gifts: Optional[bool] = None, premium_subscription: Optional[bool] = None, **kwargs): + self.unlimited_gifts: Optional[bool] = unlimited_gifts + self.limited_gifts: Optional[bool] = limited_gifts + self.unique_gifts: Optional[bool] = unique_gifts + self.premium_subscription: Optional[bool] = premium_subscription + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = {} + if self.unlimited_gifts is not None: + data['unlimited_gifts'] = self.unlimited_gifts + if self.limited_gifts is not None: + data['limited_gifts'] = self.limited_gifts + if self.unique_gifts is not None: + data['unique_gifts'] = self.unique_gifts + if self.premium_subscription is not None: + data['premium_subscription'] = self.premium_subscription + return data + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) From 3e0f06a1bd7f559430a77b3f306906c5c0f923e5 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 13:16:17 +0400 Subject: [PATCH 1744/1808] Add get_business_account_star_balance --- telebot/__init__.py | 18 +++++++++++++++++- telebot/apihelper.py | 4 ++++ telebot/async_telebot.py | 16 ++++++++++++++++ telebot/asyncio_helper.py | 5 +++++ telebot/types.py | 25 +++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index afb063588..c3b400f49 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6491,7 +6491,23 @@ def set_business_account_gift_settings( """ return apihelper.set_business_account_gift_settings(self.token, business_connection_id, show_gift_button, accepted_gift_types) - + + def get_business_account_star_balance(self, business_connection_id: str) -> types.StarAmount: + """ + Returns the amount of Telegram Stars owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns StarAmount on success. + + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountstarbalance + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :return: On success, a StarAmount object is returned. + :rtype: :class:`telebot.types.StarAmount` + """ + return types.StarAmount.de_json( + apihelper.get_business_account_star_balance(self.token, business_connection_id) + ) + def get_available_gifts(self) -> types.Gifts: """ Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 979b5b7dc..1b7335065 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2035,6 +2035,10 @@ def set_sticker_emoji_list(token, sticker, emoji_list): payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} return _make_request(token, method_url, params=payload, method='post') +def get_business_account_star_balance(token, business_connection_id): + method_url = 'getBusinessAccountStarBalance' + payload = {'business_connection_id': business_connection_id} + return _make_request(token, method_url, params=payload) def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 355d9791a..c60bc458f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7948,6 +7948,22 @@ async def set_business_account_gift_settings( """ return await asyncio_helper.set_business_account_gift_settings(self.token, business_connection_id, show_gift_button, accepted_gift_types) + async def get_business_account_star_balance(self, business_connection_id: str) -> types.StarAmount: + """ + Returns the amount of Telegram Stars owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns StarAmount on success. + + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountstarbalance + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :return: On success, a StarAmount object is returned. + :rtype: :class:`telebot.types.StarAmount` + """ + return types.StarAmount.de_json( + await asyncio_helper.get_business_account_star_balance(self.token, business_connection_id) + ) + async def get_available_gifts(self) -> types.Gifts: """ Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 25d6658ee..54733e254 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2009,6 +2009,11 @@ async def set_business_account_gift_settings(token, business_connection_id, show payload = {'business_connection_id': business_connection_id, 'show_gift_button': show_gift_button, 'accepted_gift_types': json.dumps(accepted_gift_types)} return await _process_request(token, method_url, params=payload, method='post') +async def get_business_account_star_balance(token, business_connection_id): + method_url = 'getBusinessAccountStarBalance' + payload = {'business_connection_id': business_connection_id} + return _process_request(token, method_url, params=payload) + async def get_available_gifts(token): method_url = 'getAvailableGifts' diff --git a/telebot/types.py b/telebot/types.py index 653d754e5..62107e91f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11403,4 +11403,29 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) + + +class StarAmount(JsonDeserializable): + """ + Describes an amount of Telegram Stars. + + Telegram documentation: https://core.telegram.org/bots/api#staramount + + :param amount: Integer amount of Telegram Stars, rounded to 0; can be negative + :type amount: :obj:`int` + + :param nanostar_amount: Optional. The number of 1/1000000000 shares of Telegram Stars; from -999999999 to 999999999; can be negative if and only if amount is non-positive + :type nanostar_amount: :obj:`int` + + :return: Instance of the class + :rtype: :class:`StarAmount` + """ + def __init__(self, amount, nanostar_amount=None, **kwargs): + self.amount: int = amount + self.nanostar_amount: Optional[int] = nanostar_amount + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) From af858de87f4f9067ad012edb57da8df13ce6c260 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 13:20:01 +0400 Subject: [PATCH 1745/1808] Added transfer_business_account_stars --- README.md | 2 +- telebot/__init__.py | 17 +++++++++++++++++ telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 18 ++++++++++++++++++ telebot/asyncio_helper.py | 4 ++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a23b5c330..262fc2beb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

diff --git a/telebot/__init__.py b/telebot/__init__.py index c3b400f49..241fc2fe3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6507,6 +6507,23 @@ def get_business_account_star_balance(self, business_connection_id: str) -> type return types.StarAmount.de_json( apihelper.get_business_account_star_balance(self.token, business_connection_id) ) + + def transfer_business_account_stars(self, business_connection_id: str, star_count: int) -> bool: + """ + Transfers Telegram Stars from the business account balance to the bot's balance. Requires the can_transfer_stars business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#transferbusinessaccountstars + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param star_count: Number of Telegram Stars to transfer; 1-10000 + :type star_count: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.transfer_business_account_stars(self.token, business_connection_id, star_count) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 1b7335065..b413f5b6e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2040,6 +2040,11 @@ def get_business_account_star_balance(token, business_connection_id): payload = {'business_connection_id': business_connection_id} return _make_request(token, method_url, params=payload) +def transfer_business_account_stars(token, business_connection_id, star_count): + method_url = 'transferBusinessAccountStars' + payload = {'business_connection_id': business_connection_id, 'star_count': star_count} + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index c60bc458f..845ab6028 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7963,6 +7963,24 @@ async def get_business_account_star_balance(self, business_connection_id: str) - return types.StarAmount.de_json( await asyncio_helper.get_business_account_star_balance(self.token, business_connection_id) ) + + async def transfer_business_account_stars(self, business_connection_id: str, star_count: int) -> bool: + """ + Transfers Telegram Stars from the business account balance to the bot's balance. Requires the can_transfer_stars business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#transferbusinessaccountstars + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param star_count: Number of Telegram Stars to transfer; 1-10000 + :type star_count: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.transfer_business_account_stars(self.token, business_connection_id, star_count) + async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 54733e254..0bd0559bc 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2014,6 +2014,10 @@ async def get_business_account_star_balance(token, business_connection_id): payload = {'business_connection_id': business_connection_id} return _process_request(token, method_url, params=payload) +async def transfer_business_account_stars(token, business_connection_id, star_count): + method_url = 'transferBusinessAccountStars' + payload = {'business_connection_id': business_connection_id, 'star_count': star_count} + return await _process_request(token, method_url, params=payload, method='post') async def get_available_gifts(token): method_url = 'getAvailableGifts' From c012a59086110a4a7e64f22f611fc4040452c35a Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 13:48:50 +0400 Subject: [PATCH 1746/1808] Added get_business_account_gifts, OwnedGiftRegular, OwnedGiftUnique, OwnedGifts, UniqueGift, OwnedGift, UniqueGiftModel, UniqueGiftSymbol, UniqueGiftBackdrop, UniqueGiftBackdropColors --- telebot/__init__.py | 59 +++++++ telebot/apihelper.py | 23 +++ telebot/async_telebot.py | 58 +++++++ telebot/asyncio_helper.py | 23 +++ telebot/types.py | 347 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 510 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 241fc2fe3..c47d99919 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6524,6 +6524,65 @@ def transfer_business_account_stars(self, business_connection_id: str, star_coun :rtype: :obj:`bool` """ return apihelper.transfer_business_account_stars(self.token, business_connection_id, star_count) + + def get_business_account_gifts( + self, business_connection_id: str, + exclude_unsaved: Optional[bool]=None, + exclude_saved: Optional[bool]=None, + exclude_unlimited: Optional[bool]=None, + exclude_limited: Optional[bool]=None, + exclude_unique: Optional[bool]=None, + sort_by_price: Optional[bool]=None, + offset: Optional[str]=None, + limit: Optional[int]=None) -> types.OwnedGifts: + """ + Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. + + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountgifts + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param exclude_unsaved: Pass True to exclude gifts that aren't saved to the account's profile page + :type exclude_unsaved: :obj:`bool` + + :param exclude_saved: Pass True to exclude gifts that are saved to the account's profile page + :type exclude_saved: :obj:`bool` + + :param exclude_unlimited: Pass True to exclude gifts that can be purchased an unlimited number of times + :type exclude_unlimited: :obj:`bool` + + :param exclude_limited: Pass True to exclude gifts that can be purchased a limited number of times + :type exclude_limited: :obj:`bool` + + :param exclude_unique: Pass True to exclude unique gifts + :type exclude_unique: :obj:`bool` + + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. + :type sort_by_price: :obj:`bool` + + :param offset: Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results + :type offset: :obj:`str` + + :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 + :type limit: :obj:`int` + + :return: On success, a OwnedGifts object is returned. + :rtype: :class:`telebot.types.OwnedGifts` + """ + return types.OwnedGifts.de_json( + apihelper.get_business_account_gifts( + self.token, business_connection_id, + exclude_unsaved=exclude_unsaved, + exclude_saved=exclude_saved, + exclude_unlimited=exclude_unlimited, + exclude_limited=exclude_limited, + exclude_unique=exclude_unique, + sort_by_price=sort_by_price, + offset=offset, + limit=limit + ) + ) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b413f5b6e..f4f9545be 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2045,6 +2045,29 @@ def transfer_business_account_stars(token, business_connection_id, star_count): payload = {'business_connection_id': business_connection_id, 'star_count': star_count} return _make_request(token, method_url, params=payload, method='post') +def get_business_account_gifts(token, business_connection_id, exclude_unsaved=None, exclude_saved=None, + exclude_unlimited=None, exclude_limited=None, exclude_unique=None, + sort_by_price=None, offset=None, limit=None): + method_url = 'getBusinessAccountGifts' + payload = {'business_connection_id': business_connection_id} + if exclude_unsaved is not None: + payload['exclude_unsaved'] = exclude_unsaved + if exclude_saved is not None: + payload['exclude_saved'] = exclude_saved + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_limited is not None: + payload['exclude_limited'] = exclude_limited + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + return _make_request(token, method_url, params=payload) + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 845ab6028..7ce6c03e7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7980,7 +7980,65 @@ async def transfer_business_account_stars(self, business_connection_id: str, sta :rtype: :obj:`bool` """ return await asyncio_helper.transfer_business_account_stars(self.token, business_connection_id, star_count) + + async def get_business_account_gifts( + self, business_connection_id: str, + exclude_unsaved: Optional[bool]=None, + exclude_saved: Optional[bool]=None, + exclude_unlimited: Optional[bool]=None, + exclude_limited: Optional[bool]=None, + exclude_unique: Optional[bool]=None, + sort_by_price: Optional[bool]=None, + offset: Optional[str]=None, + limit: Optional[int]=None) -> types.OwnedGifts: + """ + Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. + + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountgifts + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param exclude_unsaved: Pass True to exclude gifts that aren't saved to the account's profile page + :type exclude_unsaved: :obj:`bool` + + :param exclude_saved: Pass True to exclude gifts that are saved to the account's profile page + :type exclude_saved: :obj:`bool` + + :param exclude_unlimited: Pass True to exclude gifts that can be purchased an unlimited number of times + :type exclude_unlimited: :obj:`bool` + :param exclude_limited: Pass True to exclude gifts that can be purchased a limited number of times + :type exclude_limited: :obj:`bool` + + :param exclude_unique: Pass True to exclude unique gifts + :type exclude_unique: :obj:`bool` + + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. + :type sort_by_price: :obj:`bool` + + :param offset: Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results + :type offset: :obj:`str` + + :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 + :type limit: :obj:`int` + + :return: On success, a OwnedGifts object is returned. + :rtype: :class:`telebot.types.OwnedGifts` + """ + return types.OwnedGifts.de_json( + await asyncio_helper.get_business_account_gifts( + self.token, business_connection_id, + exclude_unsaved=exclude_unsaved, + exclude_saved=exclude_saved, + exclude_unlimited=exclude_unlimited, + exclude_limited=exclude_limited, + exclude_unique=exclude_unique, + sort_by_price=sort_by_price, + offset=offset, + limit=limit + ) + ) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0bd0559bc..3c587d7bd 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2019,6 +2019,29 @@ async def transfer_business_account_stars(token, business_connection_id, star_co payload = {'business_connection_id': business_connection_id, 'star_count': star_count} return await _process_request(token, method_url, params=payload, method='post') +async def get_business_account_gifts(token, business_connection_id, exclude_unsaved=None, exclude_saved=None, + exclude_unlimited=None, exclude_limited=None, exclude_unique=None, + sort_by_price=None, offset=None, limit=None): + method_url = 'getBusinessAccountGifts' + payload = {'business_connection_id': business_connection_id} + if exclude_unsaved is not None: + payload['exclude_unsaved'] = exclude_unsaved + if exclude_saved is not None: + payload['exclude_saved'] = exclude_saved + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_limited is not None: + payload['exclude_limited'] = exclude_limited + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + return await _process_request(token, method_url, params=payload) + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) diff --git a/telebot/types.py b/telebot/types.py index 62107e91f..8b1fc3a39 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11429,3 +11429,350 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) + +class OwnedGift(JsonDeserializable): + """ + This object describes a gift received and owned by a user or a chat. Currently, it can be one of + OwnedGiftRegular + OwnedGiftUnique + + Telegram documentation: https://core.telegram.org/bots/api#ownedgift + """ + + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if obj["type"] == "regular": + return OwnedGiftRegular.de_json(obj) + elif obj["type"] == "unique": + return OwnedGiftUnique.de_json(obj) + +class OwnedGiftRegular(OwnedGift): + """ + This object describes a regular gift owned by a user or a chat. + + Telegram documentation: https://core.telegram.org/bots/api#ownedgiftregular + + :param type: Type of the gift, always “regular” + :type type: :obj:`str` + + :param gift: Information about the regular gift + :type gift: :class:`Gift` + + :param owned_gift_id: Optional. Unique identifier of the gift for the bot; for gifts received on behalf of business accounts only + :type owned_gift_id: :obj:`str` + + :param sender_user: Optional. Sender of the gift if it is a known user + :type sender_user: :class:`User` + + :param send_date: Date the gift was sent in Unix time + :type send_date: :obj:`int` + + :param text: Optional. Text of the message that was added to the gift + :type text: :obj:`str` + + :param entities: Optional. Special entities that appear in the text + :type entities: :obj:`list` of :class:`MessageEntity` + + :param is_private: Optional. True, if the sender and gift text are shown only to the gift receiver; otherwise, everyone will be able to see them + :type is_private: :obj:`bool` + + :param is_saved: Optional. True, if the gift is displayed on the account's profile page; for gifts received on behalf of business accounts only + :type is_saved: :obj:`bool` + + :param can_be_upgraded: Optional. True, if the gift can be upgraded to a unique gift; for gifts received on behalf of business accounts only + :type can_be_upgraded: :obj:`bool` + + :param was_refunded: Optional. True, if the gift was refunded and isn't available anymore + :type was_refunded: :obj:`bool` + + :param convert_star_count: Optional. Number of Telegram Stars that can be claimed by the receiver instead of the gift; omitted if the gift cannot be converted to Telegram Stars + :type convert_star_count: :obj:`int` + + :param prepaid_upgrade_star_count: Optional. Number of Telegram Stars that were paid by the sender for the ability to upgrade the gift + :type prepaid_upgrade_star_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`OwnedGiftRegular` + """ + def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=None, text=None, entities=None, + is_private=None, is_saved=None, can_be_upgraded=None, was_refunded=None, convert_star_count=None, + prepaid_upgrade_star_count=None, **kwargs): + self.type: str = type + self.gift: Gift = gift + self.owned_gift_id: Optional[str] = owned_gift_id + self.sender_user: Optional[User] = sender_user + self.send_date: Optional[int] = send_date + self.text: Optional[str] = text + self.entities: Optional[List[MessageEntity]] = entities + self.is_private: Optional[bool] = is_private + self.is_saved: Optional[bool] = is_saved + self.can_be_upgraded: Optional[bool] = can_be_upgraded + self.was_refunded: Optional[bool] = was_refunded + self.convert_star_count: Optional[int] = convert_star_count + self.prepaid_upgrade_star_count: Optional[int] = prepaid_upgrade_star_count + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['gift'] = Gift.de_json(obj['gift']) + if 'sender_user' in obj: + obj['sender_user'] = User.de_json(obj['sender_user']) + if 'entities' in obj: + obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] + return cls(**obj) + +class OwnedGiftUnique(OwnedGift): + """ + This object describes a unique gift owned by a user or a chat. + + Telegram documentation: https://core.telegram.org/bots/api#ownedgiftunique + + :param type: Type of the gift, always “unique” + :type type: :obj:`str` + + :param gift: Information about the unique gift + :type gift: :class:`UniqueGift` + + :param owned_gift_id: Optional. Unique identifier of the received gift for the bot; for gifts received on behalf of business accounts only + :type owned_gift_id: :obj:`str` + + :param sender_user: Optional. Sender of the gift if it is a known user + :type sender_user: :class:`User` + + :param send_date: Date the gift was sent in Unix time + :type send_date: :obj:`int` + + :param is_saved: Optional. True, if the gift is displayed on the account's profile page; for gifts received on behalf of business accounts only + :type is_saved: :obj:`bool` + + :param can_be_transferred: Optional. True, if the gift can be transferred to another owner; for gifts received on behalf of business accounts only + :type can_be_transferred: :obj:`bool` + + :param transfer_star_count: Optional. Number of Telegram Stars that must be paid to transfer the gift; omitted if the bot cannot transfer the gift + :type transfer_star_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`OwnedGiftUnique` + """ + def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=None, is_saved=None, + can_be_transferred=None, transfer_star_count=None, **kwargs): + self.type: str = type + self.gift: UniqueGift = gift + self.owned_gift_id: Optional[str] = owned_gift_id + self.sender_user: Optional[User] = sender_user + self.send_date: Optional[int] = send_date + self.is_saved: Optional[bool] = is_saved + self.can_be_transferred: Optional[bool] = can_be_transferred + self.transfer_star_count: Optional[int] = transfer_star_count + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['gift'] = UniqueGift.de_json(obj['gift']) + if 'sender_user' in obj: + obj['sender_user'] = User.de_json(obj['sender_user']) + return cls(**obj) + + +class OwnedGifts(JsonDeserializable): + """ + Contains the list of gifts received and owned by a user or a chat. + + Telegram documentation: https://core.telegram.org/bots/api#ownedgifts + + :param total_count: The total number of gifts owned by the user or the chat + :type total_count: :obj:`int` + + :param gifts: The list of gifts + :type gifts: :obj:`list` of :class:`OwnedGift` + + :param next_offset: Optional. Offset for the next request. If empty, then there are no more results + :type next_offset: :obj:`str` + + :return: Instance of the class + :rtype: :class:`OwnedGifts` + + """ + def __init__(self, total_count, gifts, next_offset=None, **kwargs): + self.total_count: int = total_count + self.gifts: List[OwnedGift] = gifts + self.next_offset: Optional[str] = next_offset + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['gifts'] = [OwnedGift.de_json(gift) for gift in obj['gifts']] + return cls(**obj) + + + +class UniqueGift(JsonDeserializable): + """ + This object describes a unique gift that was upgraded from a regular gift. + + Telegram documentation: https://core.telegram.org/bots/api#uniquegift + + :param base_name: Human-readable name of the regular gift from which this unique gift was upgraded + :type base_name: :obj:`str` + + :param name: Unique name of the gift. This name can be used in https://t.me/nft/... links and story areas + :type name: :obj:`str` + + :param number: Unique number of the upgraded gift among gifts upgraded from the same regular gift + :type number: :obj:`int` + + :param model: Model of the gift + :type model: :class:`UniqueGiftModel` + + :param symbol: Symbol of the gift + :type symbol: :class:`UniqueGiftSymbol` + + :param backdrop: Backdrop of the gift + :type backdrop: :class:`UniqueGiftBackdrop` + + :return: Instance of the class + :rtype: :class:`UniqueGift` + """ + def __init__(self, base_name, name, number, model, symbol, backdrop, **kwargs): + self.base_name: str = base_name + self.name: str = name + self.number: int = number + self.model: UniqueGiftModel = model + self.symbol: UniqueGiftSymbol = symbol + self.backdrop: UniqueGiftBackdrop = backdrop + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['model'] = UniqueGiftModel.de_json(obj['model']) + obj['symbol'] = UniqueGiftSymbol.de_json(obj['symbol']) + obj['backdrop'] = UniqueGiftBackdrop.de_json(obj['backdrop']) + return cls(**obj) + + +class UniqueGiftModel(JsonDeserializable): + """ + This object describes the model of a unique gift. + + Telegram documentation: https://core.telegram.org/bots/api#uniquegiftmodel + + :param name: Name of the model + :type name: :obj:`str` + + :param sticker: The sticker that represents the unique gift + :type sticker: :class:`Sticker` + + :param rarity_per_mille: The number of unique gifts that receive this model for every 1000 gifts upgraded + :type rarity_per_mille: :obj:`int` + + :return: Instance of the class + :rtype: :class:`UniqueGiftModel` + + """ + def __init__(self, name, sticker, rarity_per_mille, **kwargs): + self.name: str = name + self.sticker: Sticker = sticker + self.rarity_per_mille: int = rarity_per_mille + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['sticker'] = Sticker.de_json(obj['sticker']) + return cls(**obj) + +class UniqueGiftSymbol(JsonDeserializable): + """ + This object describes the symbol shown on the pattern of a unique gift. + + Telegram documentation: https://core.telegram.org/bots/api#uniquegiftsymbol + + :param name: Name of the symbol + :type name: :obj:`str` + + :param sticker: The sticker that represents the unique gift + :type sticker: :class:`Sticker` + + :param rarity_per_mille: The number of unique gifts that receive this model for every 1000 gifts upgraded + :type rarity_per_mille: :obj:`int` + + :return: Instance of the class + :rtype: :class:`UniqueGiftSymbol` + """ + + def __init__(self, name, sticker, rarity_per_mille, **kwargs): + self.name: str = name + self.sticker: Sticker = sticker + self.rarity_per_mille: int = rarity_per_mille + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['sticker'] = Sticker.de_json(obj['sticker']) + return cls(**obj) + +class UniqueGiftBackdropColors(JsonDeserializable): + """ + This object describes the colors of the backdrop of a unique gift. + + Telegram documentation: https://core.telegram.org/bots/api#uniquegiftbackdropcolors + + :param center_color: The color in the center of the backdrop in RGB format + :type center_color: :obj:`int` + + :param edge_color: The color on the edges of the backdrop in RGB format + :type edge_color: :obj:`int` + + :param symbol_color: The color to be applied to the symbol in RGB format + :type symbol_color: :obj:`int` + + :param text_color: The color for the text on the backdrop in RGB format + :type text_color: :obj:`int` + + :return: Instance of the class + :rtype: :class:`UniqueGiftBackdropColors` + """ + def __init__(self, center_color, edge_color, symbol_color, text_color, **kwargs): + self.center_color: int = center_color + self.edge_color: int = edge_color + self.symbol_color: int = symbol_color + self.text_color: int = text_color + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + +class UniqueGiftBackdrop(JsonDeserializable): + """ + This object describes the backdrop of a unique gift. + + Telegram documentation: https://core.telegram.org/bots/api#uniquegiftbackdrop + + :param name: Name of the backdrop + :type name: :obj:`str` + + :param colors: Colors of the backdrop + :type colors: :class:`UniqueGiftBackdropColors` + + :param rarity_per_mille: The number of unique gifts that receive this backdrop for every 1000 gifts upgraded + :type rarity_per_mille: :obj:`int` + + :return: Instance of the class + :rtype: :class:`UniqueGiftBackdrop` + """ + def __init__(self, name, colors, rarity_per_mille, **kwargs): + self.name: str = name + self.colors: UniqueGiftBackdropColors = colors + self.rarity_per_mille: int = rarity_per_mille + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['colors'] = UniqueGiftBackdropColors.de_json(obj['colors']) + return cls(**obj) + + + From 3d238a4a2a9eea85eed2d9f862d0ebcbe157c0fd Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 13:53:07 +0400 Subject: [PATCH 1747/1808] Added convert_gift_to_stars --- telebot/__init__.py | 17 +++++++++++++++++ telebot/apihelper.py | 6 ++++++ telebot/async_telebot.py | 17 +++++++++++++++++ telebot/asyncio_helper.py | 5 +++++ 4 files changed, 45 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index c47d99919..7476ff8fa 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6584,6 +6584,23 @@ def get_business_account_gifts( ) ) + def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) -> bool: + """ + Converts a given regular gift to Telegram Stars. Requires the can_convert_gifts_to_stars business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#convertgifttostars + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param owned_gift_id: Unique identifier of the regular gift that should be converted to Telegram Stars + :type owned_gift_id: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.convert_gift_to_stars(self.token, business_connection_id, owned_gift_id) + def get_available_gifts(self) -> types.Gifts: """ Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index f4f9545be..d18a6380a 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2068,6 +2068,12 @@ def get_business_account_gifts(token, business_connection_id, exclude_unsaved=No payload['limit'] = limit return _make_request(token, method_url, params=payload) + +def convert_gift_to_stars(token, business_connection_id, owned_gift_id): + method_url = 'convertGiftToStars' + payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7ce6c03e7..6b601e33a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8039,6 +8039,23 @@ async def get_business_account_gifts( limit=limit ) ) + + async def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) -> bool: + """ + Converts a given regular gift to Telegram Stars. Requires the can_convert_gifts_to_stars business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#convertgifttostars + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param owned_gift_id: Unique identifier of the regular gift that should be converted to Telegram Stars + :type owned_gift_id: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.convert_gift_to_stars(self.token, business_connection_id, owned_gift_id) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3c587d7bd..0e21a6978 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2042,6 +2042,11 @@ async def get_business_account_gifts(token, business_connection_id, exclude_unsa payload['limit'] = limit return await _process_request(token, method_url, params=payload) +async def convert_gift_to_stars(token, business_connection_id, owned_gift_id): + method_url = 'convertGiftToStars' + payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} + return await _process_request(token, method_url, params=payload, method='post') + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) From 817f2ad79b697b4b459942e2cb9661dc8dcf755c Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 13:59:19 +0400 Subject: [PATCH 1748/1808] Added upgrade_gift and transfer_gift methods --- telebot/__init__.py | 65 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 16 ++++++++++ telebot/async_telebot.py | 64 ++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 16 ++++++++++ 4 files changed, 161 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 7476ff8fa..4a6dfc3d7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6600,6 +6600,71 @@ def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) :rtype: :obj:`bool` """ return apihelper.convert_gift_to_stars(self.token, business_connection_id, owned_gift_id) + + def upgrade_gift( + self, business_connection_id: str, owned_gift_id: str, + keep_original_details: Optional[bool]=None, + star_count: Optional[int]=None) -> bool: + """ + Upgrades a given regular gift to a unique gift. Requires the can_transfer_and_upgrade_gifts business bot right. + Additionally requires the can_transfer_stars business bot right if the upgrade is paid. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#upgradegift + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param owned_gift_id: Unique identifier of the regular gift that should be upgraded to a unique one + :type owned_gift_id: :obj:`str` + + :param keep_original_details: Pass True to keep the original gift text, sender and receiver in the upgraded gift + :type keep_original_details: :obj:`bool` + + :param star_count: The amount of Telegram Stars that will be paid for the upgrade from the business account balance. + If gift.prepaid_upgrade_star_count > 0, then pass 0, otherwise, the can_transfer_stars business bot right is required and gift.upgrade_star_count must be passed. + :type star_count: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.upgrade_gift( + self.token, business_connection_id, owned_gift_id, + keep_original_details=keep_original_details, + star_count=star_count + ) + + def transfer_gift( + self, business_connection_id: str, owned_gift_id: str, + new_owner_chat_id: Union[int, str], + star_count: Optional[int]=None) -> bool: + """ + Transfers an owned unique gift to another user. Requires the can_transfer_and_upgrade_gifts business bot right. + Requires can_transfer_stars business bot right if the transfer is paid. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#transfergift + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param owned_gift_id: Unique identifier of the regular gift that should be transferred + :type owned_gift_id: :obj:`str` + + :param new_owner_chat_id: Unique identifier of the chat which will own the gift. The chat must be active in the last 24 hours. + :type new_owner_chat_id: :obj:`int` | :obj:`str` + + :param star_count: The amount of Telegram Stars that will be paid for the transfer from the business account balance. + If positive, then the can_transfer_stars business bot right is required. + :type star_count: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.transfer_gift( + self.token, business_connection_id, owned_gift_id, + new_owner_chat_id=new_owner_chat_id, + star_count=star_count + ) + def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d18a6380a..366fb9435 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2074,6 +2074,22 @@ def convert_gift_to_stars(token, business_connection_id, owned_gift_id): payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} return _make_request(token, method_url, params=payload, method='post') +def upgrade_gift(token, business_connection_id, owned_gift_id, keep_original_details=None, star_count=None): + method_url = 'upgradeGift' + payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} + if keep_original_details is not None: + payload['keep_original_details'] = keep_original_details + if star_count is not None: + payload['star_count'] = star_count + return _make_request(token, method_url, params=payload, method='post') + +def transfer_gift(token, business_connection_id, owned_gift_id, new_owner_chat_id, star_count=None): + method_url = 'transferGift' + payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id, 'new_owner_chat_id': new_owner_chat_id} + if star_count is not None: + payload['star_count'] = star_count + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6b601e33a..8c12e7891 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8056,6 +8056,70 @@ async def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id :rtype: :obj:`bool` """ return await asyncio_helper.convert_gift_to_stars(self.token, business_connection_id, owned_gift_id) + + async def upgrade_gift( + self, business_connection_id: str, owned_gift_id: str, + keep_original_details: Optional[bool]=None, + star_count: Optional[int]=None) -> bool: + """ + Upgrades a given regular gift to a unique gift. Requires the can_transfer_and_upgrade_gifts business bot right. + Additionally requires the can_transfer_stars business bot right if the upgrade is paid. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#upgradegift + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param owned_gift_id: Unique identifier of the regular gift that should be upgraded to a unique one + :type owned_gift_id: :obj:`str` + + :param keep_original_details: Pass True to keep the original gift text, sender and receiver in the upgraded gift + :type keep_original_details: :obj:`bool` + + :param star_count: The amount of Telegram Stars that will be paid for the upgrade from the business account balance. + If gift.prepaid_upgrade_star_count > 0, then pass 0, otherwise, the can_transfer_stars business bot right is required and gift.upgrade_star_count must be passed. + :type star_count: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.upgrade_gift( + self.token, business_connection_id, owned_gift_id, + keep_original_details=keep_original_details, + star_count=star_count + ) + + async def transfer_gift( + self, business_connection_id: str, owned_gift_id: str, + new_owner_chat_id: Union[int, str], + star_count: Optional[int]=None) -> bool: + """ + Transfers an owned unique gift to another user. Requires the can_transfer_and_upgrade_gifts business bot right. + Requires can_transfer_stars business bot right if the transfer is paid. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#transfergift + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param owned_gift_id: Unique identifier of the regular gift that should be transferred + :type owned_gift_id: :obj:`str` + + :param new_owner_chat_id: Unique identifier of the chat which will own the gift. The chat must be active in the last 24 hours. + :type new_owner_chat_id: :obj:`int` | :obj:`str` + + :param star_count: The amount of Telegram Stars that will be paid for the transfer from the business account balance. + If positive, then the can_transfer_stars business bot right is required. + :type star_count: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.transfer_gift( + self.token, business_connection_id, owned_gift_id, + new_owner_chat_id=new_owner_chat_id, + star_count=star_count + ) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0e21a6978..f539bd04e 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2047,6 +2047,22 @@ async def convert_gift_to_stars(token, business_connection_id, owned_gift_id): payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} return await _process_request(token, method_url, params=payload, method='post') +async def upgrade_gift(token, business_connection_id, owned_gift_id, keep_original_details=None, star_count=None): + method_url = 'upgradeGift' + payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} + if keep_original_details is not None: + payload['keep_original_details'] = keep_original_details + if star_count is not None: + payload['star_count'] = star_count + return await _process_request(token, method_url, params=payload, method='post') + +async def transfer_gift(token, business_connection_id, owned_gift_id, new_owner_chat_id, star_count=None): + method_url = 'transferGift' + payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id, 'new_owner_chat_id': new_owner_chat_id} + if star_count is not None: + payload['star_count'] = star_count + return await _process_request(token, method_url, params=payload, method='post') + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) From b47c73f00cd0bdb930db2281ebe5421c9f9b7cd3 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 14:19:50 +0400 Subject: [PATCH 1749/1808] Added InputStoryContent --- telebot/__init__.py | 1 + telebot/types.py | 90 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 4a6dfc3d7..075d9f6e0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -9651,3 +9651,4 @@ def _notify_command_handlers(self, handlers, new_messages, update_type): handlers=handlers, middlewares=middlewares, update_type=update_type) + diff --git a/telebot/types.py b/telebot/types.py index 8b1fc3a39..637599233 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11773,6 +11773,96 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['colors'] = UniqueGiftBackdropColors.de_json(obj['colors']) return cls(**obj) + +class InputStoryContent(JsonSerializable): + """ + This object describes the content of a story to post. Currently, it can be one of + InputStoryContentPhoto + InputStoryContentVideo + + Telegram documentation: https://core.telegram.org/bots/api#inputstorycontent + + """ + + +class InputStoryContentPhoto(InputStoryContent): + """ + This object describes a photo to post as a story. + + Telegram documentation: https://core.telegram.org/bots/api#inputstorycontentphoto + + :param photo: The photo to post as a story. The photo must be of the size 1080x1920 and must not exceed 10 MB. The photo can't be reused and can only be uploaded as a new file, so you can pass “attach://” if the photo was uploaded using multipart/form-data under . More information on Sending Files + :type photo: :obj:`str` + + :return: Instance of the class + :rtype: :class:`InputStoryContentPhoto` + """ + def __init__(self, photo: InputFile, **kwargs): + self.type: str = "photo" + self.photo: InputFile = photo + self._photo_name = service_utils.generate_random_token() + self._photo_dic = "attach://{}".format(self._photo_name) + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = { + 'type': self.type, + 'photo': self._photo_dic + } + return data + + def convert_input_story(self): + return self.to_json(), {self._photo_name: self.photo} +class InputStoryContentVideo(InputStoryContent): + """ + This object describes a video to post as a story. + + Telegram documentation: https://core.telegram.org/bots/api#inputstorycontentvideo + + :param video: The video to post as a story. The video must be of the size 720x1280, streamable, encoded with H.265 codec, with key frames added each second in the MPEG4 format, and must not exceed 30 MB. The video can't be reused and can only be uploaded as a new file, so you can pass “attach://” if the video was uploaded using multipart/form-data under . More information on Sending Files + :type video: :obj:`str` + + :param duration: Optional. Precise duration of the video in seconds; 0-60 + :type duration: :obj:`float` + + :param cover_frame_timestamp: Optional. Timestamp in seconds of the frame that will be used as the static cover for the story. Defaults to 0.0. + :type cover_frame_timestamp: :obj:`float` + + :param is_animation: Optional. Pass True if the video has no sound + :type is_animation: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`InputStoryContentVideo` + """ + def __init__(self, video: InputFile, duration: Optional[float] = None, cover_frame_timestamp: Optional[float] = None, + is_animation: Optional[bool] = None, **kwargs): + self.type: str = "video" + self.video: InputFile = video + self._video_name = service_utils.generate_random_token() + self._video_dic = "attach://{}".format(self._video_name) + self.duration: Optional[float] = duration + self.cover_frame_timestamp: Optional[float] = cover_frame_timestamp + self.is_animation: Optional[bool] = is_animation + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = { + 'type': self.type, + 'video': self._video_dic + } + if self.duration is not None: + data['duration'] = self.duration + if self.cover_frame_timestamp is not None: + data['cover_frame_timestamp'] = self.cover_frame_timestamp + if self.is_animation is not None: + data['is_animation'] = self.is_animation + return data + def convert_input_story(self): + return self.to_json(), {self._video_name: self.video} + From 17139098828a1105dca2452c101f1824f815f4fb Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 14:40:00 +0400 Subject: [PATCH 1750/1808] Stories --- telebot/types.py | 305 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 637599233..8745bfa66 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11866,3 +11866,308 @@ def convert_input_story(self): return self.to_json(), {self._video_name: self.video} +class StoryAreaPosition(JsonSerializable): + """ + Describes the position of a clickable area within a story. + + Telegram documentation: https://core.telegram.org/bots/api#storyareaposition + + :param x_percentage: The abscissa of the area's center, as a percentage of the media width + :type x_percentage: :obj:`float` + + :param y_percentage: The ordinate of the area's center, as a percentage of the media height + :type y_percentage: :obj:`float` + + :param width_percentage: The width of the area's rectangle, as a percentage of the media width + :type width_percentage: :obj:`float` + + :param height_percentage: The height of the area's rectangle, as a percentage of the media height + :type height_percentage: :obj:`float` + + :param rotation_angle: The clockwise rotation angle of the rectangle, in degrees; 0-360 + :type rotation_angle: :obj:`float` + + :param corner_radius_percentage: The radius of the rectangle corner rounding, as a percentage of the media width + :type corner_radius_percentage: :obj:`float` + + :return: Instance of the class + :rtype: :class:`StoryAreaPosition` + """ + def __init__(self, x_percentage: float, y_percentage: float, width_percentage: float, + height_percentage: float, rotation_angle: float, corner_radius_percentage: float, **kwargs): + self.x_percentage: float = x_percentage + self.y_percentage: float = y_percentage + self.width_percentage: float = width_percentage + self.height_percentage: float = height_percentage + self.rotation_angle: float = rotation_angle + self.corner_radius_percentage: float = corner_radius_percentage + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'x_percentage': self.x_percentage, + 'y_percentage': self.y_percentage, + 'width_percentage': self.width_percentage, + 'height_percentage': self.height_percentage, + 'rotation_angle': self.rotation_angle, + 'corner_radius_percentage': self.corner_radius_percentage + } + return data + + +class LocationAddress(JsonSerializable): + """ + Describes the physical address of a location. + + Telegram documentation: https://core.telegram.org/bots/api#locationaddress + + :param country_code: The two-letter ISO 3166-1 alpha-2 country code of the country where the location is located + :type country_code: :obj:`str` + + :param state: Optional. State of the location + :type state: :obj:`str` + + :param city: Optional. City of the location + :type city: :obj:`str` + + :param street: Optional. Street address of the location + :type street: :obj:`str` + + :return: Instance of the class + :rtype: :class:`LocationAddress` + """ + def __init__(self, country_code: str, state: Optional[str] = None, city: Optional[str] = None, + street: Optional[str] = None, **kwargs): + self.country_code: str = country_code + self.state: Optional[str] = state + self.city: Optional[str] = city + self.street: Optional[str] = street + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'country_code': self.country_code + } + if self.state is not None: + data['state'] = self.state + if self.city is not None: + data['city'] = self.city + if self.street is not None: + data['street'] = self.street + return data + +class StoryAreaType(JsonSerializable): + """ + Describes the type of a clickable area on a story. Currently, it can be one of + StoryAreaTypeLocation + StoryAreaTypeSuggestedReaction + StoryAreaTypeLink + StoryAreaTypeWeather + StoryAreaTypeUniqueGift + + Telegram documentation: https://core.telegram.org/bots/api#storyarea + + :return: Instance of the class + :rtype: :class:`StoryArea` + """ + + +class StoryAreaTypeLocation(StoryAreaType): + """ + Describes a story area pointing to a location. Currently, a story can have up to 10 location areas. + + Telegram documentation: https://core.telegram.org/bots/api#storyareatypelocation + + :param type: Type of the area, always “location” + :type type: :obj:`str` + + :param latitude: Location latitude in degrees + :type latitude: :obj:`float` + + :param longitude: Location longitude in degrees + :type longitude: :obj:`float` + + :param address: Location address + :type address: :class:`LocationAddress` + + :return: Instance of the class + :rtype: :class:`StoryAreaTypeLocation` + """ + def __init__(self,latitude: float, longitude: float, address: LocationAddress, **kwargs): + self.type: str = "location" + self.latitude: float = latitude + self.longitude: float = longitude + self.address: LocationAddress = address + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'type': self.type, + 'latitude': self.latitude, + 'longitude': self.longitude, + 'address': self.address.to_json() + } + return data + + +class StoryAreaTypeSuggestedReaction(StoryAreaType): + """ + Describes a story area pointing to a suggested reaction. Currently, a story can have up to 5 suggested reaction areas. + + Telegram documentation: https://core.telegram.org/bots/api#storyareatypesuggestedreaction + + :param type: Type of the area, always “suggested_reaction” + :type type: :obj:`str` + + :param reaction_type: Type of the reaction + :type reaction_type: :class:`ReactionType` + + :param is_dark: Optional. Pass True if the reaction area has a dark background + :type is_dark: :obj:`bool` + + :param is_flipped: Optional. Pass True if reaction area corner is flipped + :type is_flipped: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`StoryAreaTypeSuggestedReaction` + """ + def __init__(self, reaction_type: ReactionType, is_dark: Optional[bool] = None, is_flipped: Optional[bool] = None, **kwargs): + self.type: str = "suggested_reaction" + self.reaction_type: ReactionType = reaction_type + self.is_dark: Optional[bool] = is_dark + self.is_flipped: Optional[bool] = is_flipped + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'type': self.type, + 'reaction_type': self.reaction_type.to_json() + } + if self.is_dark is not None: + data['is_dark'] = self.is_dark + if self.is_flipped is not None: + data['is_flipped'] = self.is_flipped + return data + +class StoryAreaTypeLink(StoryAreaType): + """ + Describes a story area pointing to an HTTP or tg:// link. Currently, a story can have up to 3 link areas. + + Telegram documentation: https://core.telegram.org/bots/api#storyareatypelink + + :param type: Type of the area, always “link” + :type type: :obj:`str` + + :param url: HTTP or tg:// URL to be opened when the area is clicked + :type url: :obj:`str` + + :return: Instance of the class + :rtype: :class:`StoryAreaTypeLink` + """ + def __init__(self, url: str, **kwargs): + self.type: str = "link" + self.url: str = url + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'type': self.type, + 'url': self.url + } + return data + +class StoryAreaTypeWeather(StoryAreaType): + """ + Describes a story area containing weather information. Currently, a story can have up to 3 weather areas. + + Telegram documentation: https://core.telegram.org/bots/api#storyareatypeweather + + :param type: Type of the area, always “weather” + :type type: :obj:`str` + + :param temperature: Temperature, in degree Celsius + :type temperature: :obj:`float` + + :param emoji: Emoji representing the weather + :type emoji: :obj:`str` + + :param background_color: A color of the area background in the ARGB format + :type background_color: :obj:`int` + + :return: Instance of the class + :rtype: :class:`StoryAreaTypeWeather` + """ + def __init__(self, temperature: float, emoji: str, background_color: int, **kwargs): + self.type: str = "weather" + self.temperature: float = temperature + self.emoji: str = emoji + self.background_color: int = background_color + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'type': self.type, + 'temperature': self.temperature, + 'emoji': self.emoji, + 'background_color': self.background_color + } + return data + +class StoryAreaTypeUniqueGift(StoryAreaType): + """ + Describes a story area pointing to a unique gift. Currently, a story can have at most 1 unique gift area. + + Telegram documentation: https://core.telegram.org/bots/api#storyareatypeuniquegift + + :param type: Type of the area, always “unique_gift” + :type type: :obj:`str` + + :param name: Unique name of the gift + :type name: :obj:`str` + + :return: Instance of the class + :rtype: :class:`StoryAreaTypeUniqueGift` + """ + def __init__(self, name: str, **kwargs): + self.type: str = "unique_gift" + self.name: str = name + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'type': self.type, + 'name': self.name + } + + return data + + +class StoryArea(JsonSerializable): + """ + Describes a clickable area on a story media. + + Telegram documentation: https://core.telegram.org/bots/api#storyarea + + :param position: Position of the area + :type position: :class:`StoryAreaPosition` + + :param type: Type of the area + :type type: :class:`StoryAreaType` + + :return: Instance of the class + :rtype: :class:`StoryArea` + """ + def __init__(self, position: StoryAreaPosition, type: StoryAreaType, **kwargs): + self.position: StoryAreaPosition = position + self.type: StoryAreaType = type + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'position': self.position.to_json(), + 'type': self.type.to_json() + } + return data + + + From 1c081633b5056ec4b7cc1cb733072e2da9636326 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 14:52:50 +0400 Subject: [PATCH 1751/1808] posting, editing, deleting stories(needs testing) --- telebot/__init__.py | 119 +++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 45 ++++++++++++++ telebot/async_telebot.py | 120 ++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 45 ++++++++++++++ 4 files changed, 329 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 075d9f6e0..159d4b632 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6664,7 +6664,126 @@ def transfer_gift( new_owner_chat_id=new_owner_chat_id, star_count=star_count ) + + def post_story( + self, business_connection_id: str, content: types.InputStoryContent, + active_period: int, caption: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + areas: Optional[List[types.StoryArea]]=None, + post_to_chat_page: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Story: + + """ + Posts a story on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns Story on success. + + Telegram documentation: https://core.telegram.org/bots/api#poststory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param content: Content of the story + :type content: :class:`telebot.types.InputStoryContent` + + :param active_period: Period after which the story is moved to the archive, in seconds; must be one of 6 * 3600, 12 * 3600, 86400, or 2 * 86400 + :type active_period: :obj:`int` + + :param caption: Caption of the story, 0-2048 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the story caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param areas: A JSON-serialized list of clickable areas to be shown on the story + :type areas: :obj:`list` of :class:`telebot.types.StoryArea` + + :param post_to_chat_page: Pass True to keep the story accessible after it expires + :type post_to_chat_page: :obj:`bool` + + :param protect_content: Pass True if the content of the story must be protected from forwarding and screenshotting + :type protect_content: :obj:`bool` + + :return: On success, a Story object is returned. + :rtype: :class:`telebot.types.Story` + """ + return types.Story.de_json( + apihelper.post_story( + self.token, business_connection_id, content, + active_period, caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + areas=areas, + post_to_chat_page=post_to_chat_page, + protect_content=protect_content + ) + ) + + def edit_story( + self, business_connection_id: str, story_id: int, + content: types.InputStoryContent, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + areas: Optional[List[types.StoryArea]]=None) -> types.Story: + """ + Edits a story previously posted by the bot on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns Story on success. + Telegram documentation: https://core.telegram.org/bots/api#editstory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param story_id: Unique identifier of the story to edit + :type story_id: :obj:`int` + + :param content: Content of the story + :type content: :class:`telebot.types.InputStoryContent` + + :param caption: Caption of the story, 0-2048 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the story caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param areas: A JSON-serialized list of clickable areas to be shown on the story + :type areas: :obj:`list` of :class:`telebot.types.StoryArea` + + :return: On success, a Story object is returned. + :rtype: :class:`telebot.types.Story` + + """ + return types.Story.de_json( + apihelper.edit_story( + self.token, business_connection_id, story_id, + content, caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + areas=areas + ) + ) + + def delete_story(self, business_connection_id: str, story_id: int) -> bool: + """ + Deletes a story previously posted by the bot on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletestory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param story_id: Unique identifier of the story to delete + :type story_id: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.delete_story(self.token, business_connection_id, story_id) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 366fb9435..d228023fb 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2090,6 +2090,51 @@ def transfer_gift(token, business_connection_id, owned_gift_id, new_owner_chat_i payload['star_count'] = star_count return _make_request(token, method_url, params=payload, method='post') +def post_story(token, business_connection_id, content, active_period, caption=None, parse_mode=None, + caption_entities=None, areas=None, post_to_chat_page=None, protect_content=None): + method_url = 'postStory' + payload = {'business_connection_id': business_connection_id, 'active_period': active_period} + + content_json, files = content.convert_input_story() + payload['content'] = content_json + + if caption: + payload['caption'] = caption + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if areas: + payload['areas'] = json.dumps([area.to_json() for area in areas]) + if post_to_chat_page is not None: + payload['post_to_chat_page'] = post_to_chat_page + if protect_content is not None: + payload['protect_content'] = protect_content + return _make_request(token, method_url, params=payload, files=files, method='post') + +def edit_story(token, business_connection_id, story_id, content, caption=None, parse_mode=None, + caption_entities=None, areas=None): + method_url = 'editStory' + payload = {'business_connection_id': business_connection_id, 'story_id': story_id} + + content_json, files = content.convert_input_story() + payload['content'] = content_json + + if caption: + payload['caption'] = caption + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if areas: + payload['areas'] = json.dumps([area.to_json() for area in areas]) + return _make_request(token, method_url, params=payload, files=files, method='post') + +def delete_story(token, business_connection_id, story_id): + method_url = 'deleteStory' + payload = {'business_connection_id': business_connection_id, 'story_id': story_id} + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8c12e7891..ed6db2be3 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8120,6 +8120,126 @@ async def transfer_gift( new_owner_chat_id=new_owner_chat_id, star_count=star_count ) + + async def post_story( + self, business_connection_id: str, content: types.InputStoryContent, + active_period: int, caption: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + areas: Optional[List[types.StoryArea]]=None, + post_to_chat_page: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Story: + + """ + Posts a story on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns Story on success. + + Telegram documentation: https://core.telegram.org/bots/api#poststory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param content: Content of the story + :type content: :class:`telebot.types.InputStoryContent` + + :param active_period: Period after which the story is moved to the archive, in seconds; must be one of 6 * 3600, 12 * 3600, 86400, or 2 * 86400 + :type active_period: :obj:`int` + + :param caption: Caption of the story, 0-2048 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the story caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param areas: A JSON-serialized list of clickable areas to be shown on the story + :type areas: :obj:`list` of :class:`telebot.types.StoryArea` + + :param post_to_chat_page: Pass True to keep the story accessible after it expires + :type post_to_chat_page: :obj:`bool` + + :param protect_content: Pass True if the content of the story must be protected from forwarding and screenshotting + :type protect_content: :obj:`bool` + + :return: On success, a Story object is returned. + :rtype: :class:`telebot.types.Story` + """ + return types.Story.de_json( + await asyncio_helper.post_story( + self.token, business_connection_id, content, + active_period, caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + areas=areas, + post_to_chat_page=post_to_chat_page, + protect_content=protect_content + ) + ) + + async def edit_story( + self, business_connection_id: str, story_id: int, + content: types.InputStoryContent, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, + caption_entities: Optional[List[types.MessageEntity]]=None, + areas: Optional[List[types.StoryArea]]=None) -> types.Story: + """ + Edits a story previously posted by the bot on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns Story on success. + + Telegram documentation: https://core.telegram.org/bots/api#editstory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param story_id: Unique identifier of the story to edit + :type story_id: :obj:`int` + + :param content: Content of the story + :type content: :class:`telebot.types.InputStoryContent` + + :param caption: Caption of the story, 0-2048 characters after entities parsing + :type caption: :obj:`str` + + :param parse_mode: Mode for parsing entities in the story caption. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param caption_entities: A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode + :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :param areas: A JSON-serialized list of clickable areas to be shown on the story + :type areas: :obj:`list` of :class:`telebot.types.StoryArea` + + :return: On success, a Story object is returned. + :rtype: :class:`telebot.types.Story` + + """ + return types.Story.de_json( + await asyncio_helper.edit_story( + self.token, business_connection_id, story_id, + content, caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + areas=areas + ) + ) + + async def delete_story(self, business_connection_id: str, story_id: int) -> bool: + """ + Deletes a story previously posted by the bot on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#deletestory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param story_id: Unique identifier of the story to delete + :type story_id: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.delete_story(self.token, business_connection_id, story_id) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index f539bd04e..9f1e26364 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2063,6 +2063,51 @@ async def transfer_gift(token, business_connection_id, owned_gift_id, new_owner_ payload['star_count'] = star_count return await _process_request(token, method_url, params=payload, method='post') +async def post_story(token, business_connection_id, content, active_period, caption=None, parse_mode=None, + caption_entities=None, areas=None, post_to_chat_page=None, protect_content=None): + method_url = 'postStory' + payload = {'business_connection_id': business_connection_id, 'active_period': active_period} + + content_json, files = content.convert_input_story() + payload['content'] = content_json + + if caption: + payload['caption'] = caption + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if areas: + payload['areas'] = json.dumps([area.to_json() for area in areas]) + if post_to_chat_page is not None: + payload['post_to_chat_page'] = post_to_chat_page + if protect_content is not None: + payload['protect_content'] = protect_content + return await _process_request(token, method_url, params=payload, files=files, method='post') + +async def edit_story(token, business_connection_id, story_id, content, caption=None, parse_mode=None, + caption_entities=None, areas=None): + method_url = 'editStory' + payload = {'business_connection_id': business_connection_id, 'story_id': story_id} + + content_json, files = content.convert_input_story() + payload['content'] = content_json + + if caption: + payload['caption'] = caption + if parse_mode: + payload['parse_mode'] = parse_mode + if caption_entities: + payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) + if areas: + payload['areas'] = json.dumps([area.to_json() for area in areas]) + return await _process_request(token, method_url, params=payload, files=files, method='post') + +async def delete_story(token, business_connection_id, story_id): + method_url = 'deleteStory' + payload = {'business_connection_id': business_connection_id, 'story_id': story_id} + return await _process_request(token, method_url, params=payload, method='post') + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) From b05d209900f31e4181be07ef9226b591ede4b408 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 22:33:14 +0400 Subject: [PATCH 1752/1808] Replaced the field can_send_gift with the field accepted_gift_types of the type AcceptedGiftTypes in the class ChatFullInfo. --- telebot/types.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 8745bfa66..4cd640819 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -691,6 +691,9 @@ class ChatFullInfo(JsonDeserializable): :param can_send_gift: Optional. True, if gifts can be sent to the chat :type can_send_gift: :obj:`bool` + :param accepted_gift_types: Information about types of gifts that are accepted by the chat or by the corresponding user for private chats + :type accepted_gift_types: :class:`telebot.types.AcceptedGiftTypes` + :param can_send_paid_media: Optional. True, if paid media messages can be sent or forwarded to the channel chat. The field is available only for channel chats. :type can_send_paid_media: :obj:`bool` @@ -762,6 +765,8 @@ def de_json(cls, json_string): obj['personal_chat'] = Chat.de_json(obj['personal_chat']) if 'birthdate' in obj: obj['birthdate'] = Birthdate.de_json(obj['birthdate']) + if 'accepted_gift_types' in obj: + obj['accepted_gift_types'] = AcceptedGiftTypes.de_json(obj['accepted_gift_types']) return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -777,7 +782,8 @@ def __init__(self, id, type, title=None, username=None, first_name=None, profile_background_custom_emoji_id=None, has_visible_history=None, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, business_opening_hours=None, personal_chat=None, birthdate=None, - can_send_paid_media=None, can_send_gift=None, **kwargs): + can_send_paid_media=None, + accepted_gift_types=None, **kwargs): self.id: int = id self.type: str = type self.title: Optional[str] = title @@ -822,7 +828,11 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.personal_chat: Optional[Chat] = personal_chat self.birthdate: Optional[Birthdate] = birthdate self.can_send_paid_media: Optional[bool] = can_send_paid_media - self.can_send_gift: Optional[bool] = can_send_gift + self.accepted_gift_types: AcceptedGiftTypes = accepted_gift_types + self.can_send_gift: Optional[bool] = None + if self.accepted_gift_types is not None: # not optional but still + # skip premium subscription? + self.can_send_gift: Optional[bool] = any([self.accepted_gift_types.unique_gifts, self.accepted_gift_types.unlimited_gifts, self.accepted_gift_types.limited_gifts]) class Chat(ChatFullInfo): From 6fedece26c4f26acd6fc50064e01cbe0a1e72641 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 22:38:56 +0400 Subject: [PATCH 1753/1808] Added unique_gift and gift --- telebot/types.py | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 4cd640819..192bd7df8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1124,6 +1124,12 @@ class Message(JsonDeserializable): :param chat_shared: Optional. Service message: a chat was shared with the bot :type chat_shared: :class:`telebot.types.ChatShared` + :param gift: Optional. Service message: a regular gift was sent or received + :type gift: :class:`telebot.types.GiftInfo` + + :param unique_gift: Optional. Service message: a unique gift was sent or received + :type unique_gift: :class:`telebot.types.UniqueGiftInfo` + :param connected_website: Optional. The domain name of the website on which the user has logged in. More about Telegram Login » :type connected_website: :obj:`str` @@ -1431,6 +1437,12 @@ def de_json(cls, json_string): opts['paid_media'] = PaidMediaInfo.de_json(obj['paid_media']) if 'refunded_payment' in obj: opts['refunded_payment'] = RefundedPayment.de_json(obj['refunded_payment']) + if 'gift' in obj: + opts['gift'] = GiftInfo.de_json(obj['gift']) + content_type = 'gift' + if 'unique_gift' in obj: + opts['unique_gift'] = UniqueGiftInfo.de_json(obj['unique_gift']) + content_type = 'unique_gift' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1549,6 +1561,8 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.video_chat_participants_invited: Optional[VideoChatParticipantsInvited] = None self.web_app_data: Optional[WebAppData] = None self.message_auto_delete_timer_changed: Optional[MessageAutoDeleteTimerChanged] = None + self.gift : Optional[GiftInfo] = None + self.unique_gift : Optional[UniqueGiftInfo] = None for key in options: @@ -12180,4 +12194,91 @@ def to_dict(self): return data +class GiftInfo(JsonDeserializable): + """ + This object describes a service message about a regular gift that was sent or received. + + Telegram documentation: https://core.telegram.org/bots/api#giftinfo + + :param gift: Information about the gift + :type gift: :class:`Gift` + + :param owned_gift_id: Optional. Unique identifier of the received gift for the bot; only present for gifts received on behalf of business accounts + :type owned_gift_id: :obj:`str` + :param convert_star_count: Optional. Number of Telegram Stars that can be claimed by the receiver by converting the gift; omitted if conversion to Telegram Stars is impossible + :type convert_star_count: :obj:`int` + + :param prepaid_upgrade_star_count: Optional. Number of Telegram Stars that were prepaid by the sender for the ability to upgrade the gift + :type prepaid_upgrade_star_count: :obj:`int` + + :param can_be_upgraded: Optional. True, if the gift can be upgraded to a unique gift + :type can_be_upgraded: :obj:`bool` + + :param text: Optional. Text of the message that was added to the gift + :type text: :obj:`str` + + :param entities: Optional. Special entities that appear in the text + :type entities: :obj:`list` of :class:`MessageEntity` + + :param is_private: Optional. True, if the sender and gift text are shown only to the gift receiver; otherwise, everyone will be able to see them + :type is_private: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`GiftInfo` + """ + def __init__(self, gift: Gift, owned_gift_id: Optional[str] = None, convert_star_count: Optional[int] = None, + prepaid_upgrade_star_count: Optional[int] = None, can_be_upgraded: Optional[bool] = None, + text: Optional[str] = None, entities: Optional[List[MessageEntity]] = None, + is_private: Optional[bool] = None, **kwargs): + self.gift: Gift = gift + self.owned_gift_id: Optional[str] = owned_gift_id + self.convert_star_count: Optional[int] = convert_star_count + self.prepaid_upgrade_star_count: Optional[int] = prepaid_upgrade_star_count + self.can_be_upgraded: Optional[bool] = can_be_upgraded + self.text: Optional[str] = text + self.entities: Optional[List[MessageEntity]] = entities + self.is_private: Optional[bool] = is_private + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['gift'] = Gift.de_json(obj['gift']) + if 'entities' in obj: + obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] + return cls(**obj) + +class UniqueGiftInfo(JsonDeserializable): + """ + This object describes a service message about a unique gift that was sent or received. + + Telegram documentation: https://core.telegram.org/bots/api#uniquegiftinfo + + :param gift: Information about the gift + :type gift: :class:`UniqueGift` + + :param origin: Origin of the gift. Currently, either “upgrade” or “transfer” + :type origin: :obj:`str` + + :param owned_gift_id: Optional. Unique identifier of the received gift for the bot; only present for gifts received on behalf of business accounts + :type owned_gift_id: :obj:`str` + + :param transfer_star_count: Optional. Number of Telegram Stars that must be paid to transfer the gift; omitted if the bot cannot transfer the gift + :type transfer_star_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`UniqueGiftInfo` + """ + def __init__(self, gift: UniqueGift, origin: str, owned_gift_id: Optional[str] = None, + transfer_star_count: Optional[int] = None, **kwargs): + self.gift: UniqueGift = gift + self.origin: str = origin + self.owned_gift_id: Optional[str] = owned_gift_id + self.transfer_star_count: Optional[int] = transfer_star_count + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['gift'] = UniqueGift.de_json(obj['gift']) + return cls(**obj) + \ No newline at end of file From e3d7f9640fbbfa8ba71531aae3055aee355023c7 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 22:41:45 +0400 Subject: [PATCH 1754/1808] Added gift_premium_subscription --- telebot/__init__.py | 36 ++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 12 ++++++++++++ telebot/async_telebot.py | 36 ++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 13 +++++++++++++ 4 files changed, 97 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 159d4b632..9063bb48e 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6785,6 +6785,42 @@ def delete_story(self, business_connection_id: str, story_id: int) -> bool: """ return apihelper.delete_story(self.token, business_connection_id, story_id) + def gift_premium_subscription( + self, user_id: int, month_count: int, star_count: int, + text: Optional[str]=None, text_parse_mode: Optional[str]=None, + text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + """ + Gifts a Telegram Premium subscription to the given user. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#giftpremiumsubscription + + :param user_id: Unique identifier of the target user who will receive a Telegram Premium subscription + :type user_id: :obj:`int` + + :param month_count: Number of months the Telegram Premium subscription will be active for the user; must be one of 3, 6, or 12 + :type month_count: :obj:`int` + + :param star_count: Number of Telegram Stars to pay for the Telegram Premium subscription; must be 1000 for 3 months, 1500 for 6 months, and 2500 for 12 months + :type star_count: :obj:`int` + + :param text: Text that will be shown along with the service message about the subscription; 0-128 characters + :type text: :obj:`str` + + :param text_parse_mode: Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_parse_mode: :obj:`str` + + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.gift_premium_subscription( + self.token, user_id, month_count, star_count, + text=text, text_parse_mode=text_parse_mode, + text_entities=text_entities + ) + def get_available_gifts(self) -> types.Gifts: """ Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d228023fb..486aea481 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2135,6 +2135,18 @@ def delete_story(token, business_connection_id, story_id): payload = {'business_connection_id': business_connection_id, 'story_id': story_id} return _make_request(token, method_url, params=payload, method='post') +def gift_premium_subscription(token, user_id, month_count, star_count, text=None, text_parse_mode=None, + text_entities=None): + method_url = 'giftPremiumSubscription' + payload = {'user_id': user_id, 'month_count': month_count, 'star_count': star_count} + if text: + payload['text'] = text + if text_parse_mode: + payload['text_parse_mode'] = text_parse_mode + if text_entities: + payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ed6db2be3..0c81b9c99 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8240,6 +8240,42 @@ async def delete_story(self, business_connection_id: str, story_id: int) -> bool :rtype: :obj:`bool` """ return await asyncio_helper.delete_story(self.token, business_connection_id, story_id) + + async def gift_premium_subscription( + self, user_id: int, month_count: int, star_count: int, + text: Optional[str]=None, text_parse_mode: Optional[str]=None, + text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + """ + Gifts a Telegram Premium subscription to the given user. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#giftpremiumsubscription + + :param user_id: Unique identifier of the target user who will receive a Telegram Premium subscription + :type user_id: :obj:`int` + + :param month_count: Number of months the Telegram Premium subscription will be active for the user; must be one of 3, 6, or 12 + :type month_count: :obj:`int` + + :param star_count: Number of Telegram Stars to pay for the Telegram Premium subscription; must be 1000 for 3 months, 1500 for 6 months, and 2500 for 12 months + :type star_count: :obj:`int` + + :param text: Text that will be shown along with the service message about the subscription; 0-128 characters + :type text: :obj:`str` + + :param text_parse_mode: Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_parse_mode: :obj:`str` + + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_entities: :obj:`list` of :class:`telebot.types.MessageEntity` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.gift_premium_subscription( + self.token, user_id, month_count, star_count, + text=text, text_parse_mode=text_parse_mode, + text_entities=text_entities + ) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 9f1e26364..1969b72f8 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2108,6 +2108,19 @@ async def delete_story(token, business_connection_id, story_id): payload = {'business_connection_id': business_connection_id, 'story_id': story_id} return await _process_request(token, method_url, params=payload, method='post') +async def gift_premium_subscription(token, user_id, month_count, star_count, text=None, text_parse_mode=None, + text_entities=None): + method_url = 'giftPremiumSubscription' + payload = {'user_id': user_id, 'month_count': month_count, 'star_count': star_count} + if text: + payload['text'] = text + if text_parse_mode: + payload['text_parse_mode'] = text_parse_mode + if text_entities: + payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + return await _process_request(token, method_url, params=payload, method='post') + + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) From f76cac835a2fb99e54212c828d8deb7a5ad96838 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 22:43:38 +0400 Subject: [PATCH 1755/1808] Added premium_subscription_duration to TransactionPartnerUser --- telebot/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 192bd7df8..98fea4423 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10569,12 +10569,15 @@ class TransactionPartnerUser(TransactionPartner): :param gift: Optional. The gift sent to the user by the bot :type gift: :class:`Gift` + :param premium_subscription_duration: Optional. Number of months the gifted Telegram Premium subscription will be active for; for “premium_purchase” transactions only + :type premium_subscription_duration: :obj:`int` + :return: Instance of the class :rtype: :class:`TransactionPartnerUser` """ def __init__(self, type, user, affiliate=None, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, - subscription_period=None, gift: Optional[Gift] = None, **kwargs): + subscription_period=None, gift: Optional[Gift] = None, premium_subscription_duration: Optional[int] = None, **kwargs): self.type: str = type self.user: User = user self.affiliate: Optional[AffiliateInfo] = affiliate @@ -10582,6 +10585,7 @@ def __init__(self, type, user, affiliate=None, invoice_payload=None, paid_media: self.paid_media: Optional[List[PaidMedia]] = paid_media self.subscription_period: Optional[int] = subscription_period self.gift: Optional[Gift] = gift + self.premium_subscription_duration: Optional[int] = premium_subscription_duration @classmethod def de_json(cls, json_string): From 0bd9133cc3f83b1b60f7db535fabc7a8637dcbae Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 22:45:12 +0400 Subject: [PATCH 1756/1808] Added transaction_type to TransactionPartnerUser --- telebot/types.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 98fea4423..7435a680e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10551,6 +10551,11 @@ class TransactionPartnerUser(TransactionPartner): :param type: Type of the transaction partner, always “user” :type type: :obj:`str` + :param transaction_type: Type of the transaction, currently one of “invoice_payment” for payments via invoices, + “paid_media_payment” for payments for paid media, “gift_purchase” for gifts sent by the bot, “premium_purchase” for Telegram Premium subscriptions + gifted by the bot, “business_account_transfer” for direct transfers from managed business accounts + :type transaction_type: :obj:`str` + :param user: Information about the user :type user: :class:`User` @@ -10577,7 +10582,8 @@ class TransactionPartnerUser(TransactionPartner): """ def __init__(self, type, user, affiliate=None, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, - subscription_period=None, gift: Optional[Gift] = None, premium_subscription_duration: Optional[int] = None, **kwargs): + subscription_period=None, gift: Optional[Gift] = None, premium_subscription_duration: Optional[int] = None, + transaction_type: Optional[str] = None, **kwargs): self.type: str = type self.user: User = user self.affiliate: Optional[AffiliateInfo] = affiliate @@ -10586,6 +10592,7 @@ def __init__(self, type, user, affiliate=None, invoice_payload=None, paid_media: self.subscription_period: Optional[int] = subscription_period self.gift: Optional[Gift] = gift self.premium_subscription_duration: Optional[int] = premium_subscription_duration + self.transaction_type: Optional[str] = transaction_type @classmethod def de_json(cls, json_string): From 950d7c6da012cfee67db1c3651573d8e03b2340c Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 22:50:21 +0400 Subject: [PATCH 1757/1808] Added paid_message_price_changed --- telebot/types.py | 28 ++++++++++++++++++++++++++++ telebot/version.py | 3 --- 2 files changed, 28 insertions(+), 3 deletions(-) delete mode 100644 telebot/version.py diff --git a/telebot/types.py b/telebot/types.py index 7435a680e..6c681ba2b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1181,6 +1181,9 @@ class Message(JsonDeserializable): :param giveaway_completed: Optional. Service message: giveaway completed, without public winners :type giveaway_completed: :class:`telebot.types.GiveawayCompleted` + :param paid_message_price_changed: Optional. Service message: the price for paid messages has changed in the chat + :type paid_message_price_changed: :class:`telebot.types.PaidMessagePriceChanged` + :param video_chat_scheduled: Optional. Service message: video chat scheduled :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` @@ -1443,6 +1446,9 @@ def de_json(cls, json_string): if 'unique_gift' in obj: opts['unique_gift'] = UniqueGiftInfo.de_json(obj['unique_gift']) content_type = 'unique_gift' + if 'paid_message_price_changed' in obj: + opts['paid_message_price_changed'] = PaidMessagePriceChanged.de_json(obj['paid_message_price_changed']) + content_type = 'paid_message_price_changed' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1563,6 +1569,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.message_auto_delete_timer_changed: Optional[MessageAutoDeleteTimerChanged] = None self.gift : Optional[GiftInfo] = None self.unique_gift : Optional[UniqueGiftInfo] = None + self.paid_message_price_changed: Optional[PaidMessagePriceChanged] = None for key in options: @@ -12292,4 +12299,25 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['gift'] = UniqueGift.de_json(obj['gift']) return cls(**obj) + + +class PaidMessagePriceChanged(JsonDeserializable): + """ + Describes a service message about a change in the price of paid messages within a chat. + + Telegram documentation: https://core.telegram.org/bots/api#paidmessagepricechanged + + :param paid_message_star_count: The new number of Telegram Stars that must be paid by non-administrator users of the supergroup chat for each sent message + :type paid_message_star_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`PaidMessagePriceChanged` + """ + def __init__(self, paid_message_star_count: int, **kwargs): + self.paid_message_star_count: int = paid_message_star_count + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) \ No newline at end of file diff --git a/telebot/version.py b/telebot/version.py deleted file mode 100644 index b37f3e662..000000000 --- a/telebot/version.py +++ /dev/null @@ -1,3 +0,0 @@ -# Versions should comply with PEP440. -# This line is parsed in setup.py: -__version__ = '4.26.0' From 9fb4fdd78ba954aca732f90fa6f74969f3a3811a Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 22:52:55 +0400 Subject: [PATCH 1758/1808] Added paid_star_count --- telebot/types.py | 7 +++++++ telebot/version.py | 3 +++ 2 files changed, 10 insertions(+) create mode 100644 telebot/version.py diff --git a/telebot/types.py b/telebot/types.py index 6c681ba2b..33aff6822 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -981,6 +981,9 @@ class Message(JsonDeserializable): anonymous group administrator :type author_signature: :obj:`str` + :param paid_star_count: Optional. The number of Telegram Stars that were paid by the sender of the message to send it + :type paid_star_count: :obj:`int` + :param text: Optional. For text messages, the actual UTF-8 text of the message :type text: :obj:`str` @@ -1449,6 +1452,9 @@ def de_json(cls, json_string): if 'paid_message_price_changed' in obj: opts['paid_message_price_changed'] = PaidMessagePriceChanged.de_json(obj['paid_message_price_changed']) content_type = 'paid_message_price_changed' + if 'paid_star_count' in obj: + opts['paid_star_count'] = obj['paid_star_count'] + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1570,6 +1576,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.gift : Optional[GiftInfo] = None self.unique_gift : Optional[UniqueGiftInfo] = None self.paid_message_price_changed: Optional[PaidMessagePriceChanged] = None + self.paid_star_count: Optional[int] = None for key in options: diff --git a/telebot/version.py b/telebot/version.py new file mode 100644 index 000000000..b37f3e662 --- /dev/null +++ b/telebot/version.py @@ -0,0 +1,3 @@ +# Versions should comply with PEP440. +# This line is parsed in setup.py: +__version__ = '4.26.0' From 0d1e51519c012fcb5f8cc6899f01a1e01fe914fa Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 12 Apr 2025 23:01:31 +0400 Subject: [PATCH 1759/1808] Added set_business_account_profile_photo and remove_business_account_profile_photo --- telebot/__init__.py | 42 +++++++++++++++++++ telebot/apihelper.py | 16 +++++++ telebot/async_telebot.py | 42 +++++++++++++++++++ telebot/asyncio_helper.py | 16 +++++++ telebot/types.py | 87 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 202 insertions(+), 1 deletion(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 9063bb48e..28f479a3b 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6820,6 +6820,48 @@ def gift_premium_subscription( text=text, text_parse_mode=text_parse_mode, text_entities=text_entities ) + + def set_business_account_profile_photo( + self, business_connection_id: str, photo: types.InputProfilePhoto, + is_public: Optional[bool]=None) -> bool: + """ + Changes the profile photo of a managed business account. Requires the can_edit_profile_photo business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountprofilephoto + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param photo: The new profile photo to set + :type photo: :class:`telebot.types.InputProfilePhoto` + + :param is_public: Pass True to set the public photo, which will be visible even if the main photo is hidden by the business account's privacy settings. An account can have only one public photo. + :type is_public: :obj:`bool` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.set_business_account_profile_photo(self.token, business_connection_id, photo, is_public=is_public) + + + def remove_business_account_profile_photo( + self, business_connection_id: str, + is_public: Optional[bool]=None) -> bool: + """ + Removes the current profile photo of a managed business account. Requires the can_edit_profile_photo business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#removebusinessaccountprofilephoto + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param is_public: Pass True to remove the public photo, which is visible even if the main photo is hidden by the business account's privacy settings. After the main photo is removed, the previous profile photo (if present) becomes the main photo. + :type is_public: :obj:`bool` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.remove_business_account_profile_photo(self.token, business_connection_id, is_public=is_public) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 486aea481..d4aba417f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2147,6 +2147,22 @@ def gift_premium_subscription(token, user_id, month_count, star_count, text=None payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) return _make_request(token, method_url, params=payload, method='post') +def set_business_account_profile_photo(token, business_connection_id, photo, is_public=None): + method_url = 'setBusinessAccountProfilePhoto' + payload = {'business_connection_id': business_connection_id} + photo_json, files = photo.convert_input_profile_photo() + payload['photo'] = photo_json + if is_public is not None: + payload['is_public'] = is_public + return _make_request(token, method_url, params=payload, files=files, method='post') + +def remove_business_account_profile_photo(token, business_connection_id, is_public=None): + method_url = 'removeBusinessAccountProfilePhoto' + payload = {'business_connection_id': business_connection_id} + if is_public is not None: + payload['is_public'] = is_public + return _make_request(token, method_url, params=payload, method='post') + def create_new_sticker_set( token, user_id, name, title, stickers, sticker_type=None, needs_repainting=None): method_url = 'createNewStickerSet' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0c81b9c99..1eaa8271e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8241,6 +8241,48 @@ async def delete_story(self, business_connection_id: str, story_id: int) -> bool """ return await asyncio_helper.delete_story(self.token, business_connection_id, story_id) + async def set_business_account_profile_photo( + self, business_connection_id: str, photo: types.InputProfilePhoto, + is_public: Optional[bool]=None) -> bool: + """ + Changes the profile photo of a managed business account. Requires the can_edit_profile_photo business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setbusinessaccountprofilephoto + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param photo: The new profile photo to set + :type photo: :class:`telebot.types.InputProfilePhoto` + + :param is_public: Pass True to set the public photo, which will be visible even if the main photo is hidden by the business account's privacy settings. An account can have only one public photo. + :type is_public: :obj:`bool` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.set_business_account_profile_photo(self.token, business_connection_id, photo, is_public=is_public) + + + async def remove_business_account_profile_photo( + self, business_connection_id: str, + is_public: Optional[bool]=None) -> bool: + """ + Removes the current profile photo of a managed business account. Requires the can_edit_profile_photo business bot right. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#removebusinessaccountprofilephoto + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param is_public: Pass True to remove the public photo, which is visible even if the main photo is hidden by the business account's privacy settings. After the main photo is removed, the previous profile photo (if present) becomes the main photo. + :type is_public: :obj:`bool` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.remove_business_account_profile_photo(self.token, business_connection_id, is_public=is_public) + async def gift_premium_subscription( self, user_id: int, month_count: int, star_count: int, text: Optional[str]=None, text_parse_mode: Optional[str]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 1969b72f8..14203900d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2120,6 +2120,22 @@ async def gift_premium_subscription(token, user_id, month_count, star_count, tex payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) return await _process_request(token, method_url, params=payload, method='post') +async def set_business_account_profile_photo(token, business_connection_id, photo, is_public=None): + method_url = 'setBusinessAccountProfilePhoto' + payload = {'business_connection_id': business_connection_id} + photo_json, files = photo.convert_input_profile_photo() + payload['photo'] = photo_json + if is_public is not None: + payload['is_public'] = is_public + return await _process_request(token, method_url, params=payload, files=files, method='post') + +async def remove_business_account_profile_photo(token, business_connection_id, is_public=None): + method_url = 'removeBusinessAccountProfilePhoto' + payload = {'business_connection_id': business_connection_id} + if is_public is not None: + payload['is_public'] = is_public + return await _process_request(token, method_url, params=payload, method='post') + async def get_available_gifts(token): method_url = 'getAvailableGifts' diff --git a/telebot/types.py b/telebot/types.py index 33aff6822..a97c60c34 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12327,4 +12327,89 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - \ No newline at end of file + + +class InputProfilePhoto(JsonSerializable): + """ + This object describes a profile photo to set. Currently, it can be one of + InputProfilePhotoStatic + InputProfilePhotoAnimated + + Telegram documentation: https://core.telegram.org/bots/api#inputprofilephoto + + :return: Instance of the class + :rtype: :class:`InputProfilePhoto` + """ + +class InputProfilePhotoStatic(InputProfilePhoto): + """ + This object describes a static profile photo to set. + + Telegram documentation: https://core.telegram.org/bots/api#inputprofilephotostatic + + :param type: Type of the profile photo, must be static + :type type: :obj:`str` + + :param photo: The static profile photo. Profile photos can't be reused and can only be uploaded as a new file, so you can pass “attach://” if the photo was uploaded using multipart/form-data under . More information on Sending Files + :type photo: :obj:`str` + + :return: Instance of the class + :rtype: :class:`InputProfilePhotoStatic` + + """ + def __init__(self, photo: InputFile, **kwargs): + self.type: str = "static" + self.photo: InputFile = photo + + self._photo_name = service_utils.generate_random_token() + self._photo_dic = "attach://{}".format(self._photo_name) + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = { + 'type': self.type, + 'photo': self._photo_dic + } + return data + def convert_input_profile_photo(self): + return self.to_json(), {self._photo_name: self.photo} + + +class InputProfilePhotoAnimated(InputProfilePhoto): + """ + This object describes an animated profile photo to set. + + Telegram documentation: https://core.telegram.org/bots/api#inputprofilephotoanimated + + :param type: Type of the profile photo, must be animated + :type type: :obj:`str` + + :param animation: The animated profile photo. Profile photos can't be reused and can only be uploaded as a new file, so you can pass “attach://” if the photo was uploaded using multipart/form-data under . More information on Sending Files + :type animation: :obj:`str` + + :param main_frame_timestamp: Optional. Timestamp in seconds of the frame that will be used as the static profile photo. Defaults to 0.0. + :type main_frame_timestamp: :obj:`float` + + :return: Instance of the class + :rtype: :class:`InputProfilePhotoAnimated` + + """ + def __init__(self, animation: InputFile, main_frame_timestamp: Optional[float] = None, **kwargs): + self.type: str = "animated" + self.animation: InputFile = animation + self._animation_name = service_utils.generate_random_token() + self._animation_dic = "attach://{}".format(self._animation_name) + self.main_frame_timestamp: Optional[float] = main_frame_timestamp + def to_json(self): + return json.dumps(self.to_dict()) + def to_dict(self): + data = { + 'type': self.type, + 'animation': self._animation_dic + } + if self.main_frame_timestamp is not None: + data['main_frame_timestamp'] = self.main_frame_timestamp + return data + def convert_input_profile_photo(self): + return self.to_json(), {self._animation_name: self.animation} From d63b07af0bd03ccafca188559bfdb1204dcd3513 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 13 Apr 2025 20:09:44 +0400 Subject: [PATCH 1760/1808] Fix issues with posting stories --- telebot/apihelper.py | 4 ++-- telebot/types.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d4aba417f..0e2ce593e 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2105,7 +2105,7 @@ def post_story(token, business_connection_id, content, active_period, caption=No if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if areas: - payload['areas'] = json.dumps([area.to_json() for area in areas]) + payload['areas'] = json.dumps([area.to_dict() for area in areas]) if post_to_chat_page is not None: payload['post_to_chat_page'] = post_to_chat_page if protect_content is not None: @@ -2127,7 +2127,7 @@ def edit_story(token, business_connection_id, story_id, content, caption=None, p if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if areas: - payload['areas'] = json.dumps([area.to_json() for area in areas]) + payload['areas'] = json.dumps([area.to_dict() for area in areas]) return _make_request(token, method_url, params=payload, files=files, method='post') def delete_story(token, business_connection_id, story_id): diff --git a/telebot/types.py b/telebot/types.py index a97c60c34..2939c9e5f 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12054,7 +12054,7 @@ def to_dict(self): 'type': self.type, 'latitude': self.latitude, 'longitude': self.longitude, - 'address': self.address.to_json() + 'address': self.address.to_dict() } return data @@ -12090,7 +12090,7 @@ def to_json(self): def to_dict(self): data = { 'type': self.type, - 'reaction_type': self.reaction_type.to_json() + 'reaction_type': self.reaction_type.to_dict() } if self.is_dark is not None: data['is_dark'] = self.is_dark @@ -12213,8 +12213,8 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): data = { - 'position': self.position.to_json(), - 'type': self.type.to_json() + 'position': self.position.to_dict(), + 'type': self.type.to_dict() } return data From 49684b5f9ca4391426c95bf22228c1cc3882f028 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 13 Apr 2025 20:15:50 +0400 Subject: [PATCH 1761/1808] Fix stories for async --- telebot/asyncio_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 14203900d..67990da54 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2078,7 +2078,7 @@ async def post_story(token, business_connection_id, content, active_period, capt if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if areas: - payload['areas'] = json.dumps([area.to_json() for area in areas]) + payload['areas'] = json.dumps([area.to_dict() for area in areas]) if post_to_chat_page is not None: payload['post_to_chat_page'] = post_to_chat_page if protect_content is not None: @@ -2100,7 +2100,7 @@ async def edit_story(token, business_connection_id, story_id, content, caption=N if caption_entities: payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities)) if areas: - payload['areas'] = json.dumps([area.to_json() for area in areas]) + payload['areas'] = json.dumps([area.to_dict() for area in areas]) return await _process_request(token, method_url, params=payload, files=files, method='post') async def delete_story(token, business_connection_id, story_id): From 7007eca960ca5f463d59e32e39096ff90b32e806 Mon Sep 17 00:00:00 2001 From: "g.zinovev" Date: Mon, 14 Apr 2025 01:02:31 +0300 Subject: [PATCH 1762/1808] Fix: Add thumbnail support for edit_message_media --- telebot/types.py | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index af0d4cf74..2cd6b8066 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6695,12 +6695,20 @@ class InputMedia(Dictionaryable, JsonSerializable): * :class:`InputMediaPhoto` * :class:`InputMediaVideo` """ - def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None): + def __init__(self, type, media, caption=None, parse_mode=None, caption_entities=None, thumbnail=None): self.type: str = type self.media: str = media self.caption: Optional[str] = caption self.parse_mode: Optional[str] = parse_mode self.caption_entities: Optional[List[MessageEntity]] = caption_entities + self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + + if isinstance(self.thumbnail, InputFile): + self._thumbnail_name = service_utils.generate_random_token() + self._thumbnail_dic = 'attach://{0}'.format(self._thumbnail_name) + else: + self._thumbnail_name = '' + self._thumbnail_dic = self.thumbnail if service_utils.is_string(self.media): self._media_name = '' @@ -6713,7 +6721,7 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - json_dict = {'type': self.type, 'media': self._media_dic} + json_dict = {'type': self.type, 'media': self._media_dic, 'thumbnail': self._thumbnail_dic} if self.caption: json_dict['caption'] = self.caption if self.parse_mode: @@ -6729,7 +6737,7 @@ def convert_input_media(self): if service_utils.is_string(self.media): return self.to_json(), None - return self.to_json(), {self._media_name: self.media} + return self.to_json(), {self._media_name: self.media, self._thumbnail_name: self.thumbnail} class InputMediaPhoto(InputMedia): @@ -6850,8 +6858,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, show_caption_above_media: Optional[bool] = None, cover: Optional[Union[str, InputFile]] = None, start_timestamp: Optional[int] = None): super(InputMediaVideo, self).__init__( - type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + type="video", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, thumbnail=thumbnail) self.width: Optional[int] = width self.height: Optional[int] = height self.duration: Optional[int] = duration @@ -6868,8 +6875,6 @@ def thumb(self) -> Optional[Union[str, Any]]: def to_dict(self): ret = super(InputMediaVideo, self).to_dict() - if self.thumbnail: - ret['thumbnail'] = self.thumbnail if self.width: ret['width'] = self.width if self.height: @@ -6943,8 +6948,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, height: Optional[int] = None, duration: Optional[int] = None, has_spoiler: Optional[bool] = None, show_caption_above_media: Optional[bool] = None): super(InputMediaAnimation, self).__init__( - type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + type="animation", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, thumbnail=thumbnail) self.width: Optional[int] = width self.height: Optional[int] = height self.duration: Optional[int] = duration @@ -6959,8 +6963,6 @@ def thumb(self) -> Optional[Union[str, Any]]: def to_dict(self): ret = super(InputMediaAnimation, self).to_dict() - if self.thumbnail: - ret['thumbnail'] = self.thumbnail if self.width: ret['width'] = self.width if self.height: @@ -7020,8 +7022,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, caption_entities: Optional[List[MessageEntity]] = None, duration: Optional[int] = None, performer: Optional[str] = None, title: Optional[str] = None): super(InputMediaAudio, self).__init__( - type="audio", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + type="audio", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, thumbnail=thumbnail) self.duration: Optional[int] = duration self.performer: Optional[str] = performer self.title: Optional[str] = title @@ -7033,8 +7034,6 @@ def thumb(self) -> Optional[Union[str, Any]]: def to_dict(self): ret = super(InputMediaAudio, self).to_dict() - if self.thumbnail: - ret['thumbnail'] = self.thumbnail if self.duration: ret['duration'] = self.duration if self.performer: @@ -7085,8 +7084,7 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[Union[str, caption_entities: Optional[List[MessageEntity]] = None, disable_content_type_detection: Optional[bool] = None): super(InputMediaDocument, self).__init__( - type="document", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) - self.thumbnail: Optional[Union[str, InputFile]] = thumbnail + type="document", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities, thumbnail=thumbnail) self.disable_content_type_detection: Optional[bool] = disable_content_type_detection @property @@ -7096,8 +7094,6 @@ def thumb(self) -> Optional[Union[str, Any]]: def to_dict(self): ret = super(InputMediaDocument, self).to_dict() - if self.thumbnail: - ret['thumbnail'] = self.thumbnail if self.disable_content_type_detection is not None: ret['disable_content_type_detection'] = self.disable_content_type_detection return ret From 533b52c79376a9143d09a090a329f6d07835f199 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 19 Apr 2025 21:13:36 +0400 Subject: [PATCH 1763/1808] Fix issues related with Bot api 9.0 --- telebot/__init__.py | 14 +++--- telebot/apihelper.py | 15 ++++-- telebot/async_telebot.py | 14 +++--- telebot/asyncio_helper.py | 14 ++++-- telebot/types.py | 96 +++++++++++++++++++++++---------------- 5 files changed, 91 insertions(+), 62 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 28f479a3b..5a3064c48 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6364,7 +6364,7 @@ def remove_user_verification(self, user_id: int) -> bool: return apihelper.remove_user_verification(self.token, user_id) - def remove_chat_verification(self, chat_id: Union[int, str]) -> bool: + def remove_chat_verification(self, chat_id: int) -> bool: """ Removes verification from a chat that is currently verified on behalf of the organization represented by the bot. Returns True on success. @@ -6433,7 +6433,7 @@ def set_business_account_name(self, business_connection_id: str, first_name: str :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.set_business_account_name(self.token, business_connection_id, first_name, last_name) + return apihelper.set_business_account_name(self.token, business_connection_id, first_name, last_name=last_name) def set_business_account_username(self, business_connection_id: str, username: Optional[str]=None) -> bool: """ @@ -6451,7 +6451,7 @@ def set_business_account_username(self, business_connection_id: str, username: O :rtype: :obj:`bool` """ - return apihelper.set_business_account_username(self.token, business_connection_id, username) + return apihelper.set_business_account_username(self.token, business_connection_id, username=username) def set_business_account_bio(self, business_connection_id: str, bio: Optional[str]=None) -> bool: """ @@ -6468,7 +6468,7 @@ def set_business_account_bio(self, business_connection_id: str, bio: Optional[st :return: Returns True on success. :rtype: :obj:`bool` """ - return apihelper.set_business_account_bio(self.token, business_connection_id, bio) + return apihelper.set_business_account_bio(self.token, business_connection_id, bio=bio) def set_business_account_gift_settings( self, business_connection_id: str, show_gift_button: bool, accepted_gift_types: types.AcceptedGiftTypes) -> bool: @@ -6635,7 +6635,7 @@ def upgrade_gift( def transfer_gift( self, business_connection_id: str, owned_gift_id: str, - new_owner_chat_id: Union[int, str], + new_owner_chat_id: int, star_count: Optional[int]=None) -> bool: """ Transfers an owned unique gift to another user. Requires the can_transfer_and_upgrade_gifts business bot right. @@ -6650,7 +6650,7 @@ def transfer_gift( :type owned_gift_id: :obj:`str` :param new_owner_chat_id: Unique identifier of the chat which will own the gift. The chat must be active in the last 24 hours. - :type new_owner_chat_id: :obj:`int` | :obj:`str` + :type new_owner_chat_id: :obj:`int` :param star_count: The amount of Telegram Stars that will be paid for the transfer from the business account balance. If positive, then the can_transfer_stars business bot right is required. @@ -6661,7 +6661,7 @@ def transfer_gift( """ return apihelper.transfer_gift( self.token, business_connection_id, owned_gift_id, - new_owner_chat_id=new_owner_chat_id, + new_owner_chat_id, star_count=star_count ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0e2ce593e..460c3c4a6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2013,21 +2013,26 @@ def set_business_account_name(token, business_connection_id, first_name, last_na return _make_request(token, method_url, params=payload, method='post') -def set_business_account_username(token, business_connection_id, username): +def set_business_account_username(token, business_connection_id, username=None): method_url = 'setBusinessAccountUsername' - payload = {'business_connection_id': business_connection_id, 'username': username} + payload = {'business_connection_id': business_connection_id} + if username: + payload['username'] = username return _make_request(token, method_url, params=payload, method='post') -def set_business_account_bio(token, business_connection_id, bio): +def set_business_account_bio(token, business_connection_id, bio=None): method_url = 'setBusinessAccountBio' - payload = {'business_connection_id': business_connection_id, 'bio': bio} + payload = {'business_connection_id': business_connection_id} + if bio: + payload['bio'] = bio return _make_request(token, method_url, params=payload, method='post') def set_business_account_gift_settings(token, business_connection_id, show_gift_button, accepted_gift_types): method_url = 'setBusinessAccountGiftSettings' - payload = {'business_connection_id': business_connection_id, 'show_gift_button': show_gift_button, 'accepted_gift_types': json.dumps(accepted_gift_types)} + payload = {'business_connection_id': business_connection_id, 'show_gift_button': show_gift_button, + 'accepted_gift_types': accepted_gift_types.to_json()} return _make_request(token, method_url, params=payload, method='post') def set_sticker_emoji_list(token, sticker, emoji_list): diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 1eaa8271e..26e34934b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7821,7 +7821,7 @@ async def remove_user_verification(self, user_id: int) -> bool: """ return await asyncio_helper.remove_user_verification(self.token, user_id) - async def remove_chat_verification(self, chat_id: Union[int, str]) -> bool: + async def remove_chat_verification(self, chat_id: int) -> bool: """ Removes verification from a chat that is currently verified on behalf of the organization represented by the bot. Returns True on success. @@ -7890,7 +7890,7 @@ async def set_business_account_name(self, business_connection_id: str, first_nam :return: Returns True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.set_business_account_name(self.token, business_connection_id, first_name, last_name) + return await asyncio_helper.set_business_account_name(self.token, business_connection_id, first_name, last_name=last_name) async def set_business_account_username(self, business_connection_id: str, username: Optional[str]=None) -> bool: """ @@ -7908,7 +7908,7 @@ async def set_business_account_username(self, business_connection_id: str, usern :rtype: :obj:`bool` """ - return await asyncio_helper.set_business_account_username(self.token, business_connection_id, username) + return await asyncio_helper.set_business_account_username(self.token, business_connection_id, username=username) async def set_business_account_bio(self, business_connection_id: str, bio: Optional[str]=None) -> bool: """ @@ -7925,7 +7925,7 @@ async def set_business_account_bio(self, business_connection_id: str, bio: Optio :return: Returns True on success. :rtype: :obj:`bool` """ - return await asyncio_helper.set_business_account_bio(self.token, business_connection_id, bio) + return await asyncio_helper.set_business_account_bio(self.token, business_connection_id, bio=bio) async def set_business_account_gift_settings( self, business_connection_id: str, show_gift_button: bool, accepted_gift_types: types.AcceptedGiftTypes) -> bool: @@ -8091,7 +8091,7 @@ async def upgrade_gift( async def transfer_gift( self, business_connection_id: str, owned_gift_id: str, - new_owner_chat_id: Union[int, str], + new_owner_chat_id: int, star_count: Optional[int]=None) -> bool: """ Transfers an owned unique gift to another user. Requires the can_transfer_and_upgrade_gifts business bot right. @@ -8106,7 +8106,7 @@ async def transfer_gift( :type owned_gift_id: :obj:`str` :param new_owner_chat_id: Unique identifier of the chat which will own the gift. The chat must be active in the last 24 hours. - :type new_owner_chat_id: :obj:`int` | :obj:`str` + :type new_owner_chat_id: :obj:`int` :param star_count: The amount of Telegram Stars that will be paid for the transfer from the business account balance. If positive, then the can_transfer_stars business bot right is required. @@ -8117,7 +8117,7 @@ async def transfer_gift( """ return await asyncio_helper.transfer_gift( self.token, business_connection_id, owned_gift_id, - new_owner_chat_id=new_owner_chat_id, + new_owner_chat_id, star_count=star_count ) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 67990da54..c582aa3e4 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1993,20 +1993,24 @@ async def set_business_account_name(token, business_connection_id, first_name, l return await _process_request(token, method_url, params=payload, method='post') -async def set_business_account_username(token, business_connection_id, username): +async def set_business_account_username(token, business_connection_id, username=None): method_url = 'setBusinessAccountUsername' - payload = {'business_connection_id': business_connection_id, 'username': username} + payload = {'business_connection_id': business_connection_id} + if username is not None: + payload['username'] = username return await _process_request(token, method_url, params=payload, method='post') -async def set_business_account_bio(token, business_connection_id, bio): +async def set_business_account_bio(token, business_connection_id, bio=None): method_url = 'setBusinessAccountBio' - payload = {'business_connection_id': business_connection_id, 'bio': bio} + payload = {'business_connection_id': business_connection_id} + if bio: + payload['bio'] = bio return await _process_request(token, method_url, params=payload, method='post') async def set_business_account_gift_settings(token, business_connection_id, show_gift_button, accepted_gift_types): method_url = 'setBusinessAccountGiftSettings' - payload = {'business_connection_id': business_connection_id, 'show_gift_button': show_gift_button, 'accepted_gift_types': json.dumps(accepted_gift_types)} + payload = {'business_connection_id': business_connection_id, 'show_gift_button': show_gift_button, 'accepted_gift_types': accepted_gift_types.to_json()} return await _process_request(token, method_url, params=payload, method='post') async def get_business_account_star_balance(token, business_connection_id): diff --git a/telebot/types.py b/telebot/types.py index 2939c9e5f..ff24374a8 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -688,7 +688,7 @@ class ChatFullInfo(JsonDeserializable): :param permissions: Optional. Default chat member permissions, for groups and supergroups. Returned only in getChat. :type permissions: :class:`telebot.types.ChatPermissions` - :param can_send_gift: Optional. True, if gifts can be sent to the chat + :param can_send_gift: deprecated, use accepted_gift_types instead. :type can_send_gift: :obj:`bool` :param accepted_gift_types: Information about types of gifts that are accepted by the chat or by the corresponding user for private chats @@ -829,10 +829,17 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.birthdate: Optional[Birthdate] = birthdate self.can_send_paid_media: Optional[bool] = can_send_paid_media self.accepted_gift_types: AcceptedGiftTypes = accepted_gift_types - self.can_send_gift: Optional[bool] = None - if self.accepted_gift_types is not None: # not optional but still - # skip premium subscription? - self.can_send_gift: Optional[bool] = any([self.accepted_gift_types.unique_gifts, self.accepted_gift_types.unlimited_gifts, self.accepted_gift_types.limited_gifts]) + @property + def can_send_gift(self) -> bool: + """ + Deprecated. Use `accepted_gift_types` instead. + + :return: True if the chat can send gifts + """ + log_deprecation_warning("The parameter 'can_send_gift' is deprecated. Use 'accepted_gift_types' instead.") + if self.accepted_gift_types is not None: # just in case + return any([self.accepted_gift_types.unique_gifts, self.accepted_gift_types.unlimited_gifts, self.accepted_gift_types.limited_gifts]) + return False class Chat(ChatFullInfo): @@ -9774,7 +9781,7 @@ class BusinessConnection(JsonDeserializable): :param date: Date the connection was established in Unix time :type date: :obj:`int` - :param can_reply: Deprecated, use :attr:`can_reply` instead. True, if the bot can reply to messages from the business account + :param can_reply: Deprecated, use :attr:`rights` instead. True, if the bot can reply to messages from the business account :type can_reply: :obj:`bool` :param rights: Optional. Rights of the business bot @@ -9792,6 +9799,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) + obj['rights'] = BusinessBotRights.de_json(obj.get('rights')) return cls(**obj) def __init__(self, id, user, user_chat_id, date, can_reply, is_enabled, @@ -9801,10 +9809,16 @@ def __init__(self, id, user, user_chat_id, date, can_reply, is_enabled, self.user_chat_id: int = user_chat_id self.date: int = date self.rights: Optional[BusinessBotRights] = rights - # Deprecated, use self.rights instead - self.can_reply: Optional[bool] = can_reply self.is_enabled: bool = is_enabled + @property + def can_reply(self) -> bool: + """ + Deprecated, use :attr:`rights` instead. + """ + log_deprecation_warning('The field "can_reply" is deprecated, use "rights" instead') + return self.rights is not None and self.rights.can_reply + class BusinessMessagesDeleted(JsonDeserializable): @@ -11426,8 +11440,8 @@ class AcceptedGiftTypes(JsonDeserializable, JsonSerializable): :return: Instance of the class :rtype: :class:`AcceptedGiftTypes` """ - def __init__(self, unlimited_gifts: Optional[bool] = None, limited_gifts: Optional[bool] = None, - unique_gifts: Optional[bool] = None, premium_subscription: Optional[bool] = None, **kwargs): + def __init__(self, unlimited_gifts: Optional[bool], limited_gifts: Optional[bool], + unique_gifts: Optional[bool], premium_subscription: Optional[bool], **kwargs): self.unlimited_gifts: Optional[bool] = unlimited_gifts self.limited_gifts: Optional[bool] = limited_gifts self.unique_gifts: Optional[bool] = unique_gifts @@ -11437,15 +11451,12 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - data = {} - if self.unlimited_gifts is not None: - data['unlimited_gifts'] = self.unlimited_gifts - if self.limited_gifts is not None: - data['limited_gifts'] = self.limited_gifts - if self.unique_gifts is not None: - data['unique_gifts'] = self.unique_gifts - if self.premium_subscription is not None: - data['premium_subscription'] = self.premium_subscription + data = { + 'unlimited_gifts': self.unlimited_gifts, + 'limited_gifts': self.limited_gifts, + 'unique_gifts': self.unique_gifts, + 'premium_subscription': self.premium_subscription + } return data @classmethod def de_json(cls, json_string): @@ -11479,7 +11490,7 @@ def de_json(cls, json_string): return cls(**obj) -class OwnedGift(JsonDeserializable): +class OwnedGift(JsonDeserializable, ABC): """ This object describes a gift received and owned by a user or a chat. Currently, it can be one of OwnedGiftRegular @@ -11487,6 +11498,10 @@ class OwnedGift(JsonDeserializable): Telegram documentation: https://core.telegram.org/bots/api#ownedgift """ + + def __init__(self, type, **kwargs): + self.type: str = type + self.gift: Union[Gift, UniqueGift] = None @classmethod @@ -11549,7 +11564,7 @@ class OwnedGiftRegular(OwnedGift): def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=None, text=None, entities=None, is_private=None, is_saved=None, can_be_upgraded=None, was_refunded=None, convert_star_count=None, prepaid_upgrade_star_count=None, **kwargs): - self.type: str = type + super().__init__(type=type) self.gift: Gift = gift self.owned_gift_id: Optional[str] = owned_gift_id self.sender_user: Optional[User] = sender_user @@ -11608,7 +11623,7 @@ class OwnedGiftUnique(OwnedGift): """ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=None, is_saved=None, can_be_transferred=None, transfer_star_count=None, **kwargs): - self.type: str = type + super().__init__(type=type) self.gift: UniqueGift = gift self.owned_gift_id: Optional[str] = owned_gift_id self.sender_user: Optional[User] = sender_user @@ -11823,7 +11838,7 @@ def de_json(cls, json_string): obj['colors'] = UniqueGiftBackdropColors.de_json(obj['colors']) return cls(**obj) -class InputStoryContent(JsonSerializable): +class InputStoryContent(JsonSerializable, ABC): """ This object describes the content of a story to post. Currently, it can be one of InputStoryContentPhoto @@ -11832,6 +11847,8 @@ class InputStoryContent(JsonSerializable): Telegram documentation: https://core.telegram.org/bots/api#inputstorycontent """ + def __init__(self, type: str, **kwargs): + self.type: str = type class InputStoryContentPhoto(InputStoryContent): @@ -11841,13 +11858,13 @@ class InputStoryContentPhoto(InputStoryContent): Telegram documentation: https://core.telegram.org/bots/api#inputstorycontentphoto :param photo: The photo to post as a story. The photo must be of the size 1080x1920 and must not exceed 10 MB. The photo can't be reused and can only be uploaded as a new file, so you can pass “attach://” if the photo was uploaded using multipart/form-data under . More information on Sending Files - :type photo: :obj:`str` + :type photo: :class:`telebot.types.InputFile` :return: Instance of the class :rtype: :class:`InputStoryContentPhoto` """ def __init__(self, photo: InputFile, **kwargs): - self.type: str = "photo" + super().__init__(type="photo") self.photo: InputFile = photo self._photo_name = service_utils.generate_random_token() self._photo_dic = "attach://{}".format(self._photo_name) @@ -11873,7 +11890,7 @@ class InputStoryContentVideo(InputStoryContent): Telegram documentation: https://core.telegram.org/bots/api#inputstorycontentvideo :param video: The video to post as a story. The video must be of the size 720x1280, streamable, encoded with H.265 codec, with key frames added each second in the MPEG4 format, and must not exceed 30 MB. The video can't be reused and can only be uploaded as a new file, so you can pass “attach://” if the video was uploaded using multipart/form-data under . More information on Sending Files - :type video: :obj:`str` + :type video: :class:`telebot.types.InputFile` :param duration: Optional. Precise duration of the video in seconds; 0-60 :type duration: :obj:`float` @@ -11889,7 +11906,7 @@ class InputStoryContentVideo(InputStoryContent): """ def __init__(self, video: InputFile, duration: Optional[float] = None, cover_frame_timestamp: Optional[float] = None, is_animation: Optional[bool] = None, **kwargs): - self.type: str = "video" + super().__init__(type="video") self.video: InputFile = video self._video_name = service_utils.generate_random_token() self._video_dic = "attach://{}".format(self._video_name) @@ -11997,7 +12014,7 @@ def to_dict(self): data = { 'country_code': self.country_code } - if self.state is not None: + if self.state: data['state'] = self.state if self.city is not None: data['city'] = self.city @@ -12005,7 +12022,7 @@ def to_dict(self): data['street'] = self.street return data -class StoryAreaType(JsonSerializable): +class StoryAreaType(JsonSerializable, ABC): """ Describes the type of a clickable area on a story. Currently, it can be one of StoryAreaTypeLocation @@ -12019,6 +12036,8 @@ class StoryAreaType(JsonSerializable): :return: Instance of the class :rtype: :class:`StoryArea` """ + def __init__(self, type: str, **kwargs): + self.type: str = type class StoryAreaTypeLocation(StoryAreaType): @@ -12036,17 +12055,17 @@ class StoryAreaTypeLocation(StoryAreaType): :param longitude: Location longitude in degrees :type longitude: :obj:`float` - :param address: Location address + :param address: Optional, Location address :type address: :class:`LocationAddress` :return: Instance of the class :rtype: :class:`StoryAreaTypeLocation` """ - def __init__(self,latitude: float, longitude: float, address: LocationAddress, **kwargs): - self.type: str = "location" + def __init__(self,latitude: float, longitude: float, address: LocationAddress = None, **kwargs): + super().__init__(type="location") self.latitude: float = latitude self.longitude: float = longitude - self.address: LocationAddress = address + self.address: Optional[LocationAddress] = address def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): @@ -12054,8 +12073,9 @@ def to_dict(self): 'type': self.type, 'latitude': self.latitude, 'longitude': self.longitude, - 'address': self.address.to_dict() } + if self.address is not None: + data['address'] = self.address.to_dict() return data @@ -12081,7 +12101,7 @@ class StoryAreaTypeSuggestedReaction(StoryAreaType): :rtype: :class:`StoryAreaTypeSuggestedReaction` """ def __init__(self, reaction_type: ReactionType, is_dark: Optional[bool] = None, is_flipped: Optional[bool] = None, **kwargs): - self.type: str = "suggested_reaction" + super().__init__(type="suggested_reaction") self.reaction_type: ReactionType = reaction_type self.is_dark: Optional[bool] = is_dark self.is_flipped: Optional[bool] = is_flipped @@ -12114,7 +12134,7 @@ class StoryAreaTypeLink(StoryAreaType): :rtype: :class:`StoryAreaTypeLink` """ def __init__(self, url: str, **kwargs): - self.type: str = "link" + super().__init__(type="link") self.url: str = url def to_json(self): return json.dumps(self.to_dict()) @@ -12147,7 +12167,7 @@ class StoryAreaTypeWeather(StoryAreaType): :rtype: :class:`StoryAreaTypeWeather` """ def __init__(self, temperature: float, emoji: str, background_color: int, **kwargs): - self.type: str = "weather" + super().__init__(type="weather") self.temperature: float = temperature self.emoji: str = emoji self.background_color: int = background_color @@ -12178,7 +12198,7 @@ class StoryAreaTypeUniqueGift(StoryAreaType): :rtype: :class:`StoryAreaTypeUniqueGift` """ def __init__(self, name: str, **kwargs): - self.type: str = "unique_gift" + super().__init__(type="unique_gift") self.name: str = name def to_json(self): return json.dumps(self.to_dict()) From d4f5ead9c10ba73fead33f77df9474823ac0a0c3 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 20 Apr 2025 01:18:33 +0400 Subject: [PATCH 1764/1808] Fix typehints --- telebot/types.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ff24374a8..817688351 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11440,12 +11440,12 @@ class AcceptedGiftTypes(JsonDeserializable, JsonSerializable): :return: Instance of the class :rtype: :class:`AcceptedGiftTypes` """ - def __init__(self, unlimited_gifts: Optional[bool], limited_gifts: Optional[bool], - unique_gifts: Optional[bool], premium_subscription: Optional[bool], **kwargs): - self.unlimited_gifts: Optional[bool] = unlimited_gifts - self.limited_gifts: Optional[bool] = limited_gifts - self.unique_gifts: Optional[bool] = unique_gifts - self.premium_subscription: Optional[bool] = premium_subscription + def __init__(self, unlimited_gifts: bool, limited_gifts: bool, + unique_gifts: bool, premium_subscription: bool, **kwargs): + self.unlimited_gifts: bool = unlimited_gifts + self.limited_gifts: bool = limited_gifts + self.unique_gifts: bool = unique_gifts + self.premium_subscription: bool = premium_subscription def to_json(self): return json.dumps(self.to_dict()) From f6906ecf6415540cb8f02d9ebfb32be924b4c45f Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sun, 20 Apr 2025 00:44:59 +0300 Subject: [PATCH 1765/1808] Bump version to 4.27 --- 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 0ce35e8db..000225031 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.26.0' +release = '4.27.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 471aaa45b..e7bf04f85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.26.0" +version = "4.27.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 b37f3e662..e0fe9ac48 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.26.0' +__version__ = '4.27.0' From 93ce320302872f66cdc805834bfd02c24b2c65bb Mon Sep 17 00:00:00 2001 From: Badiboy Date: Mon, 21 Apr 2025 11:00:04 +0300 Subject: [PATCH 1766/1808] Fix states functions with new parameters ... and some minor issues. --- telebot/__init__.py | 40 ++++++++++++++----------- telebot/async_telebot.py | 40 +++++++++++++++---------- telebot/asyncio_storage/base_storage.py | 36 ++++++++++++++++++---- telebot/states/asyncio/context.py | 4 +-- telebot/storage/base_storage.py | 36 ++++++++++++++++++---- 5 files changed, 109 insertions(+), 47 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 5a3064c48..8b651a5b0 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5564,7 +5564,7 @@ def create_invoice_link(self, # noinspection PyShadowingBuiltins def send_poll( - self, chat_id: Union[int, str], question: str, options: List[types.InputPollOption], + self, chat_id: Union[int, str], question: str, options: List[Union[str, types.InputPollOption]], is_anonymous: Optional[bool]=None, type: Optional[str]=None, allows_multiple_answers: Optional[bool]=None, correct_option_id: Optional[int]=None, @@ -5600,7 +5600,7 @@ def send_poll( :type question: :obj:`str` :param options: A JSON-serialized list of 2-10 answer options - :type options: :obj:`list` of :obj:`InputPollOption` + :type options: :obj:`list` of :obj:`InputPollOption` | :obj:`list` of :obj:`str` :param is_anonymous: True, if the poll needs to be anonymous, defaults to True :type is_anonymous: :obj:`bool` @@ -7578,9 +7578,8 @@ def set_state(self, user_id: int, state: Union[str, State], chat_id: Optional[in chat_id = user_id if bot_id is None: bot_id = self.bot_id - return self.current_states.set_state( - chat_id=chat_id, user_id=user_id, state=state, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return self.current_states.set_state(chat_id, user_id, state, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) def reset_data(self, user_id: int, chat_id: Optional[int]=None, @@ -7611,8 +7610,8 @@ def reset_data(self, user_id: int, chat_id: Optional[int]=None, chat_id = user_id if bot_id is None: bot_id = self.bot_id - return self.current_states.reset_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return self.current_states.reset_data(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, @@ -7630,6 +7629,15 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_conne :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier, defaults to current bot id + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: True on success :rtype: :obj:`bool` """ @@ -7637,8 +7645,8 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_conne chat_id = user_id if bot_id is None: bot_id = self.bot_id - return self.current_states.delete_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return self.current_states.delete_state(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, @@ -7668,9 +7676,8 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn chat_id = user_id if bot_id is None: bot_id = self.bot_id - return self.current_states.get_interactive_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, - message_thread_id=message_thread_id) + return self.current_states.get_interactive_data(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) def get_state(self, user_id: int, chat_id: Optional[int]=None, @@ -7707,8 +7714,8 @@ def get_state(self, user_id: int, chat_id: Optional[int]=None, chat_id = user_id if bot_id is None: bot_id = self.bot_id - return self.current_states.get_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return self.current_states.get_state(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) def add_data(self, user_id: int, chat_id: Optional[int]=None, @@ -7742,8 +7749,8 @@ def add_data(self, user_id: int, chat_id: Optional[int]=None, if bot_id is None: bot_id = self.bot_id for key, value in kwargs.items(): - self.current_states.set_data(chat_id=chat_id, user_id=user_id, key=key, value=value, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + self.current_states.set_data(chat_id, user_id, key, value, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) def register_next_step_handler_by_chat_id( @@ -9848,4 +9855,3 @@ def _notify_command_handlers(self, handlers, new_messages, update_type): handlers=handlers, middlewares=middlewares, update_type=update_type) - diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 26e34934b..8c021b9a7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8,6 +8,7 @@ import sys # this imports are used to avoid circular import error +# noinspection PyUnresolvedReferences import telebot.util import telebot.types @@ -7008,7 +7009,7 @@ async def create_invoice_link(self, # noinspection PyShadowingBuiltins async def send_poll( - self, chat_id: Union[int, str], question: str, options: List[types.InputPollOption], + self, chat_id: Union[int, str], question: str, options: List[Union[str, types.InputPollOption]], is_anonymous: Optional[bool]=None, type: Optional[str]=None, allows_multiple_answers: Optional[bool]=None, correct_option_id: Optional[int]=None, @@ -7044,7 +7045,7 @@ async def send_poll( :type question: :obj:`str` :param options: A JSON-serialized list of 2-10 answer options - :type options: :obj:`list` of :obj:`InputPollOption` + :type options: :obj:`list` of :obj:`InputPollOption` | :obj:`list` of :obj:`str` :param is_anonymous: True, if the poll needs to be anonymous, defaults to True :type is_anonymous: :obj:`bool` @@ -8828,9 +8829,8 @@ async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: chat_id = user_id if bot_id is None: bot_id = self.bot_id - return await self.current_states.set_state( - chat_id=chat_id, user_id=user_id, state=state, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return await self.current_states.set_state(chat_id, user_id, state, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) async def reset_data(self, user_id: int, chat_id: Optional[int]=None, @@ -8861,8 +8861,8 @@ async def reset_data(self, user_id: int, chat_id: Optional[int]=None, chat_id = user_id if bot_id is None: bot_id = self.bot_id - return await self.current_states.reset_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return await self.current_states.reset_data(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) async def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, @@ -8876,14 +8876,23 @@ async def delete_state(self, user_id: int, chat_id: Optional[int]=None, business :param chat_id: Chat's identifier :type chat_id: :obj:`int` + :param bot_id: Bot's identifier, defaults to current bot id + :type bot_id: :obj:`int` + + :param business_connection_id: Business identifier + :type business_connection_id: :obj:`str` + + :param message_thread_id: Identifier of the message thread + :type message_thread_id: :obj:`int` + :return: None """ if chat_id is None: chat_id = user_id if bot_id is None: bot_id = self.bot_id - return await self.current_states.delete_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return await self.current_states.delete_state(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, @@ -8913,9 +8922,8 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn chat_id = user_id if bot_id is None: bot_id = self.bot_id - return self.current_states.get_interactive_data(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, - message_thread_id=message_thread_id) + return self.current_states.get_interactive_data(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) async def get_state(self, user_id: int, chat_id: Optional[int]=None, @@ -8952,8 +8960,8 @@ async def get_state(self, user_id: int, chat_id: Optional[int]=None, chat_id = user_id if bot_id is None: bot_id = self.bot_id - return await self.current_states.get_state(chat_id=chat_id, user_id=user_id, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + return await self.current_states.get_state(chat_id, user_id, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) async def add_data(self, user_id: int, chat_id: Optional[int]=None, @@ -8987,5 +8995,5 @@ async def add_data(self, user_id: int, chat_id: Optional[int]=None, if bot_id is None: bot_id = self.bot_id for key, value in kwargs.items(): - await self.current_states.set_data(chat_id=chat_id, user_id=user_id, key=key, value=value, bot_id=bot_id, - business_connection_id=business_connection_id, message_thread_id=message_thread_id) + await self.current_states.set_data(chat_id, user_id, key, value, + bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) diff --git a/telebot/asyncio_storage/base_storage.py b/telebot/asyncio_storage/base_storage.py index 20d0b0587..191ac45f4 100644 --- a/telebot/asyncio_storage/base_storage.py +++ b/telebot/asyncio_storage/base_storage.py @@ -5,7 +5,11 @@ class StateStorageBase: def __init__(self) -> None: pass - async def set_data(self, chat_id, user_id, key, value): + async def set_data(self, chat_id, user_id, key, value, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Set data for a user in a particular chat. """ @@ -17,7 +21,11 @@ async def get_data(self, chat_id, user_id): """ raise NotImplementedError - async def set_state(self, chat_id, user_id, state): + async def set_state(self, chat_id, user_id, state, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Set state for a particular user. @@ -28,22 +36,38 @@ async def set_state(self, chat_id, user_id, state): """ raise NotImplementedError - async def delete_state(self, chat_id, user_id): + async def delete_state(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Delete state for a particular user. """ raise NotImplementedError - async def reset_data(self, chat_id, user_id): + async def reset_data(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Reset data for a particular user in a chat. """ raise NotImplementedError - async def get_state(self, chat_id, user_id): + async def get_state(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): raise NotImplementedError - def get_interactive_data(self, chat_id, user_id): + def get_interactive_data(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Should be sync, but should provide a context manager with __aenter__ and __aexit__ methods. diff --git a/telebot/states/asyncio/context.py b/telebot/states/asyncio/context.py index 4c9ad61a3..50f252021 100644 --- a/telebot/states/asyncio/context.py +++ b/telebot/states/asyncio/context.py @@ -1,4 +1,4 @@ -from telebot.states import State, StatesGroup +from telebot.states import State from telebot.types import CallbackQuery, Message from telebot.async_telebot import AsyncTeleBot from telebot.states import resolve_context @@ -21,7 +21,7 @@ async def start_ex(message: types.Message, state_context: StateContext): # also, state_context.data(), .add_data(), .reset_data(), .delete() methods available. """ - def __init__(self, message: Union[Message, CallbackQuery], bot: str) -> None: + def __init__(self, message: Union[Message, CallbackQuery], bot: AsyncTeleBot) -> None: self.message: Union[Message, CallbackQuery] = message self.bot: AsyncTeleBot = bot self.bot_id = self.bot.bot_id diff --git a/telebot/storage/base_storage.py b/telebot/storage/base_storage.py index 9956c7ab9..033f63231 100644 --- a/telebot/storage/base_storage.py +++ b/telebot/storage/base_storage.py @@ -5,7 +5,11 @@ class StateStorageBase: def __init__(self) -> None: pass - def set_data(self, chat_id, user_id, key, value): + def set_data(self, chat_id, user_id, key, value, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Set data for a user in a particular chat. """ @@ -17,7 +21,11 @@ def get_data(self, chat_id, user_id): """ raise NotImplementedError - def set_state(self, chat_id, user_id, state): + def set_state(self, chat_id, user_id, state, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Set state for a particular user. @@ -28,22 +36,38 @@ def set_state(self, chat_id, user_id, state): """ raise NotImplementedError - def delete_state(self, chat_id, user_id): + def delete_state(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Delete state for a particular user. """ raise NotImplementedError - def reset_data(self, chat_id, user_id): + def reset_data(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): """ Reset data for a particular user in a chat. """ raise NotImplementedError - def get_state(self, chat_id, user_id): + def get_state(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): raise NotImplementedError - def get_interactive_data(self, chat_id, user_id): + def get_interactive_data(self, chat_id, user_id, + business_connection_id=None, + message_thread_id=None, + bot_id=None, + ): raise NotImplementedError def save(self, chat_id, user_id, data): From e5a02fa19073d7395baec737261cf6ef5b0a1ba2 Mon Sep 17 00:00:00 2001 From: "g.zinovev" Date: Tue, 6 May 2025 13:21:16 +0300 Subject: [PATCH 1767/1808] Address maintainer feedback: [fixed optional values] --- telebot/types.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 2cd6b8066..0d0361dcb 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6703,13 +6703,14 @@ def __init__(self, type, media, caption=None, parse_mode=None, caption_entities= self.caption_entities: Optional[List[MessageEntity]] = caption_entities self.thumbnail: Optional[Union[str, InputFile]] = thumbnail - if isinstance(self.thumbnail, InputFile): - self._thumbnail_name = service_utils.generate_random_token() - self._thumbnail_dic = 'attach://{0}'.format(self._thumbnail_name) - else: + if service_utils.is_string(self.thumbnail): self._thumbnail_name = '' self._thumbnail_dic = self.thumbnail + else: + self._thumbnail_name = service_utils.generate_random_token() + self._thumbnail_dic = 'attach://{0}'.format(self._thumbnail_name) + if service_utils.is_string(self.media): self._media_name = '' self._media_dic = self.media @@ -6721,7 +6722,9 @@ def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - json_dict = {'type': self.type, 'media': self._media_dic, 'thumbnail': self._thumbnail_dic} + json_dict = {'type': self.type, 'media': self._media_dic} + if self._thumbnail_dic: + json_dict['thumbnail'] = self._thumbnail_dic if self.caption: json_dict['caption'] = self.caption if self.parse_mode: @@ -6736,8 +6739,12 @@ def convert_input_media(self): """ if service_utils.is_string(self.media): return self.to_json(), None + + media_dict = {self._media_name: self.media} + if self._thumbnail_name: + media_dict[self._thumbnail_name] = self.thumbnail - return self.to_json(), {self._media_name: self.media, self._thumbnail_name: self.thumbnail} + return self.to_json(), media_dict class InputMediaPhoto(InputMedia): From ded461fcc1e6a48ec86e50f3495055f0688e30fe Mon Sep 17 00:00:00 2001 From: "g.zinovev" Date: Tue, 6 May 2025 22:52:02 +0300 Subject: [PATCH 1768/1808] Address maintainer feedback: [fixed thumbnail is None] --- telebot/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 0d0361dcb..2cd194d21 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6703,10 +6703,12 @@ def __init__(self, type, media, caption=None, parse_mode=None, caption_entities= self.caption_entities: Optional[List[MessageEntity]] = caption_entities self.thumbnail: Optional[Union[str, InputFile]] = thumbnail - if service_utils.is_string(self.thumbnail): + if thumbnail is None: + self._thumbnail_name = '' + self._thumbnail_dic = None + elif service_utils.is_string(self.thumbnail): self._thumbnail_name = '' self._thumbnail_dic = self.thumbnail - else: self._thumbnail_name = service_utils.generate_random_token() self._thumbnail_dic = 'attach://{0}'.format(self._thumbnail_name) From ad5a711b8c0ea128b5f5447d4e198a197b17e738 Mon Sep 17 00:00:00 2001 From: Roman A <121314722+GameRoMan@users.noreply.github.com> Date: Wed, 7 May 2025 18:04:55 +0100 Subject: [PATCH 1769/1808] Fix type annotations --- telebot/__init__.py | 4 ++-- telebot/util.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8b651a5b0..0dffa65ac 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3528,7 +3528,7 @@ def edit_message_live_location( proximity_alert_radius: Optional[int]=None, live_period: Optional[int]=None, business_connection_id: Optional[str]=None - ) -> types.Message or bool: + ) -> Union[types.Message, 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 @@ -3590,7 +3590,7 @@ def stop_message_live_location( inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, - business_connection_id: Optional[str]=None) -> types.Message or bool: + business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: """ Use this method to stop updating a live location message before live_period expires. On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. diff --git a/telebot/util.py b/telebot/util.py index bed0e8c19..8daf1d43c 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -255,7 +255,7 @@ def extract_command(text: str) -> Union[str, None]: return text.split()[0].split('@')[0][1:] if is_command(text) else None -def extract_arguments(text: str) -> str or None: +def extract_arguments(text: str) -> Union[str, None]: """ Returns the argument after the command. From b1392b232a56883a5c181f584a3e127147dfc60e Mon Sep 17 00:00:00 2001 From: Mohammad Date: Fri, 16 May 2025 17:43:10 +0200 Subject: [PATCH 1770/1808] Add py.typed marker file for PEP 561 compatibility This file signals to type checkers that the library provides inline type hints and should be type-checked, improving integration with tools like Mypy. --- telebot/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 telebot/py.typed diff --git a/telebot/py.typed b/telebot/py.typed new file mode 100644 index 000000000..e69de29bb From 5f2c4882beabc6a735a62e0b1bfd4e3860a7c6df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 08:15:48 +0000 Subject: [PATCH 1771/1808] Bump requests from 2.32.0 to 2.32.4 Bumps [requests](https://github.com/psf/requests) from 2.32.0 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.0...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 55dd7e486..febcca257 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest -requests==2.32.0 +requests==2.32.4 wheel==0.38.1 aiohttp==3.10.11 From 9e7f570d51c072303b676510bc90408ab66b011b Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 4 Jul 2025 21:41:25 +0500 Subject: [PATCH 1772/1808] Bot API 9.1 changes --- telebot/__init__.py | 97 ++++++++++- telebot/apihelper.py | 34 ++++ telebot/async_telebot.py | 94 ++++++++++- telebot/asyncio_helper.py | 34 ++++ telebot/types.py | 330 +++++++++++++++++++++++++++++++++++++- 5 files changed, 581 insertions(+), 8 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 0dffa65ac..59a8d7db8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2104,6 +2104,90 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption) return [types.MessageID.de_json(message_id) for message_id in result] + def send_checklist( + self, business_connection_id: str, chat_id: Union[int, str], + checklist: types.InputChecklist, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + message_effect_id: Optional[str]=None, + reply_parameters: Optional[types.ReplyParameters]=None, + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Message: + """ + Use this method to send a checklist on behalf of a connected business account. On success, + the sent Message is returned. + + Telegram documentation: https://core.telegram.org/bots/api#sendchecklist + + :param business_connection_id: Unique identifier of the business connection on behalf of which the message will be sent + :type business_connection_id: :obj:`str` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param checklist: A JSON-serialized object for the checklist to send + :type checklist: :class:`telebot.types.InputChecklist` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + + :param reply_parameters: Additional parameters for replies to messages + :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` + """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply + + return types.Message.de_json( + apihelper.send_checklist( + self.token, business_connection_id, chat_id, checklist, disable_notification=disable_notification, + protect_content=protect_content, message_effect_id=message_effect_id, + reply_parameters=reply_parameters, reply_markup=reply_markup)) + + def edit_message_checklist( + self, business_connection_id: str, chat_id: Union[int, str], + message_id: int, checklist: types.InputChecklist, + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Message: + """ + Use this method to edit a checklist on behalf of a connected business account. On success, + the edited Message is returned. + + Telegram documentation: https://core.telegram.org/bots/api#editmessagechecklist + + :param business_connection_id: Unique identifier of the business connection on behalf of which the message will be sent + :type business_connection_id: :obj:`str` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Unique identifier for the target message + :type message_id: :obj:`int` + + :param checklist: A JSON-serialized object for the new checklist + :type checklist: :class:`telebot.types.InputChecklist` + + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :return: On success, the edited Message is returned. + :rtype: :class:`telebot.types.Message` + """ + return types.Message.de_json( + apihelper.edit_message_checklist( + self.token, business_connection_id, chat_id, message_id, checklist, reply_markup=reply_markup)) def send_dice( self, chat_id: Union[int, str], @@ -5599,7 +5683,7 @@ def send_poll( :param question: Poll question, 1-300 characters :type question: :obj:`str` - :param options: A JSON-serialized list of 2-10 answer options + :param options: A JSON-serialized list of 2-12 answer options :type options: :obj:`list` of :obj:`InputPollOption` | :obj:`list` of :obj:`str` :param is_anonymous: True, if the poll needs to be anonymous, defaults to True @@ -5818,6 +5902,17 @@ def answer_pre_checkout_query( """ return apihelper.answer_pre_checkout_query( self.token, pre_checkout_query_id, ok, error_message=error_message) + + def get_my_star_balance(self) -> types.StarAmount: + """ + Returns the bot's current Telegram Stars balance. On success, returns a StarAmount object. + + Telegram documentation: https://core.telegram.org/bots/api#getmystarbalance + + :return: On success, returns a StarAmount object. + :rtype: :obj:`types.StarAmount` + """ + return types.StarAmount.de_json(apihelper.get_my_star_balance(self.token)) def get_star_transactions(self, offset: Optional[int]=None, limit: Optional[int]=None) -> types.StarTransactions: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 460c3c4a6..634b805d3 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -473,6 +473,36 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['video_start_timestamp'] = video_start_timestamp return _make_request(token, method_url, params=payload) +def send_checklist( + token, business_connection_id, chat_id, checklist, + disable_notification=None, protect_content=None, message_effect_id=None, + reply_parameters=None, reply_markup=None +): + method_url = r'sendChecklist' + payload = {'chat_id': chat_id, 'checklist': checklist, 'business_connection_id': business_connection_id} + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if protect_content is not None: + payload['protect_content'] = protect_content + if message_effect_id is not None: + payload['message_effect_id'] = message_effect_id + if reply_parameters is not None: + payload['reply_parameters'] = reply_parameters.to_json() + if reply_markup is not None: + payload['reply_markup'] = _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + + +def edit_message_checklist( + token, business_connection_id, chat_id, message_id, checklist, + reply_markup=None +): + method_url = r'editMessageChecklist' + payload = {'chat_id': chat_id, 'message_id': message_id, 'checklist': checklist, 'business_connection_id': business_connection_id} + if reply_markup is not None: + payload['reply_markup'] = await _convert_markup(reply_markup) + return _make_request(token, method_url, params=payload) + def send_dice( token, chat_id, @@ -1817,6 +1847,10 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No payload['error_message'] = error_message return _make_request(token, method_url, params=payload) +def get_my_star_balance(token): + method_url = 'getMyStarBalance' + return _make_request(token, method_url) + def get_star_transactions(token, offset=None, limit=None): method_url = 'getStarTransactions' payload = {} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 8c021b9a7..fe8013135 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3619,6 +3619,91 @@ async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, protect_content, remove_caption) return [types.MessageID.de_json(message_id) for message_id in result] + + async def send_checklist( + self, business_connection_id: str, chat_id: Union[int, str], + checklist: types.InputChecklist, + disable_notification: Optional[bool]=None, + protect_content: Optional[bool]=None, + message_effect_id: Optional[str]=None, + reply_parameters: Optional[types.ReplyParameters]=None, + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Message: + """ + Use this method to send a checklist on behalf of a connected business account. On success, + the sent Message is returned. + + Telegram documentation: https://core.telegram.org/bots/api#sendchecklist + + :param business_connection_id: Unique identifier of the business connection on behalf of which the message will be sent + :type business_connection_id: :obj:`str` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param checklist: A JSON-serialized object for the checklist to send + :type checklist: :class:`telebot.types.InputChecklist` + + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`bool` + + :param protect_content: Protects the contents of the sent message from forwarding and saving + :type protect_content: :obj:`bool` + + :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only + :type message_effect_id: :obj:`str` + + :param reply_parameters: Additional parameters for replies to messages + :type reply_parameters: :class:`telebot.types.ReplyParameters` + + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :return: On success, the sent Message is returned. + :rtype: :class:`telebot.types.Message` + """ + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification + protect_content = self.protect_content if (protect_content is None) else protect_content + + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): + reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply + + return types.Message.de_json( + await asyncio_helper.send_checklist( + self.token, business_connection_id, chat_id, checklist, disable_notification=disable_notification, + protect_content=protect_content, message_effect_id=message_effect_id, + reply_parameters=reply_parameters, reply_markup=reply_markup)) + + async def edit_message_checklist( + self, business_connection_id: str, chat_id: Union[int, str], + message_id: int, checklist: types.InputChecklist, + reply_markup: Optional[types.InlineKeyboardMarkup]=None) -> types.Message: + """ + Use this method to edit a checklist on behalf of a connected business account. On success, + the edited Message is returned. + + Telegram documentation: https://core.telegram.org/bots/api#editmessagechecklist + + :param business_connection_id: Unique identifier of the business connection on behalf of which the message will be sent + :type business_connection_id: :obj:`str` + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Unique identifier for the target message + :type message_id: :obj:`int` + + :param checklist: A JSON-serialized object for the new checklist + :type checklist: :class:`telebot.types.InputChecklist` + + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard. + :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` + + :return: On success, the edited Message is returned. + :rtype: :class:`telebot.types.Message` + """ + return types.Message.de_json( + await asyncio_helper.edit_message_checklist( + self.token, business_connection_id, chat_id, message_id, checklist, reply_markup=reply_markup)) async def send_dice( self, chat_id: Union[int, str], @@ -7256,8 +7341,15 @@ async def answer_pre_checkout_query( :rtype: :obj:`bool` """ return await asyncio_helper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message) - + + async def get_my_star_balance(self) -> types.StarAmount: + """ + A method to get the current Telegram Stars balance of the bot. Requires no parameters. + On success, returns a StarAmount object. + """ + return types.StarAmount.de_json(await asyncio_helper.get_my_star_balance(self.token)) + async def get_star_transactions(self, offset: Optional[int]=None, limit: Optional[int]=None) -> types.StarTransactions: """ Returns the bot's Telegram Star transactions in chronological order. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index c582aa3e4..4a542ffcf 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -477,6 +477,35 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['video_start_timestamp'] = video_start_timestamp return await _process_request(token, method_url, params=payload) +async def send_checklist( + token, business_connection_id, chat_id, checklist, + disable_notification=None, protect_content=None, message_effect_id=None, + reply_parameters=None, reply_markup=None +): + method_url = r'sendChecklist' + payload = {'chat_id': chat_id, 'checklist': checklist, 'business_connection_id': business_connection_id} + if disable_notification is not None: + payload['disable_notification'] = disable_notification + if protect_content is not None: + payload['protect_content'] = protect_content + if message_effect_id is not None: + payload['message_effect_id'] = message_effect_id + if reply_parameters is not None: + payload['reply_parameters'] = reply_parameters.to_json() + if reply_markup is not None: + payload['reply_markup'] = _convert_markup(reply_markup) + return await _process_request(token, method_url, params=payload) + + +async def edit_message_checklist( + token, business_connection_id, chat_id, message_id, checklist, + reply_markup=None +): + method_url = r'editMessageChecklist' + payload = {'chat_id': chat_id, 'message_id': message_id, 'checklist': checklist, 'business_connection_id': business_connection_id} + if reply_markup is not None: + payload['reply_markup'] = await _convert_markup(reply_markup) + return await _process_request(token, method_url, params=payload) async def send_dice( token, chat_id, @@ -1816,6 +1845,11 @@ async def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_mess payload['error_message'] = error_message return await _process_request(token, method_url, params=payload) + +async def get_my_star_balance(token): + method_url = 'getMyStarBalance' + return await _process_request(token, method_url) + async def get_star_transactions(token, offset=None, limit=None): method_url = 'getStarTransactions' payload = {} diff --git a/telebot/types.py b/telebot/types.py index 513921fbd..8cc573246 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1049,6 +1049,9 @@ class Message(JsonDeserializable): :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation :type has_media_spoiler: :obj:`bool` + :param checklist: Optional. Message is a checklist + :type checklist: :class:`telebot.types.Checklist` + :param contact: Optional. Message is a shared contact, information about the contact :type contact: :class:`telebot.types.Contact` @@ -1161,6 +1164,15 @@ class Message(JsonDeserializable): :param chat_background_set: Optional. Service message: chat background set :type chat_background_set: :class:`telebot.types.ChatBackground` + :param checklist_tasks_done: Optional. Service message: some tasks in a checklist were marked as done or not done + :type checklist_tasks_done: :class:`telebot.types.ChecklistTasksDone` + + :param checklist_tasks_added: Optional. Service message: tasks were added to a checklist + :type checklist_tasks_added: :class:`telebot.types.ChecklistTasksAdded` + + :param direct_message_price_changed: Optional. Service message: the price for paid messages in the corresponding direct messages chat of a channel has changed + :type direct_message_price_changed: :class:`telebot.types.DirectMessagePriceChanged` + :param forum_topic_created: Optional. Service message: forum topic created :type forum_topic_created: :class:`telebot.types.ForumTopicCreated` @@ -1461,8 +1473,18 @@ def de_json(cls, json_string): content_type = 'paid_message_price_changed' if 'paid_star_count' in obj: opts['paid_star_count'] = obj['paid_star_count'] + if 'checklist' in obj: + opts['checklist'] = Checklist.de_json(obj['checklist']) + if 'checklist_tasks_done' in obj: + opts['checklist_tasks_done'] = ChecklistTasksDone.de_json(obj['checklist_tasks_done']) + content_type = 'checklist_tasks_done' + if 'checklist_tasks_added' in obj: + opts['checklist_tasks_added'] = ChecklistTasksAdded.de_json(obj['checklist_tasks_added']) + content_type = 'checklist_tasks_added' + if 'direct_message_price_changed' in obj: + opts['direct_message_price_changed'] = DirectMessagePriceChanged.de_json(obj['direct_message_price_changed']) + content_type = 'direct_message_price_changed' - return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1584,7 +1606,10 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.unique_gift : Optional[UniqueGiftInfo] = None self.paid_message_price_changed: Optional[PaidMessagePriceChanged] = None self.paid_star_count: Optional[int] = None - + self.checklist: Optional[Checklist] = None + self.checklist_tasks_done: Optional[ChecklistTasksDone] = None + self.checklist_tasks_added: Optional[List[ChecklistTasksAdded]] = None + self.direct_message_price_changed: Optional[DirectMessagePriceChanged] = None for key in options: setattr(self, key, options[key]) @@ -8786,6 +8811,9 @@ class ExternalReplyInfo(JsonDeserializable): :param has_media_spoiler: Optional. True, if the message media is covered by a spoiler animation :type has_media_spoiler: :obj:`bool` + :param checklist: Optional. Message is a checklist + :type checklist: :class:`telebot.types.Checklist` + :param contact: Optional. Message is a shared contact, information about the contact :type contact: :class:`Contact` @@ -8873,7 +8901,8 @@ def __init__( dice: Optional[Dice]=None, game: Optional[Game]=None, giveaway: Optional[Giveaway]=None, giveaway_winners: Optional[GiveawayWinners]=None, invoice: Optional[Invoice]=None, location: Optional[Location]=None, poll: Optional[Poll]=None, - venue: Optional[Venue]=None, paid_media: Optional[PaidMediaInfo]=None, **kwargs) -> None: + venue: Optional[Venue]=None, paid_media: Optional[PaidMediaInfo]=None, + checklist: Optional[Checklist]=None, **kwargs) -> None: self.origin: MessageOrigin = origin self.chat: Optional[Chat] = chat self.message_id: Optional[int] = message_id @@ -8898,6 +8927,7 @@ def __init__( self.poll: Optional[Poll] = poll self.venue: Optional[Venue] = venue self.paid_media: Optional[PaidMediaInfo] = paid_media + self.checklist: Optional[Checklist] = checklist # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -11623,11 +11653,14 @@ class OwnedGiftUnique(OwnedGift): :param transfer_star_count: Optional. Number of Telegram Stars that must be paid to transfer the gift; omitted if the bot cannot transfer the gift :type transfer_star_count: :obj:`int` + :param next_transfer_date: Optional. Point in time (Unix timestamp) when the gift can be transferred. If it is in the past, then the gift can be transferred now + :type next_transfer_date: :obj:`int` + :return: Instance of the class :rtype: :class:`OwnedGiftUnique` """ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=None, is_saved=None, - can_be_transferred=None, transfer_star_count=None, **kwargs): + can_be_transferred=None, transfer_star_count=None, next_transfer_date=None, **kwargs): super().__init__(type=type) self.gift: UniqueGift = gift self.owned_gift_id: Optional[str] = owned_gift_id @@ -11636,6 +11669,7 @@ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=N self.is_saved: Optional[bool] = is_saved self.can_be_transferred: Optional[bool] = can_be_transferred self.transfer_star_count: Optional[int] = transfer_star_count + self.next_transfer_date: Optional[int] = next_transfer_date @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -12307,24 +12341,35 @@ class UniqueGiftInfo(JsonDeserializable): :param gift: Information about the gift :type gift: :class:`UniqueGift` - :param origin: Origin of the gift. Currently, either “upgrade” or “transfer” + :param origin: Origin of the gift. Currently, either “upgrade” for gifts upgraded from regular gifts, “transfer” for gifts transferred from other users or channels, + or “resale” for gifts bought from other users :type origin: :obj:`str` + :param last_resale_star_count: Optional. For gifts bought from other users, the price paid for the gift + :type last_resale_star_count: :obj:`int` + :param owned_gift_id: Optional. Unique identifier of the received gift for the bot; only present for gifts received on behalf of business accounts :type owned_gift_id: :obj:`str` :param transfer_star_count: Optional. Number of Telegram Stars that must be paid to transfer the gift; omitted if the bot cannot transfer the gift :type transfer_star_count: :obj:`int` + :param next_transfer_date: Optional. Point in time (Unix timestamp) when the gift can be transferred. If it is in the past, then the gift can be transferred now + :type next_transfer_date: :obj:`int` + :return: Instance of the class :rtype: :class:`UniqueGiftInfo` """ def __init__(self, gift: UniqueGift, origin: str, owned_gift_id: Optional[str] = None, - transfer_star_count: Optional[int] = None, **kwargs): + transfer_star_count: Optional[int] = None, next_transfer_date: Optional[int] = None, + last_resale_star_count: Optional[int] = None, **kwargs): self.gift: UniqueGift = gift self.origin: str = origin + self.last_resale_star_count: Optional[int] = last_resale_star_count self.owned_gift_id: Optional[str] = owned_gift_id self.transfer_star_count: Optional[int] = transfer_star_count + self.next_transfer_date: Optional[int] = next_transfer_date + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -12438,3 +12483,276 @@ def to_dict(self): return data def convert_input_profile_photo(self): return self.to_json(), {self._animation_name: self.animation} + + +class ChecklistTask(JsonDeserializable): + """ + Describes a task in a checklist. + + Telegram documentation: https://core.telegram.org/bots/api#checklisttask + + :param id: Unique identifier of the task + :type id: :obj:`int` + + :param text: Text of the task + :type text: :obj:`str` + + :param text_entities: Optional. Special entities that appear in the task text + :type text_entities: :obj:`list` of :class:`MessageEntity` + + :param completed_by_user: Optional. User that completed the task; omitted if the task wasn't completed + :type completed_by_user: :class:`User` + + :param completion_date: Optional. Point in time (Unix timestamp) when the task was completed; 0 if the task wasn't completed + :type completion_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`ChecklistTask` + """ + def __init__(self, id: int, text: str, text_entities: Optional[List[MessageEntity]] = None, + completed_by_user: Optional[User] = None, + completion_date: Optional[int] = None, **kwargs): + self.id: int = id + self.text: str = text + self.text_entities: Optional[List[MessageEntity]] = text_entities + self.completed_by_user: Optional[User] = completed_by_user + self.completion_date: Optional[int] = completion_date + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'text_entities' in obj: + obj['text_entities'] = [MessageEntity.de_json(entity) for entity in obj['text_entities']] + if 'completed_by_user' in obj: + obj['completed_by_user'] = User.de_json(obj['completed_by_user']) + return cls(**obj) + +class Checklist(JsonDeserializable): + """ + Describes a checklist. + + Telegram documentation: https://core.telegram.org/bots/api#checklist + + :param title: Title of the checklist + :type title: :obj:`str` + + :param title_entities: Optional. Special entities that appear in the checklist title + :type title_entities: :obj:`list` of :class:`MessageEntity` + + :param tasks: List of tasks in the checklist + :type tasks: :obj:`list` of :class:`ChecklistTask` + + :param others_can_add_tasks: Optional. True, if users other than the creator of the list can add tasks to the list + :type others_can_add_tasks: :obj:`bool` + + :param others_can_mark_tasks_as_done: Optional. True, if users other than the creator of the list can mark tasks as done or not done in the list + :type others_can_mark_tasks_as_done: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`Checklist` + """ + def __init__(self, title: str, tasks: List[ChecklistTask], + title_entities: Optional[List[MessageEntity]] = None, + others_can_add_tasks: Optional[bool] = None, + others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): + self.title: str = title + self.title_entities: Optional[List[MessageEntity]] = title_entities + self.tasks: List[ChecklistTask] = tasks + self.others_can_add_tasks: Optional[bool] = others_can_add_tasks + self.others_can_mark_tasks_as_done: Optional[bool] = others_can_mark_tasks_as_done + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'title_entities' in obj: + obj['title_entities'] = [MessageEntity.de_json(entity) for entity in obj['title_entities']] + if 'tasks' in obj: + obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] + return cls(**obj) + +class InputChecklistTask(JsonSerializable): + """ + Describes a task to add to a checklist. + + Telegram documentation: https://core.telegram.org/bots/api#inputchecklisttask + + :param id: Unique identifier of the task; must be positive and unique among all task identifiers currently present in the checklist + :type id: :obj:`int` + + :param text: Text of the task; 1-100 characters after entities parsing + :type text: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the text. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param text_entities: Optional. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed. + :type text_entities: :obj:`list` of :class:`MessageEntity` + + :return: Instance of the class + :rtype: :class:`InputChecklistTask` + """ + def __init__(self, id: int, text: str, parse_mode: Optional[str] = None, + text_entities: Optional[List[MessageEntity]] = None, **kwargs): + self.id: int = id + self.text: str = text + self.parse_mode: Optional[str] = parse_mode + self.text_entities: Optional[List[MessageEntity]] = text_entities + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = { + 'id': self.id, + 'text': self.text + } + if self.parse_mode is not None: + data['parse_mode'] = self.parse_mode + if self.text_entities is not None: + data['text_entities'] = [entity.to_dict() for entity in self.text_entities] + return data + +class InputChecklist(JsonSerializable): + """ + Describes a checklist to create. + + Telegram documentation: https://core.telegram.org/bots/api#inputchecklist + + :param title: Title of the checklist; 1-255 characters after entities parsing + :type title: :obj:`str` + + :param parse_mode: Optional. Mode for parsing entities in the title. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param title_entities: Optional. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed. + :type title_entities: :obj:`list` of :class:`MessageEntity` + + :param tasks: List of 1-30 tasks in the checklist + :type tasks: :obj:`list` of :class:`InputChecklistTask` + + :param others_can_add_tasks: Optional. Pass True if other users can add tasks to the checklist + :type others_can_add_tasks: :obj:`bool` + + :param others_can_mark_tasks_as_done: Optional. Pass True if other users can mark tasks as done or not done in the checklist + :type others_can_mark_tasks_as_done: :obj:`bool` + + :return: Instance of the class + :rtype: :class:`InputChecklist` + """ + def __init__(self, title: str, tasks: List[InputChecklistTask], + parse_mode: Optional[str] = None, + title_entities: Optional[List[MessageEntity]] = None, + others_can_add_tasks: Optional[bool] = None, + others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): + self.title: str = title + self.parse_mode: Optional[str] = parse_mode + self.title_entities: Optional[List[MessageEntity]] = title_entities + self.tasks: List[InputChecklistTask] = tasks + self.others_can_add_tasks: Optional[bool] = others_can_add_tasks + self.others_can_mark_tasks_as_done: Optional[bool] = others_can_mark_tasks_as_done + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = { + 'title': self.title, + 'tasks': [task.to_dict() for task in self.tasks] + } + if self.parse_mode is not None: + data['parse_mode'] = self.parse_mode + if self.title_entities is not None: + data['title_entities'] = [entity.to_dict() for entity in self.title_entities] + if self.others_can_add_tasks is not None: + data['others_can_add_tasks'] = self.others_can_add_tasks + if self.others_can_mark_tasks_as_done is not None: + data['others_can_mark_tasks_as_done'] = self.others_can_mark_tasks_as_done + + +class ChecklistTasksDone(JsonDeserializable): + """ + Describes a service message about checklist tasks marked as done or not done. + + Telegram documentation: https://core.telegram.org/bots/api#checklisttasksdone + + :param checklist_message: Optional. Message containing the checklist whose tasks were marked as done or not done. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. + :type checklist_message: :class:`Message` + + :param marked_as_done_task_ids: Optional. Identifiers of the tasks that were marked as done + :type marked_as_done_task_ids: :obj:`list` of :obj:`int + + :param marked_as_not_done_task_ids: Optional. Identifiers of the tasks that were marked as not done + :type marked_as_not_done_task_ids: :obj:`list` of :obj:`int` + + :return: Instance of the class + :rtype: :class:`ChecklistTasksDone` + """ + def __init__(self, checklist_message: Optional[Message] = None, + marked_as_done_task_ids: Optional[List[int]] = None, + marked_as_not_done_task_ids: Optional[List[int]] = None, **kwargs): + self.checklist_message: Optional[Message] = checklist_message + self.marked_as_done_task_ids: Optional[List[int]] = marked_as_done_task_ids + self.marked_as_not_done_task_ids: Optional[List[int]] = marked_as_not_done_task_ids + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'checklist_message' in obj: + obj['checklist_message'] = Message.de_json(obj['checklist_message']) + return cls(**obj) + + +class ChecklistTasksAdded(JsonDeserializable): + """ + Describes a service message about tasks added to a checklist. + + Telegram documentation: https://core.telegram.org/bots/api#checklisttasksadded + + :param checklist_message: Optional. Message containing the checklist to which the tasks were added. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. + :type checklist_message: :class:`Message` + + :param tasks: List of tasks added to the checklist + :type tasks: :obj:`list` of :class:`ChecklistTask` + + :return: Instance of the class + :rtype: :class:`ChecklistTasksAdded` + """ + def __init__(self, tasks: List[ChecklistTask], checklist_message: Optional[Message] = None, **kwargs): + self.checklist_message: Optional[Message] = checklist_message + self.tasks: List[ChecklistTask] = tasks + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'checklist_message' in obj: + obj['checklist_message'] = Message.de_json(obj['checklist_message']) + obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] + return cls(**obj) + +class DirectMessagePriceChanged(JsonDeserializable): + """ + Describes a service message about a change in the price of direct messages sent to a channel chat. + + Telegram documentation: https://core.telegram.org/bots/api#directmessagepricechanged + + :param are_direct_messages_enabled: True, if direct messages are enabled for the channel chat; false otherwise + :type are_direct_messages_enabled: :obj:`bool` + + :param direct_message_star_count: Optional. The new number of Telegram Stars that must be paid by users for each direct message sent to the channel. Does not apply to users who have been exempted by administrators. Defaults to 0. + :type direct_message_star_count: :obj:`int` + + :return: Instance of the class + :rtype: :class:`DirectMessagePriceChanged` + """ + def __init__(self, are_direct_messages_enabled: bool, direct_message_star_count: Optional[int] = None, **kwargs): + self.are_direct_messages_enabled: bool = are_direct_messages_enabled + self.direct_message_star_count: Optional[int] = direct_message_star_count + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) From b9f0f9a3c7a9e50ed717c002726a7cc9c432a9d0 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 4 Jul 2025 21:44:04 +0500 Subject: [PATCH 1773/1808] Fix test error --- telebot/apihelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 634b805d3..d3eb9c6b6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -500,7 +500,7 @@ def edit_message_checklist( method_url = r'editMessageChecklist' payload = {'chat_id': chat_id, 'message_id': message_id, 'checklist': checklist, 'business_connection_id': business_connection_id} if reply_markup is not None: - payload['reply_markup'] = await _convert_markup(reply_markup) + payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) From 2621a56a0e4e0568ab44e8976e5279bd5aba9142 Mon Sep 17 00:00:00 2001 From: _run Date: Fri, 4 Jul 2025 21:48:16 +0500 Subject: [PATCH 1774/1808] Add new util service messages --- telebot/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telebot/util.py b/telebot/util.py index 8daf1d43c..05a7866f1 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -40,7 +40,8 @@ 'write_access_allowed', 'proximity_alert_triggered', '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', + 'video_chat_started', 'video_chat_ended', 'video_chat_participants_invited','checklist_tasks_done', + 'checklist_tasks_added','direct_message_price_changed' ] #: All update types, should be used for allowed_updates parameter in polling. From f21e681dc833bb1cdcc84ec4d0c26612e600326f Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 6 Jul 2025 14:08:16 +0500 Subject: [PATCH 1775/1808] Fix inconsistent parts and bugs --- telebot/apihelper.py | 8 ++++---- telebot/asyncio_helper.py | 8 ++++---- telebot/types.py | 25 ++++++++++++++----------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d3eb9c6b6..9318d926d 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -484,11 +484,11 @@ def send_checklist( payload['disable_notification'] = disable_notification if protect_content is not None: payload['protect_content'] = protect_content - if message_effect_id is not None: + if message_effect_id: payload['message_effect_id'] = message_effect_id - if reply_parameters is not None: + if reply_parameters: payload['reply_parameters'] = reply_parameters.to_json() - if reply_markup is not None: + if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) @@ -499,7 +499,7 @@ def edit_message_checklist( ): method_url = r'editMessageChecklist' payload = {'chat_id': chat_id, 'message_id': message_id, 'checklist': checklist, 'business_connection_id': business_connection_id} - if reply_markup is not None: + if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 4a542ffcf..bc50068f6 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -488,11 +488,11 @@ async def send_checklist( payload['disable_notification'] = disable_notification if protect_content is not None: payload['protect_content'] = protect_content - if message_effect_id is not None: + if message_effect_id: payload['message_effect_id'] = message_effect_id - if reply_parameters is not None: + if reply_parameters: payload['reply_parameters'] = reply_parameters.to_json() - if reply_markup is not None: + if reply_markup: payload['reply_markup'] = _convert_markup(reply_markup) return await _process_request(token, method_url, params=payload) @@ -503,7 +503,7 @@ async def edit_message_checklist( ): method_url = r'editMessageChecklist' payload = {'chat_id': chat_id, 'message_id': message_id, 'checklist': checklist, 'business_connection_id': business_connection_id} - if reply_markup is not None: + if reply_markup: payload['reply_markup'] = await _convert_markup(reply_markup) return await _process_request(token, method_url, params=payload) diff --git a/telebot/types.py b/telebot/types.py index 8cc573246..3f04a6d62 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8889,6 +8889,10 @@ def de_json(cls, json_string): obj['poll'] = Poll.de_json(obj['poll']) if 'venue' in obj: obj['venue'] = Venue.de_json(obj['venue']) + if 'paid_media' in obj: + obj['paid_media'] = PaidMediaInfo.de_json(obj['paid_media']) + if 'checklist' in obj: + obj['checklist'] = Checklist.de_json(obj['checklist']) return cls(**obj) def __init__( @@ -12523,7 +12527,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) if 'text_entities' in obj: - obj['text_entities'] = [MessageEntity.de_json(entity) for entity in obj['text_entities']] + obj['text_entities'] = Message.parse_entities(obj['text_entities']) if 'completed_by_user' in obj: obj['completed_by_user'] = User.de_json(obj['completed_by_user']) return cls(**obj) @@ -12552,8 +12556,8 @@ class Checklist(JsonDeserializable): :return: Instance of the class :rtype: :class:`Checklist` """ - def __init__(self, title: str, tasks: List[ChecklistTask], - title_entities: Optional[List[MessageEntity]] = None, + def __init__(self, title: str,title_entities: Optional[List[MessageEntity]] = None, + tasks: List[ChecklistTask] = None, others_can_add_tasks: Optional[bool] = None, others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): self.title: str = title @@ -12567,7 +12571,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) if 'title_entities' in obj: - obj['title_entities'] = [MessageEntity.de_json(entity) for entity in obj['title_entities']] + obj['title_entities'] = Message.parse_entities(obj['title_entities']) if 'tasks' in obj: obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] return cls(**obj) @@ -12608,9 +12612,9 @@ def to_dict(self): 'id': self.id, 'text': self.text } - if self.parse_mode is not None: + if self.parse_mode: data['parse_mode'] = self.parse_mode - if self.text_entities is not None: + if self.text_entities: data['text_entities'] = [entity.to_dict() for entity in self.text_entities] return data @@ -12641,10 +12645,9 @@ class InputChecklist(JsonSerializable): :return: Instance of the class :rtype: :class:`InputChecklist` """ - def __init__(self, title: str, tasks: List[InputChecklistTask], - parse_mode: Optional[str] = None, + def __init__(self, title: str,parse_mode: Optional[str] = None, title_entities: Optional[List[MessageEntity]] = None, - others_can_add_tasks: Optional[bool] = None, + tasks: List[InputChecklistTask], others_can_add_tasks: Optional[bool] = None, others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): self.title: str = title self.parse_mode: Optional[str] = parse_mode @@ -12661,9 +12664,9 @@ def to_dict(self): 'title': self.title, 'tasks': [task.to_dict() for task in self.tasks] } - if self.parse_mode is not None: + if self.parse_mode: data['parse_mode'] = self.parse_mode - if self.title_entities is not None: + if self.title_entities: data['title_entities'] = [entity.to_dict() for entity in self.title_entities] if self.others_can_add_tasks is not None: data['others_can_add_tasks'] = self.others_can_add_tasks From 7a8d76c045bc60be58f9ce1addb4b11b2c918dd9 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 6 Jul 2025 14:17:48 +0500 Subject: [PATCH 1776/1808] Fix the test --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 3f04a6d62..26be8ed16 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12647,7 +12647,7 @@ class InputChecklist(JsonSerializable): """ def __init__(self, title: str,parse_mode: Optional[str] = None, title_entities: Optional[List[MessageEntity]] = None, - tasks: List[InputChecklistTask], others_can_add_tasks: Optional[bool] = None, + tasks: List[InputChecklistTask]=None, others_can_add_tasks: Optional[bool] = None, others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): self.title: str = title self.parse_mode: Optional[str] = parse_mode From 9980e6e6529a195567c2794f0182143f3c110597 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 6 Jul 2025 16:02:45 +0500 Subject: [PATCH 1777/1808] Parameter order fix --- telebot/types.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 26be8ed16..971f5824c 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12556,13 +12556,13 @@ class Checklist(JsonDeserializable): :return: Instance of the class :rtype: :class:`Checklist` """ - def __init__(self, title: str,title_entities: Optional[List[MessageEntity]] = None, - tasks: List[ChecklistTask] = None, + def __init__(self, title: str, tasks: List[ChecklistTask], + title_entities: Optional[List[MessageEntity]] = None, others_can_add_tasks: Optional[bool] = None, others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): self.title: str = title - self.title_entities: Optional[List[MessageEntity]] = title_entities self.tasks: List[ChecklistTask] = tasks + self.title_entities: Optional[List[MessageEntity]] = title_entities self.others_can_add_tasks: Optional[bool] = others_can_add_tasks self.others_can_mark_tasks_as_done: Optional[bool] = others_can_mark_tasks_as_done @@ -12572,8 +12572,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'title_entities' in obj: obj['title_entities'] = Message.parse_entities(obj['title_entities']) - if 'tasks' in obj: - obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] + obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] return cls(**obj) class InputChecklistTask(JsonSerializable): @@ -12645,14 +12644,15 @@ class InputChecklist(JsonSerializable): :return: Instance of the class :rtype: :class:`InputChecklist` """ - def __init__(self, title: str,parse_mode: Optional[str] = None, + def __init__(self, title: str, tasks: List[InputChecklistTask], + parse_mode: Optional[str] = None, title_entities: Optional[List[MessageEntity]] = None, - tasks: List[InputChecklistTask]=None, others_can_add_tasks: Optional[bool] = None, + others_can_add_tasks: Optional[bool] = None, others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): self.title: str = title + self.tasks: List[InputChecklistTask] = tasks self.parse_mode: Optional[str] = parse_mode self.title_entities: Optional[List[MessageEntity]] = title_entities - self.tasks: List[InputChecklistTask] = tasks self.others_can_add_tasks: Optional[bool] = others_can_add_tasks self.others_can_mark_tasks_as_done: Optional[bool] = others_can_mark_tasks_as_done From 012eac4211f83a463bf96ce48bbd6cb73a11e5d8 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 26 Jul 2025 10:25:30 +0300 Subject: [PATCH 1778/1808] Bump version --- README.md | 2 +- docs/source/conf.py | 2 +- pyproject.toml | 5 ++--- telebot/version.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 262fc2beb..7a67b7d78 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

diff --git a/docs/source/conf.py b/docs/source/conf.py index 000225031..4dcffc212 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.27.0' +release = '4.28.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index e7bf04f85..d2db6d2b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.27.0" +version = "4.28.0" description = "Python Telegram bot api." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} @@ -22,7 +22,7 @@ classifiers = [ "Environment :: Console", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)" ] -dependencies = ["requests"] +dependencies = ["pytest", "requests", "wheel", "aiohttp"] [project.urls] Homepage = "https://github.com/eternnoir/pyTelegramBotAPI" @@ -35,7 +35,6 @@ Issues = "https://github.com/eternnoir/pyTelegramBotAPI/issues" json = ["ujson"] PIL = ["Pillow"] redis = ["redis>=3.4.1"] -aiohttp = ["aiohttp"] fastapi = ["fastapi"] uvicorn = ["uvicorn"] psutil = ["psutil"] diff --git a/telebot/version.py b/telebot/version.py index e0fe9ac48..c2bb2a64b 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.27.0' +__version__ = '4.28.0' From 86056efaa8777a2705c4d56fa53ca3a194aef472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Jul 2025 07:29:07 +0000 Subject: [PATCH 1779/1808] Bump aiohttp from 3.10.11 to 3.12.14 --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.12.14 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index febcca257..ced176c9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pytest requests==2.32.4 wheel==0.38.1 -aiohttp==3.10.11 +aiohttp==3.12.14 From 492919e213d79b14da4b9e1c68ca6741ceeb33db Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 26 Jul 2025 10:49:49 +0300 Subject: [PATCH 1780/1808] content_type_xxx aligned 1. content_type_media, content_type_service, update_types ordered according to presence in code to make add missing ones easier. 2. Added some missed content types. --- telebot/util.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/telebot/util.py b/telebot/util.py index 05a7866f1..2f92ae781 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -27,29 +27,30 @@ #: Contains all media content types. content_type_media = [ - 'text', 'animation', 'audio', 'document', 'photo', 'sticker', 'story', 'video', 'video_note', 'voice', 'contact', - 'dice', 'game', 'poll', 'venue', 'location', 'invoice', 'successful_payment', 'connected_website', - 'passport_data', 'web_app_data', + 'text', 'audio', 'document', 'animation', 'game', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', + 'location', 'venue', 'dice', 'invoice', 'successful_payment', 'connected_website', 'poll', 'passport_data', + 'web_app_data', 'story', 'giveaway', 'gift', 'unique_gift', ] #: Contains all service content types such as `User joined the group`. content_type_service = [ 'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', - 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'message_auto_delete_timer_changed', - 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message', 'users_shared', 'chat_shared', - 'write_access_allowed', 'proximity_alert_triggered', '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','checklist_tasks_done', - 'checklist_tasks_added','direct_message_price_changed' + 'group_chat_created', 'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', + 'migrate_from_chat_id', 'pinned_message', 'proximity_alert_triggered', 'video_chat_scheduled', + 'video_chat_started', 'video_chat_ended', 'video_chat_participants_invited', 'message_auto_delete_timer_changed', + 'chat_background_set', 'forum_topic_created', 'forum_topic_closed', 'forum_topic_reopened', 'forum_topic_edited', + 'general_forum_topic_hidden', 'general_forum_topic_unhidden', 'write_access_allowed', 'users_shared', 'chat_shared', + 'giveaway_created', 'giveaway_winners', 'giveaway_completed', 'boost_added', 'paid_message_price_changed', + 'checklist_tasks_done', 'checklist_tasks_added', 'direct_message_price_changed', ] #: All update types, should be used for allowed_updates parameter in polling. update_types = [ "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", "chat_boost", "removed_chat_boost", - "business_connection", "business_message", "edited_business_message", "deleted_business_messages", "purchased_paid_media" + "chat_join_request", "message_reaction", "message_reaction_count", "removed_chat_boost", "chat_boost", + "business_connection", "business_message", "edited_business_message", "deleted_business_messages", + "purchased_paid_media", ] From 6bc1c36251c83f0cfa606eb396a7bf7ad40fc6a2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 26 Jul 2025 14:42:41 +0300 Subject: [PATCH 1781/1808] Some deprecation warnings, formal and typo fixes --- telebot/apihelper.py | 7 ++- telebot/types.py | 146 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 124 insertions(+), 29 deletions(-) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 9318d926d..cba38bbdd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -168,6 +168,8 @@ def _make_request(token, method_name, method='get', params=None, files=None): json_result = _check_result(method_name, result) if json_result: return json_result['result'] + else: + return None def _check_result(method_name, result): @@ -1139,8 +1141,10 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non def get_method_by_type(data_type): if data_type == 'document': return r'sendDocument' - if data_type == 'sticker': + elif data_type == 'sticker': return r'sendSticker' + else: + raise ValueError(f"Unsupported data type: {data_type}.") def ban_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None): @@ -2524,6 +2528,7 @@ def convert_input_media_array(array): for input_media in array: if isinstance(input_media, types.InputMedia) or isinstance(input_media, types.InputPaidMedia): media_dict = input_media.to_dict() + key = "x" # stub if media_dict['media'].startswith('attach://'): key = media_dict['media'].replace('attach://', '') files[key] = input_media.media diff --git a/telebot/types.py b/telebot/types.py index 971f5824c..4a58c2529 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -276,7 +276,6 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.purchased_paid_media: Optional[PaidMediaPurchased] = purchased_paid_media - class ChatMemberUpdated(JsonDeserializable): """ This object represents changes in the status of a chat member. @@ -403,6 +402,7 @@ def __init__(self, chat, from_user, user_chat_id, date, bio=None, invite_link=No self.invite_link: Optional[ChatInviteLink] = invite_link self.user_chat_id: int = user_chat_id + class WebhookInfo(JsonDeserializable): """ Describes the current status of a webhook. @@ -6790,6 +6790,10 @@ def __init__(self, type, media, caption=None, parse_mode=None, caption_entities= self._media_name = service_utils.generate_random_token() self._media_dic = 'attach://{0}'.format(self._media_name) + if self.__class__ is InputMedia: + # Make InputMedia as ABC some time... + log_deprecation_warning('The InputMedia class should not be instantiated directly. Use particular InputMediaXXX class instead') + def to_json(self): return json.dumps(self.to_dict()) @@ -8547,9 +8551,16 @@ def de_json(cls, json_string): elif obj['type'] == 'custom_emoji': del obj['type'] return ReactionTypeCustomEmoji(**obj) + elif obj['type'] == 'paid': + del obj['type'] + return ReactionTypePaid(**obj) + else: + raise ValueError(f"Unknown reaction type: {obj['type']}.") def __init__(self, type: str) -> None: self.type: str = type + if self.__class__ is ReactionType: + log_deprecation_warning('The ReactionType class should not be instantiated directly. Use particular ReactionTypeXXX class instead') def to_dict(self) -> dict: json_dict = { @@ -8935,7 +8946,7 @@ def __init__( # noinspection PyUnresolvedReferences,PyShadowingBuiltins -class MessageOrigin(JsonDeserializable): +class MessageOrigin(JsonDeserializable, ABC): """ This object describes the origin of a message. @@ -8979,6 +8990,8 @@ def de_json(cls, json_string): elif message_type == 'channel': chat = Chat.de_json(obj['chat']) return MessageOriginChannel(date=obj['date'], chat=chat, message_id=obj['message_id'], author_signature=obj.get('author_signature')) + else: + raise ValueError(f"Unknown message origin type: {message_type}.") def __init__(self, type: str, date: int) -> None: self.type: str = type @@ -10513,13 +10526,17 @@ def de_json(cls, json_string): return cls(**obj) -class TransactionPartner(JsonDeserializable): +class TransactionPartner(JsonDeserializable, ABC): # noinspection PyUnresolvedReferences """ This object describes the source of a transaction, or its recipient for outgoing transactions. Currently, it can be one of TransactionPartnerFragment TransactionPartnerUser TransactionPartnerOther + TransactionPartnerTelegramAds + TransactionPartnerTelegramApi + TransactionPartnerAffiliateProgram + TransactionPartnerChat Telegram documentation: https://core.telegram.org/bots/api#transactionpartner @@ -10548,6 +10565,8 @@ def de_json(cls, json_string): return TransactionPartnerOther.de_json(obj) elif obj["type"] == "chat": return TransactionPartnerChat.de_json(obj) + else: + raise ValueError(f"Unknown transaction partner type: {obj['type']}") # noinspection PyShadowingBuiltins @@ -10581,6 +10600,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class TransactionPartnerTelegramApi(TransactionPartner): """ Describes a transaction with payment for paid broadcasting. @@ -10796,7 +10816,7 @@ def __init__(self, transactions, **kwargs): self.transactions: List[StarTransaction] = transactions -class PaidMedia(JsonDeserializable): +class PaidMedia(JsonDeserializable, ABC): """ This object describes paid media. Currently, it can be one of @@ -10820,6 +10840,8 @@ def de_json(cls, json_string): return PaidMediaPhoto.de_json(obj) elif obj["type"] == "video": return PaidMediaVideo.de_json(obj) + else: + raise ValueError("Unknown type of PaidMedia: {0}".format(obj["type"])) # noinspection PyShadowingBuiltins @@ -10947,7 +10969,7 @@ def __init__(self, star_count, paid_media, **kwargs): # noinspection PyShadowingBuiltins -class InputPaidMedia(JsonSerializable): +class InputPaidMedia(Dictionaryable, JsonSerializable): """ This object describes the paid media to be sent. Currently, it can be one of InputPaidMediaPhoto @@ -10970,6 +10992,10 @@ def __init__(self, type: str, media: Union[str, InputFile], **kwargs): self._media_name = service_utils.generate_random_token() self._media_dic = 'attach://{0}'.format(self._media_name) + if self.__class__ is InputPaidMedia: + # Make InputPaidMedia as ABC some time... + log_deprecation_warning('The InputPaidMedia class should not be instantiated directly. Use particular InputPaidMediaXXX class instead') + def to_json(self): return json.dumps(self.to_dict()) @@ -10979,7 +11005,8 @@ def to_dict(self): 'media': self._media_dic } return data - + + class InputPaidMediaPhoto(InputPaidMedia): """ The paid media to send is a photo. @@ -11000,7 +11027,8 @@ class InputPaidMediaPhoto(InputPaidMedia): def __init__(self, media: Union[str, InputFile], **kwargs): super().__init__(type='photo', media=media) - + + class InputPaidMediaVideo(InputPaidMedia): """ The paid media to send is a video. @@ -11058,8 +11086,6 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[InputFile] self.cover: Optional[Union[str,InputFile]] = cover self.start_timestamp: Optional[int] = start_timestamp - - def to_dict(self): data = super().to_dict() if self.thumbnail: @@ -11078,6 +11104,7 @@ def to_dict(self): data['start_timestamp'] = self.start_timestamp return data + class RefundedPayment(JsonDeserializable): """ This object contains basic information about a refunded payment. @@ -11176,6 +11203,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class PreparedInlineMessage(JsonDeserializable): """ Describes an inline message to be sent by a user of a Mini App. @@ -11203,6 +11231,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class Gift(JsonDeserializable): """ This object represents a gift that can be sent by the bot. @@ -11245,7 +11274,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) return cls(**obj) - + + class Gifts(JsonDeserializable): """ This object represent a list of gifts. @@ -11270,6 +11300,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class TransactionPartnerAffiliateProgram(TransactionPartner): """ Describes the affiliate program that issued the affiliate commission received via this transaction. @@ -11347,6 +11378,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class TransactionPartnerChat(TransactionPartner): """ Describes a transaction with a chat. @@ -11497,6 +11529,7 @@ def to_dict(self): 'premium_subscription': self.premium_subscription } return data + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11522,13 +11555,15 @@ class StarAmount(JsonDeserializable): def __init__(self, amount, nanostar_amount=None, **kwargs): self.amount: int = amount self.nanostar_amount: Optional[int] = nanostar_amount + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - + +# noinspection PyShadowingBuiltins class OwnedGift(JsonDeserializable, ABC): """ This object describes a gift received and owned by a user or a chat. Currently, it can be one of @@ -11540,9 +11575,8 @@ class OwnedGift(JsonDeserializable, ABC): def __init__(self, type, **kwargs): self.type: str = type - self.gift: Union[Gift, UniqueGift] = None - - + self.gift: Optional[Union[Gift, UniqueGift]] = None + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11551,7 +11585,11 @@ def de_json(cls, json_string): return OwnedGiftRegular.de_json(obj) elif obj["type"] == "unique": return OwnedGiftUnique.de_json(obj) - + else: + raise ValueError(f"Unknown gift type: {obj['type']}.") + + +# noinspection PyShadowingBuiltins class OwnedGiftRegular(OwnedGift): """ This object describes a regular gift owned by a user or a chat. @@ -11616,6 +11654,7 @@ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=N self.was_refunded: Optional[bool] = was_refunded self.convert_star_count: Optional[int] = convert_star_count self.prepaid_upgrade_star_count: Optional[int] = prepaid_upgrade_star_count + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11626,7 +11665,9 @@ def de_json(cls, json_string): if 'entities' in obj: obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] return cls(**obj) - + + +# noinspection PyShadowingBuiltins class OwnedGiftUnique(OwnedGift): """ This object describes a unique gift owned by a user or a chat. @@ -11674,6 +11715,7 @@ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=N self.can_be_transferred: Optional[bool] = can_be_transferred self.transfer_star_count: Optional[int] = transfer_star_count self.next_transfer_date: Optional[int] = next_transfer_date + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11707,6 +11749,7 @@ def __init__(self, total_count, gifts, next_offset=None, **kwargs): self.total_count: int = total_count self.gifts: List[OwnedGift] = gifts self.next_offset: Optional[str] = next_offset + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11715,7 +11758,6 @@ def de_json(cls, json_string): return cls(**obj) - class UniqueGift(JsonDeserializable): """ This object describes a unique gift that was upgraded from a regular gift. @@ -11750,6 +11792,7 @@ def __init__(self, base_name, name, number, model, symbol, backdrop, **kwargs): self.model: UniqueGiftModel = model self.symbol: UniqueGiftSymbol = symbol self.backdrop: UniqueGiftBackdrop = backdrop + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11783,13 +11826,15 @@ def __init__(self, name, sticker, rarity_per_mille, **kwargs): self.name: str = name self.sticker: Sticker = sticker self.rarity_per_mille: int = rarity_per_mille + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) return cls(**obj) - + + class UniqueGiftSymbol(JsonDeserializable): """ This object describes the symbol shown on the pattern of a unique gift. @@ -11813,13 +11858,15 @@ def __init__(self, name, sticker, rarity_per_mille, **kwargs): self.name: str = name self.sticker: Sticker = sticker self.rarity_per_mille: int = rarity_per_mille + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) return cls(**obj) - + + class UniqueGiftBackdropColors(JsonDeserializable): """ This object describes the colors of the backdrop of a unique gift. @@ -11846,12 +11893,14 @@ def __init__(self, center_color, edge_color, symbol_color, text_color, **kwargs) self.edge_color: int = edge_color self.symbol_color: int = symbol_color self.text_color: int = text_color + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - + + class UniqueGiftBackdrop(JsonDeserializable): """ This object describes the backdrop of a unique gift. @@ -11874,6 +11923,7 @@ def __init__(self, name, colors, rarity_per_mille, **kwargs): self.name: str = name self.colors: UniqueGiftBackdropColors = colors self.rarity_per_mille: int = rarity_per_mille + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11881,6 +11931,8 @@ def de_json(cls, json_string): obj['colors'] = UniqueGiftBackdropColors.de_json(obj['colors']) return cls(**obj) + +# noinspection PyShadowingBuiltins class InputStoryContent(JsonSerializable, ABC): """ This object describes the content of a story to post. Currently, it can be one of @@ -11888,7 +11940,6 @@ class InputStoryContent(JsonSerializable, ABC): InputStoryContentVideo Telegram documentation: https://core.telegram.org/bots/api#inputstorycontent - """ def __init__(self, type: str, **kwargs): self.type: str = type @@ -11956,6 +12007,7 @@ def __init__(self, video: InputFile, duration: Optional[float] = None, cover_fra self.duration: Optional[float] = duration self.cover_frame_timestamp: Optional[float] = cover_frame_timestamp self.is_animation: Optional[bool] = is_animation + def to_json(self): return json.dumps(self.to_dict()) @@ -11971,6 +12023,7 @@ def to_dict(self): if self.is_animation is not None: data['is_animation'] = self.is_animation return data + def convert_input_story(self): return self.to_json(), {self._video_name: self.video} @@ -12010,8 +12063,10 @@ def __init__(self, x_percentage: float, y_percentage: float, width_percentage: f self.height_percentage: float = height_percentage self.rotation_angle: float = rotation_angle self.corner_radius_percentage: float = corner_radius_percentage + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'x_percentage': self.x_percentage, @@ -12051,8 +12106,10 @@ def __init__(self, country_code: str, state: Optional[str] = None, city: Optiona self.state: Optional[str] = state self.city: Optional[str] = city self.street: Optional[str] = street + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'country_code': self.country_code @@ -12064,7 +12121,9 @@ def to_dict(self): if self.street is not None: data['street'] = self.street return data + +# noinspection PyShadowingBuiltins class StoryAreaType(JsonSerializable, ABC): """ Describes the type of a clickable area on a story. Currently, it can be one of @@ -12109,8 +12168,10 @@ def __init__(self,latitude: float, longitude: float, address: LocationAddress = self.latitude: float = latitude self.longitude: float = longitude self.address: Optional[LocationAddress] = address + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12148,8 +12209,10 @@ def __init__(self, reaction_type: ReactionType, is_dark: Optional[bool] = None, self.reaction_type: ReactionType = reaction_type self.is_dark: Optional[bool] = is_dark self.is_flipped: Optional[bool] = is_flipped + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12160,7 +12223,8 @@ def to_dict(self): if self.is_flipped is not None: data['is_flipped'] = self.is_flipped return data - + + class StoryAreaTypeLink(StoryAreaType): """ Describes a story area pointing to an HTTP or tg:// link. Currently, a story can have up to 3 link areas. @@ -12179,15 +12243,18 @@ class StoryAreaTypeLink(StoryAreaType): def __init__(self, url: str, **kwargs): super().__init__(type="link") self.url: str = url + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, 'url': self.url } return data - + + class StoryAreaTypeWeather(StoryAreaType): """ Describes a story area containing weather information. Currently, a story can have up to 3 weather areas. @@ -12214,8 +12281,10 @@ def __init__(self, temperature: float, emoji: str, background_color: int, **kwar self.temperature: float = temperature self.emoji: str = emoji self.background_color: int = background_color + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12224,7 +12293,8 @@ def to_dict(self): 'background_color': self.background_color } return data - + + class StoryAreaTypeUniqueGift(StoryAreaType): """ Describes a story area pointing to a unique gift. Currently, a story can have at most 1 unique gift area. @@ -12243,8 +12313,10 @@ class StoryAreaTypeUniqueGift(StoryAreaType): def __init__(self, name: str, **kwargs): super().__init__(type="unique_gift") self.name: str = name + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12254,6 +12326,7 @@ def to_dict(self): return data +# noinspection PyShadowingBuiltins class StoryArea(JsonSerializable): """ Describes a clickable area on a story media. @@ -12272,8 +12345,10 @@ class StoryArea(JsonSerializable): def __init__(self, position: StoryAreaPosition, type: StoryAreaType, **kwargs): self.position: StoryAreaPosition = position self.type: StoryAreaType = type + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'position': self.position.to_dict(), @@ -12327,6 +12402,7 @@ def __init__(self, gift: Gift, owned_gift_id: Optional[str] = None, convert_star self.text: Optional[str] = text self.entities: Optional[List[MessageEntity]] = entities self.is_private: Optional[bool] = is_private + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -12335,7 +12411,8 @@ def de_json(cls, json_string): if 'entities' in obj: obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] return cls(**obj) - + + class UniqueGiftInfo(JsonDeserializable): """ This object describes a service message about a unique gift that was sent or received. @@ -12396,6 +12473,7 @@ class PaidMessagePriceChanged(JsonDeserializable): """ def __init__(self, paid_message_star_count: int, **kwargs): self.paid_message_star_count: int = paid_message_star_count + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -12403,7 +12481,7 @@ def de_json(cls, json_string): return cls(**obj) -class InputProfilePhoto(JsonSerializable): +class InputProfilePhoto(JsonSerializable, ABC): """ This object describes a profile photo to set. Currently, it can be one of InputProfilePhotoStatic @@ -12415,6 +12493,7 @@ class InputProfilePhoto(JsonSerializable): :rtype: :class:`InputProfilePhoto` """ + class InputProfilePhotoStatic(InputProfilePhoto): """ This object describes a static profile photo to set. @@ -12434,9 +12513,9 @@ class InputProfilePhotoStatic(InputProfilePhoto): def __init__(self, photo: InputFile, **kwargs): self.type: str = "static" self.photo: InputFile = photo - self._photo_name = service_utils.generate_random_token() self._photo_dic = "attach://{}".format(self._photo_name) + def to_json(self): return json.dumps(self.to_dict()) @@ -12446,6 +12525,7 @@ def to_dict(self): 'photo': self._photo_dic } return data + def convert_input_profile_photo(self): return self.to_json(), {self._photo_name: self.photo} @@ -12475,8 +12555,10 @@ def __init__(self, animation: InputFile, main_frame_timestamp: Optional[float] = self._animation_name = service_utils.generate_random_token() self._animation_dic = "attach://{}".format(self._animation_name) self.main_frame_timestamp: Optional[float] = main_frame_timestamp + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12485,10 +12567,12 @@ def to_dict(self): if self.main_frame_timestamp is not None: data['main_frame_timestamp'] = self.main_frame_timestamp return data + def convert_input_profile_photo(self): return self.to_json(), {self._animation_name: self.animation} +# noinspection PyShadowingBuiltins class ChecklistTask(JsonDeserializable): """ Describes a task in a checklist. @@ -12531,6 +12615,7 @@ def de_json(cls, json_string): if 'completed_by_user' in obj: obj['completed_by_user'] = User.de_json(obj['completed_by_user']) return cls(**obj) + class Checklist(JsonDeserializable): """ @@ -12575,6 +12660,8 @@ def de_json(cls, json_string): obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] return cls(**obj) + +# noinspection PyShadowingBuiltins class InputChecklistTask(JsonSerializable): """ Describes a task to add to a checklist. @@ -12616,7 +12703,8 @@ def to_dict(self): if self.text_entities: data['text_entities'] = [entity.to_dict() for entity in self.text_entities] return data - + + class InputChecklist(JsonSerializable): """ Describes a checklist to create. @@ -12726,6 +12814,7 @@ class ChecklistTasksAdded(JsonDeserializable): def __init__(self, tasks: List[ChecklistTask], checklist_message: Optional[Message] = None, **kwargs): self.checklist_message: Optional[Message] = checklist_message self.tasks: List[ChecklistTask] = tasks + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -12735,6 +12824,7 @@ def de_json(cls, json_string): obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] return cls(**obj) + class DirectMessagePriceChanged(JsonDeserializable): """ Describes a service message about a change in the price of direct messages sent to a channel chat. From 18c524f1fb8d0919fe98654e26d68c3a10807c46 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 26 Jul 2025 15:35:32 +0300 Subject: [PATCH 1782/1808] ChatMemberXXX classes redesigned --- telebot/types.py | 200 ++++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 98 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 4a58c2529..a707b72ba 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3251,7 +3251,7 @@ def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_un self.big_file_unique_id: str = big_file_unique_id -class ChatMember(JsonDeserializable): +class ChatMember(JsonDeserializable, ABC): """ This object contains information about one member of a chat. Currently, the following 6 types of chat members are supported: @@ -3266,78 +3266,31 @@ class ChatMember(JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#chatmember """ + def __init__(self, user, status, **kwargs): + self.user: User = user + self.status: str = status + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) - member_type = obj['status'] + status = obj['status'] # Ordered according to estimated appearance frequency. - if member_type == "member": + if status == "member": return ChatMemberMember(**obj) - elif member_type == "left": + elif status == "left": return ChatMemberLeft(**obj) - elif member_type == "kicked": + elif status == "kicked": return ChatMemberBanned(**obj) - elif member_type == "restricted": + elif status == "restricted": return ChatMemberRestricted(**obj) - elif member_type == "administrator": + elif status == "administrator": return ChatMemberAdministrator(**obj) - elif member_type == "creator": + elif status == "creator": return ChatMemberOwner(**obj) else: - # Should not be here. For "if something happen" compatibility - return cls(**obj) - - def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, - can_post_messages=None, can_edit_messages=None, can_delete_messages=None, - can_restrict_members=None, can_promote_members=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, is_member=None, - can_send_messages=None, can_send_audios=None, can_send_documents=None, - can_send_photos=None, can_send_videos=None, can_send_video_notes=None, - can_send_voice_notes=None, - can_send_polls=None, - can_send_other_messages=None, can_add_web_page_previews=None, - can_manage_chat=None, can_manage_video_chats=None, - until_date=None, can_manage_topics=None, - can_post_stories=None, can_edit_stories=None, can_delete_stories=None, - **kwargs): - self.user: User = user - self.status: str = status - self.custom_title: str = custom_title - self.is_anonymous: bool = is_anonymous - self.can_be_edited: bool = can_be_edited - self.can_post_messages: bool = can_post_messages - self.can_edit_messages: bool = can_edit_messages - self.can_delete_messages: bool = can_delete_messages - self.can_restrict_members: bool = can_restrict_members - self.can_promote_members: bool = can_promote_members - self.can_change_info: bool = can_change_info - self.can_invite_users: bool = can_invite_users - self.can_pin_messages: bool = can_pin_messages - self.is_member: bool = is_member - self.can_send_messages: bool = can_send_messages - self.can_send_polls: bool = can_send_polls - self.can_send_other_messages: bool = can_send_other_messages - self.can_add_web_page_previews: bool = can_add_web_page_previews - self.can_manage_chat: bool = can_manage_chat - self.can_manage_video_chats: bool = can_manage_video_chats - self.until_date: int = until_date - self.can_manage_topics: bool = can_manage_topics - self.can_send_audios: bool = can_send_audios - self.can_send_documents: bool = can_send_documents - self.can_send_photos: bool = can_send_photos - self.can_send_videos: bool = can_send_videos - self.can_send_video_notes: bool = can_send_video_notes - self.can_send_voice_notes: bool = can_send_voice_notes - self.can_post_stories: bool = can_post_stories - self.can_edit_stories: bool = can_edit_stories - self.can_delete_stories: bool = can_delete_stories - - @property - def can_manage_voice_chats(self): - log_deprecation_warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') - return self.can_manage_video_chats + raise ValueError(f"Unknown chat member type: {status}.") # noinspection PyUnresolvedReferences @@ -3362,7 +3315,10 @@ class ChatMemberOwner(ChatMember): :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberOwner` """ - pass + def __init__(self, user, status, is_anonymous, custom_title=None, **kwargs): + super().__init__(user, status, **kwargs) + self.is_anonymous: bool = is_anonymous + self.custom_title: Optional[str] = custom_title # noinspection PyUnresolvedReferences @@ -3409,36 +3365,60 @@ class ChatMemberAdministrator(ChatMember): :param can_invite_users: True, if the user is allowed to invite new users to the chat :type can_invite_users: :obj:`bool` + :param can_post_stories: True, if the administrator can post channel stories + :type can_post_stories: :obj:`bool` + + :param can_edit_stories: True, if the administrator can edit stories + :type can_edit_stories: :obj:`bool` + + :param can_delete_stories: True, if the administrator can delete stories of other users + :type can_delete_stories: :obj:`bool` + :param can_post_messages: Optional. True, if the administrator can post in the channel; channels only :type can_post_messages: :obj:`bool` - :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin - messages; channels only + :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin messages; channels only :type can_edit_messages: :obj:`bool` :param can_pin_messages: Optional. True, if the user is allowed to pin messages; groups and supergroups only :type can_pin_messages: :obj:`bool` - :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; - supergroups only + :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only :type can_manage_topics: :obj:`bool` :param custom_title: Optional. Custom title for this user :type custom_title: :obj:`str` - :param can_post_stories: Optional. True, if the administrator can post channel stories - :type can_post_stories: :obj:`bool` - - :param can_edit_stories: Optional. True, if the administrator can edit stories - :type can_edit_stories: :obj:`bool` - - :param can_delete_stories: Optional. True, if the administrator can delete stories of other users - :type can_delete_stories: :obj:`bool` - :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberAdministrator` """ - pass + def __init__(self, user, status, can_be_edited, is_anonymous, can_manage_chat, can_delete_messages, + can_manage_video_chats, can_restrict_members, can_promote_members, can_change_info, can_invite_users, + can_post_stories, can_edit_stories, can_delete_stories, can_post_messages=None, can_edit_messages=None, + can_pin_messages=None, can_manage_topics=None, custom_title=None, **kwargs): + super().__init__(user, status, **kwargs) + self.can_be_edited: bool = can_be_edited + self.is_anonymous: bool = is_anonymous + self.can_manage_chat: bool = can_manage_chat + self.can_delete_messages: bool = can_delete_messages + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_post_stories: bool = can_post_stories + self.can_edit_stories: bool = can_edit_stories + self.can_delete_stories: bool = can_delete_stories + self.can_post_messages: Optional[bool] = can_post_messages + self.can_edit_messages: Optional[bool] = can_edit_messages + self.can_pin_messages: Optional[bool] = can_pin_messages + self.can_manage_topics: Optional[bool] = can_manage_topics + self.custom_title: Optional[str] = custom_title + + @property + def can_manage_voice_chats(self): + log_deprecation_warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') + return self.can_manage_video_chats # noinspection PyUnresolvedReferences @@ -3454,10 +3434,15 @@ class ChatMemberMember(ChatMember): :param user: Information about the user :type user: :class:`telebot.types.User` + :param until_date: Optional. Date when the user's subscription will expire; Unix time. If 0, then the user is a member forever + :type until_date: :obj:`int` + :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberMember` """ - pass + def __init__(self, user, status, until_date=None, **kwargs): + super().__init__(user, status, **kwargs) + self.until_date: Optional[int] = until_date # noinspection PyUnresolvedReferences @@ -3476,18 +3461,6 @@ class ChatMemberRestricted(ChatMember): :param is_member: True, if the user is a member of the chat at the moment of the request :type is_member: :obj:`bool` - :param can_change_info: True, if the user is allowed to change the chat title, photo and other settings - :type can_change_info: :obj:`bool` - - :param can_invite_users: True, if the user is allowed to invite new users to the chat - :type can_invite_users: :obj:`bool` - - :param can_pin_messages: True, if the user is allowed to pin messages - :type can_pin_messages: :obj:`bool` - - :param can_manage_topics: True, if the user is allowed to create forum topics - :type can_manage_topics: :obj:`bool` - :param can_send_messages: True, if the user is allowed to send text messages, contacts, locations and venues :type can_send_messages: :obj:`bool` @@ -3512,21 +3485,52 @@ class ChatMemberRestricted(ChatMember): :param can_send_polls: True, if the user is allowed to send polls :type can_send_polls: :obj:`bool` - :param can_send_other_messages: True, if the user is allowed to send animations, games, stickers and use inline - bots + :param can_send_other_messages: True, if the user is allowed to send animations, games, stickers and use inline bots :type can_send_other_messages: :obj:`bool` :param can_add_web_page_previews: True, if the user is allowed to add web page previews to their messages :type can_add_web_page_previews: :obj:`bool` - :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is restricted - forever + :param can_change_info: True, if the user is allowed to change the chat title, photo and other settings + :type can_change_info: :obj:`bool` + + :param can_invite_users: True, if the user is allowed to invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_pin_messages: True, if the user is allowed to pin messages + :type can_pin_messages: :obj:`bool` + + :param can_manage_topics: True, if the user is allowed to create forum topics + :type can_manage_topics: :obj:`bool` + + :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is restricted forever :type until_date: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberRestricted` """ - pass + def __init__(self, user, status, is_member, can_send_messages, can_send_audios, can_send_documents, + can_send_photos, can_send_videos, can_send_video_notes, can_send_voice_notes, can_send_polls, + can_send_other_messages, can_add_web_page_previews, + can_change_info, can_invite_users, can_pin_messages, can_manage_topics, + until_date=None, **kwargs): + super().__init__(user, status, **kwargs) + self.is_member: bool = is_member + self.can_send_messages: bool = can_send_messages + self.can_send_audios: bool = can_send_audios + self.can_send_documents: bool = can_send_documents + self.can_send_photos: bool = can_send_photos + self.can_send_videos: bool = can_send_videos + self.can_send_video_notes: bool = can_send_video_notes + self.can_send_voice_notes: bool = can_send_voice_notes + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + self.can_manage_topics: bool = can_manage_topics + self.until_date: Optional[int] = until_date # noinspection PyUnresolvedReferences @@ -3561,14 +3565,15 @@ class ChatMemberBanned(ChatMember): :param user: Information about the user :type user: :class:`telebot.types.User` - :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is banned - forever + :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is banned forever :type until_date: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberBanned` """ - pass + def __init__(self, user, status, until_date=None, **kwargs): + super().__init__(user, status, **kwargs) + self.until_date: Optional[int] = until_date class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): @@ -3577,8 +3582,7 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): Telegram Documentation: https://core.telegram.org/bots/api#chatpermissions - :param can_send_messages: Optional. True, if the user is allowed to send text messages, contacts, locations and - venues + :param can_send_messages: Optional. True, if the user is allowed to send text messages, contacts, locations and venues :type can_send_messages: :obj:`bool` :param can_send_audios: Optional. True, if the user is allowed to send audios From 9dfb79661f735ca757f1d11bc2435e94ef581595 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 26 Jul 2025 15:41:20 +0300 Subject: [PATCH 1783/1808] Update test_chat_member_updated with appropriate params --- tests/test_types.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_types.py b/tests/test_types.py index 197834748..de01b0e5c 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -242,7 +242,7 @@ def test_json_chat_invite_link(): def test_chat_member_updated(): - json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "administrator"}}' + json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "left"}}' cm_updated = types.ChatMemberUpdated.de_json(json_string) assert cm_updated.chat.id == -1234567890123 assert cm_updated.from_user.id == 133869498 @@ -309,5 +309,3 @@ def test_message_entity(): sample_string_7 = r'{"update_id":934522167,"message":{"message_id":1374526,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179716,"reply_to_message":{"message_id":1374510,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en"},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1712765863,"text":"text @UserName b i s u c p #hashtag https://example.com","entities":[{"offset":5,"length":9,"type":"mention"},{"offset":15,"length":1,"type":"bold"},{"offset":17,"length":1,"type":"italic"},{"offset":19,"length":1,"type":"strikethrough"},{"offset":21,"length":1,"type":"underline"},{"offset":23,"length":1,"type":"code"},{"offset":25,"length":1,"type":"spoiler"},{"offset":27,"length":8,"type":"hashtag"},{"offset":36,"length":19,"type":"url"}],"link_preview_options":{"is_disabled":true}},"quote":{"text":"text @UserName b i s u c p #hashtag https://example.com","entities":[{"offset":15,"length":1,"type":"bold"},{"offset":17,"length":1,"type":"italic"},{"offset":19,"length":1,"type":"strikethrough"},{"offset":21,"length":1,"type":"underline"},{"offset":25,"length":1,"type":"spoiler"}],"position":0,"is_manual":true},"text":"quote reply"}}' message_7 = types.Update.de_json(sample_string_7).message assert message_7.quote.html_text == 'text @UserName b i s u c p #hashtag https://example.com' - - From c01b90452b9fa1df554709e6efad116db41ef9c2 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 26 Jul 2025 15:42:29 +0300 Subject: [PATCH 1784/1808] Update test_chat_member_updated with appropriate params --- tests/test_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_types.py b/tests/test_types.py index de01b0e5c..630ab658c 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -248,7 +248,7 @@ def test_chat_member_updated(): assert cm_updated.from_user.id == 133869498 assert cm_updated.date == 1624119999 assert cm_updated.old_chat_member.status == "member" - assert cm_updated.new_chat_member.status == "administrator" + assert cm_updated.new_chat_member.status == "left" def test_webhook_info(): From 2a2adbcf753aa6b64b23ab49b1cf3b3b0e70d3e1 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Fri, 1 Aug 2025 14:19:22 +0300 Subject: [PATCH 1785/1808] Rollback changes in dependencies. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d2db6d2b5..c46727508 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ "Environment :: Console", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)" ] -dependencies = ["pytest", "requests", "wheel", "aiohttp"] +dependencies = ["requests"] [project.urls] Homepage = "https://github.com/eternnoir/pyTelegramBotAPI" From 9a58fdad5a669d7e049e8ca359820aee3fe29715 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 21:08:36 +0500 Subject: [PATCH 1786/1808] Added reply_to_checklist_task_id and checklist_task_id --- telebot/types.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index a707b72ba..68fe8ab36 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -968,6 +968,9 @@ class Message(JsonDeserializable): :param reply_to_story: Optional. For replies to a story, the original story :type reply_to_story: :class:`telebot.types.Story` + :param reply_to_checklist_task_id: Optional. Identifier of the specific checklist task that is being replied to + :type reply_to_checklist_task_id: :obj:`str` + :param via_bot: Optional. Bot through which the message was sent :type via_bot: :class:`telebot.types.User` @@ -1484,6 +1487,8 @@ def de_json(cls, json_string): if 'direct_message_price_changed' in obj: opts['direct_message_price_changed'] = DirectMessagePriceChanged.de_json(obj['direct_message_price_changed']) content_type = 'direct_message_price_changed' + if 'reply_to_checklist_task_id' in obj: + opts['reply_to_checklist_task_id'] = obj['reply_to_checklist_task_id'] return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1610,6 +1615,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.checklist_tasks_done: Optional[ChecklistTasksDone] = None self.checklist_tasks_added: Optional[List[ChecklistTasksAdded]] = None self.direct_message_price_changed: Optional[DirectMessagePriceChanged] = None + self.reply_to_checklist_task_id: Optional[int] = None for key in options: setattr(self, key, options[key]) @@ -9390,6 +9396,9 @@ class ReplyParameters(JsonDeserializable, Dictionaryable, JsonSerializable): :param quote_position: Optional. Position of the quote in the original message in UTF-16 code units :type quote_position: :obj:`int` + :param checklist_task_id: Optional. Optional. Identifier of the specific checklist task to be replied to + :type checklist_task_id: :obj:`int` + :return: Instance of the class :rtype: :class:`ReplyParameters` """ @@ -9405,7 +9414,7 @@ def de_json(cls, json_string): def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, allow_sending_without_reply: Optional[bool] = None, quote: Optional[str] = None, quote_parse_mode: Optional[str] = None, quote_entities: Optional[List[MessageEntity]] = None, - quote_position: Optional[int] = None, **kwargs) -> None: + quote_position: Optional[int] = None, checklist_task_id: Optional[int] = None, **kwargs) -> None: self.message_id: int = message_id self.chat_id: Optional[Union[int, str]] = chat_id self.allow_sending_without_reply: Optional[bool] = allow_sending_without_reply @@ -9413,6 +9422,7 @@ def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, self.quote_parse_mode: Optional[str] = quote_parse_mode self.quote_entities: Optional[List[MessageEntity]] = quote_entities self.quote_position: Optional[int] = quote_position + self.checklist_task_id: Optional[int] = checklist_task_id def to_dict(self) -> dict: json_dict = { @@ -9430,6 +9440,8 @@ def to_dict(self) -> dict: json_dict['quote_entities'] = [entity.to_dict() for entity in self.quote_entities] if self.quote_position is not None: json_dict['quote_position'] = self.quote_position + if self.checklist_task_id is not None: + json_dict['checklist_task_id'] = self.checklist_task_id return json_dict def to_json(self) -> str: From 54b41ed5fa0e233499cd919a1254d29d9b79febf Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 21:13:18 +0500 Subject: [PATCH 1787/1808] Added the field publisher_chat to the classes Gift and UniqueGift which can be used to get information about the chat that published a gift. --- telebot/types.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 68fe8ab36..5a317d7ba 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11272,23 +11272,30 @@ class Gift(JsonDeserializable): :param remaining_count: Optional. The number of remaining gifts of this type that can be sent; for limited gifts only :type remaining_count: :obj:`int` + :param publisher_chat: Optional. Information about the chat that published the gift + :type publisher_chat: :class:`Chat` + :return: Instance of the class :rtype: :class:`Gift` """ - def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, **kwargs): + def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, + publisher_chat=None, **kwargs): self.id: str = id self.sticker: Sticker = sticker self.star_count: int = star_count self.total_count: Optional[int] = total_count self.remaining_count: Optional[int] = remaining_count self.upgrade_star_count: Optional[int] = upgrade_star_count + self.publisher_chat: Optional[Chat] = publisher_chat @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) + if 'publisher_chat' in obj: + obj['publisher_chat'] = Chat.de_json(obj['publisher_chat']) return cls(**obj) @@ -11798,16 +11805,20 @@ class UniqueGift(JsonDeserializable): :param backdrop: Backdrop of the gift :type backdrop: :class:`UniqueGiftBackdrop` + :param publisher_chat: Optional. Information about the chat that published the gift + :type publisher_chat: :class:`Chat` + :return: Instance of the class :rtype: :class:`UniqueGift` """ - def __init__(self, base_name, name, number, model, symbol, backdrop, **kwargs): + def __init__(self, base_name, name, number, model, symbol, backdrop, publisher_chat=None, **kwargs): self.base_name: str = base_name self.name: str = name self.number: int = number self.model: UniqueGiftModel = model self.symbol: UniqueGiftSymbol = symbol self.backdrop: UniqueGiftBackdrop = backdrop + self.publisher_chat: Optional[Chat] = publisher_chat @classmethod def de_json(cls, json_string): @@ -11816,6 +11827,8 @@ def de_json(cls, json_string): obj['model'] = UniqueGiftModel.de_json(obj['model']) obj['symbol'] = UniqueGiftSymbol.de_json(obj['symbol']) obj['backdrop'] = UniqueGiftBackdrop.de_json(obj['backdrop']) + if 'publisher_chat' in obj: + obj['publisher_chat'] = Chat.de_json(obj['publisher_chat']) return cls(**obj) From c29dd9aada733730c4970e67d7ec29d497f4681a Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 21:16:03 +0500 Subject: [PATCH 1788/1808] Added the field is_direct_messages to the classes Chat and ChatFullInfo which can be used to identify supergroups that are used as channel direct messages chats. --- telebot/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 5a317d7ba..fad52a60a 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -615,6 +615,9 @@ class ChatFullInfo(JsonDeserializable): :param is_forum: Optional. True, if the supergroup chat is a forum (has topics enabled) :type is_forum: :obj:`bool` + :param is_direct_messages: Optional. True, if the chat is the direct messages chat of a channel + :type is_direct_messages: :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` @@ -783,7 +786,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, business_opening_hours=None, personal_chat=None, birthdate=None, can_send_paid_media=None, - accepted_gift_types=None, **kwargs): + accepted_gift_types=None, is_direct_messages=None, **kwargs): self.id: int = id self.type: str = type self.title: Optional[str] = title @@ -829,6 +832,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.birthdate: Optional[Birthdate] = birthdate self.can_send_paid_media: Optional[bool] = can_send_paid_media self.accepted_gift_types: AcceptedGiftTypes = accepted_gift_types + self.is_direct_messages: Optional[bool] = is_direct_messages @property def can_send_gift(self) -> bool: """ From a848089eef610c22ef13db8a5097e6f27425f8f5 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 21:17:46 +0500 Subject: [PATCH 1789/1808] Added the field parent_chat to the class ChatFullInfo which indicates the parent channel chat for a channel direct messages chat. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index fad52a60a..acd0c2cc1 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -642,6 +642,9 @@ class ChatFullInfo(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 parent_chat: Optional. Information about the corresponding channel chat; for direct messages chats only + :type parent_chat: :class:`telebot.types.Chat` + :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` @@ -770,6 +773,8 @@ def de_json(cls, json_string): obj['birthdate'] = Birthdate.de_json(obj['birthdate']) if 'accepted_gift_types' in obj: obj['accepted_gift_types'] = AcceptedGiftTypes.de_json(obj['accepted_gift_types']) + if 'parent_chat' in obj: + obj['parent_chat'] = Chat.de_json(obj['parent_chat']) return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -786,7 +791,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, business_opening_hours=None, personal_chat=None, birthdate=None, can_send_paid_media=None, - accepted_gift_types=None, is_direct_messages=None, **kwargs): + accepted_gift_types=None, is_direct_messages=None, parent_chat=None, **kwargs): self.id: int = id self.type: str = type self.title: Optional[str] = title @@ -833,6 +838,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.can_send_paid_media: Optional[bool] = can_send_paid_media self.accepted_gift_types: AcceptedGiftTypes = accepted_gift_types self.is_direct_messages: Optional[bool] = is_direct_messages + self.parent_chat: Optional[Chat] = parent_chat @property def can_send_gift(self) -> bool: """ From 0df175748326cf509fb6e52abb18022d7a9923d6 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 21:20:56 +0500 Subject: [PATCH 1790/1808] Added the class DirectMessagesTopic and the field direct_messages_topic to the class Message, describing a topic of a direct messages chat. --- telebot/types.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index acd0c2cc1..b3958ac79 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -929,6 +929,9 @@ class Message(JsonDeserializable): :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups only :type message_thread_id: :obj:`int` + :param direct_messages_topic: Optional. Information about the direct messages chat topic that contains the message + :type direct_messages_topic: :class:`telebot.types.DirectMessagesTopic` + :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat. :type from_user: :class:`telebot.types.User` @@ -1499,6 +1502,8 @@ def de_json(cls, json_string): content_type = 'direct_message_price_changed' if 'reply_to_checklist_task_id' in obj: opts['reply_to_checklist_task_id'] = obj['reply_to_checklist_task_id'] + if 'direct_messages_topic' in obj: + opts['direct_messages_topic'] = DirectMessagesTopic.de_json(obj['direct_messages_topic']) return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1626,6 +1631,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.checklist_tasks_added: Optional[List[ChecklistTasksAdded]] = None self.direct_message_price_changed: Optional[DirectMessagePriceChanged] = None self.reply_to_checklist_task_id: Optional[int] = None + self.direct_messages_topic: Optional[DirectMessagesTopic] = None for key in options: setattr(self, key, options[key]) @@ -12888,3 +12894,32 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) + + +class DirectMessagesTopic(JsonDeserializable): + """ + Describes a topic of a direct messages chat. + + Telegram documentation: https://core.telegram.org/bots/api#directmessagestopic + + :param topic_id: Unique identifier of the topic + :type topic_id: :obj:`int` + + :param user: Optional. Information about the user that created the topic. Currently, it is always present + :type user: :class:`User` + + :return: Instance of the class + :rtype: :class:`DirectMessagesTopic` + """ + def __init__(self, topic_id: int, user: Optional[User] = None, **kwargs): + self.topic_id: int = topic_id + self.user: Optional[User] = user # for future compatibility, currently always present + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'user' in obj: + obj['user'] = User.de_json(obj['user']) + return cls(**obj) + From 1f6c6d96d17ef9d1c83053ad7cfe8b59ac2fc0b4 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 21:57:06 +0500 Subject: [PATCH 1791/1808] Added the parameter direct_messages_topic_id to the methods sendMessage, sendPhoto, sendVideo, sendAnimation, sendAudio, sendDocument, sendPaidMedia, sendSticker, sendVideoNote, sendVoice, sendLocation, sendVenue, sendContact, sendDice, sendInvoice, sendMediaGroup, copyMessage, copyMessages, forwardMessage and forwardMessages. This parameter can be used to send a message to a direct messages chat topic. --- telebot/__init__.py | 203 ++++++++++++++++++++++++++++-------- telebot/apihelper.py | 79 ++++++++++---- telebot/async_telebot.py | 209 ++++++++++++++++++++++++++++++-------- telebot/asyncio_helper.py | 77 ++++++++++---- 4 files changed, 447 insertions(+), 121 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 59a8d7db8..8506303ce 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1691,7 +1691,8 @@ def send_message( link_preview_options : Optional[types.LinkPreviewOptions]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send text messages. @@ -1754,6 +1755,10 @@ def send_message( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1802,7 +1807,9 @@ def send_message( reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, entities=entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, link_preview_options=link_preview_options, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + ) + ) def forward_message( @@ -1811,7 +1818,8 @@ def forward_message( protect_content: Optional[bool]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - video_start_timestamp: Optional[int]=None) -> types.Message: + video_start_timestamp: Optional[int]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -1841,6 +1849,10 @@ def forward_message( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1851,7 +1863,9 @@ def forward_message( apihelper.forward_message( self.token, chat_id, from_chat_id, message_id, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, - video_start_timestamp=video_start_timestamp)) + video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id + ) + ) def copy_message( @@ -1871,7 +1885,8 @@ def copy_message( reply_parameters: Optional[types.ReplyParameters]=None, show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, - video_start_timestamp: Optional[int]=None) -> types.MessageID: + video_start_timestamp: Optional[int]=None, + direct_messages_topic_id: Optional[int]=None) -> types.MessageID: """ Use this method to copy messages of any kind. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. @@ -1933,6 +1948,10 @@ def copy_message( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -1966,8 +1985,8 @@ def copy_message( reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - video_start_timestamp=video_start_timestamp)) - + video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id + )) def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -2019,7 +2038,7 @@ def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, - protect_content: Optional[bool]=None) -> List[types.MessageID]: + protect_content: Optional[bool]=None, direct_messages_topic_id: Optional[int]=None) -> List[types.MessageID]: """ Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content can't be forwarded. Album grouping is kept for forwarded messages. @@ -2045,6 +2064,10 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in :param protect_content: Protects the contents of the forwarded message from forwarding and saving :type protect_content: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.MessageID` """ @@ -2055,13 +2078,14 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in result = apihelper.forward_messages( self.token, chat_id, from_chat_id, message_ids, disable_notification=disable_notification, message_thread_id=message_thread_id, - protect_content=protect_content) + protect_content=protect_content, direct_messages_topic_id=direct_messages_topic_id) return [types.MessageID.de_json(message_id) for message_id in result] def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, - protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: + protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None, + direct_messages_topic_id: Optional[int] = None) -> List[types.MessageID]: """ Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, @@ -2093,6 +2117,10 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], :param remove_caption: Pass True to copy the messages without their captions :type remove_caption: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, an array of MessageId of the sent messages is returned. :rtype: :obj:`list` of :class:`telebot.types.MessageID` """ @@ -2101,7 +2129,8 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], result = apihelper.copy_messages( self.token, chat_id, from_chat_id, message_ids, disable_notification=disable_notification, - message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption) + message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption, + direct_messages_topic_id=direct_messages_topic_id) return [types.MessageID.de_json(message_id) for message_id in result] def send_checklist( @@ -2201,7 +2230,8 @@ def send_dice( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -2250,6 +2280,10 @@ def send_dice( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2279,7 +2313,9 @@ def send_dice( self.token, chat_id, emoji=emoji, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + ) + ) @@ -2299,7 +2335,8 @@ def send_photo( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2363,6 +2400,10 @@ def send_photo( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2396,8 +2437,10 @@ def send_photo( caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) - + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id + ) + ) def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -2417,7 +2460,8 @@ def send_audio( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2497,6 +2541,10 @@ def send_audio( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2532,8 +2580,10 @@ def send_audio( reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) - + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id + ) + ) def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -2550,7 +2600,8 @@ def send_voice( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=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, 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. @@ -2611,6 +2662,10 @@ def send_voice( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2641,7 +2696,7 @@ def send_voice( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) ) @@ -2664,7 +2719,8 @@ def send_document( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send general files. @@ -2737,6 +2793,10 @@ def send_document( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2781,7 +2841,8 @@ def send_document( timeout=timeout, caption=caption, thumbnail=thumbnail, caption_entities=caption_entities, disable_content_type_detection=disable_content_type_detection, visible_file_name=visible_file_name, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) ) @@ -2800,7 +2861,8 @@ def send_sticker( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2856,6 +2918,10 @@ def send_sticker( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2890,7 +2956,8 @@ def send_sticker( reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) ) @@ -2920,7 +2987,8 @@ def send_video( show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, cover: Optional[Union[Any, str]]=None, - start_timestamp: Optional[int]=None) -> types.Message: + start_timestamp: Optional[int]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -3014,6 +3082,10 @@ def send_video( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3056,7 +3128,7 @@ def send_video( protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - cover=cover, start_timestamp=start_timestamp) + cover=cover, start_timestamp=start_timestamp, direct_messages_topic_id=direct_messages_topic_id) ) @@ -3082,7 +3154,8 @@ def send_animation( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -3165,6 +3238,10 @@ def send_animation( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3201,7 +3278,8 @@ def send_animation( thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, width=width, height=height, message_thread_id=message_thread_id, reply_parameters=reply_parameters, has_spoiler=has_spoiler, business_connection_id=business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) ) @@ -3221,7 +3299,8 @@ def send_video_note( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -3286,6 +3365,10 @@ def send_video_note( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3319,7 +3402,8 @@ def send_video_note( self.token, chat_id, data, duration=duration, length=length, reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) ) def send_paid_media( @@ -3328,7 +3412,8 @@ def send_paid_media( show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, - payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None + payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None ) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -3378,6 +3463,10 @@ def send_paid_media( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3387,7 +3476,7 @@ def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - payload=payload, allow_paid_broadcast=allow_paid_broadcast) + payload=payload, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) ) @@ -3405,7 +3494,8 @@ def send_media_group( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> List[types.Message]: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -3449,6 +3539,10 @@ def send_media_group( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -3482,7 +3576,8 @@ def send_media_group( result = apihelper.send_media_group( self.token, chat_id, media, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) return [types.Message.de_json(msg) for msg in result] @@ -3503,7 +3598,8 @@ def send_location( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -3566,6 +3662,10 @@ def send_location( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3596,7 +3696,7 @@ def send_location( disable_notification=disable_notification, timeout=timeout, horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) ) @@ -3728,7 +3828,8 @@ def send_venue( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -3798,6 +3899,10 @@ def send_venue( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3828,7 +3933,7 @@ def send_venue( foursquare_type=foursquare_type, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, google_place_id=google_place_id, google_place_type=google_place_type, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) ) @@ -3845,7 +3950,8 @@ def send_contact( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -3902,6 +4008,10 @@ def send_contact( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3931,7 +4041,8 @@ def send_contact( self.token, chat_id, phone_number, first_name, last_name=last_name, vcard=vcard, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, - business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) ) @@ -5374,7 +5485,8 @@ def send_invoice( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Sends invoice. @@ -5487,6 +5599,10 @@ def send_invoice( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5526,7 +5642,8 @@ def send_invoice( provider_data=provider_data, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, max_tip_amount=max_tip_amount, suggested_tip_amounts=suggested_tip_amounts, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) ) def create_invoice_link(self, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index cba38bbdd..be8844373 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -247,7 +247,8 @@ def send_message( parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, link_preview_options=None, - business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): + business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, + direct_messages_topic_id=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -274,6 +275,8 @@ def send_message( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, method='post') @@ -427,7 +430,7 @@ def get_chat_member(token, chat_id, user_id): def forward_message( token, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None, protect_content=None, message_thread_id=None, - video_start_timestamp=None): + video_start_timestamp=None, direct_messages_topic_id=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -440,13 +443,15 @@ def forward_message( payload['message_thread_id'] = message_thread_id if video_start_timestamp: payload['video_start_timestamp'] = video_start_timestamp + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None, - video_start_timestamp=None): + video_start_timestamp=None, direct_messages_topic_id=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -473,6 +478,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['allow_paid_broadcast'] = allow_paid_broadcast if video_start_timestamp: payload['video_start_timestamp'] = video_start_timestamp + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) def send_checklist( @@ -510,7 +517,7 @@ def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, - business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): + business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -533,6 +540,8 @@ def send_dice( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) @@ -542,7 +551,8 @@ def send_photo( parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): + message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, + direct_messages_topic_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -580,13 +590,15 @@ def send_photo( payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, payload=None, allow_paid_broadcast=None): + business_connection_id=None, payload=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -612,6 +624,8 @@ def send_paid_media( _payload['payload'] = payload if allow_paid_broadcast is not None: _payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + _payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request( token, method_url, params=_payload, method='post' if files else 'get', @@ -622,7 +636,7 @@ def send_media_group( token, chat_id, media, disable_notification=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendMediaGroup' media_json, files = convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -642,6 +656,8 @@ def send_media_group( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request( token, method_url, params=payload, method='post' if files else 'get', @@ -655,7 +671,7 @@ def send_location( timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -684,6 +700,8 @@ def send_location( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) @@ -740,7 +758,7 @@ def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_markup=None, timeout=None, google_place_id=None, google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -769,6 +787,8 @@ def send_venue( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) @@ -776,7 +796,7 @@ def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -801,6 +821,8 @@ def send_contact( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) @@ -822,7 +844,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, - cover=None, start_timestamp=None): + cover=None, start_timestamp=None, direct_messages_topic_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -884,6 +906,8 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['cover'] = cover if start_timestamp: payload['start_timestamp'] = start_timestamp + if direct_messages_topic_id: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, files=files, method='post') @@ -893,7 +917,7 @@ def send_animation( parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None, has_spoiler=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -943,13 +967,15 @@ def send_animation( payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -983,13 +1009,15 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup= payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, message_thread_id=None, reply_parameters=None,business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -1029,13 +1057,15 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -1081,6 +1111,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1088,7 +1120,7 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -1135,6 +1167,8 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1726,7 +1760,7 @@ def send_invoice( disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1814,6 +1848,8 @@ def send_invoice( payload['provider_token'] = provider_token if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) @@ -2464,7 +2500,7 @@ def delete_messages(token, chat_id, message_ids): return _make_request(token, method_url, params=payload) def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, - message_thread_id=None, protect_content=None): + message_thread_id=None, protect_content=None, direct_messages_topic_id=None): method_url = 'forwardMessages' payload = { 'chat_id': chat_id, @@ -2477,10 +2513,13 @@ def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notifica payload['message_thread_id'] = message_thread_id if protect_content is not None: payload['protect_content'] = protect_content + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, - message_thread_id=None, protect_content=None, remove_caption=None, show_caption_above_media=None): + message_thread_id=None, protect_content=None, remove_caption=None, show_caption_above_media=None, + direct_messages_topic_id=None): method_url = 'copyMessages' payload = { 'chat_id': chat_id, @@ -2497,6 +2536,8 @@ def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notificatio payload['remove_caption'] = remove_caption if show_caption_above_media is not None: payload['show_caption_above_media'] = show_caption_above_media + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return _make_request(token, method_url, params=payload) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fe8013135..0be94c0a8 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3213,7 +3213,8 @@ async def send_message( link_preview_options: Optional[types.LinkPreviewOptions]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send text messages. @@ -3276,6 +3277,10 @@ async def send_message( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3331,7 +3336,10 @@ async def send_message( self.token, chat_id, text, reply_markup, parse_mode, disable_notification, timeout, entities, protect_content, message_thread_id, reply_parameters, link_preview_options, business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id + ) + ) async def forward_message( self, chat_id: Union[int, str], from_chat_id: Union[int, str], @@ -3339,7 +3347,8 @@ async def forward_message( protect_content: Optional[bool]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, - video_start_timestamp: Optional[int]=None) -> types.Message: + video_start_timestamp: Optional[int]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -3369,6 +3378,10 @@ async def forward_message( :param message_thread_id: Unique identifier for the target message thread (topic) of the forum; for forum supergroups only :type message_thread_id: :obj:`int` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3378,7 +3391,9 @@ async def forward_message( return types.Message.de_json( await asyncio_helper.forward_message(self.token, chat_id=chat_id, from_chat_id=from_chat_id, message_id=message_id, disable_notification=disable_notification, protect_content=protect_content, - timeout=timeout, message_thread_id=message_thread_id, video_start_timestamp=video_start_timestamp)) + timeout=timeout, message_thread_id=message_thread_id, video_start_timestamp=video_start_timestamp, + direct_messages_topic_id=direct_messages_topic_id) + ) async def copy_message( self, chat_id: Union[int, str], @@ -3397,7 +3412,8 @@ async def copy_message( reply_parameters: Optional[types.ReplyParameters]=None, show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, - video_start_timestamp: Optional[bool]=None) -> types.MessageID: + video_start_timestamp: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.MessageID: """ Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, @@ -3460,6 +3476,10 @@ async def copy_message( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -3494,7 +3514,8 @@ async def copy_message( disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, timeout=timeout, message_thread_id=message_thread_id, show_caption_above_media=show_caption_above_media, - allow_paid_broadcast=allow_paid_broadcast, video_start_timestamp=video_start_timestamp)) + allow_paid_broadcast=allow_paid_broadcast, video_start_timestamp=video_start_timestamp, + direct_messages_topic_id=direct_messages_topic_id)) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -3544,7 +3565,8 @@ async def delete_messages(self, chat_id: Union[int, str], message_ids: List[int] return await asyncio_helper.delete_messages(self.token, chat_id, message_ids) async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, - message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: + message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> List[types.MessageID]: """ Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content can't be forwarded. @@ -3570,18 +3592,24 @@ async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[s :param protect_content: Protects the contents of the forwarded message from forwarding and saving :type protect_content: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.MessageID` """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - result = await asyncio_helper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, protect_content) + result = await asyncio_helper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, protect_content, + direct_messages_topic_id=direct_messages_topic_id) return [types.MessageID.de_json(message_id) for message_id in result] async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, - protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: + protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None, + direct_messages_topic_id: Optional[int] = None) -> List[types.MessageID]: """ Use this method to copy messages of any kind. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. @@ -3611,13 +3639,17 @@ async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, :param remove_caption: Pass True to copy the messages without their captions :type remove_caption: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, an array of MessageId of the sent messages is returned. :rtype: :obj:`list` of :class:`telebot.types.MessageID` """ disable_notification = self.disable_notification if disable_notification is None else disable_notification protect_content = self.protect_content if protect_content is None else protect_content result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, - protect_content, remove_caption) + protect_content, remove_caption, direct_messages_topic_id) return [types.MessageID.de_json(message_id) for message_id in result] async def send_checklist( @@ -3717,7 +3749,8 @@ async def send_dice( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -3766,6 +3799,10 @@ async def send_dice( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3797,8 +3834,9 @@ async def send_dice( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast)) - + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + ) + ) async def send_photo( self, chat_id: Union[int, str], photo: Union[Any, str], @@ -3816,7 +3854,8 @@ async def send_photo( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -3880,6 +3919,10 @@ async def send_photo( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -3914,7 +3957,10 @@ async def send_photo( self.token, chat_id, photo, caption, reply_markup, parse_mode, disable_notification, timeout, caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id + ) + ) async def send_audio( self, chat_id: Union[int, str], audio: Union[Any, str], @@ -3934,7 +3980,8 @@ async def send_audio( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -4014,6 +4061,10 @@ async def send_audio( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4050,7 +4101,10 @@ async def send_audio( await asyncio_helper.send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_markup, parse_mode, disable_notification, timeout, thumbnail, - caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id + ) + ) async def send_voice( self, chat_id: Union[int, str], voice: Union[Any, str], @@ -4067,7 +4121,8 @@ async def send_voice( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=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, 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. @@ -4128,6 +4183,10 @@ async def send_voice( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -4160,7 +4219,9 @@ async def send_voice( self.token, chat_id, voice, caption, duration, reply_markup, parse_mode, disable_notification, timeout, caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast)) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + ) + ) async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], @@ -4182,7 +4243,8 @@ async def send_document( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send general files. @@ -4255,6 +4317,10 @@ async def send_document( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4303,7 +4369,10 @@ async def send_document( disable_notification = disable_notification, timeout = timeout, caption = caption, thumbnail= thumbnail, caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content, - message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id + ) + ) async def send_sticker( self, chat_id: Union[int, str], sticker: Union[Any, str], @@ -4319,7 +4388,8 @@ async def send_sticker( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -4375,6 +4445,10 @@ async def send_sticker( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4441,7 +4515,8 @@ async def send_video( show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, cover: Optional[Union[Any, str]]=None, - start_timestamp: Optional[int]=None) -> types.Message: + start_timestamp: Optional[int]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -4533,6 +4608,9 @@ async def send_video( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of a topic in a forum supergroup or channel, in which the message will be sent + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4575,7 +4653,8 @@ async def send_video( self.token, chat_id, video, duration, caption, reply_markup, parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, cover=cover, start_timestamp=start_timestamp)) + show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, cover=cover, start_timestamp=start_timestamp, + direct_messages_topic_id=direct_messages_topic_id)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -4599,7 +4678,8 @@ async def send_animation( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -4682,6 +4762,10 @@ async def send_animation( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4719,7 +4803,8 @@ async def send_animation( self.token, chat_id, animation, duration, caption, reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, - message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) + message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -4737,7 +4822,8 @@ async def send_video_note( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -4802,6 +4888,10 @@ async def send_video_note( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4837,7 +4927,7 @@ async def send_video_note( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_markup, disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast)) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id)) async def send_paid_media( self, chat_id: Union[int, str], star_count: int, media: List[types.InputPaidMedia], @@ -4845,7 +4935,8 @@ async def send_paid_media( show_caption_above_media: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, - payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None) -> types.Message: + payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -4894,6 +4985,10 @@ async def send_paid_media( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4903,7 +4998,9 @@ async def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - payload=payload, allow_paid_broadcast=allow_paid_broadcast)) + payload=payload, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + ) + ) async def send_media_group( self, chat_id: Union[int, str], @@ -4919,7 +5016,8 @@ async def send_media_group( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> List[types.Message]: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -4963,6 +5061,10 @@ async def send_media_group( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -4997,7 +5099,7 @@ async def send_media_group( result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -5017,7 +5119,8 @@ async def send_location( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -5080,6 +5183,10 @@ async def send_location( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -5112,7 +5219,9 @@ async def send_location( self.token, chat_id, latitude, longitude, live_period, reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, - protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) + ) async def edit_message_live_location( self, latitude: float, longitude: float, @@ -5238,7 +5347,8 @@ async def send_venue( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -5308,6 +5418,10 @@ async def send_venue( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -5341,7 +5455,8 @@ async def send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_markup, timeout, google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast)) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) + ) async def send_contact( @@ -5358,7 +5473,8 @@ async def send_contact( reply_parameters: Optional[types.ReplyParameters]=None, business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -5416,6 +5532,10 @@ async def send_contact( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -5447,7 +5567,9 @@ async def send_contact( await asyncio_helper.send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_markup, timeout, - protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id) + ) async def send_chat_action( @@ -6827,7 +6949,8 @@ async def send_invoice( message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, message_effect_id: Optional[str]=None, - allow_paid_broadcast: Optional[bool]=None) -> types.Message: + allow_paid_broadcast: Optional[bool]=None, + direct_messages_topic_id: Optional[int]=None) -> types.Message: """ Sends invoice. @@ -6940,6 +7063,10 @@ async def send_invoice( of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` + :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; + required if the message is sent to a direct messages chat + :type direct_messages_topic_id: :obj:`int` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -6974,7 +7101,9 @@ async def send_invoice( send_phone_number_to_provider, send_email_to_provider, is_flexible, disable_notification, reply_markup, provider_data, timeout, max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + direct_messages_topic_id=direct_messages_topic_id + ) return types.Message.de_json(result) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index bc50068f6..704029994 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -284,7 +284,7 @@ async def send_message( parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -311,6 +311,8 @@ async def send_message( params['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: params['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + params['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_name, params=params, method='post') @@ -429,7 +431,7 @@ async def get_chat_member(token, chat_id, user_id): async def forward_message( token, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None, protect_content=None, - message_thread_id=None, video_start_timestamp=None): + message_thread_id=None, video_start_timestamp=None, direct_messages_topic_id=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -442,13 +444,15 @@ async def forward_message( payload['message_thread_id'] = message_thread_id if video_start_timestamp: payload['video_start_timestamp'] = video_start_timestamp + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload) async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, - allow_paid_broadcast=None, video_start_timestamp=None): + allow_paid_broadcast=None, video_start_timestamp=None, direct_messages_topic_id=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -475,6 +479,8 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['allow_paid_broadcast'] = allow_paid_broadcast if video_start_timestamp: payload['video_start_timestamp'] = video_start_timestamp + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload) async def send_checklist( @@ -511,7 +517,8 @@ async def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, - message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, + direct_messages_topic_id=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -543,7 +550,8 @@ async def send_photo( parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, - business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None): + business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, + direct_messages_topic_id=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -581,13 +589,15 @@ async def send_photo( payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, payload=None, allow_paid_broadcast=None): + business_connection_id=None, payload=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -614,6 +624,8 @@ async def send_paid_media( _payload['payload'] = payload if allow_paid_broadcast is not None: _payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + _payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request( token, method_url, params=_payload, @@ -624,7 +636,7 @@ async def send_media_group( token, chat_id, media, disable_notification=None, timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendMediaGroup' media_json, files = await convert_input_media_array(media) payload = {'chat_id': chat_id, 'media': media_json} @@ -644,6 +656,8 @@ async def send_media_group( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request( token, method_url, params=payload, method='post' if files else 'get', @@ -656,7 +670,7 @@ async def send_location( reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -685,6 +699,8 @@ async def send_location( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload) @@ -742,7 +758,7 @@ async def send_venue( reply_markup=None, timeout=None, google_place_id=None, google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -771,6 +787,8 @@ async def send_venue( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload) @@ -778,7 +796,7 @@ async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -822,7 +840,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, cover=None, start_timestamp=None): + message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, cover=None, start_timestamp=None, + direct_messages_topic_id=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -884,6 +903,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m payload['cover'] = cover if start_timestamp: payload['start_timestamp'] = start_timestamp + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -892,7 +913,7 @@ async def send_animation( parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, width=None, height=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -942,13 +963,15 @@ async def send_animation( payload['show_caption_above_media'] = show_caption_above_media if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -982,12 +1005,15 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_ payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, - message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None): + message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, + direct_messages_topic_id=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -1027,13 +1053,15 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, rep payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -1079,6 +1107,8 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1086,7 +1116,7 @@ async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_m disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, message_thread_id=None, emoji=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None): + allow_paid_broadcast=None, direct_messages_topic_id=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -1133,6 +1163,8 @@ async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_m payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1719,7 +1751,8 @@ async def send_invoice( send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None, disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, - protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None, allow_paid_broadcast=None): + protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None, allow_paid_broadcast=None, + direct_messages_topic_id=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1808,6 +1841,8 @@ async def send_invoice( payload['provider_token'] = provider_token if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id return await _process_request(token, method_url, params=payload) @@ -2428,7 +2463,7 @@ async def delete_messages(token, chat_id, message_ids): return await _process_request(token, method_url, params=payload) async def forward_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, - message_thread_id=None, protect_content=None): + message_thread_id=None, protect_content=None, direct_messages_topic_id=None): method_url = 'forwardMessages' payload = { 'chat_id': chat_id, @@ -2441,12 +2476,14 @@ async def forward_messages(token, chat_id, from_chat_id, message_ids, disable_no payload['message_thread_id'] = message_thread_id if protect_content is not None: payload['protect_content'] = protect_content + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id result = await _process_request(token, method_url, params=payload) return result async def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notification=None, - message_thread_id=None, protect_content=None, remove_caption=None): + message_thread_id=None, protect_content=None, remove_caption=None, direct_messages_topic_id=None): method_url = 'copyMessages' payload = { 'chat_id': chat_id, @@ -2461,6 +2498,8 @@ async def copy_messages(token, chat_id, from_chat_id, message_ids, disable_notif payload['protect_content'] = protect_content if remove_caption is not None: payload['remove_caption'] = remove_caption + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id result = await _process_request(token, method_url, params=payload) return result From 9a44dba54af0d8ae1214465d141fb5d8ed9b8858 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 22:34:02 +0500 Subject: [PATCH 1792/1808] Added the class SuggestedPostParameters and the parameter suggested_post_parameters to the methods sendMessage, sendPhoto, sendVideo, sendAnimation, sendAudio, sendDocument, sendPaidMedia, sendSticker, sendVideoNote, sendVoice, sendLocation, sendVenue, sendContact, sendDice, sendInvoice, copyMessage, forwardMessage. This parameter can be used to send a suggested post to a direct messages chat topic. --- telebot/__init__.py | 180 ++++++++++++++++++++++++++++------- telebot/apihelper.py | 67 +++++++++---- telebot/async_telebot.py | 191 +++++++++++++++++++++++++++++++------- telebot/asyncio_helper.py | 70 ++++++++++---- telebot/types.py | 61 ++++++++++++ 5 files changed, 467 insertions(+), 102 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 8506303ce..ecad66fa8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1692,7 +1692,8 @@ def send_message( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send text messages. @@ -1759,6 +1760,11 @@ def send_message( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1807,7 +1813,8 @@ def send_message( reply_markup=reply_markup, parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, entities=entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, link_preview_options=link_preview_options, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters ) ) @@ -1819,7 +1826,8 @@ def forward_message( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, video_start_timestamp: Optional[int]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -1853,6 +1861,11 @@ def forward_message( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1863,7 +1876,8 @@ def forward_message( apihelper.forward_message( self.token, chat_id, from_chat_id, message_id, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, - video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id + video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters ) ) @@ -1886,7 +1900,8 @@ def copy_message( show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, video_start_timestamp: Optional[int]=None, - direct_messages_topic_id: Optional[int]=None) -> types.MessageID: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.MessageID: """ Use this method to copy messages of any kind. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. @@ -1952,6 +1967,11 @@ def copy_message( :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -1985,7 +2005,8 @@ def copy_message( reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id + video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters )) def delete_message(self, chat_id: Union[int, str], message_id: int, @@ -2231,7 +2252,8 @@ def send_dice( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -2284,6 +2306,11 @@ def send_dice( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2313,7 +2340,8 @@ def send_dice( self.token, chat_id, emoji=emoji, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters ) ) @@ -2336,7 +2364,8 @@ def send_photo( message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -2404,6 +2433,11 @@ def send_photo( :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` @@ -2438,7 +2472,7 @@ def send_photo( message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -2461,7 +2495,8 @@ def send_audio( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -2545,6 +2580,11 @@ def send_audio( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2581,7 +2621,7 @@ def send_audio( timeout=timeout, thumbnail=thumbnail, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -2601,7 +2641,8 @@ def send_voice( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=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, 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. @@ -2666,6 +2707,11 @@ def send_voice( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -2696,7 +2742,9 @@ def send_voice( parse_mode=parse_mode, disable_notification=disable_notification, timeout=timeout, caption_entities=caption_entities, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters + ) ) @@ -2720,7 +2768,8 @@ def send_document( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send general files. @@ -2797,6 +2846,11 @@ def send_document( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2842,7 +2896,7 @@ def send_document( disable_content_type_detection=disable_content_type_detection, visible_file_name=visible_file_name, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) @@ -2862,7 +2916,8 @@ def send_sticker( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -2922,6 +2977,11 @@ def send_sticker( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2957,7 +3017,7 @@ def send_sticker( protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) @@ -2988,7 +3048,8 @@ def send_video( allow_paid_broadcast: Optional[bool]=None, cover: Optional[Union[Any, str]]=None, start_timestamp: Optional[int]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -3086,6 +3147,11 @@ def send_video( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3128,7 +3194,8 @@ def send_video( protect_content=protect_content, message_thread_id=message_thread_id, has_spoiler=has_spoiler, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - cover=cover, start_timestamp=start_timestamp, direct_messages_topic_id=direct_messages_topic_id) + cover=cover, start_timestamp=start_timestamp, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters) ) @@ -3155,7 +3222,8 @@ def send_animation( message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -3242,6 +3310,11 @@ def send_animation( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3279,7 +3352,7 @@ def send_animation( width=width, height=height, message_thread_id=message_thread_id, reply_parameters=reply_parameters, has_spoiler=has_spoiler, business_connection_id=business_connection_id, message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) @@ -3300,7 +3373,8 @@ def send_video_note( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -3369,6 +3443,11 @@ def send_video_note( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3403,7 +3482,7 @@ def send_video_note( disable_notification=disable_notification, timeout=timeout, thumbnail=thumbnail, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) def send_paid_media( @@ -3413,7 +3492,8 @@ def send_paid_media( protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None ) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -3467,6 +3547,11 @@ def send_paid_media( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3476,7 +3561,8 @@ def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - payload=payload, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) + payload=payload, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters) ) @@ -3599,7 +3685,8 @@ def send_location( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -3666,6 +3753,11 @@ def send_location( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3696,7 +3788,8 @@ def send_location( disable_notification=disable_notification, timeout=timeout, horizontal_accuracy=horizontal_accuracy, heading=heading, proximity_alert_radius=proximity_alert_radius, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters) ) @@ -3829,7 +3922,8 @@ def send_venue( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -3903,6 +3997,11 @@ def send_venue( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3933,7 +4032,8 @@ def send_venue( foursquare_type=foursquare_type, disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, google_place_id=google_place_id, google_place_type=google_place_type, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, - message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) + message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters) ) @@ -3951,7 +4051,8 @@ def send_contact( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -4012,6 +4113,11 @@ def send_contact( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4042,7 +4148,7 @@ def send_contact( disable_notification=disable_notification, reply_markup=reply_markup, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) @@ -5486,7 +5592,8 @@ def send_invoice( reply_parameters: Optional[types.ReplyParameters]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Sends invoice. @@ -5603,6 +5710,11 @@ def send_invoice( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -5643,7 +5755,7 @@ def send_invoice( message_thread_id=message_thread_id, reply_parameters=reply_parameters, max_tip_amount=max_tip_amount, suggested_tip_amounts=suggested_tip_amounts, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) def create_invoice_link(self, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index be8844373..beb778d06 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -248,7 +248,7 @@ def send_message( entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, - direct_messages_topic_id=None): + direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendMessage' payload = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -277,6 +277,8 @@ def send_message( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, method='post') @@ -430,7 +432,7 @@ def get_chat_member(token, chat_id, user_id): def forward_message( token, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None, protect_content=None, message_thread_id=None, - video_start_timestamp=None, direct_messages_topic_id=None): + video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -445,13 +447,15 @@ def forward_message( payload['video_start_timestamp'] = video_start_timestamp if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload) def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None, - video_start_timestamp=None, direct_messages_topic_id=None): + video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -480,6 +484,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['video_start_timestamp'] = video_start_timestamp if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload) def send_checklist( @@ -517,7 +523,8 @@ def send_dice( token, chat_id, emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, - business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, + suggested_post_parameters=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -542,6 +549,8 @@ def send_dice( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -552,7 +561,7 @@ def send_photo( caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, - direct_messages_topic_id=None): + direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -592,13 +601,16 @@ def send_photo( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, payload=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + business_connection_id=None, payload=None, allow_paid_broadcast=None, direct_messages_topic_id=None, + suggested_post_parameters=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -626,6 +638,8 @@ def send_paid_media( _payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: _payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + _payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request( token, method_url, params=_payload, method='post' if files else 'get', @@ -671,7 +685,8 @@ def send_location( timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, + suggested_post_parameters=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -702,6 +717,8 @@ def send_location( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -758,7 +775,7 @@ def send_venue( foursquare_id=None, foursquare_type=None, disable_notification=None, reply_markup=None, timeout=None, google_place_id=None, google_place_type=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -789,6 +806,8 @@ def send_venue( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -796,7 +815,7 @@ def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -823,6 +842,8 @@ def send_contact( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload) @@ -844,7 +865,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None, reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, - cover=None, start_timestamp=None, direct_messages_topic_id=None): + cover=None, start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -908,6 +929,8 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_markup=N payload['start_timestamp'] = start_timestamp if direct_messages_topic_id: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -917,7 +940,7 @@ def send_animation( parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, width=None, height=None, message_thread_id=None, reply_parameters=None, has_spoiler=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -969,13 +992,15 @@ def send_animation( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -1011,13 +1036,15 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup= payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, message_thread_id=None, reply_parameters=None,business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -1059,13 +1086,15 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_mark payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -1113,6 +1142,8 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1120,7 +1151,7 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content = None, message_thread_id=None, emoji=None, reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -1169,6 +1200,8 @@ def send_data(token, chat_id, data, data_type, reply_markup=None, parse_mode=Non payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload, files=files, method='post') @@ -1760,7 +1793,7 @@ def send_invoice( disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1850,6 +1883,8 @@ def send_invoice( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return _make_request(token, method_url, params=payload) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 0be94c0a8..04325e1c0 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3214,7 +3214,8 @@ async def send_message( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send text messages. @@ -3281,6 +3282,11 @@ async def send_message( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3337,7 +3343,7 @@ async def send_message( reply_markup, parse_mode, disable_notification, timeout, entities, protect_content, message_thread_id, reply_parameters, link_preview_options, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -3348,7 +3354,8 @@ async def forward_message( timeout: Optional[int]=None, message_thread_id: Optional[int]=None, video_start_timestamp: Optional[int]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -3382,6 +3389,11 @@ async def forward_message( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3392,7 +3404,7 @@ async def forward_message( await asyncio_helper.forward_message(self.token, chat_id=chat_id, from_chat_id=from_chat_id, message_id=message_id, disable_notification=disable_notification, protect_content=protect_content, timeout=timeout, message_thread_id=message_thread_id, video_start_timestamp=video_start_timestamp, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) async def copy_message( @@ -3413,7 +3425,8 @@ async def copy_message( show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, video_start_timestamp: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.MessageID: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.MessageID: """ Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, @@ -3480,6 +3493,11 @@ async def copy_message( :param direct_messages_topic_id: Identifier of the direct messages topic to which the message will be sent; required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` @@ -3515,7 +3533,9 @@ async def copy_message( reply_parameters=reply_parameters, reply_markup=reply_markup, timeout=timeout, message_thread_id=message_thread_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, video_start_timestamp=video_start_timestamp, - direct_messages_topic_id=direct_messages_topic_id)) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters + ) + ) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: @@ -3750,7 +3770,8 @@ async def send_dice( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send an animated emoji that will display a random value. On success, the sent Message is returned. @@ -3803,6 +3824,11 @@ async def send_dice( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3834,7 +3860,7 @@ async def send_dice( await asyncio_helper.send_dice( self.token, chat_id, emoji, disable_notification, reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -3855,7 +3881,8 @@ async def send_photo( message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send photos. On success, the sent Message is returned. @@ -3924,6 +3951,11 @@ async def send_photo( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3958,7 +3990,7 @@ async def send_photo( parse_mode, disable_notification, timeout, caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -3981,7 +4013,8 @@ async def send_audio( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, @@ -4065,6 +4098,11 @@ async def send_audio( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4102,7 +4140,7 @@ async def send_audio( self.token, chat_id, audio, caption, duration, performer, title, reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -4122,7 +4160,8 @@ async def send_voice( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=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, 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. @@ -4187,6 +4226,11 @@ async def send_voice( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode @@ -4219,7 +4263,7 @@ async def send_voice( self.token, chat_id, voice, caption, duration, reply_markup, parse_mode, disable_notification, timeout, caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -4244,7 +4288,8 @@ async def send_document( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send general files. @@ -4321,6 +4366,11 @@ async def send_document( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4370,7 +4420,7 @@ async def send_document( caption_entities = caption_entities, disable_content_type_detection = disable_content_type_detection, visible_file_name = visible_file_name, protect_content = protect_content, message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) @@ -4389,7 +4439,8 @@ async def send_sticker( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send static .WEBP, animated .TGS, or video .WEBM stickers. On success, the sent Message is returned. @@ -4449,6 +4500,11 @@ async def send_sticker( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4487,7 +4543,10 @@ async def send_sticker( reply_markup=reply_markup, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, - message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, + suggested_post_parameters=suggested_post_parameters, direct_messages_topic_id=direct_messages_topic_id + ) + ) async def send_video( self, chat_id: Union[int, str], video: Union[Any, str], @@ -4516,7 +4575,8 @@ async def send_video( allow_paid_broadcast: Optional[bool]=None, cover: Optional[Union[Any, str]]=None, start_timestamp: Optional[int]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -4611,6 +4671,11 @@ async def send_video( :param direct_messages_topic_id: Identifier of a topic in a forum supergroup or channel, in which the message will be sent :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4654,7 +4719,7 @@ async def send_video( parse_mode, supports_streaming, disable_notification, timeout, thumbnail, width, height, caption_entities, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, cover=cover, start_timestamp=start_timestamp, - direct_messages_topic_id=direct_messages_topic_id)) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters)) async def send_animation( self, chat_id: Union[int, str], animation: Union[Any, str], @@ -4679,7 +4744,8 @@ async def send_animation( message_effect_id: Optional[str]=None, show_caption_above_media: Optional[bool]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. @@ -4766,6 +4832,11 @@ async def send_animation( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4804,7 +4875,7 @@ async def send_animation( reply_markup, parse_mode, disable_notification, timeout, thumbnail, caption_entities, width, height, protect_content, message_thread_id, has_spoiler, reply_parameters, business_connection_id, message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id)) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters)) async def send_video_note( self, chat_id: Union[int, str], data: Union[Any, str], @@ -4823,7 +4894,8 @@ async def send_video_note( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ As of v.4.0, Telegram clients support rounded square MPEG4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned. @@ -4892,6 +4964,11 @@ async def send_video_note( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4927,7 +5004,10 @@ async def send_video_note( await asyncio_helper.send_video_note( self.token, chat_id, data, duration, length, reply_markup, disable_notification, timeout, thumbnail, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id)) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters + ) + ) async def send_paid_media( self, chat_id: Union[int, str], star_count: int, media: List[types.InputPaidMedia], @@ -4936,7 +5016,8 @@ async def send_paid_media( protect_content: Optional[bool]=None, reply_parameters: Optional[types.ReplyParameters]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, business_connection_id: Optional[str]=None, payload: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send paid media to channel chats. On success, the sent Message is returned. @@ -4989,6 +5070,11 @@ async def send_paid_media( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -4998,7 +5084,8 @@ async def send_paid_media( caption_entities=caption_entities, show_caption_above_media=show_caption_above_media, disable_notification=disable_notification, protect_content=protect_content, reply_parameters=reply_parameters, reply_markup=reply_markup, business_connection_id=business_connection_id, - payload=payload, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id + payload=payload, allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters ) ) @@ -5017,7 +5104,8 @@ async def send_media_group( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> List[types.Message]: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -5065,6 +5153,11 @@ async def send_media_group( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -5099,7 +5192,8 @@ async def send_media_group( result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, + suggested_post_parameters=suggested_post_parameters) return [types.Message.de_json(msg) for msg in result] async def send_location( @@ -5120,7 +5214,8 @@ async def send_location( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send point on the map. On success, the sent Message is returned. @@ -5187,6 +5282,11 @@ async def send_location( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -5220,7 +5320,8 @@ async def send_location( reply_markup, disable_notification, timeout, horizontal_accuracy, heading, proximity_alert_radius, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters + ) ) async def edit_message_live_location( @@ -5348,7 +5449,8 @@ async def send_venue( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. @@ -5423,6 +5525,11 @@ async def send_venue( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -5455,7 +5562,7 @@ async def send_venue( self.token, chat_id, latitude, longitude, title, address, foursquare_id, foursquare_type, disable_notification, reply_markup, timeout, google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) @@ -5474,7 +5581,8 @@ async def send_contact( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Use this method to send phone contacts. On success, the sent Message is returned. @@ -5536,6 +5644,11 @@ async def send_contact( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -5568,7 +5681,7 @@ async def send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) ) @@ -6950,7 +7063,8 @@ async def send_invoice( reply_parameters: Optional[types.ReplyParameters]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None) -> types.Message: + direct_messages_topic_id: Optional[int]=None, + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: """ Sends invoice. @@ -7067,6 +7181,11 @@ async def send_invoice( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` + :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; + for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post + is automatically declined. + :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ @@ -7102,7 +7221,7 @@ async def send_invoice( reply_markup, provider_data, timeout, max_tip_amount, suggested_tip_amounts, protect_content, message_thread_id, reply_parameters, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast, - direct_messages_topic_id=direct_messages_topic_id + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) return types.Message.de_json(result) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 704029994..05ad2a8a0 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -284,7 +284,7 @@ async def send_message( parse_mode=None, disable_notification=None, timeout=None, entities=None, protect_content=None, message_thread_id=None, reply_parameters=None, link_preview_options=None, business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_name = 'sendMessage' params = {'chat_id': str(chat_id), 'text': text} if link_preview_options is not None: @@ -313,6 +313,8 @@ async def send_message( params['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: params['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + params['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_name, params=params, method='post') @@ -431,7 +433,8 @@ async def get_chat_member(token, chat_id, user_id): async def forward_message( token, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None, protect_content=None, - message_thread_id=None, video_start_timestamp=None, direct_messages_topic_id=None): + message_thread_id=None, video_start_timestamp=None, direct_messages_topic_id=None, + suggested_post_parameters=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -446,13 +449,15 @@ async def forward_message( payload['video_start_timestamp'] = video_start_timestamp if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload) async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, - allow_paid_broadcast=None, video_start_timestamp=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -481,6 +486,8 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['video_start_timestamp'] = video_start_timestamp if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload) async def send_checklist( @@ -518,7 +525,7 @@ async def send_dice( emoji=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, - direct_messages_topic_id=None): + direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendDice' payload = {'chat_id': chat_id} if emoji: @@ -541,6 +548,10 @@ async def send_dice( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload) @@ -551,7 +562,7 @@ async def send_photo( caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, - direct_messages_topic_id=None): + direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendPhoto' payload = {'chat_id': chat_id} files = None @@ -591,13 +602,16 @@ async def send_photo( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_paid_media( token, chat_id, star_count, media, caption=None, parse_mode=None, caption_entities=None, show_caption_above_media=None, disable_notification=None, protect_content=None, reply_parameters=None, reply_markup=None, - business_connection_id=None, payload=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + business_connection_id=None, payload=None, allow_paid_broadcast=None, direct_messages_topic_id=None, + suggested_post_parameters=None): method_url = r'sendPaidMedia' media_json, files = convert_input_media_array(media) _payload = {'chat_id': chat_id, 'star_count': star_count, 'media': media_json} @@ -626,6 +640,8 @@ async def send_paid_media( _payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: _payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + _payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request( token, method_url, params=_payload, @@ -670,7 +686,7 @@ async def send_location( reply_markup=None, disable_notification=None, timeout=None, horizontal_accuracy=None, heading=None, proximity_alert_radius=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendLocation' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} if live_period: @@ -701,6 +717,8 @@ async def send_location( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload) @@ -758,7 +776,7 @@ async def send_venue( reply_markup=None, timeout=None, google_place_id=None, google_place_type=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVenue' payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address} if foursquare_id: @@ -789,6 +807,8 @@ async def send_venue( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload) @@ -796,7 +816,7 @@ async def send_contact( token, chat_id, phone_number, first_name, last_name=None, vcard=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendContact' payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name} if last_name: @@ -821,6 +841,10 @@ async def send_contact( payload['message_effect_id'] = message_effect_id if allow_paid_broadcast is not None: payload['allow_paid_broadcast'] = allow_paid_broadcast + if direct_messages_topic_id is not None: + payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload) @@ -841,7 +865,7 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m thumbnail=None, width=None, height=None, caption_entities=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, allow_paid_broadcast=None, cover=None, start_timestamp=None, - direct_messages_topic_id=None): + direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVideo' payload = {'chat_id': chat_id} files = None @@ -905,6 +929,8 @@ async def send_video(token, chat_id, data, duration=None, caption=None, reply_m payload['start_timestamp'] = start_timestamp if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -913,7 +939,7 @@ async def send_animation( parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, width=None, height=None, protect_content=None, message_thread_id=None, has_spoiler=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, show_caption_above_media=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendAnimation' payload = {'chat_id': chat_id} files = None @@ -965,13 +991,15 @@ async def send_animation( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None,business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVoice' payload = {'chat_id': chat_id} files = None @@ -1007,13 +1035,15 @@ async def send_voice(token, chat_id, voice, caption=None, duration=None, reply_ payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_video_note(token, chat_id, data, duration=None, length=None, reply_markup=None, disable_notification=None, timeout=None, thumbnail=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, allow_paid_broadcast=None, - direct_messages_topic_id=None): + direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendVideoNote' payload = {'chat_id': chat_id} files = None @@ -1055,13 +1085,15 @@ async def send_video_note(token, chat_id, data, duration=None, length=None, rep payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload, files=files, method='post') async def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumbnail=None, caption_entities=None, protect_content=None, message_thread_id=None,reply_parameters=None, business_connection_id=None, - message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None): + message_effect_id=None, allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = r'sendAudio' payload = {'chat_id': chat_id} files = None @@ -1109,6 +1141,8 @@ async def send_audio(token, chat_id, audio, caption=None, duration=None, perform payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1116,7 +1150,7 @@ async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_m disable_notification=None, timeout=None, caption=None, thumbnail=None, caption_entities=None, disable_content_type_detection=None, visible_file_name=None, protect_content=None, message_thread_id=None, emoji=None,reply_parameters=None, business_connection_id=None, message_effect_id=None, - allow_paid_broadcast=None, direct_messages_topic_id=None): + allow_paid_broadcast=None, direct_messages_topic_id=None, suggested_post_parameters=None): method_url = await get_method_by_type(data_type) payload = {'chat_id': chat_id} files = None @@ -1165,6 +1199,8 @@ async def send_data(token, chat_id, data, data_type, reply_markup=None, parse_m payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload, files=files, method='post') @@ -1752,7 +1788,7 @@ async def send_invoice( disable_notification=None, reply_markup=None, provider_data=None, timeout=None, max_tip_amount=None, suggested_tip_amounts=None, protect_content=None, message_thread_id=None, reply_parameters=None, message_effect_id=None, allow_paid_broadcast=None, - direct_messages_topic_id=None): + direct_messages_topic_id=None, suggested_post_parameters=None): """ Use this method to send invoices. On success, the sent Message is returned. :param token: Bot's token (you don't need to fill this) @@ -1843,6 +1879,8 @@ async def send_invoice( payload['allow_paid_broadcast'] = allow_paid_broadcast if direct_messages_topic_id is not None: payload['direct_messages_topic_id'] = direct_messages_topic_id + if suggested_post_parameters is not None: + payload['suggested_post_parameters'] = suggested_post_parameters.to_json() return await _process_request(token, method_url, params=payload) diff --git a/telebot/types.py b/telebot/types.py index b3958ac79..8b983e742 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12923,3 +12923,64 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) return cls(**obj) + +class SuggestedPostPrice(JsonSerializable): + """ + Describes the price of a suggested post. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostprice + + :param currency: Currency in which the post will be paid. Currently, must be one of “XTR” for Telegram Stars or “TON” for toncoins + :type currency: :obj:`str` + + :param amount: The amount of the currency that will be paid for the post in the smallest units of the currency, i.e. Telegram Stars or nanotoncoins. Currently, price in Telegram Stars must be between 5 and 100000, and price in nanotoncoins must be between 10000000 and 10000000000000. + :type amount: :obj:`int` + + :return: Instance of the class + :rtype: :class:`SuggestedPostPrice` + """ + def __init__(self, currency: str, amount: int, **kwargs): + self.currency: str = currency + self.amount: int = amount + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = { + 'currency': self.currency, + 'amount': self.amount + } + return data + + +class SuggestedPostParameters(JsonSerializable): + """ + Contains parameters of a post that is being suggested by the bot. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostparameters + + :param price: Optional. Proposed price for the post. If the field is omitted, then the post is unpaid. + :type price: :class:`SuggestedPostPrice` + + :param send_date: Optional. Proposed send date of the post. If specified, then the date must be between 300 second and 2678400 seconds (30 days) in the future. If the field is omitted, then the post can be published at any time within 30 days at the sole discretion of the user who approves it. + :type send_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`SuggestedPostParameters` + """ + def __init__(self, price: Optional[SuggestedPostPrice] = None, send_date: Optional[int] = None, **kwargs): + self.price: Optional[SuggestedPostPrice] = price + self.send_date: Optional[int] = send_date + + def to_json(self): + return json.dumps(self.to_dict()) + + def to_dict(self): + data = {} + if self.price is not None: + data['price'] = self.price.to_dict() + if self.send_date is not None: + data['send_date'] = self.send_date + return data + From 5bd15376146e0f2da2f950504160cc4dc9cc746f Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 22:37:15 +0500 Subject: [PATCH 1793/1808] Added the method approveSuggestedPost, allowing bots to approve incoming suggested posts. Added the method declineSuggestedPost, allowing bots to decline incoming suggested posts. --- telebot/__init__.py | 45 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 14 ++++++++++++ telebot/async_telebot.py | 44 ++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 15 +++++++++++++ 4 files changed, 118 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index ecad66fa8..f62d6ca08 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2008,6 +2008,51 @@ def copy_message( video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters )) + + + def approve_suggested_post(self, chat_id: Union[int, str], message_id: int, send_date: Optional[int]=None) -> bool: + """ + Use this method to approve a suggested post in a direct messages chat. The bot must have the 'can_post_messages' administrator right in the corresponding channel chat. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#approvesuggestedpost + + :param chat_id: Unique identifier for the target direct messages chat + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of a suggested post message to approve + :type message_id: :obj:`int` + + :param send_date: Point in time (Unix timestamp) when the post is expected to be published; omit if the date has already been specified when the suggested post was created. + If specified, then the date must be not more than 2678400 seconds (30 days) in the future + :type send_date: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.approve_suggested_post(self.token, chat_id, message_id, + send_date=send_date) + + def decline_suggested_post(self, chat_id: Union[int, str], message_id: int, comment: Optional[str]=None) -> bool: + """ + Use this method to decline a suggested post in a direct messages chat. The bot must have + the 'can_manage_direct_messages' administrator right in the corresponding channel chat. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#declinesuggestedpost + + :param chat_id: Unique identifier for the target direct messages chat + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of a suggested post message to decline + :type message_id: :obj:`int` + + :param comment: Comment for the creator of the suggested post; 0-128 characters + :type comment: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.decline_suggested_post(self.token, chat_id, message_id, + comment=comment) def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: diff --git a/telebot/apihelper.py b/telebot/apihelper.py index beb778d06..faee96fce 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1691,6 +1691,20 @@ def edit_message_reply_markup( payload['timeout'] = timeout return _make_request(token, method_url, params=payload, method='post') +def approve_suggested_post(token, chat_id, message_id, send_date=None): + method_url = r'approveSuggestedPost' + payload = {'chat_id': chat_id, 'message_id': message_id} + if send_date is not None: + payload['send_date'] = send_date + return _make_request(token, method_url, params=payload, method='post') + +def decline_suggested_post(token, chat_id, message_id, comment=None): + method_url = r'declineSuggestedPost' + payload = {'chat_id': chat_id, 'message_id': message_id} + if comment is not None: + payload['comment'] = comment + return _make_request(token, method_url, params=payload, method='post') + def delete_message(token, chat_id, message_id, timeout=None): method_url = r'deleteMessage' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 04325e1c0..c310cf620 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3536,6 +3536,50 @@ async def copy_message( direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters ) ) + + async def approve_suggested_post(self, chat_id: Union[int, str], message_id: int, send_date: Optional[int]=None) -> bool: + """ + Use this method to approve a suggested post in a direct messages chat. The bot must have the 'can_post_messages' administrator right in the corresponding channel chat. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#approvesuggestedpost + + :param chat_id: Unique identifier for the target direct messages chat + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of a suggested post message to approve + :type message_id: :obj:`int` + + :param send_date: Point in time (Unix timestamp) when the post is expected to be published; omit if the date has already been specified when the suggested post was created. + If specified, then the date must be not more than 2678400 seconds (30 days) in the future + :type send_date: :obj:`int` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.approve_suggested_post(self.token, chat_id, message_id, + send_date=send_date) + + async def decline_suggested_post(self, chat_id: Union[int, str], message_id: int, comment: Optional[str]=None) -> bool: + """ + Use this method to decline a suggested post in a direct messages chat. The bot must have + the 'can_manage_direct_messages' administrator right in the corresponding channel chat. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#declinesuggestedpost + + :param chat_id: Unique identifier for the target direct messages chat + :type chat_id: :obj:`int` or :obj:`str` + + :param message_id: Identifier of a suggested post message to decline + :type message_id: :obj:`int` + + :param comment: Comment for the creator of the suggested post; 0-128 characters + :type comment: :obj:`str` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.decline_suggested_post(self.token, chat_id, message_id, + comment=comment) async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 05ad2a8a0..074324f71 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1686,6 +1686,21 @@ async def edit_message_reply_markup( payload['timeout'] = timeout return await _process_request(token, method_url, params=payload, method='post') + +async def approve_suggested_post(token, chat_id, message_id, send_date=None): + method_url = r'approveSuggestedPost' + payload = {'chat_id': chat_id, 'message_id': message_id} + if send_date is not None: + payload['send_date'] = send_date + return await _process_request(token, method_url, params=payload, method='post') + + +async def decline_suggested_post(token, chat_id, message_id, comment=None): + method_url = r'declineSuggestedPost' + payload = {'chat_id': chat_id, 'message_id': message_id} + if comment is not None: + payload['comment'] = comment + return await _process_request(token, method_url, params=payload, method='post') async def delete_message(token, chat_id, message_id, timeout=None): method_url = r'deleteMessage' From 181a15218c5ffb58df486b181cde71d2524c8988 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 22:46:02 +0500 Subject: [PATCH 1794/1808] Added the field can_manage_direct_messages to the classes ChatMemberAdministrator and ChatAdministratorRights. --- telebot/types.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 8b983e742..0e0236271 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -3408,6 +3408,9 @@ class ChatMemberAdministrator(ChatMember): :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only :type can_manage_topics: :obj:`bool` + :param can_manage_direct_messages: Optional. True, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only + :type can_manage_direct_messages: :obj:`bool` + :param custom_title: Optional. Custom title for this user :type custom_title: :obj:`str` @@ -3417,7 +3420,7 @@ class ChatMemberAdministrator(ChatMember): def __init__(self, user, status, can_be_edited, is_anonymous, can_manage_chat, can_delete_messages, can_manage_video_chats, can_restrict_members, can_promote_members, can_change_info, can_invite_users, can_post_stories, can_edit_stories, can_delete_stories, can_post_messages=None, can_edit_messages=None, - can_pin_messages=None, can_manage_topics=None, custom_title=None, **kwargs): + can_pin_messages=None, can_manage_topics=None, custom_title=None, can_manage_direct_messages=None, **kwargs): super().__init__(user, status, **kwargs) self.can_be_edited: bool = can_be_edited self.is_anonymous: bool = is_anonymous @@ -3436,6 +3439,7 @@ def __init__(self, user, status, can_be_edited, is_anonymous, can_manage_chat, c self.can_pin_messages: Optional[bool] = can_pin_messages self.can_manage_topics: Optional[bool] = can_manage_topics self.custom_title: Optional[str] = custom_title + self.can_manage_direct_messages: Optional[bool] = can_manage_direct_messages @property def can_manage_voice_chats(self): @@ -7903,6 +7907,9 @@ class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryab :param can_delete_stories: Optional. True, if the administrator can delete stories of other users :type can_delete_stories: :obj:`bool` + :param can_manage_direct_messages: Optional. True, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only + :type can_manage_direct_messages: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ChatAdministratorRights` """ @@ -7919,7 +7926,8 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, can_post_messages: Optional[bool]=None, can_edit_messages: Optional[bool]=None, can_pin_messages: Optional[bool]=None, can_manage_topics: Optional[bool]=None, can_post_stories: Optional[bool]=None, can_edit_stories: Optional[bool]=None, - can_delete_stories: Optional[bool]=None, **kwargs + can_delete_stories: Optional[bool]=None, can_manage_direct_messages: Optional[bool]=None, + **kwargs ) -> None: self.is_anonymous: bool = is_anonymous @@ -7937,6 +7945,7 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, self.can_post_stories: Optional[bool] = can_post_stories self.can_edit_stories: Optional[bool] = can_edit_stories self.can_delete_stories: Optional[bool] = can_delete_stories + self.can_manage_direct_messages: Optional[bool] = can_manage_direct_messages def to_dict(self): json_dict = { @@ -7963,6 +7972,8 @@ def to_dict(self): json_dict['can_edit_stories'] = self.can_edit_stories if self.can_delete_stories is not None: json_dict['can_delete_stories'] = self.can_delete_stories + if self.can_manage_direct_messages is not None: + json_dict['can_manage_direct_messages'] = self.can_manage_direct_messages return json_dict From 2400f25d77288921394916d2c6726a092ce6f960 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 22:57:43 +0500 Subject: [PATCH 1795/1808] Added the parameter can_manage_direct_messages to the method promoteChatMember. --- telebot/__init__.py | 10 ++++++++-- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 10 ++++++++-- telebot/asyncio_helper.py | 5 ++++- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index f62d6ca08..679afc311 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -4416,7 +4416,8 @@ def promote_chat_member( can_manage_topics: Optional[bool]=None, can_post_stories: Optional[bool]=None, can_edit_stories: Optional[bool]=None, - can_delete_stories: Optional[bool]=None) -> bool: + can_delete_stories: Optional[bool]=None, + can_manage_direct_messages: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -4486,6 +4487,10 @@ def promote_chat_member( :param can_delete_stories: Pass True if the administrator can delete the channel's stories :type can_delete_stories: :obj:`bool` + :param can_manage_direct_messages: Pass True if the administrator can manage direct messages + within the channel and decline suggested posts; for channels only + :type can_manage_direct_messages: :obj:`bool` + :return: True on success. :rtype: :obj:`bool` """ @@ -4502,7 +4507,8 @@ def promote_chat_member( is_anonymous=is_anonymous, can_manage_chat=can_manage_chat, can_manage_video_chats=can_manage_video_chats, can_manage_topics=can_manage_topics, can_post_stories=can_post_stories, can_edit_stories=can_edit_stories, - can_delete_stories=can_delete_stories) + can_delete_stories=can_delete_stories, can_manage_direct_messages=can_manage_direct_messages, + ) def set_chat_administrator_custom_title( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index faee96fce..68f2418da 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1257,7 +1257,7 @@ def promote_chat_member( can_restrict_members=None, can_pin_messages=None, can_promote_members=None, is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None, can_manage_topics=None, can_post_stories=None, can_edit_stories=None, - can_delete_stories=None): + can_delete_stories=None, can_manage_direct_messages=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -1290,6 +1290,8 @@ def promote_chat_member( payload['can_edit_stories'] = can_edit_stories if can_delete_stories is not None: payload['can_delete_stories'] = can_delete_stories + if can_manage_direct_messages is not None: + payload['can_manage_direct_messages'] = can_manage_direct_messages return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index c310cf620..bc01a012e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5939,7 +5939,8 @@ async def promote_chat_member( can_manage_topics: Optional[bool]=None, can_post_stories: Optional[bool]=None, can_edit_stories: Optional[bool]=None, - can_delete_stories: Optional[bool]=None) -> bool: + can_delete_stories: Optional[bool]=None, + can_manage_direct_messages: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -6009,6 +6010,10 @@ async def promote_chat_member( :param can_delete_stories: Pass True if the administrator can delete the channel's stories :type can_delete_stories: :obj:`bool` + :param can_manage_direct_messages: Pass True if the administrator can manage direct messages + within the channel and decline suggested posts; for channels only + :type can_manage_direct_messages: :obj:`bool` + :return: True on success. :rtype: :obj:`bool` """ @@ -6023,7 +6028,8 @@ async def promote_chat_member( can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, is_anonymous, can_manage_chat, can_manage_video_chats, can_manage_topics, - can_post_stories, can_edit_stories, can_delete_stories) + can_post_stories, can_edit_stories, can_delete_stories, can_manage_direct_messages=can_manage_direct_messages + ) async def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 074324f71..f127f316f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1252,7 +1252,8 @@ async def promote_chat_member( can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None, can_manage_topics=None, - can_post_stories=None, can_edit_stories=None, can_delete_stories=None): + can_post_stories=None, can_edit_stories=None, can_delete_stories=None, + can_manage_direct_messages=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -1285,6 +1286,8 @@ async def promote_chat_member( payload['can_edit_stories'] = can_edit_stories if can_delete_stories is not None: payload['can_delete_stories'] = can_delete_stories + if can_manage_direct_messages is not None: + payload['can_manage_direct_messages'] = can_manage_direct_messages return await _process_request(token, method_url, params=payload, method='post') From 6f88714091068a9b3dde476ccc56c96beec32d23 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 22:59:14 +0500 Subject: [PATCH 1796/1808] Added the field is_paid_post to the class Message, which can be used to identify paid posts. Such posts must not be deleted for 24 hours to receive the payment. --- telebot/types.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 0e0236271..54bac6dae 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -997,6 +997,10 @@ class Message(JsonDeserializable): as an away or a greeting business message, or as a scheduled message :type is_from_offline: :obj:`bool` + :param is_paid_post: Optional. True, if the message is a paid post. Note that such posts must not be + deleted for 24 hours to receive the payment and can't be edited. + :type is_paid_post: :obj:`bool` + :param media_group_id: Optional. The unique identifier of a media message group this message belongs to :type media_group_id: :obj:`str` @@ -1504,6 +1508,8 @@ def de_json(cls, json_string): opts['reply_to_checklist_task_id'] = obj['reply_to_checklist_task_id'] if 'direct_messages_topic' in obj: opts['direct_messages_topic'] = DirectMessagesTopic.de_json(obj['direct_messages_topic']) + if 'is_paid_post' in obj: + opts['is_paid_post'] = obj['is_paid_post'] return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1632,6 +1638,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.direct_message_price_changed: Optional[DirectMessagePriceChanged] = None self.reply_to_checklist_task_id: Optional[int] = None self.direct_messages_topic: Optional[DirectMessagesTopic] = None + self.is_paid_post: Optional[bool] = None for key in options: setattr(self, key, options[key]) From 5ab505630169065d69852fa70e8499627e831a21 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 23:13:08 +0500 Subject: [PATCH 1797/1808] Rest classes added --- telebot/types.py | 237 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 54bac6dae..33cf8dba6 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1022,6 +1022,10 @@ class Message(JsonDeserializable): if it is a text message and link preview options were changed :type link_preview_options: :class:`telebot.types.LinkPreviewOptions` + :param suggested_post_info: Optional. Information about suggested post parameters if the message is a suggested post + in a channel direct messages chat. If the message is an approved or declined suggested post, then it can't be edited. + :type suggested_post_info: :class:`telebot.types.SuggestedPostInfo` + :param effect_id: Optional. Unique identifier of the message effect added to the message :type effect_id: :obj:`str` @@ -1226,6 +1230,21 @@ class Message(JsonDeserializable): :param paid_message_price_changed: Optional. Service message: the price for paid messages has changed in the chat :type paid_message_price_changed: :class:`telebot.types.PaidMessagePriceChanged` + :param suggested_post_approved: Optional. Service message: a suggested post was approved + :type suggested_post_approved: :class:`telebot.types.SuggestedPostApproved + + :param suggested_post_approval_failed: Optional. Service message: approval of a suggested post has failed + :type suggested_post_approval_failed: :class:`telebot.types.SuggestedPost + + :param suggested_post_declined: Optional. Service message: a suggested post was declined + :type suggested_post_declined: :class:`telebot.types.SuggestedPostDecl + + :param suggested_post_paid: Optional. Service message: payment for a suggested post was received + :type suggested_post_paid: :class:`telebot.types.SuggestedPostPaid` + + :param suggested_post_refunded: Optional. Service message: payment for a suggested post was refunded + :type suggested_post_refunded: :class:`telebot.types.SuggestedPostRefunded` + :param video_chat_scheduled: Optional. Service message: video chat scheduled :type video_chat_scheduled: :class:`telebot.types.VideoChatScheduled` @@ -1510,6 +1529,24 @@ def de_json(cls, json_string): opts['direct_messages_topic'] = DirectMessagesTopic.de_json(obj['direct_messages_topic']) if 'is_paid_post' in obj: opts['is_paid_post'] = obj['is_paid_post'] + if 'suggested_post_info' in obj: + opts['suggested_post_info'] = SuggestedPostInfo.de_json(obj['suggested_post_info']) + content_type = 'suggested_post_info' + if 'suggested_post_approved' in obj: + opts['suggested_post_approved'] = SuggestedPostApproved.de_json(obj['suggested_post_approved']) + content_type = 'suggested_post_approved' + if 'suggested_post_approval_failed' in obj: + opts['suggested_post_approval_failed'] = SuggestedPostApprovalFailed.de_json(obj['suggested_post_approval_failed']) + content_type = 'suggested_post_approval_failed' + if 'suggested_post_declined' in obj: + opts['suggested_post_declined'] = SuggestedPostDeclined.de_json(obj['suggested_post_declined']) + content_type = 'suggested_post_declined' + if 'suggested_post_paid' in obj: + opts['suggested_post_paid'] = SuggestedPostPaid.de_json(obj['suggested_post_paid']) + content_type = 'suggested_post_paid' + if 'suggested_post_refunded' in obj: + opts['suggested_post_refunded'] = SuggestedPostRefunded.de_json(obj['suggested_post_refunded']) + content_type = 'suggested_post_refunded' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1639,6 +1676,12 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.reply_to_checklist_task_id: Optional[int] = None self.direct_messages_topic: Optional[DirectMessagesTopic] = None self.is_paid_post: Optional[bool] = None + self.suggested_post_info: Optional[SuggestedPostInfo] = None + self.suggested_post_approved: Optional[SuggestedPostApproved] = None + self.suggested_post_approval_failed: Optional[SuggestedPostApprovalFailed] = None + self.suggested_post_declined: Optional[SuggestedPostDeclined] = None + self.suggested_post_paid: Optional[SuggestedPostPaid] = None + self.suggested_post_refunded: Optional[SuggestedPostRefunded] = None for key in options: setattr(self, key, options[key]) @@ -12942,7 +12985,7 @@ def de_json(cls, json_string): return cls(**obj) -class SuggestedPostPrice(JsonSerializable): +class SuggestedPostPrice(JsonSerializable, JsonDeserializable): """ Describes the price of a suggested post. @@ -12969,7 +13012,13 @@ def to_dict(self): 'currency': self.currency, 'amount': self.amount } - return data + return data + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) class SuggestedPostParameters(JsonSerializable): @@ -13002,3 +13051,187 @@ def to_dict(self): data['send_date'] = self.send_date return data + +class SuggestedPostInfo(JsonDeserializable): + """ + Contains information about a suggested post. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostinfo + + :param state: State of the suggested post. Currently, it can be one of “pending”, “approved”, “declined”. + :type state: :obj:`str` + + :param price: Optional. Proposed price of the post. If the field is omitted, then the post is unpaid. + :type price: :class:`SuggestedPostPrice` + + :param send_date: Optional. Proposed send date of the post. If the field is omitted, then the post can be published at any time within 30 days at the sole discretion of the user or administrator who approves it. + :type send_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`SuggestedPostInfo` + """ + def __init__(self, state: str, price: Optional[SuggestedPostPrice] + = None, send_date: Optional[int] = None, **kwargs): + self.state: str = state + self.price: Optional[SuggestedPostPrice] = price + self.send_date: Optional[int] = send_date + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'price' in obj: + obj['price'] = SuggestedPostPrice.de_json(obj['price']) + return cls(**obj) + + +class SuggestedPostApproved(JsonDeserializable): + """ + Describes a service message about the approval of a suggested post. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostapproved + + :param suggested_post_message: Optional. Message containing the suggested post. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. + :type suggested_post_message: :class:`Message` + + :param price: Optional. Amount paid for the post + :type price: :class:`SuggestedPostPrice` + + :param send_date: Optional. Date when the post will be published + :type send_date: int + + :return: Instance of the class + :rtype: :class:`SuggestedPostApproved` + """ + def __init__(self, suggested_post_message: Optional[Message] = None, + price: Optional[SuggestedPostPrice] = None, + send_date: Optional[int] = None, **kwargs): + self.suggested_post_message: Optional[Message] = suggested_post_message + self.price: Optional[SuggestedPostPrice] = price + self.send_date: Optional[int] = send_date + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'suggested_post_message' in obj: + obj['suggested_post_message'] = Message.de_json(obj['suggested_post_message']) + if 'price' in obj: + obj['price'] = SuggestedPostPrice.de_json(obj['price']) + return cls(**obj) + +class SuggestedPostApprovalFailed(JsonDeserializable): + """ + Describes a service message about the failed approval of a suggested post. + Currently, only caused by insufficient user funds at the time of approval. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostapprovalfailed + + :param suggested_post_message: Optional. Message containing the suggested post whose approval has failed. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. + :type suggested_post_message: :class:`Message` + + :return: Instance of the class + :rtype: :class:`SuggestedPostApprovalFailed` + """ + def __init__(self, suggested_post_message: Optional[Message] = None, **kwargs): + self.suggested_post_message: Optional[Message] = suggested_post_message + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'suggested_post_message' in obj: + obj['suggested_post_message'] = Message.de_json(obj['suggested_post_message']) + return cls(**obj) + +class SuggestedPostDeclined(JsonDeserializable): + """ + Describes a service message about the rejection of a suggested post. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostdeclined + + :param suggested_post_message: Optional. Message containing the suggested post. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. + :type suggested_post_message: :class:`Message` + + :return: Instance of the class + :rtype: :class:`SuggestedPostDeclined` + """ + def __init__(self, suggested_post_message: Optional[Message] = None, comment: Optional[str] = None, **kwargs): + self.suggested_post_message: Optional[Message] = suggested_post_message + self.comment: Optional[str] = comment + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'suggested_post_message' in obj: + obj['suggested_post_message'] = Message.de_json(obj['suggested_post_message']) + return cls(**obj) + +class SuggestedPostPaid(JsonDeserializable): + """ + Describes a service message about a successful payment for a suggested post. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostpaid + + :param suggested_post_message: Optional. Message containing the suggested post. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. + :type suggested_post_message: :class:`Message` + + :param currency: Currency in which the payment was made. Currently, one of “XTR” for Telegram Stars or “TON” for toncoins + :type currency: :obj:`str` + + :param amount: Optional. The amount of the currency that was received by the channel in nanotoncoins; for payments in toncoins only + :type amount: :obj:`int` + + :param star_amount: Optional. The amount of Telegram Stars that was received by the channel; for payments in Telegram Stars only + :type star_amount: :class:`StarAmount` + + :return: Instance of the class + :rtype: :class:`SuggestedPostPaid` + """ + def __init__(self, currency: str,suggested_post_message: Optional[Message] = None, + amount: Optional[int] = None, + star_amount: Optional[StarAmount] = None, **kwargs): + self.suggested_post_message: Optional[Message] = suggested_post_message + self.currency: str = currency + self.amount: Optional[int] = amount + self.star_amount: Optional[StarAmount] = star_amount + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'suggested_post_message' in obj: + obj['suggested_post_message'] = Message.de_json(obj['suggested_post_message']) + if 'star_amount' in obj: + obj['star_amount'] = StarAmount.de_json(obj['star_amount']) + return cls(**obj) + +class SuggestedPostRefunded(JsonDeserializable): + """ + Describes a service message about a payment refund for a suggested post. + + Telegram documentation: https://core.telegram.org/bots/api#suggestedpostrefunded + + :param suggested_post_message: Optional. Message containing the suggested post. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. + :type suggested_post_message: :class:`Message` + + :param reason: Reason for the refund. Currently, one of “post_deleted” if the post was deleted within 24 hours of being posted or removed from scheduled messages without being posted, or “payment_refunded” if the payer refunded their payment. + :type reason: :obj:`str` + + :return: Instance of the class + :rtype: :class:`SuggestedPostRefunded` + """ + def __init__(self, suggested_post_message: Optional[Message] = None, reason: Optional[str] = None, **kwargs): + self.suggested_post_message: Optional[Message] = suggested_post_message + self.reason: Optional[str] = reason + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + if 'suggested_post_message' in obj: + obj['suggested_post_message'] = Message.de_json(obj['suggested_post_message']) + return cls(**obj) + + + \ No newline at end of file From 3fad50de693d51356af1a49671f41ddf02c324ea Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Aug 2025 23:13:41 +0500 Subject: [PATCH 1798/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a67b7d78..29764fb3c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From 95d43c82df1f265580773166b0ca030c0de882d8 Mon Sep 17 00:00:00 2001 From: All-The-Foxes <116322192+All-The-Foxes@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:06:21 -0400 Subject: [PATCH 1799/1808] Default to None for filter func in inline_handler --- telebot/async_telebot.py | 896 +++++++++++++++++++-------------------- 1 file changed, 448 insertions(+), 448 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fe8013135..e9f18d157 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -26,7 +26,7 @@ logger = logging.getLogger('TeleBot') REPLY_MARKUP_TYPES = Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, + types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply] @@ -76,7 +76,7 @@ class AsyncTeleBot: from telebot.async_telebot import AsyncTeleBot bot = AsyncTeleBot('token') # get token from @BotFather - # now you can register other handlers/update listeners, + # now you can register other handlers/update listeners, # and use bot methods. # Remember to use async/await keywords when necessary. @@ -114,7 +114,7 @@ class AsyncTeleBot: :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. Default value for allow_sending_without_reply, defaults to None :type allow_sending_without_reply: :obj:`bool`, optional - + :param colorful_logs: Outputs colorful logs :type colorful_logs: :obj:`bool`, optional @@ -134,7 +134,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ allow_sending_without_reply: Optional[bool]=None, colorful_logs: Optional[bool]=False, validate_token: Optional[bool]=True) -> None: - + # update-related self.token = token self.offset = offset @@ -148,7 +148,7 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ raise ImportError( 'Install coloredlogs module to use colorful_logs option.' ) - + # properties self.parse_mode = parse_mode self.disable_web_page_preview = disable_web_page_preview @@ -194,9 +194,9 @@ def __init__(self, token: str, parse_mode: Optional[str]=None, offset: Optional[ if validate_token: util.validate_token(self.token) - + self.bot_id: Union[int, None] = util.extract_bot_id(self.token) # subject to change, unspecified - + @property def user(self): @@ -237,7 +237,7 @@ async def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=Non :return: An Array of Update objects is returned. :rtype: :obj:`list` of :class:`telebot.types.Update` - """ + """ json_updates = await asyncio_helper.get_updates(self.token, offset, limit, timeout, allowed_updates, request_timeout) return [types.Update.de_json(ju) for ju in json_updates] @@ -255,7 +255,7 @@ def _setup_change_detector(self, path_to_watch: str) -> None: if path is None: # Make it possible to specify --path argument to the script path = sys.argv[sys.argv.index('--path') + 1] if '--path' in sys.argv else '.' - + self.event_observer = Observer() self.event_observer.schedule(self.event_handler, path, recursive=True) self.event_observer.start() @@ -268,7 +268,7 @@ async def polling(self, non_stop: bool=True, skip_pending=False, interval: int=0 This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly. Warning: Do not call this function more than once! - + Always gets updates. .. note:: @@ -277,7 +277,7 @@ async def polling(self, non_stop: bool=True, skip_pending=False, interval: int=0 :param non_stop: Do not stop polling when an ApiException occurs. :type non_stop: :obj:`bool` - + :param skip_pending: skip old updates :type skip_pending: :obj:`bool` @@ -286,17 +286,17 @@ async def polling(self, non_stop: bool=True, skip_pending=False, interval: int=0 :param timeout: Request connection timeout :type timeout: :obj:`int` - + :param request_timeout: Timeout in seconds for get_updates(Defaults to None) :type request_timeout: :obj:`int` :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - - Please note that this parameter doesn't affect updates created before the call to the get_updates, + + 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. :type allowed_updates: :obj:`list` of :obj:`str` @@ -308,7 +308,7 @@ async def polling(self, non_stop: bool=True, skip_pending=False, interval: int=0 :param path_to_watch: Path to watch for changes. Defaults to current directory :type path_to_watch: :obj:`str` - + :return: """ if none_stop is not None: @@ -346,12 +346,12 @@ async def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Option :type logger_level: :obj:`int` :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - - Please note that this parameter doesn't affect updates created before the call to the get_updates, + + 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. :type allowed_updates: :obj:`list` of :obj:`str` @@ -402,7 +402,7 @@ def __hide_token(self, message: str) -> str: return message.replace(code, "*" * len(code)) else: return message - + async def _handle_error_interval(self, error_interval: float): logger.debug('Waiting for %s seconds before retrying', error_interval) await asyncio.sleep(error_interval) @@ -439,7 +439,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: logger.warning("Setting non_stop to False will stop polling on API and system exceptions.") self._user = await self.get_me() - + logger.info('Starting your bot with username: [@%s]', self.user.username) self._polling = True @@ -469,7 +469,7 @@ async def _process_polling(self, non_stop: bool=False, interval: int=0, timeout: if non_stop: error_interval = await self._handle_error_interval(error_interval) - + if non_stop or handled: continue else: @@ -837,7 +837,7 @@ async def process_new_poll_answer(self, poll_answers): :meta private: """ await self._process_updates(self.poll_answer_handlers, poll_answers, 'poll_answer') - + async def process_new_my_chat_member(self, my_chat_members): """ :meta private: @@ -885,7 +885,7 @@ async def process_new_edited_business_message(self, new_edited_business_messages :meta private: """ await self._process_updates(self.edited_business_message_handlers, new_edited_business_messages, 'edited_business_message') - + async def process_new_deleted_business_messages(self, new_deleted_business_messages): """ :meta private: @@ -906,7 +906,7 @@ async def _get_middlewares(self, update_type): middlewares = [middleware for middleware in self.middlewares if update_type in middleware.update_types] return middlewares return None - + async def __notify_update(self, new_messages): if len(self.update_listener) == 0: return @@ -945,7 +945,7 @@ async def update_listener(new_messages): print(message.text) # Prints message text bot.set_update_listener(update_listener) - + :return: None """ self.update_listener.append(func) @@ -1033,7 +1033,7 @@ def setup_middleware(self, middleware: BaseMiddleware): :param middleware: Middleware-class. :type middleware: :class:`telebot.asyncio_handler_backends.BaseMiddleware` - + :return: None """ if not hasattr(middleware, 'update_types'): @@ -1106,7 +1106,7 @@ async def default_command(message): :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. - + :param content_types: Supported message content types. Must be a list. Defaults to ['text']. :type content_types: :obj:`list` of :obj:`str` @@ -1147,7 +1147,7 @@ def add_message_handler(self, handler_dict): """ Adds a message handler. Note that you should use register_message_handler to add message_handler. - + :meta private: :param handler_dict: @@ -1371,7 +1371,7 @@ def add_channel_post_handler(self, handler_dict): :return: """ self.channel_post_handlers.append(handler_dict) - + def register_channel_post_handler(self, callback: Callable[[Any], Awaitable], content_types: Optional[List[str]]=None, commands: Optional[List[str]]=None, regexp: Optional[str]=None, func: Optional[Callable]=None, pass_bot: Optional[bool]=False, **kwargs): """ @@ -1535,7 +1535,7 @@ def decorator(handler): return handler return decorator - + def add_message_reaction_handler(self, handler_dict): """ Adds message reaction handler. @@ -1560,7 +1560,7 @@ def register_message_reaction_handler(self, callback: Callable[[Any], Awaitable] :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -1587,7 +1587,7 @@ def decorator(handler): return handler return decorator - + def add_message_reaction_count_handler(self, handler_dict): """ Adds message reaction count handler @@ -1612,7 +1612,7 @@ def register_message_reaction_count_handler(self, callback: Callable[[Any], Awai :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -1621,7 +1621,7 @@ def register_message_reaction_count_handler(self, callback: Callable[[Any], Awai self.add_message_reaction_count_handler(handler_dict) - def inline_handler(self, func, **kwargs): + def inline_handler(self, func=None, **kwargs): """ Handles new incoming inline query. As a parameter to the decorator function, it passes :class:`telebot.types.InlineQuery` object. @@ -1681,7 +1681,7 @@ def chosen_inline_handler(self, func, **kwargs): :param func: Function executed as a filter :type func: :obj:`function` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -1735,7 +1735,7 @@ def callback_query_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ @@ -1787,7 +1787,7 @@ def shipping_query_handler(self, func, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ @@ -1861,7 +1861,7 @@ def add_pre_checkout_query_handler(self, handler_dict): :return: """ self.pre_checkout_query_handlers.append(handler_dict) - + def register_pre_checkout_query_handler(self, callback: Callable[[Any], Awaitable], func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers pre-checkout request handler. @@ -1896,9 +1896,9 @@ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_purchased_paid_media_handler(handler_dict) return handler - + return decorator - + def add_purchased_paid_media_handler(self, handler_dict): """ Adds a purchased paid media handler @@ -1917,7 +1917,7 @@ def register_purchased_paid_media_handler(self, callback: Callable, func: Callab :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -1992,7 +1992,7 @@ def poll_answer_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ @@ -2152,7 +2152,7 @@ def chat_join_request_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ @@ -2198,7 +2198,7 @@ def register_chat_join_request_handler(self, callback: Callable[[Any], Awaitable def chat_boost_handler(self, func=None, **kwargs): """ - Handles new incoming chat boost state. + Handles new incoming chat boost state. it passes :class:`telebot.types.ChatBoostUpdated` object. :param func: Function executed as a filter @@ -2213,7 +2213,7 @@ def decorator(handler): return handler return decorator - + def add_chat_boost_handler(self, handler_dict): """ Adds a chat_boost handler. @@ -2232,7 +2232,7 @@ def register_chat_boost_handler(self, callback: Callable, func: Optional[Callabl :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -2247,7 +2247,7 @@ def register_chat_boost_handler(self, callback: Callable, func: Optional[Callabl def removed_chat_boost_handler(self, func=None, **kwargs): """ - Handles new incoming chat boost state. + Handles new incoming chat boost state. it passes :class:`telebot.types.ChatBoostRemoved` object. :param func: Function executed as a filter @@ -2262,7 +2262,7 @@ def decorator(handler): return handler return decorator - + def add_removed_chat_boost_handler(self, handler_dict): """ Adds a removed_chat_boost handler. @@ -2281,7 +2281,7 @@ def register_removed_chat_boost_handler(self, callback: Callable, func: Optional :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -2309,9 +2309,9 @@ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_business_connection_handler(handler_dict) return handler - + return decorator - + def add_business_connection_handler(self, handler_dict): """ Adds a business_connection handler. @@ -2426,7 +2426,7 @@ def decorator(handler): return handler return decorator - + def add_business_message_handler(self, handler_dict): """ Adds a business_message handler. @@ -2476,8 +2476,8 @@ def register_business_message_handler(self, pass_bot=pass_bot,**kwargs) self.add_business_message_handler(handler_dict) - - + + def edited_business_message_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs): """ @@ -2534,7 +2534,7 @@ def add_edited_business_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_business_message_handler to add edited_business_message_handler to the bot. - + :meta private: :param handler_dict: @@ -2594,7 +2594,7 @@ def register_edited_business_message_handler(self, callback: Callable, content_t **kwargs) self.add_edited_business_message_handler(handler_dict) - + def deleted_business_messages_handler(self, func=None, **kwargs): """ Handles new incoming deleted messages state. @@ -2611,9 +2611,9 @@ def decorator(handler): self.add_deleted_business_messages_handler(handler_dict) return handler - + return decorator - + def add_deleted_business_messages_handler(self, handler_dict): """ Adds a deleted_business_messages handler. @@ -2670,7 +2670,7 @@ async def skip_updates(self): return True # all methods begin here - + async def get_me(self) -> types.User: """ Returns basic information about the bot in form of a User object. @@ -2683,9 +2683,9 @@ async def get_me(self) -> types.User: async def get_file(self, file_id: Optional[str]) -> types.File: """ Use this method to get basic info about a file and prepare it for downloading. - For the moment, bots can download files of up to 20MB in size. - On success, a File object is returned. - It is guaranteed that the link will be valid for at least 1 hour. + For the moment, bots can download files of up to 20MB in size. + On success, a File object is returned. + It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling get_file again. Telegram documentation: https://core.telegram.org/bots/api#getfile @@ -2715,7 +2715,7 @@ async def download_file(self, file_path: Optional[str]) -> bytes: :param file_path: Path where the file should be downloaded. :type file_path: str - + :return: bytes :rtype: :obj:`bytes` """ @@ -2723,11 +2723,11 @@ async def download_file(self, file_path: Optional[str]) -> bytes: async def log_out(self) -> bool: """ - Use this method to log out from the cloud Bot API server before launching the bot locally. + Use this method to log out from the cloud Bot API server before launching the bot locally. You MUST log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. - After a successful call, you can immediately log in on a local server, - but will not be able to log in back to the cloud Bot API server for 10 minutes. + After a successful call, you can immediately log in on a local server, + but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#logout @@ -2736,13 +2736,13 @@ async def log_out(self) -> bool: :rtype: :obj:`bool` """ return await asyncio_helper.log_out(self.token) - + async def close(self) -> bool: """ - Use this method to close the bot instance before moving it from one local server to another. + Use this method to close the bot instance before moving it from one local server to another. You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. - The method will return error 429 in the first 10 minutes after the bot is launched. + The method will return error 429 in the first 10 minutes after the bot is launched. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#close @@ -2790,15 +2790,15 @@ async def set_webhook(self, url: Optional[str]=None, certificate: Optional[Union Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput, defaults to None :type max_connections: :obj:`int`, optional - + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - + Please note that this parameter doesn't affect updates created before the call to the setWebhook, so unwanted updates may be received for a short period of time. Defaults to None - + :type allowed_updates: :obj:`list`, optional :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address @@ -2941,7 +2941,7 @@ async def get_webhook_info(self, timeout: Optional[int]=None) -> types.WebhookIn async def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reaction: Optional[List[types.ReactionType]]=None, is_big: Optional[bool]=None) -> bool: """ - Use this method to change the chosen reactions on a message. + Use this method to change the chosen reactions on a message. Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same available reactions as messages in the channel. Returns True on success. @@ -2965,7 +2965,7 @@ async def set_message_reaction(self, chat_id: Union[int, str], message_id: int, result = await asyncio_helper.set_message_reaction(self.token, chat_id, message_id, reaction, is_big) return result - async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, + async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ Use this method to get a list of profile pictures for a user. @@ -2988,7 +2988,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 set_user_emoji_status(self, user_id: int, emoji_status_custom_emoji_id: Optional[str]=None, emoji_status_expiration_date: Optional[int]=None) -> bool: """ Use this method to change the emoji status for a given user that previously allowed the bot to manage their emoji status via the Mini App method requestEmojiStatusAccess. @@ -3045,7 +3045,7 @@ async def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types. On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. - Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators + Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) @@ -3075,7 +3075,7 @@ async def get_chat_members_count(self, chat_id: Union[int, str]) -> int: """ result = await asyncio_helper.get_chat_member_count(self.token, chat_id) return result - + async def get_chat_member_count(self, chat_id: Union[int, str]) -> int: """ Use this method to get the number of members in a chat. @@ -3096,7 +3096,7 @@ async def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -3116,7 +3116,7 @@ async def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -3132,7 +3132,7 @@ async def answer_web_app_query(self, web_app_query_id: str, result: types.Inline """ Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which - the query originated. + the query originated. On success, a SentWebAppMessage object is returned. Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery @@ -3182,7 +3182,7 @@ async def save_prepared_inline_message(self, user_id: int, result: types.InlineQ async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. - + Telegram documentation: https://core.telegram.org/bots/api#getchatmember :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -3198,13 +3198,13 @@ async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types return types.ChatMember.de_json(result) async def send_message( - self, chat_id: Union[int, str], text: str, - parse_mode: Optional[str]=None, + self, chat_id: Union[int, str], text: str, + parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, - disable_web_page_preview: Optional[bool]=None, - disable_notification: Optional[bool]=None, + disable_web_page_preview: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, @@ -3218,7 +3218,7 @@ async def send_message( Use this method to send text messages. Warning: Do not send more than about 4096 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 4096 characters, + If you must send more than 4096 characters, use the `split_string` or `smart_split` function in util.py. Telegram documentation: https://core.telegram.org/bots/api#sendmessage @@ -3283,11 +3283,11 @@ async def send_message( disable_web_page_preview = self.disable_web_page_preview if (disable_web_page_preview is None) else disable_web_page_preview disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -3308,7 +3308,7 @@ async def send_message( if disable_web_page_preview is not None: # show a deprecation warning logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") - + if link_preview_options: # show a conflict warning logger.warning("Both 'link_preview_options' and 'disable_web_page_preview' parameters are set: conflicting, 'disable_web_page_preview' is deprecated") @@ -3334,7 +3334,7 @@ async def send_message( message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def forward_message( - self, chat_id: Union[int, str], from_chat_id: Union[int, str], + self, chat_id: Union[int, str], from_chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, timeout: Optional[int]=None, @@ -3359,7 +3359,7 @@ async def forward_message( :param video_start_timestamp: New start timestamp for the forwarded video in the message :type video_start_timestamp: :obj:`int` - + :param protect_content: Protects the contents of the forwarded message from forwarding and saving :type protect_content: :obj:`bool` @@ -3381,17 +3381,17 @@ async def forward_message( timeout=timeout, message_thread_id=message_thread_id, video_start_timestamp=video_start_timestamp)) async def copy_message( - self, chat_id: Union[int, str], - from_chat_id: Union[int, str], - message_id: int, - caption: Optional[str]=None, - parse_mode: Optional[str]=None, + self, chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, @@ -3450,7 +3450,7 @@ async def copy_message( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -3460,7 +3460,7 @@ async def copy_message( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` - + :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` """ @@ -3470,7 +3470,7 @@ async def copy_message( if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -3496,7 +3496,7 @@ async def copy_message( message_thread_id=message_thread_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, video_start_timestamp=video_start_timestamp)) - async def delete_message(self, chat_id: Union[int, str], message_id: int, + async def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: """ Use this method to delete a message, including service messages, with the following limitations: @@ -3524,10 +3524,10 @@ async def delete_message(self, chat_id: Union[int, str], message_id: int, :rtype: :obj:`bool` """ return await asyncio_helper.delete_message(self.token, chat_id, message_id, timeout) - + async def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ - Use this method to delete multiple messages simultaneously. + Use this method to delete multiple messages simultaneously. If some of the specified messages can't be found, they are skipped. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemessages @@ -3542,7 +3542,7 @@ async def delete_messages(self, chat_id: Union[int, str], message_ids: List[int] """ return await asyncio_helper.delete_messages(self.token, chat_id, message_ids) - + async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: """ @@ -3578,7 +3578,7 @@ async def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[s protect_content = self.protect_content if (protect_content is None) else protect_content result = await asyncio_helper.forward_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, protect_content) return [types.MessageID.de_json(message_id) for message_id in result] - + async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: @@ -3619,7 +3619,7 @@ async def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, result = await asyncio_helper.copy_messages(self.token, chat_id, from_chat_id, message_ids, disable_notification, message_thread_id, protect_content, remove_caption) return [types.MessageID.de_json(message_id) for message_id in result] - + async def send_checklist( self, business_connection_id: str, chat_id: Union[int, str], checklist: types.InputChecklist, @@ -3663,7 +3663,7 @@ async def send_checklist( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply @@ -3672,7 +3672,7 @@ async def send_checklist( self.token, business_connection_id, chat_id, checklist, disable_notification=disable_notification, protect_content=protect_content, message_effect_id=message_effect_id, reply_parameters=reply_parameters, reply_markup=reply_markup)) - + async def edit_message_checklist( self, business_connection_id: str, chat_id: Union[int, str], message_id: int, checklist: types.InputChecklist, @@ -3707,9 +3707,9 @@ async def edit_message_checklist( async def send_dice( self, chat_id: Union[int, str], - emoji: Optional[str]=None, disable_notification: Optional[bool]=None, + emoji: Optional[str]=None, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, @@ -3752,7 +3752,7 @@ async def send_dice( :param message_thread_id: The identifier of a message thread, unique within the chat to which the message with the thread identifier belongs :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -3771,11 +3771,11 @@ async def send_dice( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -3798,15 +3798,15 @@ async def send_dice( self.token, chat_id, emoji, disable_notification, reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) - + async def send_photo( - self, chat_id: Union[int, str], photo: Union[Any, str], + self, chat_id: Union[int, str], photo: Union[Any, str], caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, @@ -3821,7 +3821,7 @@ async def send_photo( Use this method to send photos. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendphoto - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -3864,7 +3864,7 @@ async def send_photo( :param has_spoiler: Pass True, if the photo should be sent as a spoiler :type has_spoiler: :obj:`bool` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -3880,18 +3880,18 @@ async def send_photo( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` - + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -3917,14 +3917,14 @@ async def send_photo( show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) async def send_audio( - self, chat_id: Union[int, str], audio: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, + self, chat_id: Union[int, str], audio: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, performer: Optional[str]=None, title: Optional[str]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, @@ -3943,7 +3943,7 @@ async def send_audio( For sending voice messages, use the send_voice method instead. Telegram documentation: https://core.telegram.org/bots/api#sendaudio - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -4000,7 +4000,7 @@ async def send_audio( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4020,7 +4020,7 @@ async def send_audio( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if thumb is not None and thumbnail is None: thumbnail = thumb @@ -4028,7 +4028,7 @@ async def send_audio( if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -4053,12 +4053,12 @@ async def send_audio( caption_entities, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def send_voice( - self, chat_id: Union[int, str], voice: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, - reply_to_message_id: Optional[int]=None, + self, chat_id: Union[int, str], voice: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, + reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, @@ -4072,7 +4072,7 @@ async def send_voice( 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 - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -4114,7 +4114,7 @@ async def send_voice( :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4133,11 +4133,11 @@ async def send_voice( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -4164,12 +4164,12 @@ async def send_voice( async def send_document( self, chat_id: Union[int, str], document: Union[Any, str], - reply_to_message_id: Optional[int]=None, - caption: Optional[str]=None, + reply_to_message_id: Optional[int]=None, + caption: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, @@ -4187,7 +4187,7 @@ async def send_document( Use this method to send general files. Telegram documentation: https://core.telegram.org/bots/api#senddocument - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -4241,7 +4241,7 @@ async def send_document( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4261,7 +4261,7 @@ async def send_document( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if data and not(document): # function typo miss compatibility @@ -4274,7 +4274,7 @@ async def send_document( if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -4295,7 +4295,7 @@ async def send_document( if isinstance(document, types.InputFile) and visible_file_name: # inputfile name ignored, warn logger.warning('Cannot use both InputFile and visible_file_name. InputFile name will be ignored.') - + return types.Message.de_json( await asyncio_helper.send_data( self.token, chat_id, document, 'document', @@ -4306,10 +4306,10 @@ async def send_document( message_thread_id = message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def send_sticker( - self, chat_id: Union[int, str], sticker: Union[Any, str], - reply_to_message_id: Optional[int]=None, + self, chat_id: Union[int, str], sticker: Union[Any, str], + reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, @@ -4361,7 +4361,7 @@ async def send_sticker( :param emoji: Emoji associated with the sticker; only for just uploaded stickers :type emoji: :obj:`str` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4380,7 +4380,7 @@ async def send_sticker( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if data and not(sticker): # function typo miss compatibility @@ -4389,7 +4389,7 @@ async def send_sticker( if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -4411,23 +4411,23 @@ async def send_sticker( await asyncio_helper.send_data( self.token, chat_id, sticker, 'sticker', reply_markup=reply_markup, - disable_notification=disable_notification, timeout=timeout, + disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, emoji=emoji, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def send_video( - self, chat_id: Union[int, str], video: Union[Any, str], + self, chat_id: Union[int, str], video: Union[Any, str], duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, - thumbnail: Optional[Union[Any, str]]=None, - caption: Optional[str]=None, - parse_mode: Optional[str]=None, + thumbnail: Optional[Union[Any, str]]=None, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - supports_streaming: Optional[bool]=None, + supports_streaming: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, @@ -4444,7 +4444,7 @@ async def send_video( start_timestamp: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). - + Telegram documentation: https://core.telegram.org/bots/api#sendvideo :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -4472,7 +4472,7 @@ async def send_video( :param start_timestamp: Start timestamp for the video in the message :type start_timestamp: :obj:`int` - + :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -4516,7 +4516,7 @@ async def send_video( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4539,11 +4539,11 @@ async def send_video( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -4578,19 +4578,19 @@ async def send_video( show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, cover=cover, start_timestamp=start_timestamp)) async def send_animation( - self, chat_id: Union[int, str], animation: Union[Any, str], + self, chat_id: Union[int, str], animation: Union[Any, str], duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, - caption: Optional[str]=None, + caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, @@ -4603,7 +4603,7 @@ async def send_animation( """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. - + Telegram documentation: https://core.telegram.org/bots/api#sendanimation :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -4621,7 +4621,7 @@ async def send_animation( :param height: Animation height :type height: :obj:`int` - + :param thumbnail: 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, @@ -4665,7 +4665,7 @@ async def send_animation( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4688,11 +4688,11 @@ async def send_animation( parse_mode = self.parse_mode if (parse_mode is None) else parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -4722,13 +4722,13 @@ async def send_animation( message_effect_id=message_effect_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast)) async def send_video_note( - self, chat_id: Union[int, str], data: Union[Any, str], - duration: Optional[int]=None, + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, length: Optional[int]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, @@ -4743,10 +4743,10 @@ async def send_video_note( Use this method to send video messages. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendvideonote - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - + :param data: Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. Sending video notes by a URL is currently unsupported :type data: :obj:`str` or :class:`telebot.types.InputFile` @@ -4774,7 +4774,7 @@ async def send_video_note( :param thumbnail: 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 . + so you can pass “attach://” if the thumbnail was uploaded using multipart/form-data under . :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. Pass True, if the message should be sent even if the specified replied-to message is not found @@ -4788,7 +4788,7 @@ async def send_video_note( :param thumb: Deprecated. Use thumbnail instead :type thumb: :obj:`str` or :class:`telebot.types.InputFile` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4807,11 +4807,11 @@ async def send_video_note( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -4906,13 +4906,13 @@ async def send_paid_media( payload=payload, allow_paid_broadcast=allow_paid_broadcast)) async def send_media_group( - self, chat_id: Union[int, str], + self, chat_id: Union[int, str], media: List[Union[ - types.InputMediaAudio, types.InputMediaDocument, + types.InputMediaAudio, types.InputMediaDocument, types.InputMediaPhoto, types.InputMediaVideo]], - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, + reply_to_message_id: Optional[int]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, message_thread_id: Optional[int]=None, @@ -4923,7 +4923,7 @@ async def send_media_group( """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. - + Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -4949,7 +4949,7 @@ async def send_media_group( :param message_thread_id: Identifier of a message thread, in which the messages will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -4973,11 +4973,11 @@ async def send_media_group( media_item.parse_mode = self.parse_mode disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -5001,16 +5001,16 @@ async def send_media_group( return [types.Message.de_json(msg) for msg in result] async def send_location( - self, chat_id: Union[int, str], - latitude: float, longitude: float, - live_period: Optional[int]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, + self, chat_id: Union[int, str], + latitude: float, longitude: float, + live_period: Optional[int]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, - proximity_alert_radius: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, + proximity_alert_radius: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, @@ -5060,13 +5060,13 @@ async def send_location( :param allow_sending_without_reply: Deprecated - Use reply_parameters instead. 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 protect_content: Protects the contents of the sent message from forwarding and saving :type protect_content: :obj:`bool` :param message_thread_id: Identifier of a message thread, in which the message will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -5085,11 +5085,11 @@ async def send_location( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -5109,20 +5109,20 @@ async def send_location( return types.Message.de_json( await asyncio_helper.send_location( - self.token, chat_id, latitude, longitude, live_period, - reply_markup, disable_notification, timeout, - horizontal_accuracy, heading, proximity_alert_radius, + self.token, chat_id, latitude, longitude, live_period, + reply_markup, disable_notification, timeout, + horizontal_accuracy, heading, proximity_alert_radius, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def edit_message_live_location( - self, latitude: float, longitude: float, - chat_id: Optional[Union[int, str]]=None, + self, latitude: float, longitude: float, + chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, live_period: Optional[int]=None, business_connection_id: Optional[str]=None @@ -5182,9 +5182,9 @@ async def edit_message_live_location( ) async def stop_message_live_location( - self, chat_id: Optional[Union[int, str]]=None, + self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, business_connection_id: Optional[str]=None) -> types.Message: @@ -5193,7 +5193,7 @@ async def stop_message_live_location( On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. Telegram documentation: https://core.telegram.org/bots/api#stopmessagelivelocation - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -5221,14 +5221,14 @@ async def stop_message_live_location( self.token, chat_id, message_id, inline_message_id, reply_markup, timeout, business_connection_id)) async def send_venue( - self, chat_id: Union[int, str], - latitude: float, longitude: float, - title: str, address: str, - foursquare_id: Optional[str]=None, + self, chat_id: Union[int, str], + latitude: float, longitude: float, + title: str, address: str, + foursquare_id: Optional[str]=None, foursquare_type: Optional[str]=None, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, google_place_id: Optional[str]=None, @@ -5241,12 +5241,12 @@ async def send_venue( allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. - + Telegram documentation: https://core.telegram.org/bots/api#sendvenue :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`int` or :obj:`str` - + :param latitude: Latitude of the venue :type latitude: :obj:`float` @@ -5295,7 +5295,7 @@ async def send_venue( :param message_thread_id: The thread to which the message will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -5304,7 +5304,7 @@ async def send_venue( :param message_effect_id: Unique identifier of the message effect :type message_effect_id: :obj:`str` - + :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` @@ -5314,11 +5314,11 @@ async def send_venue( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -5342,15 +5342,15 @@ async def send_venue( disable_notification, reply_markup, timeout, google_place_id, google_place_type, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) - + async def send_contact( - self, chat_id: Union[int, str], phone_number: str, - first_name: str, last_name: Optional[str]=None, + self, chat_id: Union[int, str], phone_number: str, + first_name: str, last_name: Optional[str]=None, vcard: Optional[str]=None, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, @@ -5402,7 +5402,7 @@ async def send_contact( :param message_thread_id: The thread to which the message will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -5421,11 +5421,11 @@ async def send_contact( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -5448,7 +5448,7 @@ async def send_contact( self.token, chat_id, phone_number, first_name, last_name, vcard, disable_notification, reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) - + async def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, @@ -5465,7 +5465,7 @@ async def send_chat_action( :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`int` or :obj:`str` - + :param action: Type of action to broadcast. Choose one, depending on what the user is about to receive: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_voice or upload_voice for voice notes, upload_document for general files, @@ -5487,8 +5487,8 @@ async def send_chat_action( return await asyncio_helper.send_chat_action(self.token, chat_id, action, timeout, message_thread_id, business_connection_id) async def kick_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date:Optional[Union[int, datetime]]=None, + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ This function is deprecated. Use `ban_chat_member` instead @@ -5497,13 +5497,13 @@ async def kick_chat_member( return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) async def ban_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date:Optional[Union[int, datetime]]=None, + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ - Use this method to ban a user in a group, a supergroup or a channel. - In the case of supergroups and channels, the user will not be able to return to the chat on their - own using invite links, etc., unless unbanned first. + Use this method to ban a user in a group, a supergroup or a channel. + In the case of supergroups and channels, the user will not be able to return to the chat on their + own using invite links, etc., unless unbanned first. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#banchatmember @@ -5520,17 +5520,17 @@ async def ban_chat_member( :type until_date: :obj:`int` or :obj:`datetime` :param revoke_messages: Bool: Pass True to delete all messages from the chat for the user that is being removed. - If False, the user will be able to see messages in the group that were sent before the user was removed. + If False, the user will be able to see messages in the group that were sent before the user was removed. Always True for supergroups and channels. :type revoke_messages: :obj:`bool` - + :return: Returns True on success. :rtype: :obj:`bool` """ return await asyncio_helper.ban_chat_member(self.token, chat_id, user_id, until_date, revoke_messages) async def unban_chat_member( - self, chat_id: Union[int, str], user_id: int, + self, chat_id: Union[int, str], user_id: int, only_if_banned: Optional[bool]=False) -> bool: """ Use this method to unban a previously kicked user in a supergroup or channel. @@ -5557,15 +5557,15 @@ async def unban_chat_member( return await asyncio_helper.unban_chat_member(self.token, chat_id, user_id, only_if_banned) async def restrict_chat_member( - self, chat_id: Union[int, str], user_id: int, + self, chat_id: Union[int, str], user_id: int, until_date: Optional[Union[int, datetime]]=None, - can_send_messages: Optional[bool]=None, + can_send_messages: Optional[bool]=None, can_send_media_messages: Optional[bool]=None, - can_send_polls: Optional[bool]=None, + can_send_polls: Optional[bool]=None, can_send_other_messages: Optional[bool]=None, - can_add_web_page_previews: Optional[bool]=None, + can_add_web_page_previews: Optional[bool]=None, can_change_info: Optional[bool]=None, - can_invite_users: Optional[bool]=None, + can_invite_users: Optional[bool]=None, can_pin_messages: Optional[bool]=None, permissions: Optional[types.ChatPermissions]=None, use_independent_chat_permissions: Optional[bool]=None) -> bool: @@ -5593,10 +5593,10 @@ async def restrict_chat_member( :param can_send_messages: deprecated :type can_send_messages: :obj:`bool` - + :param can_send_media_messages: deprecated :type can_send_media_messages: :obj:`bool` - + :param can_send_polls: deprecated :type can_send_polls: :obj:`bool` @@ -5644,17 +5644,17 @@ async def restrict_chat_member( self.token, chat_id, user_id, permissions, until_date, use_independent_chat_permissions) async def promote_chat_member( - self, chat_id: Union[int, str], user_id: int, - can_change_info: Optional[bool]=None, + self, chat_id: Union[int, str], user_id: int, + can_change_info: Optional[bool]=None, can_post_messages: Optional[bool]=None, - can_edit_messages: Optional[bool]=None, - can_delete_messages: Optional[bool]=None, + can_edit_messages: Optional[bool]=None, + can_delete_messages: Optional[bool]=None, can_invite_users: Optional[bool]=None, - can_restrict_members: Optional[bool]=None, - can_pin_messages: Optional[bool]=None, + can_restrict_members: Optional[bool]=None, + can_pin_messages: Optional[bool]=None, can_promote_members: Optional[bool]=None, - is_anonymous: Optional[bool]=None, - can_manage_chat: Optional[bool]=None, + is_anonymous: Optional[bool]=None, + can_manage_chat: Optional[bool]=None, can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None, can_manage_topics: Optional[bool]=None, @@ -5704,9 +5704,9 @@ async def promote_chat_member( :param is_anonymous: Pass True, if the administrator's presence in the chat is hidden :type is_anonymous: :obj:`bool` - :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, - message statistics in channels, see channel members, - see anonymous administrators in supergroups and ignore slow mode. + :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, + message statistics in channels, see channel members, + see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege :type can_manage_chat: :obj:`bool` @@ -5774,10 +5774,10 @@ async def set_chat_administrator_custom_title( async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ Use this method to ban a channel chat in a supergroup or a channel. - The owner of the chat will not be able to send messages and join live - streams on behalf of the chat, unless it is unbanned first. - The bot must be an administrator in the supergroup or channel - for this to work and must have the appropriate administrator rights. + The owner of the chat will not be able to send messages and join live + streams on behalf of the chat, unless it is unbanned first. + The bot must be an administrator in the supergroup or channel + for this to work and must have the appropriate administrator rights. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#banchatsenderchat @@ -5795,8 +5795,8 @@ async def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: U async def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ - Use this method to unban a previously banned channel chat in a supergroup or channel. - The bot must be an administrator for this to work and must have the appropriate + Use this method to unban a previously banned channel chat in a supergroup or channel. + The bot must be an administrator for this to work and must have the appropriate administrator rights. Returns True on success. @@ -5844,7 +5844,7 @@ async def set_chat_permissions( async def create_chat_invite_link( self, chat_id: Union[int, str], name: Optional[str]=None, - expire_date: Optional[Union[int, datetime]]=None, + expire_date: Optional[Union[int, datetime]]=None, member_limit: Optional[int]=None, creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ @@ -5916,7 +5916,7 @@ async def edit_chat_invite_link( return types.ChatInviteLink.de_json( await asyncio_helper.edit_chat_invite_link(self.token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request) ) - + async def create_chat_subscription_invite_link( self, chat_id: Union[int, str], subscription_period: int, subscription_price: int, name: Optional[str]=None) -> types.ChatInviteLink: @@ -5930,7 +5930,7 @@ async def create_chat_subscription_invite_link( :param chat_id: Unique identifier for the target channel chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - + :param name: Invite link name; 0-32 characters :type name: :obj:`str` @@ -5978,7 +5978,7 @@ async def revoke_chat_invite_link( self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: """ Use this method to revoke an invite link created by the bot. - Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator + Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink @@ -6016,7 +6016,7 @@ async def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: async def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ - Use this method to approve a chat join request. + Use this method to approve a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. @@ -6036,7 +6036,7 @@ async def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Uni async def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ - Use this method to decline a chat join request. + Use this method to decline a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. @@ -6092,7 +6092,7 @@ async def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: :rtype: :obj:`bool` """ return await asyncio_helper.delete_chat_photo(self.token, chat_id) - + async def set_my_description(self, description: Optional[str]=None, language_code: Optional[str]=None): """ Use this method to change the bot's description, which is shown in @@ -6123,11 +6123,11 @@ async def get_my_description(self, language_code: Optional[str]=None): """ result = await asyncio_helper.get_my_description(self.token, language_code) return types.BotDescription.de_json(result) - + async def set_my_short_description(self, short_description:Optional[str]=None, language_code:Optional[str]=None): """ Use this method to change the bot's short description, which is shown on the bot's profile page and - is sent together with the link when users share the bot. + is sent together with the link when users share the bot. Returns True on success. :param short_description: New short description for the bot; 0-120 characters. Pass an empty string to remove the dedicated short description for the given language. @@ -6141,7 +6141,7 @@ async def set_my_short_description(self, short_description:Optional[str]=None, l """ return await asyncio_helper.set_my_short_description(self.token, short_description, language_code) - + async def get_my_short_description(self, language_code: Optional[str]=None): """ Use this method to get the current bot short description for the given user language. @@ -6154,21 +6154,21 @@ async def get_my_short_description(self, language_code: Optional[str]=None): """ result = await asyncio_helper.get_my_short_description(self.token, language_code) return types.BotShortDescription.de_json(result) - - async def get_my_commands(self, scope: Optional[types.BotCommandScope], + + async def get_my_commands(self, scope: Optional[types.BotCommandScope], language_code: Optional[str]) -> List[types.BotCommand]: """ - Use this method to get the current list of the bot's commands. + Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. Telegram documentation: https://core.telegram.org/bots/api#getmycommands - :param scope: The scope of users for which the commands are relevant. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -6212,16 +6212,16 @@ async def get_my_name(self, language_code: Optional[str]=None): result = await asyncio_helper.get_my_name(self.token, language_code) return types.BotName.de_json(result) - async def set_chat_menu_button(self, chat_id: Union[int, str]=None, + async def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: """ - Use this method to change the bot's menu button in a private chat, - or the default menu button. + Use this method to change the bot's menu button in a private chat, + or the default menu button. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton - :param chat_id: Unique identifier for the target private chat. + :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be changed. :type chat_id: :obj:`int` or :obj:`str` @@ -6252,13 +6252,13 @@ async def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.Men return types.MenuButton.de_json(await asyncio_helper.get_chat_menu_button(self.token, chat_id)) - async def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, + async def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, for_channels: bool=None) -> bool: """ Use this method to change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. These rights will be suggested to users, but they are are free to modify - the list before adding the bot. + the list before adding the bot. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights @@ -6276,7 +6276,7 @@ async def set_my_default_administrator_rights(self, rights: types.ChatAdministra """ return await asyncio_helper.set_my_default_administrator_rights(self.token, rights, for_channels) - + async def get_my_default_administrator_rights(self, for_channels: bool=None) -> types.ChatAdministratorRights: """ @@ -6291,9 +6291,9 @@ async def get_my_default_administrator_rights(self, for_channels: bool=None) -> :return: Returns ChatAdministratorRights on success. :rtype: :class:`telebot.types.ChatAdministratorRights` """ - + return types.ChatAdministratorRights.de_json(await asyncio_helper.get_my_default_administrator_rights(self.token, for_channels)) - + async def get_business_connection(self, business_connection_id: str) -> types.BusinessConnection: """ Use this method to get information about the connection of the bot with a business account. @@ -6312,9 +6312,9 @@ async def get_business_connection(self, business_connection_id: str) -> types.Bu return types.BusinessConnection.de_json( result ) - - async def set_my_commands(self, commands: List[types.BotCommand], + + async def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: """ @@ -6325,12 +6325,12 @@ async def set_my_commands(self, commands: List[types.BotCommand], :param commands: List of BotCommand. At most 100 commands can be specified. :type commands: :obj:`list` of :class:`telebot.types.BotCommand` - :param scope: The scope of users for which the commands are relevant. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -6338,22 +6338,22 @@ async def set_my_commands(self, commands: List[types.BotCommand], :rtype: :obj:`bool` """ return await asyncio_helper.set_my_commands(self.token, commands, scope, language_code) - - async def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, + + async def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, language_code: Optional[int]=None) -> bool: """ - Use this method to delete the list of the bot's commands for the given scope and user language. - After deletion, higher level commands will be shown to affected users. + Use this method to delete the list of the bot's commands for the given scope and user language. + After deletion, higher level commands will be shown to affected users. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemycommands - - :param scope: The scope of users for which the commands are relevant. + + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -6404,7 +6404,7 @@ async def set_chat_description(self, chat_id: Union[int, str], description: Opti return await asyncio_helper.set_chat_description(self.token, chat_id, description) async def pin_chat_message( - self, chat_id: Union[int, str], message_id: int, + self, chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=False, business_connection_id: Optional[str]=None) -> bool: """ Use this method to pin a message in a supergroup. @@ -6475,10 +6475,10 @@ async def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: return await asyncio_helper.unpin_all_chat_messages(self.token, chat_id) async def edit_message_text( - self, text: str, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + self, text: str, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, @@ -6556,9 +6556,9 @@ async def edit_message_text( return types.Message.de_json(result) async def edit_message_media( - self, media: Any, chat_id: Optional[Union[int, str]]=None, + self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: @@ -6567,7 +6567,7 @@ async def edit_message_media( If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. When an inline message is edited, a new file can't be uploaded; use a previously uploaded file via its file_id or specify a URL. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. - Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. + Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia @@ -6576,7 +6576,7 @@ async def edit_message_media( :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message :type message_id: :obj:`int` :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message @@ -6601,9 +6601,9 @@ async def edit_message_media( return types.Message.de_json(result) async def edit_message_reply_markup( - self, chat_id: Optional[Union[int, str]]=None, + self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: @@ -6640,10 +6640,10 @@ async def edit_message_reply_markup( return types.Message.de_json(result) async def send_game( - self, chat_id: Union[int, str], game_short_name: str, + self, chat_id: Union[int, str], game_short_name: str, disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, protect_content: Optional[bool]=None, @@ -6666,7 +6666,7 @@ async def send_game( :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`bool` - :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message + :param reply_to_message_id: Deprecated - Use reply_parameters instead. If the message is a reply, ID of the original message :type reply_to_message_id: :obj:`int` :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. @@ -6683,7 +6683,7 @@ async def send_game( :param message_thread_id: Identifier of the thread to which the message will be sent. :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -6702,11 +6702,11 @@ async def send_game( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -6726,15 +6726,15 @@ async def send_game( result = await asyncio_helper.send_game( self.token, chat_id, game_short_name, disable_notification, - reply_markup, timeout, + reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) return types.Message.de_json(result) async def set_game_score( - self, user_id: Union[int, str], score: int, - force: Optional[bool]=None, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + self, user_id: Union[int, str], score: int, + force: Optional[bool]=None, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None, disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: """ @@ -6774,7 +6774,7 @@ async def set_game_score( async def get_game_high_scores( self, user_id: int, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ Use this method to get data for high score tables. Will return the score of the specified user and several of @@ -6805,20 +6805,20 @@ async def get_game_high_scores( return [types.GameHighScore.de_json(r) for r in result] async def send_invoice( - self, chat_id: Union[int, str], title: str, description: str, - invoice_payload: str, provider_token: Union[str, None], currency: str, - prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, - photo_url: Optional[str]=None, photo_size: Optional[int]=None, + self, chat_id: Union[int, str], title: str, description: str, + invoice_payload: str, provider_token: Union[str, None], currency: str, + prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, + photo_url: Optional[str]=None, photo_size: Optional[int]=None, photo_width: Optional[int]=None, photo_height: Optional[int]=None, - need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, + need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, need_email: Optional[bool]=None, need_shipping_address: Optional[bool]=None, - send_phone_number_to_provider: Optional[bool]=None, - send_email_to_provider: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, is_flexible: Optional[bool]=None, - disable_notification: Optional[bool]=None, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - provider_data: Optional[str]=None, + disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + provider_data: Optional[str]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, max_tip_amount: Optional[int] = None, @@ -6869,7 +6869,7 @@ async def send_invoice( :param photo_size: Photo size in bytes :type photo_size: :obj:`int` - :param photo_width: Photo width + :param photo_width: Photo width :type photo_width: :obj:`int` :param photo_height: Photo height @@ -6929,7 +6929,7 @@ async def send_invoice( :param message_thread_id: The identifier of a message thread, in which the invoice message will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -6945,11 +6945,11 @@ async def send_invoice( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if allow_sending_without_reply is not None: logger.warning("The parameter 'allow_sending_without_reply' is deprecated. Use 'reply_parameters' instead.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -6979,9 +6979,9 @@ async def send_invoice( async def create_invoice_link(self, - title: str, description: str, payload:str, provider_token: Union[str, None], + title: str, description: str, payload:str, provider_token: Union[str, None], currency: str, prices: List[types.LabeledPrice], - max_tip_amount: Optional[int] = None, + max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, provider_data: Optional[str]=None, photo_url: Optional[str]=None, @@ -6997,14 +6997,14 @@ async def create_invoice_link(self, is_flexible: Optional[bool]=None, subscription_period: Optional[int]=None, business_connection_id: Optional[str]=None) -> str: - + """ - Use this method to create a link for an invoice. + Use this method to create a link for an invoice. Returns the created invoice link as String on success. Telegram documentation: https://core.telegram.org/bots/api#createinvoicelink - + :param business_connection_id: Unique identifier of the business connection on behalf of which the link will be created :type business_connection_id: :obj:`str` @@ -7095,18 +7095,18 @@ async def create_invoice_link(self, # noinspection PyShadowingBuiltins async def send_poll( self, chat_id: Union[int, str], question: str, options: List[Union[str, types.InputPollOption]], - is_anonymous: Optional[bool]=None, type: Optional[str]=None, - allows_multiple_answers: Optional[bool]=None, + is_anonymous: Optional[bool]=None, type: Optional[str]=None, + allows_multiple_answers: Optional[bool]=None, correct_option_id: Optional[int]=None, - explanation: Optional[str]=None, - explanation_parse_mode: Optional[str]=None, - open_period: Optional[int]=None, - close_date: Optional[Union[int, datetime]]=None, + explanation: Optional[str]=None, + explanation_parse_mode: Optional[str]=None, + open_period: Optional[int]=None, + close_date: Optional[Union[int, datetime]]=None, is_closed: Optional[bool]=None, disable_notification: Optional[bool]=False, - reply_to_message_id: Optional[int]=None, - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - allow_sending_without_reply: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + allow_sending_without_reply: Optional[bool]=None, timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, protect_content: Optional[bool]=None, @@ -7186,7 +7186,7 @@ async def send_poll( :param message_thread_id: The identifier of a message thread, in which the poll will be sent :type message_thread_id: :obj:`int` - + :param reply_parameters: Reply parameters. :type reply_parameters: :class:`telebot.types.ReplyParameters` @@ -7211,13 +7211,13 @@ async def send_poll( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification 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.") - + if reply_to_message_id: # show a deprecation warning logger.warning("The parameter 'reply_to_message_id' is deprecated. Use 'reply_parameters' instead.") @@ -7261,7 +7261,7 @@ async def send_poll( message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) async def stop_poll( - self, chat_id: Union[int, str], message_id: int, + self, chat_id: Union[int, str], message_id: int, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None) -> types.Poll: """ @@ -7287,8 +7287,8 @@ async def stop_poll( return types.Poll.de_json(await asyncio_helper.stop_poll(self.token, chat_id, message_id, reply_markup, business_connection_id)) async def answer_shipping_query( - self, shipping_query_id: str, ok: bool, - shipping_options: Optional[List[types.ShippingOption]]=None, + self, shipping_query_id: str, ok: bool, + shipping_options: Optional[List[types.ShippingOption]]=None, error_message: Optional[str]=None) -> bool: """ Asks for an answer to a shipping question. @@ -7314,7 +7314,7 @@ async def answer_shipping_query( return await asyncio_helper.answer_shipping_query(self.token, shipping_query_id, ok, shipping_options, error_message) async def answer_pre_checkout_query( - self, pre_checkout_query_id: str, ok: bool, + self, pre_checkout_query_id: str, ok: bool, error_message: Optional[str]=None) -> bool: """ Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the @@ -7326,7 +7326,7 @@ async def answer_pre_checkout_query( Telegram documentation: https://core.telegram.org/bots/api#answerprecheckoutquery - :param pre_checkout_query_id: Unique identifier for the query to be answered + :param pre_checkout_query_id: Unique identifier for the query to be answered :type pre_checkout_query_id: :obj:`int` :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. @@ -7349,7 +7349,7 @@ async def get_my_star_balance(self) -> types.StarAmount: On success, returns a StarAmount object. """ return types.StarAmount.de_json(await asyncio_helper.get_my_star_balance(self.token)) - + async def get_star_transactions(self, offset: Optional[int]=None, limit: Optional[int]=None) -> types.StarTransactions: """ Returns the bot's Telegram Star transactions in chronological order. @@ -7367,7 +7367,7 @@ async def get_star_transactions(self, offset: Optional[int]=None, limit: Optiona """ return types.StarTransactions.de_json(await asyncio_helper.get_star_transactions(self.token, offset, limit)) - + async def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> bool: """ Refunds a successful payment in Telegram Stars. Returns True on success. @@ -7406,10 +7406,10 @@ async def edit_user_star_subscription(self, user_id: int, telegram_payment_charg return await asyncio_helper.edit_user_star_subscription(self.token, user_id, telegram_payment_charge_id, is_canceled) async def edit_message_caption( - self, caption: str, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + self, caption: str, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - parse_mode: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, show_caption_above_media: Optional[bool]=None, @@ -7466,7 +7466,7 @@ async def edit_message_caption( async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message: """ Convenience function for `send_message(message.chat.id, text, reply_parameters=(message.message_id...), **kwargs)` - + :param message: Instance of :class:`telebot.types.Message` :type message: :obj:`types.Message` @@ -7497,12 +7497,12 @@ async def reply_to(self, message: types.Message, text: str, **kwargs) -> types.M return await self.send_message(message.chat.id, text, reply_parameters=reply_parameters, **kwargs) async def answer_inline_query( - self, inline_query_id: str, - results: List[Any], - cache_time: Optional[int]=None, - is_personal: Optional[bool]=None, + self, inline_query_id: str, + results: List[Any], + cache_time: Optional[int]=None, + is_personal: Optional[bool]=None, next_offset: Optional[str]=None, - switch_pm_text: Optional[str]=None, + switch_pm_text: Optional[str]=None, switch_pm_parameter: Optional[str]=None, button: Optional[types.InlineQueryResultsButton]=None) -> bool: """ @@ -7555,7 +7555,7 @@ async def answer_inline_query( async def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool: """ - Use this method to clear the list of pinned messages in a General forum topic. + Use this method to clear the list of pinned messages in a General forum topic. The bot must be an administrator in the chat for this to work and must have the can_pin_messages administrator right in the supergroup. Returns True on success. @@ -7572,8 +7572,8 @@ async def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) return await asyncio_helper.unpin_all_general_forum_topic_messages(self.token, chat_id) async def answer_callback_query( - self, callback_query_id: int, - text: Optional[str]=None, show_alert: Optional[bool]=None, + self, callback_query_id: int, + text: Optional[str]=None, show_alert: Optional[bool]=None, url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to @@ -7601,14 +7601,14 @@ async def answer_callback_query( :rtype: :obj:`bool` """ return await asyncio_helper.answer_callback_query(self.token, callback_query_id, text, show_alert, url, cache_time) - + # getUserChatBoosts # Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. # Parameter Type Required Description # chat_id Integer or String Yes Unique identifier for the chat or username of the channel (in the format @channelusername) # user_id Integer Yes Unique identifier of the target user - + async def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types.UserChatBoosts: """ Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. @@ -7627,11 +7627,11 @@ async def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> result = await asyncio_helper.get_user_chat_boosts(self.token, chat_id, user_id) return types.UserChatBoosts.de_json(result) - + async 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. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumbnail @@ -7657,11 +7657,11 @@ async def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Un logger.warning("Deprecation warning. 'format' parameter is required in set_sticker_set_thumbnail. Setting format to 'static'.") format = "static" return await asyncio_helper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) - + @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") async def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]=None): """ - Use this method to set the thumbnail of a sticker set. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb @@ -7684,7 +7684,7 @@ async def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, async def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. - + Telegram documentation: https://core.telegram.org/bots/api#getstickerset :param name: Sticker set name @@ -7695,7 +7695,7 @@ async def get_sticker_set(self, name: str) -> types.StickerSet: """ result = await asyncio_helper.get_sticker_set(self.token, name) return types.StickerSet.de_json(result) - + async def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: """ Use this method to change search keywords assigned to a regular or custom emoji sticker. @@ -7712,7 +7712,7 @@ async def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> :rtype: :obj:`bool` """ return await asyncio_helper.set_sticker_keywords(self.token, sticker, keywords) - + async def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosition=None) -> bool: """ Use this method to change the mask position of a mask sticker. @@ -7748,7 +7748,7 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=N """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. - + Telegram documentation: https://core.telegram.org/bots/api#uploadstickerfile :param user_id: User identifier of sticker set owner @@ -7762,7 +7762,7 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=N See https://core.telegram.org/stickers for technical requirements. More information on Sending Files » :type sticker: :class:`telebot.types.InputFile` - :param sticker_format: One of "static", "animated", "video". + :param sticker_format: One of "static", "animated", "video". :type sticker_format: :obj:`str` :return: On success, the sent file is returned. @@ -7772,10 +7772,10 @@ async def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=N logger.warning('The parameter "png_sticker" is deprecated. Use "sticker" instead.') sticker = png_sticker sticker_format = "static" - + result = await asyncio_helper.upload_sticker_file(self.token, user_id, sticker, sticker_format) return types.File.de_json(result) - + async def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: """ Use this method to set the thumbnail of a custom emoji sticker set. @@ -7791,7 +7791,7 @@ async def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_i :rtype: :obj:`bool` """ return await asyncio_helper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id) - + async def set_sticker_set_title(self, name: str, title: str) -> bool: """ Use this method to set the title of a created sticker set. @@ -7857,13 +7857,13 @@ async def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: st """ if user_id is None and chat_id is None: raise ValueError("Either user_id or chat_id must be specified.") - + if gift_id is None: raise ValueError("gift_id must be specified.") - + return await asyncio_helper.send_gift(self.token, gift_id, text, text_parse_mode, text_entities, pay_for_upgrade=pay_for_upgrade, chat_id=chat_id, user_id=user_id) - + async def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool: """ Verifies a user on behalf of the organization which is represented by the bot. Returns True on success. @@ -7927,7 +7927,7 @@ async def remove_chat_verification(self, chat_id: int) -> bool: :rtype: :obj:`bool` """ return await asyncio_helper.remove_chat_verification(self.token, chat_id) - + async def read_business_message(self, business_connection_id: str, chat_id: Union[int, str], message_id: int) -> bool: """ Marks incoming message as read on behalf of a business account. Requires the can_read_messages business bot right. Returns True on success. @@ -8019,7 +8019,7 @@ async def set_business_account_bio(self, business_connection_id: str, bio: Optio :rtype: :obj:`bool` """ return await asyncio_helper.set_business_account_bio(self.token, business_connection_id, bio=bio) - + async def set_business_account_gift_settings( self, business_connection_id: str, show_gift_button: bool, accepted_gift_types: types.AcceptedGiftTypes) -> bool: """ @@ -8040,11 +8040,11 @@ async def set_business_account_gift_settings( :rtype: :obj:`bool` """ return await asyncio_helper.set_business_account_gift_settings(self.token, business_connection_id, show_gift_button, accepted_gift_types) - + async def get_business_account_star_balance(self, business_connection_id: str) -> types.StarAmount: """ Returns the amount of Telegram Stars owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns StarAmount on success. - + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountstarbalance :param business_connection_id: Unique identifier of the business connection @@ -8056,7 +8056,7 @@ async def get_business_account_star_balance(self, business_connection_id: str) - return types.StarAmount.de_json( await asyncio_helper.get_business_account_star_balance(self.token, business_connection_id) ) - + async def transfer_business_account_stars(self, business_connection_id: str, star_count: int) -> bool: """ Transfers Telegram Stars from the business account balance to the bot's balance. Requires the can_transfer_stars business bot right. Returns True on success. @@ -8073,7 +8073,7 @@ async def transfer_business_account_stars(self, business_connection_id: str, sta :rtype: :obj:`bool` """ return await asyncio_helper.transfer_business_account_stars(self.token, business_connection_id, star_count) - + async def get_business_account_gifts( self, business_connection_id: str, exclude_unsaved: Optional[bool]=None, @@ -8086,7 +8086,7 @@ async def get_business_account_gifts( limit: Optional[int]=None) -> types.OwnedGifts: """ Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. - + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountgifts :param business_connection_id: Unique identifier of the business connection @@ -8132,7 +8132,7 @@ async def get_business_account_gifts( limit=limit ) ) - + async def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) -> bool: """ Converts a given regular gift to Telegram Stars. Requires the can_convert_gifts_to_stars business bot right. Returns True on success. @@ -8149,7 +8149,7 @@ async def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id :rtype: :obj:`bool` """ return await asyncio_helper.convert_gift_to_stars(self.token, business_connection_id, owned_gift_id) - + async def upgrade_gift( self, business_connection_id: str, owned_gift_id: str, keep_original_details: Optional[bool]=None, @@ -8213,7 +8213,7 @@ async def transfer_gift( new_owner_chat_id, star_count=star_count ) - + async def post_story( self, business_connection_id: str, content: types.InputStoryContent, active_period: int, caption: Optional[str]=None, @@ -8320,7 +8320,7 @@ async def edit_story( async def delete_story(self, business_connection_id: str, story_id: int) -> bool: """ Deletes a story previously posted by the bot on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletestory :param business_connection_id: Unique identifier of the business connection @@ -8333,7 +8333,7 @@ async def delete_story(self, business_connection_id: str, story_id: int) -> bool :rtype: :obj:`bool` """ return await asyncio_helper.delete_story(self.token, business_connection_id, story_id) - + async def set_business_account_profile_photo( self, business_connection_id: str, photo: types.InputProfilePhoto, is_public: Optional[bool]=None) -> bool: @@ -8375,7 +8375,7 @@ async def remove_business_account_profile_photo( :rtype: :obj:`bool` """ return await asyncio_helper.remove_business_account_profile_photo(self.token, business_connection_id, is_public=is_public) - + async def gift_premium_subscription( self, user_id: int, month_count: int, star_count: int, text: Optional[str]=None, text_parse_mode: Optional[str]=None, @@ -8424,7 +8424,7 @@ async def get_available_gifts(self) -> types.Gifts: return types.Gifts.de_json(await asyncio_helper.get_available_gifts(self.token)) - + async def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: """ Use this method to replace an existing sticker in a sticker set with a new one. The method is equivalent to calling deleteStickerFromSet, then addStickerToSet, @@ -8469,10 +8469,10 @@ async def set_sticker_emoji_list(self, name: str, emoji_list: List[str]) -> bool async def create_new_sticker_set( - self, user_id: int, name: str, title: str, + self, user_id: int, name: str, title: str, emojis: Optional[List[str]]=None, - png_sticker: Union[Any, str]=None, - tgs_sticker: Union[Any, str]=None, + png_sticker: Union[Any, str]=None, + tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, sticker_type: Optional[str]=None, @@ -8481,7 +8481,7 @@ async def create_new_sticker_set( stickers: List[types.InputSticker]=None, sticker_format: Optional[str]=None) -> bool: """ - Use this method to create new sticker set owned by a user. + Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success. @@ -8545,7 +8545,7 @@ async def create_new_sticker_set( sticker_format = 'video' elif png_sticker: sticker_format = 'static' - + if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') if sticker_type is None: @@ -8556,7 +8556,7 @@ async def create_new_sticker_set( if stickers is None: raise ValueError('You must pass at least one sticker') stickers = [types.InputSticker(sticker=stickers, emoji_list=emojis, mask_position=mask_position)] - + if sticker_format: logger.warning('The parameter "sticker_format" is deprecated since Bot API 7.2. Stickers can now be mixed') @@ -8566,8 +8566,8 @@ async def create_new_sticker_set( async def add_sticker_to_set( self, user_id: int, name: str, emojis: Union[List[str], str]=None, - png_sticker: Optional[Union[Any, str]]=None, - tgs_sticker: Optional[Union[Any, str]]=None, + png_sticker: Optional[Union[Any, str]]=None, + tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, mask_position: Optional[types.MaskPosition]=None, sticker: Optional[types.InputSticker]=None) -> bool: @@ -8631,7 +8631,7 @@ async def add_sticker_to_set( async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#setstickerpositioninset :param sticker: File identifier of the sticker @@ -8648,7 +8648,7 @@ async def set_sticker_position_in_set(self, sticker: str, position: int) -> bool async def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletestickerfromset :param sticker: File identifier of the sticker @@ -8799,9 +8799,9 @@ async def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#editgeneralforumtopic - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -8892,7 +8892,7 @@ async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: Added additional parameters to support topics, business connections, and message threads. - .. seealso:: + .. seealso:: For more details, visit the `custom_states.py example `_. @@ -8925,7 +8925,7 @@ async def set_state(self, user_id: int, state: Union[int, str, State], chat_id: bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - async def reset_data(self, user_id: int, chat_id: Optional[int]=None, + async def reset_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ @@ -8964,7 +8964,7 @@ async def delete_state(self, user_id: int, chat_id: Optional[int]=None, business :param user_id: User's identifier :type user_id: :obj:`int` - + :param chat_id: Chat's identifier :type chat_id: :obj:`int` @@ -9018,7 +9018,7 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - async def get_state(self, user_id: int, chat_id: Optional[int]=None, + async def get_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> str: """ @@ -9026,7 +9026,7 @@ async def get_state(self, user_id: int, chat_id: Optional[int]=None, Not recommended to use this method. But it is ok for debugging. .. warning:: - + Even if you are using :class:`telebot.types.State`, this method will return a string. When comparing(not recommended), you should compare this string with :class:`telebot.types.State`.name @@ -9056,9 +9056,9 @@ async def get_state(self, user_id: int, chat_id: Optional[int]=None, bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - async def add_data(self, user_id: int, chat_id: Optional[int]=None, + async def add_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None, **kwargs) -> None: """ From dd3626fbc9468acf75446e5a3061d829d34084f1 Mon Sep 17 00:00:00 2001 From: All-The-Foxes <116322192+All-The-Foxes@users.noreply.github.com> Date: Fri, 22 Aug 2025 12:32:24 -0400 Subject: [PATCH 1800/1808] Default to None for filter func in inline_handler for sync version --- telebot/__init__.py | 720 ++++++++++++++++++++++---------------------- 1 file changed, 360 insertions(+), 360 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 59a8d7db8..6949842f7 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -46,7 +46,7 @@ REPLY_MARKUP_TYPES = Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, + types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply] @@ -93,7 +93,7 @@ class TeleBot: from telebot import TeleBot bot = TeleBot('token') # get token from @BotFather - # now you can register other handlers/update listeners, + # now you can register other handlers/update listeners, # and use bot methods. See more examples in examples/ directory: @@ -139,7 +139,7 @@ class TeleBot: :param use_class_middlewares: Use class middlewares, defaults to False :type use_class_middlewares: :obj:`bool`, optional - + :param disable_web_page_preview: Default value for disable_web_page_preview, defaults to None :type disable_web_page_preview: :obj:`bool`, optional @@ -151,7 +151,7 @@ class TeleBot: :param allow_sending_without_reply: Default value for allow_sending_without_reply, defaults to None :type allow_sending_without_reply: :obj:`bool`, optional - + :param colorful_logs: Outputs colorful logs :type colorful_logs: :obj:`bool`, optional @@ -168,7 +168,7 @@ def __init__( next_step_backend: Optional[HandlerBackend]=None, reply_backend: Optional[HandlerBackend]=None, exception_handler: Optional[ExceptionHandler]=None, last_update_id: Optional[int]=0, suppress_middleware_excepions: Optional[bool]=False, state_storage: Optional[StateStorageBase]=StateMemoryStorage(), - use_class_middlewares: Optional[bool]=False, + use_class_middlewares: Optional[bool]=False, disable_web_page_preview: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, @@ -194,7 +194,7 @@ def __init__( if validate_token: util.validate_token(self.token) - + self.bot_id: Union[int, None] = util.extract_bot_id(self.token) # subject to change in future, unspecified # logs-related @@ -292,7 +292,7 @@ def __init__( self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(self, num_threads=num_threads) - + @property def user(self) -> types.User: """ @@ -438,15 +438,15 @@ def set_webhook(self, url: Optional[str]=None, certificate: Optional[Union[str, Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput, defaults to None :type max_connections: :obj:`int`, optional - + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - + Please note that this parameter doesn't affect updates created before the call to the setWebhook, so unwanted updates may be received for a short period of time. Defaults to None - + :type allowed_updates: :obj:`list`, optional :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address @@ -516,7 +516,7 @@ def run_webhooks(self, :type max_connections: :obj:`int`, optional :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] - to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). + to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. defaults to None :type allowed_updates: :obj:`list`, optional @@ -545,7 +545,7 @@ def run_webhooks(self, if not url_path: url_path = self.token + '/' if url_path[-1] != '/': url_path += '/' - + protocol = "https" if certificate else "http" if not webhook_url: webhook_url = "{}://{}:{}/{}".format(protocol, listen, port, url_path) @@ -627,8 +627,8 @@ def remove_webhook(self) -> bool: return self.set_webhook() # No params resets webhook - def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, - timeout: Optional[int]=20, allowed_updates: Optional[List[str]]=None, + def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, + timeout: Optional[int]=20, allowed_updates: Optional[List[str]]=None, long_polling_timeout: int=20) -> List[types.Update]: """ Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. @@ -678,14 +678,14 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update Registered listeners and applicable message handlers will be notified when a new message arrives. :meta private: - + :raises ApiException when a call has failed. """ if self.skip_pending: self.__skip_updates() logger.debug('Skipped all pending messages') self.skip_pending = False - updates = self.get_updates(offset=(self.last_update_id + 1), + updates = self.get_updates(offset=(self.last_update_id + 1), allowed_updates=allowed_updates, timeout=timeout, long_polling_timeout=long_polling_timeout) self.process_new_updates(updates) @@ -726,7 +726,7 @@ def process_new_updates(self, updates: List[types.Update]): new_edited_business_messages = None new_deleted_business_messages = None new_purchased_paid_media = None - + for update in updates: if apihelper.ENABLE_MIDDLEWARE and not self.use_class_middlewares: try: @@ -810,7 +810,7 @@ def process_new_updates(self, updates: List[types.Update]): if update.purchased_paid_media: if new_purchased_paid_media is None: new_purchased_paid_media = [] new_purchased_paid_media.append(update.purchased_paid_media) - + if new_messages: self.process_new_messages(new_messages) if new_edited_messages: @@ -890,7 +890,7 @@ def process_new_message_reaction(self, new_message_reactions): :meta private: """ self._notify_command_handlers(self.message_reaction_handlers, new_message_reactions, 'message_reaction') - + def process_new_message_reaction_count(self, new_message_reaction_counts): """ :meta private: @@ -938,7 +938,7 @@ def process_new_poll_answer(self, new_poll_answers): :meta private: """ self._notify_command_handlers(self.poll_answer_handlers, new_poll_answers, 'poll_answer') - + def process_new_my_chat_member(self, new_my_chat_members): """ :meta private: @@ -986,13 +986,13 @@ def process_new_edited_business_message(self, new_edited_business_messages): :meta private: """ self._notify_command_handlers(self.edited_business_message_handlers, new_edited_business_messages, 'edited_business_message') - + def process_new_deleted_business_messages(self, new_deleted_business_messages): """ :meta private: """ self._notify_command_handlers(self.deleted_business_messages_handlers, new_deleted_business_messages, 'deleted_business_messages') - + def process_new_purchased_paid_media(self, new_purchased_paid_media): """ :meta private: @@ -1047,7 +1047,7 @@ def _setup_change_detector(self, path_to_watch: str): # Make it possible to specify --path argument to the script path = sys.argv[sys.argv.index('--path') + 1] if '--path' in sys.argv else '.' - + self.event_observer = Observer() self.event_observer.schedule(self.event_handler, path, recursive=True) self.event_observer.start() @@ -1066,7 +1066,7 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo Wrap polling with infinite loop and exception handling to avoid bot stops polling. .. note:: - + Install watchdog and psutil before using restart_on_change option. :param timeout: Request connection timeout. @@ -1083,11 +1083,11 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo :type logger_level: :obj:`int`. :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - Please note that this parameter doesn't affect updates created before the call to the get_updates, + 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. :type allowed_updates: :obj:`list` of :obj:`str` @@ -1132,14 +1132,14 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. Warning: Do not call this function more than once! - + Always gets updates. .. deprecated:: 4.1.1 Use :meth:`infinity_polling` instead. .. note:: - + Install watchdog and psutil before using restart_on_change option. :param interval: Delay between two update retrivals @@ -1153,7 +1153,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :param skip_pending: skip old updates :type skip_pending: :obj:`bool` - + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :type long_polling_timeout: :obj:`int` @@ -1162,12 +1162,12 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :type logger_level: :obj:`int` :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - - Please note that this parameter doesn't affect updates created before the call to the get_updates, + + 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. :type allowed_updates: :obj:`list` of :obj:`str` @@ -1179,7 +1179,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :param path_to_watch: Path to watch for changes. Defaults to None :type path_to_watch: :obj:`str` - + :return: """ if none_stop is not None: @@ -1193,7 +1193,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F self._setup_change_detector(path_to_watch) logger.info('Starting your bot with username: [@%s]', self.user.username) - + if self.threaded: self.__threaded_polling(non_stop=non_stop, interval=interval, timeout=timeout, long_polling_timeout=long_polling_timeout, logger_level=logger_level, allowed_updates=allowed_updates) @@ -1386,9 +1386,9 @@ def get_me(self) -> types.User: def get_file(self, file_id: Optional[str]) -> types.File: """ Use this method to get basic info about a file and prepare it for downloading. - For the moment, bots can download files of up to 20MB in size. - On success, a File object is returned. - It is guaranteed that the link will be valid for at least 1 hour. + For the moment, bots can download files of up to 20MB in size. + On success, a File object is returned. + It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling get_file again. Telegram documentation: https://core.telegram.org/bots/api#getfile @@ -1431,11 +1431,11 @@ def download_file(self, file_path: str) -> bytes: def log_out(self) -> bool: """ - Use this method to log out from the cloud Bot API server before launching the bot locally. + Use this method to log out from the cloud Bot API server before launching the bot locally. You MUST log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. - After a successful call, you can immediately log in on a local server, - but will not be able to log in back to the cloud Bot API server for 10 minutes. + After a successful call, you can immediately log in on a local server, + but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#logout @@ -1444,14 +1444,14 @@ def log_out(self) -> bool: :rtype: :obj:`bool` """ return apihelper.log_out(self.token) - - + + def close(self) -> bool: """ - Use this method to close the bot instance before moving it from one local server to another. + Use this method to close the bot instance before moving it from one local server to another. You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. - The method will return error 429 in the first 10 minutes after the bot is launched. + The method will return error 429 in the first 10 minutes after the bot is launched. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#close @@ -1459,10 +1459,10 @@ def close(self) -> bool: :return: :obj:`bool` """ return apihelper.close(self.token) - + def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reaction: Optional[List[types.ReactionType]]=None, is_big: Optional[bool]=None) -> bool: """ - Use this method to change the chosen reactions on a message. + Use this method to change the chosen reactions on a message. Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same available reactions as messages in the channel. Returns True on success. @@ -1487,7 +1487,7 @@ def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reacti self.token, chat_id, message_id, reaction = reaction, is_big = is_big) - def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, + def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ Use this method to get a list of profile pictures for a user. @@ -1531,7 +1531,7 @@ def set_user_emoji_status(self, user_id: int, emoji_status_custom_emoji_id: Opti """ return apihelper.set_user_emoji_status( self.token, user_id, emoji_status_custom_emoji_id=emoji_status_custom_emoji_id, emoji_status_expiration_date=emoji_status_expiration_date) - + def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ @@ -1571,7 +1571,7 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. - Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators + Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) @@ -1623,7 +1623,7 @@ def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1643,7 +1643,7 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1658,7 +1658,7 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. - + Telegram documentation: https://core.telegram.org/bots/api#getchatmember :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1676,11 +1676,11 @@ def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatM def send_message( - self, chat_id: Union[int, str], text: str, - parse_mode: Optional[str]=None, + self, chat_id: Union[int, str], text: str, + parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -1696,7 +1696,7 @@ def send_message( Use this method to send text messages. Warning: Do not send more than about 4096 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 4096 characters, + If you must send more than 4096 characters, use the `split_string` or `smart_split` function in util.py. Telegram documentation: https://core.telegram.org/bots/api#sendmessage @@ -1806,7 +1806,7 @@ def send_message( def forward_message( - self, chat_id: Union[int, str], from_chat_id: Union[int, str], + self, chat_id: Union[int, str], from_chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, timeout: Optional[int]=None, @@ -1855,13 +1855,13 @@ def forward_message( def copy_message( - self, chat_id: Union[int, str], - from_chat_id: Union[int, str], - message_id: int, - caption: Optional[str]=None, - parse_mode: Optional[str]=None, + self, chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -1933,11 +1933,11 @@ def copy_message( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` - + :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` """ - + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification parse_mode = self.parse_mode if (parse_mode is None) else parse_mode protect_content = self.protect_content if (protect_content is None) else protect_content @@ -1969,7 +1969,7 @@ def copy_message( video_start_timestamp=video_start_timestamp)) - def delete_message(self, chat_id: Union[int, str], message_id: int, + def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: """ Use this method to delete a message, including service messages, with the following limitations: @@ -1998,7 +1998,7 @@ def delete_message(self, chat_id: Union[int, str], message_id: int, """ return apihelper.delete_message(self.token, chat_id, message_id, timeout=timeout) - + def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ Use this method to delete multiple messages simultaneously. If some of the specified messages can't be found, they are skipped. @@ -2016,7 +2016,7 @@ def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ return apihelper.delete_messages(self.token, chat_id, message_ids) - + def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: @@ -2058,7 +2058,7 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in protect_content=protect_content) return [types.MessageID.de_json(message_id) for message_id in result] - + def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: @@ -2103,7 +2103,7 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], self.token, chat_id, from_chat_id, message_ids, disable_notification=disable_notification, message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption) return [types.MessageID.de_json(message_id) for message_id in result] - + def send_checklist( self, business_connection_id: str, chat_id: Union[int, str], checklist: types.InputChecklist, @@ -2147,7 +2147,7 @@ def send_checklist( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply @@ -2156,7 +2156,7 @@ def send_checklist( self.token, business_connection_id, chat_id, checklist, disable_notification=disable_notification, protect_content=protect_content, message_effect_id=message_effect_id, reply_parameters=reply_parameters, reply_markup=reply_markup)) - + def edit_message_checklist( self, business_connection_id: str, chat_id: Union[int, str], message_id: int, checklist: types.InputChecklist, @@ -2191,9 +2191,9 @@ def edit_message_checklist( def send_dice( self, chat_id: Union[int, str], - emoji: Optional[str]=None, disable_notification: Optional[bool]=None, + emoji: Optional[str]=None, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, @@ -2284,7 +2284,7 @@ def send_dice( def send_photo( - self, chat_id: Union[int, str], photo: Union[Any, str], + self, chat_id: Union[int, str], photo: Union[Any, str], caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, @@ -2304,7 +2304,7 @@ def send_photo( Use this method to send photos. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendphoto - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2363,7 +2363,7 @@ def send_photo( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` - + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2400,14 +2400,14 @@ def send_photo( def send_audio( - self, chat_id: Union[int, str], audio: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, + self, chat_id: Union[int, str], audio: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, performer: Optional[str]=None, title: Optional[str]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -2426,7 +2426,7 @@ def send_audio( For sending voice messages, use the send_voice method instead. Telegram documentation: https://core.telegram.org/bots/api#sendaudio - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2536,12 +2536,12 @@ def send_audio( def send_voice( - self, chat_id: Union[int, str], voice: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, + self, chat_id: Union[int, str], voice: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -2555,7 +2555,7 @@ def send_voice( 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 - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2648,11 +2648,11 @@ def send_voice( def send_document( self, chat_id: Union[int, str], document: Union[Any, str], reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - caption: Optional[str]=None, + caption: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -2669,7 +2669,7 @@ def send_document( Use this method to send general files. Telegram documentation: https://core.telegram.org/bots/api#senddocument - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2883,7 +2883,7 @@ def send_sticker( if data and (not sticker): logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data - + return types.Message.de_json( apihelper.send_data( self.token, chat_id, sticker, 'sticker', @@ -2895,15 +2895,15 @@ def send_sticker( def send_video( - self, chat_id: Union[int, str], video: Union[Any, str], + self, chat_id: Union[int, str], video: Union[Any, str], duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, - thumbnail: Optional[Union[Any, str]]=None, - caption: Optional[str]=None, - parse_mode: Optional[str]=None, + thumbnail: Optional[Union[Any, str]]=None, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - supports_streaming: Optional[bool]=None, + supports_streaming: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility @@ -2923,7 +2923,7 @@ def send_video( start_timestamp: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). - + Telegram documentation: https://core.telegram.org/bots/api#sendvideo :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -2953,7 +2953,7 @@ def send_video( :param start_timestamp: Start timestamp for the video in the message :type start_timestamp: :obj:`int` - + :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -3061,19 +3061,19 @@ def send_video( def send_animation( - self, chat_id: Union[int, str], animation: Union[Any, str], + self, chat_id: Union[int, str], animation: Union[Any, str], duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, - caption: Optional[str]=None, + caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, @@ -3086,7 +3086,7 @@ def send_animation( """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. - + Telegram documentation: https://core.telegram.org/bots/api#sendanimation :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -3104,7 +3104,7 @@ def send_animation( :param height: Animation height :type height: :obj:`int` - + :param thumbnail: 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, @@ -3206,13 +3206,13 @@ def send_animation( def send_video_note( - self, chat_id: Union[int, str], data: Union[Any, str], - duration: Optional[int]=None, + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, length: Optional[int]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, @@ -3227,10 +3227,10 @@ def send_video_note( Use this method to send video messages. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendvideonote - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - + :param data: Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. Sending video notes by a URL is currently unsupported :type data: :obj:`str` or :class:`telebot.types.InputFile` @@ -3261,7 +3261,7 @@ def send_video_note( :param thumbnail: 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 . + so you can pass “attach://” if the thumbnail was uploaded using multipart/form-data under . :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param protect_content: Protects the contents of the sent message from forwarding and saving @@ -3321,7 +3321,7 @@ def send_video_note( protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) - + def send_paid_media( self, chat_id: Union[int, str], star_count: int, media: List[types.InputPaidMedia], caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, @@ -3392,11 +3392,11 @@ def send_paid_media( def send_media_group( - self, chat_id: Union[int, str], + self, chat_id: Union[int, str], media: List[Union[ - types.InputMediaAudio, types.InputMediaDocument, + types.InputMediaAudio, types.InputMediaDocument, types.InputMediaPhoto, types.InputMediaVideo]], - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, @@ -3409,7 +3409,7 @@ def send_media_group( """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. - + Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -3487,17 +3487,17 @@ def send_media_group( def send_location( - self, chat_id: Union[int, str], - latitude: float, longitude: float, + self, chat_id: Union[int, str], + latitude: float, longitude: float, live_period: Optional[int]=None, - reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, @@ -3601,14 +3601,14 @@ def send_location( def edit_message_live_location( - self, latitude: float, longitude: float, - chat_id: Optional[Union[int, str]]=None, + self, latitude: float, longitude: float, + chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, live_period: Optional[int]=None, business_connection_id: Optional[str]=None @@ -3669,9 +3669,9 @@ def edit_message_live_location( def stop_message_live_location( - self, chat_id: Optional[Union[int, str]]=None, + self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: @@ -3680,7 +3680,7 @@ def stop_message_live_location( On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. Telegram documentation: https://core.telegram.org/bots/api#stopmessagelivelocation - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -3711,14 +3711,14 @@ def stop_message_live_location( def send_venue( - self, chat_id: Union[int, str], - latitude: Optional[float], longitude: Optional[float], - title: str, address: str, - foursquare_id: Optional[str]=None, + self, chat_id: Union[int, str], + latitude: Optional[float], longitude: Optional[float], + title: str, address: str, + foursquare_id: Optional[str]=None, foursquare_type: Optional[str]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility google_place_id: Optional[str]=None, @@ -3731,12 +3731,12 @@ def send_venue( allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. - + Telegram documentation: https://core.telegram.org/bots/api#sendvenue :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`int` or :obj:`str` - + :param latitude: Latitude of the venue :type latitude: :obj:`float` @@ -3833,12 +3833,12 @@ def send_venue( def send_contact( - self, chat_id: Union[int, str], phone_number: str, - first_name: str, last_name: Optional[str]=None, + self, chat_id: Union[int, str], phone_number: str, + first_name: str, last_name: Optional[str]=None, vcard: Optional[str]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, @@ -3950,7 +3950,7 @@ def send_chat_action( :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`int` or :obj:`str` - + :param action: Type of action to broadcast. Choose one, depending on what the user is about to receive: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_voice or upload_voice for voice notes, upload_document for general files, @@ -3972,11 +3972,11 @@ def send_chat_action( return apihelper.send_chat_action( self.token, chat_id, action, timeout=timeout, message_thread_id=message_thread_id, business_connection_id=business_connection_id) - + @util.deprecated(deprecation_text="Use ban_chat_member instead") def kick_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date:Optional[Union[int, datetime]]=None, + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ This function is deprecated. Use `ban_chat_member` instead. @@ -3986,13 +3986,13 @@ def kick_chat_member( def ban_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date: Optional[Union[int, datetime]]=None, + self, chat_id: Union[int, str], user_id: int, + until_date: Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ - Use this method to ban a user in a group, a supergroup or a channel. - In the case of supergroups and channels, the user will not be able to return to the chat on their - own using invite links, etc., unless unbanned first. + Use this method to ban a user in a group, a supergroup or a channel. + In the case of supergroups and channels, the user will not be able to return to the chat on their + own using invite links, etc., unless unbanned first. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#banchatmember @@ -4009,10 +4009,10 @@ def ban_chat_member( :type until_date: :obj:`int` or :obj:`datetime` :param revoke_messages: Pass True to delete all messages from the chat for the user that is being removed. - If False, the user will be able to see messages in the group that were sent before the user was removed. + If False, the user will be able to see messages in the group that were sent before the user was removed. Always True for supergroups and channels. :type revoke_messages: :obj:`bool` - + :return: Returns True on success. :rtype: :obj:`bool` """ @@ -4021,7 +4021,7 @@ def ban_chat_member( def unban_chat_member( - self, chat_id: Union[int, str], user_id: int, + self, chat_id: Union[int, str], user_id: int, only_if_banned: Optional[bool]=False) -> bool: """ Use this method to unban a previously kicked user in a supergroup or channel. @@ -4049,15 +4049,15 @@ def unban_chat_member( def restrict_chat_member( - self, chat_id: Union[int, str], user_id: int, + self, chat_id: Union[int, str], user_id: int, until_date: Optional[Union[int, datetime]]=None, - can_send_messages: Optional[bool]=None, + can_send_messages: Optional[bool]=None, can_send_media_messages: Optional[bool]=None, - can_send_polls: Optional[bool]=None, + can_send_polls: Optional[bool]=None, can_send_other_messages: Optional[bool]=None, - can_add_web_page_previews: Optional[bool]=None, + can_add_web_page_previews: Optional[bool]=None, can_change_info: Optional[bool]=None, - can_invite_users: Optional[bool]=None, + can_invite_users: Optional[bool]=None, can_pin_messages: Optional[bool]=None, permissions: Optional[types.ChatPermissions]=None, use_independent_chat_permissions: Optional[bool]=None) -> bool: @@ -4085,10 +4085,10 @@ def restrict_chat_member( :param can_send_messages: deprecated :type can_send_messages: :obj:`bool` - + :param can_send_media_messages: deprecated :type can_send_media_messages: :obj:`bool` - + :param can_send_polls: deprecated :type can_send_polls: :obj:`bool` @@ -4139,16 +4139,16 @@ def restrict_chat_member( def promote_chat_member( self, chat_id: Union[int, str], user_id: int, - can_change_info: Optional[bool]=None, + can_change_info: Optional[bool]=None, can_post_messages: Optional[bool]=None, - can_edit_messages: Optional[bool]=None, - can_delete_messages: Optional[bool]=None, + can_edit_messages: Optional[bool]=None, + can_delete_messages: Optional[bool]=None, can_invite_users: Optional[bool]=None, - can_restrict_members: Optional[bool]=None, - can_pin_messages: Optional[bool]=None, + can_restrict_members: Optional[bool]=None, + can_pin_messages: Optional[bool]=None, can_promote_members: Optional[bool]=None, - is_anonymous: Optional[bool]=None, - can_manage_chat: Optional[bool]=None, + is_anonymous: Optional[bool]=None, + can_manage_chat: Optional[bool]=None, can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None, can_manage_topics: Optional[bool]=None, @@ -4198,9 +4198,9 @@ def promote_chat_member( :param is_anonymous: Pass True, if the administrator's presence in the chat is hidden :type is_anonymous: :obj:`bool` - :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, - message statistics in channels, see channel members, - see anonymous administrators in supergroups and ignore slow mode. + :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, + message statistics in channels, see channel members, + see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege :type can_manage_chat: :obj:`bool` @@ -4267,14 +4267,14 @@ def set_chat_administrator_custom_title( """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - + def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ Use this method to ban a channel chat in a supergroup or a channel. - The owner of the chat will not be able to send messages and join live - streams on behalf of the chat, unless it is unbanned first. - The bot must be an administrator in the supergroup or channel - for this to work and must have the appropriate administrator rights. + The owner of the chat will not be able to send messages and join live + streams on behalf of the chat, unless it is unbanned first. + The bot must be an administrator in the supergroup or channel + for this to work and must have the appropriate administrator rights. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#banchatsenderchat @@ -4293,8 +4293,8 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ - Use this method to unban a previously banned channel chat in a supergroup or channel. - The bot must be an administrator for this to work and must have the appropriate + Use this method to unban a previously banned channel chat in a supergroup or channel. + The bot must be an administrator for this to work and must have the appropriate administrator rights. Returns True on success. @@ -4345,7 +4345,7 @@ def set_chat_permissions( def create_chat_invite_link( self, chat_id: Union[int, str], name: Optional[str]=None, - expire_date: Optional[Union[int, datetime]]=None, + expire_date: Optional[Union[int, datetime]]=None, member_limit: Optional[int]=None, creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ @@ -4432,7 +4432,7 @@ def create_chat_subscription_invite_link( :param chat_id: Unique identifier for the target channel chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - + :param name: Invite link name; 0-32 characters :type name: :obj:`str` @@ -4450,7 +4450,7 @@ def create_chat_subscription_invite_link( return types.ChatInviteLink.de_json( apihelper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name=name) ) - + def edit_chat_subscription_invite_link( self, chat_id: Union[int, str], invite_link: str, name: Optional[str]=None) -> types.ChatInviteLink: """ @@ -4480,7 +4480,7 @@ def revoke_chat_invite_link( self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: """ Use this method to revoke an invite link created by the bot. - Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator + Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink @@ -4519,7 +4519,7 @@ def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ - Use this method to approve a chat join request. + Use this method to approve a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. @@ -4540,7 +4540,7 @@ def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ - Use this method to decline a chat join request. + Use this method to decline a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. @@ -4599,21 +4599,21 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ return apihelper.delete_chat_photo(self.token, chat_id) - - def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, + + def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> List[types.BotCommand]: """ - Use this method to get the current list of the bot's commands. + Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. Telegram documentation: https://core.telegram.org/bots/api#getmycommands - :param scope: The scope of users for which the commands are relevant. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -4641,7 +4641,7 @@ def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=Non """ return apihelper.set_my_name(self.token, name=name, language_code=language_code) - + def get_my_name(self, language_code: Optional[str]=None): """ Use this method to get the current bot name for the given user language. @@ -4680,7 +4680,7 @@ def set_my_description(self, description: Optional[str]=None, language_code: Opt return apihelper.set_my_description( self.token, description=description, language_code=language_code) - + def get_my_description(self, language_code: Optional[str]=None): """ Use this method to get the current bot description for the given user language. @@ -4696,11 +4696,11 @@ def get_my_description(self, language_code: Optional[str]=None): return types.BotDescription.de_json( apihelper.get_my_description(self.token, language_code=language_code)) - + def set_my_short_description(self, short_description:Optional[str]=None, language_code:Optional[str]=None): """ Use this method to change the bot's short description, which is shown on the bot's profile page and - is sent together with the link when users share the bot. + is sent together with the link when users share the bot. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setmyshortdescription @@ -4717,7 +4717,7 @@ def set_my_short_description(self, short_description:Optional[str]=None, languag return apihelper.set_my_short_description( self.token, short_description=short_description, language_code=language_code) - + def get_my_short_description(self, language_code: Optional[str]=None): """ Use this method to get the current bot short description for the given user language. @@ -4736,13 +4736,13 @@ def get_my_short_description(self, language_code: Optional[str]=None): def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: """ - Use this method to change the bot's menu button in a private chat, - or the default menu button. + Use this method to change the bot's menu button in a private chat, + or the default menu button. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton - :param chat_id: Unique identifier for the target private chat. + :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be changed. :type chat_id: :obj:`int` or :obj:`str` @@ -4774,13 +4774,13 @@ def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButto apihelper.get_chat_menu_button(self.token, chat_id=chat_id)) - def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, + def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, for_channels: Optional[bool]=None) -> bool: """ Use this method to change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. These rights will be suggested to users, but they are are free to modify - the list before adding the bot. + the list before adding the bot. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights @@ -4834,8 +4834,8 @@ def get_business_connection(self, business_connection_id: str) -> types.Business return types.BusinessConnection.de_json( apihelper.get_business_connection(self.token, business_connection_id) ) - - + + def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: @@ -4847,12 +4847,12 @@ def set_my_commands(self, commands: List[types.BotCommand], :param commands: List of BotCommand. At most 100 commands can be specified. :type commands: :obj:`list` of :class:`telebot.types.BotCommand` - :param scope: The scope of users for which the commands are relevant. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -4861,22 +4861,22 @@ def set_my_commands(self, commands: List[types.BotCommand], """ return apihelper.set_my_commands(self.token, commands, scope=scope, language_code=language_code) - - def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, + + def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: """ - Use this method to delete the list of the bot's commands for the given scope and user language. - After deletion, higher level commands will be shown to affected users. + Use this method to delete the list of the bot's commands for the given scope and user language. + After deletion, higher level commands will be shown to affected users. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemycommands - - :param scope: The scope of users for which the commands are relevant. + + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -4930,7 +4930,7 @@ def set_chat_description(self, chat_id: Union[int, str], description: Optional[s def pin_chat_message( - self, chat_id: Union[int, str], message_id: int, + self, chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=False, business_connection_id: Optional[str]=None) -> bool: """ Use this method to pin a message in a supergroup. @@ -5005,10 +5005,10 @@ def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: def edit_message_text( - self, text: str, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + self, text: str, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility @@ -5058,7 +5058,7 @@ def edit_message_text( :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - + if disable_web_page_preview is not None: # show a deprecation warning logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") @@ -5089,9 +5089,9 @@ def edit_message_text( def edit_message_media( - self, media: Any, chat_id: Optional[Union[int, str]]=None, + self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: @@ -5100,7 +5100,7 @@ def edit_message_media( If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. When an inline message is edited, a new file can't be uploaded; use a previously uploaded file via its file_id or specify a URL. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. - Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. + Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia @@ -5109,7 +5109,7 @@ def edit_message_media( :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message :type message_id: :obj:`int` :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message @@ -5137,9 +5137,9 @@ def edit_message_media( def edit_message_reply_markup( - self, chat_id: Optional[Union[int, str]]=None, + self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: @@ -5179,10 +5179,10 @@ def edit_message_reply_markup( def send_game( - self, chat_id: Union[int, str], game_short_name: str, + self, chat_id: Union[int, str], game_short_name: str, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, @@ -5273,10 +5273,10 @@ def send_game( def set_game_score( - self, user_id: Union[int, str], score: int, - force: Optional[bool]=None, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + self, user_id: Union[int, str], score: int, + force: Optional[bool]=None, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None, disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: """ @@ -5319,7 +5319,7 @@ def set_game_score( def get_game_high_scores( self, user_id: int, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ Use this method to get data for high score tables. Will return the score of the specified user and several of @@ -5352,20 +5352,20 @@ def get_game_high_scores( def send_invoice( - self, chat_id: Union[int, str], title: str, description: str, - invoice_payload: str, provider_token: Union[str, None], currency: str, - prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, - photo_url: Optional[str]=None, photo_size: Optional[int]=None, + self, chat_id: Union[int, str], title: str, description: str, + invoice_payload: str, provider_token: Union[str, None], currency: str, + prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, + photo_url: Optional[str]=None, photo_size: Optional[int]=None, photo_width: Optional[int]=None, photo_height: Optional[int]=None, - need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, + need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, need_email: Optional[bool]=None, need_shipping_address: Optional[bool]=None, - send_phone_number_to_provider: Optional[bool]=None, - send_email_to_provider: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, is_flexible: Optional[bool]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - provider_data: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + provider_data: Optional[str]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility max_tip_amount: Optional[int] = None, @@ -5416,7 +5416,7 @@ def send_invoice( :param photo_size: Photo size in bytes :type photo_size: :obj:`int` - :param photo_width: Photo width + :param photo_width: Photo width :type photo_width: :obj:`int` :param photo_height: Photo height @@ -5530,9 +5530,9 @@ def send_invoice( ) def create_invoice_link(self, - title: str, description: str, payload:str, provider_token: Union[str, None], + title: str, description: str, payload:str, provider_token: Union[str, None], currency: str, prices: List[types.LabeledPrice], - max_tip_amount: Optional[int] = None, + max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, provider_data: Optional[str]=None, photo_url: Optional[str]=None, @@ -5548,9 +5548,9 @@ def create_invoice_link(self, is_flexible: Optional[bool]=None, subscription_period: Optional[int]=None, business_connection_id: Optional[str]=None) -> str: - + """ - Use this method to create a link for an invoice. + Use this method to create a link for an invoice. Returns the created invoice link as String on success. Telegram documentation: @@ -5649,17 +5649,17 @@ def create_invoice_link(self, # noinspection PyShadowingBuiltins def send_poll( self, chat_id: Union[int, str], question: str, options: List[Union[str, types.InputPollOption]], - is_anonymous: Optional[bool]=None, type: Optional[str]=None, - allows_multiple_answers: Optional[bool]=None, + is_anonymous: Optional[bool]=None, type: Optional[str]=None, + allows_multiple_answers: Optional[bool]=None, correct_option_id: Optional[int]=None, - explanation: Optional[str]=None, - explanation_parse_mode: Optional[str]=None, - open_period: Optional[int]=None, - close_date: Optional[Union[int, datetime]]=None, + explanation: Optional[str]=None, + explanation_parse_mode: Optional[str]=None, + open_period: Optional[int]=None, + close_date: Optional[Union[int, datetime]]=None, is_closed: Optional[bool]=None, disable_notification: Optional[bool]=False, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, @@ -5816,7 +5816,7 @@ def send_poll( def stop_poll( - self, chat_id: Union[int, str], message_id: int, + self, chat_id: Union[int, str], message_id: int, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None) -> types.Poll: """ @@ -5845,8 +5845,8 @@ def stop_poll( def answer_shipping_query( - self, shipping_query_id: str, ok: bool, - shipping_options: Optional[List[types.ShippingOption]]=None, + self, shipping_query_id: str, ok: bool, + shipping_options: Optional[List[types.ShippingOption]]=None, error_message: Optional[str]=None) -> bool: """ Asks for an answer to a shipping question. @@ -5874,7 +5874,7 @@ def answer_shipping_query( def answer_pre_checkout_query( - self, pre_checkout_query_id: str, ok: bool, + self, pre_checkout_query_id: str, ok: bool, error_message: Optional[str]=None) -> bool: """ Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the @@ -5886,7 +5886,7 @@ def answer_pre_checkout_query( Telegram documentation: https://core.telegram.org/bots/api#answerprecheckoutquery - :param pre_checkout_query_id: Unique identifier for the query to be answered + :param pre_checkout_query_id: Unique identifier for the query to be answered :type pre_checkout_query_id: :obj:`int` :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. @@ -5902,11 +5902,11 @@ def answer_pre_checkout_query( """ return apihelper.answer_pre_checkout_query( self.token, pre_checkout_query_id, ok, error_message=error_message) - + def get_my_star_balance(self) -> types.StarAmount: """ Returns the bot's current Telegram Stars balance. On success, returns a StarAmount object. - + Telegram documentation: https://core.telegram.org/bots/api#getmystarbalance :return: On success, returns a StarAmount object. @@ -5933,8 +5933,8 @@ def get_star_transactions(self, offset: Optional[int]=None, limit: Optional[int] return types.StarTransactions.de_json( apihelper.get_star_transactions(self.token, offset=offset, limit=limit) ) - - + + def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> bool: """ Refunds a successful payment in Telegram Stars. Returns True on success. @@ -5973,10 +5973,10 @@ def edit_user_star_subscription(self, user_id: int, telegram_payment_charge_id: return apihelper.edit_user_star_subscription(self.token, user_id, telegram_payment_charge_id, is_canceled) def edit_message_caption( - self, caption: str, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + self, caption: str, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - parse_mode: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, show_caption_above_media: Optional[bool]=None, @@ -6068,12 +6068,12 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message def answer_inline_query( - self, inline_query_id: str, - results: List[Any], - cache_time: Optional[int]=None, - is_personal: Optional[bool]=None, + self, inline_query_id: str, + results: List[Any], + cache_time: Optional[int]=None, + is_personal: Optional[bool]=None, next_offset: Optional[str]=None, - switch_pm_text: Optional[str]=None, + switch_pm_text: Optional[str]=None, switch_pm_parameter: Optional[str]=None, button: Optional[types.InlineQueryResultsButton]=None) -> bool: """ @@ -6120,7 +6120,7 @@ def answer_inline_query( if not button and (switch_pm_text or switch_pm_parameter): logger.warning("switch_pm_text and switch_pm_parameter are deprecated for answer_inline_query. Use button instead.") button = types.InlineQueryResultsButton(text=switch_pm_text, start_parameter=switch_pm_parameter) - + return apihelper.answer_inline_query( self.token, inline_query_id, results, cache_time=cache_time, is_personal=is_personal, next_offset=next_offset, button=button) @@ -6128,7 +6128,7 @@ def answer_inline_query( def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool: """ - Use this method to clear the list of pinned messages in a General forum topic. + Use this method to clear the list of pinned messages in a General forum topic. The bot must be an administrator in the chat for this to work and must have the can_pin_messages administrator right in the supergroup. Returns True on success. @@ -6145,8 +6145,8 @@ def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bo def answer_callback_query( - self, callback_query_id: int, - text: Optional[str]=None, show_alert: Optional[bool]=None, + self, callback_query_id: int, + text: Optional[str]=None, show_alert: Optional[bool]=None, url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to @@ -6176,7 +6176,7 @@ def answer_callback_query( return apihelper.answer_callback_query( self.token, callback_query_id, text=text, show_alert=show_alert, url=url, cache_time=cache_time) - + def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types.UserChatBoosts: """ Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. @@ -6199,7 +6199,7 @@ def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types. # 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. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumbnail @@ -6230,11 +6230,11 @@ def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[An return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) - + @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]=None): """ - Use this method to set the thumbnail of a sticker set. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb @@ -6258,7 +6258,7 @@ def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]= def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. - + Telegram documentation: https://core.telegram.org/bots/api#getstickerset :param name: Sticker set name @@ -6285,7 +6285,7 @@ def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.S result = apihelper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] - + def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: """ Use this method to change search keywords assigned to a regular or custom emoji sticker. @@ -6303,7 +6303,7 @@ def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: """ return apihelper.set_sticker_keywords(self.token, sticker, keywords=keywords) - + def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosition=None) -> bool: """ Use this method to change the mask position of a mask sticker. @@ -6320,7 +6320,7 @@ def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosit :rtype: :obj:`bool` """ return apihelper.set_sticker_mask_position(self.token, sticker, mask_position=mask_position) - + def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: """ @@ -6338,7 +6338,7 @@ def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Opt """ return apihelper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id=custom_emoji_id) - + def set_sticker_set_title(self, name: str, title: str) -> bool: """ Use this method to set the title of a created sticker set. @@ -6355,7 +6355,7 @@ def set_sticker_set_title(self, name: str, title: str) -> bool: """ return apihelper.set_sticker_set_title(self.token, name, title) - + def delete_sticker_set(self, name:str) -> bool: """ Use this method to delete a sticker set. Returns True on success. @@ -6368,7 +6368,7 @@ def delete_sticker_set(self, name:str) -> bool: """ return apihelper.delete_sticker_set(self.token, name) - def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, + def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None, chat_id: Optional[Union[str, int]] = None) -> bool: """ @@ -6403,13 +6403,13 @@ def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None """ if user_id is None and chat_id is None: raise ValueError("Either user_id or chat_id must be specified.") - + if gift_id is None: raise ValueError("gift_id must be specified.") - + return apihelper.send_gift(self.token, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities, pay_for_upgrade=pay_for_upgrade, chat_id=chat_id, user_id=user_id) - + def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool: """ Verifies a user on behalf of the organization which is represented by the bot. Returns True on success. @@ -6585,12 +6585,12 @@ def set_business_account_gift_settings( :rtype: :obj:`bool` """ return apihelper.set_business_account_gift_settings(self.token, business_connection_id, show_gift_button, accepted_gift_types) - + def get_business_account_star_balance(self, business_connection_id: str) -> types.StarAmount: """ Returns the amount of Telegram Stars owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns StarAmount on success. - + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountstarbalance :param business_connection_id: Unique identifier of the business connection @@ -6602,7 +6602,7 @@ def get_business_account_star_balance(self, business_connection_id: str) -> type return types.StarAmount.de_json( apihelper.get_business_account_star_balance(self.token, business_connection_id) ) - + def transfer_business_account_stars(self, business_connection_id: str, star_count: int) -> bool: """ Transfers Telegram Stars from the business account balance to the bot's balance. Requires the can_transfer_stars business bot right. Returns True on success. @@ -6619,7 +6619,7 @@ def transfer_business_account_stars(self, business_connection_id: str, star_coun :rtype: :obj:`bool` """ return apihelper.transfer_business_account_stars(self.token, business_connection_id, star_count) - + def get_business_account_gifts( self, business_connection_id: str, exclude_unsaved: Optional[bool]=None, @@ -6632,7 +6632,7 @@ def get_business_account_gifts( limit: Optional[int]=None) -> types.OwnedGifts: """ Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. - + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountgifts :param business_connection_id: Unique identifier of the business connection @@ -6695,7 +6695,7 @@ def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) :rtype: :obj:`bool` """ return apihelper.convert_gift_to_stars(self.token, business_connection_id, owned_gift_id) - + def upgrade_gift( self, business_connection_id: str, owned_gift_id: str, keep_original_details: Optional[bool]=None, @@ -6759,7 +6759,7 @@ def transfer_gift( new_owner_chat_id, star_count=star_count ) - + def post_story( self, business_connection_id: str, content: types.InputStoryContent, active_period: int, caption: Optional[str]=None, @@ -6866,7 +6866,7 @@ def edit_story( def delete_story(self, business_connection_id: str, story_id: int) -> bool: """ Deletes a story previously posted by the bot on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletestory :param business_connection_id: Unique identifier of the business connection @@ -6915,7 +6915,7 @@ def gift_premium_subscription( text=text, text_parse_mode=text_parse_mode, text_entities=text_entities ) - + def set_business_account_profile_photo( self, business_connection_id: str, photo: types.InputProfilePhoto, is_public: Optional[bool]=None) -> bool: @@ -6994,8 +6994,8 @@ def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, stic :rtype: :obj:`bool` """ return apihelper.replace_sticker_in_set(self.token, user_id, name, old_sticker, sticker) - - + + def set_sticker_emoji_list(self, sticker: str, emoji_list: List[str]) -> bool: """ Use this method to set the emoji list of a custom emoji sticker set. @@ -7017,7 +7017,7 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. - + Telegram documentation: https://core.telegram.org/bots/api#uploadstickerfile :param user_id: User identifier of sticker set owner @@ -7031,7 +7031,7 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s See https://core.telegram.org/stickers for technical requirements. More information on Sending Files » :type sticker: :class:`telebot.types.InputFile` - :param sticker_format: One of "static", "animated", "video". + :param sticker_format: One of "static", "animated", "video". :type sticker_format: :obj:`str` :return: On success, the sent file is returned. @@ -7041,17 +7041,17 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s logger.warning('The parameter "png_sticker" is deprecated. Use "sticker" instead.') sticker = png_sticker sticker_format = "static" - + return types.File.de_json( apihelper.upload_sticker_file(self.token, user_id, sticker, sticker_format) ) def create_new_sticker_set( - self, user_id: int, name: str, title: str, - emojis: Optional[List[str]]=None, - png_sticker: Union[Any, str]=None, - tgs_sticker: Union[Any, str]=None, + self, user_id: int, name: str, title: str, + emojis: Optional[List[str]]=None, + png_sticker: Union[Any, str]=None, + tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, sticker_type: Optional[str]=None, @@ -7060,7 +7060,7 @@ def create_new_sticker_set( stickers: List[types.InputSticker]=None, sticker_format: Optional[str]=None) -> bool: """ - Use this method to create new sticker set owned by a user. + Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success. @@ -7124,7 +7124,7 @@ def create_new_sticker_set( sticker_format = 'video' elif png_sticker: sticker_format = 'static' - + if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') if sticker_type is None: @@ -7145,8 +7145,8 @@ def create_new_sticker_set( def add_sticker_to_set( self, user_id: int, name: str, emojis: Union[List[str], str], - png_sticker: Optional[Union[Any, str]]=None, - tgs_sticker: Optional[Union[Any, str]]=None, + png_sticker: Optional[Union[Any, str]]=None, + tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, mask_position: Optional[types.MaskPosition]=None, sticker: Optional[types.InputSticker]=None) -> bool: @@ -7210,7 +7210,7 @@ def add_sticker_to_set( def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#setstickerpositioninset :param sticker: File identifier of the sticker @@ -7228,7 +7228,7 @@ def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletestickerfromset :param sticker: File identifier of the sticker @@ -7388,9 +7388,9 @@ def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool: Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#editgeneralforumtopic - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -7473,7 +7473,7 @@ def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryR """ Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which - the query originated. + the query originated. On success, a SentWebAppMessage object is returned. Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery @@ -7541,7 +7541,7 @@ def register_for_reply(self, message: types.Message, callback: Callable, *args, :param args: Optional arguments for the callback function. :param kwargs: Optional keyword arguments for the callback function. - + :return: None """ self.register_for_reply_by_message_id(message.message_id, callback, *args, **kwargs) @@ -7644,8 +7644,8 @@ def set_state(self, user_id: int, state: Union[str, State], chat_id: Optional[in Added additional parameters to support topics, business connections, and message threads. - .. seealso:: - + .. seealso:: + For more details, visit the `custom_states.py example `_. :param user_id: User's identifier @@ -7677,7 +7677,7 @@ def set_state(self, user_id: int, state: Union[str, State], chat_id: Optional[in bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def reset_data(self, user_id: int, chat_id: Optional[int]=None, + def reset_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ @@ -7720,7 +7720,7 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_conne :param user_id: User's identifier :type user_id: :obj:`int` - + :param chat_id: Chat's identifier :type chat_id: :obj:`int` @@ -7775,7 +7775,7 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def get_state(self, user_id: int, chat_id: Optional[int]=None, + def get_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> str: """ @@ -7813,9 +7813,9 @@ def get_state(self, user_id: int, chat_id: Optional[int]=None, bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def add_data(self, user_id: int, chat_id: Optional[int]=None, + def add_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None, **kwargs) -> None: """ @@ -7862,7 +7862,7 @@ def register_next_step_handler_by_chat_id( :type callback: :obj:`Callable[[telebot.types.Message], None]` :param args: Args to pass in callback func - + :param kwargs: Args to pass in callback func :return: None @@ -8032,7 +8032,7 @@ def register_middleware_handler(self, callback, update_types=None): bot = TeleBot('TOKEN') bot.register_middleware_handler(print_channel_post_text, update_types=['channel_post', 'edited_channel_post']) - + :param callback: Function that will be used as a middleware handler. :type callback: :obj:`function` @@ -8212,7 +8212,7 @@ def register_message_handler(self, callback: Callable, content_types: Optional[L logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") content_types = [content_types] - + handler_dict = self._build_handler_dict(callback, chat_types=chat_types, @@ -8284,7 +8284,7 @@ def add_edited_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_message_handler to add edited_message_handler to the bot. - + :meta private: :param handler_dict: @@ -8406,7 +8406,7 @@ def add_channel_post_handler(self, handler_dict): Note that you should use register_channel_post_handler to add channel_post_handler to the bot. :meta private: - + :param handler_dict: :return: """ @@ -8626,7 +8626,7 @@ def register_message_reaction_handler(self, callback: Callable, func: Callable=N :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -8654,7 +8654,7 @@ def decorator(handler): return decorator - + def add_message_reaction_count_handler(self, handler_dict): """ Adds message reaction count handler @@ -8680,7 +8680,7 @@ def register_message_reaction_count_handler(self, callback: Callable, func: Call :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -8689,7 +8689,7 @@ def register_message_reaction_count_handler(self, callback: Callable, func: Call self.add_message_reaction_count_handler(handler_dict) - def inline_handler(self, func, **kwargs): + def inline_handler(self, func=None, **kwargs): """ Handles new incoming inline query. As a parameter to the decorator function, it passes :class:`telebot.types.InlineQuery` object. @@ -8751,7 +8751,7 @@ def chosen_inline_handler(self, func, **kwargs): :param func: Function executed as a filter :type func: :obj:`function` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -8807,7 +8807,7 @@ def callback_query_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -8861,7 +8861,7 @@ def shipping_query_handler(self, func, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -8938,7 +8938,7 @@ def add_pre_checkout_query_handler(self, handler_dict): """ self.pre_checkout_query_handlers.append(handler_dict) - + def register_pre_checkout_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers pre-checkout request handler. @@ -8973,9 +8973,9 @@ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_purchased_paid_media_handler(handler_dict) return handler - + return decorator - + def add_purchased_paid_media_handler(self, handler_dict): """ Adds a purchased paid media handler @@ -8994,7 +8994,7 @@ def register_purchased_paid_media_handler(self, callback: Callable, func: Callab :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -9071,7 +9071,7 @@ def poll_answer_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -9238,7 +9238,7 @@ def chat_join_request_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -9286,7 +9286,7 @@ def register_chat_join_request_handler( def chat_boost_handler(self, func=None, **kwargs): """ - Handles new incoming chat boost state. + Handles new incoming chat boost state. it passes :class:`telebot.types.ChatBoostUpdated` object. :param func: Function executed as a filter @@ -9323,7 +9323,7 @@ def register_chat_boost_handler( :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -9339,7 +9339,7 @@ def register_chat_boost_handler( def removed_chat_boost_handler(self, func=None, **kwargs): """ - Handles new incoming chat boost state. + Handles new incoming chat boost state. it passes :class:`telebot.types.ChatBoostRemoved` object. :param func: Function executed as a filter @@ -9355,7 +9355,7 @@ def decorator(handler): return decorator - + def add_removed_chat_boost_handler(self, handler_dict): """ Adds a removed_chat_boost handler. @@ -9376,7 +9376,7 @@ def register_removed_chat_boost_handler( :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -9404,7 +9404,7 @@ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_business_connection_handler(handler_dict) return handler - + return decorator @@ -9572,7 +9572,7 @@ def register_business_message_handler(self, :return: None """ - handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, pass_bot=pass_bot, **kwargs) self.add_business_message_handler(handler_dict) @@ -9632,7 +9632,7 @@ def add_edited_business_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_business_message_handler to add edited_business_message_handler to the bot. - + :meta private: :param handler_dict: @@ -9709,7 +9709,7 @@ def decorator(handler): self.add_deleted_business_messages_handler(handler_dict) return handler - + return decorator @@ -9829,7 +9829,7 @@ def _get_middlewares(self, update_type): :return: """ middlewares = None - if self.middlewares: + if self.middlewares: middlewares = [i for i in self.middlewares if update_type in i.update_types] return middlewares From d113d0205e7c8250f9e9da6deca7840e8e10237a Mon Sep 17 00:00:00 2001 From: All-The-Foxes <116322192+All-The-Foxes@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:47:02 -0400 Subject: [PATCH 1801/1808] fix line break, fix poll option IDs are not optional --- telebot/types.py | 1189 +++++++++++++++++++++++++--------------------- 1 file changed, 641 insertions(+), 548 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 971f5824c..27d6c3f22 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -95,7 +95,7 @@ def check_json(json_type, dict_copy = True): If it is not, it is converted to a dict by means of json.loads(json_type) :meta private: - + :param json_type: input json or parsed dict :param dict_copy: if dict is passed and it is changed outside - should be True! :return: Dictionary parsed from json or original dict @@ -121,9 +121,9 @@ class Update(JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#update - :param update_id: The update's unique identifier. Update identifiers start from a certain positive number and - increase sequentially. This ID becomes especially handy if you're using webhooks, since it allows you to ignore - repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates + :param update_id: The update's unique identifier. Update identifiers start from a certain positive number and + increase sequentially. This ID becomes especially handy if you're using webhooks, since it allows you to ignore + repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially. :type update_id: :obj:`int` @@ -150,8 +150,8 @@ class Update(JsonDeserializable): :param inline_query: Optional. New incoming inline query :type inline_query: :class:`telebot.types.InlineQuery` - :param chosen_inline_result: Optional. The result of an inline query that was chosen by a user and sent to their chat - partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your + :param chosen_inline_result: Optional. The result of an inline query that was chosen by a user and sent to their chat + partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot. :type chosen_inline_result: :class:`telebot.types.ChosenInlineResult` @@ -161,30 +161,30 @@ class Update(JsonDeserializable): :param shipping_query: Optional. New incoming shipping query. Only for invoices with flexible price :type shipping_query: :class:`telebot.types.ShippingQuery` - :param pre_checkout_query: Optional. New incoming pre-checkout query. Contains full information about + :param pre_checkout_query: Optional. New incoming pre-checkout query. Contains full information about checkout :type pre_checkout_query: :class:`telebot.types.PreCheckoutQuery` :purchased_paid_media: Optional. A user purchased paid media with a non-empty payload sent by the bot in a non-channel chat :type purchased_paid_media: :class:`telebot.types.PaidMediaPurchased` - :param poll: Optional. New poll state. Bots receive only updates about stopped polls and polls, which are sent by the + :param poll: Optional. New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot :type poll: :class:`telebot.types.Poll` - :param poll_answer: Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in + :param poll_answer: Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself. :type poll_answer: :class:`telebot.types.PollAnswer` - :param my_chat_member: Optional. The bot's chat member status was updated in a chat. For private chats, this update + :param my_chat_member: Optional. The bot's chat member status was updated in a chat. For private chats, this update is received only when the bot is blocked or unblocked by the user. :type my_chat_member: :class:`telebot.types.ChatMemberUpdated` - :param chat_member: Optional. A chat member's status was updated in a chat. The bot must be an administrator in the + :param chat_member: Optional. A chat member's status was updated in a chat. The bot must be an administrator in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. :type chat_member: :class:`telebot.types.ChatMemberUpdated` - :param chat_join_request: Optional. A request to join the chat has been sent. The bot must have the + :param chat_join_request: Optional. A request to join the chat has been sent. The bot must have the can_invite_users administrator right in the chat to receive these updates. :type chat_join_request: :class:`telebot.types.ChatJoinRequest` @@ -276,7 +276,6 @@ def __init__(self, update_id, message, edited_message, channel_post, edited_chan self.purchased_paid_media: Optional[PaidMediaPurchased] = purchased_paid_media - class ChatMemberUpdated(JsonDeserializable): """ This object represents changes in the status of a chat member. @@ -298,7 +297,7 @@ class ChatMemberUpdated(JsonDeserializable): :param new_chat_member: New information about the chat member :type new_chat_member: :class:`telebot.types.ChatMember` - :param invite_link: Optional. Chat invite link, which was used by the user to join the chat; for joining by invite + :param invite_link: Optional. Chat invite link, which was used by the user to join the chat; for joining by invite link events only. :type invite_link: :class:`telebot.types.ChatInviteLink` @@ -321,7 +320,7 @@ def de_json(cls, json_string): obj['new_chat_member'] = ChatMember.de_json(obj['new_chat_member']) 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_join_request=None, via_chat_folder_invite_link=None, **kwargs): @@ -353,7 +352,7 @@ def difference(self) -> Dict[str, List]: if new[key] != old[key]: dif[key] = [old[key], new[key]] return dif - + class ChatJoinRequest(JsonDeserializable): """ @@ -394,7 +393,7 @@ def de_json(cls, json_string): obj['from_user'] = User.de_json(obj['from']) obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link')) return cls(**obj) - + def __init__(self, chat, from_user, user_chat_id, date, bio=None, invite_link=None, **kwargs): self.chat: Chat = chat self.from_user: User = from_user @@ -403,6 +402,7 @@ def __init__(self, chat, from_user, user_chat_id, date, bio=None, invite_link=No self.invite_link: Optional[ChatInviteLink] = invite_link self.user_chat_id: int = user_chat_id + class WebhookInfo(JsonDeserializable): """ Describes the current status of a webhook. @@ -421,23 +421,23 @@ class WebhookInfo(JsonDeserializable): :param ip_address: Optional. Currently used webhook IP address :type ip_address: :obj:`str` - :param last_error_date: Optional. Unix time for the most recent error that happened when trying to deliver an + :param last_error_date: Optional. Unix time for the most recent error that happened when trying to deliver an update via webhook :type last_error_date: :obj:`int` - :param last_error_message: Optional. Error message in human-readable format for the most recent error that + :param last_error_message: Optional. Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook :type last_error_message: :obj:`str` - :param last_synchronization_error_date: Optional. Unix time of the most recent error that happened when trying + :param last_synchronization_error_date: Optional. Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters :type last_synchronization_error_date: :obj:`int` - :param max_connections: Optional. The maximum allowed number of simultaneous HTTPS connections to the webhook + :param max_connections: Optional. The maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery :type max_connections: :obj:`int` - :param allowed_updates: Optional. A list of update types the bot is subscribed to. Defaults to all update types + :param allowed_updates: Optional. A list of update types the bot is subscribed to. Defaults to all update types except chat_member :type allowed_updates: :obj:`list` of :obj:`str` @@ -450,7 +450,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string, dict_copy=False) return cls(**obj) - def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, + def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, last_error_date=None, last_error_message=None, last_synchronization_error_date=None, max_connections=None, allowed_updates=None, **kwargs): self.url: str = url @@ -470,8 +470,8 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#user - :param id: Unique identifier for this user or bot. This number may have more than 32 significant bits and some - programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant + :param id: Unique identifier for this user or bot. This number may have more than 32 significant bits and some + programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. :type id: :obj:`int` @@ -499,7 +499,7 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): :param can_join_groups: Optional. True, if the bot can be invited to groups. Returned only in getMe. :type can_join_groups: :obj:`bool` - :param can_read_all_group_messages: Optional. True, if privacy mode is disabled for the bot. Returned only in + :param can_read_all_group_messages: Optional. True, if privacy mode is disabled for the bot. Returned only in getMe. :type can_read_all_group_messages: :obj:`bool` @@ -523,8 +523,8 @@ def de_json(cls, json_string): # 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, + 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, has_main_web_app=None, **kwargs): self.id: int = id self.is_bot: bool = is_bot @@ -592,8 +592,8 @@ class ChatFullInfo(JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#chat - :param id: Unique identifier for this chat. This number may have more than 32 significant bits and some programming - languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed + :param id: Unique identifier for this chat. This number may have more than 32 significant bits and some programming + languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier. :type id: :obj:`int` @@ -729,9 +729,9 @@ class ChatFullInfo(JsonDeserializable): Custom emoji from this set can be used by all users and bots in the group. Returned only in getChat. :param custom_emoji_sticker_set_name: :obj:`str` - :param linked_chat_id: Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for - a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some - programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a + :param linked_chat_id: Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for + a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some + programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a 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` @@ -771,17 +771,17 @@ def de_json(cls, json_string): def __init__(self, id, type, title=None, username=None, first_name=None, last_name=None, photo=None, bio=None, has_private_forwards=None, - description=None, invite_link=None, pinned_message=None, + description=None, invite_link=None, pinned_message=None, permissions=None, slow_mode_delay=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, + 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, 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, + 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, + profile_background_custom_emoji_id=None, has_visible_history=None, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, - business_opening_hours=None, personal_chat=None, birthdate=None, + business_opening_hours=None, personal_chat=None, birthdate=None, can_send_paid_media=None, accepted_gift_types=None, **kwargs): self.id: int = id @@ -885,7 +885,7 @@ class WebAppData(JsonDeserializable, Dictionaryable): :param data: The data. Be aware that a bad client can send arbitrary data in this field. :type data: :obj:`str` - :param button_text: Text of the web_app keyboard button from which the Web App was opened. Be aware that a bad client + :param button_text: Text of the web_app keyboard button from which the Web App was opened. Be aware that a bad client can send arbitrary data in this field. :type button_text: :obj:`str` @@ -1484,7 +1484,7 @@ def de_json(cls, json_string): if 'direct_message_price_changed' in obj: opts['direct_message_price_changed'] = DirectMessagePriceChanged.de_json(obj['direct_message_price_changed']) content_type = 'direct_message_price_changed' - + return cls(message_id, from_user, date, chat, content_type, opts, json_string) @classmethod @@ -1838,7 +1838,7 @@ class PhotoSize(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -1877,7 +1877,7 @@ class Audio(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -1896,8 +1896,8 @@ class Audio(JsonDeserializable): :param mime_type: Optional. MIME type of the file as defined by sender :type mime_type: :obj:`str` - :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. :type file_size: :obj:`int` @@ -1913,11 +1913,11 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) - else: + else: obj['thumbnail'] = None return cls(**obj) - def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, + def __init__(self, file_id, file_unique_id, duration, performer=None, title=None, file_name=None, mime_type=None, file_size=None, thumbnail=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id @@ -1944,7 +1944,7 @@ class Voice(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -1954,8 +1954,8 @@ class Voice(JsonDeserializable): :param mime_type: Optional. MIME type of the file as defined by sender :type mime_type: :obj:`str` - :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. :type file_size: :obj:`int` @@ -1985,7 +1985,7 @@ class Document(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -1998,8 +1998,8 @@ class Document(JsonDeserializable): :param mime_type: Optional. MIME type of the file as defined by sender :type mime_type: :obj:`str` - :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. :type file_size: :obj:`int` @@ -2012,7 +2012,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'thumbnail' in obj and 'file_id' in obj['thumbnail']: obj['thumbnail'] = PhotoSize.de_json(obj['thumbnail']) - else: + else: obj['thumbnail'] = None return cls(**obj) @@ -2039,7 +2039,7 @@ class Video(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -2067,8 +2067,8 @@ class Video(JsonDeserializable): :param mime_type: Optional. MIME type of the file as defined by sender :type mime_type: :obj:`str` - :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. :type file_size: :obj:`int` @@ -2114,7 +2114,7 @@ class VideoNote(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -2170,8 +2170,8 @@ class Contact(JsonDeserializable): :param last_name: Optional. Contact's last name :type last_name: :obj:`str` - :param user_id: Optional. Contact's user identifier in Telegram. This number may have more than 32 significant bits - and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 + :param user_id: Optional. Contact's user identifier in Telegram. This number may have more than 32 significant bits + and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. :type user_id: :obj:`int` @@ -2210,14 +2210,14 @@ class Location(JsonDeserializable, JsonSerializable, 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. Time relative to the message sending date, during which the location can be updated; + :param live_period: Optional. Time relative to the message sending date, during which the location can be updated; in seconds. For active live locations only. :type live_period: :obj:`int` :param heading: Optional. The direction in which user is moving, in degrees; 1-360. For active live locations only. :type heading: :obj:`int` - :param proximity_alert_radius: Optional. The maximum distance for proximity alerts about approaching another + :param proximity_alert_radius: Optional. The maximum distance for proximity alerts about approaching another chat member, in meters. For sent live locations only. :type proximity_alert_radius: :obj:`int` @@ -2238,10 +2238,10 @@ def __init__(self, longitude, latitude, horizontal_accuracy=None, self.live_period: Optional[int] = live_period self.heading: Optional[int] = heading self.proximity_alert_radius: Optional[int] = proximity_alert_radius - + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): return { "longitude": self.longitude, @@ -2271,7 +2271,7 @@ class Venue(JsonDeserializable): :param foursquare_id: Optional. Foursquare identifier of the venue :type foursquare_id: :obj:`str` - :param foursquare_type: Optional. Foursquare type of the venue. (For example, “arts_entertainment/default”, + :param foursquare_type: Optional. Foursquare type of the venue. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) :type foursquare_type: :obj:`str` @@ -2291,7 +2291,7 @@ def de_json(cls, json_string): obj['location'] = Location.de_json(obj['location']) return cls(**obj) - def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, + def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None, google_place_id=None, google_place_type=None, **kwargs): self.location: Location = location self.title: str = title @@ -2340,16 +2340,16 @@ class File(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` - :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. :type file_size: :obj:`int` - :param file_path: Optional. File path. Use https://api.telegram.org/file/bot/ to get the + :param file_path: Optional. File path. Use https://api.telegram.org/file/bot/ to get the file. :type file_path: :obj:`str` @@ -2376,11 +2376,11 @@ class ForceReply(JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#forcereply - :param force_reply: Shows reply interface to the user, as if they manually selected the bot's message and tapped + :param force_reply: Shows reply interface to the user, as if they manually selected the bot's message and tapped 'Reply' :type force_reply: :obj:`bool` - :param input_field_placeholder: Optional. The placeholder to be shown in the input field when the reply is active; + :param input_field_placeholder: Optional. The placeholder to be shown in the input field when the reply is active; 1-64 characters :type input_field_placeholder: :obj:`str` @@ -2412,16 +2412,16 @@ class ReplyKeyboardRemove(JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#replykeyboardremove - :param remove_keyboard: Requests clients to remove the custom keyboard (user will not be able to summon this - keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in + :param remove_keyboard: Requests clients to remove the custom keyboard (user will not be able to summon this + keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in ReplyKeyboardMarkup) Note that this parameter is set to True by default by the library. You cannot modify it. :type remove_keyboard: :obj:`bool` - :param selective: Optional. Use this parameter if you want to remove the keyboard for specific users only. Targets: - 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has - reply_to_message_id), sender of the original message.Example: A user votes in a poll, bot returns confirmation - message in reply to the vote and removes the keyboard for that user, while still showing the keyboard with poll options + :param selective: Optional. Use this parameter if you want to remove the keyboard for specific users only. Targets: + 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has + reply_to_message_id), sender of the original message.Example: A user votes in a poll, bot returns confirmation + message in reply to the vote and removes the keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet. :type selective: :obj:`bool` @@ -2483,21 +2483,21 @@ class ReplyKeyboardMarkup(JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#replykeyboardmarkup - :param keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of + :param keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of :class:`telebot.types.KeyboardButton` objects :type keyboard: :obj:`list` of :obj:`list` of :class:`telebot.types.KeyboardButton` - :param resize_keyboard: Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make - the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is + :param resize_keyboard: Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make + the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard. :type resize_keyboard: :obj:`bool` - :param one_time_keyboard: Optional. Requests clients to hide the keyboard as soon as it's been used. The keyboard - will still be available, but clients will automatically display the usual letter-keyboard in the chat - the user can + :param one_time_keyboard: Optional. Requests clients to hide the keyboard as soon as it's been used. The keyboard + will still be available, but clients will automatically display the usual letter-keyboard in the chat - the user can press a special button in the input field to see the custom keyboard again. Defaults to false. :type one_time_keyboard: :obj:`bool` - :param input_field_placeholder: Optional. The placeholder to be shown in the input field when the keyboard is + :param input_field_placeholder: Optional. The placeholder to be shown in the input field when the keyboard is active; 1-64 characters :type input_field_placeholder: :obj:`str` @@ -2511,7 +2511,7 @@ class ReplyKeyboardMarkup(JsonSerializable): :param is_persistent: Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. - + Example: A user requests to change the bot's language, bot replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard. @@ -2520,7 +2520,7 @@ class ReplyKeyboardMarkup(JsonSerializable): """ max_row_keys = 12 - def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, + def __init__(self, resize_keyboard: Optional[bool]=None, one_time_keyboard: Optional[bool]=None, selective: Optional[bool]=None, row_width: int=3, input_field_placeholder: Optional[str]=None, is_persistent: Optional[bool]=None): if row_width > self.max_row_keys: @@ -2562,7 +2562,7 @@ def add(self, *args, row_width=None) -> 'ReplyKeyboardMarkup': if not DISABLE_KEYLEN_ERROR: logger.error('Telegram does not support reply keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - + for row in service_utils.chunks(args, row_width): button_array = [] for button in row: @@ -2624,8 +2624,8 @@ def __init__(self, type=None): def to_dict(self): return {'type': self.type} - - + + class KeyboardButtonRequestUsers(Dictionaryable): """ This object defines the criteria used to request a suitable user. @@ -2795,23 +2795,23 @@ class KeyboardButton(Dictionaryable, JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#keyboardbutton - :param text: Text of the button. If none of the optional fields are used, it will be sent as a message when the button is + :param text: Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed :type text: :obj:`str` - :param request_contact: Optional. If True, the user's phone number will be sent as a contact when the button is + :param request_contact: Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only. :type request_contact: :obj:`bool` - :param request_location: Optional. If True, the user's current location will be sent when the button is pressed. + :param request_location: Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only. :type request_location: :obj:`bool` - :param request_poll: Optional. If specified, the user will be asked to create a poll and send it to the bot when the + :param request_poll: Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only. :type request_poll: :class:`telebot.types.KeyboardButtonPollType` - :param web_app: Optional. If specified, the described Web App will be launched when the button is pressed. The Web App + :param web_app: Optional. If specified, the described Web App will be launched when the button is pressed. The Web App will be able to send a “web_app_data” service message. Available in private chats only. :type web_app: :class:`telebot.types.WebAppInfo` @@ -2829,7 +2829,7 @@ class KeyboardButton(Dictionaryable, JsonSerializable): :return: Instance of the class :rtype: :class:`telebot.types.KeyboardButton` """ - def __init__(self, text: str, request_contact: Optional[bool]=None, + def __init__(self, text: str, request_contact: Optional[bool]=None, request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, web_app: Optional[WebAppInfo]=None, request_user: Optional[KeyboardButtonRequestUser]=None, request_chat: Optional[KeyboardButtonRequestChat]=None, request_users: Optional[KeyboardButtonRequestUsers]=None): @@ -2889,7 +2889,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) Telegram Documentation: https://core.telegram.org/bots/api#inlinekeyboardmarkup - :param keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of + :param keyboard: :obj:`list` of button rows, each represented by an :obj:`list` of :class:`telebot.types.InlineKeyboardButton` objects :type keyboard: :obj:`list` of :obj:`list` of :class:`telebot.types.InlineKeyboardButton` @@ -2900,7 +2900,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable) :rtype: :class:`telebot.types.InlineKeyboardMarkup` """ max_row_keys = 8 - + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -2913,7 +2913,7 @@ def __init__(self, keyboard=None, row_width=3): # Todo: Will be replaced with Exception in future releases logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - + self.row_width: int = row_width self.keyboard: List[List[InlineKeyboardButton]] = keyboard or [] @@ -2927,7 +2927,7 @@ def add(self, *args, row_width=None) -> 'InlineKeyboardMarkup': When row_width is set to 2, the result: {keyboard: [["A", "B"], ["C"]]} See https://core.telegram.org/bots/api#inlinekeyboardmarkup - + :param args: Array of InlineKeyboardButton to append to the keyboard :type args: :obj:`list` of :class:`telebot.types.InlineKeyboardButton` @@ -2939,18 +2939,18 @@ def add(self, *args, row_width=None) -> 'InlineKeyboardMarkup': """ if row_width is None: row_width = self.row_width - + if row_width > self.max_row_keys: # Todo: Will be replaced with Exception in future releases logger.error('Telegram does not support inline keyboard row width over %d.' % self.max_row_keys) row_width = self.max_row_keys - + for row in service_utils.chunks(args, row_width): button_array = [button for button in row] self.keyboard.append(button_array) - + return self - + def row(self, *args) -> 'InlineKeyboardMarkup': """ Adds a list of InlineKeyboardButton to the keyboard. @@ -2959,14 +2959,14 @@ def row(self, *args) -> 'InlineKeyboardMarkup': InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: '{keyboard: [["A"], ["B", "C"]]}' See https://core.telegram.org/bots/api#inlinekeyboardmarkup - + :param args: Array of InlineKeyboardButton to append to the keyboard :type args: :obj:`list` of :class:`telebot.types.InlineKeyboardButton` :return: self, to allow function chaining. :rtype: :class:`telebot.types.InlineKeyboardMarkup` """ - + return self.add(*args, row_width=self.max_row_keys) def to_json(self): @@ -2987,32 +2987,32 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable) :param text: Label text on the button :type text: :obj:`str` - :param url: Optional. HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id= can be + :param url: Optional. HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id= can be used to mention a user by their ID without using a username, if this is allowed by their privacy settings. :type url: :obj:`str` :param callback_data: Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes :type callback_data: :obj:`str` - :param web_app: Optional. Description of the Web App that will be launched when the user presses the button. The Web - App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only + :param web_app: Optional. Description of the Web App that will be launched when the user presses the button. The Web + App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only in private chats between a user and the bot. :type web_app: :class:`telebot.types.WebAppInfo` - :param login_url: Optional. An HTTPS URL used to automatically authorize the user. Can be used as a replacement for + :param login_url: Optional. An HTTPS URL used to automatically authorize the user. Can be used as a replacement for the Telegram Login Widget. :type login_url: :class:`telebot.types.LoginUrl` - :param switch_inline_query: Optional. If set, pressing the button will prompt the user to select one of their chats, - open that chat and insert the bot's username and the specified inline query in the input field. May be empty, in which - case just the bot's username will be inserted.Note: This offers an easy way for users to start using your bot in inline - mode when they are currently in a private chat with it. Especially useful when combined with switch_pm… actions - in + :param switch_inline_query: Optional. If set, pressing the button will prompt the user to select one of their chats, + open that chat and insert the bot's username and the specified inline query in the input field. May be empty, in which + case just the bot's username will be inserted.Note: This offers an easy way for users to start using your bot in inline + mode when they are currently in a private chat with it. Especially useful when combined with switch_pm… actions - in this case the user will be automatically returned to the chat they switched from, skipping the chat selection screen. :type switch_inline_query: :obj:`str` - :param switch_inline_query_current_chat: Optional. If set, pressing the button will insert the bot's username - and the specified inline query in the current chat's input field. May be empty, in which case only the bot's username - will be inserted.This offers a quick way for the user to open your bot in inline mode in the same chat - good for selecting + :param switch_inline_query_current_chat: Optional. If set, pressing the button will insert the bot's username + and the specified inline query in the current chat's input field. May be empty, in which case only the bot's username + will be inserted.This offers a quick way for the user to open your bot in inline mode in the same chat - good for selecting something from multiple options. :type switch_inline_query_current_chat: :obj:`str` @@ -3020,11 +3020,11 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable) specified type, open that chat and insert the bot's username and the specified inline query in the input field :type switch_inline_query_chosen_chat: :class:`telebot.types.SwitchInlineQueryChosenChat` - :param callback_game: Optional. Description of the game that will be launched when the user presses the + :param callback_game: Optional. Description of the game that will be launched when the user presses the button. NOTE: This type of button must always be the first button in the first row. :type callback_game: :class:`telebot.types.CallbackGame` - :param pay: Optional. Specify True, to send a Pay button. NOTE: This type of button must always be the first button in + :param pay: Optional. Specify True, to send a Pay button. NOTE: This type of button must always be the first button in the first row and can only be used in invoice messages. :type pay: :obj:`bool` @@ -3046,7 +3046,7 @@ def de_json(cls, json_string): obj['switch_inline_query_chosen_chat'] = SwitchInlineQueryChosenChat.de_json(obj.get('switch_inline_query_chosen_chat')) if 'copy_text' in obj: obj['copy_text'] = CopyTextButton.de_json(obj.get('copy_text')) - + return cls(**obj) def __init__(self, text: str, url: Optional[str]=None, callback_data: Optional[str]=None, web_app: Optional[WebAppInfo]=None, @@ -3099,22 +3099,22 @@ class LoginUrl(Dictionaryable, JsonSerializable, JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#loginurl - :param url: An HTTPS URL to be opened with user authorization data added to the query string when the button is pressed. - If the user refuses to provide authorization data, the original URL without information about the user will be - opened. The data added is the same as described in Receiving authorization data. NOTE: You must always check the hash - of the received data to verify the authentication and the integrity of the data as described in Checking + :param url: An HTTPS URL to be opened with user authorization data added to the query string when the button is pressed. + If the user refuses to provide authorization data, the original URL without information about the user will be + opened. The data added is the same as described in Receiving authorization data. NOTE: You must always check the hash + of the received data to verify the authentication and the integrity of the data as described in Checking authorization. :type url: :obj:`str` :param forward_text: Optional. New text of the button in forwarded messages. :type forward_text: :obj:`str` - :param bot_username: Optional. Username of a bot, which will be used for user authorization. See Setting up a bot for - more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the + :param bot_username: Optional. Username of a bot, which will be used for user authorization. See Setting up a bot for + more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details. :type bot_username: :obj:`str` - :param request_write_access: Optional. Pass True to request the permission for your bot to send messages to the + :param request_write_access: Optional. Pass True to request the permission for your bot to send messages to the user. :type request_write_access: :obj:`bool` @@ -3164,15 +3164,15 @@ class CallbackQuery(JsonDeserializable): :param message: Optional. Message sent by the bot with the callback button that originated the query :type message: :class:`telebot.types.Message` or :class:`telebot.types.InaccessibleMessage` - :param inline_message_id: Optional. Identifier of the message sent via the bot in inline mode, that originated the + :param inline_message_id: Optional. Identifier of the message sent via the bot in inline mode, that originated the query. :type inline_message_id: :obj:`str` - :param chat_instance: Global identifier, uniquely corresponding to the chat to which the message with the callback + :param chat_instance: Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games. :type chat_instance: :obj:`str` - :param data: Optional. Data associated with the callback button. Be aware that the message originated the query can + :param data: Optional. Data associated with the callback button. Be aware that the message originated the query can contain no callback buttons with this data. :type data: :obj:`str` @@ -3211,27 +3211,27 @@ def __init__( self.data: Optional[str] = data self.game_short_name: Optional[str] = game_short_name self.json = json_string - - + + class ChatPhoto(JsonDeserializable): """ This object represents a chat photo. Telegram Documentation: https://core.telegram.org/bots/api#chatphoto - :param small_file_id: File identifier of small (160x160) chat photo. This file_id can be used only for photo + :param small_file_id: File identifier of small (160x160) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed. :type small_file_id: :obj:`str` - :param small_file_unique_id: Unique file identifier of small (160x160) chat photo, which is supposed to be the same + :param small_file_unique_id: Unique file identifier of small (160x160) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type small_file_unique_id: :obj:`str` - :param big_file_id: File identifier of big (640x640) chat photo. This file_id can be used only for photo download and + :param big_file_id: File identifier of big (640x640) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed. :type big_file_id: :obj:`str` - :param big_file_unique_id: Unique file identifier of big (640x640) chat photo, which is supposed to be the same over + :param big_file_unique_id: Unique file identifier of big (640x640) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type big_file_unique_id: :obj:`str` @@ -3251,11 +3251,11 @@ def __init__(self, small_file_id, small_file_unique_id, big_file_id, big_file_un self.big_file_unique_id: str = big_file_unique_id -class ChatMember(JsonDeserializable): +class ChatMember(JsonDeserializable, ABC): """ This object contains information about one member of a chat. Currently, the following 6 types of chat members are supported: - + * :class:`telebot.types.ChatMemberOwner` * :class:`telebot.types.ChatMemberAdministrator` * :class:`telebot.types.ChatMemberMember` @@ -3266,78 +3266,31 @@ class ChatMember(JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#chatmember """ + def __init__(self, user, status, **kwargs): + self.user: User = user + self.status: str = status + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['user'] = User.de_json(obj['user']) - member_type = obj['status'] + status = obj['status'] # Ordered according to estimated appearance frequency. - if member_type == "member": + if status == "member": return ChatMemberMember(**obj) - elif member_type == "left": + elif status == "left": return ChatMemberLeft(**obj) - elif member_type == "kicked": + elif status == "kicked": return ChatMemberBanned(**obj) - elif member_type == "restricted": + elif status == "restricted": return ChatMemberRestricted(**obj) - elif member_type == "administrator": + elif status == "administrator": return ChatMemberAdministrator(**obj) - elif member_type == "creator": + elif status == "creator": return ChatMemberOwner(**obj) else: - # Should not be here. For "if something happen" compatibility - return cls(**obj) - - def __init__(self, user, status, custom_title=None, is_anonymous=None, can_be_edited=None, - can_post_messages=None, can_edit_messages=None, can_delete_messages=None, - can_restrict_members=None, can_promote_members=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, is_member=None, - can_send_messages=None, can_send_audios=None, can_send_documents=None, - can_send_photos=None, can_send_videos=None, can_send_video_notes=None, - can_send_voice_notes=None, - can_send_polls=None, - can_send_other_messages=None, can_add_web_page_previews=None, - can_manage_chat=None, can_manage_video_chats=None, - until_date=None, can_manage_topics=None, - can_post_stories=None, can_edit_stories=None, can_delete_stories=None, - **kwargs): - self.user: User = user - self.status: str = status - self.custom_title: str = custom_title - self.is_anonymous: bool = is_anonymous - self.can_be_edited: bool = can_be_edited - self.can_post_messages: bool = can_post_messages - self.can_edit_messages: bool = can_edit_messages - self.can_delete_messages: bool = can_delete_messages - self.can_restrict_members: bool = can_restrict_members - self.can_promote_members: bool = can_promote_members - self.can_change_info: bool = can_change_info - self.can_invite_users: bool = can_invite_users - self.can_pin_messages: bool = can_pin_messages - self.is_member: bool = is_member - self.can_send_messages: bool = can_send_messages - self.can_send_polls: bool = can_send_polls - self.can_send_other_messages: bool = can_send_other_messages - self.can_add_web_page_previews: bool = can_add_web_page_previews - self.can_manage_chat: bool = can_manage_chat - self.can_manage_video_chats: bool = can_manage_video_chats - self.until_date: int = until_date - self.can_manage_topics: bool = can_manage_topics - self.can_send_audios: bool = can_send_audios - self.can_send_documents: bool = can_send_documents - self.can_send_photos: bool = can_send_photos - self.can_send_videos: bool = can_send_videos - self.can_send_video_notes: bool = can_send_video_notes - self.can_send_voice_notes: bool = can_send_voice_notes - self.can_post_stories: bool = can_post_stories - self.can_edit_stories: bool = can_edit_stories - self.can_delete_stories: bool = can_delete_stories - - @property - def can_manage_voice_chats(self): - log_deprecation_warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') - return self.can_manage_video_chats + raise ValueError(f"Unknown chat member type: {status}.") # noinspection PyUnresolvedReferences @@ -3362,7 +3315,10 @@ class ChatMemberOwner(ChatMember): :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberOwner` """ - pass + def __init__(self, user, status, is_anonymous, custom_title=None, **kwargs): + super().__init__(user, status, **kwargs) + self.is_anonymous: bool = is_anonymous + self.custom_title: Optional[str] = custom_title # noinspection PyUnresolvedReferences @@ -3384,8 +3340,8 @@ class ChatMemberAdministrator(ChatMember): :param is_anonymous: True, if the user's presence in the chat is hidden :type is_anonymous: :obj:`bool` - :param can_manage_chat: True, if the administrator can access the chat event log, chat statistics, message - statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. + :param can_manage_chat: True, if the administrator can access the chat event log, chat statistics, message + statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege :type can_manage_chat: :obj:`bool` @@ -3398,8 +3354,8 @@ class ChatMemberAdministrator(ChatMember): :param can_restrict_members: True, if the administrator can restrict, ban or unban chat members :type can_restrict_members: :obj:`bool` - :param can_promote_members: True, if the administrator can add new administrators with a subset of their own - privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that + :param can_promote_members: True, if the administrator can add new administrators with a subset of their own + privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user) :type can_promote_members: :obj:`bool` @@ -3409,36 +3365,60 @@ class ChatMemberAdministrator(ChatMember): :param can_invite_users: True, if the user is allowed to invite new users to the chat :type can_invite_users: :obj:`bool` + :param can_post_stories: True, if the administrator can post channel stories + :type can_post_stories: :obj:`bool` + + :param can_edit_stories: True, if the administrator can edit stories + :type can_edit_stories: :obj:`bool` + + :param can_delete_stories: True, if the administrator can delete stories of other users + :type can_delete_stories: :obj:`bool` + :param can_post_messages: Optional. True, if the administrator can post in the channel; channels only :type can_post_messages: :obj:`bool` - :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin - messages; channels only + :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin messages; channels only :type can_edit_messages: :obj:`bool` :param can_pin_messages: Optional. True, if the user is allowed to pin messages; groups and supergroups only :type can_pin_messages: :obj:`bool` - :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; - supergroups only + :param can_manage_topics: Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only :type can_manage_topics: :obj:`bool` :param custom_title: Optional. Custom title for this user :type custom_title: :obj:`str` - :param can_post_stories: Optional. True, if the administrator can post channel stories - :type can_post_stories: :obj:`bool` - - :param can_edit_stories: Optional. True, if the administrator can edit stories - :type can_edit_stories: :obj:`bool` - - :param can_delete_stories: Optional. True, if the administrator can delete stories of other users - :type can_delete_stories: :obj:`bool` - :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberAdministrator` """ - pass + def __init__(self, user, status, can_be_edited, is_anonymous, can_manage_chat, can_delete_messages, + can_manage_video_chats, can_restrict_members, can_promote_members, can_change_info, can_invite_users, + can_post_stories, can_edit_stories, can_delete_stories, can_post_messages=None, can_edit_messages=None, + can_pin_messages=None, can_manage_topics=None, custom_title=None, **kwargs): + super().__init__(user, status, **kwargs) + self.can_be_edited: bool = can_be_edited + self.is_anonymous: bool = is_anonymous + self.can_manage_chat: bool = can_manage_chat + self.can_delete_messages: bool = can_delete_messages + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_post_stories: bool = can_post_stories + self.can_edit_stories: bool = can_edit_stories + self.can_delete_stories: bool = can_delete_stories + self.can_post_messages: Optional[bool] = can_post_messages + self.can_edit_messages: Optional[bool] = can_edit_messages + self.can_pin_messages: Optional[bool] = can_pin_messages + self.can_manage_topics: Optional[bool] = can_manage_topics + self.custom_title: Optional[str] = custom_title + + @property + def can_manage_voice_chats(self): + log_deprecation_warning('The parameter "can_manage_voice_chats" is deprecated. Use "can_manage_video_chats" instead.') + return self.can_manage_video_chats # noinspection PyUnresolvedReferences @@ -3454,10 +3434,15 @@ class ChatMemberMember(ChatMember): :param user: Information about the user :type user: :class:`telebot.types.User` + :param until_date: Optional. Date when the user's subscription will expire; Unix time. If 0, then the user is a member forever + :type until_date: :obj:`int` + :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberMember` """ - pass + def __init__(self, user, status, until_date=None, **kwargs): + super().__init__(user, status, **kwargs) + self.until_date: Optional[int] = until_date # noinspection PyUnresolvedReferences @@ -3476,18 +3461,6 @@ class ChatMemberRestricted(ChatMember): :param is_member: True, if the user is a member of the chat at the moment of the request :type is_member: :obj:`bool` - :param can_change_info: True, if the user is allowed to change the chat title, photo and other settings - :type can_change_info: :obj:`bool` - - :param can_invite_users: True, if the user is allowed to invite new users to the chat - :type can_invite_users: :obj:`bool` - - :param can_pin_messages: True, if the user is allowed to pin messages - :type can_pin_messages: :obj:`bool` - - :param can_manage_topics: True, if the user is allowed to create forum topics - :type can_manage_topics: :obj:`bool` - :param can_send_messages: True, if the user is allowed to send text messages, contacts, locations and venues :type can_send_messages: :obj:`bool` @@ -3512,21 +3485,52 @@ class ChatMemberRestricted(ChatMember): :param can_send_polls: True, if the user is allowed to send polls :type can_send_polls: :obj:`bool` - :param can_send_other_messages: True, if the user is allowed to send animations, games, stickers and use inline - bots + :param can_send_other_messages: True, if the user is allowed to send animations, games, stickers and use inline bots :type can_send_other_messages: :obj:`bool` :param can_add_web_page_previews: True, if the user is allowed to add web page previews to their messages :type can_add_web_page_previews: :obj:`bool` - :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is restricted - forever + :param can_change_info: True, if the user is allowed to change the chat title, photo and other settings + :type can_change_info: :obj:`bool` + + :param can_invite_users: True, if the user is allowed to invite new users to the chat + :type can_invite_users: :obj:`bool` + + :param can_pin_messages: True, if the user is allowed to pin messages + :type can_pin_messages: :obj:`bool` + + :param can_manage_topics: True, if the user is allowed to create forum topics + :type can_manage_topics: :obj:`bool` + + :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is restricted forever :type until_date: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberRestricted` """ - pass + def __init__(self, user, status, is_member, can_send_messages, can_send_audios, can_send_documents, + can_send_photos, can_send_videos, can_send_video_notes, can_send_voice_notes, can_send_polls, + can_send_other_messages, can_add_web_page_previews, + can_change_info, can_invite_users, can_pin_messages, can_manage_topics, + until_date=None, **kwargs): + super().__init__(user, status, **kwargs) + self.is_member: bool = is_member + self.can_send_messages: bool = can_send_messages + self.can_send_audios: bool = can_send_audios + self.can_send_documents: bool = can_send_documents + self.can_send_photos: bool = can_send_photos + self.can_send_videos: bool = can_send_videos + self.can_send_video_notes: bool = can_send_video_notes + self.can_send_voice_notes: bool = can_send_voice_notes + self.can_send_polls: bool = can_send_polls + self.can_send_other_messages: bool = can_send_other_messages + self.can_add_web_page_previews: bool = can_add_web_page_previews + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_pin_messages: bool = can_pin_messages + self.can_manage_topics: bool = can_manage_topics + self.until_date: Optional[int] = until_date # noinspection PyUnresolvedReferences @@ -3561,14 +3565,15 @@ class ChatMemberBanned(ChatMember): :param user: Information about the user :type user: :class:`telebot.types.User` - :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is banned - forever + :param until_date: Date when restrictions will be lifted for this user; unix time. If 0, then the user is banned forever :type until_date: :obj:`int` :return: Instance of the class :rtype: :class:`telebot.types.ChatMemberBanned` """ - pass + def __init__(self, user, status, until_date=None, **kwargs): + super().__init__(user, status, **kwargs) + self.until_date: Optional[int] = until_date class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): @@ -3577,8 +3582,7 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): Telegram Documentation: https://core.telegram.org/bots/api#chatpermissions - :param can_send_messages: Optional. True, if the user is allowed to send text messages, contacts, locations and - venues + :param can_send_messages: Optional. True, if the user is allowed to send text messages, contacts, locations and venues :type can_send_messages: :obj:`bool` :param can_send_audios: Optional. True, if the user is allowed to send audios @@ -3602,15 +3606,15 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): :param can_send_polls: Optional. True, if the user is allowed to send polls, implies can_send_messages :type can_send_polls: :obj:`bool` - :param can_send_other_messages: Optional. True, if the user is allowed to send animations, games, stickers and use + :param can_send_other_messages: Optional. True, if the user is allowed to send animations, games, stickers and use inline bots :type can_send_other_messages: :obj:`bool` - :param can_add_web_page_previews: Optional. True, if the user is allowed to add web page previews to their + :param can_add_web_page_previews: Optional. True, if the user is allowed to add web page previews to their messages :type can_add_web_page_previews: :obj:`bool` - :param can_change_info: Optional. True, if the user is allowed to change the chat title, photo and other settings. + :param can_change_info: Optional. True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups :type can_change_info: :obj:`bool` @@ -3622,7 +3626,7 @@ class ChatPermissions(JsonDeserializable, JsonSerializable, Dictionaryable): :param can_manage_topics: Optional. True, if the user is allowed to create forum topics. If omitted defaults to the value of can_pin_messages - :type can_manage_topics: :obj:`bool` + :type can_manage_topics: :obj:`bool` :param can_send_media_messages: deprecated. :type can_send_media_messages: :obj:`bool` @@ -3641,7 +3645,7 @@ def __init__(self, can_send_messages=None, can_send_media_messages=None,can_send can_send_videos=None, can_send_video_notes=None, can_send_voice_notes=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, can_change_info=None, - can_invite_users=None, can_pin_messages=None, + can_invite_users=None, can_pin_messages=None, can_manage_topics=None, **kwargs): self.can_send_messages: Optional[bool] = can_send_messages self.can_send_polls: Optional[bool] = can_send_polls @@ -3704,7 +3708,7 @@ def to_dict(self): json_dict['can_pin_messages'] = self.can_pin_messages if self.can_manage_topics is not None: json_dict['can_manage_topics'] = self.can_manage_topics - + return json_dict @@ -3714,7 +3718,7 @@ class BotCommand(JsonSerializable, JsonDeserializable, Dictionaryable): Telegram Documentation: https://core.telegram.org/bots/api#botcommand - :param command: Text of the command; 1-32 characters. Can contain only lowercase English letters, digits and + :param command: Text of the command; 1-32 characters. Can contain only lowercase English letters, digits and underscores. :type command: :obj:`str` @@ -3891,7 +3895,7 @@ class BotCommandScopeChat(BotCommandScope): :param type: Scope type, must be chat :type type: :obj:`str` - :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :type chat_id: :obj:`int` or :obj:`str` @@ -3912,7 +3916,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): :param type: Scope type, must be chat_administrators :type type: :obj:`str` - :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :type chat_id: :obj:`int` or :obj:`str` @@ -3933,7 +3937,7 @@ class BotCommandScopeChatMember(BotCommandScope): :param type: Scope type, must be chat_member :type type: :obj:`str` - :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) :type chat_id: :obj:`int` or :obj:`str` @@ -3968,9 +3972,9 @@ class InlineQuery(JsonDeserializable): :param offset: Offset of the results to be returned, can be controlled by the bot :type offset: :obj:`str` - :param chat_type: Optional. Type of the chat from which the inline query was sent. Can be either “sender” for a private - chat with the inline query sender, “private”, “group”, “supergroup”, or “channel”. The chat type should be always - known for requests sent from official clients and most third-party clients, unless the request was sent from a secret + :param chat_type: Optional. Type of the chat from which the inline query was sent. Can be either “sender” for a private + chat with the inline query sender, “private”, “group”, “supergroup”, or “channel”. The chat type should be always + known for requests sent from official clients and most third-party clients, unless the request was sent from a secret chat :type chat_type: :obj:`str` @@ -4007,11 +4011,11 @@ class InputTextMessageContent(Dictionaryable): :param message_text: Text of the message to be sent, 1-4096 characters :type message_text: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the message text. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the message text. See formatting options for more details. :type parse_mode: :obj:`str` - :param entities: Optional. List of special entities that appear in message text, which can be specified instead of + :param entities: Optional. List of special entities that appear in message text, which can be specified instead of parse_mode :type entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -4118,7 +4122,7 @@ class InputVenueMessageContent(Dictionaryable): :param foursquare_id: Optional. Foursquare identifier of the venue, if known :type foursquare_id: :obj:`str` - :param foursquare_type: Optional. Foursquare type of the venue, if known. (For example, + :param foursquare_type: Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) :type foursquare_type: :obj:`str` @@ -4131,7 +4135,7 @@ class InputVenueMessageContent(Dictionaryable): :return: Instance of the class :rtype: :class:`telebot.types.InputVenueMessageContent` """ - def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, + def __init__(self, latitude, longitude, title, address, foursquare_id=None, foursquare_type=None, google_place_id=None, google_place_type=None): self.latitude: float = latitude self.longitude: float = longitude @@ -4208,7 +4212,7 @@ class InputInvoiceMessageContent(Dictionaryable): :param description: Product description, 1-255 characters :type description: :obj:`str` - :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. :type payload: :obj:`str` @@ -4219,26 +4223,26 @@ class InputInvoiceMessageContent(Dictionaryable): :param currency: Three-letter ISO 4217 currency code, see more on currencies :type currency: :obj:`str` - :param prices: Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery + :param prices: Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`list` of :class:`telebot.types.LabeledPrice` - :param max_tip_amount: Optional. The maximum accepted amount for tips in the smallest units of the currency - (integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_amount = 145. See the exp - parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the + :param max_tip_amount: Optional. The maximum accepted amount for tips in the smallest units of the currency + (integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_amount = 145. See the exp + parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0 :type max_tip_amount: :obj:`int` - :param suggested_tip_amounts: Optional. A JSON-serialized array of suggested amounts of tip in the smallest units - of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified. The suggested tip + :param suggested_tip_amounts: Optional. A JSON-serialized array of suggested amounts of tip in the smallest units + of the currency (integer, not float/double). 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. :type suggested_tip_amounts: :obj:`list` of :obj:`int` - :param provider_data: Optional. A JSON-serialized object for data about the invoice, which will be shared with the + :param provider_data: Optional. A JSON-serialized object for data about the invoice, which will be shared with the payment provider. A detailed description of the required fields should be provided by the payment provider. :type provider_data: :obj:`str` - :param photo_url: Optional. URL of the product photo for the invoice. Can be a photo of the goods or a marketing image + :param photo_url: Optional. URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. :type photo_url: :obj:`str` @@ -4260,7 +4264,7 @@ class InputInvoiceMessageContent(Dictionaryable): :param need_email: Optional. Pass True, if you require the user's email address to complete the order :type need_email: :obj:`bool` - :param need_shipping_address: Optional. Pass True, if you require the user's shipping address to complete the + :param need_shipping_address: Optional. Pass True, if you require the user's shipping address to complete the order :type need_shipping_address: :obj:`bool` @@ -4276,7 +4280,7 @@ class InputInvoiceMessageContent(Dictionaryable): :return: Instance of the class :rtype: :class:`telebot.types.InputInvoiceMessageContent` """ - + def __init__(self, title: str, description: str, payload: str, provider_token: Optional[str], currency: str, prices: List[LabeledPrice], max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]] = None, provider_data: Optional[str] = None, photo_url: Optional[str] = None, photo_size: Optional[int] = None, photo_width: Optional[int] = None, photo_height: Optional[int] = None, @@ -4302,10 +4306,10 @@ def __init__(self, title: str, description: str, payload: str, provider_token: O self.send_phone_number_to_provider: Optional[bool] = send_phone_number_to_provider self.send_email_to_provider: Optional[bool] = send_email_to_provider self.is_flexible: Optional[bool] = is_flexible - + def to_dict(self): json_dict = { - 'title': self.title, + 'title': self.title, 'description': self.description, 'payload': self.payload, 'provider_token': self.provider_token, @@ -4313,33 +4317,33 @@ def to_dict(self): 'prices': [LabeledPrice.to_dict(lp) for lp in self.prices] } if self.max_tip_amount: - json_dict['max_tip_amount'] = self.max_tip_amount + json_dict['max_tip_amount'] = self.max_tip_amount if self.suggested_tip_amounts: - json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts + json_dict['suggested_tip_amounts'] = self.suggested_tip_amounts if self.provider_data: - json_dict['provider_data'] = self.provider_data + json_dict['provider_data'] = self.provider_data if self.photo_url: - json_dict['photo_url'] = self.photo_url + json_dict['photo_url'] = self.photo_url if self.photo_size: - json_dict['photo_size'] = self.photo_size + json_dict['photo_size'] = self.photo_size if self.photo_width: - json_dict['photo_width'] = self.photo_width + json_dict['photo_width'] = self.photo_width if self.photo_height: - json_dict['photo_height'] = self.photo_height + json_dict['photo_height'] = self.photo_height if self.need_name is not None: - json_dict['need_name'] = self.need_name + json_dict['need_name'] = self.need_name if self.need_phone_number is not None: - json_dict['need_phone_number'] = self.need_phone_number + json_dict['need_phone_number'] = self.need_phone_number if self.need_email is not None: - json_dict['need_email'] = self.need_email + json_dict['need_email'] = self.need_email if self.need_shipping_address is not None: - json_dict['need_shipping_address'] = self.need_shipping_address + json_dict['need_shipping_address'] = self.need_shipping_address if self.send_phone_number_to_provider is not None: - json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider + json_dict['send_phone_number_to_provider'] = self.send_phone_number_to_provider if self.send_email_to_provider is not None: - json_dict['send_email_to_provider'] = self.send_email_to_provider + json_dict['send_email_to_provider'] = self.send_email_to_provider if self.is_flexible is not None: - json_dict['is_flexible'] = self.is_flexible + json_dict['is_flexible'] = self.is_flexible return json_dict InputMessageContent = Union[InputTextMessageContent, InputLocationMessageContent, InputVenueMessageContent, InputContactMessageContent, InputInvoiceMessageContent] @@ -4359,7 +4363,7 @@ class ChosenInlineResult(JsonDeserializable): :param location: Optional. Sender location, only for bots that require user location :type location: :class:`telebot.types.Location` - :param inline_message_id: Optional. Identifier of the sent inline message. Available only if there is an inline + :param inline_message_id: Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message. :type inline_message_id: :obj:`str` @@ -4414,7 +4418,7 @@ class InlineQueryResultBase(ABC, Dictionaryable, JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#inlinequeryresult """ - + def __init__(self, type: str, id: str, title: Optional[str] = None, caption: Optional[str] = None, input_message_content: Optional[InputMessageContent] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None): self.type: str = type @@ -4455,7 +4459,7 @@ class SentWebAppMessage(JsonDeserializable, Dictionaryable): Telegram Documentation: https://core.telegram.org/bots/api#sentwebappmessage - :param inline_message_id: Optional. Identifier of the sent inline message. Available only if there is an inline + :param inline_message_id: Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. :type inline_message_id: :obj:`str` @@ -4521,11 +4525,11 @@ class InlineQueryResultArticle(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultArticle` """ - + def __init__(self, id: str, title: str, input_message_content: InputMessageContent, reply_markup: Optional[InlineKeyboardMarkup] = None, url: Optional[str] = None, hide_url: Optional[bool] = None, description: Optional[str] = None, thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = None): - + super().__init__('article', id, title = title, input_message_content = input_message_content, reply_markup = reply_markup) self.url: Optional[str] = url self.hide_url: Optional[bool] = hide_url @@ -4605,11 +4609,11 @@ class InlineQueryResultPhoto(InlineQueryResultBase): :param caption: Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -4688,7 +4692,7 @@ class InlineQueryResultGif(InlineQueryResultBase): :param thumbnail_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result :type thumbnail_url: :obj:`str` - :param thumbnail_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or + :param thumbnail_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg” :type thumbnail_mime_type: :obj:`str` @@ -4701,7 +4705,7 @@ class InlineQueryResultGif(InlineQueryResultBase): :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -4722,7 +4726,7 @@ def __init__(self, id: str, gif_url: str, thumbnail_url: str, gif_width: Optiona reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, gif_duration: Optional[int] = None, parse_mode: Optional[str] = None, thumbnail_mime_type: Optional[str] = None, show_caption_above_media: Optional[bool] = None): - + 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) @@ -4789,7 +4793,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResultBase): :param thumbnail_url: URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result :type thumbnail_url: :obj:`str` - :param thumbnail_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or + :param thumbnail_mime_type: Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg” :type thumbnail_mime_type: :obj:`str` @@ -4802,7 +4806,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResultBase): :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -4822,7 +4826,7 @@ def __init__(self, id: str, mpeg4_url: str, thumbnail_url: str, mpeg4_width: Opt title: Optional[str] = None, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, mpeg4_duration: Optional[int] = None, thumbnail_mime_type: Optional[str] = None, show_caption_above_media: Optional[bool] = None): - + 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) @@ -4889,11 +4893,11 @@ class InlineQueryResultVideo(InlineQueryResultBase): :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -4912,7 +4916,7 @@ class InlineQueryResultVideo(InlineQueryResultBase): :param reply_markup: Optional. Inline keyboard attached to the message :type reply_markup: :class:`telebot.types.InlineKeyboardMarkup` - :param input_message_content: Optional. Content of the message to be sent instead of the video. This field is + :param input_message_content: Optional. Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). :type input_message_content: :class:`telebot.types.InputMessageContent` @@ -4927,7 +4931,7 @@ def __init__(self, id: str, video_url: str, mime_type: str, thumbnail_url: str, parse_mode: Optional[str] = None, video_width: Optional[int] = None, video_height: Optional[int] = None, video_duration: Optional[int] = None, description: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): - + 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) @@ -4983,11 +4987,11 @@ class InlineQueryResultAudio(InlineQueryResultBase): :param caption: Optional. Caption, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5009,7 +5013,7 @@ class InlineQueryResultAudio(InlineQueryResultBase): def __init__(self, id: str, audio_url: str, title: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, performer: Optional[str] = None, audio_duration: Optional[int] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = 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) @@ -5049,11 +5053,11 @@ class InlineQueryResultVoice(InlineQueryResultBase): :param caption: Optional. Caption, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the voice message caption. See formatting options for + :param parse_mode: Optional. Mode for parsing entities in the voice message caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5072,7 +5076,7 @@ class InlineQueryResultVoice(InlineQueryResultBase): def __init__(self, id: str, voice_url: str, title: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, voice_duration: Optional[int] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = 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) @@ -5106,11 +5110,11 @@ class InlineQueryResultDocument(InlineQueryResultBase): :param caption: Optional. Caption of the document to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5145,7 +5149,7 @@ def __init__(self, id: str, title: str, document_url: str, mime_type: str, capti parse_mode: Optional[str] = None, description: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = 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) @@ -5243,7 +5247,7 @@ def __init__(self, id: str, title: str, latitude: float, longitude: float, horiz reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = None, heading: Optional[int] = None, proximity_alert_radius: Optional[int] = None): - + super().__init__('location', id, title = title, input_message_content = input_message_content, reply_markup = reply_markup) self.latitude: float = latitude @@ -5320,7 +5324,7 @@ class InlineQueryResultVenue(InlineQueryResultBase): :param foursquare_id: Optional. Foursquare identifier of the venue if known :type foursquare_id: :obj:`str` - :param foursquare_type: Optional. Foursquare type of the venue, if known. (For example, + :param foursquare_type: Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) :type foursquare_type: :obj:`str` @@ -5446,7 +5450,7 @@ class InlineQueryResultContact(InlineQueryResultBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultContact` """ - + def __init__(self, id: str, phone_number: str, first_name: str, last_name: Optional[str] = None, vcard: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, thumbnail_url: Optional[str] = None, thumbnail_width: Optional[int] = None, thumbnail_height: Optional[int] = None): @@ -5591,11 +5595,11 @@ class InlineQueryResultCachedPhoto(InlineQueryResultCachedBase): :param caption: Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5657,7 +5661,7 @@ class InlineQueryResultCachedGif(InlineQueryResultCachedBase): :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5677,7 +5681,7 @@ def __init__(self, id: str, gif_file_id: str, title: Optional[str] = None, descr caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): - + InlineQueryResultCachedBase.__init__(self) self.type: str = 'gif' self.id: str = id @@ -5718,7 +5722,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResultCachedBase): :param parse_mode: Optional. Mode for parsing entities in the caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5738,7 +5742,7 @@ def __init__(self, id: str, mpeg4_file_id: str, title: Optional[str] = None, des caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): - + InlineQueryResultCachedBase.__init__(self) self.type: str = 'mpeg4_gif' self.id: str = id @@ -5815,11 +5819,11 @@ class InlineQueryResultCachedDocument(InlineQueryResultCachedBase): :param caption: Optional. Caption of the document to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5837,7 +5841,7 @@ def __init__(self, id: str, document_file_id: str, title: str, description: Opti caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None): - + InlineQueryResultCachedBase.__init__(self) self.type: str = 'document' self.id: str = id @@ -5877,11 +5881,11 @@ class InlineQueryResultCachedVideo(InlineQueryResultCachedBase): :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5902,7 +5906,7 @@ def __init__(self, id: str, video_file_id: str, title: str, description: Optiona caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None, show_caption_above_media: Optional[bool] = None): - + InlineQueryResultCachedBase.__init__(self) self.type: str = 'video' self.id: str = id @@ -5940,11 +5944,11 @@ class InlineQueryResultCachedVoice(InlineQueryResultCachedBase): :param caption: Optional. Caption, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the voice message caption. See formatting options for + :param parse_mode: Optional. Mode for parsing entities in the voice message caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -5957,7 +5961,7 @@ class InlineQueryResultCachedVoice(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedVoice` """ - + def __init__(self, id: str, voice_file_id: str, title: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None): @@ -5972,7 +5976,7 @@ def __init__(self, id: str, voice_file_id: str, title: str, caption: Optional[st self.input_message_content: Optional[InputMessageContent] = input_message_content self.parse_mode: Optional[str] = parse_mode self.payload_dic['voice_file_id'] = voice_file_id - + # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -5994,11 +5998,11 @@ class InlineQueryResultCachedAudio(InlineQueryResultCachedBase): :param caption: Optional. Caption, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -6011,7 +6015,7 @@ class InlineQueryResultCachedAudio(InlineQueryResultCachedBase): :return: Instance of the class :rtype: :class:`telebot.types.InlineQueryResultCachedAudio` """ - + def __init__(self, id: str, audio_file_id: str, caption: Optional[str] = None, caption_entities: Optional[List[MessageEntity]] = None, parse_mode: Optional[str] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, input_message_content: Optional[InputMessageContent] = None): @@ -6044,8 +6048,8 @@ class Game(JsonDeserializable): :param photo: Photo that will be displayed in the game message in chats. :type photo: :obj:`list` of :class:`telebot.types.PhotoSize` - :param text: Optional. Brief description of the game or high scores included in the game message. Can be - automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited + :param text: Optional. Brief description of the game or high scores included in the game message. Can be + automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters. :type text: :obj:`str` @@ -6107,7 +6111,7 @@ class Animation(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -6129,8 +6133,8 @@ class Animation(JsonDeserializable): :param mime_type: Optional. MIME type of the file as defined by sender :type mime_type: :obj:`str` - :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have - difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or + :param file_size: Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have + difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. :type file_size: :obj:`int` @@ -6147,7 +6151,7 @@ def de_json(cls, json_string): obj['thumbnail'] = None return cls(**obj) - def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, + def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None, thumbnail=None, file_name=None, mime_type=None, file_size=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id @@ -6207,8 +6211,8 @@ class LabeledPrice(JsonSerializable, Dictionaryable): :param label: Portion label :type label: :obj:`str` - :param amount: Price of the product in the smallest units of the currency (integer, not float/double). For example, - for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + :param amount: Price of the product in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). :type amount: :obj:`int` @@ -6246,8 +6250,8 @@ class Invoice(JsonDeserializable): :param currency: Three-letter ISO 4217 currency code :type currency: :obj:`str` - :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, - for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). :type total_amount: :obj:`int` @@ -6372,7 +6376,7 @@ def __init__(self, id, title): def add_price(self, *args) -> 'ShippingOption': """ Add LabeledPrice to ShippingOption - + :param args: LabeledPrices :type args: :obj:`LabeledPrice` @@ -6399,8 +6403,8 @@ class SuccessfulPayment(JsonDeserializable): :param currency: Three-letter ISO 4217 currency code :type currency: :obj:`str` - :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, - for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). :type total_amount: :obj:`int` @@ -6439,7 +6443,7 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, - telegram_payment_charge_id=None, provider_payment_charge_id=None, + telegram_payment_charge_id=None, provider_payment_charge_id=None, subscription_expiration_date=None, is_recurring=None, is_first_recurring=None, **kwargs): self.currency: str = currency self.total_amount: int = total_amount @@ -6506,8 +6510,8 @@ class PreCheckoutQuery(JsonDeserializable): :param currency: Three-letter ISO 4217 currency code :type currency: :obj:`str` - :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, - for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past + :param total_amount: Total price in the smallest units of the currency (integer, not float/double). For example, + for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). :type total_amount: :obj:`int` @@ -6619,7 +6623,7 @@ class Sticker(JsonDeserializable): :param file_id: Identifier for this file, which can be used to download or reuse the file :type file_id: :obj:`str` - :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different + :param file_unique_id: Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. :type file_unique_id: :obj:`str` @@ -6683,8 +6687,8 @@ def de_json(cls, json_string): obj['premium_animation'] = File.de_json(obj['premium_animation']) return cls(**obj) - def __init__(self, file_id, file_unique_id, type, width, height, is_animated, - is_video, thumbnail=None, emoji=None, set_name=None, mask_position=None, file_size=None, + def __init__(self, file_id, file_unique_id, type, width, height, is_animated, + is_video, thumbnail=None, emoji=None, set_name=None, mask_position=None, file_size=None, premium_animation=None, custom_emoji_id=None, needs_repainting=None, **kwargs): self.file_id: str = file_id self.file_unique_id: str = file_unique_id @@ -6714,15 +6718,15 @@ class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): Telegram Documentation: https://core.telegram.org/bots/api#maskposition - :param point: The part of the face relative to which the mask should be placed. One of “forehead”, “eyes”, “mouth”, or + :param point: The part of the face relative to which the mask should be placed. One of “forehead”, “eyes”, “mouth”, or “chin”. :type point: :obj:`str` - :param x_shift: Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, + :param x_shift: Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position. :type x_shift: :obj:`float` number - :param y_shift: Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For + :param y_shift: Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position. :type y_shift: :obj:`float` number @@ -6790,6 +6794,10 @@ def __init__(self, type, media, caption=None, parse_mode=None, caption_entities= self._media_name = service_utils.generate_random_token() self._media_dic = 'attach://{0}'.format(self._media_name) + if self.__class__ is InputMedia: + # Make InputMedia as ABC some time... + log_deprecation_warning('The InputMedia class should not be instantiated directly. Use particular InputMediaXXX class instead') + def to_json(self): return json.dumps(self.to_dict()) @@ -6811,7 +6819,7 @@ def convert_input_media(self): """ if service_utils.is_string(self.media): return self.to_json(), None - + media_dict = {self._media_name: self.media} if self._thumbnail_name: media_dict[self._thumbnail_name] = self.thumbnail @@ -6825,19 +6833,19 @@ class InputMediaPhoto(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediaphoto - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an - HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` :param caption: Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the photo caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -6855,7 +6863,7 @@ def __init__(self, media: Union[str, InputFile], caption: Optional[str] = None, has_spoiler: Optional[bool] = None, show_caption_above_media: Optional[bool] = None): if service_utils.is_pil_image(media): media = service_utils.pil_image_to_file(media) - + super(InputMediaPhoto, self).__init__( type="photo", media=media, caption=caption, parse_mode=parse_mode, caption_entities=caption_entities) @@ -6877,15 +6885,15 @@ class InputMediaVideo(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediavideo - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an - HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` - :param thumbnail: Optional. 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 + :param thumbnail: Optional. 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 . More information on Sending Files » :type thumbnail: InputFile or :obj:`str` @@ -6900,11 +6908,11 @@ class InputMediaVideo(InputMedia): :param caption: Optional. Caption of the video to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the video caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -6979,26 +6987,26 @@ class InputMediaAnimation(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediaanimation - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an - HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` :param thumbnail: Optional. 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 + 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 . More information on Sending Files » :type thumbnail: InputFile or :obj:`str` :param caption: Optional. Caption of the animation to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the animation caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the animation caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -7061,26 +7069,26 @@ class InputMediaAudio(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediaaudio - :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an - HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` - :param thumbnail: Optional. 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 + :param thumbnail: Optional. 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 . More information on Sending Files » :type thumbnail: InputFile or :obj:`str` :param caption: Optional. Caption of the audio to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the audio caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` @@ -7129,29 +7137,29 @@ class InputMediaDocument(InputMedia): Telegram Documentation: https://core.telegram.org/bots/api#inputmediadocument :param media: File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an - HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using + HTTP URL for Telegram to get a file from the Internet, or pass “attach://” to upload a new one using multipart/form-data under name. More information on Sending Files » :type media: :obj:`str` - :param thumbnail: Optional. 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 + :param thumbnail: Optional. 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 . More information on Sending Files » :type thumbnail: InputFile or :obj:`str` :param caption: Optional. Caption of the document to be sent, 0-1024 characters after entities parsing :type caption: :obj:`str` - :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more + :param parse_mode: Optional. Mode for parsing entities in the document caption. See formatting options for more details. :type parse_mode: :obj:`str` - :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified + :param caption_entities: Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode :type caption_entities: :obj:`list` of :class:`telebot.types.MessageEntity` - :param disable_content_type_detection: Optional. Disables automatic server-side content type detection for + :param disable_content_type_detection: Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always True, if the document is sent as part of an album. :type disable_content_type_detection: :obj:`bool` @@ -7374,8 +7382,7 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable): :param user: Optional. The user, who changed the answer to the poll :type user: :class:`telebot.types.User` - :param option_ids: 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted - their vote. + :param option_ids: 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote. :type option_ids: :obj:`list` of :obj:`int` :return: Instance of the class @@ -7394,7 +7401,7 @@ def de_json(cls, json_string): def __init__(self, poll_id: str, option_ids: List[int], user: Optional[User] = None, voter_chat: Optional[Chat] = None, **kwargs): self.poll_id: str = poll_id self.user: Optional[User] = user - self.option_ids: Optional[List[int]] = option_ids + self.option_ids: [List[int]] = option_ids self.voter_chat: Optional[Chat] = voter_chat @@ -7412,7 +7419,7 @@ def to_dict(self): if self.voter_chat: json_dict["voter_chat"] = self.voter_chat return json_dict - + class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable): @@ -7436,14 +7443,14 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['location'] = Location.de_json(obj['location']) return cls(**obj) - + def __init__(self, location: Location, address: str, **kwargs): self.location: Location = location self.address: str = address def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): return { "location": self.location.to_dict(), @@ -7457,7 +7464,7 @@ class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): Telegram Documentation: https://core.telegram.org/bots/api#chatinvitelink - :param invite_link: The invite link. If the link was created by another chat administrator, then the second part of + :param invite_link: The invite link. If the link was created by another chat administrator, then the second part of the link will be replaced with “…”. :type invite_link: :obj:`str` @@ -7479,7 +7486,7 @@ class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable): :param expire_date: Optional. Point in time (Unix timestamp) when the link will expire or has been expired :type expire_date: :obj:`int` - :param member_limit: Optional. The maximum number of users that can be members of the chat simultaneously after + :param member_limit: Optional. The maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999 :type member_limit: :obj:`int` @@ -7495,7 +7502,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['creator'] = User.de_json(obj['creator']) return cls(**obj) - + def __init__(self, invite_link: str, creator: User, creates_join_request: bool, is_primary: bool, is_revoked: bool, name: Optional[str] = None, expire_date: Optional[int] = None, member_limit: Optional[int] = None, pending_join_request_count: Optional[int] = None, **kwargs): @@ -7508,10 +7515,10 @@ def __init__(self, invite_link: str, creator: User, creates_join_request: bool, self.expire_date: Optional[int] = expire_date self.member_limit: Optional[int] = member_limit self.pending_join_request_count: Optional[int] = pending_join_request_count - + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): json_dict = { "invite_link": self.invite_link, @@ -7554,7 +7561,7 @@ 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) - + def __init__(self, traveler, watcher, distance, **kwargs): self.traveler: User = traveler self.watcher: User = watcher @@ -7568,7 +7575,7 @@ class VideoChatStarted(JsonDeserializable): @classmethod def de_json(cls, json_string): return cls() - + def __init__(self): pass @@ -7588,7 +7595,7 @@ class VideoChatScheduled(JsonDeserializable): Telegram Documentation: https://core.telegram.org/bots/api#videochatscheduled - :param start_date: Point in time (Unix timestamp) when the video chat is supposed to be started by a chat + :param start_date: Point in time (Unix timestamp) when the video chat is supposed to be started by a chat administrator :type start_date: :obj:`int` @@ -7600,7 +7607,7 @@ 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) - + def __init__(self, start_date, **kwargs): self.start_date: int = start_date @@ -7631,7 +7638,7 @@ 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) - + def __init__(self, duration, **kwargs): self.duration: int = duration @@ -7664,7 +7671,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['users'] = [User.de_json(u) for u in obj['users']] return cls(**obj) - + def __init__(self, users=None, **kwargs): self.users: List[User] = users @@ -7689,7 +7696,7 @@ class MessageAutoDeleteTimerChanged(JsonDeserializable): :return: Instance of the class :rtype: :class:`telebot.types.MessageAutoDeleteTimerChanged` - """ + """ @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -7707,7 +7714,7 @@ class MenuButton(JsonDeserializable, JsonSerializable, Dictionaryable): * :class:`MenuButtonCommands` * :class:`MenuButtonWebApp` * :class:`MenuButtonDefault` - + If a menu button other than MenuButtonDefault is set for a private chat, then it is applied in the chat. Otherwise the default menu button is applied. By default, the menu button opens the list of bot commands. """ @@ -7721,7 +7728,7 @@ def de_json(cls, json_string): 'default': MenuButtonDefault } return types[obj['type']](**obj) - + def to_json(self): """ :meta private: @@ -7754,7 +7761,7 @@ def __init__(self, type: str = None, **kwargs): def to_dict(self): return {'type': self.type} - + def to_json(self): return json.dumps(self.to_dict()) @@ -7772,7 +7779,7 @@ class MenuButtonWebApp(MenuButton): :param text: Text on the button :type text: :obj:`str` - :param web_app: Description of the Web App that will be launched when the user presses the button. The Web App will be + :param web_app: Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Alternatively, a t.me link to a Web App of the bot can be specified in the object instead of the Web App's URL, in which case the Web App will be opened as if the user pressed the link. @@ -7816,7 +7823,7 @@ def to_dict(self): def to_json(self): return json.dumps(self.to_dict()) - + class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryable): """ Represents the rights of an administrator in a chat. @@ -7826,8 +7833,8 @@ class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryab :param is_anonymous: True, if the user's presence in the chat is hidden :type is_anonymous: :obj:`bool` - :param can_manage_chat: True, if the administrator can access the chat event log, chat statistics, message - statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. + :param can_manage_chat: True, if the administrator can access the chat event log, chat statistics, message + statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege :type can_manage_chat: :obj:`bool` @@ -7840,8 +7847,8 @@ class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryab :param can_restrict_members: True, if the administrator can restrict, ban or unban chat members :type can_restrict_members: :obj:`bool` - :param can_promote_members: True, if the administrator can add new administrators with a subset of their own - privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that + :param can_promote_members: True, if the administrator can add new administrators with a subset of their own + privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user) :type can_promote_members: :obj:`bool` @@ -7854,7 +7861,7 @@ class ChatAdministratorRights(JsonDeserializable, JsonSerializable, Dictionaryab :param can_post_messages: Optional. True, if the administrator can post in the channel; channels only :type can_post_messages: :obj:`bool` - :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin + :param can_edit_messages: Optional. True, if the administrator can edit messages of other users and can pin messages; channels only :type can_edit_messages: :obj:`bool` @@ -7883,7 +7890,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, is_anonymous: bool, can_manage_chat: bool, + def __init__(self, is_anonymous: bool, can_manage_chat: bool, can_delete_messages: bool, can_manage_video_chats: bool, can_restrict_members: bool, can_promote_members: bool, can_change_info: bool, can_invite_users: bool, can_post_messages: Optional[bool]=None, can_edit_messages: Optional[bool]=None, @@ -7891,7 +7898,7 @@ def __init__(self, is_anonymous: bool, can_manage_chat: bool, can_post_stories: Optional[bool]=None, can_edit_stories: Optional[bool]=None, can_delete_stories: Optional[bool]=None, **kwargs ) -> None: - + self.is_anonymous: bool = is_anonymous self.can_manage_chat: bool = can_manage_chat self.can_delete_messages: bool = can_delete_messages @@ -7935,17 +7942,17 @@ def to_dict(self): json_dict['can_delete_stories'] = self.can_delete_stories return json_dict - + def to_json(self): return json.dumps(self.to_dict()) - + class InputFile: """ A class to send files through Telegram Bot API. - You need to pass a file, which should be an instance of :class:`io.IOBase` or + You need to pass a file, which should be an instance of :class:`io.IOBase` or :class:`pathlib.Path`, or :obj:`str`. If you pass an :obj:`str` as a file, it will be opened and closed by the class. @@ -7982,7 +7989,7 @@ def __init__(self, file: Union[str, IOBase, Path], file_name: Optional[str] = No self._file, self._file_name = self._resolve_file(file) if file_name: self._file_name = file_name - + @staticmethod def _resolve_file(file): @@ -8003,7 +8010,7 @@ def file(self) -> Union[IOBase, str]: File object. """ return self._file - + @property def file_name(self) -> str: """ @@ -8015,7 +8022,7 @@ def file_name(self) -> str: class ForumTopicCreated(JsonDeserializable): """ This object represents a service message about a new forum topic created in the chat. - + Telegram documentation: https://core.telegram.org/bots/api#forumtopiccreated :param name: Name of the topic @@ -8045,7 +8052,7 @@ def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[st class ForumTopicClosed(JsonDeserializable): """ This object represents a service message about a forum topic closed in the chat. Currently holds no information. - + Telegram documentation: https://core.telegram.org/bots/api#forumtopicclosed """ # for future use @@ -8117,7 +8124,7 @@ class GeneralForumTopicUnhidden(JsonDeserializable): Telegram documentation: https://core.telegram.org/bots/api#generalforumtopicunhidden """ - + @classmethod def de_json(cls, json_string): return cls() @@ -8186,7 +8193,7 @@ 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, from_request: Optional[bool]=None, web_app_name: Optional[str]=None, from_attachment_menu: Optional[bool]=None, **kwargs) -> None: @@ -8292,7 +8299,7 @@ class InputSticker(Dictionaryable, JsonSerializable): :param sticker: The added sticker. Pass a file_id as a String to send a file that already exists on the Telegram servers, 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. - Animated and video stickers can't be uploaded via HTTP URL. + Animated and video stickers can't be uploaded via HTTP URL. :type sticker: :obj:`str` or :obj:`telebot.types.InputFile` :param emoji_list: One or more(up to 20) emoji(s) corresponding to the sticker @@ -8300,7 +8307,7 @@ class InputSticker(Dictionaryable, JsonSerializable): :param mask_position: Optional. Position where the mask should be placed on faces. For “mask” stickers only. :type mask_position: :class:`telebot.types.MaskPosition` - + :param keywords: Optional. List of 0-20 search keywords for the sticker with total length of up to 64 characters. For “regular” and “custom_emoji” stickers only. :type keywords: :obj:`list` of :obj:`str` @@ -8347,18 +8354,18 @@ def to_dict(self) -> dict: json_dict['keywords'] = self.keywords return json_dict - + def to_json(self) -> str: return json.dumps(self.to_dict()) - + def convert_input_sticker(self) -> Tuple[str, Optional[dict]]: if service_utils.is_string(self.sticker): return self.to_json(), None return self.to_json(), {self._sticker_name: self.sticker} - - - + + + class SwitchInlineQueryChosenChat(JsonDeserializable, Dictionaryable, JsonSerializable): """ Represents an inline button that switches the current user to inline mode in a chosen chat, @@ -8457,7 +8464,7 @@ class InlineQueryResultsButton(JsonSerializable, Dictionaryable): :param web_app: Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method web_app_switch_inline_query inside the Web App. :type web_app: :class:`telebot.types.WebAppInfo` - + :param start_parameter: Optional. Deep-linking parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed. Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search @@ -8487,7 +8494,7 @@ def to_dict(self) -> dict: json_dict['start_parameter'] = self.start_parameter return json_dict - + def to_json(self) -> str: return json.dumps(self.to_dict()) @@ -8515,7 +8522,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) return cls(**obj) - + def __init__(self, chat: Chat, id: int, **kwargs) -> None: self.chat: Chat = chat self.id: int = id @@ -8540,23 +8547,30 @@ class ReactionType(JsonDeserializable, Dictionaryable, JsonSerializable): def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) - # remove type + # remove type if obj['type'] == 'emoji': del obj['type'] return ReactionTypeEmoji(**obj) elif obj['type'] == 'custom_emoji': del obj['type'] return ReactionTypeCustomEmoji(**obj) + elif obj['type'] == 'paid': + del obj['type'] + return ReactionTypePaid(**obj) + else: + raise ValueError(f"Unknown reaction type: {obj['type']}.") def __init__(self, type: str) -> None: self.type: str = type + if self.__class__ is ReactionType: + log_deprecation_warning('The ReactionType class should not be instantiated directly. Use particular ReactionTypeXXX class instead') def to_dict(self) -> dict: json_dict = { 'type': self.type } return json_dict - + def to_json(self) -> str: return json.dumps(self.to_dict()) @@ -8633,7 +8647,7 @@ def __init__(self, **kwargs) -> None: def to_dict(self) -> dict: return super().to_dict() - + class MessageReactionUpdated(JsonDeserializable): @@ -8753,7 +8767,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['type'] = ReactionType.de_json(obj['type']) return cls(**obj) - + def __init__(self, type: ReactionType, total_count: int, **kwargs) -> None: self.type: ReactionType = type self.total_count: int = total_count @@ -8935,7 +8949,7 @@ def __init__( # noinspection PyUnresolvedReferences,PyShadowingBuiltins -class MessageOrigin(JsonDeserializable): +class MessageOrigin(JsonDeserializable, ABC): """ This object describes the origin of a message. @@ -8979,6 +8993,8 @@ def de_json(cls, json_string): elif message_type == 'channel': chat = Chat.de_json(obj['chat']) return MessageOriginChannel(date=obj['date'], chat=chat, message_id=obj['message_id'], author_signature=obj.get('author_signature')) + else: + raise ValueError(f"Unknown message origin type: {message_type}.") def __init__(self, type: str, date: int) -> None: self.type: str = type @@ -9165,7 +9181,7 @@ def __init__(self, chats: List[Chat], winners_selection_date: int, winner_count: self.country_codes: Optional[List[str]] = country_codes self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count self.prize_star_count: Optional[int] = prize_star_count - + class GiveawayWinners(JsonDeserializable): """ @@ -9220,7 +9236,7 @@ def de_json(cls, json_string): obj['chat'] = Chat.de_json(obj['chat']) obj['winners'] = [User.de_json(user) for user in obj['winners']] return cls(**obj) - + def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: int, winner_count: int, winners: List[User], additional_chat_count: Optional[int] = None, premium_subscription_month_count: Optional[int] = None, unclaimed_prize_count: Optional[int] = None, @@ -9238,8 +9254,8 @@ def __init__(self, chat: Chat, giveaway_message_id: int, winners_selection_date: self.was_refunded: Optional[bool] = was_refunded self.prize_description: Optional[str] = prize_description self.prize_star_count: Optional[int] = prize_star_count - - + + class GiveawayCompleted(JsonDeserializable): """ This object represents a service message about the completion of a giveaway without public winners. @@ -9269,14 +9285,14 @@ def de_json(cls, json_string): if 'giveaway_message' in obj: obj['giveaway_message'] = Message.de_json(obj['giveaway_message']) return cls(**obj) - + def __init__(self, winner_count: int, unclaimed_prize_count: Optional[int] = None, giveaway_message: Optional[Message] = None, is_star_giveaway: Optional[bool] = None, **kwargs) -> None: self.winner_count: int = winner_count self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count self.giveaway_message: Optional[Message] = giveaway_message self.is_star_giveaway: Optional[bool] = is_star_giveaway - + class GiveawayCreated(JsonDeserializable): """ @@ -9383,7 +9399,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'quote_entities' in obj: obj['quote_entities'] = [MessageEntity.de_json(entity) for entity in obj['quote_entities']] - return cls(**obj) + return cls(**obj) def __init__(self, message_id: int, chat_id: Optional[Union[int, str]] = None, allow_sending_without_reply: Optional[bool] = None, quote: Optional[str] = None, @@ -9414,11 +9430,11 @@ def to_dict(self) -> dict: if self.quote_position is not None: json_dict['quote_position'] = self.quote_position return json_dict - + def to_json(self) -> str: return json.dumps(self.to_dict()) - - + + class UsersShared(JsonDeserializable): """ This object contains information about the users whose identifiers were shared with the bot @@ -9476,7 +9492,7 @@ class ChatBoostUpdated(JsonDeserializable): @classmethod def de_json(cls, json_string): if json_string is None: return None - obj = cls.check_json(json_string) + obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) obj['boost'] = ChatBoost.de_json(obj['boost']) return cls(**obj) @@ -9484,8 +9500,8 @@ def de_json(cls, json_string): def __init__(self, chat, boost, **kwargs): self.chat: Chat = chat self.boost: ChatBoost = boost - - + + class ChatBoostRemoved(JsonDeserializable): """ This object represents a boost removed from a chat. @@ -9513,7 +9529,7 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) - obj['source'] = ChatBoostSource.de_json(obj['source']) + obj['source'] = ChatBoostSource.de_json(obj['source']) return cls(**obj) def __init__(self, chat, boost_id, remove_date, source, **kwargs): @@ -9521,8 +9537,8 @@ def __init__(self, chat, boost_id, remove_date, source, **kwargs): self.boost_id: str = boost_id self.remove_date: int = remove_date self.source: ChatBoostSource = source - - + + class ChatBoostSource(ABC, JsonDeserializable): """ This object describes the source of a chat boost. It can be one of @@ -9695,7 +9711,7 @@ def __init__(self, boost_id, add_date, expiration_date, source, **kwargs): self.add_date: int = add_date self.expiration_date: int = expiration_date self.source: ChatBoostSource = source - + class UserChatBoosts(JsonDeserializable): """ @@ -9719,7 +9735,7 @@ def de_json(cls, json_string): def __init__(self, boosts, **kwargs): self.boosts: List[ChatBoost] = boosts - + class InaccessibleMessage(JsonDeserializable): """ @@ -9796,7 +9812,7 @@ 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, boost_count, **kwargs): self.boost_count: int = boost_count @@ -9840,7 +9856,7 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) obj['rights'] = BusinessBotRights.de_json(obj.get('rights')) return cls(**obj) - + def __init__(self, id, user, user_chat_id, date, can_reply, is_enabled, rights=None, **kwargs): self.id: str = id @@ -9885,13 +9901,13 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['chat'] = Chat.de_json(obj['chat']) return cls(**obj) - + def __init__(self, business_connection_id, chat, message_ids, **kwargs): self.business_connection_id: str = business_connection_id self.chat: Chat = chat self.message_ids: List[int] = message_ids - + class BusinessIntro(JsonDeserializable): """ @@ -9919,7 +9935,7 @@ def de_json(cls, json_string): if 'sticker' in obj: obj['sticker'] = Sticker.de_json(obj['sticker']) return cls(**obj) - + def __init__(self, title=None, message=None, sticker=None, **kwargs): self.title: Optional[str] = title self.message: Optional[str] = message @@ -9949,7 +9965,7 @@ def de_json(cls, json_string): if 'location' in obj: obj['location'] = Location.de_json(obj['location']) return cls(**obj) - + def __init__(self, address, location=None, **kwargs): self.address: str = address self.location: Optional[Location] = location @@ -9976,7 +9992,7 @@ 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, opening_minute, closing_minute, **kwargs): self.opening_minute: int = opening_minute self.closing_minute: int = closing_minute @@ -10006,7 +10022,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['opening_hours'] = [BusinessOpeningHoursInterval.de_json(interval) for interval in obj['opening_hours']] return cls(**obj) - + def __init__(self, time_zone_name, opening_hours, **kwargs): self.time_zone_name: str = time_zone_name self.opening_hours: List[BusinessOpeningHoursInterval] = opening_hours @@ -10044,7 +10060,7 @@ def de_json(cls, json_string): if 'photo' in obj: obj['photo'] = [PhotoSize.de_json(photo) for photo in obj['photo']] return cls(**obj) - + def __init__(self, user_id, first_name=None, last_name=None, username=None, photo=None, **kwargs): self.user_id: int = user_id self.first_name: Optional[str] = first_name @@ -10077,7 +10093,7 @@ 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, day, month, year=None, **kwargs): self.day: int = day self.month: int = month @@ -10513,13 +10529,17 @@ def de_json(cls, json_string): return cls(**obj) -class TransactionPartner(JsonDeserializable): +class TransactionPartner(JsonDeserializable, ABC): # noinspection PyUnresolvedReferences """ This object describes the source of a transaction, or its recipient for outgoing transactions. Currently, it can be one of TransactionPartnerFragment TransactionPartnerUser TransactionPartnerOther + TransactionPartnerTelegramAds + TransactionPartnerTelegramApi + TransactionPartnerAffiliateProgram + TransactionPartnerChat Telegram documentation: https://core.telegram.org/bots/api#transactionpartner @@ -10548,6 +10568,8 @@ def de_json(cls, json_string): return TransactionPartnerOther.de_json(obj) elif obj["type"] == "chat": return TransactionPartnerChat.de_json(obj) + else: + raise ValueError(f"Unknown transaction partner type: {obj['type']}") # noinspection PyShadowingBuiltins @@ -10581,6 +10603,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class TransactionPartnerTelegramApi(TransactionPartner): """ Describes a transaction with payment for paid broadcasting. @@ -10648,8 +10671,8 @@ class TransactionPartnerUser(TransactionPartner): :rtype: :class:`TransactionPartnerUser` """ - def __init__(self, type, user, affiliate=None, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, - subscription_period=None, gift: Optional[Gift] = None, premium_subscription_duration: Optional[int] = None, + def __init__(self, type, user, affiliate=None, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, + subscription_period=None, gift: Optional[Gift] = None, premium_subscription_duration: Optional[int] = None, transaction_type: Optional[str] = None, **kwargs): self.type: str = type self.user: User = user @@ -10681,7 +10704,7 @@ class TransactionPartnerTelegramAds(TransactionPartner): Describes a transaction with Telegram Ads. Telegram documentation: https://core.telegram.org/bots/api#transactionpartnertelegramads - + :param type: Type of the transaction partner, always “telegram_ads” :type type: :obj:`str` @@ -10761,7 +10784,7 @@ def de_json(cls, json_string): if 'receiver' in obj: obj['receiver'] = TransactionPartner.de_json(obj['receiver']) return cls(**obj) - + def __init__(self, id, amount, date, source=None, receiver=None, nanostar_amount=None, **kwargs): self.id: str = id self.amount: int = amount @@ -10791,12 +10814,12 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['transactions'] = [StarTransaction.de_json(transaction) for transaction in obj['transactions']] return cls(**obj) - + def __init__(self, transactions, **kwargs): self.transactions: List[StarTransaction] = transactions -class PaidMedia(JsonDeserializable): +class PaidMedia(JsonDeserializable, ABC): """ This object describes paid media. Currently, it can be one of @@ -10820,6 +10843,8 @@ def de_json(cls, json_string): return PaidMediaPhoto.de_json(obj) elif obj["type"] == "video": return PaidMediaVideo.de_json(obj) + else: + raise ValueError("Unknown type of PaidMedia: {0}".format(obj["type"])) # noinspection PyShadowingBuiltins @@ -10940,14 +10965,14 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['paid_media'] = [PaidMedia.de_json(media) for media in obj['paid_media']] return cls(**obj) - + def __init__(self, star_count, paid_media, **kwargs): self.star_count: int = star_count self.paid_media: List[PaidMedia] = paid_media # noinspection PyShadowingBuiltins -class InputPaidMedia(JsonSerializable): +class InputPaidMedia(Dictionaryable, JsonSerializable): """ This object describes the paid media to be sent. Currently, it can be one of InputPaidMediaPhoto @@ -10970,16 +10995,21 @@ def __init__(self, type: str, media: Union[str, InputFile], **kwargs): self._media_name = service_utils.generate_random_token() self._media_dic = 'attach://{0}'.format(self._media_name) + if self.__class__ is InputPaidMedia: + # Make InputPaidMedia as ABC some time... + log_deprecation_warning('The InputPaidMedia class should not be instantiated directly. Use particular InputPaidMediaXXX class instead') + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'type': self.type, 'media': self._media_dic } return data - + + class InputPaidMediaPhoto(InputPaidMedia): """ The paid media to send is a photo. @@ -11000,7 +11030,8 @@ class InputPaidMediaPhoto(InputPaidMedia): def __init__(self, media: Union[str, InputFile], **kwargs): super().__init__(type='photo', media=media) - + + class InputPaidMediaVideo(InputPaidMedia): """ The paid media to send is a video. @@ -11058,8 +11089,6 @@ def __init__(self, media: Union[str, InputFile], thumbnail: Optional[InputFile] self.cover: Optional[Union[str,InputFile]] = cover self.start_timestamp: Optional[int] = start_timestamp - - def to_dict(self): data = super().to_dict() if self.thumbnail: @@ -11078,6 +11107,7 @@ def to_dict(self): data['start_timestamp'] = self.start_timestamp return data + class RefundedPayment(JsonDeserializable): """ This object contains basic information about a refunded payment. @@ -11115,8 +11145,8 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - - + + class PaidMediaPurchased(JsonDeserializable): """ This object contains information about a paid media purchase. @@ -11143,7 +11173,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['from_user'] = User.de_json(obj['from_user']) return cls(**obj) - + class CopyTextButton(JsonSerializable, JsonDeserializable): """ @@ -11162,13 +11192,13 @@ def __init__(self, text: str, **kwargs): def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'text': self.text } return data - + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11176,6 +11206,7 @@ def de_json(cls, json_string): return cls(**obj) +# noinspection PyShadowingBuiltins class PreparedInlineMessage(JsonDeserializable): """ Describes an inline message to be sent by a user of a Mini App. @@ -11201,8 +11232,9 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - + +# noinspection PyShadowingBuiltins class Gift(JsonDeserializable): """ This object represents a gift that can be sent by the bot. @@ -11245,7 +11277,8 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) return cls(**obj) - + + class Gifts(JsonDeserializable): """ This object represent a list of gifts. @@ -11268,8 +11301,9 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['gifts'] = [Gift.de_json(gift) for gift in obj['gifts']] return cls(**obj) - - + + +# noinspection PyShadowingBuiltins class TransactionPartnerAffiliateProgram(TransactionPartner): """ Describes the affiliate program that issued the affiliate commission received via this transaction. @@ -11302,7 +11336,7 @@ def de_json(cls, json_string): obj['sponsor_user'] = User.de_json(obj['sponsor_user']) return cls(**obj) - + class AffiliateInfo(JsonDeserializable): """ @@ -11345,8 +11379,9 @@ def de_json(cls, json_string): if 'affiliate_chat' in obj: obj['affiliate_chat'] = Chat.de_json(obj['affiliate_chat']) return cls(**obj) - + +# noinspection PyShadowingBuiltins class TransactionPartnerChat(TransactionPartner): """ Describes a transaction with a chat. @@ -11379,7 +11414,7 @@ def de_json(cls, json_string): if 'gift' in obj: obj['gift'] = Gift.de_json(obj['gift']) return cls(**obj) - + class BusinessBotRights(JsonDeserializable): """ @@ -11430,10 +11465,10 @@ class BusinessBotRights(JsonDeserializable): :type can_manage_stories: :obj:`bool` :return: Instance of the class - :rtype: :class:`BusinessBotRights` + :rtype: :class:`BusinessBotRights` """ def __init__(self, can_reply=None, can_read_messages=None, can_delete_outgoing_messages=None, can_delete_all_messages=None, - can_edit_name=None, can_edit_bio=None, can_edit_profile_photo=None, can_edit_username=None, + can_edit_name=None, can_edit_bio=None, can_edit_profile_photo=None, can_edit_username=None, can_change_gift_settings=None, can_view_gifts_and_stars=None, can_convert_gifts_to_stars=None, can_transfer_and_upgrade_gifts=None, can_transfer_stars=None, can_manage_stories=None, **kwargs): self.can_reply: Optional[bool] = can_reply @@ -11450,7 +11485,7 @@ def __init__(self, can_reply=None, can_read_messages=None, can_delete_outgoing_m self.can_transfer_and_upgrade_gifts: Optional[bool] = can_transfer_and_upgrade_gifts self.can_transfer_stars: Optional[bool] = can_transfer_stars self.can_manage_stories: Optional[bool] = can_manage_stories - + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11472,7 +11507,7 @@ class AcceptedGiftTypes(JsonDeserializable, JsonSerializable): :param unique_gifts: True, if unique gifts or gifts that can be upgraded to unique for free are accepted :type unique_gifts: :obj:`bool` - + :param premium_subscription: True, if a Telegram Premium subscription is accepted :type premium_subscription: :obj:`bool` @@ -11485,10 +11520,10 @@ def __init__(self, unlimited_gifts: bool, limited_gifts: bool, self.limited_gifts: bool = limited_gifts self.unique_gifts: bool = unique_gifts self.premium_subscription: bool = premium_subscription - + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'unlimited_gifts': self.unlimited_gifts, @@ -11497,6 +11532,7 @@ def to_dict(self): 'premium_subscription': self.premium_subscription } return data + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11522,13 +11558,15 @@ class StarAmount(JsonDeserializable): def __init__(self, amount, nanostar_amount=None, **kwargs): self.amount: int = amount self.nanostar_amount: Optional[int] = nanostar_amount + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - + +# noinspection PyShadowingBuiltins class OwnedGift(JsonDeserializable, ABC): """ This object describes a gift received and owned by a user or a chat. Currently, it can be one of @@ -11540,9 +11578,8 @@ class OwnedGift(JsonDeserializable, ABC): def __init__(self, type, **kwargs): self.type: str = type - self.gift: Union[Gift, UniqueGift] = None - - + self.gift: Optional[Union[Gift, UniqueGift]] = None + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11551,7 +11588,11 @@ def de_json(cls, json_string): return OwnedGiftRegular.de_json(obj) elif obj["type"] == "unique": return OwnedGiftUnique.de_json(obj) - + else: + raise ValueError(f"Unknown gift type: {obj['type']}.") + + +# noinspection PyShadowingBuiltins class OwnedGiftRegular(OwnedGift): """ This object describes a regular gift owned by a user or a chat. @@ -11572,7 +11613,7 @@ class OwnedGiftRegular(OwnedGift): :param send_date: Date the gift was sent in Unix time :type send_date: :obj:`int` - + :param text: Optional. Text of the message that was added to the gift :type text: :obj:`str` @@ -11616,6 +11657,7 @@ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=N self.was_refunded: Optional[bool] = was_refunded self.convert_star_count: Optional[int] = convert_star_count self.prepaid_upgrade_star_count: Optional[int] = prepaid_upgrade_star_count + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11626,7 +11668,9 @@ def de_json(cls, json_string): if 'entities' in obj: obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] return cls(**obj) - + + +# noinspection PyShadowingBuiltins class OwnedGiftUnique(OwnedGift): """ This object describes a unique gift owned by a user or a chat. @@ -11674,6 +11718,7 @@ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=N self.can_be_transferred: Optional[bool] = can_be_transferred self.transfer_star_count: Optional[int] = transfer_star_count self.next_transfer_date: Optional[int] = next_transfer_date + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11682,7 +11727,7 @@ def de_json(cls, json_string): if 'sender_user' in obj: obj['sender_user'] = User.de_json(obj['sender_user']) return cls(**obj) - + class OwnedGifts(JsonDeserializable): """ @@ -11707,6 +11752,7 @@ def __init__(self, total_count, gifts, next_offset=None, **kwargs): self.total_count: int = total_count self.gifts: List[OwnedGift] = gifts self.next_offset: Optional[str] = next_offset + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11715,7 +11761,6 @@ def de_json(cls, json_string): return cls(**obj) - class UniqueGift(JsonDeserializable): """ This object describes a unique gift that was upgraded from a regular gift. @@ -11750,6 +11795,7 @@ def __init__(self, base_name, name, number, model, symbol, backdrop, **kwargs): self.model: UniqueGiftModel = model self.symbol: UniqueGiftSymbol = symbol self.backdrop: UniqueGiftBackdrop = backdrop + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11758,8 +11804,8 @@ def de_json(cls, json_string): obj['symbol'] = UniqueGiftSymbol.de_json(obj['symbol']) obj['backdrop'] = UniqueGiftBackdrop.de_json(obj['backdrop']) return cls(**obj) - - + + class UniqueGiftModel(JsonDeserializable): """ This object describes the model of a unique gift. @@ -11783,13 +11829,15 @@ def __init__(self, name, sticker, rarity_per_mille, **kwargs): self.name: str = name self.sticker: Sticker = sticker self.rarity_per_mille: int = rarity_per_mille + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) return cls(**obj) - + + class UniqueGiftSymbol(JsonDeserializable): """ This object describes the symbol shown on the pattern of a unique gift. @@ -11813,13 +11861,15 @@ def __init__(self, name, sticker, rarity_per_mille, **kwargs): self.name: str = name self.sticker: Sticker = sticker self.rarity_per_mille: int = rarity_per_mille + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) return cls(**obj) - + + class UniqueGiftBackdropColors(JsonDeserializable): """ This object describes the colors of the backdrop of a unique gift. @@ -11846,12 +11896,14 @@ def __init__(self, center_color, edge_color, symbol_color, text_color, **kwargs) self.edge_color: int = edge_color self.symbol_color: int = symbol_color self.text_color: int = text_color + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - + + class UniqueGiftBackdrop(JsonDeserializable): """ This object describes the backdrop of a unique gift. @@ -11874,6 +11926,7 @@ def __init__(self, name, colors, rarity_per_mille, **kwargs): self.name: str = name self.colors: UniqueGiftBackdropColors = colors self.rarity_per_mille: int = rarity_per_mille + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -11881,6 +11934,8 @@ def de_json(cls, json_string): obj['colors'] = UniqueGiftBackdropColors.de_json(obj['colors']) return cls(**obj) + +# noinspection PyShadowingBuiltins class InputStoryContent(JsonSerializable, ABC): """ This object describes the content of a story to post. Currently, it can be one of @@ -11888,7 +11943,6 @@ class InputStoryContent(JsonSerializable, ABC): InputStoryContentVideo Telegram documentation: https://core.telegram.org/bots/api#inputstorycontent - """ def __init__(self, type: str, **kwargs): self.type: str = type @@ -11911,20 +11965,20 @@ def __init__(self, photo: InputFile, **kwargs): self.photo: InputFile = photo self._photo_name = service_utils.generate_random_token() self._photo_dic = "attach://{}".format(self._photo_name) - + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'type': self.type, 'photo': self._photo_dic } return data - + def convert_input_story(self): return self.to_json(), {self._photo_name: self.photo} - + class InputStoryContentVideo(InputStoryContent): """ @@ -11956,9 +12010,10 @@ def __init__(self, video: InputFile, duration: Optional[float] = None, cover_fra self.duration: Optional[float] = duration self.cover_frame_timestamp: Optional[float] = cover_frame_timestamp self.is_animation: Optional[bool] = is_animation + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'type': self.type, @@ -11971,9 +12026,10 @@ def to_dict(self): if self.is_animation is not None: data['is_animation'] = self.is_animation return data + def convert_input_story(self): return self.to_json(), {self._video_name: self.video} - + class StoryAreaPosition(JsonSerializable): """ @@ -12010,8 +12066,10 @@ def __init__(self, x_percentage: float, y_percentage: float, width_percentage: f self.height_percentage: float = height_percentage self.rotation_angle: float = rotation_angle self.corner_radius_percentage: float = corner_radius_percentage + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'x_percentage': self.x_percentage, @@ -12022,7 +12080,7 @@ def to_dict(self): 'corner_radius_percentage': self.corner_radius_percentage } return data - + class LocationAddress(JsonSerializable): """ @@ -12051,8 +12109,10 @@ def __init__(self, country_code: str, state: Optional[str] = None, city: Optiona self.state: Optional[str] = state self.city: Optional[str] = city self.street: Optional[str] = street + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'country_code': self.country_code @@ -12064,7 +12124,9 @@ def to_dict(self): if self.street is not None: data['street'] = self.street return data - + + +# noinspection PyShadowingBuiltins class StoryAreaType(JsonSerializable, ABC): """ Describes the type of a clickable area on a story. Currently, it can be one of @@ -12109,8 +12171,10 @@ def __init__(self,latitude: float, longitude: float, address: LocationAddress = self.latitude: float = latitude self.longitude: float = longitude self.address: Optional[LocationAddress] = address + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12120,7 +12184,7 @@ def to_dict(self): if self.address is not None: data['address'] = self.address.to_dict() return data - + class StoryAreaTypeSuggestedReaction(StoryAreaType): """ @@ -12148,8 +12212,10 @@ def __init__(self, reaction_type: ReactionType, is_dark: Optional[bool] = None, self.reaction_type: ReactionType = reaction_type self.is_dark: Optional[bool] = is_dark self.is_flipped: Optional[bool] = is_flipped + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12160,7 +12226,8 @@ def to_dict(self): if self.is_flipped is not None: data['is_flipped'] = self.is_flipped return data - + + class StoryAreaTypeLink(StoryAreaType): """ Describes a story area pointing to an HTTP or tg:// link. Currently, a story can have up to 3 link areas. @@ -12179,15 +12246,18 @@ class StoryAreaTypeLink(StoryAreaType): def __init__(self, url: str, **kwargs): super().__init__(type="link") self.url: str = url + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, 'url': self.url } return data - + + class StoryAreaTypeWeather(StoryAreaType): """ Describes a story area containing weather information. Currently, a story can have up to 3 weather areas. @@ -12214,8 +12284,10 @@ def __init__(self, temperature: float, emoji: str, background_color: int, **kwar self.temperature: float = temperature self.emoji: str = emoji self.background_color: int = background_color + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12224,7 +12296,8 @@ def to_dict(self): 'background_color': self.background_color } return data - + + class StoryAreaTypeUniqueGift(StoryAreaType): """ Describes a story area pointing to a unique gift. Currently, a story can have at most 1 unique gift area. @@ -12243,8 +12316,10 @@ class StoryAreaTypeUniqueGift(StoryAreaType): def __init__(self, name: str, **kwargs): super().__init__(type="unique_gift") self.name: str = name + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12252,8 +12327,9 @@ def to_dict(self): } return data - + +# noinspection PyShadowingBuiltins class StoryArea(JsonSerializable): """ Describes a clickable area on a story media. @@ -12272,15 +12348,17 @@ class StoryArea(JsonSerializable): def __init__(self, position: StoryAreaPosition, type: StoryAreaType, **kwargs): self.position: StoryAreaPosition = position self.type: StoryAreaType = type + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'position': self.position.to_dict(), 'type': self.type.to_dict() } return data - + class GiftInfo(JsonDeserializable): """ @@ -12327,6 +12405,7 @@ def __init__(self, gift: Gift, owned_gift_id: Optional[str] = None, convert_star self.text: Optional[str] = text self.entities: Optional[List[MessageEntity]] = entities self.is_private: Optional[bool] = is_private + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -12335,7 +12414,8 @@ def de_json(cls, json_string): if 'entities' in obj: obj['entities'] = [MessageEntity.de_json(entity) for entity in obj['entities']] return cls(**obj) - + + class UniqueGiftInfo(JsonDeserializable): """ This object describes a service message about a unique gift that was sent or received. @@ -12380,7 +12460,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) obj['gift'] = UniqueGift.de_json(obj['gift']) return cls(**obj) - + class PaidMessagePriceChanged(JsonDeserializable): """ @@ -12396,14 +12476,15 @@ class PaidMessagePriceChanged(JsonDeserializable): """ def __init__(self, paid_message_star_count: int, **kwargs): self.paid_message_star_count: int = paid_message_star_count + @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) - -class InputProfilePhoto(JsonSerializable): + +class InputProfilePhoto(JsonSerializable, ABC): """ This object describes a profile photo to set. Currently, it can be one of InputProfilePhotoStatic @@ -12415,10 +12496,11 @@ class InputProfilePhoto(JsonSerializable): :rtype: :class:`InputProfilePhoto` """ + class InputProfilePhotoStatic(InputProfilePhoto): """ This object describes a static profile photo to set. - + Telegram documentation: https://core.telegram.org/bots/api#inputprofilephotostatic :param type: Type of the profile photo, must be static @@ -12434,18 +12516,19 @@ class InputProfilePhotoStatic(InputProfilePhoto): def __init__(self, photo: InputFile, **kwargs): self.type: str = "static" self.photo: InputFile = photo - self._photo_name = service_utils.generate_random_token() self._photo_dic = "attach://{}".format(self._photo_name) + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'type': self.type, 'photo': self._photo_dic } return data + def convert_input_profile_photo(self): return self.to_json(), {self._photo_name: self.photo} @@ -12475,8 +12558,10 @@ def __init__(self, animation: InputFile, main_frame_timestamp: Optional[float] = self._animation_name = service_utils.generate_random_token() self._animation_dic = "attach://{}".format(self._animation_name) self.main_frame_timestamp: Optional[float] = main_frame_timestamp + def to_json(self): return json.dumps(self.to_dict()) + def to_dict(self): data = { 'type': self.type, @@ -12485,10 +12570,12 @@ def to_dict(self): if self.main_frame_timestamp is not None: data['main_frame_timestamp'] = self.main_frame_timestamp return data + def convert_input_profile_photo(self): return self.to_json(), {self._animation_name: self.animation} +# noinspection PyShadowingBuiltins class ChecklistTask(JsonDeserializable): """ Describes a task in a checklist. @@ -12531,7 +12618,8 @@ def de_json(cls, json_string): if 'completed_by_user' in obj: obj['completed_by_user'] = User.de_json(obj['completed_by_user']) return cls(**obj) - + + class Checklist(JsonDeserializable): """ Describes a checklist. @@ -12557,7 +12645,7 @@ class Checklist(JsonDeserializable): :rtype: :class:`Checklist` """ def __init__(self, title: str, tasks: List[ChecklistTask], - title_entities: Optional[List[MessageEntity]] = None, + title_entities: Optional[List[MessageEntity]] = None, others_can_add_tasks: Optional[bool] = None, others_can_mark_tasks_as_done: Optional[bool] = None, **kwargs): self.title: str = title @@ -12575,6 +12663,8 @@ def de_json(cls, json_string): obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] return cls(**obj) + +# noinspection PyShadowingBuiltins class InputChecklistTask(JsonSerializable): """ Describes a task to add to a checklist. @@ -12602,10 +12692,10 @@ def __init__(self, id: int, text: str, parse_mode: Optional[str] = None, self.text: str = text self.parse_mode: Optional[str] = parse_mode self.text_entities: Optional[List[MessageEntity]] = text_entities - + def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'id': self.id, @@ -12616,7 +12706,8 @@ def to_dict(self): if self.text_entities: data['text_entities'] = [entity.to_dict() for entity in self.text_entities] return data - + + class InputChecklist(JsonSerializable): """ Describes a checklist to create. @@ -12644,7 +12735,7 @@ class InputChecklist(JsonSerializable): :return: Instance of the class :rtype: :class:`InputChecklist` """ - def __init__(self, title: str, tasks: List[InputChecklistTask], + def __init__(self, title: str, tasks: List[InputChecklistTask], parse_mode: Optional[str] = None, title_entities: Optional[List[MessageEntity]] = None, others_can_add_tasks: Optional[bool] = None, @@ -12658,7 +12749,7 @@ def __init__(self, title: str, tasks: List[InputChecklistTask], def to_json(self): return json.dumps(self.to_dict()) - + def to_dict(self): data = { 'title': self.title, @@ -12706,8 +12797,8 @@ def de_json(cls, json_string): if 'checklist_message' in obj: obj['checklist_message'] = Message.de_json(obj['checklist_message']) return cls(**obj) - - + + class ChecklistTasksAdded(JsonDeserializable): """ Describes a service message about tasks added to a checklist. @@ -12726,6 +12817,7 @@ class ChecklistTasksAdded(JsonDeserializable): def __init__(self, tasks: List[ChecklistTask], checklist_message: Optional[Message] = None, **kwargs): self.checklist_message: Optional[Message] = checklist_message self.tasks: List[ChecklistTask] = tasks + @classmethod def de_json(cls, json_string): if json_string is None: return None @@ -12735,6 +12827,7 @@ def de_json(cls, json_string): obj['tasks'] = [ChecklistTask.de_json(task) for task in obj['tasks']] return cls(**obj) + class DirectMessagePriceChanged(JsonDeserializable): """ Describes a service message about a change in the price of direct messages sent to a channel chat. From 220772b0ddbc39d8f820dfdb4288619c0d168734 Mon Sep 17 00:00:00 2001 From: All-The-Foxes <116322192+All-The-Foxes@users.noreply.github.com> Date: Fri, 22 Aug 2025 18:55:42 -0400 Subject: [PATCH 1802/1808] Fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 27d6c3f22..926262263 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -7401,7 +7401,7 @@ def de_json(cls, json_string): def __init__(self, poll_id: str, option_ids: List[int], user: Optional[User] = None, voter_chat: Optional[Chat] = None, **kwargs): self.poll_id: str = poll_id self.user: Optional[User] = user - self.option_ids: [List[int]] = option_ids + self.option_ids: List[int] = option_ids self.voter_chat: Optional[Chat] = voter_chat From d3d7a52a4cb618ee64b2b3643d7b883f0187002e Mon Sep 17 00:00:00 2001 From: _run Date: Thu, 28 Aug 2025 21:00:47 +0500 Subject: [PATCH 1803/1808] Fix missing parameters, inconsistent descriptions, content type services --- telebot/__init__.py | 8 ++++---- telebot/async_telebot.py | 8 ++++---- telebot/types.py | 26 ++++++++++++++++++-------- telebot/util.py | 4 +++- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 679afc311..dcdeb8eb3 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2010,14 +2010,14 @@ def copy_message( )) - def approve_suggested_post(self, chat_id: Union[int, str], message_id: int, send_date: Optional[int]=None) -> bool: + def approve_suggested_post(self, chat_id: int, message_id: int, send_date: Optional[int]=None) -> bool: """ Use this method to approve a suggested post in a direct messages chat. The bot must have the 'can_post_messages' administrator right in the corresponding channel chat. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#approvesuggestedpost :param chat_id: Unique identifier for the target direct messages chat - :type chat_id: :obj:`int` or :obj:`str` + :type chat_id: :obj:`int` :param message_id: Identifier of a suggested post message to approve :type message_id: :obj:`int` @@ -2032,7 +2032,7 @@ def approve_suggested_post(self, chat_id: Union[int, str], message_id: int, send return apihelper.approve_suggested_post(self.token, chat_id, message_id, send_date=send_date) - def decline_suggested_post(self, chat_id: Union[int, str], message_id: int, comment: Optional[str]=None) -> bool: + def decline_suggested_post(self, chat_id: int, message_id: int, comment: Optional[str]=None) -> bool: """ Use this method to decline a suggested post in a direct messages chat. The bot must have the 'can_manage_direct_messages' administrator right in the corresponding channel chat. Returns True on success. @@ -2040,7 +2040,7 @@ def decline_suggested_post(self, chat_id: Union[int, str], message_id: int, comm Telegram documentation: https://core.telegram.org/bots/api#declinesuggestedpost :param chat_id: Unique identifier for the target direct messages chat - :type chat_id: :obj:`int` or :obj:`str` + :type chat_id: :obj:`int` :param message_id: Identifier of a suggested post message to decline :type message_id: :obj:`int` diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index bc01a012e..33783893f 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3537,14 +3537,14 @@ async def copy_message( ) ) - async def approve_suggested_post(self, chat_id: Union[int, str], message_id: int, send_date: Optional[int]=None) -> bool: + async def approve_suggested_post(self, chat_id: int, message_id: int, send_date: Optional[int]=None) -> bool: """ Use this method to approve a suggested post in a direct messages chat. The bot must have the 'can_post_messages' administrator right in the corresponding channel chat. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#approvesuggestedpost :param chat_id: Unique identifier for the target direct messages chat - :type chat_id: :obj:`int` or :obj:`str` + :type chat_id: :obj:`int` :param message_id: Identifier of a suggested post message to approve :type message_id: :obj:`int` @@ -3559,7 +3559,7 @@ async def approve_suggested_post(self, chat_id: Union[int, str], message_id: int return await asyncio_helper.approve_suggested_post(self.token, chat_id, message_id, send_date=send_date) - async def decline_suggested_post(self, chat_id: Union[int, str], message_id: int, comment: Optional[str]=None) -> bool: + async def decline_suggested_post(self, chat_id: int, message_id: int, comment: Optional[str]=None) -> bool: """ Use this method to decline a suggested post in a direct messages chat. The bot must have the 'can_manage_direct_messages' administrator right in the corresponding channel chat. Returns True on success. @@ -3567,7 +3567,7 @@ async def decline_suggested_post(self, chat_id: Union[int, str], message_id: int Telegram documentation: https://core.telegram.org/bots/api#declinesuggestedpost :param chat_id: Unique identifier for the target direct messages chat - :type chat_id: :obj:`int` or :obj:`str` + :type chat_id: :obj:`int` :param message_id: Identifier of a suggested post message to decline :type message_id: :obj:`int` diff --git a/telebot/types.py b/telebot/types.py index 33cf8dba6..3f01fec54 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -13097,18 +13097,19 @@ class SuggestedPostApproved(JsonDeserializable): :param price: Optional. Amount paid for the post :type price: :class:`SuggestedPostPrice` - :param send_date: Optional. Date when the post will be published - :type send_date: int + :param send_date: Date when the post will be published + :type send_date: :obj:`int` :return: Instance of the class :rtype: :class:`SuggestedPostApproved` """ - def __init__(self, suggested_post_message: Optional[Message] = None, + def __init__(self, send_date: int, + suggested_post_message: Optional[Message] = None, price: Optional[SuggestedPostPrice] = None, - send_date: Optional[int] = None, **kwargs): + **kwargs): self.suggested_post_message: Optional[Message] = suggested_post_message self.price: Optional[SuggestedPostPrice] = price - self.send_date: Optional[int] = send_date + self.send_date: int = send_date @classmethod def de_json(cls, json_string): @@ -13130,11 +13131,16 @@ class SuggestedPostApprovalFailed(JsonDeserializable): :param suggested_post_message: Optional. Message containing the suggested post whose approval has failed. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. :type suggested_post_message: :class:`Message` + :param price: Expected price of the post + :type price: :class:`SuggestedPostPrice` + :return: Instance of the class :rtype: :class:`SuggestedPostApprovalFailed` """ - def __init__(self, suggested_post_message: Optional[Message] = None, **kwargs): + def __init__(self, price: SuggestedPostPrice, + suggested_post_message: Optional[Message] = None, **kwargs): self.suggested_post_message: Optional[Message] = suggested_post_message + self.price: SuggestedPostPrice = price @classmethod def de_json(cls, json_string): @@ -13142,6 +13148,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) if 'suggested_post_message' in obj: obj['suggested_post_message'] = Message.de_json(obj['suggested_post_message']) + obj['price'] = SuggestedPostPrice.de_json(obj['price']) return cls(**obj) class SuggestedPostDeclined(JsonDeserializable): @@ -13153,6 +13160,9 @@ class SuggestedPostDeclined(JsonDeserializable): :param suggested_post_message: Optional. Message containing the suggested post. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply. :type suggested_post_message: :class:`Message` + :param comment: Optional. Comment with which the post was declined + :type comment: :obj:`str` + :return: Instance of the class :rtype: :class:`SuggestedPostDeclined` """ @@ -13221,9 +13231,9 @@ class SuggestedPostRefunded(JsonDeserializable): :return: Instance of the class :rtype: :class:`SuggestedPostRefunded` """ - def __init__(self, suggested_post_message: Optional[Message] = None, reason: Optional[str] = None, **kwargs): + def __init__(self, reason: str, suggested_post_message: Optional[Message] = None, **kwargs): self.suggested_post_message: Optional[Message] = suggested_post_message - self.reason: Optional[str] = reason + self.reason: str = reason @classmethod def de_json(cls, json_string): diff --git a/telebot/util.py b/telebot/util.py index 2f92ae781..0d37866e1 100644 --- a/telebot/util.py +++ b/telebot/util.py @@ -41,7 +41,9 @@ 'chat_background_set', 'forum_topic_created', 'forum_topic_closed', 'forum_topic_reopened', 'forum_topic_edited', 'general_forum_topic_hidden', 'general_forum_topic_unhidden', 'write_access_allowed', 'users_shared', 'chat_shared', 'giveaway_created', 'giveaway_winners', 'giveaway_completed', 'boost_added', 'paid_message_price_changed', - 'checklist_tasks_done', 'checklist_tasks_added', 'direct_message_price_changed', + 'checklist_tasks_done', 'checklist_tasks_added', 'direct_message_price_changed', 'suggested_post_refunded', + 'suggested_post_info', 'suggested_post_approved', 'suggested_post_approval_failed', 'suggested_post_declined', + 'suggested_post_paid' ] #: All update types, should be used for allowed_updates parameter in polling. From 5f1153a079dddd01d11243d1f9b33e6cecf771d0 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 30 Aug 2025 10:23:26 +0300 Subject: [PATCH 1804/1808] Bump version --- docs/source/conf.py | 2 +- pyproject.toml | 4 ++-- telebot/version.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 4dcffc212..514d9dad4 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.28.0' +release = '4.29.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index c46727508..5271bfa16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.28.0" -description = "Python Telegram bot api." +version = "4.29.0" +description = "Python Telegram bot API." authors = [{name = "eternnoir", email = "eternnoir@gmail.com"}] license = {text = "GPL2"} readme = "README.md" diff --git a/telebot/version.py b/telebot/version.py index c2bb2a64b..e4d2a2e0c 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.28.0' +__version__ = '4.29.0' From 8876319660757aaedf0ae3e706be5fc44d3352d9 Mon Sep 17 00:00:00 2001 From: _run Date: Mon, 1 Sep 2025 17:46:44 +0400 Subject: [PATCH 1805/1808] Fix urgent suggested_post_parameters presence in sendmediagroup --- telebot/async_telebot.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 81cdc955d..192d9d369 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5148,8 +5148,7 @@ async def send_media_group( business_connection_id: Optional[str]=None, message_effect_id: Optional[str]=None, allow_paid_broadcast: Optional[bool]=None, - direct_messages_topic_id: Optional[int]=None, - suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> List[types.Message]: + direct_messages_topic_id: Optional[int]=None) -> List[types.Message]: """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. @@ -5197,11 +5196,6 @@ async def send_media_group( required if the message is sent to a direct messages chat :type direct_messages_topic_id: :obj:`int` - :param suggested_post_parameters: A JSON-serialized object containing the parameters of the suggested post to send; - for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post - is automatically declined. - :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` - :return: On success, an array of Messages that were sent is returned. :rtype: List[types.Message] """ @@ -5236,8 +5230,7 @@ async def send_media_group( result = await asyncio_helper.send_media_group( self.token, chat_id, media, disable_notification, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, - allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id, - suggested_post_parameters=suggested_post_parameters) + allow_paid_broadcast=allow_paid_broadcast, direct_messages_topic_id=direct_messages_topic_id) return [types.Message.de_json(msg) for msg in result] async def send_location( From bf351eeb05b37fde11e5baf4a666c38292ea512a Mon Sep 17 00:00:00 2001 From: All-The-Foxes <116322192+All-The-Foxes@users.noreply.github.com> Date: Mon, 1 Sep 2025 15:13:40 -0400 Subject: [PATCH 1806/1808] Fix indentation error in init --- telebot/__init__.py | 720 ++++++++++++++++++++++---------------------- 1 file changed, 360 insertions(+), 360 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 59a8d7db8..368b531d4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -46,7 +46,7 @@ REPLY_MARKUP_TYPES = Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, + types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply] @@ -93,7 +93,7 @@ class TeleBot: from telebot import TeleBot bot = TeleBot('token') # get token from @BotFather - # now you can register other handlers/update listeners, + # now you can register other handlers/update listeners, # and use bot methods. See more examples in examples/ directory: @@ -139,7 +139,7 @@ class TeleBot: :param use_class_middlewares: Use class middlewares, defaults to False :type use_class_middlewares: :obj:`bool`, optional - + :param disable_web_page_preview: Default value for disable_web_page_preview, defaults to None :type disable_web_page_preview: :obj:`bool`, optional @@ -151,7 +151,7 @@ class TeleBot: :param allow_sending_without_reply: Default value for allow_sending_without_reply, defaults to None :type allow_sending_without_reply: :obj:`bool`, optional - + :param colorful_logs: Outputs colorful logs :type colorful_logs: :obj:`bool`, optional @@ -168,7 +168,7 @@ def __init__( next_step_backend: Optional[HandlerBackend]=None, reply_backend: Optional[HandlerBackend]=None, exception_handler: Optional[ExceptionHandler]=None, last_update_id: Optional[int]=0, suppress_middleware_excepions: Optional[bool]=False, state_storage: Optional[StateStorageBase]=StateMemoryStorage(), - use_class_middlewares: Optional[bool]=False, + use_class_middlewares: Optional[bool]=False, disable_web_page_preview: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, @@ -194,7 +194,7 @@ def __init__( if validate_token: util.validate_token(self.token) - + self.bot_id: Union[int, None] = util.extract_bot_id(self.token) # subject to change in future, unspecified # logs-related @@ -292,7 +292,7 @@ def __init__( self.threaded = threaded if self.threaded: self.worker_pool = util.ThreadPool(self, num_threads=num_threads) - + @property def user(self) -> types.User: """ @@ -438,15 +438,15 @@ def set_webhook(self, url: Optional[str]=None, certificate: Optional[Union[str, Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput, defaults to None :type max_connections: :obj:`int`, optional - + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - + Please note that this parameter doesn't affect updates created before the call to the setWebhook, so unwanted updates may be received for a short period of time. Defaults to None - + :type allowed_updates: :obj:`list`, optional :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address @@ -516,7 +516,7 @@ def run_webhooks(self, :type max_connections: :obj:`int`, optional :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] - to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). + to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. defaults to None :type allowed_updates: :obj:`list`, optional @@ -545,7 +545,7 @@ def run_webhooks(self, if not url_path: url_path = self.token + '/' if url_path[-1] != '/': url_path += '/' - + protocol = "https" if certificate else "http" if not webhook_url: webhook_url = "{}://{}:{}/{}".format(protocol, listen, port, url_path) @@ -627,8 +627,8 @@ def remove_webhook(self) -> bool: return self.set_webhook() # No params resets webhook - def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, - timeout: Optional[int]=20, allowed_updates: Optional[List[str]]=None, + def get_updates(self, offset: Optional[int]=None, limit: Optional[int]=None, + timeout: Optional[int]=20, allowed_updates: Optional[List[str]]=None, long_polling_timeout: int=20) -> List[types.Update]: """ Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned. @@ -678,14 +678,14 @@ def __retrieve_updates(self, timeout=20, long_polling_timeout=20, allowed_update Registered listeners and applicable message handlers will be notified when a new message arrives. :meta private: - + :raises ApiException when a call has failed. """ if self.skip_pending: self.__skip_updates() logger.debug('Skipped all pending messages') self.skip_pending = False - updates = self.get_updates(offset=(self.last_update_id + 1), + updates = self.get_updates(offset=(self.last_update_id + 1), allowed_updates=allowed_updates, timeout=timeout, long_polling_timeout=long_polling_timeout) self.process_new_updates(updates) @@ -726,7 +726,7 @@ def process_new_updates(self, updates: List[types.Update]): new_edited_business_messages = None new_deleted_business_messages = None new_purchased_paid_media = None - + for update in updates: if apihelper.ENABLE_MIDDLEWARE and not self.use_class_middlewares: try: @@ -810,7 +810,7 @@ def process_new_updates(self, updates: List[types.Update]): if update.purchased_paid_media: if new_purchased_paid_media is None: new_purchased_paid_media = [] new_purchased_paid_media.append(update.purchased_paid_media) - + if new_messages: self.process_new_messages(new_messages) if new_edited_messages: @@ -890,7 +890,7 @@ def process_new_message_reaction(self, new_message_reactions): :meta private: """ self._notify_command_handlers(self.message_reaction_handlers, new_message_reactions, 'message_reaction') - + def process_new_message_reaction_count(self, new_message_reaction_counts): """ :meta private: @@ -938,7 +938,7 @@ def process_new_poll_answer(self, new_poll_answers): :meta private: """ self._notify_command_handlers(self.poll_answer_handlers, new_poll_answers, 'poll_answer') - + def process_new_my_chat_member(self, new_my_chat_members): """ :meta private: @@ -986,13 +986,13 @@ def process_new_edited_business_message(self, new_edited_business_messages): :meta private: """ self._notify_command_handlers(self.edited_business_message_handlers, new_edited_business_messages, 'edited_business_message') - + def process_new_deleted_business_messages(self, new_deleted_business_messages): """ :meta private: """ self._notify_command_handlers(self.deleted_business_messages_handlers, new_deleted_business_messages, 'deleted_business_messages') - + def process_new_purchased_paid_media(self, new_purchased_paid_media): """ :meta private: @@ -1047,7 +1047,7 @@ def _setup_change_detector(self, path_to_watch: str): # Make it possible to specify --path argument to the script path = sys.argv[sys.argv.index('--path') + 1] if '--path' in sys.argv else '.' - + self.event_observer = Observer() self.event_observer.schedule(self.event_handler, path, recursive=True) self.event_observer.start() @@ -1066,7 +1066,7 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo Wrap polling with infinite loop and exception handling to avoid bot stops polling. .. note:: - + Install watchdog and psutil before using restart_on_change option. :param timeout: Request connection timeout. @@ -1083,11 +1083,11 @@ def infinity_polling(self, timeout: Optional[int]=20, skip_pending: Optional[boo :type logger_level: :obj:`int`. :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - Please note that this parameter doesn't affect updates created before the call to the get_updates, + 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. :type allowed_updates: :obj:`list` of :obj:`str` @@ -1132,14 +1132,14 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F This allows the bot to retrieve Updates automatically and notify listeners and message handlers accordingly. Warning: Do not call this function more than once! - + Always gets updates. .. deprecated:: 4.1.1 Use :meth:`infinity_polling` instead. .. note:: - + Install watchdog and psutil before using restart_on_change option. :param interval: Delay between two update retrivals @@ -1153,7 +1153,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :param skip_pending: skip old updates :type skip_pending: :obj:`bool` - + :param long_polling_timeout: Timeout in seconds for long polling (see API docs) :type long_polling_timeout: :obj:`int` @@ -1162,12 +1162,12 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :type logger_level: :obj:`int` :param allowed_updates: A list of the update types you want your bot to receive. - For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. - See util.update_types for a complete list of available update types. - Specify an empty list to receive all update types except chat_member (default). + For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. + See util.update_types for a complete list of available update types. + Specify an empty list to receive all update types except chat_member (default). If not specified, the previous setting will be used. - - Please note that this parameter doesn't affect updates created before the call to the get_updates, + + 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. :type allowed_updates: :obj:`list` of :obj:`str` @@ -1179,7 +1179,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F :param path_to_watch: Path to watch for changes. Defaults to None :type path_to_watch: :obj:`str` - + :return: """ if none_stop is not None: @@ -1193,7 +1193,7 @@ def polling(self, non_stop: Optional[bool]=False, skip_pending: Optional[bool]=F self._setup_change_detector(path_to_watch) logger.info('Starting your bot with username: [@%s]', self.user.username) - + if self.threaded: self.__threaded_polling(non_stop=non_stop, interval=interval, timeout=timeout, long_polling_timeout=long_polling_timeout, logger_level=logger_level, allowed_updates=allowed_updates) @@ -1386,9 +1386,9 @@ def get_me(self) -> types.User: def get_file(self, file_id: Optional[str]) -> types.File: """ Use this method to get basic info about a file and prepare it for downloading. - For the moment, bots can download files of up to 20MB in size. - On success, a File object is returned. - It is guaranteed that the link will be valid for at least 1 hour. + For the moment, bots can download files of up to 20MB in size. + On success, a File object is returned. + It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling get_file again. Telegram documentation: https://core.telegram.org/bots/api#getfile @@ -1431,11 +1431,11 @@ def download_file(self, file_path: str) -> bytes: def log_out(self) -> bool: """ - Use this method to log out from the cloud Bot API server before launching the bot locally. + Use this method to log out from the cloud Bot API server before launching the bot locally. You MUST log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. - After a successful call, you can immediately log in on a local server, - but will not be able to log in back to the cloud Bot API server for 10 minutes. + After a successful call, you can immediately log in on a local server, + but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#logout @@ -1444,14 +1444,14 @@ def log_out(self) -> bool: :rtype: :obj:`bool` """ return apihelper.log_out(self.token) - - + + def close(self) -> bool: """ - Use this method to close the bot instance before moving it from one local server to another. + Use this method to close the bot instance before moving it from one local server to another. You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. - The method will return error 429 in the first 10 minutes after the bot is launched. + The method will return error 429 in the first 10 minutes after the bot is launched. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#close @@ -1459,10 +1459,10 @@ def close(self) -> bool: :return: :obj:`bool` """ return apihelper.close(self.token) - + def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reaction: Optional[List[types.ReactionType]]=None, is_big: Optional[bool]=None) -> bool: """ - Use this method to change the chosen reactions on a message. + Use this method to change the chosen reactions on a message. Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same available reactions as messages in the channel. Returns True on success. @@ -1487,7 +1487,7 @@ def set_message_reaction(self, chat_id: Union[int, str], message_id: int, reacti self.token, chat_id, message_id, reaction = reaction, is_big = is_big) - def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, + def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, limit: Optional[int]=None) -> types.UserProfilePhotos: """ Use this method to get a list of profile pictures for a user. @@ -1531,7 +1531,7 @@ def set_user_emoji_status(self, user_id: int, emoji_status_custom_emoji_id: Opti """ return apihelper.set_user_emoji_status( self.token, user_id, emoji_status_custom_emoji_id=emoji_status_custom_emoji_id, emoji_status_expiration_date=emoji_status_expiration_date) - + def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ @@ -1571,7 +1571,7 @@ def get_chat_administrators(self, chat_id: Union[int, str]) -> List[types.ChatMe On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. - Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators + Telegram documentation: https://core.telegram.org/bots/api#getchatadministrators :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format @channelusername) @@ -1623,7 +1623,7 @@ def set_chat_sticker_set(self, chat_id: Union[int, str], sticker_set_name: str) Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#setchatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1643,7 +1643,7 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletechatstickerset :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1658,7 +1658,7 @@ def delete_chat_sticker_set(self, chat_id: Union[int, str]) -> bool: def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. - + Telegram documentation: https://core.telegram.org/bots/api#getchatmember :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1676,11 +1676,11 @@ def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatM def send_message( - self, chat_id: Union[int, str], text: str, - parse_mode: Optional[str]=None, + self, chat_id: Union[int, str], text: str, + parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -1696,7 +1696,7 @@ def send_message( Use this method to send text messages. Warning: Do not send more than about 4096 characters each message, otherwise you'll risk an HTTP 414 error. - If you must send more than 4096 characters, + If you must send more than 4096 characters, use the `split_string` or `smart_split` function in util.py. Telegram documentation: https://core.telegram.org/bots/api#sendmessage @@ -1806,7 +1806,7 @@ def send_message( def forward_message( - self, chat_id: Union[int, str], from_chat_id: Union[int, str], + self, chat_id: Union[int, str], from_chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, timeout: Optional[int]=None, @@ -1855,13 +1855,13 @@ def forward_message( def copy_message( - self, chat_id: Union[int, str], - from_chat_id: Union[int, str], - message_id: int, - caption: Optional[str]=None, - parse_mode: Optional[str]=None, + self, chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -1933,11 +1933,11 @@ def copy_message( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` - + :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` """ - + disable_notification = self.disable_notification if (disable_notification is None) else disable_notification parse_mode = self.parse_mode if (parse_mode is None) else parse_mode protect_content = self.protect_content if (protect_content is None) else protect_content @@ -1969,7 +1969,7 @@ def copy_message( video_start_timestamp=video_start_timestamp)) - def delete_message(self, chat_id: Union[int, str], message_id: int, + def delete_message(self, chat_id: Union[int, str], message_id: int, timeout: Optional[int]=None) -> bool: """ Use this method to delete a message, including service messages, with the following limitations: @@ -1998,7 +1998,7 @@ def delete_message(self, chat_id: Union[int, str], message_id: int, """ return apihelper.delete_message(self.token, chat_id, message_id, timeout=timeout) - + def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ Use this method to delete multiple messages simultaneously. If some of the specified messages can't be found, they are skipped. @@ -2016,7 +2016,7 @@ def delete_messages(self, chat_id: Union[int, str], message_ids: List[int]): """ return apihelper.delete_messages(self.token, chat_id, message_ids) - + def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool]=None, message_thread_id: Optional[int]=None, protect_content: Optional[bool]=None) -> List[types.MessageID]: @@ -2058,7 +2058,7 @@ def forward_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, in protect_content=protect_content) return [types.MessageID.de_json(message_id) for message_id in result] - + def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], message_ids: List[int], disable_notification: Optional[bool] = None, message_thread_id: Optional[int] = None, protect_content: Optional[bool] = None, remove_caption: Optional[bool] = None) -> List[types.MessageID]: @@ -2103,7 +2103,7 @@ def copy_messages(self, chat_id: Union[str, int], from_chat_id: Union[str, int], self.token, chat_id, from_chat_id, message_ids, disable_notification=disable_notification, message_thread_id=message_thread_id, protect_content=protect_content, remove_caption=remove_caption) return [types.MessageID.de_json(message_id) for message_id in result] - + def send_checklist( self, business_connection_id: str, chat_id: Union[int, str], checklist: types.InputChecklist, @@ -2147,7 +2147,7 @@ def send_checklist( """ disable_notification = self.disable_notification if (disable_notification is None) else disable_notification protect_content = self.protect_content if (protect_content is None) else protect_content - + if reply_parameters and (reply_parameters.allow_sending_without_reply is None): reply_parameters.allow_sending_without_reply = self.allow_sending_without_reply @@ -2156,7 +2156,7 @@ def send_checklist( self.token, business_connection_id, chat_id, checklist, disable_notification=disable_notification, protect_content=protect_content, message_effect_id=message_effect_id, reply_parameters=reply_parameters, reply_markup=reply_markup)) - + def edit_message_checklist( self, business_connection_id: str, chat_id: Union[int, str], message_id: int, checklist: types.InputChecklist, @@ -2191,9 +2191,9 @@ def edit_message_checklist( def send_dice( self, chat_id: Union[int, str], - emoji: Optional[str]=None, disable_notification: Optional[bool]=None, + emoji: Optional[str]=None, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, @@ -2284,7 +2284,7 @@ def send_dice( def send_photo( - self, chat_id: Union[int, str], photo: Union[Any, str], + self, chat_id: Union[int, str], photo: Union[Any, str], caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, @@ -2304,7 +2304,7 @@ def send_photo( Use this method to send photos. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendphoto - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2363,7 +2363,7 @@ def send_photo( :param allow_paid_broadcast: Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :type allow_paid_broadcast: :obj:`bool` - + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -2400,14 +2400,14 @@ def send_photo( def send_audio( - self, chat_id: Union[int, str], audio: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, + self, chat_id: Union[int, str], audio: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, performer: Optional[str]=None, title: Optional[str]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + parse_mode: Optional[str]=None, disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -2426,7 +2426,7 @@ def send_audio( For sending voice messages, use the send_voice method instead. Telegram documentation: https://core.telegram.org/bots/api#sendaudio - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2536,12 +2536,12 @@ def send_audio( def send_voice( - self, chat_id: Union[int, str], voice: Union[Any, str], - caption: Optional[str]=None, duration: Optional[int]=None, + self, chat_id: Union[int, str], voice: Union[Any, str], + caption: Optional[str]=None, duration: Optional[int]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -2555,7 +2555,7 @@ def send_voice( 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 - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2648,11 +2648,11 @@ def send_voice( def send_document( self, chat_id: Union[int, str], document: Union[Any, str], reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - caption: Optional[str]=None, + caption: Optional[str]=None, reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - parse_mode: Optional[str]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + parse_mode: Optional[str]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, caption_entities: Optional[List[types.MessageEntity]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility @@ -2669,7 +2669,7 @@ def send_document( Use this method to send general files. Telegram documentation: https://core.telegram.org/bots/api#senddocument - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -2883,7 +2883,7 @@ def send_sticker( if data and (not sticker): logger.warning('The parameter "data" is deprecated. Use "sticker" instead.') sticker = data - + return types.Message.de_json( apihelper.send_data( self.token, chat_id, sticker, 'sticker', @@ -2895,15 +2895,15 @@ def send_sticker( def send_video( - self, chat_id: Union[int, str], video: Union[Any, str], + self, chat_id: Union[int, str], video: Union[Any, str], duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, - thumbnail: Optional[Union[Any, str]]=None, - caption: Optional[str]=None, - parse_mode: Optional[str]=None, + thumbnail: Optional[Union[Any, str]]=None, + caption: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, - supports_streaming: Optional[bool]=None, + supports_streaming: Optional[bool]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility @@ -2923,7 +2923,7 @@ def send_video( start_timestamp: Optional[int]=None) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). - + Telegram documentation: https://core.telegram.org/bots/api#sendvideo :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -2953,7 +2953,7 @@ def send_video( :param start_timestamp: Start timestamp for the video in the message :type start_timestamp: :obj:`int` - + :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing :type caption: :obj:`str` @@ -3061,19 +3061,19 @@ def send_video( def send_animation( - self, chat_id: Union[int, str], animation: Union[Any, str], + self, chat_id: Union[int, str], animation: Union[Any, str], duration: Optional[int]=None, width: Optional[int]=None, height: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, - caption: Optional[str]=None, + caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, has_spoiler: Optional[bool]=None, @@ -3086,7 +3086,7 @@ def send_animation( """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. - + Telegram documentation: https://core.telegram.org/bots/api#sendanimation :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -3104,7 +3104,7 @@ def send_animation( :param height: Animation height :type height: :obj:`int` - + :param thumbnail: 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, @@ -3206,13 +3206,13 @@ def send_animation( def send_video_note( - self, chat_id: Union[int, str], data: Union[Any, str], - duration: Optional[int]=None, + self, chat_id: Union[int, str], data: Union[Any, str], + duration: Optional[int]=None, length: Optional[int]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, - timeout: Optional[int]=None, + disable_notification: Optional[bool]=None, + timeout: Optional[int]=None, thumbnail: Optional[Union[Any, str]]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, @@ -3227,10 +3227,10 @@ def send_video_note( Use this method to send video messages. On success, the sent Message is returned. Telegram documentation: https://core.telegram.org/bots/api#sendvideonote - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - + :param data: Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. Sending video notes by a URL is currently unsupported :type data: :obj:`str` or :class:`telebot.types.InputFile` @@ -3261,7 +3261,7 @@ def send_video_note( :param thumbnail: 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 . + so you can pass “attach://” if the thumbnail was uploaded using multipart/form-data under . :type thumbnail: :obj:`str` or :class:`telebot.types.InputFile` :param protect_content: Protects the contents of the sent message from forwarding and saving @@ -3321,7 +3321,7 @@ def send_video_note( protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) - + def send_paid_media( self, chat_id: Union[int, str], star_count: int, media: List[types.InputPaidMedia], caption: Optional[str]=None, parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, @@ -3392,11 +3392,11 @@ def send_paid_media( def send_media_group( - self, chat_id: Union[int, str], + self, chat_id: Union[int, str], media: List[Union[ - types.InputMediaAudio, types.InputMediaDocument, + types.InputMediaAudio, types.InputMediaDocument, types.InputMediaPhoto, types.InputMediaVideo]], - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, protect_content: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, @@ -3409,7 +3409,7 @@ def send_media_group( """ Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of Messages that were sent is returned. - + Telegram documentation: https://core.telegram.org/bots/api#sendmediagroup :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -3487,17 +3487,17 @@ def send_media_group( def send_location( - self, chat_id: Union[int, str], - latitude: float, longitude: float, + self, chat_id: Union[int, str], + latitude: float, longitude: float, live_period: Optional[int]=None, - reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - disable_notification: Optional[bool]=None, + reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + disable_notification: Optional[bool]=None, timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, - allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility + allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, reply_parameters: Optional[types.ReplyParameters]=None, @@ -3601,14 +3601,14 @@ def send_location( def edit_message_live_location( - self, latitude: float, longitude: float, - chat_id: Optional[Union[int, str]]=None, + self, latitude: float, longitude: float, + chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, - horizontal_accuracy: Optional[float]=None, - heading: Optional[int]=None, + horizontal_accuracy: Optional[float]=None, + heading: Optional[int]=None, proximity_alert_radius: Optional[int]=None, live_period: Optional[int]=None, business_connection_id: Optional[str]=None @@ -3669,9 +3669,9 @@ def edit_message_live_location( def stop_message_live_location( - self, chat_id: Optional[Union[int, str]]=None, + self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, timeout: Optional[int]=None, business_connection_id: Optional[str]=None) -> Union[types.Message, bool]: @@ -3680,7 +3680,7 @@ def stop_message_live_location( On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned. Telegram documentation: https://core.telegram.org/bots/api#stopmessagelivelocation - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -3711,14 +3711,14 @@ def stop_message_live_location( def send_venue( - self, chat_id: Union[int, str], - latitude: Optional[float], longitude: Optional[float], - title: str, address: str, - foursquare_id: Optional[str]=None, + self, chat_id: Union[int, str], + latitude: Optional[float], longitude: Optional[float], + title: str, address: str, + foursquare_id: Optional[str]=None, foursquare_type: Optional[str]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility google_place_id: Optional[str]=None, @@ -3731,12 +3731,12 @@ def send_venue( allow_paid_broadcast: Optional[bool]=None) -> types.Message: """ Use this method to send information about a venue. On success, the sent Message is returned. - + Telegram documentation: https://core.telegram.org/bots/api#sendvenue :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`int` or :obj:`str` - + :param latitude: Latitude of the venue :type latitude: :obj:`float` @@ -3833,12 +3833,12 @@ def send_venue( def send_contact( - self, chat_id: Union[int, str], phone_number: str, - first_name: str, last_name: Optional[str]=None, + self, chat_id: Union[int, str], phone_number: str, + first_name: str, last_name: Optional[str]=None, vcard: Optional[str]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, message_thread_id: Optional[int]=None, @@ -3950,7 +3950,7 @@ def send_chat_action( :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`int` or :obj:`str` - + :param action: Type of action to broadcast. Choose one, depending on what the user is about to receive: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_voice or upload_voice for voice notes, upload_document for general files, @@ -3972,11 +3972,11 @@ def send_chat_action( return apihelper.send_chat_action( self.token, chat_id, action, timeout=timeout, message_thread_id=message_thread_id, business_connection_id=business_connection_id) - + @util.deprecated(deprecation_text="Use ban_chat_member instead") def kick_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date:Optional[Union[int, datetime]]=None, + self, chat_id: Union[int, str], user_id: int, + until_date:Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ This function is deprecated. Use `ban_chat_member` instead. @@ -3986,13 +3986,13 @@ def kick_chat_member( def ban_chat_member( - self, chat_id: Union[int, str], user_id: int, - until_date: Optional[Union[int, datetime]]=None, + self, chat_id: Union[int, str], user_id: int, + until_date: Optional[Union[int, datetime]]=None, revoke_messages: Optional[bool]=None) -> bool: """ - Use this method to ban a user in a group, a supergroup or a channel. - In the case of supergroups and channels, the user will not be able to return to the chat on their - own using invite links, etc., unless unbanned first. + Use this method to ban a user in a group, a supergroup or a channel. + In the case of supergroups and channels, the user will not be able to return to the chat on their + own using invite links, etc., unless unbanned first. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#banchatmember @@ -4009,10 +4009,10 @@ def ban_chat_member( :type until_date: :obj:`int` or :obj:`datetime` :param revoke_messages: Pass True to delete all messages from the chat for the user that is being removed. - If False, the user will be able to see messages in the group that were sent before the user was removed. + If False, the user will be able to see messages in the group that were sent before the user was removed. Always True for supergroups and channels. :type revoke_messages: :obj:`bool` - + :return: Returns True on success. :rtype: :obj:`bool` """ @@ -4021,7 +4021,7 @@ def ban_chat_member( def unban_chat_member( - self, chat_id: Union[int, str], user_id: int, + self, chat_id: Union[int, str], user_id: int, only_if_banned: Optional[bool]=False) -> bool: """ Use this method to unban a previously kicked user in a supergroup or channel. @@ -4049,15 +4049,15 @@ def unban_chat_member( def restrict_chat_member( - self, chat_id: Union[int, str], user_id: int, + self, chat_id: Union[int, str], user_id: int, until_date: Optional[Union[int, datetime]]=None, - can_send_messages: Optional[bool]=None, + can_send_messages: Optional[bool]=None, can_send_media_messages: Optional[bool]=None, - can_send_polls: Optional[bool]=None, + can_send_polls: Optional[bool]=None, can_send_other_messages: Optional[bool]=None, - can_add_web_page_previews: Optional[bool]=None, + can_add_web_page_previews: Optional[bool]=None, can_change_info: Optional[bool]=None, - can_invite_users: Optional[bool]=None, + can_invite_users: Optional[bool]=None, can_pin_messages: Optional[bool]=None, permissions: Optional[types.ChatPermissions]=None, use_independent_chat_permissions: Optional[bool]=None) -> bool: @@ -4085,10 +4085,10 @@ def restrict_chat_member( :param can_send_messages: deprecated :type can_send_messages: :obj:`bool` - + :param can_send_media_messages: deprecated :type can_send_media_messages: :obj:`bool` - + :param can_send_polls: deprecated :type can_send_polls: :obj:`bool` @@ -4139,16 +4139,16 @@ def restrict_chat_member( def promote_chat_member( self, chat_id: Union[int, str], user_id: int, - can_change_info: Optional[bool]=None, + can_change_info: Optional[bool]=None, can_post_messages: Optional[bool]=None, - can_edit_messages: Optional[bool]=None, - can_delete_messages: Optional[bool]=None, + can_edit_messages: Optional[bool]=None, + can_delete_messages: Optional[bool]=None, can_invite_users: Optional[bool]=None, - can_restrict_members: Optional[bool]=None, - can_pin_messages: Optional[bool]=None, + can_restrict_members: Optional[bool]=None, + can_pin_messages: Optional[bool]=None, can_promote_members: Optional[bool]=None, - is_anonymous: Optional[bool]=None, - can_manage_chat: Optional[bool]=None, + is_anonymous: Optional[bool]=None, + can_manage_chat: Optional[bool]=None, can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None, can_manage_topics: Optional[bool]=None, @@ -4198,9 +4198,9 @@ def promote_chat_member( :param is_anonymous: Pass True, if the administrator's presence in the chat is hidden :type is_anonymous: :obj:`bool` - :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, - message statistics in channels, see channel members, - see anonymous administrators in supergroups and ignore slow mode. + :param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics, + message statistics in channels, see channel members, + see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege :type can_manage_chat: :obj:`bool` @@ -4267,14 +4267,14 @@ def set_chat_administrator_custom_title( """ return apihelper.set_chat_administrator_custom_title(self.token, chat_id, user_id, custom_title) - + def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ Use this method to ban a channel chat in a supergroup or a channel. - The owner of the chat will not be able to send messages and join live - streams on behalf of the chat, unless it is unbanned first. - The bot must be an administrator in the supergroup or channel - for this to work and must have the appropriate administrator rights. + The owner of the chat will not be able to send messages and join live + streams on behalf of the chat, unless it is unbanned first. + The bot must be an administrator in the supergroup or channel + for this to work and must have the appropriate administrator rights. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#banchatsenderchat @@ -4293,8 +4293,8 @@ def ban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[i def unban_chat_sender_chat(self, chat_id: Union[int, str], sender_chat_id: Union[int, str]) -> bool: """ - Use this method to unban a previously banned channel chat in a supergroup or channel. - The bot must be an administrator for this to work and must have the appropriate + Use this method to unban a previously banned channel chat in a supergroup or channel. + The bot must be an administrator for this to work and must have the appropriate administrator rights. Returns True on success. @@ -4345,7 +4345,7 @@ def set_chat_permissions( def create_chat_invite_link( self, chat_id: Union[int, str], name: Optional[str]=None, - expire_date: Optional[Union[int, datetime]]=None, + expire_date: Optional[Union[int, datetime]]=None, member_limit: Optional[int]=None, creates_join_request: Optional[bool]=None) -> types.ChatInviteLink: """ @@ -4432,7 +4432,7 @@ def create_chat_subscription_invite_link( :param chat_id: Unique identifier for the target channel chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - + :param name: Invite link name; 0-32 characters :type name: :obj:`str` @@ -4450,7 +4450,7 @@ def create_chat_subscription_invite_link( return types.ChatInviteLink.de_json( apihelper.create_chat_subscription_invite_link(self.token, chat_id, subscription_period, subscription_price, name=name) ) - + def edit_chat_subscription_invite_link( self, chat_id: Union[int, str], invite_link: str, name: Optional[str]=None) -> types.ChatInviteLink: """ @@ -4480,7 +4480,7 @@ def revoke_chat_invite_link( self, chat_id: Union[int, str], invite_link: str) -> types.ChatInviteLink: """ Use this method to revoke an invite link created by the bot. - Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator + Note: If the primary link is revoked, a new link is automatically generated The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Telegram documentation: https://core.telegram.org/bots/api#revokechatinvitelink @@ -4519,7 +4519,7 @@ def export_chat_invite_link(self, chat_id: Union[int, str]) -> str: def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ - Use this method to approve a chat join request. + Use this method to approve a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. @@ -4540,7 +4540,7 @@ def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool: """ - Use this method to decline a chat join request. + Use this method to decline a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. @@ -4599,21 +4599,21 @@ def delete_chat_photo(self, chat_id: Union[int, str]) -> bool: """ return apihelper.delete_chat_photo(self.token, chat_id) - - def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, + + def get_my_commands(self, scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> List[types.BotCommand]: """ - Use this method to get the current list of the bot's commands. + Use this method to get the current list of the bot's commands. Returns List of BotCommand on success. Telegram documentation: https://core.telegram.org/bots/api#getmycommands - :param scope: The scope of users for which the commands are relevant. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -4641,7 +4641,7 @@ def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=Non """ return apihelper.set_my_name(self.token, name=name, language_code=language_code) - + def get_my_name(self, language_code: Optional[str]=None): """ Use this method to get the current bot name for the given user language. @@ -4680,7 +4680,7 @@ def set_my_description(self, description: Optional[str]=None, language_code: Opt return apihelper.set_my_description( self.token, description=description, language_code=language_code) - + def get_my_description(self, language_code: Optional[str]=None): """ Use this method to get the current bot description for the given user language. @@ -4696,11 +4696,11 @@ def get_my_description(self, language_code: Optional[str]=None): return types.BotDescription.de_json( apihelper.get_my_description(self.token, language_code=language_code)) - + def set_my_short_description(self, short_description:Optional[str]=None, language_code:Optional[str]=None): """ Use this method to change the bot's short description, which is shown on the bot's profile page and - is sent together with the link when users share the bot. + is sent together with the link when users share the bot. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setmyshortdescription @@ -4717,7 +4717,7 @@ def set_my_short_description(self, short_description:Optional[str]=None, languag return apihelper.set_my_short_description( self.token, short_description=short_description, language_code=language_code) - + def get_my_short_description(self, language_code: Optional[str]=None): """ Use this method to get the current bot short description for the given user language. @@ -4736,13 +4736,13 @@ def get_my_short_description(self, language_code: Optional[str]=None): def set_chat_menu_button(self, chat_id: Union[int, str]=None, menu_button: types.MenuButton=None) -> bool: """ - Use this method to change the bot's menu button in a private chat, - or the default menu button. + Use this method to change the bot's menu button in a private chat, + or the default menu button. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton - :param chat_id: Unique identifier for the target private chat. + :param chat_id: Unique identifier for the target private chat. If not specified, default bot's menu button will be changed. :type chat_id: :obj:`int` or :obj:`str` @@ -4774,13 +4774,13 @@ def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButto apihelper.get_chat_menu_button(self.token, chat_id=chat_id)) - def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, + def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, for_channels: Optional[bool]=None) -> bool: """ Use this method to change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. These rights will be suggested to users, but they are are free to modify - the list before adding the bot. + the list before adding the bot. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights @@ -4834,8 +4834,8 @@ def get_business_connection(self, business_connection_id: str) -> types.Business return types.BusinessConnection.de_json( apihelper.get_business_connection(self.token, business_connection_id) ) - - + + def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: @@ -4847,12 +4847,12 @@ def set_my_commands(self, commands: List[types.BotCommand], :param commands: List of BotCommand. At most 100 commands can be specified. :type commands: :obj:`list` of :class:`telebot.types.BotCommand` - :param scope: The scope of users for which the commands are relevant. + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -4861,22 +4861,22 @@ def set_my_commands(self, commands: List[types.BotCommand], """ return apihelper.set_my_commands(self.token, commands, scope=scope, language_code=language_code) - - def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, + + def delete_my_commands(self, scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: """ - Use this method to delete the list of the bot's commands for the given scope and user language. - After deletion, higher level commands will be shown to affected users. + Use this method to delete the list of the bot's commands for the given scope and user language. + After deletion, higher level commands will be shown to affected users. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#deletemycommands - - :param scope: The scope of users for which the commands are relevant. + + :param scope: The scope of users for which the commands are relevant. Defaults to BotCommandScopeDefault. :type scope: :class:`telebot.types.BotCommandScope` - :param language_code: A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, + :param language_code: A two-letter ISO 639-1 language code. If empty, + commands will be applied to all users from the given scope, for whose language there are no dedicated commands :type language_code: :obj:`str` @@ -4930,7 +4930,7 @@ def set_chat_description(self, chat_id: Union[int, str], description: Optional[s def pin_chat_message( - self, chat_id: Union[int, str], message_id: int, + self, chat_id: Union[int, str], message_id: int, disable_notification: Optional[bool]=False, business_connection_id: Optional[str]=None) -> bool: """ Use this method to pin a message in a supergroup. @@ -5005,10 +5005,10 @@ def unpin_all_chat_messages(self, chat_id: Union[int, str]) -> bool: def edit_message_text( - self, text: str, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + self, text: str, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, + inline_message_id: Optional[str]=None, parse_mode: Optional[str]=None, entities: Optional[List[types.MessageEntity]]=None, disable_web_page_preview: Optional[bool]=None, # deprecated, for backward compatibility @@ -5058,7 +5058,7 @@ def edit_message_text( :rtype: :obj:`types.Message` or :obj:`bool` """ parse_mode = self.parse_mode if (parse_mode is None) else parse_mode - + if disable_web_page_preview is not None: # show a deprecation warning logger.warning("The parameter 'disable_web_page_preview' is deprecated. Use 'link_preview_options' instead.") @@ -5089,9 +5089,9 @@ def edit_message_text( def edit_message_media( - self, media: Any, chat_id: Optional[Union[int, str]]=None, + self, media: Any, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: @@ -5100,7 +5100,7 @@ def edit_message_media( If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. When an inline message is edited, a new file can't be uploaded; use a previously uploaded file via its file_id or specify a URL. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned. - Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. + Note that business messages that were not sent by the bot and do not contain an inline keyboard can only be edited within 48 hours from the time they were sent. Telegram documentation: https://core.telegram.org/bots/api#editmessagemedia @@ -5109,7 +5109,7 @@ def edit_message_media( :param chat_id: Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` - :param message_id: Required if inline_message_id is not specified. Identifier of the sent message + :param message_id: Required if inline_message_id is not specified. Identifier of the sent message :type message_id: :obj:`int` :param inline_message_id: Required if chat_id and message_id are not specified. Identifier of the inline message @@ -5137,9 +5137,9 @@ def edit_message_media( def edit_message_reply_markup( - self, chat_id: Optional[Union[int, str]]=None, + self, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, - inline_message_id: Optional[str]=None, + inline_message_id: Optional[str]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None, timeout: Optional[int]=None) -> Union[types.Message, bool]: @@ -5179,10 +5179,10 @@ def edit_message_reply_markup( def send_game( - self, chat_id: Union[int, str], game_short_name: str, + self, chat_id: Union[int, str], game_short_name: str, disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility protect_content: Optional[bool]=None, @@ -5273,10 +5273,10 @@ def send_game( def set_game_score( - self, user_id: Union[int, str], score: int, - force: Optional[bool]=None, - chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + self, user_id: Union[int, str], score: int, + force: Optional[bool]=None, + chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None, disable_edit_message: Optional[bool]=None) -> Union[types.Message, bool]: """ @@ -5319,7 +5319,7 @@ def set_game_score( def get_game_high_scores( self, user_id: int, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None) -> List[types.GameHighScore]: """ Use this method to get data for high score tables. Will return the score of the specified user and several of @@ -5352,20 +5352,20 @@ def get_game_high_scores( def send_invoice( - self, chat_id: Union[int, str], title: str, description: str, - invoice_payload: str, provider_token: Union[str, None], currency: str, - prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, - photo_url: Optional[str]=None, photo_size: Optional[int]=None, + self, chat_id: Union[int, str], title: str, description: str, + invoice_payload: str, provider_token: Union[str, None], currency: str, + prices: List[types.LabeledPrice], start_parameter: Optional[str]=None, + photo_url: Optional[str]=None, photo_size: Optional[int]=None, photo_width: Optional[int]=None, photo_height: Optional[int]=None, - need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, + need_name: Optional[bool]=None, need_phone_number: Optional[bool]=None, need_email: Optional[bool]=None, need_shipping_address: Optional[bool]=None, - send_phone_number_to_provider: Optional[bool]=None, - send_email_to_provider: Optional[bool]=None, + send_phone_number_to_provider: Optional[bool]=None, + send_email_to_provider: Optional[bool]=None, is_flexible: Optional[bool]=None, - disable_notification: Optional[bool]=None, + disable_notification: Optional[bool]=None, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, - provider_data: Optional[str]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + provider_data: Optional[str]=None, timeout: Optional[int]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility max_tip_amount: Optional[int] = None, @@ -5416,7 +5416,7 @@ def send_invoice( :param photo_size: Photo size in bytes :type photo_size: :obj:`int` - :param photo_width: Photo width + :param photo_width: Photo width :type photo_width: :obj:`int` :param photo_height: Photo height @@ -5530,9 +5530,9 @@ def send_invoice( ) def create_invoice_link(self, - title: str, description: str, payload:str, provider_token: Union[str, None], + title: str, description: str, payload:str, provider_token: Union[str, None], currency: str, prices: List[types.LabeledPrice], - max_tip_amount: Optional[int] = None, + max_tip_amount: Optional[int] = None, suggested_tip_amounts: Optional[List[int]]=None, provider_data: Optional[str]=None, photo_url: Optional[str]=None, @@ -5548,9 +5548,9 @@ def create_invoice_link(self, is_flexible: Optional[bool]=None, subscription_period: Optional[int]=None, business_connection_id: Optional[str]=None) -> str: - + """ - Use this method to create a link for an invoice. + Use this method to create a link for an invoice. Returns the created invoice link as String on success. Telegram documentation: @@ -5649,17 +5649,17 @@ def create_invoice_link(self, # noinspection PyShadowingBuiltins def send_poll( self, chat_id: Union[int, str], question: str, options: List[Union[str, types.InputPollOption]], - is_anonymous: Optional[bool]=None, type: Optional[str]=None, - allows_multiple_answers: Optional[bool]=None, + is_anonymous: Optional[bool]=None, type: Optional[str]=None, + allows_multiple_answers: Optional[bool]=None, correct_option_id: Optional[int]=None, - explanation: Optional[str]=None, - explanation_parse_mode: Optional[str]=None, - open_period: Optional[int]=None, - close_date: Optional[Union[int, datetime]]=None, + explanation: Optional[str]=None, + explanation_parse_mode: Optional[str]=None, + open_period: Optional[int]=None, + close_date: Optional[Union[int, datetime]]=None, is_closed: Optional[bool]=None, disable_notification: Optional[bool]=False, reply_to_message_id: Optional[int]=None, # deprecated, for backward compatibility - reply_markup: Optional[REPLY_MARKUP_TYPES]=None, + reply_markup: Optional[REPLY_MARKUP_TYPES]=None, allow_sending_without_reply: Optional[bool]=None, # deprecated, for backward compatibility timeout: Optional[int]=None, explanation_entities: Optional[List[types.MessageEntity]]=None, @@ -5816,7 +5816,7 @@ def send_poll( def stop_poll( - self, chat_id: Union[int, str], message_id: int, + self, chat_id: Union[int, str], message_id: int, reply_markup: Optional[types.InlineKeyboardMarkup]=None, business_connection_id: Optional[str]=None) -> types.Poll: """ @@ -5845,8 +5845,8 @@ def stop_poll( def answer_shipping_query( - self, shipping_query_id: str, ok: bool, - shipping_options: Optional[List[types.ShippingOption]]=None, + self, shipping_query_id: str, ok: bool, + shipping_options: Optional[List[types.ShippingOption]]=None, error_message: Optional[str]=None) -> bool: """ Asks for an answer to a shipping question. @@ -5874,7 +5874,7 @@ def answer_shipping_query( def answer_pre_checkout_query( - self, pre_checkout_query_id: str, ok: bool, + self, pre_checkout_query_id: str, ok: bool, error_message: Optional[str]=None) -> bool: """ Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the @@ -5886,7 +5886,7 @@ def answer_pre_checkout_query( Telegram documentation: https://core.telegram.org/bots/api#answerprecheckoutquery - :param pre_checkout_query_id: Unique identifier for the query to be answered + :param pre_checkout_query_id: Unique identifier for the query to be answered :type pre_checkout_query_id: :obj:`int` :param ok: Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems. @@ -5902,11 +5902,11 @@ def answer_pre_checkout_query( """ return apihelper.answer_pre_checkout_query( self.token, pre_checkout_query_id, ok, error_message=error_message) - + def get_my_star_balance(self) -> types.StarAmount: """ Returns the bot's current Telegram Stars balance. On success, returns a StarAmount object. - + Telegram documentation: https://core.telegram.org/bots/api#getmystarbalance :return: On success, returns a StarAmount object. @@ -5933,8 +5933,8 @@ def get_star_transactions(self, offset: Optional[int]=None, limit: Optional[int] return types.StarTransactions.de_json( apihelper.get_star_transactions(self.token, offset=offset, limit=limit) ) - - + + def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> bool: """ Refunds a successful payment in Telegram Stars. Returns True on success. @@ -5973,10 +5973,10 @@ def edit_user_star_subscription(self, user_id: int, telegram_payment_charge_id: return apihelper.edit_user_star_subscription(self.token, user_id, telegram_payment_charge_id, is_canceled) def edit_message_caption( - self, caption: str, chat_id: Optional[Union[int, str]]=None, - message_id: Optional[int]=None, + self, caption: str, chat_id: Optional[Union[int, str]]=None, + message_id: Optional[int]=None, inline_message_id: Optional[str]=None, - parse_mode: Optional[str]=None, + parse_mode: Optional[str]=None, caption_entities: Optional[List[types.MessageEntity]]=None, reply_markup: Optional[types.InlineKeyboardMarkup]=None, show_caption_above_media: Optional[bool]=None, @@ -6068,12 +6068,12 @@ def reply_to(self, message: types.Message, text: str, **kwargs) -> types.Message def answer_inline_query( - self, inline_query_id: str, - results: List[Any], - cache_time: Optional[int]=None, - is_personal: Optional[bool]=None, + self, inline_query_id: str, + results: List[Any], + cache_time: Optional[int]=None, + is_personal: Optional[bool]=None, next_offset: Optional[str]=None, - switch_pm_text: Optional[str]=None, + switch_pm_text: Optional[str]=None, switch_pm_parameter: Optional[str]=None, button: Optional[types.InlineQueryResultsButton]=None) -> bool: """ @@ -6120,7 +6120,7 @@ def answer_inline_query( if not button and (switch_pm_text or switch_pm_parameter): logger.warning("switch_pm_text and switch_pm_parameter are deprecated for answer_inline_query. Use button instead.") button = types.InlineQueryResultsButton(text=switch_pm_text, start_parameter=switch_pm_parameter) - + return apihelper.answer_inline_query( self.token, inline_query_id, results, cache_time=cache_time, is_personal=is_personal, next_offset=next_offset, button=button) @@ -6128,7 +6128,7 @@ def answer_inline_query( def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool: """ - Use this method to clear the list of pinned messages in a General forum topic. + Use this method to clear the list of pinned messages in a General forum topic. The bot must be an administrator in the chat for this to work and must have the can_pin_messages administrator right in the supergroup. Returns True on success. @@ -6145,8 +6145,8 @@ def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bo def answer_callback_query( - self, callback_query_id: int, - text: Optional[str]=None, show_alert: Optional[bool]=None, + self, callback_query_id: int, + text: Optional[str]=None, show_alert: Optional[bool]=None, url: Optional[str]=None, cache_time: Optional[int]=None) -> bool: """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to @@ -6176,7 +6176,7 @@ def answer_callback_query( return apihelper.answer_callback_query( self.token, callback_query_id, text=text, show_alert=show_alert, url=url, cache_time=cache_time) - + def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types.UserChatBoosts: """ Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. @@ -6199,7 +6199,7 @@ def get_user_chat_boosts(self, chat_id: Union[int, str], user_id: int) -> types. # 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. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumbnail @@ -6230,11 +6230,11 @@ def set_sticker_set_thumbnail(self, name: str, user_id: int, thumbnail: Union[An return apihelper.set_sticker_set_thumbnail(self.token, name, user_id, thumbnail, format) - + @util.deprecated(deprecation_text="Use set_sticker_set_thumbnail instead") def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]=None): """ - Use this method to set the thumbnail of a sticker set. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success. Telegram documentation: https://core.telegram.org/bots/api#setstickersetthumb @@ -6258,7 +6258,7 @@ def set_sticker_set_thumb(self, name: str, user_id: int, thumb: Union[Any, str]= def get_sticker_set(self, name: str) -> types.StickerSet: """ Use this method to get a sticker set. On success, a StickerSet object is returned. - + Telegram documentation: https://core.telegram.org/bots/api#getstickerset :param name: Sticker set name @@ -6285,7 +6285,7 @@ def get_custom_emoji_stickers(self, custom_emoji_ids: List[str]) -> List[types.S result = apihelper.get_custom_emoji_stickers(self.token, custom_emoji_ids) return [types.Sticker.de_json(sticker) for sticker in result] - + def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: """ Use this method to change search keywords assigned to a regular or custom emoji sticker. @@ -6303,7 +6303,7 @@ def set_sticker_keywords(self, sticker: str, keywords: List[str]=None) -> bool: """ return apihelper.set_sticker_keywords(self.token, sticker, keywords=keywords) - + def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosition=None) -> bool: """ Use this method to change the mask position of a mask sticker. @@ -6320,7 +6320,7 @@ def set_sticker_mask_position(self, sticker: str, mask_position: types.MaskPosit :rtype: :obj:`bool` """ return apihelper.set_sticker_mask_position(self.token, sticker, mask_position=mask_position) - + def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Optional[str]=None) -> bool: """ @@ -6338,7 +6338,7 @@ def set_custom_emoji_sticker_set_thumbnail(self, name: str, custom_emoji_id: Opt """ return apihelper.set_custom_emoji_sticker_set_thumbnail(self.token, name, custom_emoji_id=custom_emoji_id) - + def set_sticker_set_title(self, name: str, title: str) -> bool: """ Use this method to set the title of a created sticker set. @@ -6355,7 +6355,7 @@ def set_sticker_set_title(self, name: str, title: str) -> bool: """ return apihelper.set_sticker_set_title(self.token, name, title) - + def delete_sticker_set(self, name:str) -> bool: """ Use this method to delete a sticker set. Returns True on success. @@ -6368,7 +6368,7 @@ def delete_sticker_set(self, name:str) -> bool: """ return apihelper.delete_sticker_set(self.token, name) - def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, + def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None, chat_id: Optional[Union[str, int]] = None) -> bool: """ @@ -6403,13 +6403,13 @@ def send_gift(self, user_id: Optional[Union[str, int]] = None, gift_id: str=None """ if user_id is None and chat_id is None: raise ValueError("Either user_id or chat_id must be specified.") - + if gift_id is None: raise ValueError("gift_id must be specified.") - + return apihelper.send_gift(self.token, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities, pay_for_upgrade=pay_for_upgrade, chat_id=chat_id, user_id=user_id) - + def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool: """ Verifies a user on behalf of the organization which is represented by the bot. Returns True on success. @@ -6585,12 +6585,12 @@ def set_business_account_gift_settings( :rtype: :obj:`bool` """ return apihelper.set_business_account_gift_settings(self.token, business_connection_id, show_gift_button, accepted_gift_types) - + def get_business_account_star_balance(self, business_connection_id: str) -> types.StarAmount: """ Returns the amount of Telegram Stars owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns StarAmount on success. - + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountstarbalance :param business_connection_id: Unique identifier of the business connection @@ -6602,7 +6602,7 @@ def get_business_account_star_balance(self, business_connection_id: str) -> type return types.StarAmount.de_json( apihelper.get_business_account_star_balance(self.token, business_connection_id) ) - + def transfer_business_account_stars(self, business_connection_id: str, star_count: int) -> bool: """ Transfers Telegram Stars from the business account balance to the bot's balance. Requires the can_transfer_stars business bot right. Returns True on success. @@ -6619,7 +6619,7 @@ def transfer_business_account_stars(self, business_connection_id: str, star_coun :rtype: :obj:`bool` """ return apihelper.transfer_business_account_stars(self.token, business_connection_id, star_count) - + def get_business_account_gifts( self, business_connection_id: str, exclude_unsaved: Optional[bool]=None, @@ -6632,7 +6632,7 @@ def get_business_account_gifts( limit: Optional[int]=None) -> types.OwnedGifts: """ Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. - + Telegram documentation: https://core.telegram.org/bots/api#getbusinessaccountgifts :param business_connection_id: Unique identifier of the business connection @@ -6695,7 +6695,7 @@ def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) :rtype: :obj:`bool` """ return apihelper.convert_gift_to_stars(self.token, business_connection_id, owned_gift_id) - + def upgrade_gift( self, business_connection_id: str, owned_gift_id: str, keep_original_details: Optional[bool]=None, @@ -6759,7 +6759,7 @@ def transfer_gift( new_owner_chat_id, star_count=star_count ) - + def post_story( self, business_connection_id: str, content: types.InputStoryContent, active_period: int, caption: Optional[str]=None, @@ -6866,7 +6866,7 @@ def edit_story( def delete_story(self, business_connection_id: str, story_id: int) -> bool: """ Deletes a story previously posted by the bot on behalf of a managed business account. Requires the can_manage_stories business bot right. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletestory :param business_connection_id: Unique identifier of the business connection @@ -6915,7 +6915,7 @@ def gift_premium_subscription( text=text, text_parse_mode=text_parse_mode, text_entities=text_entities ) - + def set_business_account_profile_photo( self, business_connection_id: str, photo: types.InputProfilePhoto, is_public: Optional[bool]=None) -> bool: @@ -6994,8 +6994,8 @@ def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, stic :rtype: :obj:`bool` """ return apihelper.replace_sticker_in_set(self.token, user_id, name, old_sticker, sticker) - - + + def set_sticker_emoji_list(self, sticker: str, emoji_list: List[str]) -> bool: """ Use this method to set the emoji list of a custom emoji sticker set. @@ -7017,7 +7017,7 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. - + Telegram documentation: https://core.telegram.org/bots/api#uploadstickerfile :param user_id: User identifier of sticker set owner @@ -7031,7 +7031,7 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s See https://core.telegram.org/stickers for technical requirements. More information on Sending Files » :type sticker: :class:`telebot.types.InputFile` - :param sticker_format: One of "static", "animated", "video". + :param sticker_format: One of "static", "animated", "video". :type sticker_format: :obj:`str` :return: On success, the sent file is returned. @@ -7041,17 +7041,17 @@ def upload_sticker_file(self, user_id: int, png_sticker: Union[Any, str]=None, s logger.warning('The parameter "png_sticker" is deprecated. Use "sticker" instead.') sticker = png_sticker sticker_format = "static" - + return types.File.de_json( apihelper.upload_sticker_file(self.token, user_id, sticker, sticker_format) ) def create_new_sticker_set( - self, user_id: int, name: str, title: str, - emojis: Optional[List[str]]=None, - png_sticker: Union[Any, str]=None, - tgs_sticker: Union[Any, str]=None, + self, user_id: int, name: str, title: str, + emojis: Optional[List[str]]=None, + png_sticker: Union[Any, str]=None, + tgs_sticker: Union[Any, str]=None, webm_sticker: Union[Any, str]=None, contains_masks: Optional[bool]=None, sticker_type: Optional[str]=None, @@ -7060,7 +7060,7 @@ def create_new_sticker_set( stickers: List[types.InputSticker]=None, sticker_format: Optional[str]=None) -> bool: """ - Use this method to create new sticker set owned by a user. + Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success. @@ -7124,11 +7124,11 @@ def create_new_sticker_set( sticker_format = 'video' elif png_sticker: sticker_format = 'static' - + if contains_masks is not None: logger.warning('The parameter "contains_masks" is deprecated, use "sticker_type" instead') if sticker_type is None: - sticker_type = 'mask' if contains_masks else 'regular' + sticker_type = 'mask' if contains_masks else 'regular' if stickers is None: stickers = png_sticker or tgs_sticker or webm_sticker @@ -7145,8 +7145,8 @@ def create_new_sticker_set( def add_sticker_to_set( self, user_id: int, name: str, emojis: Union[List[str], str], - png_sticker: Optional[Union[Any, str]]=None, - tgs_sticker: Optional[Union[Any, str]]=None, + png_sticker: Optional[Union[Any, str]]=None, + tgs_sticker: Optional[Union[Any, str]]=None, webm_sticker: Optional[Union[Any, str]]=None, mask_position: Optional[types.MaskPosition]=None, sticker: Optional[types.InputSticker]=None) -> bool: @@ -7210,7 +7210,7 @@ def add_sticker_to_set( def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: """ Use this method to move a sticker in a set created by the bot to a specific position . Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#setstickerpositioninset :param sticker: File identifier of the sticker @@ -7228,7 +7228,7 @@ def set_sticker_position_in_set(self, sticker: str, position: int) -> bool: def delete_sticker_from_set(self, sticker: str) -> bool: """ Use this method to delete a sticker from a set created by the bot. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#deletestickerfromset :param sticker: File identifier of the sticker @@ -7388,9 +7388,9 @@ def edit_general_forum_topic(self, chat_id: Union[int, str], name: str) -> bool: Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. Returns True on success. - + Telegram documentation: https://core.telegram.org/bots/api#editgeneralforumtopic - + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername) :type chat_id: :obj:`int` or :obj:`str` @@ -7473,7 +7473,7 @@ def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryR """ Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which - the query originated. + the query originated. On success, a SentWebAppMessage object is returned. Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery @@ -7541,7 +7541,7 @@ def register_for_reply(self, message: types.Message, callback: Callable, *args, :param args: Optional arguments for the callback function. :param kwargs: Optional keyword arguments for the callback function. - + :return: None """ self.register_for_reply_by_message_id(message.message_id, callback, *args, **kwargs) @@ -7644,8 +7644,8 @@ def set_state(self, user_id: int, state: Union[str, State], chat_id: Optional[in Added additional parameters to support topics, business connections, and message threads. - .. seealso:: - + .. seealso:: + For more details, visit the `custom_states.py example `_. :param user_id: User's identifier @@ -7677,7 +7677,7 @@ def set_state(self, user_id: int, state: Union[str, State], chat_id: Optional[in bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def reset_data(self, user_id: int, chat_id: Optional[int]=None, + def reset_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> bool: """ @@ -7720,7 +7720,7 @@ def delete_state(self, user_id: int, chat_id: Optional[int]=None, business_conne :param user_id: User's identifier :type user_id: :obj:`int` - + :param chat_id: Chat's identifier :type chat_id: :obj:`int` @@ -7775,7 +7775,7 @@ def retrieve_data(self, user_id: int, chat_id: Optional[int]=None, business_conn bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def get_state(self, user_id: int, chat_id: Optional[int]=None, + def get_state(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, message_thread_id: Optional[int]=None, bot_id: Optional[int]=None) -> str: """ @@ -7813,9 +7813,9 @@ def get_state(self, user_id: int, chat_id: Optional[int]=None, bot_id=bot_id, business_connection_id=business_connection_id, message_thread_id=message_thread_id) - def add_data(self, user_id: int, chat_id: Optional[int]=None, + def add_data(self, user_id: int, chat_id: Optional[int]=None, business_connection_id: Optional[str]=None, - message_thread_id: Optional[int]=None, + message_thread_id: Optional[int]=None, bot_id: Optional[int]=None, **kwargs) -> None: """ @@ -7862,7 +7862,7 @@ def register_next_step_handler_by_chat_id( :type callback: :obj:`Callable[[telebot.types.Message], None]` :param args: Args to pass in callback func - + :param kwargs: Args to pass in callback func :return: None @@ -8032,7 +8032,7 @@ def register_middleware_handler(self, callback, update_types=None): bot = TeleBot('TOKEN') bot.register_middleware_handler(print_channel_post_text, update_types=['channel_post', 'edited_channel_post']) - + :param callback: Function that will be used as a middleware handler. :type callback: :obj:`function` @@ -8212,7 +8212,7 @@ def register_message_handler(self, callback: Callable, content_types: Optional[L logger.warning("register_message_handler: 'content_types' filter should be List of strings (content types), not string.") content_types = [content_types] - + handler_dict = self._build_handler_dict(callback, chat_types=chat_types, @@ -8284,7 +8284,7 @@ def add_edited_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_message_handler to add edited_message_handler to the bot. - + :meta private: :param handler_dict: @@ -8406,7 +8406,7 @@ def add_channel_post_handler(self, handler_dict): Note that you should use register_channel_post_handler to add channel_post_handler to the bot. :meta private: - + :param handler_dict: :return: """ @@ -8626,7 +8626,7 @@ def register_message_reaction_handler(self, callback: Callable, func: Callable=N :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -8654,7 +8654,7 @@ def decorator(handler): return decorator - + def add_message_reaction_count_handler(self, handler_dict): """ Adds message reaction count handler @@ -8680,7 +8680,7 @@ def register_message_reaction_count_handler(self, callback: Callable, func: Call :param pass_bot: True if you need to pass TeleBot instance to handler(useful for separating handlers into different files) :type pass_bot: :obj:`bool` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -8751,7 +8751,7 @@ def chosen_inline_handler(self, func, **kwargs): :param func: Function executed as a filter :type func: :obj:`function` - + :param kwargs: Optional keyword arguments(custom filters) :return: None @@ -8807,7 +8807,7 @@ def callback_query_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -8861,7 +8861,7 @@ def shipping_query_handler(self, func, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -8938,7 +8938,7 @@ def add_pre_checkout_query_handler(self, handler_dict): """ self.pre_checkout_query_handlers.append(handler_dict) - + def register_pre_checkout_query_handler(self, callback: Callable, func: Callable, pass_bot: Optional[bool]=False, **kwargs): """ Registers pre-checkout request handler. @@ -8973,9 +8973,9 @@ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_purchased_paid_media_handler(handler_dict) return handler - + return decorator - + def add_purchased_paid_media_handler(self, handler_dict): """ Adds a purchased paid media handler @@ -8994,7 +8994,7 @@ def register_purchased_paid_media_handler(self, callback: Callable, func: Callab :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -9071,7 +9071,7 @@ def poll_answer_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -9238,7 +9238,7 @@ def chat_join_request_handler(self, func=None, **kwargs): :type func: :obj:`function` :param kwargs: Optional keyword arguments(custom filters) - + :return: None """ def decorator(handler): @@ -9286,7 +9286,7 @@ def register_chat_join_request_handler( def chat_boost_handler(self, func=None, **kwargs): """ - Handles new incoming chat boost state. + Handles new incoming chat boost state. it passes :class:`telebot.types.ChatBoostUpdated` object. :param func: Function executed as a filter @@ -9323,7 +9323,7 @@ def register_chat_boost_handler( :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -9339,7 +9339,7 @@ def register_chat_boost_handler( def removed_chat_boost_handler(self, func=None, **kwargs): """ - Handles new incoming chat boost state. + Handles new incoming chat boost state. it passes :class:`telebot.types.ChatBoostRemoved` object. :param func: Function executed as a filter @@ -9355,7 +9355,7 @@ def decorator(handler): return decorator - + def add_removed_chat_boost_handler(self, handler_dict): """ Adds a removed_chat_boost handler. @@ -9376,7 +9376,7 @@ def register_removed_chat_boost_handler( :param callback: function to be called :type callback: :obj:`function` - + :param func: Function executed as a filter :type func: :obj:`function` @@ -9404,7 +9404,7 @@ def decorator(handler): handler_dict = self._build_handler_dict(handler, func=func, **kwargs) self.add_business_connection_handler(handler_dict) return handler - + return decorator @@ -9572,7 +9572,7 @@ def register_business_message_handler(self, :return: None """ - handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, + handler_dict = self._build_handler_dict(callback, content_types=content_types, commands=commands, regexp=regexp, func=func, pass_bot=pass_bot, **kwargs) self.add_business_message_handler(handler_dict) @@ -9632,7 +9632,7 @@ def add_edited_business_message_handler(self, handler_dict): """ Adds the edit message handler Note that you should use register_edited_business_message_handler to add edited_business_message_handler to the bot. - + :meta private: :param handler_dict: @@ -9709,7 +9709,7 @@ def decorator(handler): self.add_deleted_business_messages_handler(handler_dict) return handler - + return decorator @@ -9829,7 +9829,7 @@ def _get_middlewares(self, update_type): :return: """ middlewares = None - if self.middlewares: + if self.middlewares: middlewares = [i for i in self.middlewares if update_type in i.update_types] return middlewares From 8335648c81ec82ca9bdb546ff1966fc29ceb7da3 Mon Sep 17 00:00:00 2001 From: Badiboy Date: Wed, 3 Sep 2025 17:57:52 +0300 Subject: [PATCH 1807/1808] Bump version (fix) --- 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 514d9dad4..d1aa0b25d 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.29.0' +release = '4.29.1' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 5271bfa16..d226c3d64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTelegramBotAPI" -version = "4.29.0" +version = "4.29.1" 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 e4d2a2e0c..14e603359 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.29.0' +__version__ = '4.29.1' From 5e5661dc857ad52b6fde27130a2d843b8148bce4 Mon Sep 17 00:00:00 2001 From: Savvich Date: Tue, 21 Oct 2025 23:43:48 +0300 Subject: [PATCH 1808/1808] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29764fb3c..8a6c7fd96 100644 --- a/README.md +++ b/README.md @@ -572,7 +572,7 @@ Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra deta ## Advanced use of the API -### Using local Bot API Sever +### Using local Bot API Server Since version 5.0 of the Bot API, you have the possibility to run your own [Local Bot API Server](https://core.telegram.org/bots/api#using-a-local-bot-api-server). pyTelegramBotAPI also supports this feature. ```python